Repository: microsoft/winget-cli Branch: master Commit: 9659f5a5115b Files: 2565 Total size: 16.9 MB Directory structure: gitextract_qbjki5ie/ ├── .config/ │ ├── configuration.vsEnterprise.winget │ ├── configuration.vsProfessional.winget │ └── configuration.winget ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── Bug_Report.yml │ │ ├── Documentation_Issue.yml │ │ ├── Feature_Request.yml │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── actions/ │ │ └── spelling/ │ │ ├── README.md │ │ ├── advice.md │ │ ├── allow.txt │ │ ├── candidate.patterns │ │ ├── excludes.txt │ │ ├── expect.txt │ │ ├── line_forbidden.patterns │ │ ├── patterns.txt │ │ └── reject.txt │ ├── copilot-instructions.md │ ├── images/ │ │ └── README.md │ ├── policies/ │ │ ├── automergeTriggers.yml │ │ ├── labelAdded.noRecentActivity.yml │ │ ├── labelManagement.issueClosed.yml │ │ ├── labelManagement.issueOpened.yml │ │ ├── labelManagement.issueUpdated.yml │ │ ├── labelManagement.needsFeedbackHub.yml │ │ ├── labelManagement.triageLabels.yml │ │ ├── moderatorTriggers.yml │ │ ├── scheduledSearch.closeNoRecentActivity.yml │ │ └── scheduledSearch.markNoRecentActivity.yml │ └── workflows/ │ ├── automatic-issue-deduplication.yml │ ├── spelling.yml │ ├── spelling2.yml │ └── spelling3.yml ├── .gitignore ├── .vsconfig ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Localization/ │ ├── Policies/ │ │ ├── de-DE/ │ │ │ └── DesktopAppInstaller.adml │ │ ├── es-ES/ │ │ │ └── DesktopAppInstaller.adml │ │ ├── fr-FR/ │ │ │ └── DesktopAppInstaller.adml │ │ ├── it-IT/ │ │ │ └── DesktopAppInstaller.adml │ │ ├── ja-JP/ │ │ │ └── DesktopAppInstaller.adml │ │ ├── ko-KR/ │ │ │ └── DesktopAppInstaller.adml │ │ ├── pt-BR/ │ │ │ └── DesktopAppInstaller.adml │ │ ├── ru-RU/ │ │ │ └── DesktopAppInstaller.adml │ │ ├── zh-CN/ │ │ │ └── DesktopAppInstaller.adml │ │ └── zh-TW/ │ │ └── DesktopAppInstaller.adml │ ├── Resources/ │ │ ├── de-DE/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ ├── es-ES/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ ├── fr-FR/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ ├── it-IT/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ ├── ja-JP/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ ├── ko-KR/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ ├── pt-BR/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ ├── ru-RU/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ ├── zh-CN/ │ │ │ ├── Resources.resw │ │ │ └── winget.resw │ │ └── zh-TW/ │ │ ├── Resources.resw │ │ └── winget.resw │ └── Settings/ │ └── LocConfig.xml ├── NOTICE ├── PRIVACY.md ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── azure-pipelines.loc.yml ├── azure-pipelines.yml ├── cgmanifest.json ├── doc/ │ ├── Completion.md │ ├── Developing.md │ ├── ReleaseNotes.md │ ├── Settings.md │ ├── admx/ │ │ ├── DesktopAppInstaller.admx │ │ └── en-US/ │ │ └── DesktopAppInstaller.adml │ ├── specs/ │ │ ├── #1012 - Show dependencies.md │ │ ├── #1155 - Enhancements to list command.md │ │ ├── #1287 - Management of package type dependencies.md │ │ ├── #140 - ZIP Support.md │ │ ├── #148 - Repair Support.md │ │ ├── #163 - Dependencies.md │ │ ├── #164 - PWA Support.md │ │ ├── #182 - Support for installation of portable standalone apps.md │ │ ├── #190 - Proxy Support.md │ │ ├── #292 - winget should install an app if there is an exact match.md │ │ ├── #3026 - Bypass Microsoft Store blocked for COM API.md │ │ ├── #364 - Feature Toggle.md │ │ ├── #396 - Settings command.md │ │ ├── #476 - Package Pinning.md │ │ ├── #5949 - Authenticated GitHub API Requests for PowerShell Module.md │ │ ├── #658 - WinGet Download.md │ │ ├── #888 - Com Api.md │ │ ├── #893 - Support for arbitrary HTTP header for Rest sources.md │ │ ├── #929 #2334 - Improvements and behavior changes to winget install and winget upgrade flow.md │ │ ├── #980 - Apps and Features entries version mapping.md │ │ ├── Configuration-COM-API.md │ │ └── spec-template.md │ ├── troubleshooting/ │ │ └── README.md │ ├── windows/ │ │ └── package-manager/ │ │ ├── index.md │ │ ├── package/ │ │ │ ├── binary-validation-errors.md │ │ │ ├── index.md │ │ │ ├── manifest.md │ │ │ ├── repository.md │ │ │ ├── windows-package-manager-policies-change-history.md │ │ │ ├── windows-package-manager-policies.md │ │ │ ├── winget-validation-troubleshooter.md │ │ │ └── winget-validation.md │ │ └── winget/ │ │ ├── export.md │ │ ├── features.md │ │ ├── hash.md │ │ ├── help.md │ │ ├── import.md │ │ ├── index.md │ │ ├── install.md │ │ ├── list.md │ │ ├── returnCodes.md │ │ ├── search.md │ │ ├── settings.md │ │ ├── show.md │ │ ├── source.md │ │ ├── uninstall.md │ │ ├── upgrade.md │ │ └── validate.md │ └── windows-package-manager-release-roadmap.md ├── samples/ │ ├── MinimalCallers/ │ │ ├── C#/ │ │ │ ├── Configuration-InProc/ │ │ │ │ ├── C#_Configuration_InProc.csproj │ │ │ │ └── Program.cs │ │ │ ├── Configuration-OutOfProc/ │ │ │ │ ├── C#_Configuration_OutOfProc.csproj │ │ │ │ └── Program.cs │ │ │ ├── WinGet-InProc/ │ │ │ │ ├── C#_WinGet_InProc.csproj │ │ │ │ └── Program.cs │ │ │ └── WinGet-OutOfProc/ │ │ │ ├── C#_WinGet_OutOfProc.csproj │ │ │ └── Program.cs │ │ ├── C++/ │ │ │ ├── WinGet-InProc/ │ │ │ │ ├── WinGet-InProc.cpp │ │ │ │ ├── WinGet-InProc.vcxproj │ │ │ │ ├── WinGet-InProc.vcxproj.filters │ │ │ │ └── packages.config │ │ │ └── WinGet-OutOfProc/ │ │ │ ├── WinGet-OutOfProc.cpp │ │ │ ├── WinGet-OutOfProc.vcxproj │ │ │ ├── WinGet-OutOfProc.vcxproj.filters │ │ │ └── packages.config │ │ ├── MinimalCallers.sln │ │ └── README.md │ └── WinGetUWPCaller/ │ ├── WinGetUWPCaller/ │ │ ├── ActivePackageView.cpp │ │ ├── ActivePackageView.h │ │ ├── App.cpp │ │ ├── App.h │ │ ├── App.idl │ │ ├── App.xaml │ │ ├── MainPage.cpp │ │ ├── MainPage.h │ │ ├── MainPage.idl │ │ ├── MainPage.xaml │ │ ├── Package.appxmanifest │ │ ├── WinGetUWPCaller.vcxproj │ │ ├── WinGetUWPCaller.vcxproj.filters │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ └── WinGetUWPCaller.sln ├── schemas/ │ └── JSON/ │ ├── configuration/ │ │ ├── configuration.schema.0.1.json │ │ └── configuration.schema.0.2.json │ ├── manifests/ │ │ ├── latest/ │ │ │ ├── manifest.defaultLocale.latest.json │ │ │ ├── manifest.installer.latest.json │ │ │ ├── manifest.locale.latest.json │ │ │ ├── manifest.singleton.latest.json │ │ │ └── manifest.version.latest.json │ │ ├── preview/ │ │ │ └── manifest.0.1.0.json │ │ ├── v1.0.0/ │ │ │ ├── manifest.defaultLocale.1.0.0.json │ │ │ ├── manifest.installer.1.0.0.json │ │ │ ├── manifest.locale.1.0.0.json │ │ │ ├── manifest.singleton.1.0.0.json │ │ │ └── manifest.version.1.0.0.json │ │ ├── v1.1.0/ │ │ │ ├── manifest.defaultLocale.1.1.0.json │ │ │ ├── manifest.installer.1.1.0.json │ │ │ ├── manifest.locale.1.1.0.json │ │ │ ├── manifest.singleton.1.1.0.json │ │ │ └── manifest.version.1.1.0.json │ │ ├── v1.10.0/ │ │ │ ├── manifest.defaultLocale.1.10.0.json │ │ │ ├── manifest.installer.1.10.0.json │ │ │ ├── manifest.locale.1.10.0.json │ │ │ ├── manifest.singleton.1.10.0.json │ │ │ └── manifest.version.1.10.0.json │ │ ├── v1.12.0/ │ │ │ ├── manifest.defaultLocale.1.12.0.json │ │ │ ├── manifest.installer.1.12.0.json │ │ │ ├── manifest.locale.1.12.0.json │ │ │ ├── manifest.singleton.1.12.0.json │ │ │ └── manifest.version.1.12.0.json │ │ ├── v1.2.0/ │ │ │ ├── manifest.defaultLocale.1.2.0.json │ │ │ ├── manifest.installer.1.2.0.json │ │ │ ├── manifest.locale.1.2.0.json │ │ │ ├── manifest.singleton.1.2.0.json │ │ │ └── manifest.version.1.2.0.json │ │ ├── v1.4.0/ │ │ │ ├── manifest.defaultLocale.1.4.0.json │ │ │ ├── manifest.installer.1.4.0.json │ │ │ ├── manifest.locale.1.4.0.json │ │ │ ├── manifest.singleton.1.4.0.json │ │ │ └── manifest.version.1.4.0.json │ │ ├── v1.5.0/ │ │ │ ├── manifest.defaultLocale.1.5.0.json │ │ │ ├── manifest.installer.1.5.0.json │ │ │ ├── manifest.locale.1.5.0.json │ │ │ ├── manifest.singleton.1.5.0.json │ │ │ └── manifest.version.1.5.0.json │ │ ├── v1.6.0/ │ │ │ ├── manifest.defaultLocale.1.6.0.json │ │ │ ├── manifest.installer.1.6.0.json │ │ │ ├── manifest.locale.1.6.0.json │ │ │ ├── manifest.singleton.1.6.0.json │ │ │ └── manifest.version.1.6.0.json │ │ ├── v1.7.0/ │ │ │ ├── manifest.defaultLocale.1.7.0.json │ │ │ ├── manifest.installer.1.7.0.json │ │ │ ├── manifest.locale.1.7.0.json │ │ │ ├── manifest.singleton.1.7.0.json │ │ │ └── manifest.version.1.7.0.json │ │ └── v1.9.0/ │ │ ├── manifest.defaultLocale.1.9.0.json │ │ ├── manifest.installer.1.9.0.json │ │ ├── manifest.locale.1.9.0.json │ │ ├── manifest.singleton.1.9.0.json │ │ └── manifest.version.1.9.0.json │ └── settings/ │ ├── settings.export.schema.0.1.json │ └── settings.schema.0.2.json ├── src/ │ ├── AppInstallerCLI/ │ │ ├── AppInstallerCLI.vcxproj │ │ ├── AppInstallerCLI.vcxproj.filters │ │ ├── PropertySheet.props │ │ ├── main.cpp │ │ └── packages.config │ ├── AppInstallerCLI.sln │ ├── AppInstallerCLICore/ │ │ ├── AppInstallerCLICore.vcxproj │ │ ├── AppInstallerCLICore.vcxproj.filters │ │ ├── Argument.cpp │ │ ├── Argument.h │ │ ├── COMContext.cpp │ │ ├── COMContext.h │ │ ├── ChannelStreams.cpp │ │ ├── ChannelStreams.h │ │ ├── CheckpointManager.cpp │ │ ├── CheckpointManager.h │ │ ├── Command.cpp │ │ ├── Command.h │ │ ├── Commands/ │ │ │ ├── COMCommand.cpp │ │ │ ├── COMCommand.h │ │ │ ├── CompleteCommand.cpp │ │ │ ├── CompleteCommand.h │ │ │ ├── ConfigureCommand.cpp │ │ │ ├── ConfigureCommand.h │ │ │ ├── ConfigureListCommand.cpp │ │ │ ├── ConfigureListCommand.h │ │ │ ├── ConfigureShowCommand.cpp │ │ │ ├── ConfigureShowCommand.h │ │ │ ├── ConfigureTestCommand.cpp │ │ │ ├── ConfigureTestCommand.h │ │ │ ├── ConfigureValidateCommand.cpp │ │ │ ├── ConfigureValidateCommand.h │ │ │ ├── DebugCommand.cpp │ │ │ ├── DebugCommand.h │ │ │ ├── DownloadCommand.cpp │ │ │ ├── DownloadCommand.h │ │ │ ├── DscAdminSettingsResource.cpp │ │ │ ├── DscAdminSettingsResource.h │ │ │ ├── DscCommand.cpp │ │ │ ├── DscCommand.h │ │ │ ├── DscCommandBase.cpp │ │ │ ├── DscCommandBase.h │ │ │ ├── DscComposableObject.cpp │ │ │ ├── DscComposableObject.h │ │ │ ├── DscPackageResource.cpp │ │ │ ├── DscPackageResource.h │ │ │ ├── DscSourceResource.cpp │ │ │ ├── DscSourceResource.h │ │ │ ├── DscTestFileResource.cpp │ │ │ ├── DscTestFileResource.h │ │ │ ├── DscTestJsonResource.cpp │ │ │ ├── DscTestJsonResource.h │ │ │ ├── DscUserSettingsFileResource.cpp │ │ │ ├── DscUserSettingsFileResource.h │ │ │ ├── ErrorCommand.cpp │ │ │ ├── ErrorCommand.h │ │ │ ├── ExperimentalCommand.cpp │ │ │ ├── ExperimentalCommand.h │ │ │ ├── ExportCommand.cpp │ │ │ ├── ExportCommand.h │ │ │ ├── FeaturesCommand.cpp │ │ │ ├── FeaturesCommand.h │ │ │ ├── FontCommand.cpp │ │ │ ├── FontCommand.h │ │ │ ├── HashCommand.cpp │ │ │ ├── HashCommand.h │ │ │ ├── ImportCommand.cpp │ │ │ ├── ImportCommand.h │ │ │ ├── InstallCommand.cpp │ │ │ ├── InstallCommand.h │ │ │ ├── ListCommand.cpp │ │ │ ├── ListCommand.h │ │ │ ├── McpCommand.cpp │ │ │ ├── McpCommand.h │ │ │ ├── PinCommand.cpp │ │ │ ├── PinCommand.h │ │ │ ├── RepairCommand.cpp │ │ │ ├── RepairCommand.h │ │ │ ├── ResumeCommand.cpp │ │ │ ├── ResumeCommand.h │ │ │ ├── RootCommand.cpp │ │ │ ├── RootCommand.h │ │ │ ├── SearchCommand.cpp │ │ │ ├── SearchCommand.h │ │ │ ├── SettingsCommand.cpp │ │ │ ├── SettingsCommand.h │ │ │ ├── ShowCommand.cpp │ │ │ ├── ShowCommand.h │ │ │ ├── SourceCommand.cpp │ │ │ ├── SourceCommand.h │ │ │ ├── TestCommand.cpp │ │ │ ├── TestCommand.h │ │ │ ├── UninstallCommand.cpp │ │ │ ├── UninstallCommand.h │ │ │ ├── UpgradeCommand.cpp │ │ │ ├── UpgradeCommand.h │ │ │ ├── ValidateCommand.cpp │ │ │ └── ValidateCommand.h │ │ ├── CompletionData.cpp │ │ ├── CompletionData.h │ │ ├── ConfigurationCommon.cpp │ │ ├── ConfigurationCommon.h │ │ ├── ConfigurationContext.cpp │ │ ├── ConfigurationContext.h │ │ ├── ConfigurationDynamicRuntimeFactory.cpp │ │ ├── ConfigurationSetProcessorFactoryRemoting.cpp │ │ ├── ConfigurationWingetDscModuleUnitValidation.cpp │ │ ├── ConfigurationWingetDscModuleUnitValidation.h │ │ ├── ConfigureExportCommand.cpp │ │ ├── ConfigureExportCommand.h │ │ ├── ContextOrchestrator.cpp │ │ ├── ContextOrchestrator.h │ │ ├── Core.cpp │ │ ├── ExecutionArgs.h │ │ ├── ExecutionContext.cpp │ │ ├── ExecutionContext.h │ │ ├── ExecutionContextData.h │ │ ├── ExecutionProgress.cpp │ │ ├── ExecutionProgress.h │ │ ├── ExecutionReporter.cpp │ │ ├── ExecutionReporter.h │ │ ├── Invocation.h │ │ ├── PackageCollection.cpp │ │ ├── PackageCollection.h │ │ ├── PortableInstaller.cpp │ │ ├── PortableInstaller.h │ │ ├── PropertySheet.props │ │ ├── Public/ │ │ │ ├── AppInstallerCLICore.h │ │ │ ├── ConfigurationSetProcessorFactoryRemoting.h │ │ │ └── ShutdownMonitoring.h │ │ ├── Resources.cpp │ │ ├── Resources.h │ │ ├── Search/ │ │ │ └── Search.h │ │ ├── ShutdownMonitoring.cpp │ │ ├── Sixel.cpp │ │ ├── Sixel.h │ │ ├── TableOutput.h │ │ ├── VTSupport.cpp │ │ ├── VTSupport.h │ │ ├── Workflows/ │ │ │ ├── ArchiveFlow.cpp │ │ │ ├── ArchiveFlow.h │ │ │ ├── CompletionFlow.cpp │ │ │ ├── CompletionFlow.h │ │ │ ├── ConfigurationFlow.cpp │ │ │ ├── ConfigurationFlow.h │ │ │ ├── DependenciesFlow.cpp │ │ │ ├── DependenciesFlow.h │ │ │ ├── DependencyNodeProcessor.cpp │ │ │ ├── DependencyNodeProcessor.h │ │ │ ├── DownloadFlow.cpp │ │ │ ├── DownloadFlow.h │ │ │ ├── FontFlow.cpp │ │ │ ├── FontFlow.h │ │ │ ├── ImportExportFlow.cpp │ │ │ ├── ImportExportFlow.h │ │ │ ├── InstallFlow.cpp │ │ │ ├── InstallFlow.h │ │ │ ├── MSStoreInstallerHandler.cpp │ │ │ ├── MSStoreInstallerHandler.h │ │ │ ├── MsiInstallFlow.cpp │ │ │ ├── MsiInstallFlow.h │ │ │ ├── MultiQueryFlow.cpp │ │ │ ├── MultiQueryFlow.h │ │ │ ├── PinFlow.cpp │ │ │ ├── PinFlow.h │ │ │ ├── PortableFlow.cpp │ │ │ ├── PortableFlow.h │ │ │ ├── PromptFlow.cpp │ │ │ ├── PromptFlow.h │ │ │ ├── RepairFlow.cpp │ │ │ ├── RepairFlow.h │ │ │ ├── ResumeFlow.cpp │ │ │ ├── ResumeFlow.h │ │ │ ├── SettingsFlow.cpp │ │ │ ├── SettingsFlow.h │ │ │ ├── ShellExecuteInstallerHandler.cpp │ │ │ ├── ShellExecuteInstallerHandler.h │ │ │ ├── ShowFlow.cpp │ │ │ ├── ShowFlow.h │ │ │ ├── SourceFlow.cpp │ │ │ ├── SourceFlow.h │ │ │ ├── UninstallFlow.cpp │ │ │ ├── UninstallFlow.h │ │ │ ├── UpdateFlow.cpp │ │ │ ├── UpdateFlow.h │ │ │ ├── WorkflowBase.cpp │ │ │ └── WorkflowBase.h │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── AppInstallerCLIE2ETests/ │ │ ├── AppInstallerCLIE2ETests.csproj │ │ ├── AppShutdownTests.cs │ │ ├── BaseCommand.cs │ │ ├── ConfigureCommand.cs │ │ ├── ConfigureExportCommand.cs │ │ ├── ConfigureListCommand.cs │ │ ├── ConfigureShowCommand.cs │ │ ├── ConfigureTestCommand.cs │ │ ├── ConfigureValidateCommand.cs │ │ ├── Constants.cs │ │ ├── DSCv3AdminSettingsResourceCommand.cs │ │ ├── DSCv3PackageResourceCommand.cs │ │ ├── DSCv3ResourceTestBase.cs │ │ ├── DSCv3SourceResourceCommand.cs │ │ ├── DSCv3UserSettingsFileResourceCommand.cs │ │ ├── DownloadCommand.cs │ │ ├── ErrorCommand.cs │ │ ├── FeaturesCommand.cs │ │ ├── FontCommand.cs │ │ ├── GroupPolicy.cs │ │ ├── GroupPolicyHelper.cs │ │ ├── HashCommand.cs │ │ ├── Helpers/ │ │ │ ├── TestCommon.cs │ │ │ ├── TestIndex.cs │ │ │ ├── TestSetup.cs │ │ │ └── WinGetSettingsHelper.cs │ │ ├── ImportCommand.cs │ │ ├── InprocTestbedTests.cs │ │ ├── InstallCommand.cs │ │ ├── Interop/ │ │ │ ├── BaseInterop.cs │ │ │ ├── CheckInstalledStatusInterop.cs │ │ │ ├── DownloadInterop.cs │ │ │ ├── FindPackagesInterop.cs │ │ │ ├── GroupPolicyForInterop.cs │ │ │ ├── InstallInterop.cs │ │ │ ├── InstanceInitializersSource.cs │ │ │ ├── InteropSetUpFixture.cs │ │ │ ├── PackageCatalogInterop.cs │ │ │ ├── RepairInterop.cs │ │ │ ├── Shutdown.cs │ │ │ ├── UninstallInterop.cs │ │ │ └── UpgradeInterop.cs │ │ ├── ListCommand.cs │ │ ├── Pinning.cs │ │ ├── PowerShell/ │ │ │ └── PowerShellHost.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── README.md │ │ ├── RepairCommand.cs │ │ ├── ResumeCommand.cs │ │ ├── RunCommandException.cs │ │ ├── SearchCommand.cs │ │ ├── SetUpFixture.cs │ │ ├── ShowCommand.cs │ │ ├── SourceCommand.cs │ │ ├── Test.runsettings │ │ ├── TestData/ │ │ │ ├── AppInstallerTestMsiInstaller.msi │ │ │ ├── AppInstallerTestMsiInstallerV2.msi │ │ │ ├── Configuration/ │ │ │ │ ├── ConfigServerUnexpectedExit.yml │ │ │ │ ├── Configure_TestRepo.yml │ │ │ │ ├── Configure_TestRepo_DSCv3.yml │ │ │ │ ├── Configure_TestRepo_Location.yml │ │ │ │ ├── DependencyCycle.yml │ │ │ │ ├── DependentResources_Failure.yml │ │ │ │ ├── DuplicateIdentifiers.yml │ │ │ │ ├── Empty.yml │ │ │ │ ├── GetPSModulePath.yml │ │ │ │ ├── IndependentResources_OneFailure.yml │ │ │ │ ├── Init-TestRepository.ps1 │ │ │ │ ├── LargeContentStrings.yml │ │ │ │ ├── MissingDependency.yml │ │ │ │ ├── ModuleMismatch.yml │ │ │ │ ├── Modules/ │ │ │ │ │ ├── xE2EMalicious/ │ │ │ │ │ │ ├── xE2EMalicious.psd1 │ │ │ │ │ │ └── xE2EMalicious.psm1 │ │ │ │ │ └── xE2ETestResource/ │ │ │ │ │ ├── xE2ETestResource.psd1 │ │ │ │ │ └── xE2ETestResource.psm1 │ │ │ │ ├── NoResourceName.yml │ │ │ │ ├── NoVersion.yml │ │ │ │ ├── NotConfig.yml │ │ │ │ ├── PSGallery_NoModule_NoSettings.yml │ │ │ │ ├── PSGallery_NoSettings.yml │ │ │ │ ├── ResourceCaseInsensitive.yml │ │ │ │ ├── ResourceNotFound.yml │ │ │ │ ├── ResourcesNotASequence.yml │ │ │ │ ├── RunCommandOnSet.yml │ │ │ │ ├── ShowDetails_DSCv3.yml │ │ │ │ ├── ShowDetails_TestRepo.yml │ │ │ │ ├── ShowDetails_TestRepo_0_3.yml │ │ │ │ ├── UnitNotAMap.yml │ │ │ │ ├── UnknownVersion.yml │ │ │ │ ├── Unknown_Processor.yml │ │ │ │ ├── WinGetDscResourceValidate_DependencySourceMissing.yml │ │ │ │ ├── WinGetDscResourceValidate_Good.yml │ │ │ │ ├── WinGetDscResourceValidate_PackageNotFound.yml │ │ │ │ ├── WinGetDscResourceValidate_PackageVersionNotFound.yml │ │ │ │ ├── WinGetDscResourceValidate_SourceOpenFailed.yml │ │ │ │ ├── WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml │ │ │ │ ├── WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml │ │ │ │ └── WithParameters_0_3.yml │ │ │ ├── ImportFiles/ │ │ │ │ ├── ImportFile-Bad-Invalid.json │ │ │ │ ├── ImportFile-Bad-UnknownPackage.json │ │ │ │ ├── ImportFile-Bad-UnknownPackageVersion.json │ │ │ │ ├── ImportFile-Bad-UnknownSource.json │ │ │ │ ├── ImportFile-Good.1.0.json │ │ │ │ └── ImportFile-Good.2.0.json │ │ │ ├── IndexPackageManifest.xml │ │ │ ├── Manifests/ │ │ │ │ ├── TestArpVersionMapping_OppositeOrder_1.0.yaml │ │ │ │ ├── TestArpVersionMapping_OppositeOrder_2.0.yaml │ │ │ │ ├── TestArpVersionMapping_SameAsPackageVersion.yaml │ │ │ │ ├── TestArpVersionMapping_SameOrder_1.0.yaml │ │ │ │ ├── TestArpVersionMapping_SameOrder_2.0.yaml │ │ │ │ ├── TestBurnInstaller.MissingRepairBehavior.yaml │ │ │ │ ├── TestBurnInstaller.ModifyRepair.yaml │ │ │ │ ├── TestBurnInstaller.ModifyRepairWithNoModify.yaml │ │ │ │ ├── TestBurnInstaller.UserScopeInstallRepairInAdminContext.yaml │ │ │ │ ├── TestBurnInstaller.yaml │ │ │ │ ├── TestExampleInstaller.yaml │ │ │ │ ├── TestExeInstaller.1.0.1.0.yaml │ │ │ │ ├── TestExeInstaller.1.1.0.0.yaml │ │ │ │ ├── TestExeInstaller.2.0.0.0.yaml │ │ │ │ ├── TestExeInstaller.RebootRequired.yaml │ │ │ │ ├── TestExeInstaller.UninstallerRepair.yaml │ │ │ │ ├── TestExeInstaller.UninstallerRepairWithNoRepair.yaml │ │ │ │ ├── TestExeInstaller.WindowsFeature.yaml │ │ │ │ ├── TestExeInstaller.yaml │ │ │ │ ├── TestExeInstallerForExport.yaml │ │ │ │ ├── TestExeInstaller_CatalogPackageMetadata.yaml │ │ │ │ ├── TestExeInstaller_InapplicableOsVersion.yaml │ │ │ │ ├── TestExeInstaller_InstallMSIX.yaml │ │ │ │ ├── TestExeInstaller_MultipleLocale.yaml │ │ │ │ ├── TestExeInstaller_NoScope.yaml │ │ │ │ ├── TestExeInstaller_PackageDependency.yaml │ │ │ │ ├── TestExeInstaller_PackageDependencyRequiresPathRefresh.yaml │ │ │ │ ├── TestExeInstaller_PathVariableRefresh.yaml │ │ │ │ ├── TestExeInstaller_Sha256Mismatch.yaml │ │ │ │ ├── TestFont.yaml │ │ │ │ ├── TestGoodManifestV1_10-SchemaHeader.yaml │ │ │ │ ├── TestInnoInstaller.InstallerRepair.yaml │ │ │ │ ├── TestInnoInstaller.yaml │ │ │ │ ├── TestInstalledStatus.yaml │ │ │ │ ├── TestInvalidFont.yaml │ │ │ │ ├── TestInvalidManifest.yaml │ │ │ │ ├── TestMappingWithArchitectureX64.yaml │ │ │ │ ├── TestMappingWithArchitectureX86.yaml │ │ │ │ ├── TestMsiInstaller.Repair.yaml │ │ │ │ ├── TestMsiInstaller.yaml │ │ │ │ ├── TestMsiInstaller_UpgradeCode.yaml │ │ │ │ ├── TestMsixInstaller.yaml │ │ │ │ ├── TestMsixInstaller_SignatureHashMismatch.yaml │ │ │ │ ├── TestMsixInstaller_WithSignatureHash.yaml │ │ │ │ ├── TestMultipleInstallers.yaml │ │ │ │ ├── TestNullsoftInstaller.UninstallerRepair.yaml │ │ │ │ ├── TestNullsoftInstaller.yaml │ │ │ │ ├── TestPortableInstaller.2.0.0.0.yaml │ │ │ │ ├── TestPortableInstaller.3.0.0.0_UninstallPrevious.yaml │ │ │ │ ├── TestPortableInstaller.yaml │ │ │ │ ├── TestPortableInstallerUninstallPrevious.yaml │ │ │ │ ├── TestPortableInstaller_WithCommand.yaml │ │ │ │ ├── TestUpgradeAddsDependency.1.0.yaml │ │ │ │ ├── TestUpgradeAddsDependency.2.0.yaml │ │ │ │ ├── TestUpgradeAddsDependencyDependent.1.0.yaml │ │ │ │ ├── TestUpgradeAvailableApi.1.0.0.0.yaml │ │ │ │ ├── TestUpgradeAvailableApi.2.0.0.0.yaml │ │ │ │ ├── TestUpgradeDeny.1.0.0.0.yaml │ │ │ │ ├── TestUpgradeDeny.2.0.0.0.yaml │ │ │ │ ├── TestValidManifest.yaml │ │ │ │ ├── TestWarningManifest.yaml │ │ │ │ ├── TestWarningManifestV1_10-SchemaHeaderInvalid.yaml │ │ │ │ ├── TestWarningManifestV1_10-SchemaHeaderManifestTypeMismatch.yaml │ │ │ │ ├── TestWarningManifestV1_10-SchemaHeaderNotFound.yaml │ │ │ │ ├── TestWarningManifestV1_10-SchemaHeaderURLPatternMismatch.yaml │ │ │ │ ├── TestWarningManifestV1_10-SchemaHeaderVersionMismatch.yaml │ │ │ │ ├── TestZipInstaller_Exe.yaml │ │ │ │ ├── TestZipInstaller_Exe_InvalidRelativeFilePath.yaml │ │ │ │ ├── TestZipInstaller_Msi.yaml │ │ │ │ ├── TestZipInstaller_Msix.yaml │ │ │ │ ├── TestZipInstaller_PackageInstallerInfo.yaml │ │ │ │ ├── TestZipInstaller_Portable.2.0.0.0.yaml │ │ │ │ ├── TestZipInstaller_Portable.yaml │ │ │ │ ├── TestZipInstaller_Portable_BinariesDependOnPath.yaml │ │ │ │ ├── TëstExeInstaller.yaml │ │ │ │ ├── ZeroByteFile_CorrectHash.yaml │ │ │ │ └── ZeroByteFile_IncorrectHash.yaml │ │ │ ├── Package/ │ │ │ │ └── AppxManifest.xml │ │ │ ├── README.md │ │ │ ├── TëstPackage.msix │ │ │ ├── WinGetUtil/ │ │ │ │ ├── InstallerMetadata/ │ │ │ │ │ ├── MergeSubmissionMismatch.json │ │ │ │ │ ├── MergeValid.json │ │ │ │ │ └── Minimal.json │ │ │ │ └── Manifests/ │ │ │ │ ├── Merged/ │ │ │ │ │ ├── WinGetUtilTest.Add.yaml │ │ │ │ │ └── WinGetUtilTest.Update.yaml │ │ │ │ └── Unmerged/ │ │ │ │ └── ValidateManifest/ │ │ │ │ ├── WinGetUtilTest.installer.yaml │ │ │ │ ├── WinGetUtilTest.locale.en-US.yaml │ │ │ │ └── WinGetUtilTest.yaml │ │ │ ├── empty │ │ │ └── localsource.json │ │ ├── UninstallCommand.cs │ │ ├── UpgradeCommand.cs │ │ ├── ValidateCommand.cs │ │ └── WinGetUtil/ │ │ ├── WinGetUtilCompareVersions.cs │ │ ├── WinGetUtilDownload.cs │ │ ├── WinGetUtilInstallerMetadataCollection.cs │ │ ├── WinGetUtilLog.cs │ │ ├── WinGetUtilManifest.cs │ │ ├── WinGetUtilSQLiteIndex.cs │ │ └── WinGetUtilWrapper.cs │ ├── AppInstallerCLIPackage/ │ │ ├── AppInstallerCLIPackage.wapproj │ │ ├── Execute-AppxRecipe.ps1 │ │ ├── Package.appxmanifest │ │ ├── Register-WingetdevAutoComplete.ps1 │ │ └── Shared/ │ │ └── Strings/ │ │ └── en-us/ │ │ ├── Resources.resw │ │ └── winget.resw │ ├── AppInstallerCLITests/ │ │ ├── ARPChanges.cpp │ │ ├── AdminSettings.cpp │ │ ├── AppInstallerCLITests.vcxproj │ │ ├── AppInstallerCLITests.vcxproj.filters │ │ ├── AppShutdown.cpp │ │ ├── Archive.cpp │ │ ├── Argument.cpp │ │ ├── ArpHelper.cpp │ │ ├── Certificates.cpp │ │ ├── CheckpointDatabase.cpp │ │ ├── Command.cpp │ │ ├── Completion.cpp │ │ ├── CompositeSource.cpp │ │ ├── ContextOrchestrator.cpp │ │ ├── Correlation.cpp │ │ ├── CustomHeader.cpp │ │ ├── DateTime.cpp │ │ ├── Dependencies.cpp │ │ ├── DependenciesTestSource.h │ │ ├── DownloadFlow.cpp │ │ ├── Downloader.cpp │ │ ├── Errors.cpp │ │ ├── ExperimentalFeature.cpp │ │ ├── ExportFlow.cpp │ │ ├── FileCache.cpp │ │ ├── FileLogger.cpp │ │ ├── Filesystem.cpp │ │ ├── FolderFileWatcher.cpp │ │ ├── FontHelper.cpp │ │ ├── Fonts.cpp │ │ ├── GroupPolicy.cpp │ │ ├── HashCommand.cpp │ │ ├── HttpClientHelper.cpp │ │ ├── IconExtraction.cpp │ │ ├── ImportFlow.cpp │ │ ├── InstallDependenciesFlow.cpp │ │ ├── InstallFlow.cpp │ │ ├── InstallerMetadataCollectionContext.cpp │ │ ├── JsonHelper.cpp │ │ ├── LanguageUtilities.cpp │ │ ├── MSStoreDownloadFlow.cpp │ │ ├── ManifestComparator.cpp │ │ ├── MatchCriteriaResolver.cpp │ │ ├── MsiExecArguments.cpp │ │ ├── MsixInfo.cpp │ │ ├── MsixManifest.cpp │ │ ├── NameNormalization.cpp │ │ ├── PackageCollection.cpp │ │ ├── PackageDependenciesValidationUtil.cpp │ │ ├── PackageTrackingCatalog.cpp │ │ ├── PackageVersionDataManifest.cpp │ │ ├── PathVariable.cpp │ │ ├── PinFlow.cpp │ │ ├── PinningIndex.cpp │ │ ├── PortableIndex.cpp │ │ ├── PortableInstaller.cpp │ │ ├── PreIndexedPackageSource.cpp │ │ ├── PredefinedInstalledSource.cpp │ │ ├── PromptFlow.cpp │ │ ├── PropertySheet.props │ │ ├── Regex.cpp │ │ ├── Registry.cpp │ │ ├── Resources.cpp │ │ ├── Rest.cpp │ │ ├── RestClient.cpp │ │ ├── RestInterface_1_0.cpp │ │ ├── RestInterface_1_1.cpp │ │ ├── RestInterface_1_10.cpp │ │ ├── RestInterface_1_12.cpp │ │ ├── RestInterface_1_4.cpp │ │ ├── RestInterface_1_5.cpp │ │ ├── RestInterface_1_6.cpp │ │ ├── RestInterface_1_7.cpp │ │ ├── RestInterface_1_9.cpp │ │ ├── ResumeFlow.cpp │ │ ├── Run-TestsInPackage.ps1 │ │ ├── Runtime.cpp │ │ ├── SQLiteDynamicStorage.cpp │ │ ├── SQLiteIndex.cpp │ │ ├── SQLiteIndexSource.cpp │ │ ├── SQLiteWrapper.cpp │ │ ├── SearchRequestSerializer.cpp │ │ ├── Settings.cpp │ │ ├── ShowFlow.cpp │ │ ├── Sixel.cpp │ │ ├── SourceFlow.cpp │ │ ├── Sources.cpp │ │ ├── Strings.cpp │ │ ├── Synchronization.cpp │ │ ├── TestCommon.cpp │ │ ├── TestCommon.h │ │ ├── TestConfiguration.cpp │ │ ├── TestConfiguration.h │ │ ├── TestData/ │ │ │ ├── ContainsEscapeControlCode.yaml │ │ │ ├── ContainsTooManyNestedLayers.yaml │ │ │ ├── DownloadFlowTest_DownloadCommandProhibited.yaml │ │ │ ├── DownloadFlowTest_MSStore.yaml │ │ │ ├── ImportFile-Bad-Invalid.json │ │ │ ├── ImportFile-Bad-Malformed.json │ │ │ ├── ImportFile-Bad-UnknownPackage.json │ │ │ ├── ImportFile-Bad-UnknownPackageVersion.json │ │ │ ├── ImportFile-Bad-UnknownSource.json │ │ │ ├── ImportFile-Good-AlreadyInstalled.json │ │ │ ├── ImportFile-Good-Dependencies.json │ │ │ ├── ImportFile-Good-MachineScope.json │ │ │ ├── ImportFile-Good-WithLicenseAgreement.json │ │ │ ├── ImportFile-Good.json │ │ │ ├── InputARPData.txt │ │ │ ├── InputNames.txt │ │ │ ├── InputPublishers.txt │ │ │ ├── InstallFlowTest_AbortsTerminal.yaml │ │ │ ├── InstallFlowTest_EncodedUrl.yaml │ │ │ ├── InstallFlowTest_Exe.yaml │ │ │ ├── InstallFlowTest_ExpectedReturnCodes.yaml │ │ │ ├── InstallFlowTest_InstallLocationRequired.yaml │ │ │ ├── InstallFlowTest_InstallationNotes.yaml │ │ │ ├── InstallFlowTest_InvalidFileCharacterUrl.yaml │ │ │ ├── InstallFlowTest_LicenseAgreement.yaml │ │ │ ├── InstallFlowTest_MSStore.yaml │ │ │ ├── InstallFlowTest_Msix_DownloadFlow.yaml │ │ │ ├── InstallFlowTest_Msix_StreamingFlow.yaml │ │ │ ├── InstallFlowTest_MultipleDependencies.yaml │ │ │ ├── InstallFlowTest_NoApplicableArchitecture.yaml │ │ │ ├── InstallFlowTest_NonZeroExitCode.yaml │ │ │ ├── InstallFlowTest_Portable.yaml │ │ │ ├── InstallFlowTest_Portable_WithCommand.yaml │ │ │ ├── InstallFlowTest_UnknownVersion.yaml │ │ │ ├── InstallFlowTest_UnsupportedArguments.yaml │ │ │ ├── InstallFlowTest_WindowsFeatures.yaml │ │ │ ├── InstallFlowTest_Zip_Exe.yaml │ │ │ ├── InstallFlowTest_Zip_MissingNestedInstaller.yaml │ │ │ ├── InstallFlowTest_Zip_MultipleNonPortableNestedInstallers.yaml │ │ │ ├── InstallFlowTest_Zip_UnsupportedNestedInstaller.yaml │ │ │ ├── Installer-Bad-NoSupportedPlatforms.msix │ │ │ ├── Installer-Good-WithStub.msixbundle │ │ │ ├── Installer-Good.msix │ │ │ ├── Installer-Good.msixbundle │ │ │ ├── Installer-Signed-Good.msix │ │ │ ├── Installer-Signed-Good.msixbundle │ │ │ ├── InstallerArgTest_Inno_NoSwitches.yaml │ │ │ ├── InstallerArgTest_Inno_WithSwitches.yaml │ │ │ ├── InstallerArgTest_Msi_NoSwitches.yaml │ │ │ ├── InstallerArgTest_Msi_WithSwitches.yaml │ │ │ ├── Installer_Exe_Dependencies.yaml │ │ │ ├── Installer_Exe_DependenciesMultideclaration.yaml │ │ │ ├── Installer_Exe_DependenciesOnRoot.yaml │ │ │ ├── Installer_Msix_WFDependency.yaml │ │ │ ├── Manifest-Bad-ApproximateVersionInArpVersion.yaml │ │ │ ├── Manifest-Bad-ApproximateVersionInPackageVersion.yaml │ │ │ ├── Manifest-Bad-AppsAndFeaturesEntriesOnMSIX.yaml │ │ │ ├── Manifest-Bad-ArchInvalid.yaml │ │ │ ├── Manifest-Bad-ArchMissing.yaml │ │ │ ├── Manifest-Bad-Channel-NotSupported.yaml │ │ │ ├── Manifest-Bad-DifferentCase-UPPER.yaml │ │ │ ├── Manifest-Bad-DifferentCase-camelCase.yaml │ │ │ ├── Manifest-Bad-DifferentCase-lower.yaml │ │ │ ├── Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml │ │ │ ├── Manifest-Bad-DuplicateKey-DifferentCase.yaml │ │ │ ├── Manifest-Bad-DuplicateKey.yaml │ │ │ ├── Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml │ │ │ ├── Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml │ │ │ ├── Manifest-Bad-DuplicateSha256.yaml │ │ │ ├── Manifest-Bad-IdInvalid.yaml │ │ │ ├── Manifest-Bad-IdMissing.yaml │ │ │ ├── Manifest-Bad-InconsistentMsixBundleInstallerFields.yaml │ │ │ ├── Manifest-Bad-InconsistentMsixInstallerFields.yaml │ │ │ ├── Manifest-Bad-InconsistentSha256.yaml │ │ │ ├── Manifest-Bad-InconsistentSignedMsixBundleInstallerFields.yaml │ │ │ ├── Manifest-Bad-InconsistentSignedMsixInstallerFields.yaml │ │ │ ├── Manifest-Bad-InstallerTypeExe-NoSilent.yaml │ │ │ ├── Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml │ │ │ ├── Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml │ │ │ ├── Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml │ │ │ ├── Manifest-Bad-InstallerTypeInvalid.yaml │ │ │ ├── Manifest-Bad-InstallerTypeMissing.yaml │ │ │ ├── Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml │ │ │ ├── Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml │ │ │ ├── Manifest-Bad-InstallerTypePortable-InvalidScope.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-DuplicateCommandAlias.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-DuplicateRelativeFilePath.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-InvalidRelativeFilePath.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-MissingRelativeFilePath.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-MultipleNestedInstallers.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-NoNestedInstallerFile.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-NoNestedInstallerType.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-PortableNotExe.yaml │ │ │ ├── Manifest-Bad-InstallerTypeZip-PortableNotExe_Root.yaml │ │ │ ├── Manifest-Bad-InstallerUniqueness-DefaultScope.yaml │ │ │ ├── Manifest-Bad-InstallerUniqueness-DefaultValues.yaml │ │ │ ├── Manifest-Bad-InstallerUniqueness-SameLang.yaml │ │ │ ├── Manifest-Bad-InstallerUniqueness.yaml │ │ │ ├── Manifest-Bad-InstallersMissing.yaml │ │ │ ├── Manifest-Bad-InvalidLocale.yaml │ │ │ ├── Manifest-Bad-InvalidManifestVersionValue.yaml │ │ │ ├── Manifest-Bad-InvalidUpdateBehavior.yaml │ │ │ ├── Manifest-Bad-LicenseMissing.yaml │ │ │ ├── Manifest-Bad-MissingMsixInstallerFields.yaml │ │ │ ├── Manifest-Bad-MsixInstaller-PackageVersion.yaml │ │ │ ├── Manifest-Bad-NameMissing.yaml │ │ │ ├── Manifest-Bad-NoSupportedPlatforms.yaml │ │ │ ├── Manifest-Bad-PackageFamilyNameOnMSI.yaml │ │ │ ├── Manifest-Bad-ProductCodeOnMSIX.yaml │ │ │ ├── Manifest-Bad-PublisherMissing.yaml │ │ │ ├── Manifest-Bad-Sha256Invalid.yaml │ │ │ ├── Manifest-Bad-Sha256Missing.yaml │ │ │ ├── Manifest-Bad-SwitchInvalid.yaml │ │ │ ├── Manifest-Bad-UnknownProperty.yaml │ │ │ ├── Manifest-Bad-UnsupportedVersion.yaml │ │ │ ├── Manifest-Bad-UrlInvalid.yaml │ │ │ ├── Manifest-Bad-UrlMissing.yaml │ │ │ ├── Manifest-Bad-VersionInvalid.yaml │ │ │ ├── Manifest-Bad-VersionMissing.yaml │ │ │ ├── Manifest-Encoding-ANSI.yaml │ │ │ ├── Manifest-Encoding-UTF16BE-BOM.yaml │ │ │ ├── Manifest-Encoding-UTF16BE.yaml │ │ │ ├── Manifest-Encoding-UTF16LE-BOM.yaml │ │ │ ├── Manifest-Encoding-UTF16LE.yaml │ │ │ ├── Manifest-Encoding-UTF8-BOM.yaml │ │ │ ├── Manifest-Encoding-UTF8.yaml │ │ │ ├── Manifest-Good-AllDependencyTypes.yaml │ │ │ ├── Manifest-Good-DefaultExpectedReturnCodeInInstallerSuccessCodes.yaml │ │ │ ├── Manifest-Good-InstallerTypeExe-Silent.yaml │ │ │ ├── Manifest-Good-InstallerTypeExe-SilentRoot.yaml │ │ │ ├── Manifest-Good-InstallerTypeExeRoot-Silent.yaml │ │ │ ├── Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml │ │ │ ├── Manifest-Good-InstallerTypeZip-PortableExe.yaml │ │ │ ├── Manifest-Good-InstallerUniqueness-DiffScope.yaml │ │ │ ├── Manifest-Good-Installeruniqueness-DefaultLang.yaml │ │ │ ├── Manifest-Good-Installeruniqueness-DiffLangs.yaml │ │ │ ├── Manifest-Good-Minimum-InstallerType.yaml │ │ │ ├── Manifest-Good-Minimum.yaml │ │ │ ├── Manifest-Good-MsixBundleInstaller-WithStub.yaml │ │ │ ├── Manifest-Good-MsixBundleInstaller.yaml │ │ │ ├── Manifest-Good-MsixInstaller.yaml │ │ │ ├── Manifest-Good-MultiLocale.yaml │ │ │ ├── Manifest-Good-MultipleArpVersionDeclared.yaml │ │ │ ├── Manifest-Good-NoArpVersionDeclared.yaml │ │ │ ├── Manifest-Good-SignedMsixBundleInstaller.yaml │ │ │ ├── Manifest-Good-SignedMsixInstaller.yaml │ │ │ ├── Manifest-Good-SingleArpVersionDeclared.yaml │ │ │ ├── Manifest-Good-Spaces.yaml │ │ │ ├── Manifest-Good-Switches.yaml │ │ │ ├── Manifest-Good-SystemReferenceComplex.yaml │ │ │ ├── Manifest-Good.yaml │ │ │ ├── Manifest-MSIX-in-AppsAndFeatures.yaml │ │ │ ├── Manifest-MSIX-in-Archive.yaml │ │ │ ├── ManifestV1-Singleton.yaml │ │ │ ├── ManifestV1_1-Singleton.yaml │ │ │ ├── ManifestV1_10-Bad-SchemaHeaderInvalid.yaml │ │ │ ├── ManifestV1_10-Bad-SchemaHeaderManifestTypeMismatch.yaml │ │ │ ├── ManifestV1_10-Bad-SchemaHeaderManifestVersionMismatch.yaml │ │ │ ├── ManifestV1_10-Bad-SchemaHeaderNotFound.yaml │ │ │ ├── ManifestV1_10-Bad-SchemaHeaderURLPatternMismatch.yaml │ │ │ ├── ManifestV1_10-InstallerAuthentication.yaml │ │ │ ├── ManifestV1_10-Singleton.yaml │ │ │ ├── ManifestV1_12-Singleton.yaml │ │ │ ├── ManifestV1_2-Singleton.yaml │ │ │ ├── ManifestV1_28-PowerShellDSC.yaml │ │ │ ├── ManifestV1_28-Singleton.yaml │ │ │ ├── ManifestV1_4-Singleton.yaml │ │ │ ├── ManifestV1_5-Singleton.yaml │ │ │ ├── ManifestV1_6-Singleton.yaml │ │ │ ├── ManifestV1_7-Singleton.yaml │ │ │ ├── ManifestV1_9-Singleton.yaml │ │ │ ├── MultiFileManifestV1/ │ │ │ │ ├── ManifestV1-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_1/ │ │ │ │ ├── ManifestV1_1-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_1-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_1-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_1-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_10/ │ │ │ │ ├── ManifestV1_10-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_10-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_10-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_10-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_12/ │ │ │ │ ├── ManifestV1_12-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_12-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_12-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_12-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_2/ │ │ │ │ ├── ManifestV1_2-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_2-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_2-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_2-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_28/ │ │ │ │ ├── ManifestV1_28-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_28-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_28-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_28-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_4/ │ │ │ │ ├── ManifestV1_4-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_4-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_4-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_4-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_5/ │ │ │ │ ├── ManifestV1_5-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_5-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_5-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_5-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_6/ │ │ │ │ ├── ManifestV1_6-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_6-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_6-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_6-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_7/ │ │ │ │ ├── ManifestV1_7-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_7-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_7-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_7-MultiFile-Version.yaml │ │ │ ├── MultiFileManifestV1_9/ │ │ │ │ ├── ManifestV1_9-MultiFile-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_9-MultiFile-Installer.yaml │ │ │ │ ├── ManifestV1_9-MultiFile-Locale.yaml │ │ │ │ └── ManifestV1_9-MultiFile-Version.yaml │ │ │ ├── Node-Mapping.yaml │ │ │ ├── Node-Merge.yaml │ │ │ ├── Node-Merge2.yaml │ │ │ ├── Node-Types.yaml │ │ │ ├── NormalizationInitialIds.txt │ │ │ ├── Shadow/ │ │ │ │ └── V1_5/ │ │ │ │ ├── ManifestV1_5-Shadow-DefaultLocale.yaml │ │ │ │ ├── ManifestV1_5-Shadow-Installer.yaml │ │ │ │ ├── ManifestV1_5-Shadow-Locale.yaml │ │ │ │ ├── ManifestV1_5-Shadow-Locale2.yaml │ │ │ │ ├── ManifestV1_5-Shadow-Shadow.yaml │ │ │ │ └── ManifestV1_5-Shadow-Shadow2.yaml │ │ │ ├── TestSignedApp.msix │ │ │ ├── UpdateFlowTest_Exe.yaml │ │ │ ├── UpdateFlowTest_ExeDependencies.yaml │ │ │ ├── UpdateFlowTest_Exe_2.yaml │ │ │ ├── UpdateFlowTest_Exe_2_LicenseAgreement.yaml │ │ │ ├── UpdateFlowTest_Exe_ARPInstallerType.yaml │ │ │ ├── UpdateFlowTest_Exe_UnsupportedArgs.yaml │ │ │ ├── UpdateFlowTest_ExpectedReturnCodes.yaml │ │ │ ├── UpdateFlowTest_Msix.yaml │ │ │ ├── UpdateFlowTest_Msix_LicenseAgreement.yaml │ │ │ ├── UpdateFlowTest_Portable.yaml │ │ │ ├── UpdateFlowTest_Zip_Exe.yaml │ │ │ ├── index.1.0.0.0.msix │ │ │ ├── index.1.0.0.0.signed.msix │ │ │ ├── index.2.0.0.0.msix │ │ │ └── index.2.0.0.0.signed.msix │ │ ├── TestHooks.h │ │ ├── TestRestRequestHandler.cpp │ │ ├── TestRestRequestHandler.h │ │ ├── TestSettings.cpp │ │ ├── TestSettings.h │ │ ├── TestSource.cpp │ │ ├── TestSource.h │ │ ├── UninstallFlow.cpp │ │ ├── UpdateFlow.cpp │ │ ├── UserSettings.cpp │ │ ├── Versions.cpp │ │ ├── WindowsFeature.cpp │ │ ├── WorkFlow.cpp │ │ ├── WorkflowCommon.cpp │ │ ├── WorkflowCommon.h │ │ ├── WorkflowGroupPolicy.cpp │ │ ├── Yaml.cpp │ │ ├── YamlManifest.cpp │ │ ├── main.cpp │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── AppInstallerCommonCore/ │ │ ├── AdminSettings.cpp │ │ ├── AppInstallerCommonCore.vcxproj │ │ ├── AppInstallerCommonCore.vcxproj.filters │ │ ├── AppInstallerTelemetry.cpp │ │ ├── Architecture.cpp │ │ ├── Archive.cpp │ │ ├── Authentication/ │ │ │ ├── Authentication.cpp │ │ │ ├── WebAccountManagerAuthenticator.cpp │ │ │ └── WebAccountManagerAuthenticator.h │ │ ├── DODownloader.cpp │ │ ├── DODownloader.h │ │ ├── Debugging.cpp │ │ ├── DependenciesGraph.cpp │ │ ├── Deployment.cpp │ │ ├── Downloader.cpp │ │ ├── ExperimentalFeature.cpp │ │ ├── ExtensionCatalog.cpp │ │ ├── FileCache.cpp │ │ ├── FileLogger.cpp │ │ ├── FolderFileWatcher.cpp │ │ ├── Fonts.cpp │ │ ├── HttpClientHelper.cpp │ │ ├── HttpStream/ │ │ │ ├── HttpClientWrapper.cpp │ │ │ ├── HttpClientWrapper.h │ │ │ ├── HttpLocalCache.cpp │ │ │ ├── HttpLocalCache.h │ │ │ ├── HttpRandomAccessStream.cpp │ │ │ └── HttpRandomAccessStream.h │ │ ├── Locale.cpp │ │ ├── MSStore.cpp │ │ ├── MSStoreDownload.cpp │ │ ├── Manifest/ │ │ │ ├── Manifest.cpp │ │ │ ├── ManifestCommon.cpp │ │ │ ├── ManifestComparator.cpp │ │ │ ├── ManifestSchemaValidation.cpp │ │ │ ├── ManifestValidation.cpp │ │ │ ├── ManifestYamlPopulator.cpp │ │ │ ├── MsixManifestValidation.cpp │ │ │ ├── YamlParser.cpp │ │ │ └── YamlWriter.cpp │ │ ├── MsiExecArguments.cpp │ │ ├── MsixInfo.cpp │ │ ├── MsixManifest.cpp │ │ ├── NameNormalization.cpp │ │ ├── NetworkSettings.cpp │ │ ├── OutputDebugStringLogger.cpp │ │ ├── PackageDependenciesValidationUtil.cpp │ │ ├── PackageVersionDataManifest.cpp │ │ ├── PathVariable.cpp │ │ ├── Pin.cpp │ │ ├── PortableARPEntry.cpp │ │ ├── Progress.cpp │ │ ├── PropertySheet.props │ │ ├── Public/ │ │ │ ├── AppInstallerArchitecture.h │ │ │ ├── AppInstallerDeployment.h │ │ │ ├── AppInstallerDownloader.h │ │ │ ├── AppInstallerFileLogger.h │ │ │ ├── AppInstallerMsixInfo.h │ │ │ ├── AppInstallerProgress.h │ │ │ ├── AppInstallerRuntime.h │ │ │ ├── AppInstallerSynchronization.h │ │ │ ├── AppInstallerTelemetry.h │ │ │ └── winget/ │ │ │ ├── AdminSettings.h │ │ │ ├── Archive.h │ │ │ ├── Authentication.h │ │ │ ├── Debugging.h │ │ │ ├── DependenciesGraph.h │ │ │ ├── ExperimentalFeature.h │ │ │ ├── ExtensionCatalog.h │ │ │ ├── FileCache.h │ │ │ ├── FolderFileWatcher.h │ │ │ ├── Fonts.h │ │ │ ├── HttpClientHelper.h │ │ │ ├── Locale.h │ │ │ ├── MSStore.h │ │ │ ├── MSStoreDownload.h │ │ │ ├── Manifest.h │ │ │ ├── ManifestCommon.h │ │ │ ├── ManifestComparator.h │ │ │ ├── ManifestInstaller.h │ │ │ ├── ManifestLocalization.h │ │ │ ├── ManifestSchemaValidation.h │ │ │ ├── ManifestValidation.h │ │ │ ├── ManifestYamlParser.h │ │ │ ├── ManifestYamlPopulator.h │ │ │ ├── ManifestYamlWriter.h │ │ │ ├── MsiExecArguments.h │ │ │ ├── MsixManifest.h │ │ │ ├── MsixManifestValidation.h │ │ │ ├── NameNormalization.h │ │ │ ├── NetworkSettings.h │ │ │ ├── OutputDebugStringLogger.h │ │ │ ├── PackageDependenciesValidationUtil.h │ │ │ ├── PackageVersionDataManifest.h │ │ │ ├── PathVariable.h │ │ │ ├── Pin.h │ │ │ ├── PortableARPEntry.h │ │ │ ├── PortableFileEntry.h │ │ │ ├── Reboot.h │ │ │ ├── Regex.h │ │ │ ├── Rest.h │ │ │ ├── SelfManagement.h │ │ │ ├── Settings.h │ │ │ ├── StdErrLogger.h │ │ │ ├── ThreadGlobals.h │ │ │ ├── TraceLogger.h │ │ │ └── UserSettings.h │ │ ├── Reboot.cpp │ │ ├── Regex.cpp │ │ ├── Rest.cpp │ │ ├── Runtime.cpp │ │ ├── SelfManagement.cpp │ │ ├── Settings.cpp │ │ ├── StdErrLogger.cpp │ │ ├── Synchronization.cpp │ │ ├── Telemetry/ │ │ │ ├── TraceLogging.cpp │ │ │ └── TraceLogging.h │ │ ├── ThreadGlobals.cpp │ │ ├── TraceLogger.cpp │ │ ├── UserSettings.cpp │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── AppInstallerRepositoryCore/ │ │ ├── ARPCorrelation.cpp │ │ ├── ARPCorrelationAlgorithms.cpp │ │ ├── AppInstallerRepositoryCore.vcxproj │ │ ├── AppInstallerRepositoryCore.vcxproj.filters │ │ ├── ArpVersionValidation.cpp │ │ ├── ArpVersionValidation.h │ │ ├── CompositeSource.cpp │ │ ├── CompositeSource.h │ │ ├── ISource.h │ │ ├── IconDefs.h │ │ ├── IconExtraction.cpp │ │ ├── InstalledFilesCorrelation.cpp │ │ ├── InstallerMetadataCollectionContext.cpp │ │ ├── ManifestJSONParser.cpp │ │ ├── MatchCriteriaResolver.cpp │ │ ├── MatchCriteriaResolver.h │ │ ├── Microsoft/ │ │ │ ├── ARPHelper.cpp │ │ │ ├── ARPHelper.h │ │ │ ├── CheckpointDatabase.cpp │ │ │ ├── ConfigurableTestSourceFactory.cpp │ │ │ ├── ConfigurableTestSourceFactory.h │ │ │ ├── FontHelper.cpp │ │ │ ├── FontHelper.h │ │ │ ├── PinningIndex.cpp │ │ │ ├── PinningIndex.h │ │ │ ├── PortableIndex.cpp │ │ │ ├── PreIndexedPackageSourceFactory.cpp │ │ │ ├── PreIndexedPackageSourceFactory.h │ │ │ ├── PredefinedInstalledSourceFactory.cpp │ │ │ ├── PredefinedInstalledSourceFactory.h │ │ │ ├── PredefinedWriteableSourceFactory.cpp │ │ │ ├── PredefinedWriteableSourceFactory.h │ │ │ ├── README.md │ │ │ ├── SQLiteIndex.cpp │ │ │ ├── SQLiteIndex.h │ │ │ ├── SQLiteIndexSource.cpp │ │ │ ├── SQLiteIndexSource.h │ │ │ ├── SQLiteIndexSourceV1.cpp │ │ │ ├── SQLiteIndexSourceV1.h │ │ │ ├── SQLiteIndexSourceV2.cpp │ │ │ ├── SQLiteIndexSourceV2.h │ │ │ └── Schema/ │ │ │ ├── 1_0/ │ │ │ │ ├── ChannelTable.h │ │ │ │ ├── CommandsTable.h │ │ │ │ ├── IdTable.h │ │ │ │ ├── Interface.h │ │ │ │ ├── Interface_1_0.cpp │ │ │ │ ├── ManifestTable.cpp │ │ │ │ ├── ManifestTable.h │ │ │ │ ├── MonikerTable.h │ │ │ │ ├── NameTable.h │ │ │ │ ├── OneToManyTable.cpp │ │ │ │ ├── OneToManyTable.h │ │ │ │ ├── OneToOneTable.cpp │ │ │ │ ├── OneToOneTable.h │ │ │ │ ├── PathPartTable.cpp │ │ │ │ ├── PathPartTable.h │ │ │ │ ├── SearchResultsTable.h │ │ │ │ ├── SearchResultsTable_1_0.cpp │ │ │ │ ├── TagsTable.h │ │ │ │ ├── VersionTable.h │ │ │ │ └── VirtualTableBase.h │ │ │ ├── 1_1/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Interface_1_1.cpp │ │ │ │ ├── ManifestMetadataTable.cpp │ │ │ │ ├── ManifestMetadataTable.h │ │ │ │ ├── PackageFamilyNameTable.h │ │ │ │ ├── ProductCodeTable.h │ │ │ │ ├── SearchResultsTable.h │ │ │ │ └── SearchResultsTable_1_1.cpp │ │ │ ├── 1_2/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Interface_1_2.cpp │ │ │ │ ├── NormalizedPackageNameTable.h │ │ │ │ ├── NormalizedPackagePublisherTable.h │ │ │ │ ├── SearchResultsTable.h │ │ │ │ └── SearchResultsTable_1_2.cpp │ │ │ ├── 1_3/ │ │ │ │ ├── HashVirtualTable.h │ │ │ │ ├── Interface.h │ │ │ │ └── Interface_1_3.cpp │ │ │ ├── 1_4/ │ │ │ │ ├── DependenciesTable.cpp │ │ │ │ ├── DependenciesTable.h │ │ │ │ ├── Interface.h │ │ │ │ └── Interface_1_4.cpp │ │ │ ├── 1_5/ │ │ │ │ ├── ArpVersionVirtualTable.h │ │ │ │ ├── Interface.h │ │ │ │ └── Interface_1_5.cpp │ │ │ ├── 1_6/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Interface_1_6.cpp │ │ │ │ ├── SearchResultsTable.h │ │ │ │ ├── SearchResultsTable_1_6.cpp │ │ │ │ └── UpgradeCodeTable.h │ │ │ ├── 1_7/ │ │ │ │ ├── Interface.h │ │ │ │ └── Interface_1_7.cpp │ │ │ ├── 2_0/ │ │ │ │ ├── CommandsTable.h │ │ │ │ ├── Interface.h │ │ │ │ ├── Interface_2_0.cpp │ │ │ │ ├── NormalizedPackageNameTable.h │ │ │ │ ├── NormalizedPackagePublisherTable.h │ │ │ │ ├── OneToManyTableWithMap.cpp │ │ │ │ ├── OneToManyTableWithMap.h │ │ │ │ ├── PackageFamilyNameTable.h │ │ │ │ ├── PackageUpdateTrackingTable.cpp │ │ │ │ ├── PackageUpdateTrackingTable.h │ │ │ │ ├── PackagesTable.cpp │ │ │ │ ├── PackagesTable.h │ │ │ │ ├── ProductCodeTable.h │ │ │ │ ├── SearchResultsTable.h │ │ │ │ ├── SearchResultsTable_2_0.cpp │ │ │ │ ├── SystemReferenceStringTable.cpp │ │ │ │ ├── SystemReferenceStringTable.h │ │ │ │ ├── TagsTable.h │ │ │ │ └── UpgradeCodeTable.h │ │ │ ├── Checkpoint_1_0/ │ │ │ │ ├── CheckpointDataTable.cpp │ │ │ │ ├── CheckpointDataTable.h │ │ │ │ ├── CheckpointDatabaseInterface.h │ │ │ │ ├── CheckpointDatabaseInterface_1_0.cpp │ │ │ │ ├── CheckpointTable.cpp │ │ │ │ └── CheckpointTable.h │ │ │ ├── ICheckpointDatabase.h │ │ │ ├── IPinningIndex.h │ │ │ ├── IPortableIndex.h │ │ │ ├── ISQLiteIndex.cpp │ │ │ ├── ISQLiteIndex.h │ │ │ ├── Pinning_1_0/ │ │ │ │ ├── PinTable.cpp │ │ │ │ ├── PinTable.h │ │ │ │ ├── PinningIndexInterface.h │ │ │ │ └── PinningIndexInterface_1_0.cpp │ │ │ ├── Portable_1_0/ │ │ │ │ ├── PortableIndexInterface.h │ │ │ │ ├── PortableIndexInterface_1_0.cpp │ │ │ │ ├── PortableTable.cpp │ │ │ │ └── PortableTable.h │ │ │ └── SQLiteIndexContextData.h │ │ ├── PackageDependenciesValidation.cpp │ │ ├── PackageDependenciesValidation.h │ │ ├── PackageInstalledStatus.cpp │ │ ├── PackageTrackingCatalog.cpp │ │ ├── PackageTrackingCatalogSourceFactory.h │ │ ├── PackageVersionSelection.cpp │ │ ├── PinningData.cpp │ │ ├── PropertySheet.props │ │ ├── Public/ │ │ │ └── winget/ │ │ │ ├── ARPCorrelation.h │ │ │ ├── ARPCorrelationAlgorithms.h │ │ │ ├── Checkpoint.h │ │ │ ├── CheckpointDatabase.h │ │ │ ├── IconExtraction.h │ │ │ ├── InstalledFilesCorrelation.h │ │ │ ├── InstalledStatus.h │ │ │ ├── InstallerMetadataCollectionContext.h │ │ │ ├── ManifestJSONParser.h │ │ │ ├── PackageTrackingCatalog.h │ │ │ ├── PackageVersionSelection.h │ │ │ ├── PinningData.h │ │ │ ├── PortableIndex.h │ │ │ ├── RepositorySearch.h │ │ │ └── RepositorySource.h │ │ ├── RepositorySearch.cpp │ │ ├── RepositorySource.cpp │ │ ├── Rest/ │ │ │ ├── RestClient.cpp │ │ │ ├── RestClient.h │ │ │ ├── RestInformationCache.cpp │ │ │ ├── RestInformationCache.h │ │ │ ├── RestSource.cpp │ │ │ ├── RestSource.h │ │ │ ├── RestSourceFactory.cpp │ │ │ ├── RestSourceFactory.h │ │ │ └── Schema/ │ │ │ ├── 1_0/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ ├── ManifestDeserializer_1_0.cpp │ │ │ │ │ ├── SearchRequestSerializer.h │ │ │ │ │ ├── SearchRequestSerializer_1_0.cpp │ │ │ │ │ ├── SearchResponseDeserializer.h │ │ │ │ │ └── SearchResponseDeserializer_1_0.cpp │ │ │ │ └── RestInterface_1_0.cpp │ │ │ ├── 1_1/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ ├── ManifestDeserializer_1_1.cpp │ │ │ │ │ ├── SearchRequestSerializer.h │ │ │ │ │ └── SearchRequestSerializer_1_1.cpp │ │ │ │ └── RestInterface_1_1.cpp │ │ │ ├── 1_10/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ └── ManifestDeserializer_1_10.cpp │ │ │ │ └── RestInterface_1_10.cpp │ │ │ ├── 1_12/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ └── ManifestDeserializer_1_12.cpp │ │ │ │ └── RestInterface_1_12.cpp │ │ │ ├── 1_4/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ ├── ManifestDeserializer_1_4.cpp │ │ │ │ │ ├── SearchResponseDeserializer.h │ │ │ │ │ └── SearchResponseDeserializer_1_4.cpp │ │ │ │ └── RestInterface_1_4.cpp │ │ │ ├── 1_5/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ └── ManifestDeserializer_1_5.cpp │ │ │ │ └── RestInterface_1_5.cpp │ │ │ ├── 1_6/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ └── ManifestDeserializer_1_6.cpp │ │ │ │ └── RestInterface_1_6.cpp │ │ │ ├── 1_7/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ └── ManifestDeserializer_1_7.cpp │ │ │ │ └── RestInterface_1_7.cpp │ │ │ ├── 1_9/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Json/ │ │ │ │ │ ├── ManifestDeserializer.h │ │ │ │ │ └── ManifestDeserializer_1_9.cpp │ │ │ │ └── RestInterface_1_9.cpp │ │ │ ├── AuthenticationInfoParser.cpp │ │ │ ├── AuthenticationInfoParser.h │ │ │ ├── CommonRestConstants.h │ │ │ ├── IRestClient.h │ │ │ ├── InformationResponseDeserializer.cpp │ │ │ ├── InformationResponseDeserializer.h │ │ │ ├── SearchRequestComposer.cpp │ │ │ ├── SearchRequestComposer.h │ │ │ ├── SearchResponseParser.cpp │ │ │ └── SearchResponseParser.h │ │ ├── SourceFactory.h │ │ ├── SourceList.cpp │ │ ├── SourceList.h │ │ ├── SourcePolicy.cpp │ │ ├── SourcePolicy.h │ │ ├── SourceUpdateChecks.cpp │ │ ├── SourceUpdateChecks.h │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── AppInstallerSharedLib/ │ │ ├── AppInstallerLogging.cpp │ │ ├── AppInstallerSharedLib.vcxproj │ │ ├── AppInstallerSharedLib.vcxproj.filters │ │ ├── AppInstallerStrings.cpp │ │ ├── COMStaticStorage.cpp │ │ ├── Certificates.cpp │ │ ├── Compression.cpp │ │ ├── DateTime.cpp │ │ ├── Errors.cpp │ │ ├── Filesystem.cpp │ │ ├── GroupPolicy.cpp │ │ ├── ICU/ │ │ │ ├── SQLiteICU.c │ │ │ └── SQLiteICU.h │ │ ├── JsonSchemaValidation.cpp │ │ ├── JsonUtil.cpp │ │ ├── ManagedFile.cpp │ │ ├── PropertySheet.props │ │ ├── Public/ │ │ │ ├── AppInstallerDateTime.h │ │ │ ├── AppInstallerErrors.h │ │ │ ├── AppInstallerLanguageUtilities.h │ │ │ ├── AppInstallerLogging.h │ │ │ ├── AppInstallerSHA256.h │ │ │ ├── AppInstallerStrings.h │ │ │ ├── AppInstallerVersions.h │ │ │ ├── Telemetry/ │ │ │ │ ├── MicrosoftTelemetry.h │ │ │ │ └── WinEventLogLevels.h │ │ │ └── winget/ │ │ │ ├── AsyncTokens.h │ │ │ ├── COMStaticStorage.h │ │ │ ├── Certificates.h │ │ │ ├── Compression.h │ │ │ ├── ConfigurationSetProcessorHandlers.h │ │ │ ├── DetectMismatch.h │ │ │ ├── Filesystem.h │ │ │ ├── GroupPolicy.h │ │ │ ├── IConfigurationStaticsInternals.h │ │ │ ├── ILifetimeWatcher.h │ │ │ ├── JsonSchemaValidation.h │ │ │ ├── JsonUtil.h │ │ │ ├── LocIndependent.h │ │ │ ├── ManagedFile.h │ │ │ ├── ModuleCountBase.h │ │ │ ├── PathTree.h │ │ │ ├── Registry.h │ │ │ ├── Resources.h │ │ │ ├── Runtime.h │ │ │ ├── SQLiteDynamicStorage.h │ │ │ ├── SQLiteMetadataTable.h │ │ │ ├── SQLiteStatementBuilder.h │ │ │ ├── SQLiteStorageBase.h │ │ │ ├── SQLiteTempTable.h │ │ │ ├── SQLiteVersion.h │ │ │ ├── SQLiteWrapper.h │ │ │ ├── Security.h │ │ │ ├── SharedThreadGlobals.h │ │ │ └── Yaml.h │ │ ├── Registry.cpp │ │ ├── Resources.cpp │ │ ├── Runtime.cpp │ │ ├── SHA256.cpp │ │ ├── SQLiteDynamicStorage.cpp │ │ ├── SQLiteMetadataTable.cpp │ │ ├── SQLiteStatementBuilder.cpp │ │ ├── SQLiteStorageBase.cpp │ │ ├── SQLiteTempTable.cpp │ │ ├── SQLiteVersion.cpp │ │ ├── SQLiteWrapper.cpp │ │ ├── Security.cpp │ │ ├── SharedThreadGlobals.cpp │ │ ├── Versions.cpp │ │ ├── Yaml.cpp │ │ ├── YamlWrapper.cpp │ │ ├── YamlWrapper.h │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── AppInstallerTestExeInstaller/ │ │ ├── AppInstallerTestExeInstaller.vcxproj │ │ ├── AppInstallerTestExeInstaller.vcxproj.filters │ │ └── main.cpp │ ├── AppInstallerTestMsiInstaller/ │ │ └── AppInstallerTestMsiInstaller.vdproj │ ├── AppInstallerTestMsixInstaller/ │ │ ├── AppInstallerTestMsixInstaller.wapproj │ │ └── Package.appxmanifest │ ├── COMServer/ │ │ └── COMServer.vcxitems │ ├── CertificateResources/ │ │ ├── CertificateResources.h │ │ ├── CertificateResources.rc │ │ ├── CertificateResources.vcxitems │ │ ├── CertificateResources.vcxitems.filters │ │ ├── Microsoft_TLS_ECC_Root_G2.crt │ │ ├── Microsoft_TLS_RSA_Root_G2.crt │ │ ├── StoreIntermediate1.cer │ │ ├── StoreIntermediate2.cer │ │ ├── StoreLeaf1.cer │ │ ├── StoreLeaf2.cer │ │ ├── StoreRoot1.cer │ │ ├── StoreRoot2.cer │ │ └── resource.h │ ├── CodeAnalysis.ruleset │ ├── ComInprocTestbed/ │ │ ├── ComInprocTestbed.manifest │ │ ├── ComInprocTestbed.vcxproj │ │ ├── ComInprocTestbed.vcxproj.filters │ │ ├── PackageManager.cpp │ │ ├── PackageManager.h │ │ ├── Tests.cpp │ │ ├── Tests.h │ │ ├── main.cpp │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── ConfigurationRemotingServer/ │ │ ├── ConfigurationRemotingServer.csproj │ │ ├── EnvironmentChangeListener.cs │ │ └── Program.cs │ ├── Directory.Build.props │ ├── Directory.Packages.props │ ├── Directory.Solution.props │ ├── Get-VcxprojNugetPackageVersions.ps1 │ ├── IndexCreationTool/ │ │ ├── IndexCreationTool.csproj │ │ └── Program.cs │ ├── LocalhostWebServer/ │ │ ├── LocalhostWebServer.csproj │ │ ├── Program.cs │ │ ├── Run-LocalhostWebServer.ps1 │ │ └── Startup.cs │ ├── ManifestSchema/ │ │ ├── ManifestSchema.h │ │ ├── ManifestSchema.rc │ │ ├── ManifestSchema.vcxitems │ │ ├── ManifestSchema.vcxitems.filters │ │ └── resource.h │ ├── Microsoft.Management.Configuration/ │ │ ├── ApplyConfigurationSetResult.cpp │ │ ├── ApplyConfigurationSetResult.h │ │ ├── ApplyConfigurationUnitResult.cpp │ │ ├── ApplyConfigurationUnitResult.h │ │ ├── ApplyGroupSettingsResult.cpp │ │ ├── ApplyGroupSettingsResult.h │ │ ├── ArgumentValidation.cpp │ │ ├── ArgumentValidation.h │ │ ├── ConfigThreadGlobals.cpp │ │ ├── ConfigThreadGlobals.h │ │ ├── ConfigurationChangeData.cpp │ │ ├── ConfigurationChangeData.h │ │ ├── ConfigurationConflict.cpp │ │ ├── ConfigurationConflict.h │ │ ├── ConfigurationConflictSetting.cpp │ │ ├── ConfigurationConflictSetting.h │ │ ├── ConfigurationEnvironment.cpp │ │ ├── ConfigurationEnvironment.h │ │ ├── ConfigurationParameter.cpp │ │ ├── ConfigurationParameter.h │ │ ├── ConfigurationProcessor.cpp │ │ ├── ConfigurationProcessor.h │ │ ├── ConfigurationSequencer.cpp │ │ ├── ConfigurationSequencer.h │ │ ├── ConfigurationSet.cpp │ │ ├── ConfigurationSet.h │ │ ├── ConfigurationSetApplyProcessor.cpp │ │ ├── ConfigurationSetApplyProcessor.h │ │ ├── ConfigurationSetChangeData.cpp │ │ ├── ConfigurationSetChangeData.h │ │ ├── ConfigurationSetParser.cpp │ │ ├── ConfigurationSetParser.h │ │ ├── ConfigurationSetParserError.h │ │ ├── ConfigurationSetParser_0_1.cpp │ │ ├── ConfigurationSetParser_0_1.h │ │ ├── ConfigurationSetParser_0_2.cpp │ │ ├── ConfigurationSetParser_0_2.h │ │ ├── ConfigurationSetParser_0_3.cpp │ │ ├── ConfigurationSetParser_0_3.h │ │ ├── ConfigurationSetSerializer.cpp │ │ ├── ConfigurationSetSerializer.h │ │ ├── ConfigurationSetSerializer_0_2.cpp │ │ ├── ConfigurationSetSerializer_0_2.h │ │ ├── ConfigurationSetSerializer_0_3.cpp │ │ ├── ConfigurationSetSerializer_0_3.h │ │ ├── ConfigurationSetUtilities.cpp │ │ ├── ConfigurationSetUtilities.h │ │ ├── ConfigurationStaticFunctions.cpp │ │ ├── ConfigurationStaticFunctions.h │ │ ├── ConfigurationStatus.cpp │ │ ├── ConfigurationStatus.h │ │ ├── ConfigurationUnit.cpp │ │ ├── ConfigurationUnit.h │ │ ├── ConfigurationUnitResultInformation.cpp │ │ ├── ConfigurationUnitResultInformation.h │ │ ├── Database/ │ │ │ ├── ConfigurationDatabase.cpp │ │ │ ├── ConfigurationDatabase.h │ │ │ └── Schema/ │ │ │ ├── 0_1/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Interface_0_1.cpp │ │ │ │ ├── SetInfoTable.cpp │ │ │ │ ├── SetInfoTable.h │ │ │ │ ├── UnitInfoTable.cpp │ │ │ │ └── UnitInfoTable.h │ │ │ ├── 0_2/ │ │ │ │ ├── Interface.h │ │ │ │ ├── Interface_0_2.cpp │ │ │ │ ├── QueueTable.cpp │ │ │ │ └── QueueTable.h │ │ │ ├── 0_3/ │ │ │ │ ├── ChangeListenerTable.cpp │ │ │ │ ├── ChangeListenerTable.h │ │ │ │ ├── Interface.h │ │ │ │ ├── Interface_0_3.cpp │ │ │ │ ├── StatusItemTable.cpp │ │ │ │ └── StatusItemTable.h │ │ │ ├── IConfigurationDatabase.cpp │ │ │ └── IConfigurationDatabase.h │ │ ├── DefaultSetGroupProcessor.cpp │ │ ├── DefaultSetGroupProcessor.h │ │ ├── DiagnosticInformationInstance.cpp │ │ ├── DiagnosticInformationInstance.h │ │ ├── ExceptionResultHelpers.h │ │ ├── Filesystem.cpp │ │ ├── Filesystem.h │ │ ├── FindUnitProcessorsOptions.cpp │ │ ├── FindUnitProcessorsOptions.h │ │ ├── GetAllConfigurationUnitSettingsResult.cpp │ │ ├── GetAllConfigurationUnitSettingsResult.h │ │ ├── GetAllConfigurationUnitsResult.cpp │ │ ├── GetAllConfigurationUnitsResult.h │ │ ├── GetConfigurationSetDetailsResult.cpp │ │ ├── GetConfigurationSetDetailsResult.h │ │ ├── GetConfigurationUnitDetailsResult.cpp │ │ ├── GetConfigurationUnitDetailsResult.h │ │ ├── GetConfigurationUnitSettingsResult.cpp │ │ ├── GetConfigurationUnitSettingsResult.h │ │ ├── Microsoft.Management.Configuration.idl │ │ ├── Microsoft.Management.Configuration.vcxproj │ │ ├── Microsoft.Management.Configuration.vcxproj.filters │ │ ├── Microsoft_Management_Configuration.def │ │ ├── OpenConfigurationSetResult.cpp │ │ ├── OpenConfigurationSetResult.h │ │ ├── ParsingMacros.h │ │ ├── PropertySheet.props │ │ ├── ShutdownSynchronization.cpp │ │ ├── ShutdownSynchronization.h │ │ ├── Telemetry/ │ │ │ ├── Telemetry.cpp │ │ │ ├── Telemetry.h │ │ │ ├── TraceLogging.cpp │ │ │ └── TraceLogging.h │ │ ├── TestConfigurationSetResult.cpp │ │ ├── TestConfigurationSetResult.h │ │ ├── TestConfigurationUnitResult.cpp │ │ ├── TestConfigurationUnitResult.h │ │ ├── TestGroupSettingsResult.cpp │ │ ├── TestGroupSettingsResult.h │ │ ├── TestSettingsResult.cpp │ │ ├── TestSettingsResult.h │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── Microsoft.Management.Configuration.OutOfProc/ │ │ ├── Collect-ConfigurationOOPTests.ps1 │ │ ├── Factory.cpp │ │ ├── Factory.h │ │ ├── Microsoft.Management.Configuration.OutOfProc.vcxproj │ │ ├── Microsoft.Management.Configuration.OutOfProc.vcxproj.filters │ │ ├── Prepare-ConfigurationOOPTests.ps1 │ │ ├── PropertySheet.props │ │ ├── Source.def │ │ ├── dllmain.cpp │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── Microsoft.Management.Configuration.Processor/ │ │ ├── Constants/ │ │ │ └── DirectiveConstants.cs │ │ ├── DSCv3/ │ │ │ ├── Helpers/ │ │ │ │ ├── FindDscPackageStateMachine.cs │ │ │ │ ├── IDiagnosticsSink.cs │ │ │ │ ├── PackageInformation.cs │ │ │ │ ├── ProcessExecution.cs │ │ │ │ ├── ProcessExecutionEnvironmentVariable.cs │ │ │ │ ├── ProcessExecutionEnvironmentVariableValueType.cs │ │ │ │ ├── ProcessorRunSettings.cs │ │ │ │ ├── ProcessorSettings.cs │ │ │ │ └── ResourceDetails.cs │ │ │ ├── Model/ │ │ │ │ ├── IDSCv3.cs │ │ │ │ ├── IResourceExportItem.cs │ │ │ │ ├── IResourceGetItem.cs │ │ │ │ ├── IResourceListItem.cs │ │ │ │ ├── IResourceSetItem.cs │ │ │ │ ├── IResourceTestItem.cs │ │ │ │ └── ResourceKind.cs │ │ │ ├── Schema_2024_04/ │ │ │ │ ├── DSCv3.cs │ │ │ │ ├── Definitions/ │ │ │ │ │ └── ResourceKind.cs │ │ │ │ ├── Metadata/ │ │ │ │ │ └── ResourceInstanceResult.cs │ │ │ │ └── Outputs/ │ │ │ │ ├── ConfigurationDocument.cs │ │ │ │ ├── FullItemBase.cs │ │ │ │ ├── GetFullItem.cs │ │ │ │ ├── GetSimpleItem.cs │ │ │ │ ├── ResourceCapability.cs │ │ │ │ ├── ResourceItem.cs │ │ │ │ ├── ResourceListItem.cs │ │ │ │ ├── SetFullItem.cs │ │ │ │ ├── SetSimpleItem.cs │ │ │ │ ├── TestFullItem.cs │ │ │ │ └── TestSimpleItem.cs │ │ │ ├── Set/ │ │ │ │ └── DSCv3ConfigurationSetProcessor.cs │ │ │ └── Unit/ │ │ │ └── DSCv3ConfigurationUnitProcessor.cs │ │ ├── Exceptions/ │ │ │ ├── ErrorCodes.cs │ │ │ ├── FindDscResourceNotFoundException.cs │ │ │ ├── GetDscResourceModuleConflict.cs │ │ │ ├── GetDscResourceMultipleMatches.cs │ │ │ ├── IConfigurationUnitResultException.cs │ │ │ ├── ImportModuleException.cs │ │ │ ├── InstallDscResourceException.cs │ │ │ ├── InvokeDscResourceException.cs │ │ │ ├── UnitPropertyUnsupportedException.cs │ │ │ └── UnitSettingConfigRootException.cs │ │ ├── Extensions/ │ │ │ ├── DictionaryExtensions.cs │ │ │ ├── ExceptionExtensions.cs │ │ │ ├── HashtableExtensions.cs │ │ │ ├── JsonObjectExtensions.cs │ │ │ └── ValueSetExtensions.cs │ │ ├── Factory/ │ │ │ └── ConfigurationSetProcessorFactoryBase.cs │ │ ├── Helpers/ │ │ │ ├── ConfigurationUnitInternal.cs │ │ │ ├── DiagnosticInformation.cs │ │ │ ├── SemanticVersion.cs │ │ │ ├── StringHelpers.cs │ │ │ └── TypeHelpers.cs │ │ ├── Microsoft.Management.Configuration.Processor.csproj │ │ ├── PowerShell/ │ │ │ ├── Constants/ │ │ │ │ └── PowerShellConstants.cs │ │ │ ├── DscModules/ │ │ │ │ ├── DscModuleV2.cs │ │ │ │ └── IDscModule.cs │ │ │ ├── DscResourcesInfo/ │ │ │ │ ├── DscResourceInfoInternal.cs │ │ │ │ ├── DscResourcePropertyInfoInternal.cs │ │ │ │ └── ImplementedAsTypeInternal.cs │ │ │ ├── Extensions/ │ │ │ │ └── PowerShellExtensions.cs │ │ │ ├── Helpers/ │ │ │ │ ├── ConfigurationUnitAndModule.cs │ │ │ │ ├── ConfigurationUnitAndResource.cs │ │ │ │ ├── DscResourcesMap.cs │ │ │ │ ├── Factory.cs │ │ │ │ ├── IPowerShellGet.cs │ │ │ │ ├── PowerShellGetV2.cs │ │ │ │ └── PowerShellHelpers.cs │ │ │ ├── ProcessorEnvironments/ │ │ │ │ ├── HostedEnvironment.cs │ │ │ │ ├── IProcessorEnvironment.cs │ │ │ │ └── ProcessorEnvironmentFactory.cs │ │ │ ├── Set/ │ │ │ │ └── PowerShellConfigurationSetProcessor.cs │ │ │ └── Unit/ │ │ │ └── PowerShellConfigurationUnitProcessor.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Public/ │ │ │ ├── DSCv3ConfigurationSetProcessorFactory.cs │ │ │ ├── IPowerShellConfigurationProcessorFactoryProperties.cs │ │ │ ├── PathEnvironmentVariableHandler.cs │ │ │ ├── PowerShellConfigurationProcessorLocation.cs │ │ │ ├── PowerShellConfigurationProcessorPolicy.cs │ │ │ ├── PowerShellConfigurationProcessorType.cs │ │ │ └── PowerShellConfigurationSetProcessorFactory.cs │ │ ├── Set/ │ │ │ └── ConfigurationSetProcessorBase.cs │ │ └── Unit/ │ │ ├── ApplySettingsResult.cs │ │ ├── ConfigurationUnitProcessorBase.cs │ │ ├── ConfigurationUnitProcessorDetails.cs │ │ ├── ConfigurationUnitResultInformation.cs │ │ ├── ConfigurationUnitSettingDetails.cs │ │ ├── GetAllSettingsResult.cs │ │ ├── GetAllUnitsResult.cs │ │ ├── GetSettingsResult.cs │ │ └── TestSettingsResult.cs │ ├── Microsoft.Management.Configuration.Projection/ │ │ └── Microsoft.Management.Configuration.Projection.csproj │ ├── Microsoft.Management.Configuration.UnitTests/ │ │ ├── Fixtures/ │ │ │ ├── UnitTestCollection.cs │ │ │ └── UnitTestFixture.cs │ │ ├── Helpers/ │ │ │ ├── ApplyGroupMemberSettingsResultInstance.cs │ │ │ ├── ApplyGroupSettingsResultInstance.cs │ │ │ ├── ApplySettingsResultInstance.cs │ │ │ ├── ConfigurationEnvironmentData.cs │ │ │ ├── ConfigurationExtensions.cs │ │ │ ├── ConfigurationProcessorTestBase.cs │ │ │ ├── Constants.cs │ │ │ ├── DiagnosticsEventSink.cs │ │ │ ├── Errors.cs │ │ │ ├── FactSkipIfCI.cs │ │ │ ├── GetAllSettingsResultInstance.cs │ │ │ ├── GetSettingsResultInstance.cs │ │ │ ├── InProcAttribute.cs │ │ │ ├── InProcDiscoverer.cs │ │ │ ├── OutOfProcAttribute.cs │ │ │ ├── OutOfProcDiscoverer.cs │ │ │ ├── PowerShellTestsConstants.cs │ │ │ ├── TelemetryEvent.cs │ │ │ ├── TempDirectory.cs │ │ │ ├── TempFile.cs │ │ │ ├── TestConfigurationProcessorFactory.cs │ │ │ ├── TestConfigurationSetGroupProcessor.cs │ │ │ ├── TestConfigurationSetProcessor.cs │ │ │ ├── TestConfigurationUnitGroupProcessor.cs │ │ │ ├── TestConfigurationUnitProcessor.cs │ │ │ ├── TestConfigurationUnitProcessorDetails.cs │ │ │ ├── TestConfigurationUnitResultInformation.cs │ │ │ ├── TestDSCv3.cs │ │ │ ├── TestGetAllSettingsConfigurationUnitProcessor.cs │ │ │ ├── TestGroupSettingsResultInstance.cs │ │ │ ├── TestResourceExportItem.cs │ │ │ ├── TestResourceGetItem.cs │ │ │ ├── TestResourceListItem.cs │ │ │ ├── TestResourceSetItem.cs │ │ │ ├── TestResourceTestItem.cs │ │ │ ├── TestSettingsResultInstance.cs │ │ │ ├── TheorySkipIfCI.cs │ │ │ └── ValueSetExtensions.cs │ │ ├── Microsoft.Management.Configuration.UnitTests.csproj │ │ ├── TestCollateral/ │ │ │ └── PowerShellModules/ │ │ │ ├── xAdminTestResource/ │ │ │ │ ├── xAdminTestResource.psd1 │ │ │ │ └── xAdminTestResource.psm1 │ │ │ └── xSimpleTestResource/ │ │ │ ├── xSimpleTestResource.psd1 │ │ │ └── xSimpleTestResource.psm1 │ │ └── Tests/ │ │ ├── ConfigurationDetailsTests.cs │ │ ├── ConfigurationHistoryTests.cs │ │ ├── ConfigurationMixedElevationTests.cs │ │ ├── ConfigurationProcessorApplyTests.cs │ │ ├── ConfigurationProcessorFactoryTests.cs │ │ ├── ConfigurationProcessorGetAllTests.cs │ │ ├── ConfigurationProcessorGetTests.cs │ │ ├── ConfigurationProcessorGroupTests.cs │ │ ├── ConfigurationProcessorTelemetryTests.cs │ │ ├── ConfigurationProcessorTestTests.cs │ │ ├── ConfigurationSetAuthoringTests.cs │ │ ├── ConfigurationSetProcessorTests.cs │ │ ├── ConfigurationUnitInternalTests.cs │ │ ├── ConfigurationUnitProcessorTests.cs │ │ ├── DSCv3ProcessorTests.cs │ │ ├── DscModuleV2SimpleFileResourceTests.cs │ │ ├── DscModuleV2Tests.cs │ │ ├── DscResourceMapTests.cs │ │ ├── ExceptionExtensionsTests.cs │ │ ├── HashtableExtensionsTests.cs │ │ ├── OpenConfigurationSetTests.cs │ │ ├── PowerShellHelperTests.cs │ │ ├── ProcessorEnvironmentTests.cs │ │ ├── SemanticVersionTests.cs │ │ ├── ShutdownTests.cs │ │ ├── TypeHelpersTests.cs │ │ └── ValueSetExtensionsTests.cs │ ├── Microsoft.Management.Deployment/ │ │ ├── AddPackageCatalogOptions.cpp │ │ ├── AddPackageCatalogOptions.h │ │ ├── AddPackageCatalogResult.cpp │ │ ├── AddPackageCatalogResult.h │ │ ├── AuthenticationArguments.cpp │ │ ├── AuthenticationArguments.h │ │ ├── AuthenticationInfo.cpp │ │ ├── AuthenticationInfo.h │ │ ├── CanUnload.cpp │ │ ├── CatalogPackage.cpp │ │ ├── CatalogPackage.h │ │ ├── CatalogPackageMetadata.cpp │ │ ├── CatalogPackageMetadata.h │ │ ├── CheckInstalledStatusResult.cpp │ │ ├── CheckInstalledStatusResult.h │ │ ├── ComClsids.cpp │ │ ├── ConnectResult.cpp │ │ ├── ConnectResult.h │ │ ├── Converters.cpp │ │ ├── Converters.h │ │ ├── CreateCompositePackageCatalogOptions.cpp │ │ ├── CreateCompositePackageCatalogOptions.h │ │ ├── Documentation.cpp │ │ ├── Documentation.h │ │ ├── DownloadOptions.cpp │ │ ├── DownloadOptions.h │ │ ├── DownloadResult.cpp │ │ ├── DownloadResult.h │ │ ├── EditPackageCatalogOptions.cpp │ │ ├── EditPackageCatalogOptions.h │ │ ├── EditPackageCatalogResult.cpp │ │ ├── EditPackageCatalogResult.h │ │ ├── FindPackagesOptions.cpp │ │ ├── FindPackagesOptions.h │ │ ├── FindPackagesResult.cpp │ │ ├── FindPackagesResult.h │ │ ├── Helpers.cpp │ │ ├── Helpers.h │ │ ├── Icon.cpp │ │ ├── Icon.h │ │ ├── InstallOptions.cpp │ │ ├── InstallOptions.h │ │ ├── InstallResult.cpp │ │ ├── InstallResult.h │ │ ├── InstalledStatus.cpp │ │ ├── InstalledStatus.h │ │ ├── MatchResult.cpp │ │ ├── MatchResult.h │ │ ├── Microsoft.Management.Deployment.vcxproj │ │ ├── Microsoft.Management.Deployment.vcxproj.filters │ │ ├── MicrosoftEntraIdAuthenticationInfo.cpp │ │ ├── MicrosoftEntraIdAuthenticationInfo.h │ │ ├── Microsoft_Management_Deployment.def │ │ ├── PackageAgreement.cpp │ │ ├── PackageAgreement.h │ │ ├── PackageCatalog.cpp │ │ ├── PackageCatalog.h │ │ ├── PackageCatalogInfo.cpp │ │ ├── PackageCatalogInfo.h │ │ ├── PackageCatalogProgress.cpp │ │ ├── PackageCatalogProgress.h │ │ ├── PackageCatalogReference.cpp │ │ ├── PackageCatalogReference.h │ │ ├── PackageInstallerInfo.cpp │ │ ├── PackageInstallerInfo.h │ │ ├── PackageInstallerInstalledStatus.cpp │ │ ├── PackageInstallerInstalledStatus.h │ │ ├── PackageManager.cpp │ │ ├── PackageManager.h │ │ ├── PackageManager.idl │ │ ├── PackageManagerSettings.cpp │ │ ├── PackageManagerSettings.h │ │ ├── PackageMatchFilter.cpp │ │ ├── PackageMatchFilter.h │ │ ├── PackageVersionId.cpp │ │ ├── PackageVersionId.h │ │ ├── PackageVersionInfo.cpp │ │ ├── PackageVersionInfo.h │ │ ├── PropertySheet.props │ │ ├── Public/ │ │ │ ├── CanUnload.h │ │ │ ├── CoCreatableMicrosoftManagementDeploymentClass.h │ │ │ └── ComClsids.h │ │ ├── RefreshPackageCatalogResult.cpp │ │ ├── RefreshPackageCatalogResult.h │ │ ├── RemovePackageCatalogOptions.cpp │ │ ├── RemovePackageCatalogOptions.h │ │ ├── RemovePackageCatalogResult.cpp │ │ ├── RemovePackageCatalogResult.h │ │ ├── RepairOptions.cpp │ │ ├── RepairOptions.h │ │ ├── RepairResult.cpp │ │ ├── RepairResult.h │ │ ├── SourceAgreement.cpp │ │ ├── SourceAgreement.h │ │ ├── UninstallOptions.cpp │ │ ├── UninstallOptions.h │ │ ├── UninstallResult.cpp │ │ ├── UninstallResult.h │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── Microsoft.Management.Deployment.CsWinRTProjection/ │ │ └── Microsoft.Management.Deployment.CsWinRTProjection.csproj │ ├── Microsoft.Management.Deployment.InProc/ │ │ ├── Microsoft.Management.Deployment.InProc.dll.manifest │ │ ├── Microsoft.Management.Deployment.InProc.vcxproj │ │ ├── Microsoft.Management.Deployment.InProc.vcxproj.filters │ │ ├── PropertySheet.props │ │ ├── Source.def │ │ ├── dllmain.cpp │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── Microsoft.Management.Deployment.OutOfProc/ │ │ ├── Factory.cpp │ │ ├── Factory.h │ │ ├── Microsoft.Management.Deployment.OutOfProc.vcxproj │ │ ├── Microsoft.Management.Deployment.OutOfProc.vcxproj.filters │ │ ├── PropertySheet.props │ │ ├── Source.def │ │ ├── dllmain.cpp │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── Microsoft.Management.Deployment.Projection/ │ │ ├── ClassModel.cs │ │ ├── ClassesDefinition.cs │ │ ├── Initializers/ │ │ │ ├── ActivationFactoryInstanceInitializer.cs │ │ │ ├── IInstanceInitializer.cs │ │ │ ├── LocalServerInstanceInitializer.cs │ │ │ └── PolicyEnforcedInstanceInitializer.cs │ │ ├── Microsoft.Management.Deployment.Projection.csproj │ │ ├── Utils/ │ │ │ └── ComUtils.cs │ │ └── WinGetProjectionFactory.cs │ ├── PowerShell/ │ │ ├── CommonFiles/ │ │ │ ├── PowerShellCmdlet.cs │ │ │ ├── StreamType.cs │ │ │ └── WinGetAssemblyLoadContext.cs │ │ ├── ExternalModules/ │ │ │ ├── PackageManagement/ │ │ │ │ └── 1.4.8.1/ │ │ │ │ ├── DSCResources/ │ │ │ │ │ ├── MSFT_PackageManagement/ │ │ │ │ │ │ ├── MSFT_PackageManagement.psm1 │ │ │ │ │ │ ├── MSFT_PackageManagement.schema.mof │ │ │ │ │ │ └── MSFT_PackageManagement.strings.psd1 │ │ │ │ │ ├── MSFT_PackageManagementSource/ │ │ │ │ │ │ ├── MSFT_PackageManagementSource.psm1 │ │ │ │ │ │ ├── MSFT_PackageManagementSource.schema.mof │ │ │ │ │ │ └── MSFT_PackageManagementSource.strings.psd1 │ │ │ │ │ ├── PackageManagementDscUtilities.psm1 │ │ │ │ │ └── PackageManagementDscUtilities.strings.psd1 │ │ │ │ ├── PSGetModuleInfo.xml │ │ │ │ ├── PackageManagement.Resources.psd1 │ │ │ │ ├── PackageManagement.format.ps1xml │ │ │ │ ├── PackageManagement.psd1 │ │ │ │ ├── PackageManagement.psm1 │ │ │ │ └── PackageProviderFunctions.psm1 │ │ │ └── PowerShellGet/ │ │ │ └── 2.2.5/ │ │ │ ├── DscResources/ │ │ │ │ ├── MSFT_PSModule/ │ │ │ │ │ ├── MSFT_PSModule.psm1 │ │ │ │ │ ├── MSFT_PSModule.schema.mfl │ │ │ │ │ ├── MSFT_PSModule.schema.mof │ │ │ │ │ └── en-US/ │ │ │ │ │ └── MSFT_PSModule.strings.psd1 │ │ │ │ └── MSFT_PSRepository/ │ │ │ │ ├── MSFT_PSRepository.psm1 │ │ │ │ ├── MSFT_PSRepository.schema.mfl │ │ │ │ ├── MSFT_PSRepository.schema.mof │ │ │ │ └── en-US/ │ │ │ │ └── MSFT_PSRepository.strings.psd1 │ │ │ ├── Modules/ │ │ │ │ ├── PowerShellGet.LocalizationHelper/ │ │ │ │ │ └── PowerShellGet.LocalizationHelper.psm1 │ │ │ │ └── PowerShellGet.ResourceHelper/ │ │ │ │ ├── PowerShellGet.ResourceHelper.psm1 │ │ │ │ └── en-US/ │ │ │ │ └── PowerShellGet.ResourceHelper.strings.psd1 │ │ │ ├── PSGet.Format.ps1xml │ │ │ ├── PSGet.Resource.psd1 │ │ │ ├── PSGetModuleInfo.xml │ │ │ ├── PSModule.psm1 │ │ │ ├── PowerShellGet.psd1 │ │ │ └── en-US/ │ │ │ └── PSGet.Resource.psd1 │ │ ├── Help/ │ │ │ ├── Microsoft.WinGet.Client/ │ │ │ │ ├── Add-WinGetSource.md │ │ │ │ ├── Assert-WinGetPackageManager.md │ │ │ │ ├── Disable-WinGetSetting.md │ │ │ │ ├── Enable-WinGetSetting.md │ │ │ │ ├── Export-WinGetPackage.md │ │ │ │ ├── Find-WinGetPackage.md │ │ │ │ ├── Get-WinGetPackage.md │ │ │ │ ├── Get-WinGetSetting.md │ │ │ │ ├── Get-WinGetSource.md │ │ │ │ ├── Get-WinGetUserSetting.md │ │ │ │ ├── Get-WinGetVersion.md │ │ │ │ ├── Install-WinGetPackage.md │ │ │ │ ├── Microsoft.WinGet.Client.md │ │ │ │ ├── Remove-WinGetSource.md │ │ │ │ ├── Repair-WinGetPackage.md │ │ │ │ ├── Repair-WinGetPackageManager.md │ │ │ │ ├── Reset-WinGetSource.md │ │ │ │ ├── Set-WinGetUserSetting.md │ │ │ │ ├── Test-WinGetUserSetting.md │ │ │ │ ├── Uninstall-WinGetPackage.md │ │ │ │ └── Update-WinGetPackage.md │ │ │ ├── Microsoft.WinGet.DSC/ │ │ │ │ ├── WinGetAdminSettings.md │ │ │ │ ├── WinGetPackage.md │ │ │ │ ├── WinGetPackageManager.md │ │ │ │ ├── WinGetSource.md │ │ │ │ └── WinGetUserSettings.md │ │ │ └── markdownlint.yaml │ │ ├── Microsoft.WinGet.Client/ │ │ │ ├── Examples/ │ │ │ │ ├── Sample_AddRemoveSource.ps1 │ │ │ │ ├── Sample_EnableSettings.ps1 │ │ │ │ ├── Sample_FindPackage.ps1 │ │ │ │ ├── Sample_GetPackage.ps1 │ │ │ │ ├── Sample_GetVersion.ps1 │ │ │ │ ├── Sample_InstallPackage.ps1 │ │ │ │ ├── Sample_RepairPackage.ps1 │ │ │ │ ├── Sample_UninstallPackage.ps1 │ │ │ │ └── Sample_UpdatePackage.ps1 │ │ │ ├── ModuleFiles/ │ │ │ │ ├── Format.ps1xml │ │ │ │ └── Microsoft.WinGet.Client.psd1 │ │ │ └── README.md │ │ ├── Microsoft.WinGet.Client.Cmdlets/ │ │ │ ├── Cmdlets/ │ │ │ │ ├── AddSourceCmdlet.cs │ │ │ │ ├── AssertWinGetPackageManagerCmdlet.cs │ │ │ │ ├── Common/ │ │ │ │ │ ├── FinderCmdlet.cs │ │ │ │ │ ├── FinderExtendedCmdlet.cs │ │ │ │ │ ├── InstallCmdlet.cs │ │ │ │ │ ├── PackageCmdlet.cs │ │ │ │ │ └── WinGetPackageManagerCmdlet.cs │ │ │ │ ├── DisableSettingCmdlet.cs │ │ │ │ ├── EnableSettingCmdlet.cs │ │ │ │ ├── ExportPackageCmdlet.cs │ │ │ │ ├── FindPackageCmdlet.cs │ │ │ │ ├── GetPackageCmdlet.cs │ │ │ │ ├── GetSettingCmdlet.cs │ │ │ │ ├── GetSourceCmdlet.cs │ │ │ │ ├── GetUserSettingCmdlet.cs │ │ │ │ ├── GetVersionCmdlet.cs │ │ │ │ ├── InstallPackageCmdlet.cs │ │ │ │ ├── InstallerSelectionCmdlet.cs │ │ │ │ ├── PSObjects/ │ │ │ │ │ ├── PSPackageFieldMatchOption.cs │ │ │ │ │ ├── PSPackageInstallMode.cs │ │ │ │ │ ├── PSPackageInstallScope.cs │ │ │ │ │ ├── PSPackageInstallerType.cs │ │ │ │ │ ├── PSPackageRepairMode.cs │ │ │ │ │ ├── PSPackageUninstallMode.cs │ │ │ │ │ ├── PSProcessorArchitecture.cs │ │ │ │ │ ├── PSSourceTrustLevel.cs │ │ │ │ │ └── PSWindowsPlatform.cs │ │ │ │ ├── RemoveSourceCmdlet.cs │ │ │ │ ├── RepairPackageCmdlet.cs │ │ │ │ ├── RepairWinGetPackageManagerCmdlet.cs │ │ │ │ ├── ResetSourceCmdlet.cs │ │ │ │ ├── SetUserSettingCmdlet.cs │ │ │ │ ├── TestUserSettingCmdlet.cs │ │ │ │ ├── UninstallPackageCmdlet.cs │ │ │ │ └── UpdatePackageCmdlet.cs │ │ │ ├── Common/ │ │ │ │ └── Constants.cs │ │ │ ├── Microsoft.WinGet.Client.Cmdlets.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ └── Resolver/ │ │ │ ├── ModuleInit.cs │ │ │ └── WinGetAppDomain.cs │ │ ├── Microsoft.WinGet.Client.Engine/ │ │ │ ├── Attributes/ │ │ │ │ └── FilterAttribute.cs │ │ │ ├── Commands/ │ │ │ │ ├── CliCommand.cs │ │ │ │ ├── Common/ │ │ │ │ │ ├── BaseCommand.cs │ │ │ │ │ ├── FinderCommand.cs │ │ │ │ │ ├── FinderExtendedCommand.cs │ │ │ │ │ ├── InstallCommand.cs │ │ │ │ │ ├── ManagementDeploymentCommand.cs │ │ │ │ │ └── PackageCommand.cs │ │ │ │ ├── DownloadCommand.cs │ │ │ │ ├── FinderPackageCommand.cs │ │ │ │ ├── InstallerPackageCommand.cs │ │ │ │ ├── RepairPackageCommand.cs │ │ │ │ ├── SourceCommand.cs │ │ │ │ ├── UninstallPackageCommand.cs │ │ │ │ ├── UserSettingsCommand.cs │ │ │ │ ├── VersionCommand.cs │ │ │ │ └── WinGetPackageManagerCommand.cs │ │ │ ├── Common/ │ │ │ │ ├── Constants.cs │ │ │ │ ├── ErrorCode.cs │ │ │ │ ├── IntegrityCategory.cs │ │ │ │ ├── Utilities.cs │ │ │ │ └── WinGetIntegrity.cs │ │ │ ├── Exceptions/ │ │ │ │ ├── CatalogConnectException.cs │ │ │ │ ├── FindPackagesException.cs │ │ │ │ ├── InvalidSourceException.cs │ │ │ │ ├── InvalidVersionException.cs │ │ │ │ ├── NoPackageFoundException.cs │ │ │ │ ├── SingleThreadedApartmentException.cs │ │ │ │ ├── UserSettingsReadException.cs │ │ │ │ ├── VagueCriteriaException.cs │ │ │ │ ├── WinGetCLIException.cs │ │ │ │ ├── WinGetCLITimeoutException.cs │ │ │ │ ├── WinGetIntegrityException.cs │ │ │ │ ├── WinGetRepairException.cs │ │ │ │ ├── WinGetRepairPackageException.cs │ │ │ │ └── WindowsPowerShellNotSupported.cs │ │ │ ├── Extensions/ │ │ │ │ ├── CatalogPackageExtensions.cs │ │ │ │ └── ReleaseExtensions.cs │ │ │ ├── Helpers/ │ │ │ │ ├── AppxModuleHelper.cs │ │ │ │ ├── DownloadOperationWithProgress.cs │ │ │ │ ├── GitHubClient.cs │ │ │ │ ├── HttpClientHelper.cs │ │ │ │ ├── InstallOperationWithProgress.cs │ │ │ │ ├── ManagementDeploymentFactory.cs │ │ │ │ ├── OperationWithProgressBase.cs │ │ │ │ ├── PSEnumHelpers.cs │ │ │ │ ├── PackageDependency.cs │ │ │ │ ├── PackageManagerWrapper.cs │ │ │ │ ├── RepairOperationWithProgress.cs │ │ │ │ ├── TempDirectory.cs │ │ │ │ ├── TempFile.cs │ │ │ │ ├── UninstallOperationWithProgress.cs │ │ │ │ ├── WinGetCLICommandBuilder.cs │ │ │ │ ├── WinGetCLICommandResult.cs │ │ │ │ ├── WinGetVersion.cs │ │ │ │ ├── WinRTHelpers.cs │ │ │ │ ├── WingetCLIWrapper.cs │ │ │ │ └── WingetDependencies.cs │ │ │ ├── Microsoft.WinGet.Client.Engine.csproj │ │ │ ├── PSObjects/ │ │ │ │ ├── PSCatalogPackage.cs │ │ │ │ ├── PSCompareResult.cs │ │ │ │ ├── PSDownloadResult.cs │ │ │ │ ├── PSFoundCatalogPackage.cs │ │ │ │ ├── PSInstallResult.cs │ │ │ │ ├── PSInstalledCatalogPackage.cs │ │ │ │ ├── PSPackageVersionInfo.cs │ │ │ │ ├── PSRepairResult.cs │ │ │ │ ├── PSSourceResult.cs │ │ │ │ └── PSUninstallResult.cs │ │ │ └── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ └── Resources.resx │ │ ├── Microsoft.WinGet.Configuration/ │ │ │ ├── Examples/ │ │ │ │ ├── Sample_InvokeConfiguration.ps1 │ │ │ │ └── Sample_StartConfiguration.ps1 │ │ │ ├── ModuleFiles/ │ │ │ │ └── Microsoft.WinGet.Configuration.psd1 │ │ │ └── README.md │ │ ├── Microsoft.WinGet.Configuration.Cmdlets/ │ │ │ ├── Cmdlets/ │ │ │ │ ├── Common/ │ │ │ │ │ └── OpenConfiguration.cs │ │ │ │ ├── CompleteWinGetConfigurationCmdlet.cs │ │ │ │ ├── ConfirmWinGetConfigurationCmdlet.cs │ │ │ │ ├── ConvertToWinGetConfigurationYamlCmdlet.cs │ │ │ │ ├── GetWinGetConfigurationCmdlet.cs │ │ │ │ ├── GetWinGetConfigurationDetailsCmdlet.cs │ │ │ │ ├── InvokeWinGetConfigurationCmdlet.cs │ │ │ │ ├── RemoveWinGetConfigurationHistoryCmdlet.cs │ │ │ │ ├── StartWinGetConfigurationCmdlet.cs │ │ │ │ ├── StopWinGetConfigurationCmdlet.cs │ │ │ │ └── TestWinGetConfigurationCmdlet.cs │ │ │ ├── Helpers/ │ │ │ │ ├── Constants.cs │ │ │ │ └── Utilities.cs │ │ │ ├── Microsoft.WinGet.Configuration.Cmdlets.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ └── Resolver/ │ │ │ └── ModuleInit.cs │ │ ├── Microsoft.WinGet.Configuration.Engine/ │ │ │ ├── Commands/ │ │ │ │ └── ConfigurationCommand.cs │ │ │ ├── Exceptions/ │ │ │ │ ├── ApplyConfigurationException.cs │ │ │ │ ├── ErrorCodes.cs │ │ │ │ ├── ErrorRecordErrorId.cs │ │ │ │ ├── GetDetailsException.cs │ │ │ │ └── OpenConfigurationSetException.cs │ │ │ ├── Extensions/ │ │ │ │ └── ValueSetExtensions.cs │ │ │ ├── Helpers/ │ │ │ │ ├── ApplyConfigurationSetProgressOutput.cs │ │ │ │ ├── ConfigurationSetProgressOutputBase.cs │ │ │ │ ├── ConfigurationUnitInformation.cs │ │ │ │ ├── GetConfigurationSetDetailsProgressOutput.cs │ │ │ │ ├── OpenConfigurationParameters.cs │ │ │ │ ├── TestConfigurationSetProgressOutput.cs │ │ │ │ └── Utilities.cs │ │ │ ├── Microsoft.WinGet.Configuration.Engine.csproj │ │ │ ├── PSObjects/ │ │ │ │ ├── PSApplyConfigurationSetResult.cs │ │ │ │ ├── PSApplyConfigurationUnitResult.cs │ │ │ │ ├── PSConfigurationJob.cs │ │ │ │ ├── PSConfigurationProcessor.cs │ │ │ │ ├── PSConfigurationSet.cs │ │ │ │ ├── PSConfigurationTestResult.cs │ │ │ │ ├── PSConfigurationUnitState.cs │ │ │ │ ├── PSGetConfigurationDetailsResult.cs │ │ │ │ ├── PSTestConfigurationSetResult.cs │ │ │ │ ├── PSTestConfigurationUnitResult.cs │ │ │ │ ├── PSUnitResult.cs │ │ │ │ ├── PSValidateConfigurationSetResult.cs │ │ │ │ └── PSValidateConfigurationUnitResult.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ └── Resources/ │ │ │ ├── Resources.Designer.cs │ │ │ └── Resources.resx │ │ ├── Microsoft.WinGet.DSC/ │ │ │ ├── Microsoft.WinGet.DSC.psd1 │ │ │ └── Microsoft.WinGet.DSC.psm1 │ │ ├── Microsoft.WinGet.SharedLib/ │ │ │ ├── Exceptions/ │ │ │ │ ├── ErrorCodes.cs │ │ │ │ └── GroupPolicyException.cs │ │ │ ├── Extensions/ │ │ │ │ └── EnumPolicyExtension.cs │ │ │ ├── Microsoft.WinGet.SharedLib.csproj │ │ │ ├── PolicySettings/ │ │ │ │ ├── Enums.cs │ │ │ │ ├── GroupPolicy.cs │ │ │ │ └── TogglePolicy.cs │ │ │ └── Resources/ │ │ │ ├── GroupPolicyResource.Designer.cs │ │ │ └── GroupPolicyResource.resx │ │ ├── Microsoft.WinGet.UnitTests/ │ │ │ ├── GitHubClientTests.cs │ │ │ ├── Microsoft.WinGet.UnitTests.csproj │ │ │ └── WinGetCLICommandBuilderTests.cs │ │ ├── scripts/ │ │ │ ├── Execute-WinGetTests.ps1 │ │ │ ├── Initialize-LocalWinGetModules.ps1 │ │ │ └── samples/ │ │ │ ├── WinGetAdminSettingsResourceSample.ps1 │ │ │ ├── WinGetPackageManagerSample.ps1 │ │ │ ├── WinGetPackageResourceSample.ps1 │ │ │ ├── WinGetSourceSample.ps1 │ │ │ └── WinGetUserSettingsPackageManagerSample.ps1 │ │ └── tests/ │ │ ├── Microsoft.WinGet.Client.Tests.ps1 │ │ ├── Microsoft.WinGet.Configuration.Tests.ps1 │ │ ├── Microsoft.WinGet.DSC.Tests.ps1 │ │ └── RunTests.ps1 │ ├── PureLib/ │ │ ├── PureLib.vcxitems │ │ ├── pure/ │ │ │ ├── AUTHORS │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── make-errors.js │ │ │ ├── make-signatures.js │ │ │ ├── make-tests.js │ │ │ ├── package.json.backup │ │ │ ├── pure │ │ │ ├── pure.h │ │ │ ├── pure_constants.h │ │ │ ├── pure_errors.h │ │ │ ├── pure_routines.h │ │ │ ├── pure_signatures.h │ │ │ └── test.js │ │ └── readme.md │ ├── SfsClient/ │ │ ├── SfsClient.vcxproj │ │ ├── SfsClient.vcxproj.filters │ │ ├── readme.md │ │ └── sfs-client/ │ │ ├── .clang-format │ │ ├── .cmake-format.json │ │ ├── .github/ │ │ │ ├── CODEOWNERS │ │ │ ├── ISSUE_TEMPLATE/ │ │ │ │ ├── bug_report.md │ │ │ │ └── feature_request.md │ │ │ ├── dependabot.yml │ │ │ ├── pull_request_template.md │ │ │ └── workflows/ │ │ │ ├── initialize-codeql/ │ │ │ │ └── action.yml │ │ │ ├── install-winget/ │ │ │ │ └── action.yml │ │ │ ├── main-build-ubuntu.yml │ │ │ ├── main-build-windows.yml │ │ │ └── pr.yml │ │ ├── .gitignore │ │ ├── API.md │ │ ├── CMakeLists.txt │ │ ├── CODE_OF_CONDUCT.md │ │ ├── DEVELOPMENT.md │ │ ├── LICENSE │ │ ├── NOTICE.md │ │ ├── README.md │ │ ├── SECURITY.md │ │ ├── SUPPORT.md │ │ ├── TEST.md │ │ ├── cgmanifest.json │ │ ├── client/ │ │ │ ├── CMakeLists.txt │ │ │ ├── include/ │ │ │ │ └── sfsclient/ │ │ │ │ ├── AppContent.h │ │ │ │ ├── AppFile.h │ │ │ │ ├── ApplicabilityDetails.h │ │ │ │ ├── ClientConfig.h │ │ │ │ ├── Content.h │ │ │ │ ├── ContentId.h │ │ │ │ ├── File.h │ │ │ │ ├── Logging.h │ │ │ │ ├── RequestParams.h │ │ │ │ ├── Result.h │ │ │ │ └── SFSClient.h │ │ │ ├── src/ │ │ │ │ ├── AppContent.cpp │ │ │ │ ├── AppFile.cpp │ │ │ │ ├── ApplicabilityDetails.cpp │ │ │ │ ├── Content.cpp │ │ │ │ ├── ContentId.cpp │ │ │ │ ├── File.cpp │ │ │ │ ├── Logging.cpp │ │ │ │ ├── Result.cpp │ │ │ │ ├── SFSClient.cpp │ │ │ │ └── details/ │ │ │ │ ├── ContentUtil.cpp │ │ │ │ ├── ContentUtil.h │ │ │ │ ├── CorrelationVector.cpp │ │ │ │ ├── CorrelationVector.h │ │ │ │ ├── Env.cpp │ │ │ │ ├── Env.h │ │ │ │ ├── ErrorHandling.cpp │ │ │ │ ├── ErrorHandling.h │ │ │ │ ├── OSInfo.cpp │ │ │ │ ├── OSInfo.h │ │ │ │ ├── ReportingHandler.cpp │ │ │ │ ├── ReportingHandler.h │ │ │ │ ├── SFSClientImpl.cpp │ │ │ │ ├── SFSClientImpl.h │ │ │ │ ├── SFSClientInterface.h │ │ │ │ ├── SFSException.cpp │ │ │ │ ├── SFSException.h │ │ │ │ ├── SFSUrlBuilder.cpp │ │ │ │ ├── SFSUrlBuilder.h │ │ │ │ ├── TestOverride.cpp │ │ │ │ ├── TestOverride.h │ │ │ │ ├── UrlBuilder.cpp │ │ │ │ ├── UrlBuilder.h │ │ │ │ ├── Util.cpp │ │ │ │ ├── Util.h │ │ │ │ ├── connection/ │ │ │ │ │ ├── Connection.cpp │ │ │ │ │ ├── Connection.h │ │ │ │ │ ├── ConnectionConfig.cpp │ │ │ │ │ ├── ConnectionConfig.h │ │ │ │ │ ├── ConnectionManager.cpp │ │ │ │ │ ├── ConnectionManager.h │ │ │ │ │ ├── CurlConnection.cpp │ │ │ │ │ ├── CurlConnection.h │ │ │ │ │ ├── CurlConnectionManager.cpp │ │ │ │ │ ├── CurlConnectionManager.h │ │ │ │ │ ├── HttpHeader.cpp │ │ │ │ │ ├── HttpHeader.h │ │ │ │ │ └── mock/ │ │ │ │ │ ├── MockConnection.cpp │ │ │ │ │ ├── MockConnection.h │ │ │ │ │ ├── MockConnectionManager.cpp │ │ │ │ │ └── MockConnectionManager.h │ │ │ │ └── entity/ │ │ │ │ ├── ContentType.cpp │ │ │ │ ├── ContentType.h │ │ │ │ ├── FileEntity.cpp │ │ │ │ ├── FileEntity.h │ │ │ │ ├── VersionEntity.cpp │ │ │ │ └── VersionEntity.h │ │ │ └── tests/ │ │ │ ├── CMakeLists.txt │ │ │ ├── functional/ │ │ │ │ ├── SFSClientTests.cpp │ │ │ │ └── details/ │ │ │ │ ├── CurlConnectionTests.cpp │ │ │ │ └── SFSClientImplTests.cpp │ │ │ ├── mock/ │ │ │ │ ├── MockWebServer.cpp │ │ │ │ ├── MockWebServer.h │ │ │ │ ├── ProxyServer.cpp │ │ │ │ ├── ProxyServer.h │ │ │ │ ├── ServerCommon.cpp │ │ │ │ └── ServerCommon.h │ │ │ ├── unit/ │ │ │ │ ├── AppContentTests.cpp │ │ │ │ ├── AppFileTests.cpp │ │ │ │ ├── ApplicabilityDetailsTests.cpp │ │ │ │ ├── ContentIdTests.cpp │ │ │ │ ├── ContentTests.cpp │ │ │ │ ├── FileTests.cpp │ │ │ │ ├── ResultTests.cpp │ │ │ │ ├── SFSClientTests.cpp │ │ │ │ └── details/ │ │ │ │ ├── CurlConnectionManagerTests.cpp │ │ │ │ ├── CurlConnectionTests.cpp │ │ │ │ ├── EnvTests.cpp │ │ │ │ ├── ErrorHandlingTests.cpp │ │ │ │ ├── ReportingHandlerTests.cpp │ │ │ │ ├── SFSClientImplTests.cpp │ │ │ │ ├── SFSUrlBuilderTests.cpp │ │ │ │ ├── TestOverrideTests.cpp │ │ │ │ ├── UrlBuilderTests.cpp │ │ │ │ ├── UtilTests.cpp │ │ │ │ └── entity/ │ │ │ │ ├── FileEntityTests.cpp │ │ │ │ └── VersionEntityTests.cpp │ │ │ └── util/ │ │ │ ├── SFSExceptionMatcher.cpp │ │ │ ├── SFSExceptionMatcher.h │ │ │ ├── TestHelper.cpp │ │ │ └── TestHelper.h │ │ ├── cmake/ │ │ │ ├── SFSOptions.cmake │ │ │ └── sfsclient-config.cmake.in │ │ ├── pre-commit-wrapper.sh │ │ ├── pre-commit.sh │ │ ├── samples/ │ │ │ ├── CMakeLists.txt │ │ │ ├── README.md │ │ │ ├── integration-do-client/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ └── IntegrationDOClient.cpp │ │ │ └── tool/ │ │ │ ├── CMakeLists.txt │ │ │ └── SFSClientTool.cpp │ │ ├── scripts/ │ │ │ ├── Build.ps1 │ │ │ ├── Setup.ps1 │ │ │ ├── Test.ps1 │ │ │ ├── build.sh │ │ │ ├── check-format.py │ │ │ ├── pip.requirements.txt │ │ │ ├── setup.sh │ │ │ └── test.sh │ │ ├── sfs-client-vcpkg-port/ │ │ │ └── sfs-client/ │ │ │ ├── portfile.cmake │ │ │ ├── usage │ │ │ └── vcpkg.json │ │ ├── vcpkg-custom-triplets/ │ │ │ └── x64-windows-static-custom.cmake │ │ └── vcpkg.json │ ├── Update-VcxprojNugetPackageVersions.ps1 │ ├── VcpkgCustomTriplets/ │ │ ├── arm64-release-static.cmake │ │ ├── arm64-release.cmake │ │ ├── arm64.cmake │ │ ├── common.cmake │ │ ├── fuzzing.cmake │ │ ├── x64-fuzzing.cmake │ │ ├── x64-release-static.cmake │ │ ├── x64-release.cmake │ │ ├── x64.cmake │ │ ├── x86-fuzzing.cmake │ │ ├── x86-release-static.cmake │ │ ├── x86-release.cmake │ │ └── x86.cmake │ ├── VcpkgPortOverlay/ │ │ ├── CreatePortOverlay.ps1 │ │ ├── README.md │ │ ├── cpprestsdk/ │ │ │ ├── add-server-certificate-validation.patch │ │ │ ├── fix-find-openssl.patch │ │ │ ├── fix-uwp.patch │ │ │ ├── fix_narrowing.patch │ │ │ ├── portfile.cmake │ │ │ ├── test.patch │ │ │ └── vcpkg.json │ │ ├── detours/ │ │ │ ├── find-jmp-bounds-arm64.patch │ │ │ ├── portfile.cmake │ │ │ ├── usage │ │ │ └── vcpkg.json │ │ └── libyaml/ │ │ ├── export-pkgconfig.patch │ │ ├── fix-POSIX_name.patch │ │ ├── portfile.cmake │ │ └── vcpkg.json │ ├── WinGetMCPServer/ │ │ ├── Exceptions/ │ │ │ └── ToolResponseException.cs │ │ ├── Extensions/ │ │ │ └── PackageListExtensions.cs │ │ ├── Program.cs │ │ ├── Response/ │ │ │ ├── FindPackageResult.cs │ │ │ ├── InstallOperationResult.cs │ │ │ ├── PackageIdentityErrorResult.cs │ │ │ ├── PackageResponse.cs │ │ │ └── ToolResponse.cs │ │ ├── ServerConnection.cs │ │ ├── WinGetMCPServer.csproj │ │ └── WingetPackageTools.cs │ ├── WinGetSchemas/ │ │ ├── PackagesSchema.h │ │ ├── WinGetSchemas.rc │ │ ├── WinGetSchemas.vcxitems │ │ ├── WinGetSchemas.vcxitems.filters │ │ └── resource.h │ ├── WinGetServer/ │ │ ├── PropertySheet.props │ │ ├── Utils.cpp │ │ ├── Utils.h │ │ ├── WinGetServer.exe.manifest │ │ ├── WinGetServer.idl │ │ ├── WinGetServer.rc │ │ ├── WinGetServer.vcxproj │ │ ├── WinGetServer.vcxproj.filters │ │ ├── WinGetServerManualActivation_Client.cpp │ │ ├── WinGetServerManualActivation_Client.h │ │ ├── WinMain.cpp │ │ ├── packages.config │ │ └── resource.h │ ├── WinGetSourceCreator/ │ │ ├── Helpers.cs │ │ ├── ManifestTokens.cs │ │ ├── Model/ │ │ │ ├── DynamicInstaller.cs │ │ │ ├── Installer.cs │ │ │ ├── InstallerType.cs │ │ │ ├── LocalInstaller.cs │ │ │ ├── LocalSource.cs │ │ │ ├── Signature.cs │ │ │ └── SourceInstaller.cs │ │ ├── WinGetLocalSource.cs │ │ └── WinGetSourceCreator.csproj │ ├── WinGetTestCommon/ │ │ ├── WinGetServerInstance.cs │ │ ├── WinGetTestCommon.csproj │ │ └── WindowMessage.cs │ ├── WinGetUtil/ │ │ ├── Exports.cpp │ │ ├── PropertySheet.props │ │ ├── Source.def │ │ ├── WinGetUtil.h │ │ ├── WinGetUtil.vcxproj │ │ ├── WinGetUtil.vcxproj.filters │ │ ├── packages.config │ │ ├── pch.cpp │ │ └── pch.h │ ├── WinGetUtilInterop/ │ │ ├── Api/ │ │ │ ├── WinGetFactory.cs │ │ │ ├── WinGetInstallerMetadata.cs │ │ │ ├── WinGetLogging.cs │ │ │ ├── WinGetManifest.cs │ │ │ ├── WinGetSQLiteIndex.cs │ │ │ └── WinGetUtilities.cs │ │ ├── Build/ │ │ │ └── Microsoft.WindowsPackageManager.Utils.targets │ │ ├── Common/ │ │ │ ├── Constants.cs │ │ │ ├── CreateManifestResult.cs │ │ │ ├── Enums.cs │ │ │ ├── Helpers.cs │ │ │ └── ManifestValidationResult.cs │ │ ├── Exceptions/ │ │ │ ├── WinGetDownloadException.cs │ │ │ ├── WinGetInstallerMetadataException.cs │ │ │ ├── WinGetLoggingException.cs │ │ │ ├── WinGetManifestException.cs │ │ │ └── WinGetSQLiteIndexException.cs │ │ ├── Interfaces/ │ │ │ ├── IWinGetFactory.cs │ │ │ ├── IWinGetInstallerMetadata.cs │ │ │ ├── IWinGetLogging.cs │ │ │ ├── IWinGetManifest.cs │ │ │ ├── IWinGetSQLiteIndex.cs │ │ │ └── IWinGetUtilities.cs │ │ ├── Manifest/ │ │ │ ├── ManifestVersion.cs │ │ │ ├── Preview/ │ │ │ │ ├── InstallerSwitches.cs │ │ │ │ ├── Manifest.cs │ │ │ │ ├── ManifestInstaller.cs │ │ │ │ └── ManifestLocalization.cs │ │ │ └── V1/ │ │ │ ├── InstallerArpEntry.cs │ │ │ ├── InstallerAuthentication.cs │ │ │ ├── InstallerDependency.cs │ │ │ ├── InstallerExpectedReturnCode.cs │ │ │ ├── InstallerInstallationMetadata.cs │ │ │ ├── InstallerMarkets.cs │ │ │ ├── InstallerMicrosoftEntraIdAuthenticationInfo.cs │ │ │ ├── InstallerNestedInstallerFile.cs │ │ │ ├── InstallerPackageDependency.cs │ │ │ ├── InstallerSwitches.cs │ │ │ ├── Manifest.cs │ │ │ ├── ManifestDocumentation.cs │ │ │ ├── ManifestIcon.cs │ │ │ ├── ManifestInstaller.cs │ │ │ ├── ManifestInstallerFile.cs │ │ │ ├── ManifestLocalization.cs │ │ │ ├── ManifestShadow.cs │ │ │ ├── ManifestShadowLocalization.cs │ │ │ ├── MinManifestInfo.cs │ │ │ └── PackageAgreement.cs │ │ ├── WinGetUtilInterop.csproj │ │ └── scripts/ │ │ ├── CreateLocalNuget.ps1 │ │ └── WinGetUtilDev.nuspec │ ├── WinGetUtilInterop.UnitTests/ │ │ ├── APIUnitTests/ │ │ │ ├── ManifestUnitTests.cs │ │ │ └── SQLiteIndexUnitTests.cs │ │ ├── Common/ │ │ │ ├── DisplayTestMethodNameAttribute.cs │ │ │ └── FactSkipx64CI.cs │ │ ├── ManifestUnitTest/ │ │ │ ├── ManifestEqualityUnitTests.cs │ │ │ └── V1ManifestReadTest.cs │ │ ├── TestCollateral/ │ │ │ ├── AllEquality.yaml │ │ │ ├── AllEqualityWithDescription.yaml │ │ │ ├── DifferentId.yaml │ │ │ ├── ExpectedShadowManifest.yaml │ │ │ ├── OneInstaller.yaml │ │ │ ├── PackageTest.yaml │ │ │ ├── PackageTestNewName.yaml │ │ │ ├── PackageTestNewVersion.yaml │ │ │ ├── SomeEquality.yaml │ │ │ ├── SomeEqualityWithLocalization.yaml │ │ │ ├── SomeEqualityWithoutInstallers.yaml │ │ │ ├── SomeEqualityWithoutSwitches.yaml │ │ │ ├── Test_yaml_with_bom.yaml │ │ │ ├── Test_yaml_without_bom.yaml │ │ │ ├── V1ManifestInfoMissingRequiredPackageLocale.yaml │ │ │ ├── V1ManifestMerged.yaml │ │ │ ├── V1ManifestNoLocalization.yaml │ │ │ ├── V1_10ManifestMerged.yaml │ │ │ ├── V1_12ManifestMerged.yaml │ │ │ ├── V1_1ManifestMerged.yaml │ │ │ ├── V1_6ManifestMerged.yaml │ │ │ ├── V1_7ManifestMerged.yaml │ │ │ └── V1_9ManifestMerged.yaml │ │ └── WinGetUtilInterop.UnitTests.csproj │ ├── WinGetYamlFuzzing/ │ │ ├── OneFuzzConfig.json │ │ ├── README.md │ │ ├── WinGetYamlFuzzing.cpp │ │ ├── WinGetYamlFuzzing.vcxproj │ │ ├── WinGetYamlFuzzing.vcxproj.filters │ │ ├── dictionary.txt │ │ └── packages.config │ ├── WindowsPackageManager/ │ │ ├── ConfigurationStaticFunctions.cpp │ │ ├── PropertySheet.props │ │ ├── Source.def │ │ ├── WindowsPackageManager.h │ │ ├── WindowsPackageManager.vcxproj │ │ ├── WindowsPackageManager.vcxproj.filters │ │ ├── main.cpp │ │ └── packages.config │ ├── Xlang/ │ │ ├── README.md │ │ └── UndockedRegFreeWinRT/ │ │ └── src/ │ │ └── UndockedRegFreeWinRT/ │ │ ├── README.md │ │ ├── UndockedRegFreeWinRT/ │ │ │ ├── UndockedRegFreeWinRT.vcxproj │ │ │ ├── UndockedRegFreeWinRT.vcxproj.filters │ │ │ ├── catalog.cpp │ │ │ ├── catalog.h │ │ │ ├── cpp.hint │ │ │ ├── dllmain.cpp │ │ │ ├── extwinrt.h │ │ │ ├── packages.config │ │ │ ├── typeresolution.cpp │ │ │ ├── typeresolution.h │ │ │ └── winrtact.def │ │ └── mwinrtact/ │ │ ├── mwinrtact.cs │ │ └── mwinrtact.csproj │ ├── binver/ │ │ ├── Update-BinVer.ps1 │ │ ├── binver/ │ │ │ ├── resource.h │ │ │ ├── version.h │ │ │ └── version.rc │ │ └── binver.vcxitems │ ├── manifest/ │ │ └── shared.manifest │ ├── nuget.config │ ├── stylecop.json │ ├── targets/ │ │ ├── EmbeddedCsWinRT.targets │ │ └── ReferenceEmbeddedCsWinRTProject.targets │ ├── vcpkg.json │ └── vcpkg.props ├── templates/ │ ├── e2e-setup.yml │ └── e2e-test.template.yml └── tools/ ├── COMTrace/ │ └── ComTrace.wprp ├── CorrelationTestbed/ │ ├── InSandboxScript.ps1 │ ├── InstallAndCheckCorrelation/ │ │ ├── InstallAndCheckCorrelation/ │ │ │ ├── InstallAndCheckCorrelation.cpp │ │ │ ├── InstallAndCheckCorrelation.vcxproj │ │ │ ├── InstallAndCheckCorrelation.vcxproj.filters │ │ │ ├── Microsoft.Management.Deployment.winmd │ │ │ └── packages.config │ │ └── InstallAndCheckCorrelation.sln │ ├── Process-CorrelationResults.ps1 │ ├── Readme.md │ └── Test-CorrelationInSandbox.ps1 ├── DevInSandbox/ │ ├── InSandboxScript.ps1 │ └── Launch-DevPackageInSandbox.ps1 ├── HAMTrace/ │ └── WER.HostActivityManager.wprp ├── IndexComparisonTool/ │ ├── IndexComparisonTool.vcxproj │ ├── IndexComparisonTool.vcxproj.filters │ ├── WinGetUtil.h │ ├── main.cpp │ ├── pch.cpp │ └── pch.h └── SampleWinGetUWPCaller/ ├── AppInstallerCaller/ │ ├── App.cpp │ ├── App.h │ ├── App.idl │ ├── App.xaml │ ├── AppInstallerCaller.vcxproj │ ├── AppInstallerCaller.vcxproj.filters │ ├── GeneratedFromServer/ │ │ └── Microsoft.Management.Deployment.winmd │ ├── InstallingPackageView.cpp │ ├── InstallingPackageView.h │ ├── MainPage.cpp │ ├── MainPage.h │ ├── MainPage.idl │ ├── MainPage.xaml │ ├── Package.appxmanifest │ ├── PropertySheet.props │ ├── packages.config │ ├── pch.cpp │ └── pch.h └── AppInstallerCaller.sln ================================================ FILE CONTENTS ================================================ ================================================ FILE: .config/configuration.vsEnterprise.winget ================================================ # yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 # Reference: https://github.com/microsoft/winget-cli#building-the-client properties: resources: - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode # Requires elevation for the set operation securityContext: elevated allowPrerelease: true settings: DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: description: Install Visual Studio 2022 Enterprise # Requires elevation for the set operation securityContext: elevated settings: id: Microsoft.VisualStudio.2022.Enterprise source: winget - resource: Microsoft.VisualStudio.DSC/VSComponents dependsOn: - vsPackage directives: description: Install required VS workloads from project .vsconfig file # Requires elevation for the get and set operations securityContext: elevated allowPrerelease: true settings: productId: Microsoft.VisualStudio.Product.Enterprise channelId: VisualStudio.17.Release vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig' configurationVersion: 0.2.0 ================================================ FILE: .config/configuration.vsProfessional.winget ================================================ # yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 # Reference: https://github.com/microsoft/winget-cli#building-the-client properties: resources: - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode # Requires elevation for the set operation securityContext: elevated allowPrerelease: true settings: DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: description: Install Visual Studio 2022 Professional # Requires elevation for the set operation securityContext: elevated settings: id: Microsoft.VisualStudio.2022.Professional source: winget - resource: Microsoft.VisualStudio.DSC/VSComponents dependsOn: - vsPackage directives: description: Install required VS workloads from project .vsconfig file # Requires elevation for the get and set operations securityContext: elevated allowPrerelease: true settings: productId: Microsoft.VisualStudio.Product.Professional channelId: VisualStudio.17.Release vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig' configurationVersion: 0.2.0 ================================================ FILE: .config/configuration.winget ================================================ # yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 # Reference: https://github.com/microsoft/winget-cli#building-the-client properties: resources: - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode # Requires elevation for the set operation securityContext: elevated allowPrerelease: true settings: DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: description: Install Visual Studio 2022 Community # Requires elevation for the set operation securityContext: elevated settings: id: Microsoft.VisualStudio.2022.Community source: winget - resource: Microsoft.VisualStudio.DSC/VSComponents dependsOn: - vsPackage directives: description: Install required VS workloads from project .vsconfig file # Requires elevation for the get and set operations securityContext: elevated allowPrerelease: true settings: productId: Microsoft.VisualStudio.Product.Community channelId: VisualStudio.17.Release vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig' configurationVersion: 0.2.0 ================================================ FILE: .editorconfig ================================================ # top-most EditorConfig file root=true # Apply Windows-style newlines with a newline ending on every file, using UTF-8, and removing extra whitespace before newlines [*] end_of_line = crlf insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true # Overrides for Yaml Files - Use two spaces for indents # editorconfig/editorconfig#329 [*.{yml,yaml}] indent_style = space indent_size = 2 # Overrides for Markdown Files - Use tab for indents (accessibility) [*.md] indent_style = tab [{allow.txt,excludes.txt,patterns.txt,expect.txt}] end_of_line = lf ================================================ FILE: .github/ISSUE_TEMPLATE/Bug_Report.yml ================================================ name: '🐛 Bug Report' description: Report errors or unexpected behavior. labels: - Issue-Bug - Needs-Triage body: - type: markdown attributes: value: | > This bug tracker is monitored by the Windows Package Manager development team and other technical folks. > > **Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to GitHub issues**. > Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue. > > If this is an application crash, please provide a Feedback Hub submission link if possible so we can find your diagnostic data on the backend. > You can open the Feedback Hub directly to the prepopulated form at https://aka.ms/winget-feedback > Alternatively, use the category "Apps > Windows Package Manager" and choose "Share My Feedback" after submission to get the link. > > Please use this form and describe your issue, concisely but precisely, with as much detail as possible. - type: dropdown attributes: label: Relevant area(s) description: What things had an issue? Check all that apply. multiple: true options: - WinGet CLI - PowerShell Module - COM API - DSC Resource default: 0 validations: required: true - type: dropdown attributes: label: Relevant command(s) description: If you selected 'WinGet CLI' above, specify the command(s) that had an issue. multiple: true options: - winget configure - winget download - winget dscv3 - winget export - winget features - winget font - winget hash - winget import - winget install - winget list - winget mcp - winget pin - winget repair - winget search - winget settings - winget show - winget source - winget uninstall - winget upgrade - winget validate validations: required: false - type: textarea attributes: label: Brief description of your issue placeholder: Briefly describe your issue here. validations: required: true - type: textarea attributes: label: Steps to reproduce placeholder: A description of how to trigger this bug validations: required: true - type: textarea attributes: label: Expected behavior placeholder: What did you expect to happen? validations: required: true - type: textarea attributes: label: Actual behavior placeholder: What is currently happening? validations: required: true - type: textarea attributes: label: Environment placeholder: | [winget --info] Windows Package Manager version Windows: Windows.Desktop version Package: Microsoft.DesktopAppInstaller version Any other software? render: shell validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/Documentation_Issue.yml ================================================ name: '📚 Documentation Issue' description: Report issues in our documentation. labels: - Issue-Docs - Needs-Triage body: - type: textarea attributes: label: Brief description of your issue placeholder: Briefly describe which document needs to be corrected and why. validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/Feature_Request.yml ================================================ name: '🚀 Feature Request / Idea' description: Suggest a new feature or improvement (this does not mean you have to implement it). labels: - Issue-Feature - Needs-Triage body: - type: dropdown attributes: label: Relevant area(s) description: What area does this feature request relate to? Check all that apply. multiple: true options: - WinGet CLI - PowerShell Module - COM API - DSC Resource default: 0 validations: required: true - type: textarea attributes: label: Description of the new feature / enhancement placeholder: | A clear and concise description of what the problem is that the new feature would solve. Describe why and how a user would use this new functionality (if applicable). validations: required: true - type: textarea attributes: label: Proposed technical implementation details placeholder: A clear and concise description of what you want to happen. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: true contact_links: - name: Package issues url: https://github.com/microsoft/winget-pkgs/issues about: Please create issues related to the packages here. - name: General Question url: https://github.com/microsoft/winget-cli/discussions/new about: Have a question on something? Start a new discussion thread. - name: Review open issues url: https://github.com/microsoft/winget-cli/issues about: Please check if your issue or a similar issue has already been submitted. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ - [ ] I have signed the [Contributor License Agreement](https://cla.opensource.microsoft.com/microsoft/winget-pkgs). - [ ] I have updated the [Release Notes](../doc/ReleaseNotes.md). - [ ] This pull request is related to an issue. ----- ================================================ FILE: .github/actions/spelling/README.md ================================================ # check-spelling/check-spelling configuration File | Purpose | Format | Info -|-|-|- [dictionary.txt](dictionary.txt) | Replacement dictionary (creating this file will override the default dictionary) | one word per line | [dictionary](https://github.com/check-spelling/check-spelling/wiki/Configuration#dictionary) [allow.txt](allow.txt) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow) [reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject) [excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes) [only.txt](only.txt) | Only check matching files (applied after excludes) | perl regular expression | [only](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-only) [patterns.txt](patterns.txt) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns) [candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns) [line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns) [expect.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect) [advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice) Note: you can replace any of these files with a directory by the same name (minus the suffix) and then include multiple files inside that directory (with that suffix) to merge multiple files together. ================================================ FILE: .github/actions/spelling/advice.md ================================================
If the flagged items are :exploding_head: false positives If items relate to a ... * binary file (or some other file you wouldn't want to check at all). Please add a file path to the `excludes.txt` file matching the containing file. File paths are Perl 5 Regular Expressions - you can [test]( https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files. `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md]( ../tree/HEAD/README.md) (on whichever branch you're using). * well-formed pattern. If you can write a [pattern]( https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns ) that would match it, try adding it to the `patterns.txt` file. Patterns are Perl 5 Regular Expressions - you can [test]( https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines. Note that patterns can't match multiline strings.
================================================ FILE: .github/actions/spelling/allow.txt ================================================ ACTIONDATA ACTIONSTART activatable addfile addmanifest addpin addportablefile addstore admins AFX appinstaller appmodel appxbundle appxbundles appxrecipe appxsdk APSTUDIO ARRAYSIZE artifactstagingdirectory Asn aspirational Authenticode AUTOLISTEN azureedge binlog binver bstr BUILDNUMBER bytearray cdn cer certutil cfr cguid checkbox cla CLASSNOTAVAILABLE clsids cmake CNG COMMANDBARFLYOUT COMMONDATA comparand conemu contoso COSTDEFERRED cotaskmem cpprest cpprestsdk cppwinrt createnew createpintable createportabletable createtables CRTDECL CRYPTPROTECT CTLs curated CURSORPOSITON CUSTOMHEADER cvd datatelemetry datetime dbconn DBId debugbreak dedup defaultlocale delstore Demitrius denelon depersist differentpath DIRECTONLY DISMAPI distro dllmain dotnet downloaders dsx DWORDLONG emoji ENDDIALOG ensureandinsert ensurepathexists entra ENU EOAC errorlevel ESRP etstandard ETW EULA EVENTTAG EWX exeenus exeinteractive exelog exesilent exeswp EXTRADEBUG EXTRAFLAGS FAILIFTHERE fakeswitch FATALEXIT FIELDTAG FILEFLAGS FILEFLAGSMASK FILEOS FILESINUSE FILESUBTYPE FILEVERSION florelis FLUSHEACHLINE fnt forcerestart gdi genai HCCE hcertstore HCRYPTMSG HGlobal HGLOBAL HIDECANCEL hinternet HKCU HKLM hresult HRSRC hstring IAppx ICheckpoint IConfiguration IFACEMETHOD ifspec IInspectable inapplicabilities inheritdoc inno Inq installertype Installeruniqueness installlocation INSTALLLOGATTRIBUTES INSTALLLOGMODE INSTALLPATH INSTALLUILEVEL INVALIDARG INVALIDSID iot IPinning ipmo IPortable ISAPPROVEDFOROUTPUT IUnknown JDKs langutil lastwritetime LCIDTo lnk LOGONLYONERROR LOGPATH LOGPERFORMANCE logsql logto LPCGUID LPCSTR Luid makeappx MAKEINTRESOURCE makemsix MANIFESTSCHEMA MANIFESTVERSION mcp Memberwise meme metadatas Minimatch Moq motw mrm msdata MSHCTX MSHLFLAGS msiexec msix msixbundle msixinfo msixsdk msixsdkx msixtest msrc Multifile Multimatch ncacn Nelon netstandard newid NOCASE NOCLOSEPROCESS nodiscard NOMINMAX nonexistentsetting norestart normalizednameandpublisher normalizedpackagenameandpublisher notcontains NTAPI NTSTATUS nullsoft nunit nupkg OAuth ODR opencode openxmlformats OSVERSIONINFOEXW otf OUTOFDISKSPACE OUTOFPROC packagefamilyname packageidentifier packagename PACKAGESSCHEMA paket Params params parentidx pathpart Pathto PBYTE pch PCWSTR pelikhan PEVENT pfns pfp PII pinnable pinningindex pipssource Pkcs portableindex powertoys pplx ppv PRECONFIG preindexed prepareforpackaging prepopulate prepopulated prioritization processthreadsapi PRODUCTNAME PRODUCTVERSION PROGRESSONLY promptrestart PROPERTYDUMP protseq PTOKEN publiccontainer PUCHAR pwa QCol Qspectre rclsid REBOOTPROMPT Redistributable REFCLSID removemanifest removepin removeportablefile repolibtest requeue rescap resetpins resheader resmimetype RESOLVESOURCE RESTAPI RESTARTAPPS resw resx rethrowing roadmap robuffer rowcount rowids rubengustorage ruleset runas runsettings SANITIZERS Screenshot screenshots SCROLLVIEWER seinfo SERVICEPACKMAJOR SERVICEPACKMINOR setschemaversion setupexitcodes setvariable shcore SHELLEXEC SHELLEXECUTEINFOW shlobj Shlwapi SHTDN shtypes signtool silentwithprogress Silverlight simplesave simpletest sixel sixels sln sqlbuilder sqliteicu sqliteindex sqliteindexsource sqlitewrapper srwlock STARTUPINFO STATEACTION STATFLAG STATSTG STDAPI STGM storeedgefd stpkgmanvalwestustest stringable STRINGID STRINGIZE STRSAFE subcontext SUBLANG subresource subselect substr SUPPRESSMSGBOXES SWIPECONTROL SYMED symlinktarget TARGETDIR targetentrypoint targetnametoken tdbuild tdd tempdb terabyte testcontainer testmoniker TESTPORTABLEFILE Testrun testsettingname TEXTFORMAT TEXTINCLUDE threadpool tpl TRACELOGGING triaged TRUSTEDPEOPLE UAC UACONLY uap UBool UBreak ubrk ucol UCollation UCollator UError UIF ULARGE und UNICODESTRING uninstalling Unmarshal unskipped unstash untimes updatefile updatemanifest updatepin updateportablefile UPLEVEL uregex URegular urlmon USEREXIT userguide usersources utext valijson vcpkg vcvars vcxitems VERYSILENT VFT visualstudio VOS vso wapproj wcsicmp webpage websites WHOLECHAIN wil wincrypt windbg WINEVENT winget winhttp wininet winmd winmeta winres winrt winsqlite WINTRUST wix wmain WNS woah workflows wpfn wrl WTD wtypesbase Xamarin Xbox XElement Xlang XResource XTOKEN xunit ================================================ FILE: .github/actions/spelling/candidate.patterns ================================================ # marker to ignore all code on line ^.*/\* #no-spell-check-line \*/.*$ # marker for ignoring a comment to the end of the line // #no-spell-check.*$ # marker to ignore all code on line ^.*\bno-spell-check(?:-line|)(?:\s.*|)$ # https://cspell.org/configuration/document-settings/ # cspell inline ^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b # patch hunk comments ^@@ -\d+(?:,\d+|) \+\d+(?:,\d+|) @@ .* # git index header index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40} # file permissions ['"`\s][-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s] # css url wrappings \burl\([^)]+\) # cid urls (['"])cid:.*?\g{-1} # data url in parens \(data:(?:[^) ][^)]*?|)(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\) # data url in quotes ([`'"])data:(?:[^ `'"].*?|)(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1} # data url \bdata:[-a-zA-Z=;:/0-9+]*,\S* # https/http/file urls #(?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/*%?=~_|!:,.;]+[-A-Za-z0-9+&@#/*%=~_|] # mailto urls mailto:[-a-zA-Z=;:/?%&0-9+@._]{3,} # magnet urls magnet:[?=:\w]+ # magnet urls "magnet:[^"]+" # obs: "obs:[^"]*" # The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read # In this examples content, I'm using a number of different ways to match things to show various approaches # asciinema \basciinema\.org/a/[0-9a-zA-Z]+ # asciinema v2 ^\[\d+\.\d+, "[io]", ".*"\]$ # apple \bdeveloper\.apple\.com/[-\w?=/]+ # Apple music \bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+ # appveyor api \bci\.appveyor\.com/api/projects/status/[0-9a-z]+ # appveyor project \bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+ # Amazon # Amazon \bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|) # AWS S3 \b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]* # AWS execute-api \b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b # AWS ELB \b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b # AWS SNS \bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]* # AWS VPC vpc-\w+ # While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there # YouTube url \b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]* # YouTube music \bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*) # YouTube tag <\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"] # YouTube image \bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]* # Google Accounts \baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]* # Google Analytics \bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]* # Google APIs \bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+ # Google Storage \b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|) # Google Calendar \bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+ \w+\@group\.calendar\.google\.com\b # Google DataStudio \bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|) # The leading `/` here is as opposed to the `\b` above # ... a short way to match `https://` or `http://` since most urls have one of those prefixes # Google Docs /docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|)) # Google Drive \bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]* # Google Groups \bgroups\.google\.com(?:/[a-z]+/(?:#!|)[^/\s"]+)* # Google Maps \bmaps\.google\.com/maps\?[\w&;=]* # Google themes themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+. # Google CDN \bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]* # Goo.gl /goo\.gl/[a-zA-Z0-9]+ # Google Chrome Store \bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|) # Google Books \bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]* # Google Fonts \bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]* # Google Forms \bforms\.gle/\w+ # Google Scholar \bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+ # Google Colab Research Drive \bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]* # GitHub SHAs (api) \bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b # GitHub SHAs (markdown) (?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|) # GitHub SHAs \bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b # GitHub SHA refs \[([0-9a-f]+)\]\(https://(?:www\.|)github.com/[-\w]+/[-\w]+/commit/\g{-1}[0-9a-f]* # GitHub wiki \bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b # githubusercontent /[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]* # githubassets \bgithubassets.com/[0-9a-f]+(?:[-/\w.]+) # gist github \bgist\.github\.com/[^/\s"]+/[0-9a-f]+ # git.io \bgit\.io/[0-9a-zA-Z]+ # GitHub JSON "node_id": "[-a-zA-Z=;:/0-9+_]*" # Contributor \[[^\]]+\]\(https://github\.com/[^/\s"]+/?\) # GHSA GHSA(?:-[0-9a-z]{4}){3} # GitHub actions \buses:\s+[-\w.]+/[-\w./]+@[-\w.]+ # GitLab commit \bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b # GitLab merge requests \bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b # GitLab uploads \bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]* # GitLab commits \bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b # binance accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]* # bitbucket diff \bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+ # bitbucket repositories commits \bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+ # bitbucket commits \bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+ # bit.ly \bbit\.ly/\w+ # bitrise \bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]* # bootstrapcdn.com \bbootstrapcdn\.com/[-./\w]+ # cdn.cloudflare.com \bcdnjs\.cloudflare\.com/[./\w]+ # circleci \bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+ # gitter \bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+ # gravatar \bgravatar\.com/avatar/[0-9a-f]+ # ibm [a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]* # imgur \bimgur\.com/[^.]+ # Internet Archive \barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*) # discord /discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,} # Disqus \bdisqus\.com/[-\w/%.()!?&=_]* # medium link \blink\.medium\.com/[a-zA-Z0-9]+ # medium \bmedium\.com/@?[^/\s"]+/[-\w]+ # microsoft \b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]* # powerbi \bapp\.powerbi\.com/reportEmbed/[^"' ]* # vs devops \bvisualstudio.com(?::443|)/[-\w/?=%&.]* # microsoft store \bmicrosoft\.com/store/apps/\w+ # mvnrepository.com \bmvnrepository\.com/[-0-9a-z./]+ # now.sh /[0-9a-z-.]+\.now\.sh\b # oracle \bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]* # chromatic.com /\S+.chromatic.com\S*[")] # codacy \bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+ # compai \bcompai\.pub/v1/png/[0-9a-f]+ # mailgun api \.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]* # mailgun \b[0-9a-z]+.mailgun.org # /message-id/ /message-id/[-\w@./%]+ # Reddit \breddit\.com/r/[/\w_]* # requestb.in \brequestb\.in/[0-9a-z]+ # sched \b[a-z0-9]+\.sched\.com\b # Slack url slack://[a-zA-Z0-9?&=]+ # Slack \bslack\.com/[-0-9a-zA-Z/_~?&=.]* # Slack edge \bslack-edge\.com/[-a-zA-Z0-9?&=%./]+ # Slack images \bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+ # shields.io \bshields\.io/[-\w/%?=&.:+;,]* # stackexchange -- https://stackexchange.com/feeds/sites \b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/) # Sentry [0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b # Twitter markdown \[@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\) # Twitter hashtag \btwitter\.com/hashtag/[\w?_=&]* # Twitter status \btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|) # Twitter profile images \btwimg\.com/profile_images/[_\w./]* # Twitter media \btwimg\.com/media/[-_\w./?=]* # Twitter link shortened \bt\.co/\w+ # facebook \bfburl\.com/[0-9a-z_]+ # facebook CDN \bfbcdn\.net/[\w/.,]* # facebook watch \bfb\.watch/[0-9A-Za-z]+ # dropbox \bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+ # ipfs protocol ipfs://[0-9a-zA-Z]{3,} # ipfs url /ipfs/[0-9a-zA-Z]{3,} # w3 \bw3\.org/[-0-9a-zA-Z/#.]+ # loom \bloom\.com/embed/[0-9a-f]+ # regex101 \bregex101\.com/r/[^/\s"]+/\d+ # figma \bfigma\.com/file(?:/[0-9a-zA-Z]+/)+ # freecodecamp.org \bfreecodecamp\.org/[-\w/.]+ # image.tmdb.org \bimage\.tmdb\.org/[/\w.]+ # mermaid \bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+ # Wikipedia \ben\.wikipedia\.org/wiki/[-\w%.#]+ # gitweb [^"\s]+/gitweb/\S+;h=[0-9a-f]+ # HyperKitty lists /archives/list/[^@/]+@[^/\s"]*/message/[^/\s"]*/ # lists /thread\.html/[^"\s]+ # list-management \blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+ # kubectl.kubernetes.io/last-applied-configuration "kubectl.kubernetes.io/last-applied-configuration": ".*" # pgp \bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]* # Spotify \bopen\.spotify\.com/embed/playlist/\w+ # Mastodon \bmastodon\.[-a-z.]*/(?:media/|@)[?&=0-9a-zA-Z_]* # scastie \bscastie\.scala-lang\.org/[^/]+/\w+ # images.unsplash.com \bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+ # pastebin \bpastebin\.com/[\w/]+ # heroku \b\w+\.heroku\.com/source/archive/\w+ # quip \b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)? # badgen.net \bbadgen\.net/badge/[^")\]'\s]+ # statuspage.io \w+\.statuspage\.io\b # media.giphy.com \bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+ # tinyurl \btinyurl\.com/\w+ # codepen \bcodepen\.io/[\w/]+ # registry.npmjs.org \bregistry\.npmjs\.org/(?:@[^/"']+/|)[^/"']+/-/[-\w@.]+ # getopts \bgetopts\s+(?:"[^"]+"|'[^']+') # ANSI color codes (?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m # URL escaped characters %[0-9A-F][A-F](?=[A-Za-z]) # lower URL escaped characters %[0-9a-f][a-f](?=[a-z]{2,}) # IPv6 \b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b # c99 hex digits (not the full format, just one I've seen) 0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP] # Punycode \bxn--[-0-9a-z]+ # sha sha\d+:[0-9a-f]*?[a-f]{3,}[0-9a-f]* # sha-... -- uses a fancy capture (\\?['"]|")[0-9a-f]{40,}\g{-1} # hex runs \b[0-9a-fA-F]{16,}\b # hex in url queries =[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?& # ssh (?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,} # PGP \b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b # GPG keys \b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b # Well known gpg keys .well-known/openpgpkey/[\w./]+ # pki -----BEGIN.*-----END # pki (base64) LS0tLS1CRUdJT.* # uuid: \b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b # hex digits including css/html color classes: (?:[\\0][xX]|\\u|[uU]\+|#x?|%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b # integrity integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1} # https://www.gnu.org/software/groff/manual/groff.html # man troff content \\f[BCIPR] # '/" \\\([ad]q # .desktop mime types ^MimeTypes?=.*$ # .desktop localized entries ^[A-Z][a-z]+\[[a-z]+\]=.*$ # Localized .desktop content Name\[[^\]]+\]=.* # IServiceProvider / isAThing (?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b)) # crypt (['"])\$2[ayb]\$.{56}\g{-1} # apache/old crypt (['"]|)\$+(?:apr|)1\$+.{8}\$+.{22}\g{-1} # sha1 hash \{SHA\}[-a-zA-Z=;:/0-9+]{3,} # machine learning (?) #\b(?i)ml(?=[a-z]{2,}) # python \b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,}) # scrypt / argon \$(?:scrypt|argon\d+[di]*)\$\S+ # go.sum \bh1:\S+ # scala imports ^import (?:[\w.]|\{\w*?(?:,\s*(?:\w*|\*))+\})+ # scala modules ("[^"]+"\s*%%?\s*){2,3}"[^"]+" # container images image: [-\w./:@]+ # Docker images ^\s*FROM\s+\S+:\S+(?:\s+AS\s+\S+|) # `docker images` REPOSITORY TAG IMAGE ID CREATED SIZE \s*\S+/\S+\s+\S+\s+[0-9a-f]{8,}\s+\d+\s+(?:hour|day|week)s ago\s+[\d.]+[KMGT]B # Intel intrinsics _mm_(?!dd)\w+ # Input to GitHub JSON content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1} # This does not cover multiline strings, if your repository has them, # you'll want to remove the `(?=.*?")` suffix. # The `(?=.*?")` suffix should limit the false positives rate # printf %(?:(?:(?:hh?|ll?|[jzt])?[diuoxn]|l?[cs]|L?[fega]|p)(?=[a-z]{2,})|(?:X|L?[FEGA])(?=[a-zA-Z]{2,}))(?!%)(?=[_a-zA-Z]+(?!%)\b)(?=.*?['"]) # Alternative printf # %s %(?:s(?=[a-z]{2,}))(?!%)(?=[_a-zA-Z]+(?!%[^s])\b)(?=.*?['"]) # Python string prefix / binary prefix # Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings #(?|m([|!/@#,;']).*?\g{-1}) # perl qr regex (?|\(.*?\)|([|!/@#,;']).*?\g{-1}) # perl run perl(?:\s+-[a-zA-Z]\w*)+ # C network byte conversions #(?:\d|\bh)to(?!ken)(?=[a-z])|to(?=[adhiklpun]\() # Go regular expressions regexp?\.MustCompile\(`[^`]*`\) # regex choice \(\?:[^)]+\|[^)]+\) # proto ^\s*(\w+)\s\g{-1} = # sed regular expressions sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2} # node packages (["'])@[^/'" ]+/[^/'" ]+\g{-1} # go install go install(?:\s+[a-z]+\.[-@\w/.]+)+ # pom.xml <(?:group|artifact)Id>.*?< # jetbrains schema https://youtrack.jetbrains.com/issue/RSRP-489571 urn:shemas-jetbrains-com # Debian changelog severity [-\w]+ \(.*\) (?:\w+|baseline|unstable|experimental); urgency=(?:low|medium|high|emergency|critical)\b # kubernetes pod status lists # https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase \w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+ # kubectl - pods in CrashLoopBackOff \w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+ # kubernetes applications \.apps/[-\w]+ # kubernetes object suffix -[0-9a-f]{10}-\w{5}\s # kubernetes crd patterns ^\s*pattern: .*$ # posthog secrets ([`'"])phc_[^"',]+\g{-1} # xcode # xcodeproject scenes (?:Controller|destination|ID|id)="\w{3}-\w{2}-\w{3}" # xcode api botches customObjectInstantitationMethod # msvc api botches PrependWithABINamepsace # configure flags .* \| --\w{2,}.*?(?=\w+\s\w+) # font awesome classes \.fa-[-a-z0-9]+ # bearer auth (['"])[Bb]ear[e][r] .*?\g{-1} # bearer auth \b[Bb]ear[e][r]:? [-a-zA-Z=;:/0-9+.]+ # basic auth (['"])[Bb]asic [-a-zA-Z=;:/0-9+]{3,}\g{-1} # base64 encoded content #([`'"])[-a-zA-Z=;:/0-9+]{3,}=\g{-1} # base64 encoded content in xml/sgml >[-a-zA-Z=;:/0-9+]{3,}== 0.0.22) \\\w{2,}\{ # American Mathematical Society (AMS) / Doxygen TeX/AMS # File extensions \*\.[+\w]+, # eslint "varsIgnorePattern": ".+" # nolint nolint:\w+ # Windows short paths [/\\][^/\\]{5,6}~\d{1,2}(?=[/\\]) # Windows Resources with accelerators \b[A-Z]&[a-z]+\b(?!;) # cygwin paths /cygdrive/[a-zA-Z]/(?:Program Files(?: \(.*?\)| ?)(?:/[-+.~\\/()\w ]+)*|[-+.~\\/()\w])+ # in check-spelling@v0.0.22+, printf markers aren't automatically consumed # printf markers #(?v# (?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) # Compiler flags (Unix, Java/Scala) # Use if you have things like `-Pdocker` and want to treat them as `docker` #(?:^|[\t ,>"'`=(])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) # Compiler flags (Windows / PowerShell) # This is a subset of the more general compiler flags pattern. # It avoids matching `-Path` to prevent it from being treated as `ath` #(?:^|[\t ,"'`=(])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})) # Compiler flags (linker) ,-B # libraries (?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z]) # WWNN/WWPN (NAA identifiers) \b(?:0x)?10[0-9a-f]{14}\b|\b(?:0x|3)?[25][0-9a-f]{15}\b|\b(?:0x|3)?6[0-9a-f]{31}\b # iSCSI iqn (approximate regex) \biqn\.[0-9]{4}-[0-9]{2}(?:[\.-][a-z][a-z0-9]*)*\b # curl arguments \b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)* # set arguments \b(?:bash|sh|set)(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)* # tar arguments \b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ # tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long... \btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b # macOS temp folders /var/folders/\w\w/[+\w]+/(?:T|-Caches-)/ # github runner temp folders /home/runner/work/_temp/[-_/a-z0-9]+ ================================================ FILE: .github/actions/spelling/excludes.txt ================================================ # See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes (?:^|/)(?i)COPYRIGHT (?:^|/)(?i)LICEN[CS]E (?:^|/)(?i)third[-_]?party/ (?:^|/)3rdparty/ (?:^|/)generated/ (?:^|/)go\.sum$ (?:^|/)Microsoft\.Management\.Deployment\.winmd$ (?:^|/)package(?:-lock|)\.json$ (?:^|/)Pipfile$ (?:^|/)PSGet\.Resource\.psd1$ (?:^|/)pyproject.toml (?:^|/)vendor/ (?:^|/|\b)requirements(?:-dev|-doc|-test|)\.txt$ /ExternalModules/ /TestData/ [^/]\.msi$ \.a$ \.ai$ \.all-contributorsrc$ \.avi$ \.bmp$ \.bz2$ \.cert?$|\.crt$ \.class$ \.coveragerc$ \.crl$ \.csr$ \.dll$ \.docx?$ \.drawio$ \.DS_Store$ \.eot$ \.eps$ \.exe$ \.fnt$ \.gif$ \.git-blame-ignore-revs$ \.gitattributes$ \.gitkeep$ \.graffle$ \.gz$ \.icns$ \.ico$ \.ipynb$ \.jar$ \.jks$ \.jpe?g$ \.key$ \.lib$ \.lock$ \.map$ \.min\.. \.mo$ \.mod$ \.mp[34]$ \.o$ \.ocf$ \.otf$ \.otc$ \.p12$ \.parquet$ \.pdf$ \.pem$ \.pfx$ \.png$ \.psd$ \.pyc$ \.pylintrc$ \.qm$ \.s$ \.sig$ \.sln$ \.so$ \.svgz?$ \.sys$ \.tar$ \.tgz$ \.tiff?$ \.ttc$ \.ttf$ \.vcxitems$ \.vcxproj$ \.vcxproj\.filters$ \.vdproj$ \.wapproj$ \.wav$ \.webm$ \.webp$ \.woff2?$ \.wprp$ \.xcf$ \.xlsx?$ \.xpm$ \.xz$ \.zip$ ^\.github/actions/spelling/ ^\.github/policies/ ^\.github/workflows/spelling\d*\.yml$ ^cgmanifest\.json$ ^Localization/ ^NOTICE ^src/AppInstallerCLICore/Commands/ExperimentalCommand\.cpp$ ^src/Microsoft.Management.Configuration.Processor/Microsoft.Management.Configuration.Processor.csproj$ ^src/PowerShell/Help/markdownlint\.yaml$ ^src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj$ ^src/PureLib/ ^src/SfsClient/ ^src/Xlang/ ^src/VcpkgPortOverlay/ ^src/WinGetMCPServer/WinGetMCPServer.csproj$ ignore$ ================================================ FILE: .github/actions/spelling/expect.txt ================================================ AAD ABCD abi ABORTIFHUNG accepteula ACCESSDENIED ACCESSTOKEN acl adjacents adml admx AFAIK aicli AICLIC allusers alreadyinstalled AMap Amd amrutha ansistring anyissuer Aot APARTMENTTHREADED apfn apicontract apiset appdata appinstallertest applic appname appshutdown APPTERMINATION archs ARMNT arp arphelper asan ASequence ashpatil Ashwini ASwitch ASYNCRTIMP Atest ATL auxdata awgpm awgs azurewebsites bak Baz bbb bcp BEBOM BEFACEF bfd BFirst bigcatalog BITMAPINFOHEADER bitmask BKMG bkup blargle blockedbypolicy blogs bluetooth Bomgar BOMs boop boundparms bpp brk Browsable BSODs Buf buildtransitive buildtrees cancelledbyuser casemap casemappings ccc cch centralus certmgr certs cgi cinq CLASSNOTREG CLOSEAPP cloudapp clsctx cmpwgc CMSG cnwgc CODEOWNERS codepage COINIT COMGLB commandline compressapi concurrencysal constexpr contactsupport contentfiles contosoinstaller contractversion copyable corecrt count'th countof countryregion Cov CPIL createmanifestmetadata crt cswinrt ctc CTL ctwgcy currentuser dacl datetimeoffset Dbg Dcom DCPPREST debian decompressor dedupe defaultlib DEFT deigh deleteifnotneeded deliveryoptimization deliveryoptimizationerrors DENYWR desktopappinstaller devblogs devhome DFX DHAVE dic diskfull DISPLAYCATALOG DMC dnld Dns Dobbeleer DONOT dsc dustojnikhummer dvinns dwgs dwrite DYAML ecfr ecfrbrowse EFGH ENDSESSION EQU ERANGE errcode errmsg ERRORONEXIT errstr ESRB etl evtx ewgp ewgs execustom EXEHASH experimentalfeatures XPRESS fdw fdwgp FECAFEB fedorapeople fileinuse filemode Filetime Filtercriteria Finalizers fintimes flargle flexera FOF FOLDERID FONTCHANGE FONTHASH FORPARSING foundfr fsanitize FULLMUTEX FULLWIDTH fundraiser fuzzer fzanollo gcpi GDK GES GESMBH getwinget GHS github gitlab gity goku GRPICONDIR GRPICONDIRENTRY guiddef Gumbalapura gwgc gwgcd gwgp gwgse gwgso gwgus gwgv Hackathon hashtables helplib helplibrary hhx highcontrast HINSTANCE hkey hlocal hmac hmodule Hostx Howto hre hresults hwnd hybridcrt Hyperlink IARP IAttachment ICONDIR ICONDIRENTRY ICONIMAGE icu idl IDSC idx IFACEMETHODIMP iid img inet innererror inproc Insta installinprogress INSTALLPROPERTY installshield instream insufficientmemory Intelli INTRESOURCE invalidparameter ishelp ISQ ISVs iswgp itr iwgc IWIC iwr JArray JDictionary JDK jfearn JObject jpalardy JREs jrsoftware jsoncpp JToken JValue Kaido KNOWNFOLDERID kool ktf LCID learnxinyminutes LEBOM lhs LIBYAML liv liwpx localizationpriority localsource Logon LONGLONG LOWORD LPARAM LPBYTE LPCWSTR lpdw LPDWORD lpfn LPGRPICONDIR LPGRPICONDIRENTRY LPICONDIR LPICONDIRENTRY LPICONIMAGE lpitemidlist LPSTR lpsz LPVOID LPW LPWCH LPWSTR LRESULT LSTATUS LTDA luffy Luffytaro maclachlan Madhusudhan MAJORVERSION malware mapdatafolding Maxed MAXLENGTH maxvalue maybenull MBH MBs mcr mday mdmp mdmpto MDs megamorf microsoftentraid microsoftentraidforazureblobstorage midl minidump MINORVERSION missingdependency mkgmtime MMmmbbbb MODULEENTRY mof monicka MPNS msdownload msft msftrubengu MSIHASH MSIXHASH MSIXSTRM msstore MSZIP mszyml mta Mugiwara Multideclaration mysource nativehandle NBLGGH ncreate NESTEDINSTALLER netlify NETSDK Newtonsoft nlohmann NNS NOAGGREGATION NOCLOSE NOCRLF NOEXPAND NOLINKINFO nomem NONAME nonetwork NONFOLDERS nonterminated NOREMOVE normer NOSEARCH notalostreference NOTIMPL NOTRACK NOUPDATE nowarn npmjs nsis NTFS objbase objidl octokitnet ofile oid omus onefuzz oop opensource OPTOUT osfhandle oss outfile OUTOFMEMORY Outptr OVERLAPPEDWINDOW OWC packageinuse packageinusebyapplication PACL PARAMETERMAP pathparts pathtree Patil pbstr pcb PCCERT PCs pcwsz pdb PDWORD peetdev PEGI pfn pgp Pherson pid pidl pidlist PKCS pkgmgr pkindex pkix placeholders PMS positionals posix postuninstall powershellgallery PPROCESS pri PRIMARYKEY processthreads productcode PRODUCTICON propkey PROPVARIANT proxystub psapi pscustomobject pseudocode PSHOST psobject psz ptstr publickey PVD pvk pvm pwabuilder PWAs PWSTR pwsz QUERYENDSESSION qword RAII ranm rebootinitiated rebootrequiredforinstall rebootrequiredtofinish redirector Redist REFIID REGDB regexes REGSAM remoting removefile reparse repeatedkey REQS requirenonleaf restsource RGBQUAD rgp rgpsz rhs riid roapi Roblox ronomon rowid roy rpwgpm RRF rrr rswgs RTTI runspace runtimeclass runtimes rwgch rwgs ryfu sacl sancov SARL savepoint sawgc schematab Scm sddl secureobject securestring seekp seof servercert servercertificate setmetadatabymanifestid SETTINGCHANGE SETTINGMAPPING sfs sfsclient SHCONTF shellapi SHGDN SHOWNORMAL sid Sideload SIGNATUREHASH silentpreferred SINGLETHREADED Skipx sku SLAPI SMTO SNAME SNAPMODULE SNAPTHREAD sortof sourceforge SOURCESDIRECTORY sourceversion spamming SPAPI spwgc sqlite Srinivasan SRL srs STDMETHODCALLTYPE storeapps storeorigin STRRET stylecop subdir subkey Sudarshan superstring swgus SYD SYG systemnotsupported Tagit TARG taskhostw tcreate tcs tellp temppath testexampleinstaller thiscouldbeapc THREADENTRY threehundred timespan Tlg tlhelp TLSCAs tombstoned Toolhelp transitioning trimstart ttl twgc twgus typedef UCase ucasemap UChars ucnv ucrt udwgp uec ULONGLONG UNAVAIL uninitialize unins uninstallation uninstaller uninstallprevious uninstalls unknwn Unknwnbase UNMARSHALING unparsable Unregisters unvirtualized UParse upgradable upgradecode URLZONE USEDEFAULT userfilesetting userprofile uswgp uwp VALUENAMECASE vclib versioned VERSIONINFO vns vsconfig vstest waitable wal wcex WDAG webpages Webserver website wesome wfsopen wgetenv WHATIF WIC wildcards WINAPI wincodec windir windowsdeveloper winerror winevt wingdi wingetconfigroot wingetcreate wingetdev wingetutil winreg winrtact winstring WMI wmmc wnd WNDCLASS WNDCLASSEX workaround WPARAM Wpp wpr wprp wputenv wsb wsl wsv wto wwinmain WZDNCRFJ xcopy Xes XFile XManifest XMUGIWARAMODULE XName XPLATSTR XRESOURCEZORO xsi yamato yao Zanollo ZIPHASH zoro ================================================ FILE: .github/actions/spelling/line_forbidden.patterns ================================================ # reject `m_data` as VxWorks defined it and that breaks things if it's used elsewhere # see [fprime](https://github.com/nasa/fprime/commit/d589f0a25c59ea9a800d851ea84c2f5df02fb529) # and [Qt](https://github.com/qtproject/qt-solutions/blame/fb7bc42bfcc578ff3fa3b9ca21a41e96eb37c1c7/qtscriptclassic/src/qscriptbuffer_p.h#L46) #\bm_data\b # Were you debugging using a framework with `fit()`? # If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test, # you might not want to check in code where you skip all the other tests. #\bfit\( # Should be `HH:MM:SS` \bHH:SS:MM\b # Should be `86400` (seconds in a standard day) \b84600\b(?:.*\bday\b) # Should probably be `2006-01-02` (yyyy-mm-dd) # Assuming that the time is being passed to https://go.dev/src/time/format.go \b2006-02-01\b # Should probably be `YYYYMMDD` \b[Yy]{4}[Dd]{2}[Mm]{2}(?!.*[Yy]{4}[Dd]{2}[Mm]{2}).*$ # Should be `a priori` or `and prior` (?i)(? Don't use `can not` when you mean `cannot`. The only time you're likely to see `can not` written as separate words is when the word `can` happens to precede some other phrase that happens to start with `not`. # > `Can't` is a contraction of `cannot`, and it's best suited for informal writing. # > In formal writing and where contractions are frowned upon, use `cannot`. # > It is possible to write `can not`, but you generally find it only as part of some other construction, such as `not only . . . but also.` # - if you encounter such a case, add a pattern for that case to patterns.txt. \b[Cc]an not\b # Do not use `(click) here` links # For more information, see: # * https://www.w3.org/QA/Tips/noClickHere # * https://webaim.org/techniques/hypertext/link_text # * https://granicus.com/blog/why-click-here-links-are-bad/ # * https://heyoka.medium.com/dont-use-click-here-f32f445d1021 (?:>|\[)(?:(?:click |)here|link|(?:read |)more)(?:) # Should be `equals` to `is equal to` \bequals to\b # Should be `GitHub` (?v# (?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) # hit-count: 171 file-count: 25 # hex digits including css/html color classes: (?:[\\0][xX]|\\u|[uU]\+|#x?|%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b # hit-count: 157 file-count: 75 # GitHub SHAs (markdown) (?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|) # hit-count: 46 file-count: 17 # base64 encoded pkcs \bMII[-a-zA-Z=;:/0-9+]+ # hit-count: 35 file-count: 7 # libraries (?:\b|_)lib(?:re(?=office)|)(?!elous|era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z]) # hit-count: 14 file-count: 7 # w3 \bw3\.org/[-0-9a-zA-Z/#.]+ # hit-count: 13 file-count: 6 # Alternatively, if you're using check-spelling v0.0.25+, and you would like to _check_ the Non-English content for spelling errors, you can. For information on how to do so, see: # https://docs.check-spelling.dev/Feature:-Configurable-word-characters.html#unicode [a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,} # hit-count: 10 file-count: 3 # URL escaped characters %[0-9A-F][A-F](?=[A-Za-z]) # hit-count: 7 file-count: 3 # Contributor \[[^\]]+\]\(https://github\.com/[^/\s"]+/?\) # hit-count: 6 file-count: 4 # Markdown anchor links \(#\S*?[a-zA-Z]\S*?\) # hit-count: 4 file-count: 4 # python \b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,}) # hit-count: 3 file-count: 3 # mailto urls mailto:[-a-zA-Z=;:/?%&0-9+@._]{3,} # hit-count: 3 file-count: 3 # githubusercontent /[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]* # hit-count: 1 file-count: 1 # hex in url queries =[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?& E2E # first-letter-bracketed-remainder \b\w\[[A-Za-z]{3,}\] # Questionably acceptable forms of `in to` # Personally, I prefer `log into`, but people object # https://www.tprteaching.com/log-into-log-in-to-login/ \b(?:(?:[Ll]og(?:g(?=[a-z])|)|[Ss]ign)(?:ed|ing)?) in to\b # to opt in \bto opt in\b # acceptable duplicates # ls directory listings [-bcdlpsw](?:[-r][-w][-SsTtx]){3}[\.+*]?\s+\d+\s+\S+\s+\S+\s+[.\d]+(?:[KMGT]|)\s+ # mount \bmount\s+-t\s+(\w+)\s+\g{-1}\b # C types and repeated CSS values \s(Architecture|auto|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?: \g{-1})+\s # C enum and struct \b(?:enum|struct)\s+(\w+)\s+\g{-1}\b # go templates \s(\w+)\s+\g{-1}\s+\`(?:graphql|inject|json|yaml): # doxygen / javadoc / .net (?:[\\@](?:brief|defgroup|groupname|link|t?param|return|retval)|(?:public|private|\[Parameter(?:\(.+\)|)\])(?:\s+(?:static|override|readonly|required|virtual))*)(?:\s+\{\w+\}|)\s+(\w+)\s+\g{-1}\s # macOS file path (?:Contents\W+|(?!iOS)/)MacOS\b # Python package registry has incorrect spelling for macOS / Mac OS X "Operating System :: MacOS :: MacOS X" # "company" in Germany \bGmbH\b # IntelliJ \bIntelliJ\b # Commit message -- Signed-off-by and friends ^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$ # Autogenerated revert commit message ^This reverts commit [0-9a-f]{40}\.$ # ignore long runs of a single character: \b([A-Za-z])\g{-1}{3,}\b # devil fruits \s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\sno Mi # PowerShell Aliases defined in .cs files (not those in AliasesToExport in psd1 files) \[Alias\("[a-z]+"\)\] ================================================ FILE: .github/actions/spelling/reject.txt ================================================ ^attache$ ^bellow$ benefitting occurences? ^dependan.* ^diables?$ ^oer$ Sorce ^[Ss]pae.* ^Teh$ ^untill$ ^untilling$ ^wether.* ================================================ FILE: .github/copilot-instructions.md ================================================ # WinGet CLI Development Guide ## Project Overview This is the Windows Package Manager (WinGet) CLI client - a native Windows application for discovering and installing packages. The codebase consists of: - **C++/WinRT client** (`src/AppInstallerCLI*`) - The main CLI and core logic - **COM API** (`src/Microsoft.Management.Deployment`) - Public Windows Runtime API for programmatic access - **PowerShell modules** (`src/PowerShell`) - Microsoft.WinGet.Client and Microsoft.WinGet.Configuration cmdlets - **Configuration system** - DSC-based system configuration using WinGet ## Building, Testing, and Running ### Initial Setup Use a configuration file in `.config` as in `winget configure .config/configuration.winget` (alternatives provided for other VS SKUs). Manual steps: 1. Install Visual Studio 2022 with required workloads (see `.vsconfig`) 2. Install Windows SDK 10.0.26100: `winget install Microsoft.WindowsSDK.10.0.26100` 3. Enable developer mode in Windows 4. Run `vcpkg integrate install` from Developer Command Prompt ### Building Open `src\AppInstallerCLI.sln` in Visual Studio and build the solution (Ctrl+Shift+B) or use msbuild.exe to build from the command line. The solution uses: - MSBuild - vcpkg for C++ dependencies - NuGet for C++ and .NET dependencies ### Running/Debugging 1. Deploy solution: Build > Deploy Solution 2. Run from command line: `wingetdev` 3. For debugging: - Right-click `AppInstallerCLIPackage` > Properties > Debug tab - Set Debugger type to "Native Only" for both Application and Background task processes - Select "Do not launch, but debug my code when it starts" - Press F5 and run `wingetdev` in a separate terminal Entry point: `src/AppInstallerCLI/main.cpp` ### Testing #### C++ Unit Tests (Catch2) Located in `AppInstallerCLITests` project. After building: ```powershell # Run all tests src\\\AppInstallerCLITests\AppInstallerCLITests.exe # Run specific test src\\\AppInstallerCLITests\AppInstallerCLITests.exe TestName # Available options AppInstallerCLITests.exe --help ``` #### .NET Tests - `Microsoft.WinGet.UnitTests` - PowerShell module tests - `Microsoft.Management.Configuration.UnitTests` - Configuration system tests - `WinGetUtilInterop.UnitTests` - Interop layer tests #### E2E Tests `AppInstallerCLIE2ETests` project contains end-to-end integration tests. ## Architecture ### Core Components **AppInstallerCLICore** - Core CLI logic organized around: - **ExecutionContext**: State container that flows through workflows. Contains arguments, reporter, flags, and data (ExecutionContextData.h) - **Workflows**: Composable functions that take ExecutionContext and perform operations (e.g., InstallFlow, UpdateFlow, SearchFlow) - **Commands**: Parse arguments and orchestrate workflows - **Reporter**: Handles all user output (ExecutionReporter.h) **AppInstallerRepositoryCore** - Package source abstraction: - Interfaces for different source types (REST, SQLite index, Microsoft Store, composite) - Search, match, and correlation logic - Package version selection and dependencies **AppInstallerCommonCore** - Shared utilities: - Manifest parsing (YAML/JSON) - Settings and group policy - Telemetry and logging - HTTP client, downloader, archive handling **Microsoft.Management.Deployment** - COM API surface: - IDL definitions in `PackageManager.idl` - WinRT projections for external consumption - Used by PowerShell modules and third-party integrations **AppInstallerCLIPackage** - Dev MSIX package definition: - Models the release package definition as closely as possible. - Contains localized string resources at src\AppInstallerCLIPackage\Shared\Strings\en-us\winget.resw ### Key Patterns **Workflow Pattern**: Functions that operate on ExecutionContext: ```cpp void WorkflowTask(Execution::Context& context) { // Check if already terminated AICLI_RETURN_IF_TERMINATED(context); // Access data auto& data = context.Get(); // Report to user context.Reporter.Info() << "Doing something"; // Store data for next workflow context.Add(result); // Terminate on error if (failed) { AICLI_TERMINATE_CONTEXT(HRESULT); } } ``` **Source Composition**: Multiple package sources can be composed: - CompositeSource combines multiple sources with conflict resolution - Installed source tracks locally installed packages - Available sources provide packages to install **Manifest Schema**: Package manifests use versioned YAML schemas: - Schema definitions in `schemas/JSON/manifests/` - Parsing in `AppInstallerCommonCore/Manifest/` - Multi-file manifests: installer, locale, version, defaultLocale ## Naming Conventions - **Namespace structure**: `AppInstaller::[::]` - `AppInstaller::CLI::Execution` - CLI execution context - `AppInstaller::CLI::Workflow` - Workflow functions - `AppInstaller::Repository` - Repository/source logic - `AppInstaller::Manifest` - Manifest types - `AppInstaller::Settings` - User/admin settings - **Macros**: Prefixed with `AICLI_` for CLI, `WINGET_` for general - **Data keys**: ExecutionContextData uses enum keys to type-safely store/retrieve data ## Windows-Specific Considerations - Use Windows-style paths with backslashes (`\`) - Leverage WinRT APIs via C++/WinRT projections - COM threading models matter - client uses multi-threaded apartment (MTA) - Package deployment uses Windows App SDK / MSIX infrastructure - Requires Windows 10 1809+ (build 17763) ## Contributing - Review `CONTRIBUTING.md` for workflow - File/discuss issues before starting work - Specs required for features (stored in `doc/specs/`) - Follow existing code style (see `stylecop.json`) - CI runs on Azure Pipelines (`azure-pipelines.yml`) ## Useful Commands ```powershell # Get WinGet client info wingetdev --info # Show experimental features wingetdev features # Check sources wingetdev source list ``` ================================================ FILE: .github/images/README.md ================================================ This directory is intended to host images for use in GitHub markdown files. ================================================ FILE: .github/policies/automergeTriggers.yml ================================================ id: automergeTriggers name: GitOps.PullRequestIssueManagement description: Handles enabling / disabling automerge owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: eventResponderTasks: - description: Enable Auto Merge when the "AutoMerge" label is present if: - payloadType: Pull_Request - hasLabel: label: AutoMerge then: - enableAutoMerge: mergeMethod: Squash - description: Disable Auto Merge when the "AutoMerge" label is not present if: - payloadType: Pull_Request - or: - labelRemoved: label: AutoMerge - not: hasLabel: label: AutoMerge then: - disableAutoMerge onFailure: onSuccess: ================================================ FILE: .github/policies/labelAdded.noRecentActivity.yml ================================================ id: labelAdded.noRecentActivity name: GitOps.PullRequestIssueManagement description: Handlers when "No-Recent-Activity" label is added owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: eventResponderTasks: - description: >- When the label "No-Recent-Activity" is added to a pull request * Add the PR specific reply notifying the issue author of pending closure if: - payloadType: Pull_Request - labelAdded: label: No-Recent-Activity then: - addReply: reply: >- Hello @${issueAuthor}, This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **7 days**. It will be closed if no further activity occurs **within 7 days of this comment**. Template: msftbot/noRecentActivity # The policy service should trigger even when the label was added by the policy service triggerOnOwnActions: true - description: >- When the label "No-Recent-Activity" is added to an issue * Add the issue specific reply notifying the issue author of pending closure if: - payloadType: Issues - labelAdded: label: No-Recent-Activity then: - addReply: reply: >- Hello @${issueAuthor}, This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **7 days**. It will be closed if no further activity occurs **within 7 days of this comment**. Template: msftbot/noRecentActivity # The policy service should trigger even when the label was added by the policy service triggerOnOwnActions: true onFailure: onSuccess: ================================================ FILE: .github/policies/labelManagement.issueClosed.yml ================================================ id: labelManagement.issueClosed name: GitOps.PullRequestIssueManagement description: Handlers when an issue gets closed owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: eventResponderTasks: - description: Remove labels when an issue is closed if: - payloadType: Issues - isAction: action: Closed then: - removeLabel: label: Needs-Triage - removeLabel: label: Needs-Attention - removeLabel: label: Needs-Author-Feedback - removeLabel: label: Help-Wanted ## TODO: Unassign author - description: Remove labels when a pull request is closed if: - payloadType: Pull_Request - isAction: action: Closed then: - removeLabel: label: Needs-Attention - removeLabel: label: Needs-Author-Feedback ## TODO: Unassign Users onFailure: onSuccess: ================================================ FILE: .github/policies/labelManagement.issueOpened.yml ================================================ id: labelManagement.issueOpened name: GitOps.PullRequestIssueManagement description: Handlers for when an issue is first opened owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: eventResponderTasks: - description: Add CodeFlow link to new PRs if: - payloadType: Pull_Request - isAction: action: Opened then: - addCodeFlowLink - description: Add Needs-Triage to new issues if: - payloadType: Issues - isAction: action: Opened then: - addLabel: label: Needs-Triage ### Area Labels - description: Add area label for PowerShell Module if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant area\(s\)\s*\n.*PowerShell Module' isRegex: True then: - addLabel: label: PowerShell - description: Add area label for COM API if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant area\(s\)\s*\n.*COM API' isRegex: True then: - addLabel: label: Area-COM-API - description: Add area label for WinGet DSC Resources if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant area\(s\)\s*\n.*DSC Resource' isRegex: True then: - addLabel: label: DSC-Resource ### Commands Labels - description: Add command label for 'winget configure' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget configure' isRegex: True then: - addLabel: label: Command-Configure - description: Add command label for 'winget download' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget download' isRegex: True then: - addLabel: label: Command-Download - description: Add command label for 'winget dscv3' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget dscv3' isRegex: True then: - addLabel: label: Command-DSCv3 - description: Add command label for 'winget export' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget export' isRegex: True then: - addLabel: label: Command-Export - description: Add command label for 'winget features' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget features' isRegex: True then: - addLabel: label: Command-Features - description: Add command label for 'winget font' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget font' isRegex: True then: - addLabel: label: Command-Font - description: Add command label for 'winget hash' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget hash' isRegex: True then: - addLabel: label: Command-Hash - description: Add command label for 'winget import' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget import' isRegex: True then: - addLabel: label: Command-Import - description: Add command label for 'winget install' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget install' isRegex: True then: - addLabel: label: Command-Install - description: Add command label for 'winget list' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget list' isRegex: True then: - addLabel: label: Command-List - description: Add command label for 'winget mcp' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget mcp' isRegex: True then: - addLabel: label: Command-MCP - description: Add command label for 'winget pin' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget pin' isRegex: True then: - addLabel: label: Command-Pin - description: Add command label for 'winget repair' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget repair' isRegex: True then: - addLabel: label: Command-Repair - description: Add command label for 'winget search' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget search' isRegex: True then: - addLabel: label: Command-Search - description: Add command label for 'winget settings' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget settings' isRegex: True then: - addLabel: label: Command-Settings - description: Add command label for 'winget show' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget show' isRegex: True then: - addLabel: label: Command-Show - description: Add command label for 'winget source' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget source' isRegex: True then: - addLabel: label: Command-Source - description: Add command label for 'winget uninstall' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget uninstall' isRegex: True then: - addLabel: label: Command-Uninstall - description: Add command label for 'winget upgrade' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget upgrade' isRegex: True then: - addLabel: label: Command-Upgrade - description: Add command label for 'winget validate' if: - payloadType: Issues - isAction: action: Opened - bodyContains: pattern: 'Relevant command\(s\)\s*\n.*winget validate' isRegex: True then: - addLabel: label: Command-Validate onFailure: onSuccess: ================================================ FILE: .github/policies/labelManagement.issueUpdated.yml ================================================ id: labelManagement.issueUpdated name: GitOps.PullRequestIssueManagement description: >- Handlers for when an issue is updated and not closed This primarily includes handlers for comments, reviews, and re-runs owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: eventResponderTasks: - description: Remove "No-Recent-Activity" when a pull request or issue is updated if: - or: - payloadType: Pull_Request - payloadType: Pull_Request_Review - payloadType: Pull_Request_Review_Comment - payloadType: Issue_Comment - payloadType: Issues - not: isAction: action: Closed - hasLabel: label: No-Recent-Activity then: - removeLabel: label: No-Recent-Activity # The policy service should not trigger itself here, or else the label would be removed immediately after being added triggerOnOwnActions: False - description: Clean email replies on every comment if: - payloadType: Issue_Comment then: - cleanEmailReply - description: Remove "Help-Wanted" label when an issue goes into PR if: - payloadType: Issues - labelAdded: label: In-PR - hasLabel: label: Help-Wanted then: - removeLabel: label: Help-Wanted - description: >- If an author responds to an issue which needs author feedback * Remove the Needs-Author-Feedback Label * Add the Needs-Attention Label if: - or: - payloadType: Pull_Request_Review - payloadType: Pull_Request_Review_Comment - payloadType: Issue_Comment - isActivitySender: issueAuthor: True - hasLabel: label: Needs-Author-Feedback - not: isAction: action: Synchronize then: - removeLabel: label: Needs-Author-Feedback - addLabel: label: Needs-Attention - description: >- When changes are requested on a pull request * Disable automerge * Assign to the author * Label with Needs-Author-Feedback if: - payloadType: Pull_Request_Review - isAction: action: Submitted - isReviewState: reviewState: Changes_requested then: - disableAutoMerge - assignTo: author: True - addLabel: label: Needs-Author-Feedback - description: Sync labels from issues on all pull request events if: - payloadType: Pull_Request then: - labelSync: pattern: Issue- - labelSync: pattern: Area- - labelSync: pattern: Priority- - labelSync: pattern: Product- - labelSync: pattern: Severity- - labelSync: pattern: Impact- - inPrLabel: label: In-PR onFailure: onSuccess: ================================================ FILE: .github/policies/labelManagement.needsFeedbackHub.yml ================================================ id: labelManagement.needsFeedbackHub name: GitOps.PullRequestIssueManagement description: Handlers when feedback hub is needed owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: eventResponderTasks: - description: >- When the label "Needs-Feedback-Hub" is added to an issue or a repo admin comments /feedback * Add a reply notifying the issue author * Assign to the author * Label with Needs-Author-Feedback * Remove Needs-Feedback-Hub label if: - or: # Trigger by label add - and: - payloadType: Issues - labelAdded: label: Needs-Feedback-Hub # Trigger by comment - and: - payloadType: Issue_Comment - commentContains: pattern: '\/feedback' isRegex: True - or: - activitySenderHasPermission: permission: Admin - activitySenderHasPermission: permission: Write then: - addReply: reply: >- Hello @${issueAuthor}, Please send us feedback with the Feedback Hub [Windows]+[f] with this issue and paste the link here so we can more easily find your crash information on the back end. Please use "Apps" and "Windows Package Manager" for the Category. The link on the bottom of the feedback report will provide the URL to paste in this Issue to share with us. Template: msftbot/feedbackHub - assignTo: author: True - addLabel: label: Needs-Author-Feedback - removeLabel: label: Needs-Feedback-Hub onFailure: onSuccess: ================================================ FILE: .github/policies/labelManagement.triageLabels.yml ================================================ id: labelAdded.triageLabels name: GitOps.PullRequestIssueManagement description: Handlers for triaging issues when various labels are applied from Triage owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: eventResponderTasks: - description: >- When specific labels are added to an issue * Remove the Needs-Triage label if: - payloadType: Issues - or: - labelAdded: label: Area-Accessibility - labelAdded: label: Area-Architecture - labelAdded: label: Area-Build - labelAdded: label: Area-External - labelAdded: label: Area-GPO - labelAdded: label: Area-Input - labelAdded: label: Area-Localization - labelAdded: label: Area-Manifest - labelAdded: label: Area-Matching - labelAdded: label: Area-Output - labelAdded: label: Area-Path - labelAdded: label: Area-Performance - labelAdded: label: Area-Scope - labelAdded: label: Area-Settings - labelAdded: label: Area-Sorting - labelAdded: label: Area-User-Interface - labelAdded: label: Blocking-Issue - labelAdded: label: Breaking-Change - labelAdded: label: Dependencies - labelAdded: label: Experimental - labelAdded: label: Hardware - labelAdded: label: In-PR - labelAdded: label: Interactive-Only-Installer - labelAdded: label: msstore - labelAdded: label: Needs-Attention - labelAdded: label: Needs-Author-Feedback - labelAdded: label: Portable - labelAdded: label: Public-Service-Announcement - labelAdded: label: Side-By-Side - labelAdded: label: Zipped-Binary then: - removeLabel: label: Needs-Triage # The policy service should trigger even when the label was added by the policy service triggerOnOwnActions: true onFailure: onSuccess: ================================================ FILE: .github/policies/moderatorTriggers.yml ================================================ id: moderatorTriggers name: GitOps.PullRequestIssueManagement description: Defines the users and permissions for the moderators owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: eventResponderTasks: - if: # If the activity sender is any one of the moderators, has Admin permission on the repo, or has Write permissions on the repo. . . - or: - activitySenderHasPermission: permission: Admin - activitySenderHasPermission: permission: Write - isActivitySender: user: stephengillie issueAuthor: False - isActivitySender: user: ImJoakim issueAuthor: False - isActivitySender: user: ItzLevvie issueAuthor: False - isActivitySender: user: jedieaston issueAuthor: False - isActivitySender: user: KaranKad issueAuthor: False - isActivitySender: user: OfficialEsco issueAuthor: False - isActivitySender: user: quhxl issueAuthor: False - isActivitySender: user: Trenly issueAuthor: False - isActivitySender: user: mdanish-kh issueAuthor: False - isActivitySender: user: russellbanks issueAuthor: False then: # If the payload is an issue_Comment or a Pull_Request_Review_Comment - if: - or: - payloadType: Issue_Comment - payloadType: Pull_Request_Review_Comment # Remove the Needs-Triage label # Take different actions based on the comment pattern then: - removeLabel: label: Needs-Triage # Area-Accessibility - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][aA]ccessibility' isRegex: True then: - addLabel: label: Area-Accessibility # Area-Architecture - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][aA]rchitecture' isRegex: True then: - addLabel: label: Area-Architecture # Area-Build - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][bB]uild' isRegex: True then: - addLabel: label: Area-Build # Area-COM-API - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][cC][oO][mM][\s-][aA][pP][iI]' isRegex: True then: - addLabel: label: Area-COM-API # Area-External - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][eE]xternal' isRegex: True then: - addLabel: label: Area-External # Area-GPO - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][gG][pP][oO]' isRegex: True then: - addLabel: label: Area-GPO # Area-Input - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][iI]nput' isRegex: True then: - addLabel: label: Area-Input # Area-Localization - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][lL]ocalization' isRegex: True then: - addLabel: label: Area-Localization # Area-Manifest - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][mM]anifest' isRegex: True then: - addLabel: label: Area-Manifest # Area-Matching - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][mM]atching' isRegex: True then: - addLabel: label: Area-Matching # Area-Output - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][oO]utput' isRegex: True then: - addLabel: label: Area-Output # Area-Path - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][pP]ath' isRegex: True then: - addLabel: label: Area-Path # Area-Performance - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][pP]erformance' isRegex: True then: - addLabel: label: Area-Performance # Area-Scope - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][sS]cope' isRegex: True then: - addLabel: label: Area-Scope # Area-Settings - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][sS]ettings' isRegex: True then: - addLabel: label: Area-Settings # Area-Sorting - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][sS]orting' isRegex: True then: - addLabel: label: Area-Sorting # Area-User-Interface - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[aA]rea[\s-][uU]ser[\s-][iI]nterface' isRegex: True then: - addLabel: label: Area-User-Interface # Blocking-Issue - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[bB]locking[\s-][iI]ssue' isRegex: True then: - removeLabel: label: Needs-Author-Feedback - removeLabel: label: Needs-Attention - addLabel: label: Blocking-Issue # Breaking-Change - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[bB]reaking[\s-][cC]hange' isRegex: True then: - addLabel: label: Breaking-Change # Command-Configure - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][cC]onfigure' isRegex: True then: - addLabel: label: Command-Configure # Command-Download - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][dD]ownload' isRegex: True then: - addLabel: label: Command-Download # Command-Export - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][eE]xport' isRegex: True then: - addLabel: label: Command-Export # Command-Import - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][iI]mport' isRegex: True then: - addLabel: label: Command-Import # Command-Install - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][iI]nstall' isRegex: True then: - addLabel: label: Command-Install # Command-List - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][lL]ist' isRegex: True then: - addLabel: label: Command-List # Command-Pin - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][pP]in' isRegex: True then: - addLabel: label: Command-Pin # Command-Search - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][sS]earch' isRegex: True then: - addLabel: label: Command-Search # Command-Show - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][sS]how' isRegex: True then: - addLabel: label: Command-Show # Command-Source - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][sS]ource' isRegex: True then: - addLabel: label: Command-Source # Command-Uninstall - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][uU]ninstall' isRegex: True then: - addLabel: label: Command-Uninstall # Command-Upgrade - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][uU]pgrade' isRegex: True then: - addLabel: label: Command-Upgrade # Command-Validate - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ommand[\s-][vV]alidate' isRegex: True then: - addLabel: label: Command-Validate # Context-Elevated-User - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ontext[\s-][eE]levated([\s-][uU]ser)?' isRegex: True then: - addLabel: label: Context-Elevated-User # Context-User - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ontext[\s-][uU]ser' isRegex: True then: - addLabel: label: Context-User # Context-System - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[cC]ontext[\s-][sS]ystem' isRegex: True then: - addLabel: label: Context-System # Dependencies - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[dD]ependencies' isRegex: True then: - addLabel: label: Dependencies # DSC-Resource - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[dD][sS][cC][\s-][rR]esource' isRegex: True then: - addLabel: label: DSC-Resource # Experimental - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[eE]xperimental' isRegex: True then: - addLabel: label: Experimental # Hardware - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[hH]ardware' isRegex: True then: - addLabel: label: Hardware # Interactive-Only-Installer - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[iI]nteractive[\s-][oO]nly([\s-][iI]nstaller)?' isRegex: True then: - addLabel: label: Interactive-Only-Installer # Issue-Bug - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[iI]ssue[\s-][bB]ug' isRegex: True then: - addLabel: label: Issue-Bug # Issue-Docs - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[iI]ssue[\s-][dD]ocs' isRegex: True then: - addLabel: label: Issue-Docs # Issue-Feature - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[iI]ssue[\s-][fF]eature' isRegex: True then: - addLabel: label: Issue-Feature # msstore - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[mM][sS][sS]tore' isRegex: True then: - addLabel: label: msstore # Needs-Attention - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[Nn]eeds[\s-][Aa]ttention' isRegex: True then: - addLabel: label: Needs-Attention # Needs-Author-Feedback - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[Nn]eeds[\s-][Aa]uthor[\s-][fF]eedback' isRegex: True then: - addLabel: label: Needs-Author-Feedback # Needs-Repro - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[Nn]eeds[\s-][Rr]epro' isRegex: True then: - addLabel: label: Needs-Repro # Portable - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[pP]ortable' isRegex: True then: - addLabel: label: Portable # PowerShell - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[pP]ower[sS]hell' isRegex: True then: - addLabel: label: PowerShell # PSA - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[Pp][Ss][Aa]' isRegex: True then: - addLabel: label: Public-Service-Announcement # Side-By-Side - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[sS]ide[\s-][bB]y[\s-][sS]ide' isRegex: True then: - addLabel: label: Side-By-Side # Zipped-Binary - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[zZ]ip(ped)?[\s-][bB]inary' isRegex: True then: - addLabel: label: Zipped-Binary # Zipped-Binary - if: - commentContains: pattern: '\[[Pp]olicy\]\s+([wW]indows[\s-])?[sS]andbox' isRegex: True then: - addLabel: label: Windows-Sandbox # Unblocked - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[uU]nblocked' isRegex: True then: - removeLabel: label: Blocking-Issue # Duplicate of # - if: - commentContains: pattern: Duplicate\s+of\s+\#?\s*\d+ isRegex: True then: - addReply: reply: >- Hello @${issueAuthor}, We've identified this as a duplicate of another issue or PR that already exists. This specific instance is being closed in favor of the linked issue. Please add your 👍 to the other issue to raise its priority. Thanks for your contribution! Template: msftbot/duplicate/closed - closeIssue - removeLabel: label: Needs-Triage - removeLabel: label: Needs-Attention - removeLabel: label: Needs-Feedback-Hub - removeLabel: label: Needs-Author-Feedback - addLabel: label: Resolution-Duplicate # Close with reason <>; - if: - commentContains: pattern: "[cC]lose\\s+[wW]ith\\s+[rR]eason\\s*:[\\w\\s\\-\\(\\)\\[\\]\\{\\}\\\\\\/.+=@\\#$%&^*`~|'\",<>?]*(?=;)" isRegex: True then: - closeIssue - removeLabel: label: Needs-Triage - removeLabel: label: Needs-Attention - removeLabel: label: Needs-Feedback-Hub - removeLabel: label: Needs-Author-Feedback # Reopen with reason <>; - if: - commentContains: pattern: "[rR]eopen\\s+[wW]ith\\s+[rR]eason\\s*:[\\w\\s\\-\\(\\)\\[\\]\\{\\}\\\\\\/.+=@\\#$%&^*`~|'\",<>?]*(?=;)" isRegex: True then: - reopenIssue - removeLabel: label: Resolution-Duplicate - removeLabel: label: No-Recent-Activity # Reset-Feedback - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[rR]eset\s+[fF]eedback' isRegex: True then: - removeLabel: label: Needs-Author-Feedback - removeLabel: label: Needs-Attention # Reset-Labels - if: - commentContains: pattern: '\[[Pp]olicy\]\s+[rR]eset\s+[lL]abels' isRegex: True then: - removeLabel: label: Area-Accessibility - removeLabel: label: Area-Architecture - removeLabel: label: Area-Build - removeLabel: label: Area-COM-API - removeLabel: label: Area-External - removeLabel: label: Area-GPO - removeLabel: label: Area-Input - removeLabel: label: Area-Localization - removeLabel: label: Area-Manifest - removeLabel: label: Area-Matching - removeLabel: label: Area-Output - removeLabel: label: Area-Path - removeLabel: label: Area-Performance - removeLabel: label: Area-Scope - removeLabel: label: Area-Settings - removeLabel: label: Area-Sorting - removeLabel: label: Area-User-Interface - removeLabel: label: Blocking-Issue - removeLabel: label: Breaking-Change - removeLabel: label: Command-Configure - removeLabel: label: Command-Download - removeLabel: label: Command-Export - removeLabel: label: Command-Import - removeLabel: label: Command-Install - removeLabel: label: Command-List - removeLabel: label: Command-Pin - removeLabel: label: Command-Search - removeLabel: label: Command-Show - removeLabel: label: Command-Source - removeLabel: label: Command-Uninstall - removeLabel: label: Command-Upgrade - removeLabel: label: Command-Validate - removeLabel: label: Context-Elevated-User - removeLabel: label: Context-User - removeLabel: label: Context-System - removeLabel: label: Dependencies - removeLabel: label: DSC-Resource - removeLabel: label: Experimental - removeLabel: label: Hardware - removeLabel: label: In-PR - removeLabel: label: Interactive-Only-Installer - removeLabel: label: Issue-Bug - removeLabel: label: Issue-Docs - removeLabel: label: Issue-Feature - removeLabel: label: msstore - removeLabel: label: Needs-Attention - removeLabel: label: Needs-Author-Feedback - removeLabel: label: Portable - removeLabel: label: PowerShell - removeLabel: label: Public-Service-Announcement - removeLabel: label: Side-By-Side - removeLabel: label: Windows-Sandbox - removeLabel: label: Zipped-Binary onFailure: onSuccess: ================================================ FILE: .github/policies/scheduledSearch.closeNoRecentActivity.yml ================================================ id: scheduledSearch.closeNoRecentActivity name: GitOps.PullRequestIssueManagement description: Closes issues that are inactive owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: scheduledSearches: - description: >- Search for PR where - * Pull Request is Open * Pull request has the label No-Recent-Activity * Pull request has the label Needs-Author-Feedback * Pull request does not have the label Blocking-Issue * Has not had activity in the last 7 days Then - * Close the PR frequencies: - hourly: hour: 6 filters: - isPullRequest - isOpen - hasLabel: label: No-Recent-Activity - hasLabel: label: Needs-Author-Feedback - isNotLabeledWith: label: Blocking-Issue - noActivitySince: days: 7 actions: - closeIssue - description: >- Search for Issues where - * Issue is Open * Issue has the label No-Recent-Activity * Issue has the label Needs-Author-Feedback * Issue does not have the label Blocking-Issue * Issue does not have the label Issue-Feature * Has not had activity in the last 7 days Then - * Close the Issue frequencies: - hourly: hour: 6 filters: - isIssue - isOpen - hasLabel: label: No-Recent-Activity - hasLabel: label: Needs-Author-Feedback - isNotLabeledWith: label: Blocking-Issue - isNotLabeledWith: label: Issue-Feature - noActivitySince: days: 7 actions: - closeIssue onFailure: onSuccess: ================================================ FILE: .github/policies/scheduledSearch.markNoRecentActivity.yml ================================================ id: scheduledSearch.closeNoRecentActivity name: GitOps.PullRequestIssueManagement description: Markss issues that are inactive owner: resource: repository disabled: false where: configuration: resourceManagementConfiguration: scheduledSearches: - description: >- Search for PR where - * Pull Request is Open * Pull request does not have the label No-Recent-Activity * Pull request does not have the label Blocking-Issue * Pull request has the label Needs-Author-Feedback * Has not had activity in the last 7 days Then - * Add No-Recent-Activity label frequencies: - hourly: hour: 6 filters: - isPullRequest - isOpen - isNotLabeledWith: label: No-Recent-Activity - isNotLabeledWith: label: Blocking-Issue - hasLabel: label: Needs-Author-Feedback - noActivitySince: days: 7 actions: - addLabel: label: No-Recent-Activity - description: >- Search for Issues where - * Issue is Open * Issue has the label Needs-Author-Feedback * Issue does not have the label No-Recent-Activity * Issue does not have the label Blocking-Issue * Issue does not have the label Issue-Feature * Has not had activity in the last 7 days Then - * Close the Issue frequencies: - hourly: hour: 6 filters: - isIssue - isOpen - hasLabel: label: Needs-Author-Feedback - isNotLabeledWith: label: No-Recent-Activity - isNotLabeledWith: label: Blocking-Issue - isNotLabeledWith: label: Issue-Feature - noActivitySince: days: 7 actions: - addLabel: label: No-Recent-Activity onFailure: onSuccess: ================================================ FILE: .github/workflows/automatic-issue-deduplication.yml ================================================ name: Automatic New Issue Deduplication on: issues: types: [opened, reopened] permissions: models: read issues: write concurrency: group: ${{ github.workflow }}-${{ github.event.issue.number }} cancel-in-progress: true jobs: deduplicate: runs-on: ubuntu-latest steps: - name: Run Deduplicate Action uses: pelikhan/action-genai-issue-dedup@v0 with: github_token: ${{ secrets.GITHUB_TOKEN }} label_as_duplicate: false ================================================ FILE: .github/workflows/spelling.yml ================================================ # spelling.yml is disabled per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p name: Workflow should not run! on: push: branches: '' jobs: placeholder: name: Should be disabled runs-on: ubuntu-latest if: false steps: - name: Task run: | echo 'Running this task would be bad' exit 1 ================================================ FILE: .github/workflows/spelling2.yml ================================================ # spelling.yml is disabled per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-p8r9-69g4-jwqq name: Workflow should not run! on: push: branches: '' jobs: placeholder: name: Should be disabled runs-on: ubuntu-latest if: false steps: - name: Task run: | echo 'Running this task would be bad' exit 1 ================================================ FILE: .github/workflows/spelling3.yml ================================================ # spelling.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p # spelling2.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-p8r9-69g4-jwqq name: Check Spelling # Comment management is handled through a secondary job, for details see: # https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions # # `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment # (in odd cases, it might actually run just to collapse a comment, but that's fairly rare) # it needs `contents: write` in order to add a comment. # # `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment # or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment) # it needs `pull-requests: write` in order to manipulate those comments. # Updating pull request branches is managed via comment handling. # For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list # # These elements work together to make it happen: # # `on.issue_comment` # This event listens to comments by users asking to update the metadata. # # `jobs.update` # This job runs in response to an issue_comment and will push a new commit # to update the spelling metadata. # # `with.experimental_apply_changes_via_bot` # Tells the action to support and generate messages that enable it # to make a commit to update the spelling metadata. # # `with.ssh_key` # In order to trigger workflows when the commit is made, you can provide a # secret (typically, a write-enabled github deploy key). # # For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key # SARIF reporting # # Access to SARIF reports is generally restricted (by GitHub) to members of the repository. # # Requires enabling `security-events: write` # and configuring the action with `use_sarif: 1` # # For information on the feature, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-SARIF-output # Minimal workflow structure: # # on: # push: # ... # pull_request_target: # ... # jobs: # # you only want the spelling job, all others should be omitted # spelling: # # remove `security-events: write` and `use_sarif: 1` # # remove `experimental_apply_changes_via_bot: 1` # ... otherwise adjust the `with:` as you wish on: push: branches: - "**" tags-ignore: - "**" pull_request_target: branches: - "**" types: - "opened" - "reopened" - "synchronize" jobs: spelling: name: Check Spelling permissions: contents: read pull-requests: read actions: read outputs: followup: ${{ steps.spelling.outputs.followup }} runs-on: ubuntu-latest if: ${{ contains(github.event_name, 'pull_request') || github.event_name == 'push' }} concurrency: group: spelling-${{ github.event.pull_request.number || github.ref }} # note: If you use only_check_changed_files, you do not want cancel-in-progress cancel-in-progress: true steps: - name: check-spelling id: spelling uses: check-spelling/check-spelling@v0.0.24 with: suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} checkout: true check_file_names: 1 post_comment: 0 use_magic_file: 1 warnings: bad-regex,binary-file,deprecated-feature,ignored-expect-variant,large-file,limited-references,no-newline-at-eof,noisy-file,non-alpha-in-dictionary,token-is-substring,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,no-files-to-check,unclosed-block-ignore-begin,unclosed-block-ignore-end check_extra_dictionaries: "" dictionary_source_prefixes: > { "cspell": "https://raw.githubusercontent.com/check-spelling/cspell-dicts/v20241114/dictionaries/" } extra_dictionaries: | cspell:software-terms/softwareTerms.txt cspell:cpp/stdlib-cpp.txt cspell:filetypes/filetypes.txt cspell:cpp/stdlib-c.txt cspell:php/php.txt cspell:python/python/python-lib.txt cspell:dotnet/dotnet.txt cspell:golang/go.txt cspell:cpp/compiler-msvc.txt cspell:dart/dart.txt cspell:html/html.txt cspell:powershell/powershell.txt cspell:aws/aws.txt cspell:python/common/extra.txt cspell:node/node.txt cspell:npm/npm.txt cspell:fullstack/fullstack.txt cspell:java/java.txt cspell:csharp/csharp.txt cspell:cpp/ecosystem.txt cspell:typescript/typescript.txt cspell:cpp/lang-keywords.txt comment-pr: name: Report (PR) # If you workflow isn't running on pull_request*, you can remove this job runs-on: ubuntu-latest needs: spelling permissions: actions: read contents: read pull-requests: write if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment uses: check-spelling/check-spelling@v0.0.24 with: checkout: true task: ${{ needs.spelling.outputs.followup }} ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates settings.json # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]elease[Ss]tatic/ [Rr]eleases/ x64/ x86/ arm64/ AnyCPU/ Fuzzing/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to check in your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # check in your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Vcpkg install directory **/vcpkg_installed/* # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Generated files from WinGetServer.idl **/WinGetServer/WinGetServer.h **/WinGetServer/WinGetServer_c.c **/WinGetServer/WinGetServer_s.c # Crescendo generated module files src/PowerShell/Microsoft.WinGet.Client/Crescendo/*.psd1 src/PowerShell/Microsoft.WinGet.Client/Crescendo/*.psm1 # Dev PowerShell module path src/PowerShell/scripts/Module # Interop nuget src/WinGetUtilInterop/scripts/Nuget* ================================================ FILE: .vsconfig ================================================ { "version": "1.0", "components": [ "Microsoft.VisualStudio.Component.CoreEditor", "Microsoft.VisualStudio.Workload.CoreEditor", "Microsoft.Net.Component.4.8.SDK", "Microsoft.Net.Component.4.7.2.TargetingPack", "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites", "Microsoft.VisualStudio.Component.TypeScript.TSServer", "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions", "Microsoft.VisualStudio.Component.JavaScript.TypeScript", "Microsoft.VisualStudio.Component.Roslyn.Compiler", "Microsoft.Component.MSBuild", "Microsoft.VisualStudio.Component.Roslyn.LanguageServices", "Microsoft.VisualStudio.Component.TextTemplating", "Microsoft.VisualStudio.Component.NuGet", "Microsoft.VisualStudio.Component.SQL.CLR", "Microsoft.Component.ClickOnce", "Microsoft.VisualStudio.Component.ManagedDesktop.Core", "Microsoft.NetCore.Component.Runtime.6.0", "Microsoft.NetCore.Component.SDK", "Microsoft.VisualStudio.Component.FSharp", "Microsoft.ComponentGroup.ClickOnce.Publish", "Microsoft.NetCore.Component.DevelopmentTools", "Microsoft.VisualStudio.Component.AppInsights.Tools", "Microsoft.Net.Component.4.8.TargetingPack", "Microsoft.Net.ComponentGroup.4.8.DeveloperTools", "Microsoft.VisualStudio.Component.DiagnosticTools", "Microsoft.VisualStudio.Component.EntityFramework", "Microsoft.VisualStudio.Component.Debugger.JustInTime", "Component.Microsoft.VisualStudio.LiveShare.2022", "Microsoft.VisualStudio.Component.IntelliCode", "Microsoft.VisualStudio.Component.VC.CoreIde", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "Microsoft.VisualStudio.Component.Graphics.Tools", "Microsoft.VisualStudio.Component.VC.DiagnosticTools", "Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging", "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites", "Microsoft.VisualStudio.Component.DotNetModelBuilder", "Microsoft.ComponentGroup.Blend", "Microsoft.VisualStudio.Workload.ManagedDesktop", "Microsoft.VisualStudio.Component.VC.ATL", "Microsoft.VisualStudio.Component.VC.Tools.ARM64", "Microsoft.VisualStudio.Component.VC.Redist.14.Latest", "Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core", "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake", "Microsoft.VisualStudio.Component.VC.CMake.Project", "Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest", "Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest", "Microsoft.VisualStudio.Component.VC.ASAN", "Microsoft.VisualStudio.Component.Vcpkg", "Microsoft.VisualStudio.Component.Windows10SDK.19041", "Microsoft.VisualStudio.Component.Windows11SDK.26100", "Microsoft.VisualStudio.Workload.NativeDesktop", "Microsoft.Component.NetFX.Native", "Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard", "Microsoft.VisualStudio.Component.Graphics", "Microsoft.VisualStudio.ComponentGroup.UWP.Xamarin", "Microsoft.VisualStudio.ComponentGroup.UWP.Support", "Microsoft.VisualStudio.Workload.Universal", "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre", "Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre", "Microsoft.NetCore.Component.Runtime.3.1" ] } ================================================ FILE: CODEOWNERS ================================================ * @microsoft/winget-developers ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Microsoft Open Source Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). Resources: - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns. ================================================ FILE: CONTRIBUTING.md ================================================ # Windows Package Manager Contributor's Guide Below is our guidance for how to report issues, propose new features, and submit contributions via Pull Requests (PRs). ## Open Development Workflow The Windows Package Manager team is VERY active in this GitHub Repository. In fact, we live in it all day long and carry out all our development in the open! When the team finds issues we file them in the repository. When we propose new ideas or think up new features, we file new feature requests. When we work on fixes or features, we create branches and work on those improvements. And when PRs are reviewed, we review them in public - including all the good, the bad, and the ugly parts. The point of doing all this work in public is to ensure that we are holding ourselves to a high degree of transparency, and so that the community sees that we apply the same processes and hold ourselves to the same quality bar as we do to community-submitted issues and PRs. We also want to make sure that we expose our team culture and "tribal knowledge" that is inherent in any closely-knit team, which often contains considerable value to those new to the project who are trying to figure out "why the heck does this thing look/work like this???" ### Repository Bot The team triages new issues several times a week. During triage, the team uses labels to categorize, manage, and drive the project workflow. We employ a bot to help us automate common processes within our workflow. We drive the bot by tagging issues with specific labels which cause the bot engine to close issues, merge branches, etc. This bot engine helps us keep the repository clean by automating the process of notifying appropriate parties if/when information/follow-up is needed, and closing stale issues/PRs after reminders have remained unanswered for several days. Therefore, if you do file issues, or create PRs, please keep an eye on your GitHub notifications. If you do not respond to requests for information, your issues/PRs may be closed automatically. --- ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC). See [SECURITY.md](./SECURITY.md) for more information. ## Before you start, file an issue Please follow this simple rule to help us eliminate any unnecessary wasted effort & frustration, and ensure an efficient and effective use of everyone's time - yours, ours, and other community members': > 👉 If you have a question, think you've discovered an issue, would like to propose a new feature, etc., then find/file an issue **BEFORE** starting work to fix/implement it. ### Search existing issues first Before filing a new issue, search existing open and closed issues first: This project is moving fast! It is likely someone else has found the problem you're seeing, and someone may be working on or have already contributed a fix! If no existing item describes your issue/feature, great - please file a new issue: ### File a new Issue * Don't know whether you're reporting an issue or requesting a feature? File an issue * Have a question that you don't see answered in docs, videos, etc.? File an issue * Want to know if we're planning on building a particular feature? Create a discussion * Got a great idea for a new feature? File an issue/request/idea * Don't understand how to do something? Create a discussion * Found an existing issue that describes yours? Great - upvote and add additional commentary / info / repro-steps / etc. When you hit "New Issue", select the type of issue closest to what you want to report/ask/request: ### Complete the template **Complete the information requested in the issue template, providing as much information as possible**. The more information you provide, the more likely your issue/ask will be understood and implemented. Helpful information includes: 👉 Tip: Run the following command `winget --info` * What tools and apps you're using (e.g. VS 2019, VSCode, etc.) * Don't assume we're experts in setting up YOUR environment and don't assume we are experts in ``. Teach us to help you! * **We LOVE detailed reproduction steps!** What steps do we need to take to reproduce the issue? Assume we love to read reproduction steps. As much detail as you can stand is probably _barely_ enough detail for us! * Prefer error message text where possible or screenshots of errors if text cannot be captured. * We MUCH prefer text command-line script than screenshots of command-line script. * **If you intend to implement the fix/feature yourself then say so!** If you do not indicate otherwise we will assume that the issue is our to solve, or may label the issue as `Help-Wanted`. ### DO NOT post "+1" comments > ⚠ DO NOT post "+1", "me too", or similar comments - they just add noise to an issue. If you don't have any additional info/context to add but would like to indicate that you're affected by the issue, upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) icon. This way we can actually measure how impactful an issue is. --- ## Contributing fixes / features For those able & willing to help fix issues and/or implement features ... ### To Spec or not to Spec Some issues/features may be quick and simple to describe and understand. For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Fork, Branch, and Create your PR", below. Small issues that do not require a spec will be labeled Issue-Bug or Issue-Task. However, some issues/features will require careful thought & formal design before implementation. For these scenarios, we'll request that a spec is written and the associated issue will be labeled Issue-Feature. Specs help collaborators discuss different approaches to solving a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run. Specs will be managed in a very similar manner as code contributions so please follow the "Fork, Branch, and Create your PR" below. ### Writing / Contributing-to a Spec To write/contribute to a spec: fork, branch, and commit via PRs, as you would with any code changes. Specs are written in markdown, stored under the `/doc/specs` folder, and named `[issue id] - [spec description].md`. 👉 **It is important to follow the spec templates and complete the requested information**. The available spec templates will help ensure that specs contain the minimum information & decisions necessary to permit development to begin. In particular, specs require you to confirm that you've already discussed the issue/idea with the team in an issue and that you provide the issue ID for reference. Team members will be happy to help review specs and guide them to completion. ### Help Wanted Once the team has approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/winget-cli/labels/Help%20Wanted). --- ## Development ### Fork, Clone, Branch and Create your PR Once you've discussed your proposed feature/fix/etc. with a team member, and you've agreed an approach or a spec has been written and approved, it's time to start development: 1. Fork the repository if you haven't already. 1. Clone your fork locally. 1. Create & push a feature branch. 1. Create a [Draft Pull Request (PR)](https://github.blog/2019-02-14-introducing-draft-pull-requests/). 1. Work on your changes. 1. Build and see if it works. ### Testing Testing is a key component in the development workflow. ### Code Review When you'd like the team to take a look, (even if the work is not yet fully-complete), mark the Draft PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the result will be solid, testable, conformant code that is safe for us to merge. > ⚠ Remember: **changes you make may affect both the Windows Package Manager and the schema support implemented in our validation pipelines!** Because of this, we will treat community PRs with the same level of scrutiny and rigor as commits submitted to the official Windows source by team members and partners. ### Merge Once your code has been reviewed and approved by the requisite number of team members, it will be merged into the main branch. Once merged, your PR will be automatically closed. --- ## Thank you Thank you in advance for your contribution! Now, [what's next on the list](https://github.com/microsoft/winget-cli/labels/Help%20Wanted)? 😜 ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) Microsoft Corporation. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE ================================================ FILE: Localization/Policies/de-DE/DesktopAppInstaller.adml ================================================ App-Installer App-Installer Desktop-App-Installer Windows-Paket-Manager aktivieren Mit dieser Richt Linie wird gesteuert, ob der Windows-Paket-Manager von Benutzern verwendet werden kann. Wenn Sie diese Einstellung aktivieren oder nicht konfigurieren, können Benutzer die Windows-Paket-Manager verwenden. Wenn Sie diese Einstellung deaktivieren, können Benutzer den Windows-Paket-Manager nicht verwenden. Einstellungen des Windows-Paket-Manager aktivieren Mit dieser Richt Linie wird gesteuert, ob Benutzer Ihre Einstellungen ändern können. Wenn Sie diese Einstellung aktivieren oder nicht konfigurieren, können Benutzer Einstellungen für die Windows-Paket-Manager ändern. Wenn Sie diese Einstellung deaktivieren, können Benutzer Einstellungen für die Windows-Paket-Manager nicht ändern. Windows-Paket-Manager experimentelle Features aktivieren Mit dieser Richt Linie wird gesteuert, ob Benutzer Experiment elle Funktionen in der Windows-Paket-Manager aktivieren können. Wenn Sie diese Einstellung aktivieren oder nicht konfigurieren, können Benutzer Experiment elle Features für die Windows-Paket-Manager aktivieren. Wenn Sie diese Einstellung deaktivieren, können Benutzer Experiment elle Features für die Windows-Paket-Manager nicht aktivieren. Lokale Manifestdateien Windows-Paket-Manager aktivieren Mit dieser Richt Linie wird gesteuert, ob Benutzer Pakete mit lokalen Manifestdateien installieren können. Wenn Sie diese Einstellung aktivieren oder nicht konfigurieren, können Benutzer Pakete mit lokalen Manifeste mithilfe des Windows-Paket-Manager installieren. Wenn Sie diese Einstellung deaktivieren, können Benutzer keine Pakete mit lokalen Manifeste unter Verwendung des Windows-Paket-Manager installieren. Umgehung der Quellzertifikatsüberprüfung des Windows-Paket-Managers im Microsoft Store Diese Richtlinie steuert, ob der Windows-Paket-Manager beim Initiieren einer Verbindung mit der Microsoft Speicherquelle überprüft, ob der Microsoft Speicherzertifikathash mit einem bekannten Microsoft Speicherzertifikat übereinstimmt. Wenn Sie diese Richtlinie aktivieren, umgehen die Windows-Paket-Manager die Überprüfung des Microsoft Speicherzertifikats. Wenn Sie diese Richtlinie deaktivieren, überprüft der Windows-Paket-Manager, ob das verwendete Microsoft Speicherzertifikat gültig ist und zum Microsoft Speicher gehört, bevor mit der Microsoft Store-Quelle kommuniziert wird. Wenn Sie diese Richtlinie nicht konfigurieren, werden die Windows-Paket-Manager Administratoreinstellungen eingehalten. Außerkraftsetzung von Windows-Paket-Manager-Hash aktivieren Diese Richtlinie steuert, ob der Windows-Paket-Manager so konfiguriert werden kann, dass die Fähigkeit „SHA256-Sicherheitsvalidierung überschreiben“ in den Einstellungen aktiviert werden kann. Wenn Sie diese Richtlinie aktivieren oder nicht konfigurieren, können Benutzer die Fähigkeit „SHA256-Sicherheitsvalidierung überschreiben“ in den Windows-Paket-Manager-Einstellungen aktivieren. Wenn Sie diese Richtlinie deaktivieren, können Benutzer die Fähigkeit „SHA256-Sicherheitsvalidierung überschreiben“ in den Windows-Paket-Manager-Einstellungen nicht aktivieren. Außerkraftsetzung des Malware-Scans für lokale Archive im Windows-Paket-Manager aktivieren Diese Richtlinie steuert die Möglichkeit, Überprüfungen auf Sicherheitsrisiken durch Schadsoftware außer Kraft zu setzen, wenn eine Archivdatei mithilfe eines lokalen Manifests mithilfe der Befehlszeilenargumente installiert wird. Wenn Sie diese Richtlinie aktivieren, können Benutzer die Überprüfung auf Schadsoftware außer Kraft setzen, wenn sie eine lokale Manifestinstallation einer Archivdatei ausführen. Wenn Sie diese Richtlinie deaktivieren, können Benutzer die Schadsoftwareüberprüfung einer Archivdatei bei der Installation mithilfe eines lokalen Manifests nicht außer Kraft setzen. Wenn Sie diese Richtlinie nicht konfigurieren, werden die Windows-Paket-Manager Administratoreinstellungen eingehalten. Windows-Paket-Manager-Standardquelle aktivieren Diese Richtlinie steuert die Standardquelle, die im Windows-Paket-Manager enthalten ist. Wenn Sie diese Einstellung nicht konfigurieren, ist die Standardquelle für den Windows-Paket-Manager verfügbar und kann entfernt werden. Wenn Sie diese Einstellung aktivieren, ist die Standardquelle für den Windows-Paket-Manager verfügbar und kann nicht entfernt werden. Wenn Sie diese Einstellung deaktivieren, ist die Standardquelle für den Windows-Paket-Manager nicht verfügbar. Microsoft Store-Quelle des Windows-Paket-Managers aktivieren Diese Richtlinie steuert die Microsoft Store-Quelle, die im Windows-Paket-Manager enthalten ist. Wenn Sie diese Einstellung nicht konfigurieren, ist die Microsoft Store-Quelle für den Windows-Paket-Manager verfügbar und kann entfernt werden. Wenn Sie diese Einstellung aktivieren, ist die Microsoft Store-Quelle für den Windows-Paket-Manager verfügbar und kann nicht entfernt werden. Wenn Sie diese Einstellung deaktivieren, ist die Microsoft Store-Quelle für den Windows-Paket-Manager nicht verfügbar. Windows-Paket-Manager-Schriftartquelle aktivieren Diese Richtlinie steuert die im Windows-Paket-Manager enthaltene Schriftartquelle. Wenn Sie diese Einstellung nicht konfigurieren, ist die Schriftartquelle für den Windows-Paket-Manager verfügbar und kann entfernt werden. Wenn Sie diese Einstellung aktivieren, ist die Schriftartquelle für den Windows-Paket-Manager verfügbar und kann nicht entfernt werden. Wenn Sie diese Einstellung deaktivieren, ist die Schriftartquelle für den Windows-Paket-Manager nicht verfügbar. Intervall für das automatische Aktualisieren der Windows-Paket-Manager-Quelle in Minuten festlegen Diese Richtlinie steuert das Intervall für die automatische Aktualisierung paketbasierter Quellen. Die Standardquelle für Windows-Paket-Manager ist so konfiguriert, dass ein Index der Pakete auf dem lokalen Computer zwischengespeichert wird. Der Index wird heruntergeladen, wenn ein Benutzer einen Befehl aufruft und das Intervall abgelaufen ist. Wenn Sie diese Einstellung deaktivieren oder nicht konfigurieren, wird das Standardintervall oder der in den Windows-Paket-Manager Einstellungen angegebene Wert verwendet. Wenn Sie diese Einstellung aktivieren, wird die angegebene Anzahl von Minuten vom Windows-Paket-Manager verwendet. Windows-Paket-Manager zusätzliche Quellen aktivieren Diese Richtlinie steuert zusätzliche Quellen, die vom IT-Administrator des Unternehmens bereitgestellt werden. Wenn Sie diese Richtlinie nicht konfigurieren, werden keine zusätzlichen Quellen für den Windows-Paket-Manager konfiguriert. Wenn Sie diese Richtlinie aktivieren, werden die zusätzlichen Quellen dem Windows-Paket-Manager hinzugefügt und können nicht entfernt werden. Die Repräsentation für jede zusätzliche Quelle kann aus den installierten Quellen mit „winget source export“ bezogen werden. Wenn Sie diese Richtlinie deaktivieren, können keine zusätzlichen Quellen für den Windows-Paket-Manager konfiguriert werden. Windows-Paket-Manager zulässige Quellen aktivieren Diese Richtlinie steuert zusätzliche Quellen, die vom IT-Administrator des Unternehmens zugelassen werden. Wenn Sie diese Richtlinie nicht konfigurieren, können Benutzer zusätzliche Quellen neben den in der Richtlinie konfigurierten hinzufügen oder entfernen. Wenn Sie diese Richtlinie aktivieren, können nur die angegebenen Quellen dem Windows-Paket-Manager hinzugefügt oder von ihm entfernt werden. Die Repräsentation für jede erlaubte Quelle kann aus den installierten Quellen mit „winget source export“ bezogen werden. Wenn Sie diese Richtlinie deaktivieren, können keine zusätzlichen Quellen für den Windows-Paket-Manager konfiguriert werden. Aktivieren des ms-appinstaller-Protokolls für den App-Installer Diese Richtlinie steuert, ob Benutzer Pakete von einer Website installieren können, die das ms-appinstaller-Protokoll verwendet. Wenn Sie diese Einstellung aktivieren, können Benutzer Pakete von Websites installieren, die dieses Protokoll verwenden. Wenn Sie diese Einstellung deaktivieren oder nicht konfigurieren, können Benutzer keine Pakete von Websites installieren, die dieses Protokoll verwenden. Aktivieren der Windows-Paketmanager-Befehlszeilenschnittstellen Diese Richtlinie legt fest, ob ein Benutzer eine Aktion mit dem Windows-Paketmanager über eine Befehlszeilenschnittstelle (WinGet CLI oder WinGet PowerShell) durchführen kann. Wenn Sie diese Richtlinie deaktivieren, können Benutzer die Windows-Paketverwaltungs-CLI und PowerShell-Cmdlets nicht ausführen. Wenn Sie diese Richtlinie aktivieren oder nicht konfigurieren, können Benutzer die CLI-Befehle des Windows-Paket-Managers und PowerShell-Cmdlets ausführen. (Vorausgesetzt, die Richtlinie „App Installer aktivieren“ ist nicht deaktiviert.) Diese Richtlinie setzt die Richtlinie „App-Installer aktivieren“ nicht außer Kraft. Aktivieren der Windows-Paket-Manager-Konfiguration Diese Richtlinie steuert, ob das Windows-Paket-Manager Konfigurationsfeature von Benutzern verwendet werden kann. Wenn Sie diese Einstellung aktivieren oder nicht konfigurieren, können Benutzer das Konfigurationsfeature Windows-Paket-Manager verwenden. Wenn Sie diese Einstellung deaktivieren, können Benutzer das Konfigurationsfeature Windows-Paket-Manager nicht verwenden. Befehlszeilenoptionen für Windows-Paket-Manager Proxy aktivieren Diese Richtlinie steuert, ob die Windows-Paket-Manager Verwendung des Proxys von Benutzern über die Befehlszeile konfiguriert werden kann. Wenn Sie diese Einstellung aktivieren, können Benutzer die Verwendung des Proxys durch die Windows-Paket-Manager über die Befehlszeile konfigurieren. Wenn Sie diese Einstellung deaktivieren oder nicht konfigurieren, können Benutzer die Verwendung des Proxys durch die Windows-Paket-Manager über die Befehlszeile nicht konfigurieren. MCP-Server für den Windows-Paket-Manager aktivieren Diese Richtlinie steuert, ob der Model Context Protocol (MCP)-Server des Windows-Paket-Managers verwendet werden kann. Wenn Sie diese Einstellung aktivieren oder nicht konfigurieren, können Benutzer den Windows-Paket-Manager verwenden. Wenn Sie diese Einstellung deaktivieren, können Benutzer den Windows-Paket-Manager nicht verwenden. Standardproxy für den Windows-Paket-Manager festlegen Diese Richtlinie steuert den Standardproxy, der vom Windows-Paket-Manager verwendet wird. Wenn Sie diese Einstellung deaktivieren oder nicht konfigurieren, wird standardmäßig kein Proxy verwendet. Wenn Sie diese Einstellung aktivieren, wird der angegebene Proxy standardmäßig verwendet. App-Installer zugelassene Zonen für MSIX-Pakete aktivieren Diese Richtlinie steuert, ob App-Installer die Installation von Paketen aus bestimmten URL-Zonen zulässt. Der Ursprung eines Pakets wird durch seinen URI bestimmt und ob ein Mart-of-the-Web (MotW) vorhanden ist. Wenn mehrere URIs beteiligt sind, werden alle berücksichtigt; z. B. bei Verwendung einer .appinstaller-Datei, die umleitungsbezieht. Wenn Sie diese Richtlinie aktivieren, können Benutzer MSIX-Pakete gemäß der Konfiguration für jede Zone installieren. Wenn Sie diese Richtlinie deaktivieren oder nicht konfigurieren, können Benutzer MSIX-Pakete aus einer beliebigen Zone installieren, mit Ausnahme von "Nicht vertrauenswürdig". Zulassen Blockieren Microsoft SmartScreen-Überprüfungen für MSIX-Pakete aktivieren Diese Richtlinie steuert, ob App-Installer bei der Installation von MSIX-Paketen Microsoft SmartScreen-Überprüfungen durchführt. Wenn Sie diese Richtlinie aktivieren oder nicht konfigurieren, wird der Paket-URI vor der Installation mit Microsoft SmartScreen ausgewertet. Diese Überprüfung wird nur für Pakete durchgeführt, die aus dem Internet stammen. Wenn Sie diese Option deaktivieren, wird Microsoft SmartScreen vor der Installation eines Pakets nicht abgefragt. Intervall für automatische Aktualisierung der Quelle in Minuten Zusätzliche Quellen: Erlaubte Quellen: Lokaler Computer Intranet Vertrauenswürdige Sites Internet Nicht vertrauenswürdige Websites ================================================ FILE: Localization/Policies/es-ES/DesktopAppInstaller.adml ================================================ Instalador de aplicación Instalador de aplicación Instalador de aplicaciones de escritorio Habilitar el Administrador de paquetes de Windows Esta directiva controla si los usuarios pueden usar el administrador de paquetes de Windows. Si habilitas o no estableces esta configuración, los usuarios podrán usar el administrador de paquetes de Windows. Si deshabilitas esta configuración, los usuarios no podrán usar el administrador de paquetes de Windows. Habilitar la configuración del Administrador de paquetes de Windows Esta directiva controla si los usuarios pueden cambiar su configuración. Si habilita o no establece esta configuración, los usuarios podrán cambiar la configuración del administrador de paquetes de Windows. Si deshabilitas esta configuración, los usuarios no podrán cambiar la configuración del administrador de paquetes de Windows. Habilitar Administrador de paquetes de Windows características experimentales Esta directiva controla si los usuarios pueden habilitar características experimentales en el administrador de paquetes de Windows. Si habilitas o no configuras esta opción, los usuarios podrán habilitar las características experimentales para el administrador de paquetes de Windows. Si deshabilitas esta configuración, los usuarios no podrán habilitar las características experimentales para el administrador de paquetes de Windows. Habilitar Administrador de paquetes de Windows archivos de manifiesto local Esta directiva controla si los usuarios pueden instalar paquetes con archivos de manifiesto locales. Si habilitas o no configuras esta opción, los usuarios podrán instalar paquetes con manifiestos locales mediante el administrador de paquetes de Windows. Si deshabilitas esta configuración, los usuarios no podrán instalar paquetes con manifiestos locales mediante el administrador de paquetes de Windows. Habilitar Administrador de paquetes de Windows Microsoft Store omisión de validación de certificado de origen Esta directiva controla si el Administrador de paquetes de Windows validará las coincidencias del hash del certificado de Microsoft Store con un certificado de Microsoft Store conocido al iniciar una conexión con el origen de Microsoft Store. Si habilita esta directiva, el Administrador de paquetes de Windows omitirá la validación del certificado de Microsoft Store. Si deshabilita esta directiva, el Administrador de paquetes de Windows validará que el certificado de Microsoft Store usado es válido y pertenece a Microsoft Store antes de comunicarse con el origen de Microsoft Store. Si no establece esta directiva, se usará la configuración de administrador del Administrador de paquetes de Windows. Habilitar invalidación de hash de Administrador de paquetes de Windows Esta directiva controla si se puede configurar el administrador de paquetes de Windows para que permita revertir la validación de seguridad SHA256 en la configuración. Si habilita o no configura esta directiva, los usuarios podrán habilitar la capacidad de revertir la validación de seguridad SHA256 en la configuración del administrador de paquetes de Windows. Si deshabilita esta directiva, los usuarios no podrán habilitar la capacidad de revertir la validación de seguridad SHA256 en la configuración del administrador de paquetes de Windows. Habilitar Administrador de paquetes de Windows invalidación del examen de malware de archivo local Esta directiva controla la capacidad de invalidar los exámenes de vulnerabilidades de malware al instalar un archivo de almacenamiento mediante un manifiesto local mediante los argumentos de la línea de comandos. Si habilita esta directiva, los usuarios pueden invalidar el examen de malware al realizar una instalación de manifiesto local de un archivo de almacenamiento. Si deshabilita esta directiva, los usuarios no podrán invalidar el examen de malware de un archivo de almacenamiento al instalarlo mediante un manifiesto local. Si no establece esta directiva, se cumplirá la configuración de administrador de Administrador de paquetes de Windows. Habilitar Administrador de paquetes de Windows origen predeterminado Esta directiva controla la fuente predeterminada incluida con el Administrador de paquetes de Windows. Si no configura esta opción, el origen predeterminado para el Administrador de paquetes de Windows estará disponible y podrá eliminarse. Si habilita esta configuración, el origen predeterminado para el Administrador de paquetes de Windows estará disponible y no se podrá eliminar. Si desactiva esta configuración, la fuente por defecto para el Administrador de Paquetes de Windows no estará disponible. Habilitar Administrador de paquetes de Windows origen de Microsoft Store Esta directiva controla la fuente de la Tienda Microsoft incluida en el Administrador de paquetes de Windows. Si no configura esta opción, el origen de la Tienda Microsoft para el Administrador de paquetes de Windows estará disponible y se podrá elimina. Si habilita esta configuración, el origen de Microsoft Store para el Administrador de paquetes de Windows estará disponible y no se podrá eliminar. Si desactiva esta configuración, el origen de la Tienda Microsoft para el Administrador de paquetes de Windows no estará disponible. Habilitar el origen de la fuente de Administrador de paquetes de Windows Esta directiva controla la fuente de la fuente incluida en el Administrador de paquetes de Windows. Si no configuras esta opción, el origen de la fuente para el Administrador de paquetes de Windows estará disponible y se podrá elimina. Si habilitas esta configuración, el origen de la fuente para el Administrador de paquetes de Windows estará disponible y no se podrá eliminar. Si desactivas esta configuración, el origen de la fuente para el Administrador de paquetes de Windows no estará disponible. Establecer el intervalo de actualización automática del origen del Administrador de paquetes de Windows en minutos Esta directiva controla el intervalo de actualización automática para orígenes basados en paquetes. El origen predeterminado para Administrador de paquetes de Windows está configurado de forma que un índice de los paquetes se almacena en caché en el equipo local. El índice se descarga cuando un usuario invoca un comando y el intervalo ha pasado. Si deshabilita o no establece esta configuración, se usará el intervalo predeterminado o el valor especificado en la configuración de Administrador de paquetes de Windows. Si habilita esta configuración, el Administrador de paquetes de Windows usará el número de minutos especificado. Habilitar Administrador de paquetes de Windows orígenes adicionales Esta directiva controla los orígenes adicionales proporcionados por el administrador de TI de la empresa. Si no configura esta directiva, los orígenes adicionales no se podrán configurar para el Administrador de paquetes de Windows. Si habilita esta directiva, los orígenes adicionales se agregarán al Administrador de paquetes de Windows y no se podrán eliminar. La representación de cada origen adicional puede obtenerse de las fuentes instaladas mediante "winget source export". Si desactiva esta directiva, no se podrán configurar orígenes adicionales para el Administrador de paquetes de Windows. Habilitar Administrador de paquetes de Windows orígenes permitidos Esta directiva controla los orígenes adicionales permitidos por el administrador de TI de la empresa Si no configura esta directiva, los usuarios podrán agregar o eliminar fuentes adicionales distintas de las configuradas por la directiva. Si habilita esta directiva, sólo las fuentes especificadas podrán agregarse o eliminarse del Administrador de paquetes de Windows. La representación de cada fuente permitida puede obtenerse de las fuentes instaladas mediante "winget source export". Si desactiva esta directiva, no se pueden configurar fuentes adicionales para el Administrador de paquetes de Windows. Habilitar protocolo ms-appinstaller del instalador de aplicaciones Esta directiva controla si los usuarios pueden instalar paquetes desde un sitio web que usa el protocolo ms-appinstaller. Si habilita esta configuración, los usuarios podrán instalar paquetes de sitios web que usen este protocolo. Si deshabilita o no establece esta configuración, los usuarios no podrán instalar paquetes de sitios web que usen este protocolo. Habilitar Administrador de paquetes de Windows interfaces de línea de comandos Esta directiva determina si un usuario puede realizar una acción mediante el Administrador de paquetes de Windows a través de una interfaz de línea de comandos (CLI de WinGet o WinGet PowerShell). Si deshabilita esta directiva, los usuarios no podrán ejecutar la CLI de Administrador de paquetes de Windows y los cmdlets de PowerShell. Si habilita o no configura esta directiva, los usuarios podrán ejecutar los comandos de la CLI de Administrador de paquetes de Windows y los cmdlets de PowerShell. (Proporcionado “Habilitar Instalador de aplicación” directiva no está deshabilitada). Esta directiva no invalida la directiva “Habilitar Instalador de aplicación”. Habilitar configuración de Administrador de paquetes de Windows Esta directiva controla si los usuarios pueden usar la característica de configuración Administrador de paquetes de Windows. Si habilita o no establece esta configuración, los usuarios podrán usar la característica de configuración Administrador de paquetes de Windows. Si deshabilita esta configuración, los usuarios no podrán usar la característica de configuración Administrador de paquetes de Windows. Habilitar Administrador de paquetes de Windows opciones de la línea de comandos del proxy Esta directiva controla si los usuarios pueden configurar el uso Administrador de paquetes de Windows del proxy a través de la línea de comandos. Si habilita esta configuración, los usuarios podrán configurar el uso del proxy del Administrador de paquetes de Windows a través de la línea de comandos. Si deshabilita o no establece esta configuración, los usuarios no podrán configurar el uso del proxy del Administrador de paquetes de Windows a través de la línea de comandos. Habilitar el servidor MCP para el Administrador de paquetes de Windows Esta directiva controla si se puede usar el servidor del Protocolo de contexto de modelo de Administrador de paquetes de Windows (MCP). Si habilita o no establece esta configuración, los usuarios podrán usar el servidor MCP del Administrador de paquetes de Windows. Si deshabilita esta configuración, los usuarios no podrán usar el servidor MCP del Administrador de paquetes de Windows. Establecer Administrador de paquetes de Windows proxy predeterminado Esta directiva controla el proxy predeterminado que usa el Administrador de paquetes de Windows. Si deshabilita o no establece esta configuración, no se usará ningún proxy de forma predeterminada. Si habilita esta configuración, el proxy especificado se usará de forma predeterminada. Habilitar Instalador de aplicación zonas permitidas para paquetes MSIX Esta directiva controla si Instalador de aplicación permite instalar paquetes procedentes de zonas URL específicas. El origen de un paquete está determinado por su URI y si hay presente un Mart-of-the-Web (MotW). Si hay varios URI implicados, se consideran todos; por ejemplo, cuando se usa un archivo .appinstaller que implica redirección. Si habilita esta directiva, los usuarios podrán instalar paquetes MSIX según la configuración de cada zona. Si deshabilitas o no configuras esta directiva, los usuarios podrán instalar paquetes MSIX desde cualquier zona excepto si no son de confianza. Permitir Bloquear Habilitar las comprobaciones de Microsoft SmartScreen para paquetes MSIX Esta directiva controla si Instalador de aplicación realiza comprobaciones de SmartScreen de Microsoft al instalar paquetes MSIX. Si habilitas o no configuras esta directiva, el URI del paquete se evaluará con Microsoft SmartScreen antes de la instalación. Esta comprobación solo se realiza para los paquetes procedentes de Internet. Si lo deshabilita, no se consultará a Microsoft SmartScreen antes de instalar un paquete. Intervalo de actualización automática de la fuente en minutos Orígenes adicionales: Orígenes permitidos: Equipo local Intranet Sitios de confianza Internet Sitio que no es de confianza ================================================ FILE: Localization/Policies/fr-FR/DesktopAppInstaller.adml ================================================ Programme d'installation d'application Programme d'installation d'application Programme d’installation des applications de bureau Activer le Gestionnaire de package Windows Cette stratégie contrôle si le gestionnaire de packages Windows peut être utilisé par les utilisateurs. Si vous activez ou ne configurez pas ce paramètre, les utilisateurs peuvent utiliser le gestionnaire de packages Windows. Si vous désactivez ce paramètre, les utilisateurs ne peuvent pas utiliser le gestionnaire de packages Windows. Activer les paramètres du Gestionnaire de package Windows Cette stratégie contrôle si les utilisateurs peuvent modifier leurs paramètres. Si vous activez ou ne configurez pas ce paramètre, les utilisateurs peuvent modifier les paramètres du gestionnaire de packages Windows. Si vous désactivez ce paramètre, les utilisateurs ne peuvent pas modifier les paramètres du gestionnaire de packages Windows. Activer les fonctionnalités expérimentales du Gestionnaire de package Windows Cette stratégie contrôle si les utilisateurs peuvent activer les fonctionnalités expérimentales dans le gestionnaire de packages Windows. Si vous activez ou ne configurez pas ce paramètre, les utilisateurs peuvent activer des fonctionnalités expérimentales pour le gestionnaire de package Windows. Si vous désactivez ce paramètre, les utilisateurs ne peuvent pas activer les fonctionnalités expérimentales pour le gestionnaire de package Windows. Activer les fichiers manifeste locaux du Gestionnaire de package Windows Cette stratégie contrôle si les utilisateurs peuvent installer des packages avec des fichiers de manifeste locaux. Si vous activez ou ne configurez pas ce paramètre, les utilisateurs peuvent installer des packages avec des manifestes locaux à l’aide du gestionnaire de packages Windows. Si vous désactivez ce paramètre, les utilisateurs ne peuvent pas installer de packages avec des manifestes locaux à l’aide du gestionnaire de packages Windows. Activer le contournement de validation du certificat source Microsoft Store du Gestionnaire de package Windows Cette stratégie contrôle si le Gestionnaire de package Windows validera si les correspondances de hachage de certificat Microsoft Store correspond à un certificat Microsoft Store connu lors de l’initialisation d’une connexion à la source Microsoft Store. Si vous activez cette stratégie, le Gestionnaire de package Windows contourne la validation du certificat Microsoft Store. Si vous désactivez cette stratégie, le Gestionnaire de package Windows vérifie que le certificat Microsoft Store utilisé est valide et appartient au Microsoft Store avant de communiquer avec la source Microsoft Store. Si vous ne configurez pas cette stratégie, les paramètres d’administrateur Gestionnaire de package Windows sont respectés. Activer le remplacement de hachage du Gestionnaire de package Windows Cette stratégie détermine si les utilisateurs peuvent configurer le Gestionnaire de package Windows ou non pour activer la fonction de remplacement de la validation de sécurité SHA256 dans les paramètres. Si vous activez ou ne configurez pas cette stratégie, les utilisateurs pourront activer la fonction de remplacement la validation de sécurité SHA256 dans les paramètres du Gestionnaire de package Windows. Si vous désactivez cette stratégie, les utilisateurs ne pourront pas activer la fonction de remplacement la validation de sécurité SHA256 dans les paramètres du Gestionnaire de package Windows. Activer le remplacement de l’analyse des programmes malveillants des archives locales du Gestionnaire de package Windows Cette stratégie contrôle la possibilité de remplacer les analyses de vulnérabilités de programmes malveillants lors de l’installation d’un fichier d’archive à l’aide d’un manifeste local à l’aide des arguments de ligne de commande. Si vous activez ce paramètre de stratégie, les utilisateurs peuvent remplacer l’analyse des programmes malveillants lors de l’installation d’un manifeste local d’un fichier d’archive. Si vous désactivez cette stratégie, les utilisateurs ne peuvent pas remplacer l’analyse des programmes malveillants d’un fichier d’archive lors de l’installation à l’aide d’un manifeste local. Si vous ne configurez pas cette stratégie, les paramètres d’administrateur Gestionnaire de package Windows sont conformes. Activer la source par défaut du Gestionnaire de package Windows Cette stratégie contrôle la source par défaut incluse dans le Gestionnaire de package Windows. Si vous ne configurez pas ce paramètre, la source par défaut pour le Gestionnaire de package Windows est disponible et peut être supprimée. Si vous activez ce paramètre, la source par défaut pour le Gestionnaire de package Windows est disponible et ne peut pas être supprimée. Si vous désactivez ce paramètre, la source par défaut pour le Gestionnaire de package Windows n’est pas disponible. Activer la source du Microsoft Store du Gestionnaire de package Windows Cette stratégie contrôle la source du Microsoft Store incluse dans le Gestionnaire de package Windows. Si vous ne configurez pas ce paramètre, la source du Microsoft Store pour le Gestionnaire de package Windows est disponible et peut être supprimée. Si vous activez ce paramètre, la source du Microsoft Store pour le Gestionnaire de package Windows est disponible et ne peut pas être supprimée. Si vous désactivez ce paramètre, la source du Microsoft Store pour le Gestionnaire de package Windows n’est pas disponible. Activer la source de polices du Gestionnaire de package Windows Cette stratégie contrôle la source des polices incluse dans le Gestionnaire de package Windows. Si vous ne configurez pas ce paramètre, la source de polices pour le Gestionnaire de package Windows sera disponible et pourra être supprimée. Si vous activez ce paramètre, la source de polices pour le Gestionnaire de package Windows sera disponible et ne pourra pas être supprimée. Si vous désactivez ce paramètre, la source de polices pour le Gestionnaire de package Windows ne sera pas disponible. Définir l’intervalle en minutes de la mise à jour automatique source du Gestionnaire de package Windows Cette stratégie contrôle l’intervalle de mise à jour automatique pour les sources basées sur les packages. La source par défaut de Gestionnaire de package Windows est configurée de sorte qu’un index des packages soit mis en cache sur l’ordinateur local. L’index est téléchargé lorsqu’un utilisateur appelle une commande et que l’intervalle est écoulé. Si vous désactivez ou ne configurez pas ce paramètre, l’intervalle par défaut ou la valeur spécifié dans les paramètres de Gestionnaire de package Windows sont utilisés. Si vous activez ce paramètre, le nombre de minutes spécifié est utilisé par le Gestionnaire de package Windows. Activer les sources supplémentaires du Gestionnaire de package Windows Cette stratégie contrôle les sources supplémentaires offertes par l’administrateur informatique de l’entreprise. Si vous ne configurez pas ce paramètre de stratégie, aucune source supplémentaire n’est configurée pour le Gestionnaire de package Windows. Si vous activez ce paramètre de stratégie, les sources supplémentaires sont ajoutées au Gestionnaire de package Windows et ne peuvent pas être supprimées. La représentation de chaque source supplémentaire peut être obtenue à partir de sources installées à l’aide de « Winget source export ». Si vous désactivez ce paramètre de stratégie, aucune source supplémentaire ne peut être configurée pour le Gestionnaire de package Windows. Activer les sources autorisées du Gestionnaire de package Windows Cette stratégie contrôle les sources supplémentaires autorisées par l’administrateur informatique de l’entreprise. Si vous ne configurez pas ce paramètre de stratégie, les utilisateurs pourront ajouter ou supprimer des sources supplémentaires autres que celles configurées par la stratégie Si Si vous activez cette stratégie, seules les sources spécifiées peuvent être ajoutées ou supprimées du Gestionnaire de package Windows. La représentation de chaque source autorisée peut être obtenue à partir de sources installées à l’aide de « Winget source export ». Si vous désactivez ce paramètre de stratégie, aucune source supplémentaire ne peut être configurée pour le Gestionnaire de package Windows. Activer le protocole ms-appinstaller du Programme d'installation d'application Cette stratégie contrôle si les utilisateurs peuvent installer des packages à partir d’un site web qui utilise le protocole ms-appinstaller. Si vous activez ce paramètre, les utilisateurs peuvent installer des packages à partir de sites web qui utilisent ce protocole. Si vous désactivez ou ne configurez pas ce paramètre, les utilisateurs ne peuvent pas installer de packages à partir de sites web qui utilisent ce protocole. Activer les interfaces de ligne de commande Gestionnaire de package Windows Cette stratégie détermine si un utilisateur peut effectuer une action à l’aide du Gestionnaire de package Windows via une interface de ligne de commande (WinGet CLI ou WinGet PowerShell). Si vous désactivez cette stratégie, les utilisateurs ne pourront pas exécuter l’interface CLI Gestionnaire de package Windows et les cmdlets PowerShell. Si vous activez ou ne configurez pas cette stratégie, les utilisateurs peuvent exécuter les commandes Gestionnaire de package Windows CLI et les cmdlets PowerShell. (La stratégie « Activer Programme d'installation d'application » fournie n’est pas désactivée). Cette stratégie ne remplace pas la stratégie « Activer Programme d'installation d'application ». Activer la configuration Gestionnaire de package Windows Cette stratégie contrôle si la fonctionnalité de configuration Gestionnaire de package Windows peut être utilisée par les utilisateurs. Si vous activez ou ne configurez pas ce paramètre, les utilisateurs peuvent utiliser la fonctionnalité de configuration Gestionnaire de package Windows. Si vous désactivez ce paramètre, les utilisateurs ne peuvent pas utiliser la fonctionnalité de configuration Gestionnaire de package Windows. Activer les options de ligne de commande du proxy Gestionnaire de package Windows Cette stratégie contrôle si l’utilisation Gestionnaire de package Windows proxy peut être configurée par les utilisateurs via la ligne de commande. Si vous activez ce paramètre, les utilisateurs peuvent configurer l’utilisation du proxy par le Gestionnaire de package Windows via la ligne de commande. Si vous désactivez ou ne configurez pas ce paramètre, les utilisateurs ne peuvent pas configurer l’utilisation du proxy par le Gestionnaire de package Windows via la ligne de commande. Activer le serveur MCP pour le Gestionnaire de package Windows Cette stratégie contrôle si le serveur MCP (Model Context Protocol) Gestionnaire de package Windows peut être utilisé. Si vous activez ou ne configurez pas ce paramètre, les utilisateurs peuvent utiliser le serveur MCP du Gestionnaire de package Windows. Si vous désactivez ce paramètre, les utilisateurs ne peuvent pas utiliser le serveur MCP du Gestionnaire de package Windows. Définir Gestionnaire de package Windows proxy par défaut Cette stratégie contrôle le proxy par défaut utilisé par le Gestionnaire de package Windows. Si vous désactivez ou ne configurez pas ce paramètre, aucun proxy n’est utilisé par défaut. Si vous activez ce paramètre, le proxy spécifié est utilisé par défaut. Activer les zones autorisées du programme d’installation d’application pour les packages MSIX Cette stratégie contrôle si Programme d'installation d'application autorise l’installation de packages provenant de zones URL spécifiques. L’origine d’un package est déterminée par son URI et si un Mart-of-the-Web (MotW) est présent. Si plusieurs URI sont impliqués, tous sont pris en compte ; par exemple, lors de l’utilisation d’un fichier .appinstaller qui implique une redirection. Si vous activez cette stratégie, les utilisateurs peuvent installer des packages MSIX en fonction de la configuration de chaque zone. Si vous désactivez ou ne configurez pas cette stratégie, les utilisateurs peuvent installer des packages MSIX à partir de n’importe quelle zone, sauf pour les packages non approuvés. Autoriser Bloquer Activer les vérifications Microsoft SmartScreen pour les packages MSIX Cette stratégie contrôle si Programme d'installation d'application effectue des vérifications Microsoft SmartScreen lors de l’installation des packages MSIX. Si vous activez ou ne configurez pas cette stratégie, l’URI du package est évalué avec Microsoft SmartScreen avant l’installation. Cette case activée est effectuée uniquement pour les packages provenant d’Internet. Si vous désactivez, Microsoft SmartScreen n’est pas consulté avant d’installer un package. Intervalle en minutes de mise à jour automatique de source Sources supplémentaires : Sources autorisées : Ordinateur local Intranet Sites de confiance Internet Sites non approuvés ================================================ FILE: Localization/Policies/it-IT/DesktopAppInstaller.adml ================================================ Programma di installazione app Programma di installazione app Programma di installazione app desktop Abilita Gestione pacchetti Windows Questo criterio Controlla se Windows Gestione pacchetti può essere utilizzato dagli utenti. Se si Abilita o non si configura questa impostazione, gli utenti potranno utilizzare Windows Gestione pacchetti. Se si disabilita questa impostazione, gli utenti non potranno utilizzare Windows Gestione pacchetti. Abilita le impostazioni di Gestione pacchetti Windows Questo criterio Controlla se gli utenti possono modificare le impostazioni. Se si Abilita o non si configura questa impostazione, gli utenti potranno modificare le impostazioni di Windows Gestione pacchetti. Se si disabilita questa impostazione, gli utenti non saranno in grado di modificare le impostazioni per Windows Gestione pacchetti. Abilita le funzionalità sperimentali di Gestione pacchetti Windows Questo criterio Controlla se gli utenti possono abilitare le funzionalità sperimentali in Windows Gestione pacchetti. Se si Abilita o non si configura questa impostazione, gli utenti potranno abilitare le funzionalità sperimentali per Windows Gestione pacchetti. Se si disabilita questa impostazione, gli utenti non saranno in grado di abilitare le funzionalità sperimentali per Windows Gestione pacchetti. Abilita i file manifesto locali di Gestione pacchetti Windows Questo criterio Controlla se gli utenti possono installare pacchetti con file di manifesto locali. Se si Abilita o non si configura questa impostazione, gli utenti potranno installare pacchetti con manifesti locali tramite Windows Gestione pacchetti. Se si disabilita questa impostazione, gli utenti non saranno in grado di installare pacchetti con manifesti locali tramite Windows Gestione pacchetti. Abilita il bypass di convalida del certificato di Microsoft Store di Gestione pacchetti Windows Questo criterio consente di stabilire se Gestione pacchetti Windows convaliderà la corrispondenza dell'hash del certificato Microsoft Store con un certificato Microsoft Store noto quando si avvia una connessione all'origine Microsoft Store. Se si abilita questo criterio, Gestione pacchetti Windows ignorerà la convalida del certificato Microsoft Store. Se si disabilita questo criterio, Gestione pacchetti Windows convaliderà il certificato Microsoft Store usato affinché sia valido e appartenga a Microsoft Store prima di comunicare con l'origine Microsoft Store. Se non si configura questo criterio, verranno rispettate le impostazioni dell'amministratore di Gestione pacchetti Windows. Abilita l’override dell’hash di Gestione pacchetti Windows Questa impostazione dei criteri consente di specificare se Windows Gestione pacchetti può essere configurato per abilitare la possibilità di ignorare la convalida di sicurezza SHA256 nelle impostazioni. Se si Abilita o non si configura questa impostazione dei criteri, gli utenti potranno abilitare la funzionalità di poter ignorare la convalida della sicurezza SHA256 nelle impostazioni di Windows Gestione pacchetti. Se si disabilita questa impostazione dei criteri, gli utenti non potranno abilitare la funzionalità di ignorare la convalida della sicurezza SHA256 nelle impostazioni di Windows Gestione pacchetti. Abilita l'override dell'analisi malware dell'archivio locale di Gestione pacchetti Windows Questo criterio consente di controllare la possibilità di eseguire l'override delle analisi di vulnerabilità malware durante l'installazione di un file di archivio usando un manifesto locale usando gli argomenti della riga di comando. Se si abilita questo criterio, gli utenti potranno eseguire l'override dell'analisi antimalware durante l'installazione di un manifesto locale di un file di archivio. Se si disabilita questo criterio, gli utenti non potranno eseguire l'override dell'analisi malware di un file di archivio durante l'installazione con un manifesto locale. Se non si configura questo criterio, le impostazioni dell'amministratore di Gestione pacchetti Windows saranno conformi. Abilita l’origine predefinita di Gestione pacchetti Windows Questo criterio controlla l'origine predefinita inclusa in Gestione pacchetti Windows. Se non si configura questa impostazione, l'origine predefinita per il gestore dei pacchetti di Windows sarà disponibile e potrà essere rimossa. Se si abilita questa impostazione, l'origine predefinita per Windows Gestione pacchetti sarà disponibile e non potrà essere rimossa. Se si disabilita questa impostazione, l'origine predefinita per Windows Gestione pacchetti non sarà disponibile. Abilita l’origine di Microsoft Store di Gestione pacchetti Windows Questo criterio controlla l'origine Microsoft Store inclusa in Windows Gestione pacchetti. Se non si configura questa impostazione, l'origine Microsoft Store per il gestore dei pacchetti di Windows sarà disponibile e potrà essere rimossa. Se si abilita questa impostazione, l'origine Microsoft Store per Windows Gestione pacchetti sarà disponibile e non potrà essere rimossa. Se si disabilita questa impostazione, l'origine Microsoft Store per Windows Gestione pacchetti non sarà disponibile. Abilita origine carattere Gestione pacchetti Windows Questo criterio controlla l'origine carattere inclusa nel Gestione pacchetti Windows. Se non si configura questa impostazione, l'origine del tipo di carattere per Gestione pacchetti Windows sarà disponibile e può essere rimossa. Se si abilita questa impostazione, l'origine carattere per il Gestione pacchetti Windows sarà disponibile e non potrà essere rimossa. Se si disabilita questa impostazione, l'origine carattere per il Gestione pacchetti Windows non sarà disponibile. Imposta l’intervallo di aggiornamento automatico di origine Gestione pacchetti Windows in minuti Questo criterio controlla l'intervallo di aggiornamento automatico per le origini basate su pacchetto. L'origine predefinita per Gestione pacchetti Windows è configurata in modo che un indice dei pacchetti venga memorizzato nella cache nel computer locale. L'indice viene scaricato quando un utente richiama un comando e l'intervallo è passato. Se si disabilita o non si configura questa impostazione, verrà utilizzato l'intervallo predefinito o il valore specificato nelle impostazioni Gestione pacchetti Windows. Se si abilita questa impostazione, il numero di minuti specificato verrà utilizzato dal Gestione pacchetti Windows. Abilita le origini aggiuntive di Gestione pacchetti Windows Questo criterio controlla le origini aggiuntive fornite dall'amministratore IT dell'organizzazione. Se non si configura questa impostazione dei criteri, non verrà configurata alcuna origine aggiuntiva per Windows Gestione pacchetti. Se si Abilita questo criterio, le origini aggiuntive verranno aggiunte a Windows Gestione pacchetti e non potranno essere rimosse. È possibile ottenere la rappresentazione di ogni origine aggiuntiva dalle origini installate usando 'esportazione di origine winget'. Se si disabilita questo criterio, non sarà possibile configurare altre origini per Windows Gestione pacchetti. Abilita le origini consentite di Gestione pacchetti Windows Questo criterio controlla le origini aggiuntive fornite dall'amministratore IT dell'organizzazione. Se non si configura questa impostazione dei criteri, gli utenti potranno aggiungere o rimuovere ulteriori origini oltre quelle configurate dal criterio. Se si Abilita questo criterio, solo le origini specificate possono essere aggiunte o rimosse a Windows Gestione pacchetti. È possibile ottenere la rappresentazione di ogni origine aggiuntiva dalle origini installate usando 'esportazione di origine winget'. Se si disabilita questo criterio, non sarà possibile configurare altre origini per Windows Gestione pacchetti. Abilita protocollo del programma di installazione app ms-appinstaller Questo criterio controlla se gli utenti possono installare pacchetti da un sito Web che usa il protocollo ms-appinstaller. Se si abilita questa impostazione, gli utenti potranno installare pacchetti da siti Web che utilizzano questo protocollo. Se si disabilita o non si configura questa impostazione, gli utenti non potranno installare pacchetti da siti Web che utilizzano questo protocollo. Abilita Gestione interfacce della riga di comando dei pacchetti Windows Questo criterio determina se un utente può eseguire un'azione usando Gestione pacchetti Windows tramite un'interfaccia della riga di comando (interfaccia della riga di comando di WinGet o WinGet PowerShell). Se si disabilita questo criterio, gli utenti non saranno in grado di eseguire l'interfaccia della riga di comando Gestione pacchetti Windows e i cmdlet di PowerShell. Se si abilita o non si configura questo criterio, gli utenti non saranno in grado di eseguire l'interfaccia della riga di comando Gestione pacchetti Windows e i cmdlet di PowerShell. (se il criterio "Abilita programma di installazione app" non è disabilitato). Questo criterio non sostituisce il criterio "Abilita programma di installazione app". Abilita la configurazione di Gestione pacchetti Questo criterio controlla se la funzionalità di configurazione Gestione pacchetti Windows può essere usata dagli utenti. Se si abilita o non si configura questa impostazione, gli utenti potranno usare la funzionalità di configurazione Gestione pacchetti Windows. Se si disabilita questa impostazione, gli utenti non potranno usare la funzionalità di configurazione Gestione pacchetti Windows. Abilita le opzioni della riga di comando del proxy Gestione pacchetti Windows Questo criterio controlla se l'utilizzo Gestione pacchetti Windows del proxy può essere configurato dagli utenti tramite la riga di comando. Se si abilita questa impostazione, gli utenti potranno configurare l'uso del proxy da parte del Gestione pacchetti Windows tramite la riga di comando. Se si disabilita o non si configura questa impostazione, gli utenti non potranno configurare l'utilizzo del proxy da parte del Gestione pacchetti Windows tramite la riga di comando. Abilitare il server MCP per Gestione pacchetti Windows Questo criterio controlla se è possibile utilizzare il server MCP (Model Context Protocol) Gestione pacchetti Windows. Se si abilita o non si configura questa impostazione, gli utenti potranno utilizzare il server MCP del Gestione pacchetti Windows. Se si disabilita questa impostazione, gli utenti non potranno utilizzare il server MCP del Gestione pacchetti Windows. Imposta proxy predefinito Gestione pacchetti Windows Questo criterio controlla il proxy predefinito utilizzato dal Gestione pacchetti Windows. Se si disabilita o non si configura questa impostazione, per impostazione predefinita non verrà utilizzato alcun proxy. Se si abilita questa impostazione, per impostazione predefinita verrà utilizzato il proxy specificato. Abilita le zone consentite del programma di installazione app per i pacchetti MSIX Questo criterio controlla se Programma di installazione app consente l'installazione di pacchetti provenienti da aree URL specifiche. L'origine di un pacchetto è determinata dal relativo URI e dalla presenza di un Mart-of-the-Web (MotW). Se sono coinvolti più URI, vengono considerati tutti; ad esempio, quando si utilizza un file con estensione appinstaller che implica il reindirizzamento. Se si abilita questo criterio, gli utenti potranno installare i pacchetti MSIX in base alla configurazione per ogni area. Se si disabilita o non si configura questo criterio, gli utenti potranno installare pacchetti MSIX da qualsiasi area, ad eccezione di Non attendibili. Consenti Blocca Abilita i controlli di Microsoft SmartScreen per i pacchetti MSIX Questo criterio controlla se Programma di installazione app esegue controlli Microsoft SmartScreen durante l'installazione dei pacchetti MSIX. Se si abilita o non si configura questo criterio, l'URI del pacchetto verrà valutato con Microsoft SmartScreen prima dell'installazione. Questo controllo viene eseguito solo per i pacchetti provenienti da Internet. Se si disabilita, Microsoft SmartScreen non verrà avvisato prima di installare un pacchetto. Intervallo di aggiornamento automatico di origine in minuti Origini aggiuntive: Origini consentite: Computer locale Intranet Siti attendibili Internet Siti non attendibili ================================================ FILE: Localization/Policies/ja-JP/DesktopAppInstaller.adml ================================================ アプリ インストーラー アプリ インストーラー デスクトップ アプリ インストーラー Windows パッケージ マネージャーを有効にする このポリシーでは、ユーザーが Windows パッケージマネージャーを使用できるかどうかを制御します。 この設定を有効にした場合、または構成しなかった場合、ユーザーは Windows パッケージマネージャーを使用できます。 この設定を無効にした場合、ユーザーは Windows パッケージマネージャーを使用できなくなります。 Windows パッケージ マネージャーを有効にする このポリシーでは、ユーザーが設定を変更できるかどうかを制御します。 この設定を有効にした場合、または構成しなかった場合、ユーザーは Windows パッケージマネージャーの設定を変更することができます。 この設定を無効にした場合、ユーザーは Windows パッケージマネージャーの設定を変更できなくなります。 Windows パッケージ マネージャーの試験的な機能を有効にする このポリシーでは、ユーザーが Windows パッケージマネージャーで試験的な機能を有効にできるかどうかを制御します。 この設定を有効にした場合、または構成しなかった場合、ユーザーは Windows パッケージマネージャーに対して実験的な機能を有効にすることができます。 この設定を無効にした場合、ユーザーは Windows パッケージマネージャーの実験的な機能を有効にできなくなります。 Windows パッケージ マネージャーのローカル マニフェスト ファイルを有効にする このポリシーでは、ローカルマニフェストファイルを含むパッケージをユーザーがインストールできるかどうかを制御します。 この設定を有効にした場合、または構成しなかった場合、ユーザーは Windows パッケージマネージャーを使用してローカルのマニフェストを含むパッケージをインストールできます。 この設定を無効にした場合、ユーザーは Windows パッケージマネージャーを使用してローカルのマニフェストを含むパッケージをインストールできなくなります。 Windows パッケージ マネージャーでの Microsoft Store ソースの証明書検証バイパスを有効にする このポリシーでは、Microsoft Store ソースへの接続を開始するときに、Windows パッケージ マネージャーが既知の Microsoft Store 証明書と一致する Microsoft Store 証明書ハッシュを検証するかどうかを制御します。 このポリシーを有効にした場合、Windows パッケージ マネージャーは Microsoft Store 証明書の検証をバイパスします。 このポリシーを無効にした場合、Windows パッケージ マネージャーは、Microsoft Store ソースと通信する前に、使用されている Microsoft Store 証明書が有効であり、Microsoft Store に属していることを検証します。 このポリシーを構成しなかった場合は、Windows パッケージ マネージャー管理者の設定が適用されます。 Windows パッケージ マネージャーのハッシュ オーバーライドを有効にする このポリシーでは、設定の SHA256 セキュリティ検証を上書きする機能を有効にできるように Windows パッケージ マネージャーを構成できるかどうかを制御します。 このポリシーを有効にした場合、または構成しなかった場合、ユーザーは Windows パッケージ マネージャーの設定で SHA256 セキュリティ検証を上書きする機能を有効にすることができます。 このポリシーを無効にした場合、ユーザーは Windows パッケージ マネージャーの設定で SHA256 セキュリティ検証を上書きする機能を有効することができなくなります。 Windows パッケージ マネージャーでのローカル アーカイブ マルウェア スキャンのオーバーライドを有効にする このポリシーでは、コマンド ライン引数を使用してローカル マニフェストを使用してアーカイブ ファイルをインストールするときに、マルウェアの脆弱性スキャンをオーバーライドする機能を制御します。 このポリシーを有効にした場合、アーカイブ ファイルのローカル マニフェストのインストールを実行するときに、ユーザーはマルウェア スキャンをオーバーライドできます。 このポリシーを無効にすると、ユーザーはローカル マニフェストを使用してインストールするときにアーカイブ ファイルのマルウェア スキャンをオーバーライドできなくなります。 このポリシーを構成しなかった場合は、Windows パッケージ マネージャー管理者の設定が適用されます。 Windows パッケージ マネージャーの既定のソースを有効にする このポリシーでは、Windows パッケージ マネージャーに含まれている 既定ソースを制御します。 この設定を構成しなかった場合、Windows パッケージ マネージャーの 既定ソースが利用可能になり、削除できるようになります。 この設定を有効にした場合、Windows パッケージ マネージャーの 既定ソースが利用できるようになり、削除できなくなります。 この設定を無効にした場合、Windows パッケージマ ネージャーの 既定ソースは利用できなくなります。 Windows パッケージ マネージャーの Microsoft Store ソースを有効にする このポリシーでは、Windows パッケージ マネージャーに含まれている Microsoft Store ソースを制御します。 この設定を構成しなかった場合、Windows パッケージ マネージャーの Microsoft Store ソースが利用可能になり、削除できるようになります。 この設定を有効にした場合、Windows パッケージ マネージャーの Microsoft Store ソースが利用できるようになり、削除できなくなります。 この設定を無効にした場合、Windows パッケージマ ネージャーの Microsoft Store ソースは利用できなくなります。 Windows パッケージ マネージャーのフォントのソースを有効にする このポリシーでは、Windows パッケージ マネージャーに含まれているフォント ソースを制御します。 この設定を構成しなかった場合、Windows パッケージ マネージャーのフォント ソースが利用可能になり、削除できるようになります。 この設定を有効にした場合、Windows パッケージ マネージャーのフォント ソースが利用できるようになり、削除できなくなります。 この設定を無効にした場合、Windows パッケージ マネージャーのフォント ソースは利用できなくなります。 Windows パッケージ マネージャーのソース自動更新間隔を分単位で設定する このポリシーは、パッケージ ベースのソースの自動更新間隔を制御します。Windows パッケージ マネージャーの既定のソースは、パッケージのインデックスがローカル コンピューターにキャッシュされるように構成されています。インデックスは、ユーザーがコマンドを呼び出し、間隔が過ぎたときにダウンロードされます。 この設定を無効にした場合、または構成しなかった場合は、既定の間隔またはWindows パッケージ マネージャー設定で指定された値が使用されます。 この設定を有効にした場合、指定した分数がWindows パッケージ マネージャーで使用されます。 Windows パッケージ マネージャーの追加のソースを有効にする このポリシーは、エンタープライズIT管理者によって提供される追加のソースを制御します。 このポリシーを構成しない場合、Windows パッケージ マネージャー用に追加のソースは構成されません。 このポリシーを有効にすると、追加のソースが Windows パッケージ マネージャーに追加され、削除できなくなります。 追加の各ソースの表現は、「wingetsourceexport」を使用してインストールされたソースから取得できます。 このポリシーを無効にすると、Windows パッケージ マネージャーに追加のソースを構成できなくなります。 Windows パッケージ マネージャーの許可されたソースを有効にする このポリシーは、エンタープライズ IT 管理者が許可する追加のソースを制御します。 このポリシーを構成しない場合、ユーザーはポリシーで構成されたソース以外のソースを追加または削除できます。 このポリシーを有効にすると、指定したソースのみを Windows パッケージ マネージャーに追加または Windows パッケージ マネージャーから削除できます。 許可された各ソースの表現は、「wingetsourceexport」を使用してインストールされたソースから取得できます。 このポリシーを無効にすると、Windows パッケージ マネージャーに追加のソースを構成できなくなります。 アプリ インストーラーの ms-appinstaller プロトコルを有効にする このポリシーでは、ユーザーが ms-appinstaller プロトコルを使用している Web サイトからパッケージをインストールできるかどうかを制御します。 この設定を有効にした場合、ユーザーは、このプロトコルを使用する Web サイトからパッケージをインストールできます。 この設定を無効にするか、未構成にした場合、ユーザーはこのプロトコルを使用する Web サイトからパッケージをインストールできなくなります。 Windows パッケージ マネージャーのコマンド ライン インターフェイスを有効にする このポリシーは、ユーザーがコマンド ライン インターフェイス (WinGet CLI または WinGet PowerShell) を使用してWindows パッケージ マネージャーを使用してアクションを実行できるかどうかを決定します。 このポリシーを無効にすると、ユーザーは Windows パッケージ マネージャー CLI と PowerShell コマンドレットを実行できなくなります。 このポリシーを有効にした場合、または構成しなかった場合、ユーザーは Windows パッケージ マネージャー CLI コマンドと PowerShell コマンドレットを実行できます。(ただし、"アプリ インストーラーを有効にする" ポリシーが無効になっていない場合に限ります)。 このポリシーは、"アプリ インストーラーを有効にする" ポリシーをオーバーライドしません。 Windows パッケージ マネージャーの構成を有効にする このポリシーでは、Windows パッケージ マネージャー構成機能をユーザーが使用できるかどうかを制御します。 この設定を有効にした場合、または構成しなかった場合、ユーザーはWindows パッケージ マネージャー構成機能を使用できます。 この設定を無効にすると、ユーザーはWindows パッケージ マネージャー構成機能を使用できなくなります。 Windows パッケージ マネージャー Proxy コマンド ライン オプションを有効にする このポリシーでは、ユーザーがコマンド ラインでプロキシのWindows パッケージ マネージャーの使用を構成できるかどうかを制御します。 この設定を有効にした場合、ユーザーはコマンド ラインを使用してWindows パッケージ マネージャーのプロキシの使用を構成できます。 この設定を無効にした場合、または構成しなかった場合、ユーザーはコマンド ラインでWindows パッケージ マネージャーのプロキシの使用を構成できなくなります。 Windows パッケージ マネージャーの MCP サーバーを有効にする このポリシーでは、Windows パッケージ マネージャー モデル コンテキスト プロトコル (MCP) サーバーを使用できるかどうかを制御します。 この設定を有効にするか、未構成にした場合、ユーザーはWindows パッケージ マネージャーの MCP サーバーを使用できます。 この設定を無効にすると、ユーザーはWindows パッケージ マネージャーの MCP サーバーを使用できなくなります。 Windows パッケージ マネージャーの既定のプロキシを設定する このポリシーは、Windows パッケージ マネージャーで使用される既定のプロキシを制御します。 この設定を無効にした場合、または構成しなかった場合、既定ではプロキシは使用されません。 この設定を有効にした場合、指定したプロキシが既定で使用されます。 MSIX パッケージアプリ インストーラー許可されたゾーンを有効にする このポリシーでは、アプリ インストーラーが特定の URL ゾーンからのパッケージのインストールを許可するかどうかを制御します。パッケージの配信元は、その URI と、Mart-of-the-Web (MotW) が存在するかどうかによって決まります。複数の URI が含まれている場合、すべてが考慮されます。たとえば、リダイレクトを含む .appinstaller ファイルを使用する場合などです。 このポリシーを有効にした場合、ユーザーは各ゾーンの構成に従って MSIX パッケージをインストールできます。 このポリシーを無効にするか、未構成にした場合、ユーザーは信頼されていないゾーン以外のゾーンから MSIX パッケージをインストールできます。 許可 ブロック MSIX パッケージの Microsoft SmartScreen チェックを有効にする このポリシーでは、MSIX パッケージのインストール時にアプリ インストーラーが Microsoft SmartScreen チェックを実行するかどうかを制御します。 このポリシーを有効にするか、未構成にした場合、インストール前にパッケージ URI が Microsoft SmartScreen で評価されます。このチェックは、インターネットからのパッケージに対してのみ実行されます。 無効にした場合、パッケージをインストールする前に Microsoft SmartScreen に問い合わせてください。 ソースの自動更新間隔 (分) 追加ソース: 許可されたソース: ローカル コンピューター イントラネット 信頼済みサイト インターネット 信頼できないサイト ================================================ FILE: Localization/Policies/ko-KR/DesktopAppInstaller.adml ================================================ 앱 설치 관리자 앱 설치 관리자 데스크톱 앱 설치 관리자 Windows 패키지 관리자 사용 이 정책은 사용자가 Windows 패키지 관리자를 사용할 수 있는지 여부를 제어합니다. 이 설정을 사용하거나 구성하지 않으면 사용자가 Windows 패키지 관리자를 사용할 수 있습니다. 이 설정을 사용하지 않으면 사용자가 Windows 패키지 관리자를 사용할 수 없습니다. Windows 패키지 관리자 설정 사용 이 정책은 사용자가 설정을 변경할 수 있는지 여부를 제어합니다. 이 설정을 사용하도록 설정하거나 구성하지 않으면 사용자가 Windows 패키지 관리자의 설정을 변경할 수 있습니다. 이 설정을 사용하지 않으면 사용자가 Windows 패키지 관리자의 설정을 변경할 수 없습니다. Windows 패키지 관리자 실험적 기능 사용 이 정책은 사용자가 Windows 패키지 관리자에서 실험적인 기능을 사용하도록 설정할 수 있는지 여부를 제어합니다. 이 설정을 사용하거나 구성하지 않으면 사용자가 Windows 패키지 관리자를 위해 실험적 기능을 사용할 수 있습니다. 이 설정을 사용하지 않도록 설정하면 사용자가 Windows 패키지 관리자를 위해 실험적 기능을 사용할 수 없습니다. Windows 패키지 관리자 로컬 매니페스트 파일 사용 이 정책은 사용자가 로컬 매니페스트 파일을 사용하여 패키지를 설치할 수 있는지 여부를 제어합니다. 이 설정을 사용하거나 구성하지 않으면 사용자는 Windows 패키지 관리자를 사용하여 로컬 매니페스트와 함께 패키지를 설치할 수 있습니다. 이 설정을 사용하지 않으면 사용자는 Windows 패키지 관리자를 사용하여 로컬 매니페스트를 통해 패키지를 설치할 수 없습니다. Windows 패키지 관리자 Microsoft Store 원본 인증서 유효성 검사 바이패스 사용 이 정책은 Windows 패키지 관리자가 Microsoft 저장소 원본에 대한 연결을 시작할 때 알려진 Microsoft Store 인증서와 일치하는 Microsoft 저장소 인증서 해시의 유효성을 검사할지 여부를 제어합니다. 이 정책을 사용하면 Windows 패키지 관리자는 Microsoft 저장소 인증서 유효성 검사를 무시합니다. 이 정책을 사용하지 않으면 Windows 패키지 관리자는 Microsoft Store 원본과 통신하기 전에 사용된 Microsoft 저장소 인증서가 유효하고 Microsoft 저장소에 속하는지 확인합니다. 이 정책을 구성하지 않으면 Windows 패키지 관리자의 관리자 설정이 적용됩니다. Windows 패키지 관리자 해시 재정의 사용 이 정책은 설정에서 SHA256 보안 유효성 검사를 재정의하도록 Windows 패키지 관리자를 구성할 수 있는지 여부를 제어합니다. 이 정책을 실행하거나 구성하지 않으면 Windows 패키지 관리자 설정에서 SHA256 보안 유효성 검사를 재정의하는 기능을 사용할 수 있습니다. 이 정책을 실행 중지하면 Windows 패키지 관리자 설정에서 SHA256 보안 유효성 검사를 재정의하는 기능을 사용자가 사용하도록 설정할 수 없습니다. Windows 패키지 관리자 로컬 보관 맬웨어 검사 재정의 사용 이 정책은 명령줄 인수를 사용하여 로컬 매니페스트를 사용하여 보관 파일을 설치할 때 맬웨어 취약성 검사를 재정의하는 기능을 제어합니다. 이 정책을 사용하면 사용자가 보관 파일의 로컬 매니페스트 설치를 수행할 때 맬웨어 검사를 재정의할 수 있습니다. 이 정책을 사용하지 않으면 로컬 매니페스트를 사용하여 설치할 때 사용자가 보관 파일의 맬웨어 검사를 재정의할 수 없습니다. 이 정책을 구성하지 않으면 Windows 패키지 관리자 관리자 설정이 적용됩니다. Windows 패키지 관리자 기본 원본 사용 이 정책은 Windows 패키지 관리자에 포함된 기본 원본을 제어합니다. 이 설정을 구성하지 않는 경우 Windows 패키지 관리자의 기본 원본을 사용할 수 있으며 제거할 수 있습니다. 이 설정을 사용하면 Windows 패키지 관리자의 기본 원본을 사용할 수 있으며 제거할 수 없습니다. 이 설정을 사용하지 않도록 설정하면 Windows 패키지 관리자의 기본 원본을 사용할 수 없습니다. Windows 패키지 관리자 Microsoft Store 원본 사용 이 정책은 Windows 패키지 관리자에 포함된 Microsoft 스토어 원본을 제어합니다. 이 설정을 구성하지 않는 경우 Windows 패키지 관리자의 Microsoft 스토어 원본을 사용할 수 있으며 제거할 수 있습니다. 이 설정을 사용하면 Windows 패키지 관리자용 Microsoft 스토어 원본을 사용할 수 있으며 제거할 수 없습니다. 이 설정을 사용 중지하면 Windows 패키지 관리자용 Microsoft 스토어 원본을 사용할 수 없습니다. Windows 패키지 관리자 글꼴 원본 활성화 이 정책은 Windows 패키지 관리자에 포함된 글꼴 원본을 제어합니다. 이 설정을 구성하지 않는 경우 Windows 패키지 관리자의 글꼴 원본을 사용할 수 있으며 제거할 수 있습니다. 이 설정을 사용하면 Windows 패키지 관리자용 글꼴 원본을 사용할 수 있으며 제거할 수 없습니다. 이 설정을 사용 중지하면 Windows 패키지 관리자용 글꼴 원본을 사용할 수 없습니다. Windows 패키지 관리자 원본 자동 업데이트 간격(분) 설정 이 정책은 패키지 기반 원본의 자동 업데이트 간격을 제어합니다. 패키지의 인덱스가 로컬 컴퓨터에 캐시되도록 Windows 패키지 관리자 기본 원본이 구성됩니다. 사용자가 명령을 호출하고 간격이 지나면 인덱스가 다운로드됩니다. 이 설정을 사용하지 않거나 구성하지 않으면 Windows 패키지 관리자 설정에 지정된 기본 간격 또는 값이 사용됩니다. 이 설정을 사용하면 지정한 시간(분)이 Windows 패키지 관리자 사용됩니다. Windows 패키지 관리자 추가 원본 사용 이 정책은 엔터프라이즈 IT 관리자가 제공하는 추가 원본을 제어합니다. 정책을 구성하지 않는 경우 Windows 패키지 관리자에 대해 추가 원본이 구성되지 않습니다. 정책을 사용하도록 설정하면 추가 원본이 Windows 패키지 관리자에 추가되고 제거할 수 없습니다. 'winget 원본 내보내기'를 사용하여 설치된 원본에서 각 추가 원본에 대한 표현을 얻을 수 있습니다. 이 정책을 사용하지 않도록 설정하는 경우 Windows 패키지 관리자에 대해 추가 원본을 구성할 수 없습니다. Windows 패키지 관리자 허용된 원본 사용 이 정책은 엔터프라이즈 IT 관리자가 허용하는 추가 원본을 제어합니다. 이 정책을 구성하지 않는 경우 사용자는 정책에 의해 구성된 원본 외의 다른 원본을 추가하거나 제거할 수 있습니다. 정책을 사용하도록 설정하는 경우 지정된 원본만 Windows 패키지 관리자에서 추가하거나 제거할 수 있습니다. 허용된 각 원본에 대한 표현은 'winget 원본 내보내기'를 사용하여 설치된 원본에서 구할 수 있습니다. 이 정책을 사용하지 않도록 설정하는 경우 Windows 패키지 관리자에 대해 추가 원본을 구성할 수 없습니다. 앱 설치 관리자 ms-appinstaller 프로토콜 사용 이 정책은 사용자가 ms-appinstaller 프로토콜을 사용하는 웹 사이트에서 패키지를 설치할 수 있는지 여부를 제어합니다. 이 설정을 사용하면 사용자가 이 프로토콜을 사용하는 웹 사이트의 패키지를 설치할 수 있습니다. 이 설정을 사용하지 않거나 구성하지 않으면 사용자가 이 프로토콜을 사용하는 웹 사이트의 패키지를 설치할 수 없습니다. Windows 패키지 관리자 명령줄 인터페이스 활성화 이 정책은 사용자가 명령줄 인터페이스(WinGet CLI 또는 WinGet PowerShell)를 통해 Windows 패키지 관리자를 사용하여 작업을 수행할 수 있는지 여부를 결정합니다. 이 정책을 사용하지 않도록 설정하면 사용자는 Windows 패키지 관리자 CLI 및 PowerShell cmdlet을 실행할 수 없습니다. 이 정책을 사용하도록 설정하거나 구성하지 않을 경우 사용자는 Windows 패키지 관리자 CLI 명령 및 PowerShell cmdlet을 실행할 수 없습니다. (제공된 “앱 설치 관리자 사용” 정책을 사용하지 않도록 설정할 수 없습니다.) 이 정책은 “앱 설치 관리자 사용” 정책을 재정의하지 않습니다. Windows 패키지 관리자 구성 사용 이 정책은 사용자가 Windows 패키지 관리자 구성 기능을 사용할 수 있는지 여부를 제어합니다. 이 설정을 사용하거나 구성하지 않으면 사용자가 Windows 패키지 관리자 구성 기능을 사용할 수 있습니다. 이 설정을 사용하지 않으면 사용자가 Windows 패키지 관리자 구성 기능을 사용할 수 없습니다. 프록시 Windows 패키지 관리자 명령줄 옵션 사용 이 정책은 명령줄을 통해 사용자가 프록시의 Windows 패키지 관리자 사용을 구성할 수 있는지 여부를 제어합니다. 이 설정을 사용하면 사용자가 명령줄을 통해 Windows 패키지 관리자 프록시 사용을 구성할 수 있습니다. 이 설정을 사용하지 않거나 구성하지 않으면 사용자가 명령줄을 통해 Windows 패키지 관리자 프록시 사용을 구성할 수 없습니다. Windows 패키지 관리자용 MCP 서버 활성화 이 정책은 MCP(Windows 패키지 관리자 모델 컨텍스트 프로토콜) 서버를 사용할 수 있는지 여부를 제어합니다. 이 설정을 사용하거나 구성하지 않으면 사용자가 Windows 패키지 관리자 MCP 서버를 사용할 수 있습니다. 이 설정을 사용하지 않으면 사용자가 Windows 패키지 관리자 MCP 서버를 사용할 수 없습니다. Windows 패키지 관리자 기본 프록시 설정 이 정책은 Windows 패키지 관리자 사용하는 기본 프록시를 제어합니다. 이 설정을 사용하지 않거나 구성하지 않으면 기본적으로 프록시가 사용되지 않습니다. 이 설정을 사용하면 지정된 프록시가 기본적으로 사용됩니다. MSIX 패키지에 대해 앱 설치 관리자 허용된 영역 사용 이 정책은 앱 설치 관리자 특정 URL 영역에서 시작되는 패키지를 설치할 수 있는지 여부를 제어합니다. 패키지의 원본은 해당 URI에 따라 결정되며 MotW(Mart-of-the-Web)가 있는지 여부를 결정합니다. 여러 URI가 관련된 경우 모두 고려됩니다. 예를 들어 리디렉션과 관련된 .appinstaller 파일을 사용하는 경우 이 정책을 사용하면 사용자가 각 영역의 구성에 따라 MSIX 패키지를 설치할 수 있습니다. 이 정책을 사용하지 않거나 구성하지 않으면 신뢰할 수 없음을 제외한 모든 영역에서 MSIX 패키지를 설치할 수 있습니다. 허용 차단 MSIX 패키지에 Microsoft SmartScreen 검사 사용 이 정책은 MSIX 패키지를 설치할 때 앱 설치 관리자 Microsoft SmartScreen 검사를 수행할지 여부를 제어합니다. 이 정책을 사용하거나 구성하지 않으면 설치 전에 Microsoft SmartScreen을 사용하여 패키지 URI가 평가됩니다. 이 검사 인터넷에서 가져온 패키지에 대해서만 수행됩니다. 사용하지 않도록 설정하면 패키지를 설치하기 전에 Microsoft SmartScreen을 참조하지 않습니다. 원본 자동 업데이트 간격(분) 추가 원본: 허용된 원본: 로컬 컴퓨터 인트라넷 신뢰할 수 있는 사이트 인터넷 신뢰할 수 없는 사이트 ================================================ FILE: Localization/Policies/pt-BR/DesktopAppInstaller.adml ================================================ Instalador de Aplicativo Instalador de Aplicativo Instalador de Aplicativos de Área de Trabalho Habilitar o Gerenciador de Pacotes do Windows Esta política controla se o Gerenciador de pacotes do Windows pode ser usado por usuários. Se você habilitar ou não definir essa configuração, os usuários poderão usar o Gerenciador de pacotes do Windows. Se você desabilitar essa configuração, os usuários não poderão usar o Gerenciador de pacotes do Windows. Habilitar as Configurações do Gerenciador de Pacotes do Windows Esta política controla se os usuários podem alterar suas configurações. Se você habilitar ou não definir essa configuração, os usuários poderão alterar as configurações do Gerenciador de pacotes do Windows. Se você desabilitar essa configuração, os usuários não poderão alterar configurações para o Gerenciador de pacotes do Windows. Habilitar Recursos Experimentais do Gerenciador de Pacotes do Windows Essa política controla se os usuários podem habilitar recursos experimentais no Gerenciador de pacotes do Windows. Se você habilitar ou não definir essa configuração, os usuários poderão habilitar recursos experimentais para o Gerenciador de pacotes do Windows. Se você desabilitar essa configuração, os usuários não poderão habilitar os recursos experimentais do Gerenciador de pacotes do Windows. Habilitar Arquivos de Manifesto Local do Gerenciador de Pacotes do Windows Essa política controla se os usuários podem instalar pacotes com arquivos de manifesto locais. Se você habilitar ou não definir essa configuração, os usuários poderão instalar pacotes com manifestos locais usando o Gerenciador de pacotes do Windows. Se você desabilitar essa configuração, os usuários não poderão instalar pacotes com manifestos locais usando o Gerenciador de pacotes do Windows. Habilitar Bypass Gerenciador de Pacotes do Windows Microsoft Store Certificado de Origem Essa política controla se o Gerenciador de Pacotes do Windows validará se o hash do certificado Microsoft Store corresponde a um certificado Microsoft Store conhecido ao iniciar uma conexão com a Origem da Microsoft Store. Se você habilitar essa política, o Gerenciador de Pacotes do Windows ignorará a validação do certificado da Microsoft Store. Se você desabilitar essa política, o Gerenciador de Pacotes do Windows validará se o certificado Microsoft Store usado é válido e pertence ao Microsoft Store antes de se comunicar com a origem da Microsoft Store. Se você não definir essa política, as configurações de administrador do Gerenciador de Pacotes do Windows serão aderidas. Habilitar a Substituição de Hash do Gerenciador de Pacotes do Windows Esta política controla se o Gerenciador de Pacotes do Windows pode ou não ser configurado para permitir a capacidade de substituição da validação de segurança SHA256 nas configurações. Se você habilitar ou não configurar esta política, os usuários poderão habilitar a capacidade de substituição da validação de segurança SHA256 nas configurações do Gerenciador de Pacotes do Windows. Se você desabilitar esta política, os usuários não poderão habilitar a capacidade de substituição da validação de segurança SHA256 nas configurações do Gerenciador de Pacotes do Windows. Habilitar Gerenciador de Pacotes do Windows Verificação de Malware de Arquivo Morto Local Esta política controla a capacidade de substituir verificações de vulnerabilidade de malware ao instalar um arquivo morto usando um manifesto local usando os argumentos de linha de comando. Se você habilitar essa política, os usuários poderão substituir a verificação de malware ao executar uma instalação de manifesto local de um arquivo morto. Se você desabilitar essa política, os usuários não poderão substituir a verificação de malware de um arquivo morto durante a instalação usando um manifesto local. Se você não configurar essa política, as configurações Gerenciador de Pacotes do Windows administrador serão aderidas. Habilitar a Fonte Padrão do Gerenciador de Pacotes do Windows Esta política controla a fonte padrão incluída no Gerenciador de Pacotes do Windows. Se você não definir essa configuração, a fonte padrão do Gerenciador de Pacotes do Windows estará disponível e poderá ser removida. Se você habilitar essa configuração, a fonte padrão para o Gerenciador de Pacotes do Windows estará disponível e não poderá ser removida. Se você desabilitar esta configuração, a fonte padrão para o Gerenciador de Pacotes do Windows não estará disponível. Habilitar a Fonte da Microsoft Store do Gerenciador de Pacotes do Windows Esta política controla a fonte da Microsoft Store incluída com o Gerenciador de Pacotes do Windows. Se você não definir essa configuração, a fonte da Microsoft Store para o Gerenciador do Pacote Windows estará disponível e poderá ser removida. Se você habilitar essa configuração, a fonte da Microsoft Store para o Gerenciador de Pacotes do Windows estará disponível e não poderá ser removida. Se você desabilitar esta configuração, a fonte da Microsoft Store para o Gerenciador de Pacotes do Windows não estará disponível. Habilitar origem da fonte do Gerenciador de pacotes do Windows Essa política controla a origem da fonte incluída no Gerenciador de pacotes do Windows. Se você não definir essa configuração, a origem da fonte do Gerenciador de pacotes do Windows ficará disponível e poderá ser removida. Se você habilitar essa configuração, a origem da fonte do Gerenciador de pacotes do Windows ficará disponível e não poderá ser removida. Se você desabilitar essa configuração, a origem da fonte do Gerenciador de pacotes do Windows não ficará disponível. Definir o Intervalo de Atualização Automática da Fonte do Gerenciador de Pacotes do Windows em Minutos Esta política controla o intervalo de atualização automática para origens baseadas em pacote. A fonte padrão para Gerenciador de Pacotes do Windows é configurada de modo que um índice dos pacotes seja armazenado em cache no computador local. O índice é baixado quando um usuário invoca um comando e o intervalo é passado. Se você desabilitar ou não definir essa configuração, o intervalo padrão ou o valor especificado na Gerenciador de Pacotes do Windows configurações serão usadas. Se você habilitar essa configuração, o número de minutos especificado será usado pelo Gerenciador de Pacotes do Windows. Habilitar Fontes Adicionais do Gerenciador de Pacotes do Windows Esta política controla as fontes adicionais fornecidas pelo administrador de TI da empresa. Se você não configurar esta política, nenhuma fonte adicional será configurada para o Gerenciador de Pacotes do Windows. Se você habilitar essa política, as fontes adicionais serão adicionadas ao Gerenciador de Pacotes do Windows e não poderão ser removidas. A representação de cada fonte adicional poderá ser obtida a partir das fontes instaladas usando a 'exportação de fonte de winget'. Se você desabilitar esta política, nenhuma fonte adicional poderá ser configurada para o Gerenciador de Pacotes do Windows. Habilitar Fontes Permitidas do Gerenciador de Pacotes do Windows Esta política controla as fontes adicionais permitidas pelo administrador de TI da empresa. Se você não configurar esta política, os usuários poderão adicionar ou remover fontes adicionais além daquelas configuradas pela política. Se você habilitar essa política, apenas as fontes especificadas poderão ser adicionadas ou removidas do Gerenciador de Pacotes do Windows. A representação de cada fonte permitida pode ser obtida a partir das fontes instaladas usando a 'exportação de fonte de winget'. Se você desabilitar esta política, nenhuma fonte adicional poderá ser configurada para o Gerenciador de Pacotes do Windows. Ativar o protocolo ms-appinstaller do Instalador de Aplicativo Esta política controla se os usuários podem instalar pacotes de um site que esteja usando o protocolo ms-appinstaller. Se você habilitar essa configuração, os usuários poderão instalar pacotes de sites que usam esse protocolo. Se você desabilitar ou não definir essa configuração, os usuários não poderão instalar pacotes de sites que usam esse protocolo. Habilitar interfaces de linha de comando do Gerenciador de Pacotes do Windows Essa política determina se um usuário pode executar uma ação usando o Gerenciador de Pacotes do Windows por meio de uma interface de linha de comando (CLI do WinGet ou WinGet PowerShell). Se você desabilitar essa política, os usuários não poderão executar a CLI do Gerenciador de Pacotes do Windows e os cmdlets do PowerShell. Se você habilitar ou não configurar essa política, os usuários poderão executar os comandos CLI do Gerenciador de Pacotes do Windows e os cmdlets do PowerShell. (A política “Habilitar Instalador de Aplicativo” fornecida não está desabilitada). Essa política não substitui a política "Habilitar Instalador de Aplicativo". Habilitar as Configurações do Gerenciador de Pacotes do Windows Esta política controla se o Gerenciador de Pacotes do Windows de configuração pode ser usado pelos usuários. Se você habilitar ou não definir essa configuração, os usuários poderão usar o recurso Gerenciador de Pacotes do Windows configuração. Se você desabilitar essa configuração, os usuários não poderão usar o recurso Gerenciador de Pacotes do Windows configuração. Habilitar Gerenciador de Pacotes do Windows de linha de comando proxy Esta política controla se o uso Gerenciador de Pacotes do Windows proxy pode ser configurado pelos usuários por meio da linha de comando. Se você habilitar essa configuração, os usuários poderão configurar o Gerenciador de Pacotes do Windows do proxy por meio da linha de comando. Se você desabilitar ou não definir essa configuração, os usuários não poderão configurar o uso Gerenciador de Pacotes do Windows do proxy por meio da linha de comando. Habilitar o servidor MCP para o Gerenciador de pacotes do Windows Esta política controla se o servidor Gerenciador de Pacotes do Windows MCP (Protocolo de Contexto de Modelo) pode ser usado. Se você habilitar ou não definir essa configuração, os usuários poderão usar o Gerenciador de Pacotes do Windows MCP do servidor. Se você desabilitar essa configuração, os usuários não poderão usar o Gerenciador de Pacotes do Windows MCP do servidor. Definir Proxy Padrão do Gerenciador de Pacotes do Windows Esta política controla o proxy padrão usado pelo Gerenciador de Pacotes do Windows. Se você desabilitar ou não definir essa configuração, nenhum proxy será usado por padrão. Se você habilitar essa configuração, o proxy especificado será usado por padrão. Habilitar Zonas Permitidas do Instalador de Aplicativo para Pacotes MSIX Esta política controla se Instalador de Aplicativo permite a instalação de pacotes originados de Zonas de URL específicas. A origem de um pacote é determinada por seu URI e se um Mart-of-the-Web (MotW) está presente. Se vários URIs forem envolvidos, todos eles serão considerados; por exemplo, ao usar um arquivo .appinstaller que envolve redirecionamento. Se você habilitar essa política, os usuários poderão instalar pacotes MSIX de acordo com a configuração de cada zona. Se você desabilitar ou não configurar esta política, os usuários poderão instalar pacotes MSIX de qualquer zona, exceto por Não Confiável. Permitir Bloquear Habilitar verificações do Microsoft SmartScreen para Pacotes MSIX Esta política controla se Instalador de Aplicativo verificações do Microsoft SmartScreen ao instalar pacotes MSIX. Se você habilitar ou não configurar essa política, o URI do pacote será avaliado com o Microsoft SmartScreen antes da instalação. Este marcar é feito somente para pacotes provenientes da Internet. Se você desabilitar, o Microsoft SmartScreen não será consultado antes de instalar um pacote. Intervalo de atualização automática da fonte em minutos Fontes Adicionais: Fontes Permitidas: Computador Local Intranet Sites Confiáveis Internet Sites Não Confiáveis ================================================ FILE: Localization/Policies/ru-RU/DesktopAppInstaller.adml ================================================ Установщик приложений Установщик приложений Установщик классических приложений Включить Диспетчер пакетов Windows Эта политика определяет, могут ли пользователи использовать Диспетчер пакетов Windows. Если включить или не настроить этот параметр, пользователи смогут использовать Диспетчер пакетов Windows. Если отключить этот параметр, пользователи не смогут использовать Диспетчер пакетов Windows. Включить параметры Диспетчера пакетов Windows Эта политика определяет, могут ли пользователи изменять свои параметры. Если включить или не настроить этот параметр, пользователи смогут изменять параметры Диспетчера пакетов Windows. Если отключить этот параметр, пользователи не смогут изменять параметры Диспетчера пакетов Windows. Включить экспериментальные функции Диспетчера пакетов Windows Этот параметр политики определяет, могут ли пользователи включать экспериментальные функции в диспетчер пакетов Windows. Если включить или не настроить этот параметр, пользователи смогут включать экспериментальные функции для диспетчер пакетов Windows. Если этот параметр отключен, пользователи не смогут включать экспериментальные функции для диспетчер пакетов Windows. Включить файлы локальных манифестов Диспетчера пакетов Windows Эта политика определяет, могут ли пользователи устанавливать пакеты с локальными файлами манифестов. Если включить или не настроить этот параметр, пользователи смогут устанавливать пакеты с локальными манифестами с помощью Диспетчера пакетов Windows. Если отключить этот параметр, пользователи не смогут устанавливать пакеты с локальными манифестами с помощью Диспетчера пакетов Windows. Включить обход проверки сертификата источника Microsoft Store в Диспетчере пакетов Windows Эта политика определяет, будет ли Диспетчер пакетов Windows проверять соответствие хэша сертификата Microsoft Store известному сертификату Microsoft Store при инициации подключения к источнику Microsoft Store. При включении этой политики диспетчер пакетов Windows будет обходить проверку сертификата Microsoft Store. При отключении этой политики диспетчер пакетов Windows будет проверять принадлежность используемого сертификата Microsoft Store и его действительность, прежде чем связываться с источником Microsoft Store.. Если эта политика не настроена, будут соблюдаться параметры администратора диспетчера пакетов Windows. Включить переопределение хэша Диспетчера пакетов Windows Этот параметр политики определяет, можно ли настроить Диспетчер пакетов Windows, чтобы включить возможность переопределения параметров проверки безопасности SHA256 в настройках. Если данный параметр политики включен или не настроен, пользователи получат возможность переопределять проверку безопасности SHA256 в параметрах Диспетчера пакетов Windows. Если данный параметр политики отключен, пользователи не смогут переопределять проверку безопасности SHA256 в параметрах Диспетчера пакетов Windows. Включить переопределение сканирования вредоносных программ в локальном архиве Диспетчера пакетов Windows Эта политика управляет возможностью переопределения сканирования уязвимостей вредоносных программ при установке файла архива с использованием локального манифеста с использованием аргументов командной строки. При включении этой политики пользователи смогут переопределять сканирование вредоносных программ при выполнении локальной установки манифеста архивного файла. При отключении этой политики пользователи не смогут переопределять сканирование архивного файла на наличие вредоносных программ при установке с использованием локального манифеста. Если эта политика не настроена, будут соблюдаться параметры администратора диспетчера пакетов Windows. Включить источник по умолчанию для Диспетчера пакетов Windows Эта политика управляет источником по умолчанию, входящим в состав Диспетчера пакетов Windows. Если не задать этот параметр, источник по умолчанию для Диспетчера пакетов Windows будет доступен, и его можно будет удалить. Если включить этот параметр, источник по умолчанию для Диспетчера пакетов Windows будет доступен, и его нельзя будет удалить. Если отключить этот параметр, источник по умолчанию для Диспетчера пакетов Windows будет недоступен. Включить источник Microsoft Store в Диспетчере пакетов Windows Эта политика управляет источником Microsoft Store, входящим в состав Диспетчера пакетов Windows. Если не задать этот параметр, источник Microsoft Store для Диспетчера пакетов Windows будет доступен, и его можно будет удалить. Если включить этот параметр, источник Microsoft Store для Диспетчера пакетов Windows будет доступен, и его нельзя будет удалить. Если отключить этот параметр, источник Microsoft Store для Диспетчера пакетов Windows будет недоступен. Включить источник шрифтов для Диспетчера пакетов Windows Эта политика управляет источником шрифтов, входящим в состав Диспетчера пакетов Windows. Если этот параметр не настроен, источник шрифтов для Диспетчера пакетов Windows будет доступен, и его можно будет удалить. Если этот параметр включен, источник шрифтов для Диспетчера пакетов Windows будет доступен, и его нельзя будет удалить. Если этот параметр отключен, источник шрифтов для Диспетчера пакетов Windows будет недоступен. Задать интервал автоматического обновления источника Диспетчера пакетов Windows в минутах Эта политика управляет интервалом автоматического обновления для источников на основе пакетов. Источник по умолчанию для Диспетчера пакетов Windows настроен таким образом, что индекс пакетов кэшируется на локальном компьютере. Индекс скачивается при вызове команды пользователем и завершении интервала. Если этот параметр отключен или не настроен, будет использоваться интервал по умолчанию или значение, указанное в параметрах Диспетчера пакетов Windows. Если этот параметр включен, указанное число минут будет использоваться Диспетчером пакетов Windows. Включить дополнительные источники Диспетчера пакетов Windows Эта политика управляет дополнительными источниками, предоставленными ИТ-администратором предприятия. Если не настроить эту политику, дополнительные источники для Диспетчера пакетов Windows не будут настроены. Если включить эту политику, дополнительные источники будут добавлены в Диспетчер пакетов Windows, и их нельзя будет удалить. Представление для каждого дополнительного источника можно получить из установленных источников с помощью команды "winget source export". Если отключить эту политику, задать дополнительные источники для Диспетчера пакетов Windows будет невозможно. Включить разрешенные источники Диспетчера пакетов Windows Эта политика управляет дополнительными источниками, разрешенными ИТ-администратором предприятия. Если не настроить эту политику, пользователи смогут добавлять и удалять дополнительные источники помимо заданных в политике. Если включить эту политику, добавлять и удалять из Диспетчера пакетов Windows можно будет только указанные источники,. Представление для каждого разрешенного можно получить из установленных источников с помощью команды "winget source export". Если отключить эту политику, задать дополнительные источники для Диспетчера пакетов Windows будет невозможно. Включить протокол Установщика приложений ms-appinstaller Эта политика определяет, могут ли пользователи устанавливать пакеты с веб-сайта, который использует протокол ms-appinstaller. Если включить этот, пользователи смогут устанавливать пакеты с веб-сайтов, которые используют этот протокол. Если отключить или не настраивать этот параметр, пользователи не смогут устанавливать пакеты с веб-сайтов, которые используют этот протокол. Включить интерфейсы командной строки Диспетчера пакетов Windows Эта политика определяет, может ли пользователь выполнять действие с помощью диспетчера пакетов Windows через интерфейс командной строки (WinGet CLI или WinGet PowerShell). Если вы отключите эту политику, пользователи не смогут выполнять CLI диспетчера пакетов Windows и командлеты PowerShell. Если вы включите или не настроите эту политику, пользователи смогут выполнять команды CLI диспетчера пакетов Windows и командлеты PowerShell. (При условии, что политика "Включить установщик приложений" не отключена). Эта политика не отменяет политику "Включить установщик приложений". Включить конфигурацию Диспетчера пакетов Windows Эта политика определяет, Диспетчер пакетов Windows ли пользователи могут использовать эту Диспетчер пакетов Windows конфигурацию. Если этот параметр включен или не настроен, пользователи смогут использовать Диспетчер пакетов Windows конфигурации. Если этот параметр отключен, пользователи не смогут использовать Диспетчер пакетов Windows конфигурации. Включить Диспетчер пакетов Windows командной строки прокси-сервера Эта политика определяет, может ли Диспетчер пакетов Windows использование прокси-сервера настраивать пользователи с помощью командной строки. Если этот параметр включен, пользователи смогут Диспетчер пакетов Windows использовать прокси-сервер в командной строке. Если вы отключаете или не настраиваете этот параметр политики, пользователи не смогут Диспетчер пакетов Windows использовать прокси-сервер в командной строке. Включить сервер MCP для Диспетчера пакетов Windows Эта политика управляет тем, можно ли использовать сервер протокола контекста модели (MCP) Диспетчера пакетов Windows. Если этот параметр включен или не настроен, пользователи смогут применять сервер MCP Диспетчера пакетов Windows. Если этот параметр отключен, пользователи не смогут применять сервер MCP Диспетчера пакетов Windows. Задать прокси по умолчанию для Диспетчера пакетов Windows Эта политика управляет прокси-сервером по умолчанию, используемым Диспетчер пакетов Windows. Если этот параметр отключен или не настроен, по умолчанию прокси-сервер не будет использоваться. Если этот параметр включен, указанный прокси-сервер будет использоваться по умолчанию. Включить разрешенные зоны Установщика приложений для пакетов MSIX Эта политика определяет, Установщик приложений ли установка пакетов, полученных из определенных зон URL-адресов. Источник пакета определяется его URI и наличием киоска в Интернете (MotW). Если включено несколько URI, все они считаются; например, при использовании appinstaller-файла, который включает перенаправление. Если эта политика включена, пользователи смогут устанавливать пакеты MSIX в соответствии с конфигурацией для каждой зоны. Если эта политика отключена или не настроена, пользователи смогут устанавливать пакеты MSIX из любой зоны, кроме ненадежных. Разрешить Блокировать Включить проверки Microsoft SmartScreen для пакетов MSIX Эта политика определяет, Установщик приложений ли фильтр SmartScreen (Майкрософт) при установке пакетов MSIX. Если эта политика включена или не настроена, URI пакета будет проверяться с помощью фильтра SmartScreen (Майкрософт) перед установкой. Эта проверка только для пакетов, поступающих из Интернета. Если отключить этот параметр, microsoft SmartScreen не будет проверяться перед установкой пакета. Интервал автоматического обновления источника в минутах Дополнительные источники: Разрешенные источники: Локальный компьютер Интрасеть Надежные сайты Интернет Ненадежные сайты ================================================ FILE: Localization/Policies/zh-CN/DesktopAppInstaller.adml ================================================ 应用安装程序 应用安装程序 桌面应用安装程序 启用 Windows 程序包管理器 此策略控制用户是否可以使用 Windows 程序包管理器。 如果启用或未配置此设置,则用户可以使用 Windows 程序包管理器。 如果禁用此设置,则用户将无法使用 Windows 程序包管理器。 启用 Windows 程序包管理器设置 此策略控制用户是否可以更改其设置。 如果启用或未配置此设置,则用户可以更改 Windows 程序包管理器的设置。 如果禁用此设置,则用户将无法更改 Windows 程序包管理器的设置。 启用 Windows 程序包管理器实验性功能 此策略控制用户是否可以在 Windows 程序包管理器中启用实验性功能。 如果启用或未配置此设置,则用户将能够为 Windows 包管理器启用实验性功能。 如果禁用此设置,则用户将无法启用 Windows 包管理器的实验性功能。 启用 Windows 程序包管理器本地清单文件 此策略控制用户是否可以使用本地清单文件安装程序包。 如果启用或未配置此设置,则用户可以使用 Windows 程序包管理器在本地清单中安装包。 如果禁用此设置,则用户将无法使用 Windows 程序包管理器在本地清单中安装包。 启用Windows 程序包管理器Microsoft Store源证书验证绕过 此策略控制 Windows 程序包管理器在发起与 Microsoft Store 源的连接时是否验证 Microsoft Store 证书哈希与已知的 Microsoft Store 证书的匹配情况。 如果启用此策略,Windows 程序包管理器将绕过 Microsoft Store 证书验证。 如果禁用此策略,则 Windows 程序包管理器在与 Microsoft Store 源通信之前,将验证所使用的 Microsoft Store 证书是否有效且属于 Microsoft Store。 如果未配置此策略,则将遵循 Windows 程序包管理器的管理员设置。 启用 Windows 程序包管理器哈希替代 此策略控制是否可以配置 Windows 程序包管理器,以便在设置中启用覆盖 SHA256 安全验证的能力。 如果启用或未配置此策略,则用户将能够在 Windows 程序包管理器设置中启用覆盖 SHA256 安全验证的能力。 如果禁用此策略,则用户将无法在 Windows 程序包管理器设置中启用覆盖 SHA256 安全验证的能力。 启用Windows 程序包管理器本地存档恶意软件扫描替代 此策略控制在使用命令行参数通过本地清单安装存档文件时替代恶意软件漏洞扫描的能力。 如果启用此策略,则用户可以在对存档文件执行本地清单安装时替代恶意软件扫描。 如果禁用此策略,则在使用本地清单进行安装时,用户将无法替代对存档文件的恶意软件扫描。 如果未配置此策略,则将遵循 Windows 程序包管理器的管理员设置。 启用 Windows 程序包管理器默认源 此策略控制 Windows 程序包管理器中包含的默认源。 如果未配置此设置,Windows 程序包管理器的默认源将可用并可删除。 如果启用此设置,Windows 程序包管理器的默认源将可用,无法删除。 如果禁用此设置,Windows 程序包管理器的默认源将不可用。 启用 Windows 程序包管理器 Microsoft Store 源 此策略控制 Windows 程序包管理器中包含的 Microsoft Store 源。 如果未配置此设置,Windows 程序包管理器的 Microsoft Store 源将可用并可删除。 如果启用此设置,Windows 程序包管理器的 Microsoft Store 源将可用,无法删除。 如果禁用此设置,Windows 程序包管理器的 Microsoft Store 源将不可用。 启用 Windows 程序包管理器字体源 此策略控制 Windows 程序包管理器中包含的字体源。 如果未配置此设置,则 Windows 程序包管理器的字体源将可用并且可以移除。 如果启用此设置,则 Windows 程序包管理器的字体源将可用且无法移除。 如果禁用此设置,则 Windows 程序包管理器的字体源将不可用。 设置 Windows 程序包管理器源自动更新间隔(分钟) 此策略控制基于程序包的源的自动更新间隔。配置Windows 程序包管理器的默认源,以便在本地计算机上缓存包索引。当用户调用命令并且间隔已过时,将下载索引。 如果禁用或未配置此设置,将使用Windows 程序包管理器设置中指定的默认间隔或值。 如果启用此设置,则Windows 程序包管理器将使用指定的分钟数。 启用 Windows 程序包管理器其他源 此策略控制企业 IT 管理员提供的其他源。 如果未配置此策略,则将不会为 Windows 程序包管理器配置其他源。 如果启用此策略,则将向 Windows 程序包管理器添加其他源并且这些源不可删除。可使用“winget source export”从已安装的源获取每个其他源的表示形式。 如果禁用此策略,则不能为 Windows 程序包管理器配置其他源。 启用 Windows 程序包管理器允许的源 此策略控制企业 IT 管理员允许的其他源。 如果未配置此策略,用户将能够添加或删除策略配置的源之外的其他源。 如果启用此策略,则只能从 Windows 程序包管理器中添加或删除指定的源。可使用“winget source export”从已安装的源获取每个允许的源的表示形式。 如果禁用此策略,则不能为 Windows 程序包管理器配置其他源。 启用应用安装程序 ms-appinstaller 协议 此策略控制用户是否可以从使用 ms-appinstaller 协议的网站安装包。 如果启用此设置,则用户将能够安装来自使用此协议的网站的包。 如果禁用或未配置此设置,则用户将无法安装来自使用此协议的网站的包。 启用 Windows 程序包管理器命令行接口 此策略确定用户是否可以通过命令行界面(WinGet CLI 或 WinGet PowerShell)使用 Windows 程序包管理器执行操作。 如果禁用此策略,用户将无法执行 Windows 程序包管理器 CLI 和 PowerShell cmdlet。 如果启用或未配置此策略,用户将能够执行 Windows 程序包管理器 CLI 命令和 PowerShell cmdlet。(前提是 “启用应用安装程序” 策略未禁用)。 此策略不会替代 “启用应用安装程序” 策略。 启用 Windows 程序包管理器配置 此策略控制用户是否可以使用Windows 程序包管理器配置功能。 如果启用或未配置此设置,则用户将能够使用Windows 程序包管理器配置功能。 如果禁用此设置,则用户将无法使用Windows 程序包管理器配置功能。 启用Windows 程序包管理器代理命令行选项 此策略控制用户是否可以通过命令行配置代理的Windows 程序包管理器使用情况。 如果启用此设置,则用户将能够通过命令行配置Windows 程序包管理器使用代理。 如果禁用或未配置此设置,则用户将无法通过命令行配置Windows 程序包管理器使用代理。 为 Windows 程序包管理器启用 MCP 服务器 此策略控制是否可以使用Windows 程序包管理器模型上下文协议 (MCP) 服务器。 如果启用或未配置此设置,则用户将能够使用Windows 程序包管理器的 MCP 服务器。 如果禁用此设置,则用户将无法使用Windows 程序包管理器的 MCP 服务器。 设置 Windows 程序包管理器默认代理 此策略控制Windows 程序包管理器使用的默认代理。 如果禁用或未配置此设置,则默认情况下不会使用任何代理。 如果启用此设置,则默认情况下将使用指定的代理。 为 MSIX 包启用应用安装程序允许的区域 此策略控制应用安装程序是否允许安装源自特定 URL 区域的包。包的来源由其 URI 以及是否存在 Mart-of-the-Web (MotW) 来确定。如果涉及多个 URI,则会考虑所有这些 URI;例如,使用涉及重定向的 .appinstaller 文件时。 如果启用此策略,则用户将能够根据每个区域的配置安装 MSIX 包。 如果禁用或未配置此策略,则用户将能够从除不受信任之外的任何区域安装 MSIX 包。 允许 阻止 为 MSIX 包启用 Microsoft SmartScreen 检查 此策略控制应用安装程序在安装 MSIX 包时是否执行Microsoft SmartScreen 检查。 如果启用或未配置此策略,则在安装之前,将使用 Microsoft SmartScreen 评估包 URI。此检查仅适用于来自 Internet 的程序包。 如果禁用,则在安装包之前,不会咨询Microsoft SmartScreen。 源自动更新间隔(分钟) 其他源: 允许的源: 本地计算机 Intranet 受信任的站点 Internet 不受信任的站点 ================================================ FILE: Localization/Policies/zh-TW/DesktopAppInstaller.adml ================================================ 應用程式安裝程式 應用程式安裝程式 傳統型應用程式安裝程式 啟用 Windows 封裝管理員 此原則可控制使用者是否可以使用 Windows 套件管理員。 如果您啟用或未設定這個設定,使用者將可以使用 Windows 套件管理員。 如果停用此設定,使用者將無法使用 Windows 套件管理員。 啟用 Windows 封裝管理員設定 此原則可控制使用者是否可以變更其設定。 如果您啟用或未設定這個設定,使用者將可以變更 Windows 套件管理員的設定。 如果停用此設定,使用者將無法變更 Windows 套件管理員的設定。 啟用 Windows 封裝管理員實驗性功能 此原則可控制使用者是否能在 Windows 套件管理員中啟用實驗性功能。 如果啟用或未設定此設定,使用者將能夠啟用 Windows 套件管理員的實驗性功能。 如果停用此設定,使用者將無法啟用 Windows 套件管理員的實驗性功能。 啟用 Windows 封裝管理員本機資訊清單檔案 此原則可控制使用者是否能安裝具有本機資訊清單檔案的套件。 如果您啟用或未設定這個設定,使用者將可以使用 Windows 套件管理員來安裝具有本機資訊清單的套件。 若停用此設定,使用者將無法使用 Windows 套件管理員來安裝具有本機資訊清單的套件。 啟用 Windows 封裝管理員 Microsoft Store來源憑證驗證略過 此原則可控制 Windows 封裝管理員在起始 Microsoft Store 來源的連線時,是否要驗證與已知 Microsoft Store 憑證相符的 Microsoft Store 憑證雜湊。 如果您啟用這個原則,Windows 封裝管理員將會略過 Microsoft Store 憑證驗證。 如果停用此原則,則 Windows 封裝管理員會在與 Microsoft Store 來源通訊之前,驗證使用的 Microsoft Store 憑證有效且屬於 Microsoft Store。 如果您未設定這個原則,則會遵循 Windows 封裝管理員系統管理員設定。 啟用 Windows 封裝管理員雜湊覆寫 此原則會控制 Windows 封裝管理員是否可以設定以便啟用在設定中覆寫 SHA256 安全性驗證的能力。 如果您啟用或未設定此原則,使用者就可以在 Windows 封裝管理員設定中啟用覆寫 SHA256 安全性驗證的能力。 如果您停用此原則,使用者就無法在 Windows 封裝管理員設定中啟用覆寫 SHA256 安全性驗證的能力。 啟用 Windows 封裝管理員 本機封存惡意代碼掃描覆寫 此原則控制在使用本機資訊清單使用命令列引數安裝封存檔案時,是否可以覆寫惡意程式碼弱點掃描。 如果您啟用這個原則,使用者可以在執行封存檔案的本機資訊清單安裝時覆寫惡意程式碼掃描。 如果您停用這個原則,使用者將無法在使用本機資訊清單安裝時覆寫封存檔案的惡意程式碼掃描。 如果您未設定這個原則,則會遵循Windows 封裝管理員系統管理員設定。 設定 Windows 封裝管理員預設來源 此原則控制 Windows 封裝管理員隨附的預設來源。 如果您未設定此設定,Windows 封裝管理員的預設來源將可供使用,而且可以移除。 如果您啟用此設定,Windows 封裝管理員的預設來源將可供使用,且無法移除。 如果您停用此設定,Windows 封裝管理員的預設來源將無法使用。 啟用 Windows 封裝管理員 Microsoft Store 來源 此原則控制 Windows 封裝管理員隨附的 Microsoft Store 來源。 如果您未設定此設定,Windows 封裝管理員的 Microsoft Store 來源將可供使用,而且可以移除。 如果您啟用此設定,Windows 封裝管理員的 Microsoft Store 來源將可供使用,且無法移除。 如果您停用此設定,Windows 封裝管理員的 Microsoft Store 來源將無法使用。 設定 Windows 封裝管理員字型來源 此原則控制 Windows 封裝管理員隨附的字型來源。 如果您未設定此設定,Windows 封裝管理員的字型來源將可供使用,而且可以移除。 如果您啟用此設定,Windows 封裝管理員的字型來源將可供使用,且無法移除。 如果您停用此設定,Windows 封裝管理員的字型來源將無法使用。 設定 Windows 封裝管理員來源自動更新間隔 (分鐘) 此原則控制封裝型來源的自動更新間隔時間。Windows 封裝管理員的預設來源設定為本機電腦上快取的套件索引。當使用者調用命令且間隔已過時,下載索引。 如果您停用或不設定此設定,將會使用 Windows 封裝管理員設定中指定的預設間隔或值。 如果您啟用此設定,Windows 封裝管理員將會使用指定的分鐘數。 啟用 Windows 封裝管理員其他來源 此原則控制企業 IT 系統管理員提供的其他來源。 如果您未設定此原則,將不會為 Windows 封裝管理員設定其他來源。 如果您啟用此原則,其他來源將會新增到 Windows 封裝管理員,而且無法移除。每個其他來源的標記法都可以使用 'winget 來源匯出' 從已安裝的來源取得。 如果您停用此原則,則 Windows 封裝管理員無法為其他來源進行設定。 設定 Windows 封裝管理員允許來源 此原則控制企業 IT 系統管理員允許的其他來源。 如果您未設定此原則,使用者將可以新增或移除其他非此原則設定的來源。 如果您啟用此原則,僅可以從 Windows 封裝管理員新增或移除指定的來源。每個允許來源的代表都可以從使用 'winget 來源匯出' 的已安裝來源取得。 如果您停用此原則,則 Windows 封裝管理員無法為其他來源進行配置。 啟用應用程式安裝程式 ms-appinstaller 通訊協定 此原則控制使用者是否可以從使用 ms-appinstaller 通訊協定的網站安裝套件。 如果您啟用這個設定,使用者將可以從使用此通訊協定的網站安裝套件。 如果您停用或未設定這個設定,使用者將無法從使用此通訊協定的網站安裝套件。 啟用 Windows 封裝管理員命令列介面 此原則會判斷使用者是否可透過命令列介面 (WinGet CLI 或 WinGet PowerShell) 使用 Windows 封裝管理員執行動作。 如果停用此原則,使用者將無法執行 Windows 封裝管理員 CLI 和 PowerShell Cmdlet。 如果啟用或未設定此原則,使用者將可以執行 Windows 封裝管理員 CLI 命令和 PowerShell Cmdlet。(未停用提供的 [啟用應用程式安裝程式] 原則)。 此原則不會覆寫 [啟用應用程式安裝程式] 原則。 啟用 Windows 封裝管理員設定 此原則控制使用者是否可以使用Windows 封裝管理員設定功能。 如果您啟用或未設定這個設定,使用者將可以使用Windows 封裝管理員設定功能。 如果您停用這個設定,使用者將無法使用Windows 封裝管理員設定功能。 啟用Windows 封裝管理員 Proxy 命令行選項 此原則控制使用者是否可以透過命令行設定 proxy 的Windows 封裝管理員使用方式。 如果您啟用這個設定,使用者將可以透過命令行設定Windows 封裝管理員使用 Proxy。 如果您停用或未設定這個設定,用戶將無法透過命令行設定Windows 封裝管理員使用 Proxy。 啟用 Windows 封裝管理員的 MCP 伺服器 此原則用來控制是否允許使用 Windows 封裝管理員的模型內容通訊協定 (MCP) 伺服器。 如果您啟用或未設定此原則,使用者將能使用 Windows 封裝管理員的 MCP 伺服器。 如果您停用此原則,使用者將無法使用 Windows 封裝管理員的 MCP 伺服器。 設定 Windows 封裝管理員預設 Proxy 此原則控制Windows 封裝管理員使用的預設 Proxy。 如果停用或未設定此設定,則預設不會使用任何 Proxy。 如果您啟用這個設定,預設會使用指定的 Proxy。 為 MSIX 套件啟用應用程式安裝程式允許的區域 此原則控制應用程式安裝程式是否允許安裝來自特定 URL 區域的套件。套件的來源取決於其 URI,以及是否存在 Mart-of the Web (MotW)。如果涉及多個 URI,則會考慮所有 URI;例如,使用涉及重新導向的 .appinstaller 檔案時。 如果您啟用這個原則,使用者將可以根據每個區域的設定來安裝 MSIX 套件。 如果您停用或未設定這個原則,除了不受信任之外,使用者將可以從任何區域安裝 MSIX 套件。 允許 阻止 啟用 MSIX 套件的 Microsoft SmartScreen 檢查 此原則可控制應用程式安裝程式在安裝 MSIX 套件時是否執行Microsoft SmartScreen 檢查。 如果啟用或未設定此原則,則會在安裝前使用 Microsoft SmartScreen 評估套件 URI。這項檢查只會針對來自因特網的套件執行。 如果停用,Microsoft安裝套件前將不會詢問 SmartScreen。 來源自動更新間隔 (分鐘) 其他來源: 允許的來源: 本機電腦 內部網路 信任的網站 網際網路 未受信任的網站 ================================================ FILE: Localization/Resources/de-DE/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/de-DE/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Angrenzender Alias ist kein Kennzeichen: „{0}“ {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Angrenzender Kennzeichenalias nicht gefunden: „{0}“ {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Die folgenden Argumente sind verfügbar: Message displayed to inform the user about the available command line arguments. Die folgenden Befehlsaliase sind verfügbar: Message displayed to inform the user about the available command line alias arguments. Folgende Befehle sind verfügbar: Title displayed to inform the user about the available commands. Verfügbar As in "a new version is available to upgrade to". Die folgenden Optionen stehen zur Verfügung: Message displayed to inform the user about the available options. Folgende Unterbefehle sind verfügbar: Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} Aktualisierungen verfügbar. {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Verwendet den angegebenen Kanal. Der Standard ist die allgemeine Benutzergruppe Befehl Label displayed for a command to give the software. Ergebnisse nach Befehl filtern Description message displayed to inform the user about filtering the search results by a package command. Die vollständige Befehlszeile zur Vervollständigung Zum Ausführen dieses Befehls sind Administratorberechtigungen erforderlich. Dieser Befehl kann verwendet werden, um eine kontextbezogene Befehlszeilenvervollständigung anzufordern. Die Befehlszeile, die Cursorposition und das zu vervollständigende Wort werden übergeben. Die Ausgabe ist eine Reihe potenzieller Werte basierend auf den Eingaben, mit einem möglichen Wert pro Zeile. Aktiviert die kontextbezogene Befehlszeilenvervollständigung. Nicht mehr als die angegebene Anzahl von Ergebnissen anzeigen (zwischen 1 und 1000) Fertig Label displayed when an operation completes or is done executing. Suchen eines Pakets mithilfe exakter Übereinstimmung Description message displayed to inform the user about finding an application package using an exact matching criteria. Experimentelles Argument für Demonstrationszwecke Dieser Befehl ist ein Beispiel für das Implementieren eines experimentellen Features. Rufen Sie zum Aktivieren “winget settings” auf und aktivieren Sie “experimentalCmd” oder “experimentalArg”-Features. {Locked="winget settings"} Beispiel eines experimentellen Features Ein positionelles Argument wurde gefunden, obwohl keines erwartet wurde: „{0}“ {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. Diese Funktion ist noch in Entwicklung und kann in Zukunft geändert oder entfernt werden. Um sie zu aktivieren, bearbeiten Sie Ihre Einstellungen („winget settings“) und fügen Sie das experimentelle Feature hinzu: „{0}“ {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Zeigt den Status von experimentellen Features an. Experimentelle Features können mithilfe von “winget settings” aktiviert werden. {Locked="winget settings"} Zeigt den Status von experimentellen Features an Deaktiviert Aktiviert Funktion Link Die folgenden experimentellen Features werden zurzeit ausgeführt. Sie können über die Einstellungsdatei „winget settings“ konfiguriert werden. {Locked="winget settings"} Eigenschaft Status Zu hashende Datei Flagargument darf keinen angrenzenden Wert enthalten: „{0}“ {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Berechnet den Hash einer lokalen Datei, die für den Eintrag in ein Manifest geeignet ist. Es kann auch der Hash der Signaturdatei eines MSIX-Pakets berechnet werden, um Streaming-Installationen zu ermöglichen. Hilfsprogramm zum Hashen von Installationsdateien Zeigt Hilfe zum ausgewählten Befehl an Wenn Sie weitere Details zu einem bestimmten Befehl erfahren möchten, übergeben Sie ihm das Hilfe-Argument. Weitere Hilfe finden Sie unter: „{0}“ {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Ergebnisse nach ID filtern Unterdrückt Warnungsausgaben. Diese Anwendung wird von ihrem Besitzer an Sie lizenziert. Microsoft ist nicht verantwortlich und erteilt keine Lizenzen für Pakete von Drittanbietern. Dieses Paket wird über den Microsoft Store bereitgestellt. “winget” muss das Paket möglicherweise im Auftrag des aktuellen Benutzers aus dem Microsoft Store abrufen. {Locked="winget"} Installiert das ausgewählte Paket, entweder durch Durchsuchen einer konfigurierten Quelle oder direkt aus einem Manifest. Standardmäßig muss die Abfrage ohne Berücksichtigung der Groß- und Kleinschreibung mit id, name oder moniker des Pakets übereinstimmen. Andere Felder können verwendet werden, indem die entsprechende Option übergeben wird. Standardmäßig überprüft der Installationsbefehl den Installationsstatus des Pakets und versucht allenfalls ein Upgrade durchzuführen. Setzen Sie dies mit „--force“ außer Kraft, um eine direkte Installation auszuführen. {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Installiert das angegebene Paket Der Installer-Hash stimmt nicht überein. Dies kann nicht außer Kraft gesetzt werden, wenn Sie ihn als Administrator ausführen. Der Hash des Installationsprogramms stimmt nicht überein. Aufgrund von „--ignore-security-hash“ wird fortgefahren. {Locked="--ignore-security-hash"} Der Hash des Installationsprogramms stimmt nicht überein. Um diese Überprüfung außer Kraft zu setzen, verwenden Sie „--ignore-security-hash“. {Locked="--ignore-security-hash"} Der Installer-Hash wurde erfolgreich überprüft Erfolgreich installiert Paketinstallation wird gestartet... Fehler bei der Hashüberprüfung des Installers ignorieren Bei der Installation eines Archivtyppakets aus dem lokalen Manifest ausgeführte Schadsoftwareüberprüfung ignorieren Interaktive Installation anfordern; möglicherweise sind Benutzereingaben erforderlich. Der Argument-Alias wurde für den aktuellen Befehl nicht erkannt: „{0}“ {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Ungültige Spezifikation des Arguments: „{0}“ {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). Argumentname wurde für den aktuellen Befehl nicht erkannt: „{0}“ {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). WinGet-Verzeichnisse Header for a table detailing the directories Winget uses for key operations like logging and portable installs Zu verwendendes Gebietsschema (BCP47-Format) {Locked="BCP47"} Lizenzvereinbarung Links Links to different webpages Der Befehl „list“ zeigt die auf dem System installierten Pakete an und ob ein Upgrade verfügbar ist. Weitere Optionen können zum Filtern der Ausgabe angegeben werden, ähnlich wie der Befehl „search“. {Locked="list","search"} Installierte Pakete anzeigen Installationsspeicherort (falls unterstützt) Log-Speicherort (sofern unterstützt) Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. Startseite The primary webpage for the software Der Pfad zum Manifest des Pakets Fehler bei der Manifestüberprüfung. Die Manifestüberprüfung war erfolgreich. Die Manifestüberprüfung war mit Warnungen erfolgreich. Wert des Arguments erforderlich, aber nicht gefunden: „{0}“ {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Ergebnisse nach Moniker filtern Eingabedatei wird als msix behandelt. Signatur-Hash wird bereitgestellt, falls signiert Fehler beim Berechnen des MSIX-Signaturhashs. Installation oder Upgrade des Microsoft Store-Pakets ist fehlgeschlagen, da die betreffende App von einer Richtlinie blockiert wird. Installation oder Upgrade des Microsoft Store-Pakets ist fehlgeschlagen. Fehlercode: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Installation oder Upgrade des Microsoft Store-Pakets ist fehlgeschlagen, da der Microsoft Store-Client von einer Richtlinie blockiert wird. Überprüfen/anfordern der Paketübernahme Mehrere installierte Pakete mit übereinstimmenden Eingabekriterien gefunden. Bitte verfeinern Sie die Eingabe. Mehrere Pakete mit übereinstimmenden Eingabekriterien gefunden. Bitte verfeinern Sie die Eingabe. Ergebnisse nach Name filtern Es wurde kein anwendbarer Installer gefunden. Weitere Informationen finden Sie in den Protokollen. Derzeit sind keine experimentellen Funktionen verfügbar. Es wurde kein installiertes Paket gefunden, das den Eingabekriterien entspricht. Es wurde kein Paket gefunden, das den Eingabekriterien entspricht. Deaktiviert VirtualTerminal-Anzeige {Locked="VirtualTerminal"} Öffnen des Standardspeicherorts für Protokolle Optionen Options to change how a command works Außerkraftsetzungsargumente, die an das Installationsprogramm weitergegeben werden sollen Paket: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. Hoppla, das haben wir vergessen... Die Position des Cursors innerhalb der Befehlszeile Datenschutzerklärung Die Abfrage, mit der nach einem Paket gesucht wird Statusanzeige in Regenbogenfarben Erforderliches Argument nicht angegeben: „{0}“ {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Statusanzeige in Standardfarbe Sucht nach Paketen aus konfigurierten Quellen. Suchen und Anzeigen grundlegender Informationen zu Paketen ID Abbreviation of Identifier. Übereinstimmung Name Quelle zusätzliche Einträge wegen Ergebnisgrenzwert abgeschnitten Version Die folgenden Fehler wurden beim Überprüfen der Einstellungen gefunden: Öffnen Sie Einstellungen im standardmäßigen JSON-Text-Editor. Wenn kein Editor konfiguriert ist, werden die Einstellungen im Editor geöffnet. Verfügbare Einstellungen finden Sie unter „https://aka.ms/winget-settings“. Dieser Befehl kann auch zum Festlegen von Administratoreinstellungen verwendet werden, indem das Argument „--enable“ oder „--disable“ angegeben wird. {Locked="--enable"} {Locked="--disable"} Einstellungen öffnen oder Administratoreinstellungen festlegen Unerwarteter Fehler beim Laden der Einstellungen. Überprüfen Sie Ihre Einstellungen, indem Sie den Befehl "'settings'" ausführen. {Locked="'settings'"} Kanal Zeigt Informationen zu einem bestimmten Paket an. Standardmäßig muss die Abfrage ohne Berücksichtigung der Groß-/Kleinschreibung mit der ID, dem Namen oder dem Moniker des Pakets übereinstimmen. Andere Felder können verwendet werden, indem Sie Ihre entsprechende Option übergeben. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Zeigt Informationen zu einem Paket an Version Automatische Installation anfordern Auf einen einzelnen Bindestrich muss ein Alias aus einem einzelnen Zeichen folgen: „{0}“ {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. Eine Quelle mit dem angegebenen Namen ist bereits vorhanden und verweist auf einen anderen Speicherort: Eine Quelle mit einem anderen Namen verweist bereits auf diesen Speicherort: Eine Quelle mit dem angegebenen Namen ist bereits vorhanden und bezieht sich auf den gleichen Speicherort: Quelle wird hinzugefügt: Fügen Sie eine neue Quelle hinzu. Eine Quelle stellt die Daten bereit, mit denen Sie Pakete ermitteln und installieren können. Fügen Sie eine neue Quelle nur hinzu, wenn Sie sie für einen sicheren Ort halten. Eine neue Quelle hinzufügen Argument, das an die Quelle übergeben wird Suchen eines Pakets mithilfe der angegebenen Quelle Verwalten Sie Quellen mit den Unterbefehlen. Eine Quelle stellt die Daten bereit, mit denen Sie Pakete ermitteln und installieren können. Fügen Sie eine neue Quelle nur hinzu, wenn Sie sie für einen sicheren Ort halten. Verwalten von Paketquellen Eigenschaften einer vorhandenen Quelle bearbeiten. Eine Quelle stellt die Daten bereit, mit denen Sie Pakete ermitteln und installieren können. Eigenschaften einer Quelle bearbeiten Quelle wird bearbeitet: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Die Quelle mit dem Namen „{0}“ befindet sich bereits im gewünschten Zustand. {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Argument Value given to source. Liste aller aktuellen Quellen oder vollständiger Details einer bestimmten Quelle. Aktuelle Quellen auflisten Daten Data stored by the source. Bereich The name of a piece of information about a source. Name The name of the source. Bezeichner The source's unique identifier. Es wurde keine Quelle mit dem folgenden Namen gefunden: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. Es wurden keine Quellen konfiguriert. Typ The kind of source. Aktualisiert The last time the source was updated. Nie The source has never been updated. Wert The value of information about a source. Name der Quelle Fehler beim Öffnen der Quelle(n): Probieren Sie den Befehl "source reset" aus, wenn das Problem weiterhin besteht. {Locked="source reset"} Fehler beim Öffnen der vordefinierten Quelle. Bitte melden Sie dieses Problem an die Entwickler von winget. {Locked="winget"} Alle Quellen werden entfernt... Eine bestimmte Quelle entfernen. Aktuelle Quellen entfernen Quelle wird entfernt: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Alle Quellen werden zurückgesetzt... Mit diesem Befehl werden vorhandene Quellen verworfen, wodurch potenziell lokale Daten zurückgelassen werden. Ohne Argument werden alle Quellen verworfen und die Standardwerte hinzugefügt. Wenn eine benannte Quelle bereitgestellt wird, wird nur diese Quelle verworfen. Quellen zurücksetzen Erzwingt das Zurücksetzen der Quellen Die folgenden Quellen werden zurückgesetzt, wenn die Option „--force“ angegeben ist: {Locked="--force"} Quelle wird zurückgesetzt: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Typ der Quelle Alle Quellen werden aktualisiert... Aktualisieren aller Quellen oder nur einer bestimmten Quelle. Aktuelle Quellen aktualisieren Quelle wird aktualisiert: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Ergebnisse nach Bezeichnung filtern Vielen Dank, dass Sie WinGet nutzen Hinweise von Drittanbietern Das “winget”-Befehlszeilenprogramm ermöglicht das Installieren von Anwendungen und anderen Paketen mithilfe der Befehlszeile. Allgemeine Informationen zum Tool anzeigen Version des Tools anzeigen Argument wurde öfter angegeben als zulässig: „{0}“ {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. Es wurde mehr als ein Argument zur Ausführung angegeben: „{0}“ {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). Unerwarteter Fehler beim Ausführen des Befehls: Vorherige Version des Pakets während des Upgrades deinstallieren Nicht erkannter Befehl: „{0}“ {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Upgraden aller installierten Pakete auf den neuesten Stand, falls verfügbar Es wurde kein anwendbares Upgrade gefunden. In einer konfigurierten Quelle ist eine neuere Paketversion verfügbar, die jedoch nicht auf Ihr System oder Ihre Anforderungen zutrifft. Kein verfügbares Upgrade gefunden. In den konfigurierten Quellen sind keine neueren Paketversionen verfügbar. Aktualisiert das ausgewählte Paket, entweder durch Durchsuchen der Liste der installierten Pakete oder direkt aus einem Manifest. Standardmäßig muss die Abfrage ohne Berücksichtigung der Groß-/Kleinschreibung mit der ID, dem Namen oder dem Linkpfad des Pakets übereinstimmen. Andere Felder können verwendet werden, indem sie die entsprechende Option übergeben. Wenn keine Argumente angegeben werden, werden die Pakete mit verfügbaren Upgrades angezeigt. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Zeigt verfügbare Upgrades an und führt sie aus. Verwendung: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Überprüft ein Manifest anhand strikter Richtlinien. Hiermit können Sie das Manifest überprüfen, bevor Sie es an ein Repository senden. Überprüft eine Manifestdatei Der Pfad des zu überprüfenden Manifests Aktiviert die ausführliche Protokollierung für WinGet Überprüfen Sie, ob die Eingabedatei eine gültige, signierte MSIX-Datei ist. Die angegebene Version verwenden, Standard ist die neueste Version Verfügbare Versionen des Pakets anzeigen Der vor der Vervollständigung angegebene Wert wird angefordert. Es wurde keine übereinstimmende Version gefunden: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. Keine Quellen entsprechen dem angegebenen Wert: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. Die konfigurierten Quellen sind: Keine Quellen definiert; fügen Sie eine mit 'source add' hinzu, oder setzen Sie mittels 'source reset' auf die Standardwerte zurück {Locked="source add","source reset"} Gefunden Pfad ist ein Verzeichnis: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. Die Datei konnte nicht gefunden werden: {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Es werden sowohl lokale Manifeste als auch die Suchabfragenargumente bereitgestellt. Protokolle Label displayed for diagnostic files containing information about the application use. Der Installer wird von einer Richtlinie blockiert. Fehler bei der Sicherheitsprüfung des Installationsprogramms Ein Antivirenprodukt meldet eine Infektion im Installationsprogramm Fehler beim Versuch, die Quelle zu aktualisieren: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Deinstalliert das ausgewählte Paket, das entweder durch Suchen der Liste der installierten Pakete oder direkt aus einem Manifest gefunden wurde. Standardmäßig muss die Abfrage ohne Berücksichtigung der Groß-/Kleinschreibung mit der ID, dem Namen oder dem Moniker des Pakets übereinstimmen. Andere Felder können verwendet werden, indem Sie Ihre entsprechende Option übergeben. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Deinstalliert das angegebene Paket Paket-Deinstallation wird gestartet... Erfolgreich deinstalliert Das „winget“-Tool kann den Deinstallationsbefehl für dieses Paket nicht finden. Bitte wenden Sie sich an den Herausgeber des Pakets, um Unterstützung zu erhalten. {Locked="winget"} Deinstallation abgebrochen Deinstallation fehlgeschlagen mit Exitcode: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Exportiert eine Liste der installierten Pakete Installiert alle in einer Datei aufgeführten Pakete. Installiert alle Pakete in einer Datei Datei, in der das Ergebnis geschrieben werden soll Datei, die die zu installierenden Pakete beschreibt Pakete aus der angegebenen Quelle exportieren Schreibt eine Liste der installierten Pakete in eine Datei. Die Pakete können dann mit dem Befehl "import" installiert werden. {Locked="import"} Eine oder mehrere importierte Pakete konnten nicht installiert werden Die für den Import erforderliche Quelle ist nicht installiert: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. Das installierte Paket ist in keiner Quelle verfügbar: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. Die installierte Version des Pakets ist in keiner Quelle verfügbar: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. In der Importdatei wurden keine Pakete gefunden Die JSON-Datei ist ungültig Das Paket ist bereits installiert: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. Nicht verfügbare Pakete ignorieren Paketversionen in Exportdatei einschließen Paketversionen aus Importdatei ignorieren Der Pfad ist nicht vorhanden: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. In der JSON-Datei ist kein bekanntes Schema angegeben. Installationsbereich auswählen (user oder machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. Der für das Argument `{0}` angegebene Wert ist ungültig. Gültige Werte sind: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. Dieser Vorgang wird von Gruppenrichtlinie deaktiviert: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Zusätzliche Windows-App-Installer-Quellen aktivieren Zulässige Quellen für Windows-App-Installer aktivieren Standardquelle für den Windows-App-Installer aktivieren Experimentelle Features für den Windows-App-Installer aktivieren Microsoft Store-Quelle für den Windows-App-Installer aktivieren Schriftartquelle für Windows App-Installer aktivieren Einstellungen des Windows-Paket-Manager aktivieren Windows-Paket-Manager aktivieren Windows-Paket-Manager-Befehlszeilenschnittstellen aktivieren Intervall für das automatische Aktualisieren der Windows-Paket-Manager-Quelle in Minuten festlegen Gruppenrichtlinie Header for a table listing active Group Policies Lokale Manifestdateien für Windows-App-Installer aktivieren Aktivieren der Umgehung von angehefteten Zertifikaten für die Microsoft Store-Quelle für den Windows App-Installer Außerkraftsetzung der Schadsoftwareüberprüfung im lokalen Archiv durch Windows App-Installationsprogramm aktivieren Bereich: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. Ungültiges Feldformat. Ungültiger Feldwert. Ungültige Einstellung aus der Gruppenrichtlinie. Einstellungen wurden aus der Sicherungsdatei geladen. Fehler beim Analysieren der Datei: Wert: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. Die folgenden experimentellen Features werden zurzeit ausgeführt. Die Konfiguration ist aufgrund der Gruppenrichtlinie deaktiviert. Der Hash-Wert des Installers stimmt nicht überein. Hash-Überschreibung für den Windows-App-Installer aktivieren Aktuelle Quellen als JSON für die Gruppenrichtlinie exportieren. Aktuelle Quellen exportieren Zusätzliche Quelle An additional source required by policy. Zulässige Quelle A source that the user is allowed to add. Der für das Argument "{0}" angegebene Wert ist ungültig. {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Abgebrochen Extern Die Pakete, die in diesem Import gefunden wurden, weisen die folgenden Abhängigkeiten auf: Import command sentence showed before reporting dependencies Dieses Paket erfordert folgende Abhängigkeiten: Message shown before reporting dependencies Abhängigkeiten werden installiert: Abhängigkeitsquelle nicht gefunden Die Paketsuche ergab mehr als ein Ergebnis: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. Die neueste Version für das Paket wurde nicht gefunden: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. Keine Installationsprogramme gefunden: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Die mindestens erforderliche Version ist für das Paket nicht verfügbar: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. Keine Übereinstimmungen When package search yields no matches Hat Schleife Dependency graph has loop Für das Manifest wurde kein geeigneter Installer gefunden: {0} Version {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Fehler beim Verarbeiten der Paketabhängigkeiten. Möchten Sie die Installation fortsetzen? Prompt message shown when dependencies processing yields errors. Fehler beim Verarbeiten der Paketabhängigkeiten. Beenden... Pakete Dieses Paket hat Abhängigkeiten, die möglicherweise nicht mehr benötigt werden: Uninstall command sentence showed before reporting dependencies Das Manifest weist folgende Abhängigkeiten auf, die nicht überprüft wurden; stellen Sie sicher, dass sie gültig sind: Validate command sentence showed before reporting dependencies Windows-Features Windows-Bibliotheken Paketabhängigkeiten mithilfe der angegebenen Quelle suchen For getting package type dependencies when installing from a local manifest Windows Store-Nutzungsbedingungen Akzeptieren aller Lizenzvereinbarungen für Pakete Die exportierten Pakete erfordern eine Lizenzvereinbarung um diese zu installieren: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. Der Herausgeber verlangt, dass Sie die oben genannten Informationen anzeigen und den Vereinbarungen vor der Installation zustimmen. Stimmen Sie den Bedingungen zu? Paketvereinbarungen wurden nicht akzeptiert. Der Vorgang wurde abgebrochen. Vereinbarungen: Autor: Beschreibung: Installationsprogramm: Installer-Gebietsschema: Store-Produkt-ID: Sha256-Installer: Installertyp: Installer-URL: Lizenz: Lizenz-URL: Moniker: Startseite: Herausgeber: Markierungen: Version: Abhängigkeiten: Windows-Features: Windows-Bibliotheken: Paketabhängigkeiten: Externe Abhängigkeiten: Nein Ja Fehler beim Öffnen der hinzugefügten Quelle. Alle Quellvereinbarungen während Quellvorgängen akzeptieren Die Quelle "{0}" erfordert, dass Sie die folgenden Vereinbarungen vor der Verwendung anzeigen. {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. Stimmen Sie allen Nutzungsbedingungen der Quelle zu? Mindestens einer der Quellvereinbarungen wurde nicht zugestimmt. Vorgang abgebrochen. Akzeptieren Sie bitte die Quellvereinbarungen, oder entfernen Sie die entsprechenden Quellen. Die Quelle erfordert, dass die geografische Region des aktuellen Computers aus 2 Buchstaben an den Back-End-Dienst gesendet wird, damit er ordnungsgemäß funktioniert (z. B. „US“). Die Installation war erfolgreich. Starten Sie die Anwendung neu, um das Upgrade abzuschließen. Optionaler HTTP-Kopfzeile der REST-Quelle für Windows-Paket-Manager Die optionale Kopfzeile wird ignoriert, da sie für diese Quelle nicht anwendbar ist. Die optionale Kopfzeile ist nicht anwendbar, ohne eine Quelle anzugeben: „{0}“ {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Freigabedatum: Unterstützte Offlineverteilung: Herausgeber-URL: Kauf-URL: Herausgeber-Support-URL: Datenschutz-URL: Copyright: Copyright-URL: Versionshinweise: URL der Versionshinweise: Fehler beim Durchsuchen der Quelle. Ergebnisse werden nicht einbezogen: {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Fehler beim Durchsuchen der Quelle: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Dieses Feature muss von Administratoren aktiviert werden. Führen Sie als Administrator zum Aktivieren „winget settings --enable {0}“ aus. {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Aktiviert die spezifische Administratoreinstellung. Deaktiviert die spezifische Administratoreinstellung. Administratoreinstellung "{0}" aktiviert. {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. Administratoreinstellung "{0}" deaktiviert. {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. Administratoreinstellung Header for a table displaying admin settings. Die Anwendung wird zurzeit ausgeführt. Beenden Sie die Anwendung, und versuchen Sie es noch mal. Vom Installationsprogramm geänderte Dateien werden zurzeit von einer anderen Anwendung verwendet. Beenden Sie die Anwendungen, und versuchen Sie es noch mal. Es wird bereits eine andere Installation ausgeführt. Versuchen Sie es später noch mal. Mindestens eine Datei wird verwendet. Beenden Sie die Anwendung, und versuchen Sie es noch mal. Für dieses Paket fehlt eine Abhängigkeit im System. Auf Ihrem PC ist kein Speicherplatz mehr vorhanden. Geben Sie Speicherplatz frei, und versuchen Sie es noch mal. Es ist nicht genügend Arbeitsspeicher für die Installation verfügbar. Schließen Sie andere Anwendungen, und wiederholen Sie dann den Vorgang. Einer der Installationsparameter ist ungültig. Das Paketinstallationsprotokoll enthält möglicherweise zusätzliche Details. Diese Anwendung erfordert Internetkonnektivität. Stellen Sie eine Netzwerkverbindung her, und versuchen Sie es noch mal. Bei dieser Anwendung ist während der Installation ein Fehler aufgetreten. Wenden Sie sich an den Support. Starten Sie den PC neu, um die Installation abzuschließen. Ihr PC wird neu gestartet, um die Installation abzuschließen. Fehler bei der Installation. Starten Sie Ihren PC neu, und versuchen Sie es noch mal. Sie haben die Installation abgebrochen. Eine andere Version dieser Anwendung ist bereits installiert. Eine höhere Version dieser Anwendung ist bereits installiert. Die Installation wird durch Organisationsrichtlinien verhindert. Wenden Sie sich an Ihren Administrator. Die Installation dieses Pakets wird von der aktuellen Systemkonfiguration nicht unterstützt. Fehler bei der Installation des benutzerdefinierten Installationsprogramms. Wenden Sie sich an den Paketsupport. Deinstallation abgebrochen Installation fehlgeschlagen mit Exitcode: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. Das Installationsprotokoll ist verfügbar unter: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. Die folgenden Pakete wurden in den Arbeitsquellen gefunden. Geben Sie eine Option für --source an, um den Vorgang fortzusetzen. {Locked="--source"} "working sources" as in "sources that are working correctly" Unter den Arbeitsquellen wurden keine Pakete gefunden. "working sources" as in "sources that are working correctly" Dies ist eine stabile Version von Windows-Paket-Manager. Wenn Sie experimentelle Features ausprobieren möchten, installieren Sie bitte eine Vorabversion. Anweisungen finden Sie auf GitHub unter https://github.com/microsoft/winget-cli. {Locked="https://github.com/microsoft/winget-cli"} Windows-Paket-Manager v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. Paketversionen in Importdatei ignorieren Die angeforderte Anzahl von Ergebnissen muss zwischen 1 und 1000 betragen. Argumente, die zusätzlich zu den Standardwerten an das Installationsprogramm übergeben werden sollen Es wurde eine neuere Version gefunden, die Installationstechnologie unterscheidet sich jedoch von der aktuellen installierten Version. Deinstallieren Sie das Paket, und installieren Sie die neuere Version. Die Installationstechnologie der angegebenen neueren Version unterscheidet sich von der aktuell installierten Version. Deinstallieren Sie das Paket, und installieren Sie die neuere Version. Windows-Paket-Manager (Vorschau) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Architektur auswählen Pakete auch dann aktualisieren, wenn ihre aktuelle Version nicht bestimmt werden kann Listet Pakete auf, auch wenn deren aktuelle Version nicht ermittelt werden kann. Kann nur mit dem Argument „--upgrade-available“ verwendet werden {Locked="--upgrade-available"} Die Versionsnummer dieses Pakets kann nicht bestimmt werden. Um trotzdem ein Upgrade durchzuführen, fügen Sie dem vorherigen Befehl das Argument „--include-unknown“ hinzu. {Locked="--include-unknown"} Mindestens {0} Paket verfügt über Versionsnummern, die nicht ermittelt werden können. Verwenden Sie „--include-unknown“, um alle Ergebnisse anzuzeigen. {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} Pakete sind angeheftet und müssen explizit aktualisiert werden. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. Die angegebenen Argumente können nur mit einer Abfrage verwendet werden. Systemarchitektur: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). Behält alle vom Paket erstellten Dateien und Verzeichnisse bei (portabel) Löscht alle Dateien und Verzeichnisse im Paketverzeichnis (portabel) Drücken Sie die EINGABETASTE, um fortzufahren Der Wert zum Umbenennen der ausführbaren Datei (portabel) Fordert den Benutzer auf, vor dem Beenden eine beliebige Taste zu drücken Das Installationsprogramm fordert die Ausführung als Administrator an. Es wird ein Prompt erwartet. Das Installationsprogramm kann nicht in einem Administratorkontext ausgeführt werden. Pfadumgebungsvariable geändert; Starten Sie Ihre Shell neu, um den neuen Wert zu verwenden. Filtert nach dem Produktcode Es existiert bereits ein portables Paket mit demselben Namen, aber aus einer anderen Quelle; Fortfahren wegen --force {Locked="--force"} Das Volume unterstützt keine Analysepunkte Der angegebene Dateiname ist kein gültiger Dateiname. Vorhandene Datei überschreiben: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. Es wurde kein Paketauswahlargument angegeben; Einzelheiten zum Suchen eines Pakets finden Sie in der Hilfe. Befehlszeilenalias hinzugefügt: Portables Linkverzeichnis (Computer) Verzeichnis für portierbare Links (Benutzer) Portierbarer Paketstamm (Benutzer) Portierbarer Paketstamm Portierbares Paketstamm (x86) Portable Installation fehlgeschlagen; Aufräumen... Das Portable-Paket wurde geändert. Aufgrund von „--force“ wird fortgefahren {Locked="--force"} Das Portable-Paket kann nicht entfernt werden, da es geändert wurde. Um dies außer Kraft zu setzen, verwenden Sie „--force“ {Locked="--force"} Dateien verbleiben im Installationsverzeichnis: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Installationsverzeichnis löschen... Installationsverzeichnis kann nicht gelöscht werden, da es nicht von WinGet erstellt wurde Verwandter Link Dokumentation: Hinweise: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Installationshinweise: Ein angegebenes Argument ist für dieses Paket nicht unterstützt Fehler beim Extrahieren der Inhalte des Archivs Die geschachtelte Installerdatei ist nicht vorhanden. Stellen Sie sicher, dass der angegebene relative Pfad des geschachtelten Installers übereinstimmt: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Ungültiger relativer Dateipfad zum geschachtelten Installer. Der Pfad verweist auf einen Speicherort außerhalb des Installationsverzeichnisses Für dieses Paket wurden keine geschachtelten Installers angegeben. Für ein Archivinstallationsprogramm kann nur ein verschachteltes Installationsprogramm angegeben werden, es sei denn, es handelt sich um ein portierbares oder ein geschachteltes Schriftarteninstallationsprogramm. Inkompatible Befehlszeilenargumente bereitgestellt Listet nur Pakete auf, für die ein Upgrade verfügbar ist Für die folgenden Pakete ist ein Upgrade verfügbar, für das Upgrade ist jedoch eine explizite Zielgruppenadressierung erforderlich: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly Download läuft Label displayed while downloading an application installer. Der geschachtelte Installertyp wird nicht unterstützt. Dieser Installer ist dafür bekannt, dass er das Terminal oder die Shell neu startet Für dieses Paket ist ein Installationsspeicherort erforderlich Die folgenden Installationsprogramme sind dafür bekannt, dass sie das Terminal oder die Shell neu starten: Für die folgenden Installationsprogramme ist ein Installationsspeicherort erforderlich: Geben Sie das Installationsstammverzeichnis an: Möchten Sie den Vorgang fortsetzen? Vereinbarungen für This will be followed by a package name, and then a list of license agreements Der Installationsspeicherort ist für das Paket erforderlich, wurde jedoch nicht angegeben. Interaktive Eingabeaufforderungen deaktivieren Description for a command line argument, shown next to it in the help Es wurde bereits ein vorhandenes Paket gefunden. Es wird versucht, das installierte Paket zu aktualisieren... Führen Sie den Befehl direkt aus, und fahren Sie mit nicht sicherheitsrelevanten Problemen fort. Description for a command line argument, shown next to it in the help Ein portables Paket aus einer anderen Quelle ist bereits vorhanden Archiv erfolgreich extrahiert Archiv wird extrahiert... Bei der Archivüberprüfung wurde Schadsoftware erkannt. Verwenden Sie --ignore-local-archive-malware-scan, um diese Überprüfung außer Kraft zu setzen. {Locked="--ignore-local-archive-malware-scan"} Bei der Archivüberprüfung wurde Schadsoftware erkannt. Vorgang wird aufgrund --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} Überspringt das Upgrade, wenn bereits eine installierte Version vorhanden ist. Eine Paketversion ist bereits installiert. Die Installation wurde abgebrochen. {0} kann nicht aktiviert werden. Diese Einstellung wird durch eine Richtlinie gesteuert. Wenden Sie sich an den Systemadministrator, um weitere Informationen zu erfahren. {Locked="{0}"} The value will be replaced with the feature name {0} kann nicht deaktiviert werden. Diese Einstellung wird durch eine Richtlinie gesteuert. Wenden Sie sich an den Systemadministrator, um weitere Informationen zu erfahren. {Locked="{0}"} The value will be replaced with the feature name Heften Sie ein Paket an. Dadurch kann Windows-Paket-Manager beim Upgrade eines Pakets auf bestimmte Versionen eingeschränkt werden, oder das Upgrade des Pakets kann vollständig verhindert werden. Ein angeheftetes Paket kann trotzdem eigenständig von außerhalb des Windows-Paket-Managers aktualisiert werden. Standardmäßig kann ein angeheftetes Paket aktualisiert werden, indem es explizit im Befehl "upgrade" erwähnt wird oder indem das Flag "--include-pinned" zu "winget upgrade --all" hinzugefügt wird. {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} Neue PIN hinzufügen Verwalten Sie angeheftete Pakete mit den Unterbefehlen. Wird ein Paket angeheftet, kann Windows-Paket-Manager beim Upgrade eines Pakets auf bestimmte Versionen eingeschränkt werden, oder das Upgrade des Pakets kann vollständig verhindert werden. Ein angeheftetes Paket kann trotzdem eigenständig von außerhalb des Windows-Paket-Managers aktualisiert werden. Paketpins verwalten Listet alle aktuellen Pins oder vollständige Details eines bestimmten Pins auf. Aktuelle Pins auflisten Entfernen Sie eine bestimmte Paket-PIN. Paket-PIN entfernen Alle vorhandenen Pins zurücksetzen. Pins zurücksetzen Version, an die das Paket angeheftet werden soll. Der Platzhalter "*" kann als letzter Versionsteil verwendet werden. Upgrade blockieren, bis der Pin entfernt wird, wodurch Außerkraftsetzungsargumente verhindert werden Bestimmte installierte Version anheften Installiert Value used in a table to indicate that a package comes from the list of packages installed in the machine Einstellungen als JSON exportieren Einstellungen exportieren Benutzereinstellungen Label displayed for the file containing the user settings. Die Einstellungsdatei konnte nicht geladen werden. Standardwerte werden verwendet. Bereichsfilter für das installierte Paket auswählen (user oder machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. Pin erfolgreich hinzugefügt Für das Paket {0} ist bereits eine PIN vorhanden. {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. Für das Paket {0} ist bereits eine PIN vorhanden. Überschreiben aufgrund des --force Arguments. {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Für das Paket {0} ist bereits eine PIN vorhanden. Verwenden Sie das --force Argument, um es zu überschreiben. {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Alle aktuellen Pins werden zurückgesetzt. Verwenden Sie das argument --force, um alle Pins zurückzusetzen. Die folgenden Pins würden entfernt: {Locked="--force"} Stecknadeltyp Für das Paket {0} ist keine PIN vorhanden {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. Es sind keine Pins konfiguriert. Shown when listing or modifying existing pins if there are none. Pins erfolgreich zurückgesetzt Shown after resetting (deleting) all the pins Pin-Datenbank kann nicht geöffnet werden. Error message for when we cannot open the database containing package pins. Es wurde ein Argument angegeben, das nur für ein einzelnes Paket verwendet werden kann. Es wurden mehrere Argumente angegeben, die sich gegenseitig ausschließen: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together Argument {0} kann nur mit {1} verwendet werden. {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" Deaktiviert As in enabled/disabled Aktiviert As in enabled/disabled Status Header for a table listing the state (enabled/disabled) of Group Policies and Settings Die Abfrage, mit der nach einem Paket gesucht wird Paket nicht gefunden: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. Mehrere Pakete gefunden für: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. Fehler bei der Suche nach: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} Pakete verfügen über einen Pin, der vor dem Upgrade entfernt werden muss {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade Das Paket kann nicht mit winget aktualisiert werden. Verwenden Sie die vom Herausgeber bereitgestellte Methode zum Aktualisieren dieses Pakets. Aktualisieren von Paketen auch dann, wenn sie über einen nicht blockierenden Pin verfügen Listet Pakete auf, auch wenn sie einen Pin haben, der ein Upgrade verhindert. Kann nur mit dem Argument „--upgrade-available“ verwendet werden {Locked="--upgrade-available"} Pin erfolgreich entfernt Es wurde eine neuere Version gefunden, das Paket verfügt jedoch über eine PIN, die ein Upgrade verhindert. Das Paket ist angeheftet und kann nicht aktualisiert werden. Verwenden Sie den Befehl "winget pin", um Pins anzuzeigen und zu bearbeiten. Einige Stecknadeltypen können mit dem Argument "--include-pinned" umgangen werden. {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} Pakete verfügen über Pins, die ein Upgrade verhindern. Verwenden Sie den Befehl "winget pin", um Pins anzuzeigen und zu bearbeiten. Wenn Sie das --include-pinned-Argument verwenden, werden möglicherweise weitere Ergebnisse angezeigt. {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages Stellt sicher, dass das System dem gewünschten Zustand entspricht, wie in der angegebenen Konfiguration beschrieben. Kann Prozessoren herunterladen/ausführen, um den gewünschten Zustand zu erreichen. Die Konfiguration und die Prozessoren sollten überprüft werden, um sicherzustellen, dass sie vertrauenswürdig sind, bevor sie angewendet werden. Konfiguriert das System in einem gewünschten Zustand Zeigt Details zur angegebenen Konfiguration an. Standardmäßig wird das System nicht geändert, aber einige Optionen führen dazu, dass Dateien heruntergeladen und/oder geladen werden. Zeigt Details einer Konfiguration an Überprüft, ob das System mit dem gewünschten Zustand übereinstimmt, wie in der angegebenen Konfiguration beschrieben. Kann Prozessoren herunterladen/ausführen, um den gewünschten Zustand zu testen. Die Konfiguration und die Prozessoren sollten überprüft werden, um sicherzustellen, dass sie vertrauenswürdig sind, bevor Sie sie ausführen. Überprüft das System anhand eines gewünschten Zustands. Überprüft eine Konfigurationsdatei auf Richtigkeit. Überprüft eine Konfigurationsdatei Das Feld '{0}' in der Konfigurationsdatei weist den falschen Typ auf. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. Der Pfad zur Konfigurationsdatei Die Konfigurationsdatei ist ungültig. Die Konfigurationsdateiversion {0} ist nicht bekannt. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. Akzeptiert die Konfigurationswarnung und verhindert eine interaktive Eingabeaufforderung Anwenden Indicates that this item is used to write state Bestätigen Indicates that this item is used to check/assert the state rather than write to it Abhängigkeiten:{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. Ein Teil der Konfiguration wurde nicht erfolgreich angewendet. Fehler beim Abrufen detaillierter Informationen zur Konfiguration. Informieren Indicates that this item is used to retrieve values for future use rather than writing them Lokal Used to indicate that the item is present on the device. Modul: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. Modul: {0} von {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. Einstellungen: Label for the values that are used as inputs for this item when applying state Die Konfiguration wurde erfolgreich angewendet. Die Einheit wurde erfolgreich angewendet. Eine andere Konfiguration wird auf das System angewendet. Diese Konfiguration wird so bald wie möglich fortgesetzt... Sie sind dafür verantwortlich, die Konfigurationseinstellungen zu verstehen, die Sie ausführen möchten. Microsoft ist nicht für die von Ihnen erstellte oder importierte Konfigurationsdatei verantwortlich. Diese Konfiguration kann Einstellungen in Windows ändern, Software installieren, Softwareeinstellungen (einschließlich Sicherheitseinstellungen) ändern und Benutzervereinbarungen für Pakete und Dienste von Drittanbietern in Ihrem Namen akzeptieren. Indem Sie diese Konfigurationsdatei ausführen, bestätigen Sie, dass Sie diese Ressourcen und Einstellungen verstehen und ihnen zustimmen. Alle installierten Anwendungen werden an Sie von ihren Besitzern lizenziert. Microsoft ist weder für Pakete oder Dienste von Drittanbietern verantwortlich noch gewährt es Lizenzen für diese. Legal approved. Do not change without approval. Haben Sie die Konfiguration überprüft und möchten sie auf das System anwenden? PM approved. Die Konfiguration ist leer. Das Feature [{0}] wurde nicht gefunden. {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. Fehler beim Aktivieren von Windows-Featureabhängigkeiten. Verwenden Sie '--force', um mit der Installation fortzufahren. {Locked="--force"} Neustart erforderlich, um das/die Windows-Feature(s) vollständig zu aktivieren; um diese Prüfung außer Kraft zu setzen, verwenden Sie '--force'. "Windows Feature(s)" is the name of the options Windows features setting. Aktivieren von Windows Feature-Abhängigkeiten fehlgeschlagen; Fortfahren aufgrund von --force "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} [{0}] wird aktiviert... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. Fehler beim Aktivieren des [{0}]-Features: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. Ein Neustart ist erforderlich, um das/die Windows-Feature(s) vollständig zu aktivieren; wird aufgrund von --force durchgeführt "Windows Feature(s)" is the name of the options Windows features setting. Es wird auf den Abschluss einer weiteren Installation/Deinstallation gewartet... Angeheftete Version Table header for the version to which a package is pinned; meaning it should not update from that version. <Weitere Informationen finden Sie in der Protokolldatei> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. Konfigurationsdetails werden abgerufen Das Konfigurationssystem wird initialisiert. Konfigurationsdatei wird gelesen Das System befindet sich nicht im gewünschten Zustand, der von der Konfiguration bestätigt wird. Fehler bei dieser Konfigurationseinheit aus unbekanntem Grund: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Fehler bei der Konfigurationseinheit aufgrund der folgenden Konfiguration: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Fehler der Konfigurationseinheit beim Abrufen des aktuellen Systemstatus. Fehler bei der Konfigurationseinheit beim Anwenden des gewünschten Zustands. Fehler bei der Konfigurationseinheit beim Versuch, den aktuellen Systemstatus zu testen. Fehler bei der Konfigurationseinheit aufgrund eines internen Fehlers: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Fehler bei der Konfigurationseinheit aufgrund einer ungültigen Vorbedingung: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Fehler bei der Konfigurationseinheit aufgrund des Systemstatus: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Fehler bei der Ausführung der Konfigurationseinheit: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Die Konfiguration enthält den Bezeichner "{0}" mehrmals. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. Die Abhängigkeit "{0}" wurde in der Konfiguration nicht gefunden. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. Diese Konfigurationseinheit wurde manuell übersprungen. Das Modul für die Konfigurationseinheit ist an mehreren Speicherorten mit derselben Version verfügbar. Fehler beim Laden des Moduls für die Konfigurationseinheit. Für die Konfigurationseinheit wurden mehrere Übereinstimmungen gefunden. geben Sie das Modul an, um das richtige auszuwählen. Die Konfigurationseinheit wurde nicht gefunden. Die Konfigurationseinheit befand sich nicht wie erwartet im Modul. Diese Konfigurationseinheit wurde nicht ausgeführt, da eine Abhängigkeit fehlgeschlagen ist oder nicht ausgeführt wurde. Diese Konfigurationseinheit wurde nicht ausgeführt, da ein Assertfehler aufgetreten ist oder falsch war. Die Konfigurationseinheit hat während der Ausführung ein unerwartetes Ergebnis zurückgegeben. Diese Konfigurationseinheit wurde aus unbekanntem Grund nicht ausgeführt: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Das Feld '{0}' weist einen ungültigen Wert auf: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. Das Feld '{0}' fehlt oder ist leer. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. Siehe Zeile {0}, Spalte {1} in der Datei. {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. Vorgang wird abgebrochen Installieren Sie das Stub-Paket für AppInstaller Installieren Sie das vollständige Paket für AppInstaller Aktivieren Sie erweiterte Features. Erfordert Zugriff auf den Store. Die Option '--enable' und '--disable' kann nicht mit anderen Argumenten verwendet werden. {Locked="--enable", "--disable"} Erweiterte Features werden aktiviert. Erfordert Zugriff auf den Store. Erweiterte Features sind nicht aktiviert. Führen Sie "winget configure --enable" aus, um sie zu aktivieren. {Locked="winget configure --enable"} Erweiterte Features sind aktiviert. Deaktivieren Sie erweiterte Features. Erfordert Zugriff auf den Store. Deaktivieren erweiterter Features. Erfordert Zugriff auf den Store. Erweiterte Features sind deaktiviert. Überspringt die Verarbeitung von Paketabhängigkeiten und Windows-Features. Abhängigkeiten übersprungen. Fehler beim Aktualisieren der PATH-Variablen für den Prozess. Nachfolgende Installationen, die von Änderungen an der PATH-Variablen abhängen, können fehlschlagen. {Locked="PATH"} Einige der Konfigurationseinheiten fielen beim Testen ihres Zustands aus. Das System befindet sich im beschriebenen Konfigurationszustand. Der Konfigurationsstatus wurde nicht getestet. Das System befindet sich nicht im beschriebenen Konfigurationszustand. Unerwartetes Testergebnis: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). Haben Sie die Konfiguration überprüft und möchten Sie sie anhand des Systems verifizieren? Die Konfigurationsdatei ist keine gültige YAML Datei. {Locked="YAML"} YAML is a file format name. Diese Konfigurationseinheit ist Teil eines Abhängigkeitszyklus. Bei der Überprüfung wurden keine Probleme gefunden. Die Konfigurationseinheit ist nur als Vorabversion verfügbar, aber in der Konfiguration ist sie nicht als solche gekennzeichnet. Fügen Sie `allowPrerelease: true` zu den `directives` hinzu. {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. Die Konfigurationseinheit wurde lokal gefunden, aber in keinem konfigurierten Katalog gefunden. Stellen Sie sicher, dass sie auf allen Systemen vorhanden ist, bevor Sie die Konfiguration anwenden. Die Konfigurationseinheit ist nicht öffentlich zugänglich; stellen Sie sicher, dass jeder, der diese Konfiguration verwenden wird, Zugriff darauf hat. Das Modul wurde nicht angegeben. Die Angabe des Moduls verbessert die Leistung und verhindert zukünftige Namenskonflikte. Lädt den Installer aus dem ausgewählten Paket herunter, entweder durch Durchsuchen einer konfigurierten Quelle oder direkt aus einem Manifest. Standardmäßig muss die Abfrage ohne Berücksichtigung der Groß-/Kleinschreibung mit der ID, dem Namen oder dem Moniker des Pakets übereinstimmen. Andere Felder können verwendet werden, indem sie die entsprechende Option übergeben. Standardmäßig wird der entsprechende Installer über den Downloadbefehl in den Ordner "Downloads" des Benutzers heruntergeladen. Lädt das Installationsprogramm aus einem bestimmten Paket herunter. Verzeichnis, in das die Installationsprogramme heruntergeladen werden Abhängigkeiten werden heruntergeladen: Installationsprogramm heruntergeladen: {0} {Locked="{0}"} Full path of the downloaded installer. Wählen Sie den Installationsprogrammtyp aus Installationsprogrammdownloads Das Herunterladen des Installationsprogramms für eine spätere Offlineinstallation ist nicht zulässig. `--module-path allusers` erfordert Administratorrechte, um ausgeführt zu werden. {Locked="--module-path allusers"} Gibt den Speicherort auf dem lokalen Computer zum Speichern von Modulen an. Standard %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} Der Wert „--module-path“ muss „currentuser“, „allusers“, „default“ oder ein absoluter Pfad sein. {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Windows-Paket-Manager-Konfiguration aktivieren Hiermit werden Informationen zu Fehlern abgerufen. Bei Angabe einer Zahl enthält die Ausgabe Details zum Fehler, einschließlich des Symbolnamens, wenn es sich um einen wingetspezifischen Fehler handelt. Bei Angabe einer Zeichenfolge werden die wingetspezifischen Fehler nach diesem Wert durchsucht. Informationen zu Fehlern abrufen Ein Wert, der in den Fehlerinformationen gesucht werden soll Die angegebene Zahl ist zu groß, um ein HRESULT zu sein. Unbekannter Fehlercode Interner Fehler Ungültige Befehlszeilenargumente Fehler beim Ausführen des Befehls Fehler beim Öffnen des Manifests Abbruchsignal empfangen Fehler beim Ausführen von ShellExecute Das Manifest kann nicht verarbeitet werden. Die Manifestversion ist höher als unterstützt. Aktualisieren Sie den Client. Fehler beim Herunterladen des Installationsprogramms In index kann nicht geschrieben werden; es sich um eine höhere Schemaversion handelt. Der Index ist beschädigt Die konfigurierten Quellinformationen sind fehlerhaft Der Quellname ist bereits konfiguriert Der Quelltyp ist ungültig Die MSIX-Datei ist ein Bündel, kein Paket Von der Quelle benötigte Daten fehlen Keines der Installationsprogramme ist für das aktuelle System anwendbar. Der Hash der Installationsprogrammdatei stimmt nicht mit dem Manifest überein Der Quellname ist nicht vorhanden Der Quellspeicherort ist bereits unter einem anderen Namen konfiguriert. Es wurden keine Pakete gefunden Es sind keine Quellen konfiguriert Es wurden mehrere Pakete gefunden, die den Kriterien entsprechen Es wurde kein Manifest gefunden, das den Kriterien entspricht Fehler beim Abrufen des öffentlichen Ordners aus dem Quellpaket Zum Ausführen des Befehls sind Administratorrechte erforderlich Der Quellspeicherort ist nicht sicher Der Microsoft Store Client wird durch eine Richtlinie blockiert Die Microsoft Store-App wird durch eine Richtlinie blockiert Das Feature befindet sich zurzeit in der Entwicklung. Sie kann mithilfe von Wingeteinstellungen aktiviert werden. Fehler beim Installieren der Microsoft Store-App Fehler beim Ausführen der automatischen Vervollständigung Fehler beim Initialisieren des XML-Parsers Es wurde ein ungültiger YAML-Schlüssel gefunden Es wurde ein doppelter YAML-Schlüssel gefunden Ungültiger YAML-Vorgang Fehler beim Erstellen des YAML-Dokuments Ungültiger YAML-Senderzustand Ungültige YAML-Daten LibYAML-Fehler Die Manifestüberprüfung war mit Warnung erfolgreich Fehler bei der Manifestüberprüfung Das Manifest ist ungültig Es wurden keine anwendbaren Aktualisierungen gefunden winget-Upgrade – vollständig mit Fehlern abgeschlossen Installationsprogramm hat Sicherheitsprüfung nicht bestanden Die Downloadgröße stimmt nicht mit der erwarteten Inhaltslänge überein Der Deinstallationsbefehl wurde nicht gefunden Fehler beim Ausführen des Deinstallationsbefehls ICU-Unterbrechungsiteratorfehler ICU-Fallzuordnungsfehler ICU-RegEx-Fehler Fehler beim Installieren mindestens eines importierten Pakets. Mindestens ein angefordertes Paket wurde nicht gefunden. Die JSON-Datei ist ungültig Der Quellspeicherort ist nicht remote Die konfigurierte REST-Quelle wird nicht unterstützt. Von der REST-Quelle wurden ungültige Daten zurückgegeben Der Vorgang wird durch Gruppenrichtlinie blockiert Interner Fehler der REST-API Ungültige REST-Quell-URL Nicht unterstützter MIME-Typ, der von der REST-API zurückgegeben wird Ungültige Version des REST-Quellvertrags Die Quelldaten sind beschädigt oder manipuliert. Fehler beim Lesen aus dem Datenstrom. Paketvereinbarungen wurden nicht vereinbart Fehler beim Lesen der Eingabe in der Eingabeaufforderung Die Suchanforderung wird von mindestens einer Quelle nicht unterstützt. Der REST-API-Endpunkt wurde nicht gefunden. Fehler beim Öffnen der Quelle. Quellvereinbarungen wurde nicht zugestimmt Die Headergröße überschreitet den zulässigen Grenzwert von 1024 Zeichen. Verringern Sie die Größe, und versuchen Sie es noch mal. Fehlende Ressourcendatei Fehler beim Ausführen der MSI-Installation Argumente für msiexec sind ungültig Fehler beim Öffnen einer oder mehrerer Quellen Fehler beim Überprüfen von Abhängigkeiten Mindestens ein Paket fehlt Ungültige Tabellenspalte Die Upgradeversion ist nicht neuer als die installierte Version. Die Upgradeversion ist unbekannt, und es wurde keine Außerkraftsetzung angegeben ICU-Konvertierungsfehler Fehler beim Installieren des portierbaren Pakets Das Volume unterstützt keine Analysepunkte Ein portierbares Paket aus einer anderen Quelle ist bereits vorhanden. Symlink kann nicht erstellt werden. Der Pfad verweist auf ein Verzeichnis. Das Installationsprogramm kann nicht in einem Administratorkontext ausgeführt werden. Fehler beim Deinstallieren des portierbaren Pakets Fehler beim Überprüfen der DisplayVersion-Werte anhand des Indexes. Mindestens ein Argument wird nicht unterstützt. Eingebettete Null-Zeichen sind für SQLite nicht zulässig Fehler beim Suchen des geschachtelten Installationsprogramms im Archiv. Fehler beim Extrahieren des Archivs. Es wurde ein ungültiger relativer Dateipfad zum geschachtelten Installationsprogramm angegeben. Das Serverzertifikat stimmte mit keinem der erwarteten Werte überein. Der Installationsspeicherort muss angegeben werden. Archiv-Malware-Scan fehlgeschlagen. Es wurde mindestens eine Version des installierten Pakets gefunden. Für das Paket ist bereits eine PIN vorhanden. Für das Paket ist keine PIN vorhanden. Die PIN-Datenbank kann nicht geöffnet werden. Mindestens eine Anwendung konnte nicht installiert werden Mindestens eine Anwendung konnte nicht deinstalliert werden Mindestens eine Abfrage hat nicht genau eine Übereinstimmung zurückgegeben. Das Paket verfügt über eine PIN, die ein Upgrade verhindert. Das derzeit installierte Paket ist das Stubpaket Signal zum Herunterfahren der Anwendung empfangen Fehler beim Herunterladen von Paketabhängigkeiten. Fehler beim Herunterladen des Pakets. Das Herunterladen für die Offlineinstallation ist nicht zulässig. Ein erforderlicher Dienst ist ausgelastet oder nicht verfügbar. Versuchen Sie es später noch einmal. Die angegebene GUID entspricht keinem gültigen Fortsetzungszustand. Die aktuelle Clientversion stimmte nicht mit der Clientversion des gespeicherten Zustands überein. Die Daten zum Fortsetzungszustand sind ungültig. Die Prüfpunktdatenbank kann nicht geöffnet werden. Das maximale Fortsetzungslimit wurde überschritten. Ungültige Authentifizierungsinformationen. Authentifizierungsmethode nicht unterstützt. Fehler bei der Authentifizierung. Fehler bei der Authentifizierung. Interaktive Authentifizierung erforderlich. Fehler bei der Authentifizierung. Abbruch durch Benutzer. Fehler bei der Authentifizierung. Das authentifizierte Konto ist nicht das gewünschte Konto. Die Anwendung wird zurzeit ausgeführt. Beenden Sie die Anwendung, und versuchen Sie es noch mal. Es wird bereits eine andere Installation ausgeführt. Versuchen Sie es später noch mal. Mindestens eine Datei wird verwendet. Beenden Sie die Anwendung, und versuchen Sie es noch mal. Für dieses Paket fehlt eine Abhängigkeit im System. Auf Ihrem PC ist kein Speicherplatz mehr vorhanden. Geben Sie Speicherplatz frei, und versuchen Sie es noch mal. Es ist nicht genügend Arbeitsspeicher für die Installation verfügbar. Schließen Sie andere Anwendungen, und wiederholen Sie dann den Vorgang. Diese Anwendung erfordert Internetkonnektivität. Stellen Sie eine Netzwerkverbindung her, und versuchen Sie es noch mal. Bei dieser Anwendung ist während der Installation ein Fehler aufgetreten. Wenden Sie sich an den Support. Starten Sie den PC neu, um die Installation abzuschließen. Fehler bei der Installation. Starten Sie Ihren PC neu, und versuchen Sie es noch mal. Ihr PC wird neu gestartet, um die Installation abzuschließen. Sie haben die Installation abgebrochen. Eine andere Version dieser Anwendung ist bereits installiert. Eine höhere Version dieser Anwendung ist bereits installiert. Die Installation wird durch Organisationsrichtlinien verhindert. Wenden Sie sich an Ihren Administrator. Fehler beim Installieren der Paketabhängigkeiten. Die Anwendung wird zurzeit von einer anderen Anwendung verwendet. Unzulässiger Parameter. Das Paket wird vom System nicht unterstützt. Das Upgrade eines vorhandenen Pakets wird vom Installationsprogramm nicht unterstützt. Die Installation ist mit einem Fehler des benutzerdefinierten Installationsprogramms fehlgeschlagen. Der Eintrag "Apps und Features" für das Paket wurde nicht gefunden. Der Installationsspeicherort ist nicht anwendbar. Der Installationsspeicherort wurde nicht gefunden. Der Hash der vorhandenen Datei stimmte nicht überein. Datei nicht gefunden. Die Datei wurde gefunden, aber der Hash wurde nicht überprüft. Auf die Datei konnte nicht zugegriffen werden. Die Konfigurationsdatei ist ungültig. Die YAML-Syntax ist ungültig. Ein Konfigurationsfeld weist einen ungültigen Typ auf. Die Konfiguration weist eine unbekannte Version auf. Fehler beim Anwenden der Konfiguration. Die Konfiguration enthält einen doppelten Bezeichner. In der Konfiguration fehlt eine Abhängigkeit. Die Konfiguration weist eine nicht erfüllte Abhängigkeit auf. Fehler bei einer Assertion für die Konfigurationseinheit. Die Konfiguration wurde manuell übersprungen. Der Benutzer hat die Fortsetzung der Ausführung abgelehnt. Die Abhängigkeitsdiagramm enthält einen Zyklus, der nicht aufgelöst werden kann. Die Konfiguration weist einen ungültigen Feldwert auf. In der Konfiguration fehlt ein Feld. Einige der Konfigurationseinheiten fielen beim Testen ihres Zustands aus. Der Konfigurationsstatus wurde nicht getestet. Die Konfigurationseinheit wurde nicht installiert. Die Konfigurationseinheit wurde nicht gefunden. Für die Konfigurationseinheit wurden mehrere Übereinstimmungen gefunden. geben Sie das Modul an, um das richtige auszuwählen. Fehler der Konfigurationseinheit beim Abrufen des aktuellen Systemstatus. Fehler bei der Konfigurationseinheit beim Versuch, den aktuellen Systemstatus zu testen. Fehler bei der Konfigurationseinheit beim Anwenden des gewünschten Zustands. Das Modul für die Konfigurationseinheit ist an mehreren Speicherorten mit derselben Version verfügbar. Fehler beim Laden des Moduls für die Konfigurationseinheit. Die Konfigurationseinheit hat während der Ausführung ein unerwartetes Ergebnis zurückgegeben. Eine Einheit enthält eine Einstellung, die den Konfigurationsstamm erfordert. Der Vorgang wird vom Konfigurationsprozessor nicht unterstützt. Nicht verfügbar Windows-Featureabhängigkeiten erfolgreich aktiviert Fehler beim Laden des Moduls für die Konfigurationseinheit, da für die Ausführung Administratorrechte erforderlich sind. Eine Einheit enthält eine Einstellung, die den Konfigurationsstamm erfordert. Fehler beim Laden des Moduls für die Konfigurationseinheit, da für die Ausführung Administratorrechte erforderlich sind. Setzt die Ausführung eines zuvor gespeicherten Befehls fort, indem der eindeutige Bezeichner des gespeicherten Befehls übergeben wird. Wird verwendet, um einen ausgeführten Befehl fortzusetzen, der möglicherweise aufgrund eines Neustarts beendet wurde. Setzt die Ausführung eines zuvor gespeicherten Befehls fort. Der eindeutige Bezeichner des gespeicherten Zustands, der fortgesetzt werden soll Das Fortsetzen des Status von einer anderen Clientversion wird nicht unterstützt: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. Der Fortsetzungszustand ist nicht vorhanden: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. Im Fortsetzungszustand wurden keine Daten gefunden. Dieser Befehl unterstützt das Fortsetzen nicht. Ermöglicht ggf. einen Neustart Der Neustart zum Abschließen des Vorgangs wird initiiert... Fehler beim Initiieren eines Neustarts. Der Fortsetzungsvorgang überschreitet das zulässige Limit von {0} Fortsetzungen. Führen Sie den Befehl "{1}" aus, um den Vorgang manuell fortzusetzen. {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. Grenzwert beim Fortsetzen eines gespeicherten Zustands ignorieren Das URI-Schema wird nicht unterstützt: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. URI nicht wohlgeformt: {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. Fehler beim Analysieren {0} Inhalt der Konfigurationseinheitseinstellungen, oder der Einstellungsinhalt ist leer. {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. {0} Konfigurationseinheit fehlt das erforderliche 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} Konfigurationseinheit fehlt das empfohlene Argument: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. Die WinGetSource-Konfigurationseinheit steht in Konflikt mit einer bekannten Quelle: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. Die WinGetSource-Konfigurationseinheit wird für eine Drittanbieterquelle bestätigt: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage deklariert sowohl UseLatest als auch Version. Paket-ID: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} Die WinGetPackage-Konfigurationseinheit bestätigt ein Paket aus einer Drittanbieterquelle. Paket-ID: {0}; Quelle: {1} {Locked="WinGetPackage,{0},{1}"} Das Paket der WinGetPackage-Konfigurationseinheit ist von einer Drittanbieterquelle abhängig, die noch nicht konfiguriert wurde. Paket-ID: {0}; Quelle: {1} {Locked="WinGetPackage,{0},{1}"} Das Paket der WinGetPackage-Konfigurationseinheit ist von einer Drittanbieterquelle abhängig. Es wird empfohlen, die Abhängigkeit im Abschnitt "uni dependsOn" zu deklarieren. Paket-ID: {0}; Quelle: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} Das Paket der WinGetPackage-Konfigurationseinheit kann nicht überprüft werden. Fehler beim Öffnen der Quelle. Paket-ID: {0}; Quelle: {1} {Locked="WinGetPackage,{0},{1}"} Das Paket der WinGetPackage-Konfigurationseinheit kann nicht überprüft werden. Das Paket wurde nicht gefunden. Paket-ID: {0} {Locked="WinGetPackage,{0}"} Das Paket der WinGetPackage-Konfigurationseinheit kann nicht überprüft werden. Es wurden mehrere Pakete gefunden. Paket-ID: {0} {Locked="WinGetPackage,{0}"} Das Paket der WinGetPackage-Konfigurationseinheit kann nicht überprüft werden. Die Paketversion wurde nicht gefunden. Paket-ID: {0}; Version {1} {Locked="WinGetPackage,{0},{1}"} Das Paket der WinGetPackage-Konfigurationseinheit wurde mit einer bestimmten Version angegeben, es ist jedoch nur eine Paketversion verfügbar. Paket-ID:{0}; Version: {1} {Locked="WinGetPackage,{0},{1}"} Das Paket der WinGetPackage-Konfigurationseinheit kann nicht überprüft werden. Paket-ID: {0} {Locked="WinGetPackage,{0}"} Einstellung für Authentifizierungsfenster angeben („silent“, „silentPreferred“ oder „interactive“) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. Geben Sie das Konto an, das für die Authentifizierung verwendet werden soll Fehler beim Hinzufügen der Quelle. Diese winget Version unterstützt die Authentifizierungsmethode der Quelle nicht. Versuchen Sie, ein Upgrade auf die neueste winget Version durchzuführen. {Locked="winget"} Für die {0} Quelle ist eine Authentifizierung erforderlich. Bei Bedarf wird möglicherweise eine Authentifizierungsaufforderung angezeigt. Authentifizierte Informationen werden für die Zugriffsautorisierung mit der Quelle geteilt. {Locked="{0}"} Repariert das ausgewählte Paket, entweder durch Durchsuchen der Liste der installierten Pakete oder direkt aus einem Manifest. Standardmäßig muss die Abfrage ohne Berücksichtigung der Groß-/Kleinschreibung mit der ID, dem Namen oder dem Moniker des Pakets übereinstimmen. Andere Felder können verwendet werden, indem sie die entsprechende Option übergeben. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Repariert das ausgewählte Paket Der Reparaturbefehl für dieses Paket wurde nicht gefunden. Wenden Sie sich an den Paketherausgeber, um Unterstützung zu erfahren. Die verwendete Installertechnologie stimmt nicht mit der derzeit installierten Version überein. Der Reparaturbefehl wurde nicht gefunden. Die verwendete Installationstechnologie unterstützt keine Reparatur. Der Reparaturvorgang wurde erfolgreich abgeschlossen. Reparatur abgebrochen Paketreparatur wird gestartet... Fehler beim Reparieren des Microsoft Store-Pakets. Fehlercode: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. Fehler beim Reparaturvorgang. Der Reparaturvorgang ist nicht anwendbar. In den konfigurierten Quellen sind keine übereinstimmenden Paketversionen verfügbar. Die Reparatur dieses Pakets wird von der aktuellen Systemkonfiguration nicht unterstützt. Die verwendete Installationstechnologie unterstützt keine Reparatur. Das für den Benutzerbereich installierte Paket kann nicht repariert werden, wenn es mit Administratorrechten ausgeführt wird. Reparaturvorgänge mit Administratorrechten sind für Pakete, die innerhalb des Benutzerbereichs installiert sind, nicht zulässig. Fehler bei der Reparatur mit Exitcode: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. Die SQLite-Verbindung wurde beendet, um Beschädigungen zu verhindern. Alle Versionen deinstallieren Die Version, auf die reagiert werden soll Mehrere Versionen dieses Pakets sind installiert. Verfeinern Sie die Suche, übergeben Sie das Argument "--version", um eines auszuwählen, oder übergeben Sie das Flag "--all-versions", um alle zu deinstallieren. {Locked="--version,--all-versions"} Befehlszeilenoptionen für Windows-Paket-Manager Proxy aktivieren Describes a Group Policy that can enable the use of the --proxy option to set a proxy Legen Sie einen Proxy fest, der für diese Ausführung verwendet werden soll. Verwendung des Proxys für diese Ausführung deaktivieren {0} kann nicht zurückgesetzt werden. Diese Einstellung wird durch eine Richtlinie gesteuert. Wenden Sie sich an den Systemadministrator, um weitere Informationen zu erfahren. {Locked="{0}"} The value will be replaced with the feature name Administratoreinstellung „{0}“ zurücksetzen. {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. {0} kann nicht festgelegt werden. Diese Einstellung wird durch eine Richtlinie gesteuert. Wenden Sie sich an den Systemadministrator, um weitere Informationen zu erfahren. {Locked="{0}"} The value will be replaced with the feature name Legen Sie die Administratoreinstellung „{0}“ auf „{1}“ fest. {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced Name der zu ändernden Einstellung Wert, der für die Einstellung festgelegt werden soll. Setzt eine Administratoreinstellung auf den Standardwert zurück. Setzt eine Administratoreinstellung auf den Standardwert zurück. Legt den Wert einer Administratoreinstellung fest. Legt den Wert einer Administratoreinstellung fest. Schließt eine Quelle aus der Ermittlung aus, sofern keine Angabe erfolgt. Schließt eine Quelle aus der Ermittlung aus (TRUE oder FALSE). Anstößig Vertrauensebene der Quelle (keine oder vertrauenswürdig) Vertrauensebene Fehler beim Abrufen des Microsoft Store-Paketkatalogs. In Microsoft Store-Paketkatalog wurde kein anwendbares Microsoft Store-Paket gefunden. Fehler beim Abrufen von Microsoft Store Paketdownloadinformationen. Es wurde kein anwendbares Microsoft Store Paket zum Download gefunden. Fehler beim Abrufen der Microsoft Store-Paketlizenz. Es wurde kein anwendbares Microsoft Store-Paket gefunden. Der Pakethash für Microsoft Store wurde erfolgreich überprüft. Microsoft Store Pakethashkonflikt Microsoft Store heruntergeladenes Paket: {0} {Locked="{0}"} Full path of the downloaded package. Fehler beim Herunterladen des Microsoft Store-Pakets: {0} {Locked="{0}"} Package name. Microsoft Store-Paketdownload abgeschlossen Hauptpakete werden von Microsoft Store heruntergeladen... Abhängigkeitspakete werden von Microsoft Store heruntergeladen... Microsoft Store-Paketdownloadinformationen werden abgerufen Fehler beim Abrufen von Microsoft Store Paketdownloadinformationen Microsoft Store Paketlizenz wird abgerufen Microsoft Store-Paketlizenz gespeichert: {0} {Locked="{0}"} License file full path. Fehler beim Abrufen der Microsoft Store-Paketlizenz Microsoft Store Paketdownload unterstützt das Argument --rename nicht. Microsoft Store-Paket verwendet Namen, die von Microsoft Store-Katalog bereitgestellt werden. {Locked="--rename"} Microsoft Store Paketdownload erfordert Microsoft Entra-ID-Authentifizierung. Bei Bedarf wird möglicherweise eine Authentifizierungsaufforderung angezeigt. Authentifizierte Informationen werden für Microsoft-Dienste zur Zugriffsautorisierung freigegeben. Für Microsoft Store Paketlizenzierung muss das Microsoft Entra-ID-Konto Mitglied des globalen Administrators, Benutzeradministrators oder Lizenzadministrators sein. {Locked="Global Administrator,User Administrator,License Administrator"} Überspringt das Abrufen der Offlinelizenz des Microsoft Store-Pakets Zielplattform auswählen Konfigurationsdatei wird hinzugefügt: {0} {Locked="{0}"} Erfolgreich exportiert Konfigurationseinstellungen werden abgerufen... Es müssen mindestens „--packageId“ und/oder „--module“ mit „--resource“ angegeben werden. Alternativ können Sie „--all“ verwenden, um alle Paketkonfigurationen zu exportieren. {Locked="--packageId,--module, --resource, --all"} Die Argumente „--packageId“, „--module“ und „--resource“ können nicht zusammen mit „--all“ verwendet werden. {Locked="--packageId,--module, --resource, --all"} Exportiert Konfigurationsressourcen in eine Konfigurationsdatei. Bei Verwendung mit "--all" werden alle Paketkonfigurationen exportiert. Bei Verwendung mit "--packageId" wird eine WinGetPackage-Ressource der angegebenen Paket-ID exportiert. Bei Verwendung mit "--module" und "--resource" werden die Einstellungen der Ressource abgerufen und in die Konfigurationsdatei exportiert. Wenn die Ausgabekonfigurationsdatei bereits vorhanden ist, werden die exportierten Konfigurationsressourcen angefügt. {Locked="WinGetPackage,--packageId,--module, --resource"} Exportiert Konfigurationsressourcen in eine Konfigurationsdatei. Das Modul der zu exportierenden Ressource. Der zu exportierende Paketbezeichner. Die zu exportierende Konfigurationsressource. Exportiert alle Paketkonfigurationen. Fehler beim Abrufen der Eigenschaften der Konfigurationseinheit. Fehler beim Exportieren der Konfiguration. "{0}" konfigurieren {Locked="{0}"} {0}Installieren {Locked="{0}"} Einige der in der Konfigurationsdatei vorhandenen Daten wurden für diese Ausgabe abgeschnitten. überprüfen Sie den Dateiinhalt auf den vollständigen Inhalt. <dieser Wert wurde abgeschnitten; überprüfen Sie den Dateiinhalt auf den vollständigen Text> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. Zeigt die Details der obersten Ebene für Konfigurationen an, die auf das System angewendet wurden. Diese Daten können dann mit `configure` Befehlen verwendet werden, um weitere Details zu erhalten. {Locked="`configure`"} Zeigt den Konfigurationsverlauf an. Im Verlauf sind keine Konfigurationen vorhanden. Elemente aus dem Verlauf auswählen Es wurde keine einzelne Konfiguration gefunden, die mit den angegebenen Daten übereinstimmt. Geben Sie entweder den vollständigen Namen oder einen Teil des Bezeichners an, der eindeutig mit der gewünschten Konfiguration übereinstimmt. Element aus Verlauf entfernen Zuerst angewendet Column header for date values indicating when a configuration was first applied to the system. Bezeichner Name Ursprung Pfad Die angegebene Konfiguration wurde nicht gefunden. Abgeschlossen The state of processing an item. In Bearbeitung The state of processing an item. Ausstehend The state of processing an item. Unbekannt The state of processing an item. Konfigurationsstatus überwachen. As in "to monitor the status of a configuration being applied". Abgeschlossen The state of processing an item. In Bearbeitung The state of processing an item. Ausstehend The state of processing an item. Übersprungen The state of processing an item. Unbekannt The state of processing an item. Gestartet anwenden When the configuration application started. Beendet anwenden When the configuration application ended. Ergebnis Details Bundesland The state of processing an item. Einheit Das Paket ist nicht mit der aktuellen Windows-Version oder -Plattform kompatibel. Anzeige von Details zur Erstkonfiguration nach Möglichkeit unterdrücken Mehrere Microsoft Store können für unterschiedliche Plattformen und Architekturen heruntergeladen werden. --platform, --architecture kann zum Filtern der Pakete verwendet werden. {Locked="--platform--architecture"} Fehler beim Abrufen Microsoft Store Paketlizenz. Das Microsoft Entra-ID-Konto ist kein Mitglied des globalen Administrators, Benutzeradministrators oder Lizenzadministrators. Verwenden Sie "--skip-license", um das Abrufen Microsoft Store Paketlizenz zu überspringen. {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} Das Microsoft Store Paket unterstützt keinen Download. Das Microsoft Store Paket unterstützt keinen Download. Fehler beim Abrufen Microsoft Store Paketlizenz. Das Microsoft Entra-ID-Konto verfügt nicht über die erforderlichen Berechtigungen. Der Parameter kann nicht über die Integritätsgrenze hinweg übergeben werden. Das Zero-Byte-Installationsprogramm wurde heruntergeladen. stellen Sie sicher, dass die Netzwerkverbindung ordnungsgemäß funktioniert. Das Zero-Byte-Installationsprogramm wurde heruntergeladen. stellen Sie sicher, dass die Netzwerkverbindung ordnungsgemäß funktioniert. Schriftarten verwalten Verwalten sie Schriftarten mit Unterbefehlen. Schriftarten können ähnlich wie Pakete installiert, aktualisiert oder deinstalliert werden. Familie Gesichter "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' Ergebnisse nach Familienname filtern Gesicht "Face" represents the typeface of a font family such as 'Bold' or 'Italic' Pfade Es wurde keine installierte Schriftart gefunden, die den Eingabekriterien entspricht. Installierte Schriftarten auflisten Listet alle installierten Schriftarten, alle Schriftartdateien oder vollständige Details einer bestimmten Schriftart auf. Version Konfigurationsmodule PowerShell Modules that are used for the Configuration feature Das Paketinstallationsprogramm erfordert eine Authentifizierung. Bei Bedarf wird möglicherweise eine Authentifizierungsaufforderung angezeigt. Authentifizierte Informationen werden mit der Download-URL des Installers geteilt. Fehler beim Herunterladen des Installers. Diese winget Version unterstützt die Downloadauthentifizierungsmethode des Installers nicht. Versuchen Sie, ein Upgrade auf die neueste winget Version durchzuführen. {Locked="winget"} Fehler beim Herunterladen des Installationsprogramms. Fehler bei der Authentifizierung. Pfad zum Konfigurationsprozessor angeben DSC v3-Ressourcenbefehle DSC stands for "Desired State Configuration". It should already have a locked translation. Die untergeordneten Befehle implementieren Desired State Configuration (DSC) v3-Ressourcen zur Konfiguration von WinGet und Paketen. Abrufen des Ressourcenstatus Festlegen des Ressourcenstatus Beschreiben erforderlicher Zustandsänderungen Testen des Ressourcenstatus Löschen des Ressourcenstatus Abrufen aller Zustandsinstanzen Überprüfen von Gruppeninhalten Externen Zustand auflösen Ausführen des Adapters Abrufen des Ressourcenschemas Abrufen des Ressourcenmanifests Paketstatus verwalten Pakete über WinGet verwalten. Gibt an, ob eine Instanz vorhanden oder vorhanden sein soll. Gibt an, ob sich eine Instanz im gewünschten Zustand befindet. Der Bezeichner des Pakets. Die Quelle des Pakets. Die Version des Pakets. Die Methode zum Abgleichen des Bezeichners mit einem Paket. Geben Sie an, dass die neueste verfügbare Version des Pakets installiert werden soll. Der Installationsmodus, der bei Bedarf verwendet werden soll. Der Zielbereich des Pakets. Gibt an, ob Vereinbarungen für Quellen und Pakete akzeptiert werden sollen. Verwalten der Benutzereinstellungsdatei Verwalten Sie die Benutzereinstellungen von WinGet. Der Inhalt der JSON-Einstellungsdatei. Die Aktion, die zum Anwenden der Einstellungen verwendet wird. Der Wert wird zur Korrelation protokolliert. Quellkonfiguration verwalten Verwalten Sie die Quellen von WinGet. Der Name, der für die Quelle verwendet werden soll. Das Argument für die Quelle. Der Typ der Quelle. Die Vertrauensebene der Quelle. Gibt an, ob die Quelle eingeschlossen wird, wenn anrufe keine Quelle angeben. Konfigurationseinheit wird angewendet... Konfigurationseinheit wird exportiert... Prozessoren der Konfigurationseinheit werden abgerufen... Erforderliches Modul für den Export [{0}] sicherstellen {Locked="{0}"} Fehler beim Testen oder Abrufen des erforderlichen Moduls. Verwandte Einstellungen werden nicht exportiert. Export [{0}] {Locked="{0}"} Fehler beim Exportieren der Ressource. Fehler beim Abrufen von Einheitenprozessoren. Einzelne Paketeinstellungen werden nicht exportiert. Verzeichnis, in das die Ergebnisse geschrieben werden sollen Desired State Configuration Paket wurde im System nicht gefunden. Paket wird installiert... Fehler beim Installieren Desired State Configuration Pakets. Installieren Sie das Paket manuell, oder geben Sie den Pfad zum dsc.exe über --processor-path Argument an. {Locked="dsc.exe","--processor-path"} Ein Objekt, das die Administratoreinstellungen und deren Werte enthält. Administratoreinstellungen verwalten Verwalten Sie die Administratoreinstellungen von WinGet. Setzt alle Administratoreinstellungen zurück. Alle Administratoreinstellungen zurückgesetzt. MCP-Informationen MCP stands for Model Context Protocol and should probably remain as-is MCP(Model Context Protocol)-Informationen für die Windows-Paket-Manager. Verwenden Sie das folgende JSON-Fragment im `servers` Objekt, um den Windows-Paket-Manager-MCP-Server manuell mit Ihrem MCP-Client zu konfigurieren: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. MCP-Server des Windows-Paket-Manager aktivieren Zielbetriebssystemversion Fehler beim Installieren einer oder mehrerer Schriftarten. Die Schriftartdatei wird nicht unterstützt und kann nicht installiert werden. Mindestens eine Schriftart im Schriftartenpaket wird nicht unterstützt und kann nicht installiert werden. Fehler bei der Installation der Schriftart; Bereinigung. Schriftart bereits installiert. Paket-ID WinGet unterstützt Titel Die Schriftartdatei wurde nicht gefunden. Fehler beim Deinstallieren der Schriftart. Die Schriftart befindet sich möglicherweise nicht in einem guten Zustand. Versuchen Sie, die Deinstallation nach einem Neustart durchzuführen. Fehler bei der Schriftartüberprüfung. Fehler beim Zurücksetzen der Schriftart. Die Schriftart befindet sich möglicherweise nicht in einem guten Zustand. Versuchen Sie, die Deinstallation nach einem Neustart durchzuführen. Detaillierte Informationen zur Schriftartdatei anzeigen. Fehler bei der Überprüfung des Schriftartenpakets. Der Rollbackversuch für die fehlerhafte Schriftartinstallation war nicht erfolgreich. Möglicherweise ist ein Neustart erforderlich, um die Schriftart erfolgreich zu deinstallieren. Fehler beim Deinstallieren des Schriftartenpakets. Dies liegt häufig an den Schriftarten, die vom System oder einer Anwendung verwendet werden. Die Deinstallation ist nach dem Neustart des Computers möglicherweise erfolgreich. OK "OK" means the font is in a good healthy state Beschädigt "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. Status Unbekannt Das Schriftartpaket ist bereits installiert. Detaillierte Informationen zu Paketen anzeigen Providing this argument causes the CLI to output additional details about installed application packages. Kanal: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). Lokaler Bezeichner: Precedes a value that is the unique identifier for the installed package on the local system. Paketfamilienname: Precedes a value that is the APPX/MSIX package family name of the installed package. Produktcode: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. Upgrade-Code: Precedes a value that is the MSI Upgrade Code for the installed package. Installierter Bereich: Precedes a value that is the scope of the installation of the package (ex. user, machine). Installierte Architektur: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). Installiertes Gebietsschema: Precedes a value that is the locale of the installed package. Installationsspeicherort: Precedes a value that is the directory path to the installed package. Ursprungsquelle: Precedes a value that names the package source where the installed package originated from. Verfügbare Upgrades: Precedes a list of package upgrades available for the installed package. Installer-Kategorie: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). Alter Wert Column title for listing edit changes. Neuer Wert Column title for listing the new value. ================================================ FILE: Localization/Resources/es-ES/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/es-ES/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 El alias contiguo no es una marca: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Alias de marca contiguo no encontrado: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Los siguientes argumentos están disponibles: Message displayed to inform the user about the available command line arguments. Están disponibles los siguientes alias de comando: Message displayed to inform the user about the available command line alias arguments. Los siguientes comandos están disponibles: Title displayed to inform the user about the available commands. Disponible As in "a new version is available to upgrade to". Están disponibles las siguientes opciones: Message displayed to inform the user about the available options. Los siguientes subcomandos están disponibles: Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} actualizaciones disponibles. {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Usar el canal especificado; el valor predeterminado es público general comando Label displayed for a command to give the software. Filtrar resultados por comando Description message displayed to inform the user about filtering the search results by a package command. Línea de comandos completa para la finalización Este comando requiere que se ejecuten privilegios de administrador. Este comando puede usarse para solicitar la finalización sensible al contexto de la línea de comandos. Se pasan la línea de comandos, la posición del cursor y la palabra que se debe completar. El resultado es un conjunto de valores potenciales basado en las entradas, con un valor posible por línea. Habilita la finalización sensible al contexto de la línea de comandos No mostrar más del número de resultados especificado (entre 1 y 1000) Listo Label displayed when an operation completes or is done executing. Buscar paquete usando la coincidencia exacta Description message displayed to inform the user about finding an application package using an exact matching criteria. Argumento experimental para propósitos de demostración Este comando es un ejemplo de cómo implementar una característica experimental. Para activar, ve a 'winget settings' y habilita las características experimentalCmd o experimentalArg. {Locked="winget settings"} Ejemplo de característica experimental Se encontró un argumento posicional cuando no se esperaba nada: '{0}' {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. Esta característica es un trabajo en curso y puede cambiar drásticamente o quitarse por completo en el futuro. Para habilitarlo, edita la configuración ("winget settings") para incluir la característica experimental.'{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Muestra el estado de las características experimentales. Las características experimentales se pueden activar a través de "winget settings". {Locked="winget settings"} Muestra el estado de las características experimentales Deshabilitado Habilitado Característica Vínculo Las siguientes características experimentales están en curso. Se pueden configurar mediante el archivo de configuración "winget settings". {Locked="winget settings"} Propiedad Estado Archivo al que se va a aplicar un algoritmo hash El argumento de marca no puede contener un valor adjunto:'{0}' {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Calcula el hash de un archivo local, apropiado para la entrada en un manifiesto. También puede calcular el hash del archivo de firma de un paquete MSIX para habilitar las instalaciones en streaming. Aplicación auxiliar para aplicar un algoritmo hash a los archivos instaladores Muestra la ayuda sobre el comando seleccionado Para más información sobre un comando específico, pásalo el argumento de ayuda. Puedes encontrar más ayuda en:'{0}' {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Filtrar resultados por id Suprime las salidas de advertencia El propietario de esta aplicación le concede una licencia. Microsoft no es responsable, ni tampoco concede ninguna licencia de paquetes de terceros. Este paquete se proporciona a través de Microsoft Store. Es posible que winget necesite adquirir el paquete de Microsoft Store en nombre del usuario actual. {Locked="winget"} Instala el paquete seleccionado, ya sea buscando en un origen configurado o directamente desde un manifiesto. De forma predeterminada, la consulta debe coincidir sin distinción entre mayúsculas y minúsculas con el id, el name o el moniker del paquete. Se pueden usar otros campos pasando su opción adecuada. De forma predeterminada, el comando install comprobará el estado de instalación del paquete e intentará realizar una actualización si procede. Reemplácelo por --force para realizar una instalación directa. {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Instala el paquete proporcionado El hash del instalador no coincide; esto no se puede sobreescribir al ejecutarse como administrador El hash del instalador no coincide; se puede continuar debido a --ignore-security-hash {Locked="--ignore-security-hash"} El hash del instalador no coincide; para invalidar esta comprobación, use --ignore-security-hash {Locked="--ignore-security-hash"} El hash del instalador se verificó correctamente Instalado correctamente Iniciando instalación de paquete... Omitir el error de comprobación de hash del instalador Omitir el examen de malware realizado como parte de la instalación de un paquete de tipo de archivo desde el manifiesto local Solicitar instalación interactiva; es posible que se necesite la participación del usuario No se reconoció el alias de argumento para el comando actual: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Especificador de argumento no válido:'{0}' {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). No se reconoció el nombre del argumento para el comando actual: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Directorios de WinGet Header for a table detailing the directories Winget uses for key operations like logging and portable installs Configuración regional para usar (formato BCP47) {Locked="BCP47"} Contrato de licencia Vínculos Links to different webpages El comando "list" muestra los paquetes instalados en el sistema, así como si existe una actualización disponible. Se pueden proporcionar opciones adicionales para filtrar el resultado, como con el comando "search". {Locked="list","search"} Mostrar paquetes instalados Ubicación en la que instalar (si es posible) Ubicación del registro (si es posible) Copyright (c) Microsoft Corporation. Todos los derechos reservados. Página principal The primary webpage for the software La ruta de acceso al manifiesto del paquete Error de validación de manifiesto. Validación del manifiesto correcta. Validación del manifiesto correcta con advertencias. El argumento necesita un valor, pero no se encuentra ninguno: '{0}'' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Filtrar resultados por moniker El archivo de entrada será tratado como msix; Si está firmado, se proporcionará hash de firma No se pudo calcular el hash de firma MSIX. Error al instalar o actualizar el paquete de Microsoft Store porque la aplicación específica está bloqueada por la directiva Error al instalar o actualizar el paquete de Microsoft Store. Código de error: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Error al instalar o actualizar el paquete de Microsoft Store porque el cliente de Microsoft Store está bloqueado por la directiva Comprobando o solicitando la adquisición del paquete... Se encontraron varios paquetes instalados que coinciden con los criterios de entrada. Por favor, reajusta tu búsqueda. Se encontraron varios paquetes coincidentes con los criterios de entrada. Por favor, reajusta tu búsqueda. Filtrar resultados por nombre No se ha encontrado ningún instalador aplicable; consulte los registros para obtener más detalles. Actualmente, no hay ninguna característica experimental disponible. No se encontró ningún paquete que coincida con los criterios de entrada. No se encontró ningún paquete coincidente con los criterios de busqueda. Deshabilita la pantalla VirtualTerminal {Locked="VirtualTerminal"} Abrir la ubicación de registros predeterminada opciones Options to change how a command works Sobreescribir argumentos pasados al instalador Paquete: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. Vaya, olvidamos hacer esto... La posición del cursor en la línea de comandos Declaración de privacidad La consulta usada para buscar un paquete Progreso que muestra un arco iris de colores No se proporcionó un argumento necesario: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Pantalla de progreso como color predeterminado Busca paquetes de orígenes configurados. Buscar y mostrar información básica de paquetes Id Abbreviation of Identifier. Coincidencia Nombre Origen se omitieron otras entradas por el límite de resultados Versión Se encontraron los siguientes errores al validar la configuración: Abra la configuración en el editor de texto JSON predeterminado. Si no hay un editor configurado, abra la configuración en el bloc de notas. Para conocer la configuración disponible, consulte https://aka.ms/winget-settings. Este comando también se puede utilizar para establecer la configuración del administrador proporcionando los argumentos --enable o --disable. {Locked="--enable"} {Locked="--disable"} Abrir la configuración o establecer la configuración del administrador Error inesperado al cargar la configuración. Para comprobar la configuración, ejecute el comando 'settings'. {Locked="'settings'"} Canal Muestra información sobre un paquete específico. De forma predeterminada, la consulta debe coincidir con el id, nombre o el moniker del paquete sin distinguir entre mayúsculas y minúsculas. Puede usar otros campos si pasa su opción adecuada. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Muestra información sobre un paquete Versión Solicitar instalación silenciosa Solo el alias de un solo carácter puede aparecer después de un solo -: '{0}' {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. Ya existe un origen con el nombre proporcionado y hace referencia a una ubicación diferente: Ya hay un origen con otro nombre que hace referencia a esta ubicación: Ya existe un origen con el nombre proporcionado y hace referencia a la misma ubicación: Agregando origen: Agrega un nuevo origen. Un origen proporciona los datos para que puedas detectar e instalar paquetes. Agrega un origen nuevo solo si confías que será una ubicación segura. Agregar un nuevo origen El argumento dado al origen Buscar paquete usando el origen especificado Administrar orígenes con los subcomandos. Un origen proporciona los datos para que puedas detectar e instalar paquetes. Agrega un origen nuevo solo si confías en él como una ubicación segura. Administrar orígenes de paquetes Edite las propiedades de un origen existente. Un origen proporciona los datos para que pueda detectar e instalar paquetes. Editar propiedades de un origen Editando origen: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. El origen denominado '{0}' ya está en el estado deseado. {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Argumento Value given to source. Enumerar todos los orígenes actuales o detalles completos de un origen específico. Enumerar orígenes actuales Datos Data stored by the source. Campo The name of a piece of information about a source. Nombre The name of the source. Identificador The source's unique identifier. No se encontró un origen con nombre: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. No hay ningún origen configurado. Tipo The kind of source. Actualizado The last time the source was updated. nunca The source has never been updated. Valor The value of information about a source. Nombre del origen Error al abrir los orígenes, pruebe el comando "source reset" si el problema persiste. {Locked="source reset"} No se pudo abrir el origen predefinido; por favor informa a los mantenedores de winget. {Locked="winget"} Eliminando todos los orígenes... Quitar un origen específico. Quitar orígenes actuales Quitando origen: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Restableciendo todos los orígenes... Este comando quita los orígenes existentes lo que puede dejar atrás información local. Sin argumentos, eliminará todos los orígenes y agregará los valores predeterminados. Si se proporciona el nombre de un origen, solo se eliminará ese origen. Restablecer orígenes Fuerza el restablecimiento de los orígenes Se restablecerán los siguientes orígenes si se proporciona la opción --force: {Locked="--force"} Restableciendo origen: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Tipo del origen Actualizando todos los orígenes... Actualizar todos los orígenes o solo un origen específico. Actualizar orígenes actuales Actualizando origen: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Filtrar resultados por etiqueta Gracias por usar winget Avisos de terceros La utilidad de línea de comandos winget permite instalar aplicaciones y otros paquetes desde la línea de comandos. Mostrar información general de la herramienta Mostrar la versión de la herramienta Se proporcionó el argumento más veces de las permitidas: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. Se proporcionó más de un argumento de comportamiento de ejecución: '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). Se ha producido un error inesperado al ejecutar el comando: Desinstalar la versión anterior del paquete durante la actualización Comando no reconocido: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Actualizar todos los paquetes instalados a la versión más reciente si está disponible No se ha encontrado ninguna actualización aplicable. Una versión más reciente del paquete está disponible en una fuente configurada, pero no se aplica a su sistema o requisitos. No se ha encontrado ninguna actualización disponible. No hay versiones más recientes del paquete disponibles en las fuentes configuradas. Actualiza el paquete seleccionado, ya sea buscando en la lista de paquetes instalados o directamente desde un manifiesto. De forma predeterminada, la consulta debe coincidir sin distinción entre mayúsculas y minúsculas con el identificador, el nombre o el moniker del paquete. Se pueden usar otros campos pasando su opción adecuada. Cuando no se proporciona ningún argumento, muestra los paquetes con actualizaciones disponibles id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Muestra y realiza actualizaciones disponibles uso: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Valida un manifiesto usando un conjunto estricto de instrucciones. El propósito es permitirle comprobar el manifiesto antes de enviarlo a un repositorio. Valida un archivo de manifiesto La ruta de acceso al manifiesto que se va a validar Permite el registro detallado para winget Compruebe que el archivo de entrada es un archivo de tipo MSIX válido y firmado. Usar la versión especificada. El valor predeterminado es la última versión Mostrar las versiones disponibles del paquete Se pide el valor proporcionado antes de la finalización No se encontró ninguna versión que coincida: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. Ninguna fuente coincide con el valor especificado: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. Los orígenes configurados son: No se definió ningún origen. Agregue uno con "source add" o restablezca el valor predeterminado con "source reset" {Locked="source add","source reset"} Encontrado La ruta de acceso es un directorio: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. El archivo “{0}” no existe. {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Se proporcionan los argumentos de consulta de búsqueda y manifiesto local Registros Label displayed for diagnostic files containing information about the application use. El instalador está bloqueado debido a una directiva El instalador falló la comprobación de seguridad Un producto antivirus informa de una infección en el instalador Error al intentar actualizar el origen: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Desinstala el paquete seleccionado, encontrado al buscar en una lista de paquetes instalados o bien, directamente desde un manifiesto. De forma predeterminada, la consulta debe coincidir con el id, nombre o el moniker del paquete sin distinguir entre mayúsculas y minúsculas. Se pueden usar otros campos usando la opción apropiada. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Desinstala el paquete proporcionado Iniciando la desinstalación de paquete... Desinstalación realizada con éxito winget no encuentra el comando de desinstalación de este paquete. Póngase en contacto con el anunciante del paquete para obtener ayuda. {Locked="winget"} Desinstalación abandonada Error de desinstalación con el código de salida: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Exporta una lista de los paquetes instalados Instala todos los paquetes enumerados en un archivo. Instala todos los paquetes en un archivo. Archivo en el que se va a escribir el resultado Archivo que describe los paquetes que se instalarán Exportar paquetes del origen especificado Escribe una lista de los paquetes instalados en un archivo. Los paquetes se pueden instalar con el comando import. {Locked="import"} No se pudieron instalar uno o más paquetes importados El origen necesario para la importación no está instalado: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. El paquete instalado no está disponible desde ningún origen: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. La versión instalada del paquete no está disponible de ningún origen: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. No se encontraron paquetes en archivo de importación El archivo JSON no es válido. El paquete ya está instalado: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. Omitir los paquetes no disponibles Incluir versiones de paquete en el archivo de exportación Omitir las versiones del paquete del archivo de importación La ruta de acceso no existe: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. El archivo JSON no especifica un esquema reconocido. Seleccione el ámbito de la instalación (user o machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. El valor proporcionado para el argumento '{0}' no es válido; los valores válidos son: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. Esta operación está deshabilitada por directiva de grupo: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Habilitar orígenes adicionales del instalador de aplicaciones de Windows Habilitar orígenes permitidos del instalador de aplicaciones de Windows Habilitar el origen predeterminada del instalador de aplicaciones de Windows Habilitar las funciones experimentales del instalador de aplicaciones de Windows Habilitar el origen Microsoft Store del instalador de aplicaciones de Windows Habilitar origen de fuente de Windows App Instalador Habilitar la configuración del Administrador de paquetes de Windows Habilitar el Administrador de paquetes de Windows Habilitar Administrador de paquetes de Windows interfaces de línea de comandos Establecer el intervalo de actualización automática del origen del Administrador de paquetes de Windows en minutos Directiva de grupo Header for a table listing active Group Policies Habilitar los archivos locales de manifiesto del instalador de aplicaciones de Windows Habilitar omisión del certificado anclado de origen del instalador de aplicación de Windows para Microsoft Store Habilitar invalidación de examen de malware de archivo local de Windows Instalador de aplicación Campo: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. Formato de campo no válido. Valor de campo no válido. Configuración no válida de la directiva de grupo. Se ha cargado la configuración desde el archivo de copia de seguridad. Error al analizar el archivo: Valor: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. Las siguientes características experimentales están en curso. La configuración está deshabilitada debido a la Directiva de grupo. El hash del instalador no coincide. Habilitar la sobreescritura del hash del instalador de aplicaciones de Windows Exportar los orígenes actuales como JSON para la directiva de grupo. Exportar los orígenes actuales Origen adicional An additional source required by policy. Origen permitido A source that the user is allowed to add. El valor proporcionado para el argumento '{0}' no es válido {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Cancelado Externo Los paquetes encontrados en esta importación tienen las siguientes dependencias: Import command sentence showed before reporting dependencies Este paquete requiere las siguientes dependencias: Message shown before reporting dependencies Instalando dependencias: No se encontró el origen de la dependencia La búsqueda de paquetes produce más de un resultado: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. No se encontró la última versión del paquete: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. No se encontraron instaladores: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Versión mínima requerida no disponible para el paquete: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. Sin coincidencias When package search yields no matches Tiene bucle Dependency graph has loop No se encontró ningún instalador adecuado para el manifiesto: {0} versión {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Error al procesar las dependencias del paquete. ¿Desea continuar con la instalación? Prompt message shown when dependencies processing yields errors. Error al procesar las dependencias del paquete. Saliendo... Paquetes Este paquete tenía dependencias que es posible que ya no sean necesarias: Uninstall command sentence showed before reporting dependencies El manifiesto tiene las siguientes dependencias que no se validaron; asegúrese de que son válidas: Validate command sentence showed before reporting dependencies Características de Windows Bibliotecas de Windows Buscar dependencias de paquete con el origen especificado For getting package type dependencies when installing from a local manifest Términos de Microsoft Store Aceptar todos los contratos de licencia para paquetes El paquete exportado requiere un contrato de licencia para instalar: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. El editor requiere que vea la información anterior y acepte los contratos antes de la instalación. ¿Acepta los términos? No se han aceptado los acuerdos de paquete. Operación cancelada. Acuerdos: Autor: Descripción: Instalador: Configuración regional del instalador: Id. de producto de Store: Instalador SHA256: Tipo de instalador: Dirección URL del instalador: Licencia: Dirección URL de la licencia: Moniker: Página principal: Editor: Etiquetas: Versión: Dependencias: Características de Windows: Bibliotecas de Windows: Dependencias de paquete: Dependencias externas: No No se pudo abrir el origen agregado. Aceptar todos los contratos de origen durante las operaciones de origen El origen '{0}' requiere que vea los siguientes contratos antes de usarlo. {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. ¿Está de acuerdo con todos los términos de los contratos de origen? No se han aceptado uno o varios de los contratos de origen. Operación cancelada. Acepte los contratos de origen o quite los orígenes correspondientes. El origen requiere que la región geográfica de dos letras de la máquina actual se envíe al servicio back-end para que funcione correctamente (por ejemplo, "EE. UU."). Se instaló correctamente. Reinicie la aplicación para completar la actualización. Encabezado HTTP de origen REST opcional de Windows-Package-Manager Omitiendo el encabezado opcional porque no es aplicable para este origen. El encabezado opcional no es aplicable sin especificar un origen: '{0}' {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Fecha de lanzamiento: Distribución sin conexión admitida: Dirección URL del editor: Dirección URL de compra: Dirección URL de soporte del editor: Dirección URL de privacidad: Copyright: Dirección URL de copyright: Notas de la versión: Dirección URL de notas de la versión: Error al buscar en el origen, los resultados no se incluirán: {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Error al buscar en el origen: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Esta función debe ser activada por los administradores. Para activarla, ejecute 'winget settings --enable {0}' como administrador. {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Habilita la configuración de administrador específica Deshabilita la configuración de administrador específica Configuración de administrador habilitada "{0}". {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. Configuración de administrador deshabilitada "{0}". {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. Configuración de administrador Header for a table displaying admin settings. La aplicación se está ejecutando actualmente. Salga de la aplicación e inténtelo de nuevo. Otra aplicación está usando los archivos modificados por el instalador. Salga de las aplicaciones e inténtelo de nuevo. Ya hay otra instalación en curso. Inténtelo de nuevo más tarde. Se están usando uno o varios archivos. Salga de la aplicación e inténtelo de nuevo. Falta una dependencia de este paquete en el sistema. No hay más espacio en el equipo. Haga espacio e inténtelo de nuevo. No hay suficiente memoria disponible para instalar. Cierre otras aplicaciones e inténtelo de nuevo. Uno de los parámetros de instalación no es válido. Es posible que el registro de instalación del paquete tenga detalles adicionales. Esta aplicación requiere conectividad a Internet. Conéctese a una red e inténtelo de nuevo. Esta aplicación encontró un error durante la instalación. Póngase en contacto con el soporte técnico. Reinicie el equipo para finalizar la instalación. El equipo se reiniciará para finalizar la instalación. Error en la instalación. Reinicia el equipo e inténtalo de nuevo. Ha cancelado la instalación. Ya hay otra versión de esta aplicación instalada. Ya hay instalada una versión posterior de esta aplicación. Las directivas de la organización impiden la instalación. Póngase en contacto con su administrador. La configuración del sistema actual no admite la instalación de este paquete. La instalación falló debido a un error del instalador personalizado. Póngase en contacto con la asistencia técnica del paquete. Instalación abandonada Error del instalador con el código de salida: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. El registro del instalador está disponible en: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. Se encontraron los siguientes paquetes entre los orígenes que funcionan. Especifique uno de ellos con la opción --source para continuar. {Locked="--source"} "working sources" as in "sources that are working correctly" No se encontraron paquetes entre los orígenes que funcionan. "working sources" as in "sources that are working correctly" Esto es una versión estable del Administrador de paquetes de Windows. Si desea probar las características experimentales, instale una compilación de versión preliminar. Las instrucciones están disponibles en GitHub en https://github.com/microsoft/winget-cli. {Locked="https://github.com/microsoft/winget-cli"} Administrador de paquetes de Windows v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. Omitir las versiones del paquete de la importación del archivo El número de resultados solicitado debe estar entre 1 y 1000. Argumentos que se van a pasar al instalador además de los valores predeterminados Se encontró una versión más reciente, pero la tecnología de instalación es diferente de la versión actual instalada. Desinstale el paquete e instale la versión más reciente. La tecnología de instalación de la versión más reciente especificada es diferente de la versión actual instalada. Desinstale el paquete e instale la versión más reciente. Administrador de paquetes de Windows (Versión preliminar) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Selección de la arquitectura Actualizar paquetes aunque no se pueda determinar su versión actual Enumere los paquetes incluso si no se puede determinar su versión actual. Solo se puede usar con el argumento --upgrade-available {Locked="--upgrade-available"} No se puede determinar el número de versión de este paquete. Para actualizarlo de todos modos, agregue el argumento --include-unknown al comando anterior. {Locked="--include-unknown"} {0} paquete(s) tienen números de versión que no se pueden determinar. Use --include-unknown para ver todos los resultados. {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} paquetes están anclados y deben actualizarse explícitamente. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. Los argumentos proporcionados solo se pueden usar con una consulta. Arquitectura del sistema: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). Conserva todos los archivos y directorios creados por el paquete (portable) Elimina todos los archivos y directorios del directorio del paquete (portable) Presione Entrar para continuar . . . Valor para cambiar el nombre del archivo ejecutable (portable) Pide al usuario que presione cualquier tecla antes de salir El instalador solicitará que se ejecute como administrador. Espere una indicación. El instalador no se puede ejecutar desde un contexto de administrador. Variable de entorno PATH modificada; reinicie el shell para usar el nuevo valor. Filtra con el código de producto Ya existe un paquete portable con el mismo nombre pero de otro origen; continuando debido a --force {Locked="--force"} El volumen no admite puntos de análisis El nombre de archivo especificado no es un nombre de archivo válido Sobrescribiendo archivo existente: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. No se ha proporcionado ningún argumento de selección de paquete; vea la ayuda para obtener más información sobre cómo buscar un paquete. Alias de línea de comandos agregado: Directorio de vínculos portátiles (equipo) Directorio de vínculos portátiles (usuario) Raíz de paquete portátil (usuario) Raíz de paquete portátil Raíz de paquete portátil (x86) Error en la instalación portable. Limpiando... Se ha modificado el paquete portátil; se puede continuar debido a --force {Locked="--force"} No se puede quitar el paquete portátil porque se ha modificado; para invalidar esta comprobación, use --force {Locked="--force"} Quedan archivos en el directorio de instalación: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Purgando directorio de instalación... No se puede purgar el directorio de instalación porque WinGet no lo ha creado Vínculo relacionado Documentación: Notas: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Notas de instalación: No se admite un argumento proporcionado para este paquete No se pudo extraer el contenido del archivo El archivo de instalador anidado no existe. Asegúrese de que la ruta de acceso relativa especificada del instalador anidado coincide con: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Ruta de acceso relativa de archivo no válida para el instalador anidado; la ruta de acceso señala a una ubicación fuera del directorio de instalación No se ha especificado ningún instalador anidado para este paquete Solo se puede especificar un instalador anidado para un instalador de archivo a menos que sea un instalador anidado de fuente o portátil Argumentos de línea de comandos no compatibles proporcionados Enumere solo los paquetes que tienen una actualización disponible Los siguientes paquetes tienen una actualización disponible, pero requieren un destino explícito para la actualización: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly Descargando Label displayed while downloading an application installer. No se admite el tipo de instalador anidado Se sabe que este instalador reinicia el terminal o el shell Este paquete requiere una ubicación de instalación Se sabe que los instaladores siguientes reinician el terminal o el shell: Los instaladores siguientes requieren una ubicación de instalación: Especifique la raíz de instalación: ¿Desea continuar? Contratos para This will be followed by a package name, and then a list of license agreements El paquete requiere la ubicación de instalación, pero no se ha proporcionado Deshabilitar mensajes interactivos Description for a command line argument, shown next to it in the help Se encontró un paquete existente ya instalado. Intentando actualizar el paquete instalado... Ejecutar directamente el comando y continuar con problemas no relacionados con la seguridad Description for a command line argument, shown next to it in the help Ya existe un paquete portátil de otro origen Archivo extraído correctamente Extrayendo archivo... El examen de archivo detectó malware. Para invalidar esta comprobación, use --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} El examen de archivo detectó malware. Continuar debido a --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} Omite la actualización si ya existe una versión instalada Ya hay instalada una versión del paquete. Instalación cancelada. No se puede habilitar {0}. Esta configuración está controlada por la directiva. Para obtener más información, póngase en contacto con el administrador del sistema. {Locked="{0}"} The value will be replaced with the feature name No se puede deshabilitar {0}. Esta configuración está controlada por la directiva. Para obtener más información, póngase en contacto con el administrador del sistema. {Locked="{0}"} The value will be replaced with the feature name Agrega un nuevo PIN. Un PIN puede limitar el Administrador de paquetes de Windows de actualizar un paquete a intervalos específicos de versiones, o puede impedir que se actualice el paquete por completo. Es posible que un paquete anclado aún se actualice por sí solo y se actualice desde fuera del Administrador de paquetes de Windows. De forma predeterminada, un paquete anclado se puede actualizar mencionándolo explícitamente en el comando "upgrade" o agregando la marca "--include-pinned" a "winget upgrade --all". {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} Crear un nuevo PIN Administre los pines de paquete con los submenses. Un PIN puede limitar el Administrador de paquetes de Windows de actualizar un paquete a intervalos específicos de versiones, o puede impedir que se actualice el paquete por completo. Un paquete anclado aún puede actualizarse por sí solo y actualizarse desde fuera del Administrador de paquetes de Windows. Administrar PIN de paquete Enumere todos los PIN actuales o detalles completos de un PIN específico. Lista de PIN actuales Quite un PIN de paquete específico. Quitar un PIN de paquete Restablecer todos los PIN existentes. Restablecer PIN Versión a la que se ancla el paquete. El carácter comodín "*" se puede usar como parte de la última versión. Impedir que se actualice hasta que se quite el PIN, lo que impide invalidar argumentos Anclar una versión instalada específica Instalado Value used in a table to indicate that a package comes from the list of packages installed in the machine Exportar configuración como JSON Exportar configuración Configuración de usuario Label displayed for the file containing the user settings. No se pudo cargar el archivo de configuración. Se usarán los valores predeterminados. Seleccionar filtro de ámbito de paquete instalado (user o machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. Anclaje agregado correctamente Ya hay un PIN para el paquete {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. Ya existe un PIN para el paquete {0}. Sobrescribiendo debido al argumento --force. {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Ya existe un PIN para el paquete {0}. Use el argumento --force para sobrescribirlo. {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Restableciendo todos los anclajes actuales. Use el argumento --force para restablecer todos los anclajes. Se quitarán los siguientes anclajes: {Locked="--force"} Tipo de anclaje No hay ningún anclaje para el paquete {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. No hay anclajes configurados. Shown when listing or modifying existing pins if there are none. Los anclajes se restablecieron correctamente Shown after resetting (deleting) all the pins No se puede abrir la base de datos de anclaje. Error message for when we cannot open the database containing package pins. Se proporcionó un argumento que solo se puede usar para un solo paquete Se proporcionaron varios argumentos mutuamente excluyentes: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together La {0} de argumentos solo se puede usar con {1} {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" Deshabilitado As in enabled/disabled Habilitado As in enabled/disabled Estado Header for a table listing the state (enabled/disabled) of Group Policies and Settings La consulta usada para buscar un paquete No se encontró el paquete: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. Se encontraron varios paquetes para: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. Error en la búsqueda de: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} paquetes tienen un pin que debe quitarse antes de la actualización {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade No se puede actualizar el paquete con winget. Use el método proporcionado por el publicador para actualizar este paquete. Actualizar paquetes aunque tengan un PIN que no sea de bloqueo Enumere los paquetes incluso si tienen un pin que impide la actualización. Solo se puede usar con el argumento --upgrade-available {Locked="--upgrade-available"} El anclaje se quitó correctamente Se encontró una versión más reciente, pero el paquete tiene un PIN que impide su actualización. El paquete está anclado y no se puede actualizar. Use el comando "winget pin" para ver y editar anclajes. Algunos tipos de anclaje se pueden omitir con el argumento --include-pinned. {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} paquetes tienen anclajes que impiden la actualización. Use el comando "winget pin" para ver y editar los anclajes. Si usa el argumento --include-pinned puede mostrar más resultados. {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages Garantiza que el sistema coincida con el estado deseado tal y como se describe en la configuración proporcionada. Puede descargar o ejecutar procesadores para lograr el estado deseado. La configuración y los procesadores deben comprobarse para asegurarse de que son de confianza antes de aplicarlos. Configura el sistema en el estado deseado Muestra los detalles de la configuración proporcionada. De forma predeterminada, no se modificará el sistema, pero algunas opciones harán que los archivos se descarguen o carguen. Muestra los detalles de una configuración Comprueba que el sistema coincide con el estado deseado tal y como se describe en la configuración proporcionada. Puede descargar o ejecutar procesadores para probar el estado deseado. La configuración y los procesadores deben comprobarse para asegurarse de que son de confianza antes de ejecutarlos. Comprueba el sistema con respecto a un estado deseado Valida que un archivo de configuración sea correcto. Valida un archivo de configuración El campo '{0}' en el archivo de configuración no es del tipo correcto. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. La ruta al archivo de configuración El archivo de configuración no es válido. Se desconoce la versión {0} del archivo de configuración. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. Acepta la advertencia de configuración, evitando un aviso interactivo Aplicar Indicates that this item is used to write state Aserción Indicates that this item is used to check/assert the state rather than write to it Dependencias:{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. Parte de la configuración no se aplicó correctamente. No se pudo obtener información detallada sobre la configuración. informar Indicates that this item is used to retrieve values for future use rather than writing them Local Used to indicate that the item is present on the device. Módulo: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. Módulo: {0} por {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. Configuración: Label for the values that are used as inputs for this item when applying state Configuración aplicada correctamente. La unidad se ha aplicado correctamente. Se está aplicando otra configuración al sistema. Esta configuración continuará tan pronto como sea posible... Es responsable de comprender las opciones de configuración que va a ejecutar. Microsoft no es responsable del archivo de configuración que ha creado o importado. Esta configuración puede cambiar la configuración en Windows, instalar software, cambiar la configuración del software (incluida la configuración de seguridad) y aceptar contratos de usuario para paquetes y servicios de terceros en su nombre.  Al ejecutar este archivo de configuración, reconoce que comprende y acepta estos recursos y configuraciones. Sus propietarios le conceden licencia para todas las aplicaciones instaladas. Microsoft no es responsable ni concede licencias a paquetes o servicios de terceros. Legal approved. Do not change without approval. ¿Ha revisado la configuración y desea proceder a aplicarla al sistema? PM approved. La configuración está vacía. No se ha encontrado la característica [{0}]. {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. No se han podido habilitar las dependencias de características de Windows. Para continuar con la instalación, use "--force". {Locked="--force"} Es necesario reiniciar para habilitar completamente las características de Windows; para anular esta comprobación use "--force". "Windows Feature(s)" is the name of the options Windows features setting. No se han podido habilitar las dependencias de características de Windows; procediendo debido a --force "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} Habilitando [{0}]... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. No se ha podido habilitar la característica [{0}]: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. Es necesario reiniciar para habilitar completamente las características de Windows. continuar debido a --force "Windows Feature(s)" is the name of the options Windows features setting. Esperando a que se complete otra instalación/desinstalación... Versión anclada Table header for the version to which a package is pinned; meaning it should not update from that version. <Para más detalles, consulte el archivo de registro> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. Recuperando detalles de configuración Inicializando el sistema de configuración Leyendo archivo de configuración El sistema no está en el estado deseado establecido por la configuración. Error en esta unidad de configuración por una razón desconocida: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Error en la unidad de configuración debido a la configuración: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Error de la unidad de configuración al intentar obtener el estado actual del sistema. Error de la unidad de configuración al intentar aplicar el estado deseado. Error de la unidad de configuración al intentar probar el estado actual del sistema. Error interno en la unidad de configuración: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Error en la unidad de configuración porque una condición previa no es válida: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Error en la unidad de configuración debido al estado del sistema: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Error de la unidad de configuración al intentar ejecutar: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. La configuración contiene el identificador '{0}' varias veces. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. No se encontró la dependencia '{0}' en la configuración. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. Esta unidad de configuración se omitió manualmente. El módulo de la unidad de configuración está disponible en varias ubicaciones con la misma versión. Error al cargar el módulo para la unidad de configuración. Se encontraron varias coincidencias para la unidad de configuración; especifique el módulo para seleccionar el correcto. No se encontró la unidad de configuración. La unidad de configuración no estaba en el módulo como se esperaba. Esta unidad de configuración no se ejecutó porque se produjo un error en una dependencia o no se ejecutó. Esta unidad de configuración no se ha ejecutado porque se produjo un error en una aserción o era falsa. La unidad de configuración devolvió un resultado inesperado durante la ejecución. Esta unidad de configuración no se ejecutó por una razón desconocida: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. El campo '{0}' tiene un valor no válido: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. Falta el campo ''{0}'' o está vacío. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. Vea la línea {0}, columna {1} en el archivo. {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. Cancelando operación Instalación del paquete de código auxiliar para AppInstaller Instalar el paquete completo para AppInstaller Habilite las características extendidas. Requiere acceso a la tienda. Las opciones '--enable' y '--disable' no se pueden usar con otros argumentos. {Locked="--enable", "--disable"} Habilitación de características extendidas. Requiere acceso a la tienda. Las características extendidas no están habilitadas. Ejecute "winget configure --enable" para habilitarlos. {Locked="winget configure --enable"} Las características extendidas están habilitadas. Deshabilite las características extendidas. Requiere acceso a la tienda. Deshabilitando las características extendidas. Requiere acceso a la tienda. Las características extendidas están deshabilitadas. Omite el procesamiento de las dependencias del paquete y las características de Windows Dependencias omitidas. No se pudo actualizar PATH variable para el proceso. Las instalaciones posteriores que dependen de los cambios en la variable de PATH pueden generar errores. {Locked="PATH"} Error de algunas unidades de configuración al probar su estado. El sistema se encuentra en el estado de configuración descrito. No se probó el estado de configuración. El sistema no se encuentra en el estado de configuración descrito. Resultado de prueba inesperado: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). ¿Ha revisado la configuración y desea continuar con la comprobación en el sistema? El archivo de configuración no es un archivo de YAML válido. {Locked="YAML"} YAML is a file format name. Esta unidad de configuración forma parte de un ciclo de dependencia. La validación no encontró ningún problema. La unidad de configuración solo está disponible como versión preliminar, pero no está marcada de esa manera en la configuración. Agregue "allowPrerelease: true" a las "directives". {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. La unidad de configuración se encontró localmente, pero no se encontró en ningún catálogo configurado. Asegúrese de que está presente en cualquier sistema antes de aplicar la configuración. La unidad de configuración no está disponible públicamente; asegúrese de que cualquier persona que vaya a usar esta configuración tenga acceso a ella. No se proporcionó el módulo. Especificar el módulo mejora el rendimiento y evita futuras colisiones de nombres. Descarga el instalador del paquete seleccionado, ya sea buscando en un origen configurado o directamente desde un manifiesto. De forma predeterminada, la consulta debe coincidir sin distinción de mayúsculas y minúsculas con el identificador, nombre o moniker del paquete. Otros campos se pueden usar pasando su opción apropiada. De forma predeterminada, el comando de descarga descargará el instalador adecuado en la carpeta Descargas del usuario. Descarga el instalador de un paquete determinado Directorio en el que se descargan los instaladores Descargando dependencias: Instalador descargado: {0} {Locked="{0}"} Full path of the downloaded installer. Seleccionar el tipo de instalador Descargas del instalador Se prohíbe que el instalador se descargue para una instalación sin conexión posterior. `--module-path allusers` requiere privilegios de administrador para ejecutarse. {Locked="--module-path allusers"} Especifica la ubicación en el equipo local para almacenar módulos. La predeterminada es %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} El valor de `--module-path allusers` debe ser `currentuser`, `allusers`, `default` o una ruta de acceso absoluta. {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Habilitar configuración de Administrador de paquetes de Windows Recuperar información sobre errores. Dado un número, la salida contendrá detalles sobre el error, incluido el nombre del símbolo si se trata de un error específico de WinGet. Dado una cadena, se busca este valor en los errores específicos de WinGet. Obtener información sobre errores Valor para buscar en la información de error El número especificado es demasiado grande para ser HRESULT. Código de error desconocido Error interno Los argumentos de la línea de comandos no son válidos Error al ejecutar el comando Error al abrir el manifiesto Señal de cancelación recibida Error al ejecutar ShellExecute No se puede procesar el manifiesto. La versión del manifiesto es posterior a la admitida. Actualice el cliente. Error al descargar el instalador No se puede escribir en el índice; es una versión de esquema superior El índice está dañado La información de origen configurada está dañada El nombre de origen ya está configurado El tipo de origen no es válido El archivo MSIX es una agrupación, no un paquete Faltan los datos requeridos por el origen Ninguno de los instaladores es aplicable para el sistema actual El hash del archivo del instalador no coincide con el manifiesto El nombre de origen no existe La ubicación de origen ya está configurada con otro nombre No se encontraron paquetes No hay orígenes configurados Se encontraron varios paquetes que coinciden con los criterios No se encontró ningún manifiesto que coincida con los criterios No se pudo obtener la carpeta pública del paquete de origen El comando requiere privilegios de administrador para ejecutarse La ubicación de origen no es segura La directiva bloquea el cliente de Microsoft Store La directiva bloquea la aplicación Microsoft Store La característica está actualmente en desarrollo. Se puede habilitar con la configuración de winget. Error al instalar la aplicación Microsoft Store Error al autocompletar Error al inicializar el analizador YAML Se encontró una clave YAML no válida Se encontró una clave YAML duplicada Operación YAML no válida Error al compilar el documento YAML Estado de emisor YAML no válido Datos YAML no válidos Error de LibYAML Validación del manifiesto correcta con advertencia. Error de validación de manifiesto. El manifiesto no es válido No se encontraron actualizaciones aplicables actualización de WinGet: todas completadas con errores Error en la comprobación de seguridad del instalador El tamaño de descarga no coincide con la longitud de contenido esperada No se encontró el comando de desinstalación Error al ejecutar el comando de desinstalación Error de iterador de interrupción de ICU Error de asignación de caso de ICU Error de regex de ICU No se pudieron instalar uno o varios paquetes importados No se encontraron uno o varios paquetes solicitados El archivo JSON no es válido La ubicación de origen no es remota No se admite el origen REST configurado Datos no válidos devueltos por el origen REST La operación está bloqueada por la directiva de grupo Error interno de la API de REST Dirección URL de origen REST no válida Tipo MIME no admitido devuelto por la API de REST Versión del contrato de origen REST no válida Los datos de origen están dañados o manipulados Error al leer de la secuencia No se han aceptado los contratos de paquete Error al leer la entrada en el símbolo del sistema Uno o varios orígenes no admiten la solicitud de búsqueda. No se encuentra el punto de conexión de API de REST. Error al abrir el origen. No se han aceptado los contratos de origen El tamaño del encabezado supera el límite permitido de 1024 caracteres. Reduzca el tamaño e inténtelo de nuevo. Falta el archivo de recursos Error al ejecutar la instalación de MSI Los argumentos de msiexec no son válidos Error al abrir uno o varios orígenes Error al validar las dependencias Falta uno o varios paquetes Columna de tabla no válida La versión de actualización no es más reciente que la versión instalada La versión de actualización es desconocida y no se especificó la invalidación Error de conversión de ICU Error al instalar el paquete portátil El volumen no admite puntos de análisis Ya existe un paquete portátil de otro origen. No se puede crear symlink. La ruta de acceso apunta a un directorio. El instalador no se puede ejecutar desde un contexto de administrador. Error al desinstalar el paquete portátil Error al validar los valores de DisplayVersion con el índice. No se admiten uno o varios argumentos. No se permiten caracteres nulos incrustados para SQLite No se pudo encontrar el instalador anidado en el archivo. Error al extraer el archivo. Se proporcionó una ruta de acceso de archivo relativa no válida al instalador anidado. El certificado de servidor no coincide con ninguno de los valores esperados. Debe proporcionarse la ubicación de instalación. Error al analizar el malware de archivo. Se encontró al menos una versión del paquete instalada. Ya existe un pin para el paquete. No hay ningún pin para el paquete. No se puede abrir la base de datos del pin. Error al instalar una o varias aplicaciones Error al desinstalar una o varias aplicaciones Una o varias consultas no devolvieron exactamente una coincidencia El paquete tiene un PIN que impide la actualización. El paquete instalado actualmente es el paquete de código auxiliar Señal de apagado de la aplicación recibida Error al descargar las dependencias del paquete. No se pudo descargar el paquete. La descarga para la instalación sin conexión está prohibida. Un servicio necesario está ocupado o no está disponible. Vuelve a intentarlo más tarde. El GUID proporcionado no corresponde a un estado de reanudación válido. La versión del cliente actual no coincide con la versión del cliente del estado guardado. Los datos de estado de reanudación no son válidos. No se puede abrir la base de datos de punto de control. Se superó el límite máximo de reanudación. Información de autenticación no válida. Método de autenticación no permitido. Error de autenticación. Error de autenticación. Se requiere autenticación interactiva. Error de autenticación. Cancelado por el usuario. Error de autenticación. La cuenta autenticada no es la cuenta deseada. La aplicación se está ejecutando actualmente. Salga de la aplicación e inténtelo de nuevo. Ya hay otra instalación en curso. Inténtelo de nuevo más tarde. Se está usando uno o varios archivos. Salga de la aplicación e inténtelo de nuevo. Falta una dependencia de este paquete en el sistema. No hay más espacio en el equipo. Haga espacio e inténtelo de nuevo. No hay suficiente memoria disponible para instalar. Cierre otras aplicaciones e inténtelo de nuevo. Esta aplicación requiere conectividad a Internet. Conéctese a una red e inténtelo de nuevo. Esta aplicación encontró un error durante la instalación. Póngase en contacto con el soporte técnico. Reinicie el equipo para finalizar la instalación. Error en la instalación. Reinicia el equipo e inténtalo de nuevo. El equipo se reiniciará para finalizar la instalación. Ha cancelado la instalación. Ya hay otra versión de esta aplicación instalada. Ya hay instalada una versión posterior de esta aplicación. Las directivas de la organización impiden la instalación. Póngase en contacto con su administrador. Error al instalar las dependencias de paquetes. Otra aplicación está usando actualmente la aplicación. Parámetro no válido. Paquete no compatible con el sistema. El instalador no admite la actualización de un paquete existente. La instalación falló debido a un error del instalador personalizado. No se encontró la entrada de aplicaciones y características para el paquete. La ubicación de instalación no es aplicable. No se encontró la ubicación de instalación. El hash del archivo existente no coincide. Archivo no encontrado. Se encontró el archivo, pero no se comprobó el hash. No se pudo tener acceso al archivo. El archivo de configuración no es válido. La sintaxis YAML no es válida. Un campo de configuración tiene un tipo no válido. La configuración tiene una versión desconocida. Error al aplicar la configuración. La configuración contiene un identificador duplicado. Falta una dependencia en la configuración. La configuración tiene una dependencia no atendida. Error en una aserción para la unidad de configuración. La configuración se omitió manualmente. El usuario rechazó continuar la ejecución. El gráfico de dependencias contiene un ciclo que no se puede resolver. La configuración tiene un valor de campo no válido. Falta un campo en la configuración. Error de algunas unidades de configuración al probar su estado. No se probó el estado de configuración. No se instaló la unidad de configuración. No se encontró la unidad de configuración. Se encontraron varias coincidencias para la unidad de configuración; especifique el módulo para seleccionar el correcto. Error de la unidad de configuración al intentar obtener el estado actual del sistema. Error de la unidad de configuración al intentar probar el estado actual del sistema. Error de la unidad de configuración al intentar aplicar el estado deseado. El módulo de la unidad de configuración está disponible en varias ubicaciones con la misma versión. Error al cargar el módulo para la unidad de configuración. La unidad de configuración devolvió un resultado inesperado durante la ejecución. Una unidad contiene una configuración que requiere la raíz de configuración. El procesador de configuración no admite la operación. No disponible Dependencias de características de Windows habilitadas correctamente Error al cargar el módulo para la unidad de configuración porque requiere privilegios de administrador para ejecutarse. Una unidad contiene una configuración que requiere la raíz de configuración. Error al cargar el módulo para la unidad de configuración porque requiere privilegios de administrador para ejecutarse. Reanuda la ejecución de un comando guardado anteriormente pasando el identificador único del comando guardado. Se usa para reanudar un comando ejecutado que puede haberse terminado debido a un reinicio. Reanuda la ejecución de un comando guardado previamente. Identificador único del estado guardado que se va a reanudar No se admite la reanudación del estado desde una versión de cliente diferente: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. El estado de reanudación no existe: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. No se encontraron datos en el estado de reanudación. Este comando no admite la reanudación. Permite un reinicio si procede Iniciando reinicio para completar la operación... Error al iniciar un reinicio. La operación de reanudación supera el límite permitido de reanudaciones {0}. Para reanudar manualmente, ejecute el comando '{1}'. {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. Omitir el límite de reanudación de un estado guardado Esquema URI no admitido: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. URI no bien formado: {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. No se pudo analizar {0} contenido de configuración de la unidad de configuración o el contenido de configuración está vacío. {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. {0} unidad de configuración no tiene el argumento necesario: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. {0} unidad de configuración no tiene el argumento recomendado: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. La unidad de configuración de WinGetSource está en conflicto con un origen conocido: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. La unidad de configuración de WinGetSource aserciones en un origen de terceros: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage declara UseLatest y Version. Id. de paquete: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} La unidad de configuración WinGetPackage aserciones en un paquete de origen de terceros. Id. de paquete: {0}; Origen: {1} {Locked="WinGetPackage,{0},{1}"} El paquete de la unidad de configuración WinGetPackage depende de un origen de terceros no configurado previamente. Id. de paquete: {0}; Origen: {1} {Locked="WinGetPackage,{0},{1}"} El paquete de unidad de configuración de WinGetPackage depende de un origen de terceros. Se recomienda declarar la dependencia en la sección uni dependsOn. Id. de paquete: {0}; Origen: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} No se puede validar el paquete de la unidad de configuración de WinGetPackage. Error al abrir el origen. Id. de paquete: {0}; Origen: {1} {Locked="WinGetPackage,{0},{1}"} No se puede validar el paquete de la unidad de configuración de WinGetPackage. No se encontró el paquete. Id. de paquete: {0} {Locked="WinGetPackage,{0}"} No se puede validar el paquete de la unidad de configuración de WinGetPackage. Se encontró más de un paquete. Id. de paquete: {0} {Locked="WinGetPackage,{0}"} No se puede validar el paquete de la unidad de configuración de WinGetPackage. No se encontró la versión del paquete. Id. de paquete: {0}; Versión {1} {Locked="WinGetPackage,{0},{1}"} Paquete de unidad de configuración WinGetPackage especificado con una versión específica mientras que sólo una versión del paquete está disponible. Id. de paquete: {0}; Versión: {1} {Locked="WinGetPackage,{0},{1}"} No se puede validar el paquete de la unidad de configuración de WinGetPackage. Id. de paquete: {0} {Locked="WinGetPackage,{0}"} Especificar la preferencia de ventana de autenticación (silent, silentPreferred, or interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. Especifique la cuenta que se usará para la autenticación No se pudo agregar el origen. Esta versión de winget no admite el método de autenticación del origen. Intente actualizar a la versión de winget más reciente. {Locked="winget"} El origen de {0} requiere autenticación. La solicitud de autenticación puede aparecer cuando sea necesario. La información autenticada se compartirá con el origen para la autorización de acceso. {Locked="{0}"} Repara el paquete seleccionado, encontrado al buscar en una lista de paquetes instalados o bien, directamente desde un manifiesto. De forma predeterminada, la consulta debe coincidir con el id, nombre o el moniker del paquete sin distinguir entre mayúsculas y minúsculas. Se pueden usar otros campos usando la opción apropiada. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Repara el paquete seleccionado No se encuentra el comando de reparación para este paquete. Póngase en contacto con el anunciante del paquete para obtener soporte técnico. La tecnología del instalador en uso no coincide con la versión instalada actualmente. No se encontró el comando de reparación. La tecnología del instalador en uso no es compatible con la reparación. La operación de reparación se completó correctamente. Reparación abandonada Iniciando reparación del paquete... Error al reparar el paquete de Microsoft Store. Código de error: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. Error en la operación de reparación. La operación de reparación no es aplicable. No hay versiones de paquete coincidentes disponibles en los orígenes configurados. La configuración del sistema actual no admite la reparación de este paquete. La tecnología del instalador en uso no admite la reparación. El paquete instalado para el ámbito de usuario no se puede reparar cuando se ejecuta con privilegios de administrador. No se permiten operaciones de reparación que requieran privilegios de administrador en paquetes instalados dentro del ámbito de usuario. Error de reparación con el código de salida: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. La conexión de SQLite finalizó para evitar daños. Desinstalar todas las versiones La versión sobre la que actuar Hay varias versiones de este paquete instaladas. Restrinja la búsqueda, pase el argumento "--version" para seleccionar una o pase la marca "--all-versions" para desinstalarlas todas. {Locked="--version,--all-versions"} Habilitar Administrador de paquetes de Windows opciones de la línea de comandos del proxy Describes a Group Policy that can enable the use of the --proxy option to set a proxy Establecer un proxy para usar para esta ejecución Deshabilitar el uso del proxy para esta ejecución No se puede restablecer {0}. Esta configuración está controlada por la directiva. Para obtener más información, póngase en contacto con el administrador del sistema. {Locked="{0}"} The value will be replaced with the feature name Restablezca la configuración de administrador "{0}". {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. No se puede establecer {0}. Esta configuración está controlada por la directiva. Para obtener más información, póngase en contacto con el administrador del sistema. {Locked="{0}"} The value will be replaced with the feature name Establezca la configuración de administrador "{0}" en "{1}". {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced Nombre de la configuración que se va a modificar Valor que se va a establecer para la configuración. Restablece una configuración de administración a su valor predeterminado. Restablece una configuración de administración a su valor predeterminado. Establece el valor de una configuración de administrador. Establece el valor de una configuración de administrador. Excluye un origen de la detección a menos que se especifique Excluye un origen de la detección (true o false) Explícito Nivel de confianza del origen (ninguno o de confianza) Nivel de confianza Error al obtener el catálogo de paquetes de Microsoft Store. No se encontró ningún paquete de Microsoft Store aplicable en el catálogo de paquetes de Microsoft Store. Error al obtener la información de descarga del paquete Microsoft Store. No se encontró ningún paquete de Microsoft Store aplicable para descargar. Error al recuperar la licencia del paquete de Microsoft Store. No se encontró ningún paquete de Microsoft Store aplicable. Hash de paquete de Microsoft Store comprobado correctamente Error de coincidencia del hash del paquete Microsoft Store Paquete de Microsoft Store descargado: {0} {Locked="{0}"} Full path of the downloaded package. Error al descargar el paquete de Microsoft Store: {0} {Locked="{0}"} Package name. Descarga del paquete de Microsoft Store completada Descargando paquetes principales de Microsoft Store... Descargando paquetes de dependencias de Microsoft Store... Recuperando información de descarga del paquete de Microsoft Store Error al recuperar la información de descarga del paquete Microsoft Store Recuperar la licencia del paquete de Microsoft Store Se guardó la licencia del paquete de Microsoft Store: {0} {Locked="{0}"} License file full path. Error al recuperar la licencia del paquete de Microsoft Store La descarga del paquete de Microsoft Store no admite el argumento --rename. El paquete de Microsoft Store usará nombres proporcionados por el catálogo de Microsoft Store. {Locked="--rename"} Microsoft Store descarga del paquete requiere la autenticación de id. de Microsoft Entra. La solicitud de autenticación puede aparecer cuando sea necesario. La información autenticada se compartirá con servicios Microsoft para la autorización de acceso. Para Microsoft Store licencias de paquetes, la cuenta de id. de Microsoft Entra debe ser miembro de Administrador global, Administrador de usuarios o Administrador de licencias. {Locked="Global Administrator,User Administrator,License Administrator"} Omite la recuperación de la licencia sin conexión del paquete de Microsoft Store Seleccionar la plataforma de destino Agregando archivo de configuración: {0} {Locked="{0}"} Se ha exportado correctamente Obteniendo opciones de configuración... Se debe proporcionar al menos --packageId o --module con --resource. O use --all para exportar todas las configuraciones de paquete. {Locked="--packageId,--module, --resource, --all"} Los argumentos --packageId, --module y --resource no se pueden usar con --all. {Locked="--packageId,--module, --resource, --all"} Exporta recursos de configuración a un archivo de configuración. Cuando se usa con --all, exporta todas las configuraciones de paquete. Cuando se usa con --packageId, exporta un recurso WinGetPackage del identificador de paquete dado. Cuando se usa con --module y --resource, obtiene la configuración del recurso y lo exporta al archivo de configuración. Si el archivo de configuración de salida ya existe, anexa los recursos de configuración exportados. {Locked="WinGetPackage,--packageId,--module, --resource"} Exporta recursos de configuración a un archivo de configuración. Módulo del recurso que se va a exportar. Identificador del paquete que se va a exportar. Recurso de configuración que se va a exportar. Exporta todas las configuraciones del paquete. La unidad de configuración no pudo obtener sus propiedades. Error al exportar la configuración. Configurar {0} {Locked="{0}"} Instalar {0} {Locked="{0}"} Algunos de los datos presentes en el archivo de configuración se truncaron para esta salida; inspeccionar el contenido del archivo para obtener el contenido completo. <este valor se ha truncado; inspeccionar el contenido del archivo para ver el texto completo> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. Muestra los detalles de alto nivel de las configuraciones que se han aplicado al sistema. Estos datos se pueden usar con `configure` comandos para obtener más detalles. {Locked="`configure`"} Muestra el historial de configuración No hay ninguna configuración en el historial. Seleccionar elementos del historial No se encontró ninguna configuración única que coincida con los datos proporcionados. Proporcione el nombre completo o parte del identificador que coincida sin ambigüedad con la configuración deseada. Quitar el elemento del historial Primera aplicación Column header for date values indicating when a configuration was first applied to the system. Identificador Nombre Origen Ruta de acceso No se encontró la configuración especificada. Completadas The state of processing an item. En curso The state of processing an item. Pendiente The state of processing an item. Desconocido The state of processing an item. Supervisar el estado de configuración. As in "to monitor the status of a configuration being applied". Completadas The state of processing an item. En curso The state of processing an item. Pendiente The state of processing an item. Omitido The state of processing an item. Desconocido The state of processing an item. Aplicación iniciada When the configuration application started. Aplicación finalizada When the configuration application ended. Resultado Detalles Estado The state of processing an item. Unidad El paquete no es compatible con la plataforma o la versión actual de Windows. Suprimir la visualización de los detalles de configuración inicial cuando sea posible Se pueden descargar varios paquetes de Microsoft Store destinados a diferentes plataformas y arquitecturas. --platform, --architecture se puede usar para filtrar los paquetes. {Locked="--platform--architecture"} No se pudo recuperar Microsoft Store licencia del paquete. La cuenta de id. de Microsoft Entra no es miembro de Administrador global, Administrador de usuarios o Administrador de licencias. Use --skip-license para omitir la recuperación de Microsoft Store licencia del paquete. {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} El paquete Microsoft Store no admite descargas. El paquete Microsoft Store no admite descargas. No se pudo recuperar Microsoft Store licencia del paquete. La cuenta de id. de Microsoft Entra no tiene el privilegio necesario. El parámetro no se puede pasar a través del límite de integridad. Instalador de cero bytes descargado; asegúrese de que la conexión de red funciona correctamente. Instalador de cero bytes descargado; asegúrese de que la conexión de red funciona correctamente. Administrar fuentes Administrar fuentes con submenses. Las fuentes se pueden instalar, actualizar o desinstalar de forma similar a packages. Familia Formato "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' Filtrar resultados por nombre de familia Cara "Face" represents the typeface of a font family such as 'Bold' or 'Italic' Rutas de acceso No se encontró ninguna fuente que coincida con los criterios de entrada. Lista de fuentes instaladas Muestra todas las fuentes instaladas, todos los archivos de fuente o todos los detalles de una fuente específica. Versión Módulos de configuración PowerShell Modules that are used for the Configuration feature El instalador del paquete requiere autenticación. La solicitud de autenticación puede aparecer cuando sea necesario. La información autenticada se compartirá con la dirección URL de descarga del instalador. No se pudo descargar el instalador. Esta versión de winget no admite el método de autenticación de descarga del instalador. Intente actualizar a la versión de winget más reciente. {Locked="winget"} Error al descargar el instalador. Error de autenticación. Especificar la ruta al procesador de configuración Comandos de recursos de DSC v3 DSC stands for "Desired State Configuration". It should already have a locked translation. Los subcomandos aquí implementan recursos de Desired State Configuration (DSC) v3 para configurar WinGet y paquetes. Obtener el estado del recurso Establecer el estado del recurso Describir los cambios de estado necesarios Probar el estado del recurso Eliminar el estado del recurso Obtener todas las instancias de estado Validar el contenido del grupo Resolver el estado externo Ejecutar el adaptador Obtener el esquema de recursos Obtener el manifiesto del recurso Administrar el estado del paquete Administrar paquetes mediante WinGet. Indica si una instancia debe existir o no. Indica si una instancia se encuentra en el estado deseado. El identificador del paquete. El origen del paquete. La versión del paquete. Método para hacer coincidir el identificador con un paquete. Indica que se debe instalar la última versión disponible del paquete. El modo de instalación que se utilizará si es necesario. El ámbito de destino del paquete. Indica si se aceptan acuerdos para orígenes y paquetes. Administrar el archivo de configuración del usuario Administrar la configuración del usuario de WinGet. Contenido del archivo JSON de configuración. La acción utilizada para aplicar la configuración. El valor se ha registrado para la correlación Administrar la configuración de origen Administrar las fuentes de WinGet. El nombre que se utilizará para el origen. El argumento para el origen. El tipo de origen. El nivel de confianza del origen. Indica si el origen se incluye cuando las llamadas no especifican un origen. Aplicando la unidad de configuración... Exportando la unidad de configuración... Obteniendo procesadores de unidad de configuración... Asegúrese de que el módulo requerido para la exportación [{0}] esté disponible {Locked="{0}"} No se pudo probar o adquirir el módulo requerido. La configuración relacionada no se exportará. Exportar [{0}] {Locked="{0}"} No se pudo exportar el recurso. No se pudieron obtener los procesadores de unidad. La configuración del paquete individual no se exportará. Directorio donde se escribirán los resultados Desired State Configuration paquete no se encuentra en el sistema. Instalando el paquete... No se pudo instalar Desired State Configuration paquete. Instale el paquete manualmente o proporcione la ruta de acceso a dsc.exe mediante --processor-path argumento. {Locked="dsc.exe","--processor-path"} Objeto que contiene la configuración de administrador y sus valores. Administrar la configuración del administrador Administrar la configuración del administrador de WinGet. Restablecer todas las configuraciones de administración Se han restablecido todas las configuraciones de administración. Información de MCP MCP stands for Model Context Protocol and should probably remain as-is Información de MCP (Protocolo de contexto de modelo) para el Administrador de paquetes de Windows. Para configurar manualmente el Administrador de paquetes de Windows servidor MCP con el cliente MCP, use el siguiente fragmento JSON en el objeto `servers`: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. Habilitar Administrador de paquetes de Windows (servidor MCP) Versión del sistema operativo de destino Error al instalar una o varias fuentes. El archivo de fuente no es compatible y no se puede instalar. Una o varias fuentes del paquete de fuentes no se admiten y no se pueden instalar. Error al instalar la fuente; limpiando. Fuente ya instalada. Id. de paquete WinGet admitido Título No se encontró el archivo de fuente. Error al desinstalar la fuente. Es posible que la fuente no esté en buen estado. Intente desinstalar después de reiniciar. Error de validación de fuente. Error al revertir la fuente. Es posible que la fuente no esté en buen estado. Intente desinstalar después de reiniciar. Mostrar información detallada del archivo de fuente. Error en la validación del paquete de fuentes. El intento de reversión de la instalación de fuentes con error no se realizó correctamente. Es posible que sea necesario reiniciar para desinstalar correctamente la fuente. Error al desinstalar el paquete de fuentes. Esto suele deberse a que el sistema o una aplicación están usando fuentes. Es posible que la desinstalación se haya realizado correctamente después de reiniciar el equipo. Aceptar "OK" means the font is in a good healthy state Dañada "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. Estado Desconocido El paquete de fuentes ya está instalado. Mostrar información detallada sobre los paquetes Providing this argument causes the CLI to output additional details about installed application packages. Canal: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). Identificador local: Precedes a value that is the unique identifier for the installed package on the local system. Nombre de familia de paquete: Precedes a value that is the APPX/MSIX package family name of the installed package. Código de producto: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. Código de actualización: Precedes a value that is the MSI Upgrade Code for the installed package. Ámbito de la instalación: Precedes a value that is the scope of the installation of the package (ex. user, machine). Arquitectura instalada: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). Configuración regional instalada: Precedes a value that is the locale of the installed package. Ubicación instalada: Precedes a value that is the directory path to the installed package. Fuente de origen: Precedes a value that names the package source where the installed package originated from. Actualizaciones disponibles: Precedes a list of package upgrades available for the installed package. Categoría del instalador: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). Valor antiguo Column title for listing edit changes. Nuevo valor Column title for listing the new value. ================================================ FILE: Localization/Resources/fr-FR/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/fr-FR/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 L’alias contigu n’est pas un indicateur : '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Alias de drapeau contigu introuvable : '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Les arguments suivants sont disponibles : Message displayed to inform the user about the available command line arguments. Les alias de commande suivants sont disponibles : Message displayed to inform the user about the available command line alias arguments. Les commandes suivantes sont disponibles : Title displayed to inform the user about the available commands. Disponible As in "a new version is available to upgrade to". Les options suivantes sont disponibles : Message displayed to inform the user about the available options. Les sous-commandes suivantes sont disponibles : Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} mises à niveau disponibles. {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Utiliser le canal spécifié; par défaut, il s’agit du grand public commande Label displayed for a command to give the software. Filtrer les résultats par commande Description message displayed to inform the user about filtering the search results by a package command. La ligne de commande complète pour l’achèvement Cette commande nécessite des privilèges d'administrateur pour être exécutée. Cette commande peut être utilisée pour demander la saisie semi-automatique de la ligne de commande sensible au contexte. La ligne de commande, la position du curseur et le mot à terminer sont transmis. La sortie est un ensemble de valeurs potentielles basées sur les entrées, avec une valeur possible par ligne. Active l’achèvement de la ligne de commande sensible au contexte Afficher au maximum le nombre spécifié de résultats (entre 1 et 1 000) Terminé Label displayed when an operation completes or is done executing. Rechercher un package en utilisant une correspondance exacte Description message displayed to inform the user about finding an application package using an exact matching criteria. Argument expérimental à des fins de démonstration Cette commande illustre comment implémenter une fonctionnalité expérimentale. Pour l’activer, accédez à « winget settings » et activez les fonctionnalités experimentalCmd ou experimentalArg. {Locked="winget settings"} Exemple de fonctionnalité expérimentale Un argument positionnel a été trouvé lorsque aucune valeur n’était attendue : '{0}' {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. Cette fonctionnalité est un travail en cours et peut être modifiée considérablement ou supprimée à l’avenir. Pour l’activer, modifiez vos paramètres ('winget settings') pour inclure la fonctionnalité expérimentale : '{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Affiche le statut des fonctionnalités expérimentales. Les fonctionnalités expérimentales peuvent être activées via « winget settings ». {Locked="winget settings"} Affiche le statut des fonctionnalités expérimentales Désactivé Activé Fonctionnalité Lien Les fonctionnalités expérimentales suivantes sont en cours. Elles peuvent être configurées par le biais du fichier de paramètres « winget settings ». {Locked="winget settings"} Propriété Statut Fichier à hacher L’argument indicateur ne peut pas contenir de valeur contigüe : '{0}' {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Calcule le hachage d’un fichier local, approprié pour l’entrée dans un manifeste. Il peut également calculer le hachage du fichier de signature d’un paquet MSIX afin d’activer les installations en continu. Assistant pour le hachage des fichiers d’installation Affiche l’aide sur la commande sélectionnée Pour en savoir plus sur une commande spécifique, passez-la à l’argument aide. Vous trouverez de l’aide supplémentaire sur : '{0}' {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Filtrer les résultats par identifiant Supprime les sorties d'avertissement La licence d’utilisation de cette application vous est octroyée par son propriétaire. Microsoft n’est pas responsable des paquets tiers et n’accorde pas de licences à ceux-ci. Ce package est fourni via Microsoft Store. Le winget devra peut-être acquérir le package à partir du Microsoft Store pour le compte de l’utilisateur actuel. {Locked="winget"} Installe le package sélectionné, trouvé en recherchant une source configurée ou directement à partir d’un manifeste. Par défaut, la requête doit correspondre à l’id, name ou moniker du package sans respecter la casse. D’autres champs peuvent être utilisés en passant leur option appropriée. Par défaut, la commande install vérifie l’état d’installation du package et tente d’effectuer une mise à niveau, le cas échéant. Remplacez par --force pour effectuer une installation directe. {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Installe le package donné Le code de hachage de l’installation ne correspond pas ; ceci ne peut pas être contourné lors d’une exécution en tant qu’administrateur Le hachage du programme d’installation ne correspond pas ; continuer en raison de --ignore-security-hash {Locked="--ignore-security-hash"} Le hachage de l'installateur ne correspond pas ; pour remplacer cette vérification, utilisez --ignore-security-hash. {Locked="--ignore-security-hash"} Le code de hachage de l’installation a été vérifié avec succès Installé correctement Démarrage du package d’installation... Merci de patienter. Ignorer l’échec de la vérification de hachage du programme d’installation Ignorer l’analyse des programmes malveillants effectuée dans le cadre de l’installation d’un package de type archive à partir du manifeste local Demander une installation interactive; l’intervention de l’utilisateur peut être nécessaire L’alias d’argument n’a pas été reconnu pour la commande actuelle : '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Spécificateur d’argument non valide : '{0}' {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). Le nom de l’argument n’a pas été reconnu pour la commande actuelle : '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Répertoires Winget Header for a table detailing the directories Winget uses for key operations like logging and portable installs Paramètres régionaux à utiliser (format BCP47) {Locked="BCP47"} Contrat de licence Liens Links to different webpages La commande list affiche les packages installés sur le système, et indique si une mise à niveau est disponible. Des options supplémentaires peuvent être proposées pour filtrer la sortie, comme pour la commande search. {Locked="list","search"} Afficher les packages installés Emplacement d’installation (si pris en charge) Emplacement du journal (si pris en charge) Copyright (c) Microsoft Corporation. Tous droits réservés. Page d’accueil The primary webpage for the software Chemin d’accès au manifeste du package La validation du manifeste a échoué. La validation du manifeste a réussi. La validation des manifestes a réussi avec des avertissements. Valeur d’argument requise, mais aucune trouvé : '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Filtrer les résultats par nom Le fichier d’entrée sera traité en tant que MSIX; le hachage de la signature sera fourni s’il est signé Impossible de calculer le hachage de la signature MSIX. Échec de l’installation ou de la mise à niveau du package Microsoft Store en raison du blocage de l’application en question par une stratégie Échec de l’installation ou de la mise à niveau du package Microsoft Store. Code d’erreur : {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Échec de l’installation ou de la mise à niveau du package Microsoft Store en raison du blocage du client Microsoft Store par une stratégie Vérification/demande en cours relative à l’acquisition du package... Plusieurs packages installés correspondent à vos critères saisis. Veuillez affiner votre recherche. Plusieurs packages correspondent à vos critères. Veuillez affiner votre recherche. Filtrer les résultats par nom Aucun programme d’installation applicable n’a été trouvé. Pour plus d’informations, consultez les journaux. Aucune fonctionnalité expérimentale n’est actuellement disponible. Aucun package installé ne correspond aux critères saisis. Aucun package ne correspond aux critères sélectionnés. Désactive l’affichage VirtualTerminal {Locked="VirtualTerminal"} Ouvrir l’emplacement des journaux par défaut options Options to change how a command works Remplacer les arguments à transmettre au programme d’installation Package : {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. Désolé... nous avons oublié de le faire... Position du curseur dans la ligne de commande Déclaration de confidentialité Requête utilisée pour rechercher un package La progression affiche un arc-en-ciel de couleurs Argument obligatoire non fourni : '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Affichage de l’avancement comme couleur par défaut Recherches de packages à partir de sources configurées. Rechercher et afficher des informations de base sur les packages ID Abbreviation of Identifier. Correspondance Nom Source entrées supplémentaires tronquées en raison de la limite de résultats Version Les erreurs suivantes ont été détectées lors de la validation des paramètres : Ouvrez les paramètres dans l’éditeur de texte JSON par défaut. Si aucun éditeur n’est configuré, ouvre les paramètres dans le bloc-notes. Pour les paramètres disponibles, consultez https://aka.ms/winget-settings Cette commande peut également être utilisée pour définir les paramètres de l’administrateur en fournissant un argument '---enable' ou '---disable'. {Locked="--enable"} {Locked="--disable"} Ouvrir les paramètres ou définir les paramètres d’administrateur Erreur inattendue lors du chargement des paramètres. Vérifiez vos paramètres en exécutant la commande 'settings'. {Locked="'settings'"} Canal Affiche des informations sur un package spécifique. Par défaut, la requête doit correspondre de façon non sensible à la casse à l’ID, au nom ou au moniker du package. D’autres champs peuvent être utilisés en passant l’option appropriée. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Affiche des informations sur un package Version Demander une installation sans assistance Seul l’alias de caractère peut se produire après une seule – : '{0}' {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. Une source avec le nom spécifié existe déjà et fait référence à un autre emplacement : Une source portant un nom différent fait déjà référence à cet emplacement : Une source avec le nom spécifié existe déjà et fait référence au même emplacement : Ajout de la source : Ajoutez une nouvelle source. Une source fournit les données vous permettant de découvrir et d’installer des packages. Ajoutez une nouvelle source uniquement si elle est fiable en tant qu’emplacement sécurisé. Ajouter une nouvelle source Argument donné à la source Rechercher un package à l’aide de la source indiquée Gérer les sources à l’aide des sous-commandes. Une source fournit les données vous permettant de découvrir et d’installer des packages. Ajoutez une nouvelle source uniquement si elle est fiable en tant qu’emplacement sécurisé. Gérer les sources des packages Modifiez les propriétés d’une source existante. Une source fournit les données pour vous permettre de découvrir et d’installer des packages. Modifier les propriétés d’une source Source de modification : {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. La source nommée '{0}' est déjà dans l’état souhaité. {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Argument Value given to source. Répertorier toutes les sources actuelles ou tous les détails d’une source précise. Répertorier les sources actuelles Données Data stored by the source. Champ The name of a piece of information about a source. Nom The name of the source. Identificateur The source's unique identifier. Désolé... Nous n’avons pas pu trouver la source nommée : {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. Aucune source n’est configurée. Type The kind of source. Mis(e) à jour The last time the source was updated. jamais The source has never been updated. Valeur The value of information about a source. Nom de la source Échec lors de l’ouverture de la ou des sources ; essayez la commande « source reset» si le problème persiste. {Locked="source reset"} Échec de l’ouverture de la source prédéfinie ; veuillez le signaler aux chargés de maintenance winget. {Locked="winget"} Suppression en cours de toutes les sources... Merci de patienter. Supprimer une source précise. Supprimer les sources actuelles Suppression de la source : {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Réinitialisation en cours de toutes les sources... Merci de patienter. Cette commande supprime les sources existantes, en conservant éventuellement les données locales. Utilisée sans argument, elle supprime toutes les sources et ajoute les sources par défaut. Si une source nommée est fournie, seule cette source est supprimée. Réinitialiser les sources Force la réinitialisation des sources Les sources suivantes seront réinitialisées si l’option --force est proposée : {Locked="--force"} Réinitialisation de la source : {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Type de la source Mise à jour en cours de toutes les sources... Merci de patienter. Mettre à jour toutes les sources ou uniquement une source précise. Mettre à jour les sources actuelles Mise à jour de la source : {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Filtrer les résultats par mot-clé Merci d’utiliser winget Avis de tiers L’utilitaire de ligne de commande winget permet d’installer des applications et d’autres packages à partir de la ligne de commande. Afficher les informations générales de l’outil Afficher la version de l’outil Argument fourni plus de fois que le nombre autorisé : '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. Plusieurs arguments de comportement d’exécution fournis : '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). Une erreur inattendue s'est produite lors de l'exécution de la commande : Désinstaller la version précédente du package pendant la mise à niveau Commande non reconnue : '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Mettre à jour tous les paquets installés vers la dernière version si disponible Aucune mise à niveau applicable trouvée. Une version de package plus récente est disponible dans une source configurée, mais elle ne s’applique pas à votre système ou à la configuration requise. Mise à niveau disponible introuvable. Aucune version de package plus récente n’est disponible à partir des sources configurées. Mises à niveau du paquet sélectionné, trouvé en recherchant les paquets installés ou directement à partir d'un manifeste. Par défaut, la requête doit correspondre, sans respect de la casse, à l’identification, au nom ou au moniker du paquet. D’autres champs peuvent être utilisés en passant leur option appropriée. Si aucun argument n'est fourni, affiche les paquets avec des mises à jour disponibles. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Affiche et effectue les mises à niveau disponibles. Utilisation : {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Valide un manifeste à l’aide d’un ensemble de directives strict. Ceci est destiné à vous permettre de vérifier le manifeste avant de l’envoyer à un référentiel. Valide un fichier manifeste Chemin d’accès au manifeste à valider Active la journalisation détaillée pour WinGet Veuillez vérifier que le fichier d’entrée est un MSIX signé valide. Utiliser la version indiquée; la version par défaut est la dernière version Afficher les versions disponibles du package La valeur fournie avant l’achèvement est demandée Aucune version correspondante trouvée : {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. Aucune source ne correspond à la valeur spécifiée : {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. Les sources configurées sont : Aucune source définie ; ajoutez-en une avec « source add » ou rétablissez les valeurs par défaut de « source reset » {Locked="source add","source reset"} Trouvé Le chemin d’accès est un répertoire : {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. Le fichier n'existe pas : {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Les arguments manifeste et requête de recherche locaux sont tous deux fournis Journaux Label displayed for diagnostic files containing information about the application use. Le programme d’installation est bloqué par une stratégie Le programme d’installation a échoué vérification de sécurité Un produit antivirus signale une infection dans le programme d’installation Échec de la tentative de mise à jour de la source : {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Désinstalle le package sélectionné, que vous avez trouvé en effectuant une recherche dans la liste des packages installés ou directement à partir d’un manifeste. Par défaut, la requête doit correspondre de façon non sensible à la casse à l’ID, au nom ou au moniker du package. D’autres champs peuvent être utilisés en passant l’option appropriée. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Désinstallation du paquet donné Début de la désinstallation du paquet... Désinstallé avec succès winget ne peut pas trouver la commande de désinstallation pour ce package. Veuillez contacter l’éditeur du package pour obtenir de l’aide. {Locked="winget"} Désinstallation abandonnée Échec du programme de désinstallation avec le code de sortie : {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Exporte une liste des packages installés Installe tous les packages listés dans un fichier. Installe tous les packages dans un fichier Fichier dans lequel le résultat doit être écrit Fichier décrivant les packages à installer Exporter les packages à partir de la source spécifiée Écrit une liste des packages installés dans un fichier. Les packages peuvent ensuite être installés à l’aide de la commande import. {Locked="import"} L’installation d’un ou plusieurs packages importés a échoué La source requise pour l’importation n’est pas installée : {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. Le package installé n’est pas disponible à partir d’une source : {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. La version installée du package n’est pas disponible à partir d’aucune source : {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. Aucun package n’a été trouvé dans le fichier d’importation Le fichier JSON n'est pas valide Le package est déjà installé : {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. Ignorer les packages non disponibles Inclure les versions de paquets dans le fichier d’exportation Ignorer les versions de package du fichier d’importation Le chemin d’accès n’existe pas : {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. Le fichier JSON ne spécifie pas de schéma reconnu. Sélectionner l’étendue d’installation (user ou machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. La valeur fournie pour l’argument '{0}' n’est pas valide ; les valeurs valides sont : {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. Cette opération est désactivée par stratégie de groupe : {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Activer les sources supplémentaires du Programme d’installation de l’application Windows Activer les sources autorisées du Programme d’installation de l’application Windows Activer la source par défaut du Programme d’installation de l’application Windows Activer les fonctionnalités expérimentales du Programme d’installation de l’application Windows Activer la source Microsoft Store du Programme d’installation d’application Windows Activer la source de polices Windows App Installer Activer les paramètres du Gestionnaire de package Windows Activer le Gestionnaire de package Windows Activer les interfaces de ligne de commande Gestionnaire de package Windows Définir l’intervalle en minutes de la mise à jour automatique source du Gestionnaire de package Windows Stratégie de groupe Header for a table listing active Group Policies Activer les fichiers de manifeste locaux du Programme d'installation de l'application Windows Activer le contournement de certificat épinglé de la source Microsoft Store de l’Installeur d'applications Windows Activer le remplacement de l’analyse des programmes malveillants des archives locales Windows Programme d'installation d'application Champ : {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. Format de champ non valide. Valeur de champ non valide. Paramètre non valide dans la stratégie de groupe. Paramètres chargés à partir du fichier de sauvegarde. Erreur lors de l’analyse du message : Valeur : {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. Les fonctionnalités expérimentales suivantes sont en cours. La configuration est désactivée en raison d’une stratégie de groupe. Le code de hachage du programme d’installation ne correspond pas. Activer le remplacement de code de hachage du Programme d’installation de l’application Windows Exportez les sources actuelles en tant que JSON pour la stratégie de groupe. Exporter les sources actuelles Source supplémentaire An additional source required by policy. Source autorisée A source that the user is allowed to add. La valeur fournie pour l’argument '{0}' n’est pas valide {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Annulé Externe Les packages trouvés dans cette importation ont les dépendances suivantes : Import command sentence showed before reporting dependencies Ce package nécessite les dépendances suivantes : Message shown before reporting dependencies Installation des dépendances Source de dépendance introuvable La recherche de package produit plusieurs résultats : {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. Dernière version introuvable pour le package : {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. Aucun programme d’installation trouvé : {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Version minimale requise non disponible pour le package : {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. Aucun résultat trouvé When package search yields no matches A une boucle Dependency graph has loop Aucun programme d’installation approprié n’a été trouvé pour le manifeste : {0} version {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Erreur lors du traitement des dépendances de package. Voulez-vous continuer l’installation ? Prompt message shown when dependencies processing yields errors. Erreur lors du traitement des dépendances de package. Sortie... Packages Ce package comportait des dépendances qui n’étaient peut-être plus nécessaires : Uninstall command sentence showed before reporting dependencies Le manifeste a les dépendances suivantes qui n’ont pas été validées ; vérifiez qu’ils sont valides : Validate command sentence showed before reporting dependencies Fonctionnalités de Windows Bibliothèques Windows Rechercher les dépendances de package à l’aide de la source spécifiée For getting package type dependencies when installing from a local manifest Conditions générales du Windows Store Accepter tous les contrats de licence pour les packages L’installation du package exporté nécessite un contrat de licence : {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. L’éditeur exige que vous consultiez les informations ci-dessus et acceptiez les contrats avant de procéder à l’installation. Acceptez-vous les conditions ? Les contrats de package n’ont pas été acceptés. Opération annulée. Contrats : Auteur : Description : Programme d'installation : Paramètres régionaux du programme d’installation : ID de produit du Store : SHA256 du programme d’installation : Type du programme d’installation : URL du programme d’installation : Licence : URL de la licence : Moniker : Page d’accueil : Publisher : Mots-clés : Version : Dépendances : Fonctionnalités de Windows : Bibliothèques Windows : Dépendances de package : Dépendances externes : Non Oui Échec de l’ouverture de la source ajoutée. Accepter tous les contrats sources pendant les opérations sources La source '{0}' nécessite que vous consultiez les contrats suivants avant de l’utiliser. {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. Acceptez-vous toutes les conditions des contrats sources ? Un ou plusieurs contrats sources n’ont pas été acceptés. Opération annulée. Acceptez les contrats sources ou supprimez les sources correspondantes. La source nécessite que la région géographique à 2 lettres de l’ordinateur actuel soit envoyée au service principal pour fonctionner correctement (par exemple, « ÉTATS-UNIS »). Installation réussie. Redémarrez l’application pour terminer la mise à niveau. Optional Windows-Package-Manager REST source HTTP header Ignorer l'en-tête facultatif car il n'est pas applicable pour cette source. L’en-tête facultatif n’est pas applicable sans spécifier de source : '{0}' {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Date de version : Distribution hors ligne prise en charge : ID de l’Editeur URL d’achat : URL de support de l’éditeur : URL de confidentialité : Copyright : URL de copyright : Notes de publication : URL des notes de publication : Échec lors de la recherche de la source ; les résultats ne seront pas inclus : {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Échec lors de la recherche de la source : {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Cette fonctionnalité doit être activée par les administrateurs. Pour l’activer, exécutez « winget settings --enable {0} » en tant qu’administrateur. {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Active le paramètre d’administrateur spécifique Désactive le paramètre d’administrateur spécifique. Paramètre d’administration « {0} » activé. {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. Paramètre d’administration « {0} » désactivé. {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. Paramètre administrateur Header for a table displaying admin settings. L’application est en cours d’exécution. Quittez l’application, puis réessayez. Les fichiers modifiés par le programme d’installation sont actuellement utilisés par une autre application. Quittez les applications, puis réessayez. Une autre installation est déjà en cours. Veuillez réessayez. Un ou plusieurs fichiers sont en cours d’utilisation. Quittez l’application, puis réessayez. Ce package a une dépendance manquante dans votre système. Votre PC n’a plus d’espace disponible. Libérez de l’espace, puis réessayez. Mémoire disponible insuffisante pour l’installation. Fermez les autres applications, puis réessayez. L’un des paramètres d’installation n’est pas valide. Le journal d’installation du package peut contenir des détails supplémentaires. Cette application nécessite une connectivité Internet. Connectez-vous à un réseau, puis réessayez. Cette application a rencontré une erreur lors de l’installation. Contactez le support. Redémarrez votre PC pour terminer l’installation. Votre PC redémarrera pour terminer l’installation. Échec de l’installation. Redémarrez votre PC, puis réessayez. L’installation a été annulée par l’utilisateur. Une autre version de cette application est déjà installée. Une version supérieure de cette application est déjà installée. Les stratégies d’organisation empêchent l’installation. Contactez votre administrateur. La configuration système actuelle ne prend pas en charge l’installation de ce package. Échec de l’installation avec une erreur du programme d’installation personnalisé. Contactez le support du package. L’installation a été abandonnée Échec du programme d’installation avec le code de sortie : {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. Le journal du programme d’installation est disponible à l’adresse suivante : {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. Les packages suivants ont été trouvés parmi les sources de travail. Spécifiez l’un d’entre eux à l’aide de l’option --source pour continuer. {Locked="--source"} "working sources" as in "sources that are working correctly" Aucun package n’a été trouvé parmi les sources de travail. "working sources" as in "sources that are working correctly" Il s’agit d’une version stable du Gestionnaire de package Windows. Si vous voulez essayer des fonctionnalités expérimentales, installez une version préliminaire. Des instructions sont disponibles sur GitHub : https://github.com/microsoft/winget-cli. {Locked="https://github.com/microsoft/winget-cli"} Gestionnaire de package Windows v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. Ignorer les versions de paquets dans le fichier d’importation Le nombre de résultats demandé doit être compris entre 1 et 1 000. Arguments à passer au programme d’installation en plus des valeurs par défaut Une version plus récente a été trouvée, mais la technologie d’installation est différente de la version actuelle installée. Désinstallez le package et installez la version la plus récente. La technologie d’installation de la version la plus récente spécifiée est différente de la version actuelle installée. Désinstallez le package et installez la version la plus récente. Gestionnaire de package Windows (Préversion) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Sélectionner l’architecture Mettre à niveau les packages même si leur version actuelle ne peut pas être déterminée Répertorie les packages même si leur version actuelle ne peut pas être déterminée. Ne peut être utilisé qu’avec l’argument --upgrade-available {Locked="--upgrade-available"} Impossible de déterminer le numéro de version de ce package. Pour le mettre à niveau quand même, ajoutez l’argument --include-unknown à votre commande précédente. {Locked="--include-unknown"} {0} package(s) a(ont) des numéros de version qui ne peuvent pas être déterminés. Utilisez --include-unknown pour afficher tous les résultats. {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} package(s) sont épinglé(s) et doit(doivent) être explicitement mis à niveau. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. Les arguments fournis ne peuvent être utilisés qu’avec une requête. Architecture du système : {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). Conserve tous les fichiers et répertoires créés par le package (portables) Supprimer tous les fichiers et répertoires du répertoire du package (portable) Appuyez sur Entrée pour continuer . . . Valeur pour renommer le fichier exécutable (portable) Invite l’utilisateur à appuyer sur une touche avant de quitter Le programme d’installation demande à s’exécuter en tant qu’administrateur. Attendez-vous à une invite. Le programme d’installation ne peut pas être exécuté à partir d’un contexte d’administrateur. Variable d’environnement de chemin modifiée ; redémarrez votre interpréteur de commandes pour utiliser la nouvelle valeur. Filtres à l’aide du code de produit Un package portable portant le même nom mais provenant d’une source différente existe déjà ; poursuite en raison de --force {Locked="--force"} Le volume ne prend pas en charge les points d’analyse Le nom de fichier spécifié n’est pas un nom de fichier valide Remplacement du fichier existant : {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. Aucun argument de sélection de package n’a été fourni ; consultez l’aide pour plus d’informations sur la recherche d’un package. Alias de ligne de commande ajouté : Répertoire des liens portables (ordinateur) Répertoire des liens portables (utilisateur) Racine de package portable (utilisateur) Racine du package portable Racine de package portable (x86) Échec de l’installation portable ; Nettoyage en cours... Le package Portable a été modifié ; continuer en raison de --force {Locked="--force"} Impossible de supprimer le package Portable, car il a été modifié ; pour remplacer cette vérification, utilisez : --force {Locked="--force"} Les fichiers restent dans le répertoire d’installation : {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Purge du répertoire d’installation... Impossible de vider le répertoire d’installation, car il n’a pas été créé par WinGet Lien associé Documentation : Remarques : {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Notes d’installation : Un argument fourni n’est pas pris en charge pour ce package Échec de l’extraction du contenu de l’archive Le fichier d’installation imbriqué n’existe pas. Vérifiez que le chemin d’accès relatif spécifié du programme d’installation imbriqué correspond à : {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Chemin de fichier relatif non valide pour le programme d’installation imbriqué ; le chemin pointe vers un emplacement en dehors du répertoire d’installation Aucun programme d’installation imbriqué spécifié pour ce package Un seul programme d’installation imbriqué peut être spécifié pour un programme d’installation d’archive, sauf s’il s’agit d’un programme d’installation imbriqué portable ou de police Arguments de ligne de commande fournis incompatibles Répertorie uniquement les packages pour lesquels une mise à niveau est disponible Les packages suivants ont une mise à niveau disponible, mais nécessitent un ciblage explicite pour la mise à niveau : "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly Téléchargement en cours Label displayed while downloading an application installer. Le type de programme d’installation imbriqué n’est pas pris en charge Ce programme d’installation est connu pour redémarrer le terminal ou l’interpréteur de commandes Ce package nécessite un emplacement d’installation Les programmes d’installation suivants sont connus pour redémarrer le terminal ou l’interpréteur de commandes : Les programmes d’installation suivants nécessitent un emplacement d’installation : Spécifier la racine d’installation : Voulez-vous continuer ? Contrats pour This will be followed by a package name, and then a list of license agreements L’emplacement d’installation est requis par le package, mais il n’a pas été fourni Désactiver les invites interactives Description for a command line argument, shown next to it in the help Un package existant a déjà été installé. Tentative de mise à niveau du package installé... Exécuter directement la commande et continuer avec des problèmes non liés à la sécurité Description for a command line argument, shown next to it in the help Un package portable d’une autre source existe déjà Extraction de l’archive réussie Extraction des archives... L’analyse de l’archive a détecté un programme malveillant. Pour remplacer cette vérification, utilisez ---ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} L’analyse de l’archive a détecté un programme malveillant. Continuer en raison de ---ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} Ignore la mise à niveau si une version installée existe déjà Une version de package est déjà installée. Installation annulée. Impossible d’activer {0}. Ce paramètre est contrôlé par la stratégie. Pour plus d’informations, contactez votre administrateur système. {Locked="{0}"} The value will be replaced with the feature name Impossible de désactiver {0}. Ce paramètre est contrôlé par la stratégie. Pour plus d’informations, contactez votre administrateur système. {Locked="{0}"} The value will be replaced with the feature name Ajoutez une nouvelle épingle. Une épingle peut limiter la mise à niveau par le Gestionnaire de package Windows d’un package vers des plages de versions spécifiques, ou l’empêcher de mettre à niveau le package. Un package épinglé peut toujours être mis à niveau seul et être mis à niveau depuis l’extérieur du Gestionnaire de package Windows. Par défaut, un package épinglé peut être mis à niveau en le mentionnant explicitement dans la commande « upgrade » ou en ajoutant l’indicateur « include-pinned » à« winget upgrade all ». {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} Ajouter une nouvelle broche Gérez les épingles de package avec les sous-commandes. Une épingle peut limiter la mise à niveau par le Gestionnaire de package Windows d’un package vers des plages de versions spécifiques, ou l’empêcher de mettre à niveau le package. Un package épinglé peut toujours être mis à niveau seul et être mis à niveau depuis l’extérieur du Gestionnaire de package Windows. Gérer les broches de package Répertoriez toutes les broches actuelles ou les détails complets d’un code confidentiel spécifique. Répertorier les épingles actuelles Supprimez un code confidentiel de package spécifique. Supprimer une broche de package Réinitialisez toutes les broches existantes. Réinitialiser les codes confidentiels ? Version à laquelle épingler le package. Le caractère générique « * » peut être utilisé comme dernière partie de version Bloquer la mise à niveau jusqu’à ce que l’épingle soit supprimée, empêchant ainsi les arguments de remplacement Épingler une version installée spécifique Installé Value used in a table to indicate that a package comes from the list of packages installed in the machine Exporter les paramètres au format JSON Exporter les paramètres Paramètres utilisateurs Label displayed for the file containing the user settings. Impossible de charger le fichier de paramètres. Utilisation des valeurs par défaut. Sélectionner le filtre d’étendue de package installé (user ou machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. Épingle ajoutée avec succès Il existe déjà une épingle pour le package {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. Une épingle existe déjà pour le package {0}. Remplacement en raison de l’argument --force. {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Un code confidentiel existe déjà pour le package {0}. Utilisez l’argument --force pour le remplacer. {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Réinitialisation de toutes les épingles actives. Utilisez l’argument --force pour réinitialiser toutes les épingles. Les épingles suivantes seront supprimées : {Locked="--force"} Type d’épingle Il n’existe pas d’épingle pour le package {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. Aucune épingle n’est configurée. Shown when listing or modifying existing pins if there are none. Réinitialisé correcte des épingles Shown after resetting (deleting) all the pins Impossible d’ouvrir la base de données épinglée. Error message for when we cannot open the database containing package pins. Un argument fourni ne peut être utilisé que pour un seul package Plusieurs arguments mutuellement exclusifs ont été fournis : {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together L’argument {0} ne peut être utilisé qu’avec {1} {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" Désactivé As in enabled/disabled Activé As in enabled/disabled État Header for a table listing the state (enabled/disabled) of Group Policies and Settings Requête utilisée pour rechercher un package Le package « {0} » est introuvable {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. Plusieurs packages trouvés pour : {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. Échec de la recherche pour : {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} package(s) ont une épingle qui doit être supprimée avant la mise à niveau {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade Impossible de mettre à niveau le package à l’aide de Winget. Utilisez la méthode fournie par l’éditeur pour mettre à niveau ce package. Mettre à niveau les packages même s’ils ont une épingle non bloquante Répertorie les packages même s’ils ont un code confidentiel qui empêche la mise à niveau. Ne peut être utilisé qu’avec l’argument --upgrade-available {Locked="--upgrade-available"} L’épingle a été supprimée avec succès Une version plus récente a été trouvée, mais le package a une épingle qui empêche sa mise à niveau. Le package est épinglé et ne peut pas être mis à niveau. Utilisez la commande « winget pin » pour afficher et modifier des épingles. Certains types d’épingle peuvent être ignorés avec l’argument --include-pinned. {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} package(s) ont des épingles qui empêchent la mise à niveau. Utilisez la commande « winget pin » pour afficher et modifier les épingles. L’utilisation de l’argument --include-pinned peut afficher plus de résultats. {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages Garantit que le système correspond à l’état souhaité comme décrit par la configuration fournie. Peut télécharger/exécuter des processeurs pour atteindre l’état souhaité. La configuration et les processeurs doivent être vérifiés pour s’assurer qu’ils sont dignes de confiance avant de les appliquer. Configure le système dans un état souhaité Affiche les détails de la configuration fournie. Par défaut, le système ne sera pas modifié, mais certaines options entraîneront le téléchargement et/ou le chargement des fichiers. Affiche les détails d’une configuration Vérifie que le système correspond à l’état souhaité comme décrit par la configuration fournie. Peut télécharger/exécuter des processeurs pour tester l’état souhaité. La configuration et les processeurs doivent être vérifiés pour s’assurer qu’ils sont dignes de confiance avant de les exécuter. Vérifie l’état souhaité du système Valide l’exactitude d’un fichier de configuration. Valide un fichier de configuration Le type du champ '{0}' dans le fichier de configuration est incorrect. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. Chemin d’accès au fichier de configuration Le fichier de configuration n’est pas valide. La version du fichier de configuration {0} est inconnue. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. Accepte l’avertissement de configuration, empêchant une invite interactive Appliquer Indicates that this item is used to write state Affirmer Indicates that this item is used to check/assert the state rather than write to it Dépendances :{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. Une partie de la configuration n’a pas été appliquée correctement. Échec de l’obtention d’informations détaillées sur la configuration. informer Indicates that this item is used to retrieve values for future use rather than writing them Local Used to indicate that the item is present on the device. Module : {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. Module : {0} par {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. Paramètres : Label for the values that are used as inputs for this item when applying state La configuration a été correctement appliquée. L'unité a bien été appliquée. Une autre configuration est appliquée au système. Cette configuration se poursuivra dès que possible... Vous êtes responsable de la compréhension des paramètres de configuration que vous choisissez d'exécuter. Microsoft n'est pas responsable du fichier de configuration que vous avez créé ou importé. Cette configuration peut modifier les paramètres de Windows, installer des logiciels, modifier les paramètres des logiciels (y compris les paramètres de sécurité) et accepter les accords d'utilisation des packages et services tiers en votre nom. En exécutant ce fichier de configuration, vous reconnaissez que vous comprenez et acceptez ces ressources et paramètres. Toutes les applications installées vous sont concédées sous licence par leurs propriétaires. Microsoft n'est pas responsable des packages ou services tiers et n'accorde aucune licence à ceux-ci. Legal approved. Do not change without approval. Avez-vous examiné la configuration et souhaitez-vous continuer à l’appliquer au système ? PM approved. La configuration est vide. La fonctionnalité [{0}] est introuvable. {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. Échec de l’activation des dépendances de fonctionnalité Windows. Pour poursuivre l’installation, utilisez '--force'. {Locked="--force"} Redémarrage requis pour activer entièrement la ou les fonctionnalités Windows; pour remplacer cette vérification, utilisez « --force ». "Windows Feature(s)" is the name of the options Windows features setting. Échec de l’activation des dépendances de fonctionnalité Windows ; continuer en raison de --force "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} Activation [{0}]... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. Échec de l’activation [{0}] de la fonctionnalité : {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. Redémarrage requis pour activer entièrement la ou les fonctionnalités Windows; continuer en raison de --force "Windows Feature(s)" is the name of the options Windows features setting. En attente de la fin d’une autre installation/désinstallation... Version épinglée Table header for the version to which a package is pinned; meaning it should not update from that version. <Consultez le fichier journal pour obtenir des détails supplémentaires> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. Récupération des détails de la configuration Initialisation du système de configuration Lecture du fichier de configuration Le système n’est pas dans l’état souhaité déclaré par la configuration. Cette unité de configuration a échoué pour une raison inconnue : {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Échec de l’unité de configuration en raison de la configuration : {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Échec de l’unité de configuration lors de la tentative d’obtention de l’état actuel du système. Échec de l’unité de configuration lors de la tentative d’application de l’état souhaité. Échec de l’unité de configuration lors de la tentative de test de l’état actuel du système. Échec de l’unité de configuration en raison d’une erreur interne : {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. L’unité de configuration a échoué car une condition préalable n’est pas valide : {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Échec de l’unité de configuration en raison de l’état du système : {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Échec de l’unité de configuration lors de la tentative d’exécution : {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. La configuration contient l’identificateur '{0}' plusieurs fois. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. La dépendance «{0}» est introuvable dans la configuration. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. Cette unité de configuration a été ignorée manuellement. Le module de l’unité de configuration est disponible dans plusieurs emplacements avec la même version. Échec du chargement du module pour l’unité de configuration. Plusieurs correspondances ont été trouvées pour l’unité de configuration ; spécifiez le module pour sélectionner le module approprié. L’unité de configuration est introuvable. L’unité de configuration n’était pas dans le module comme prévu. Cette unité de configuration n’a pas été exécutée car une dépendance a échoué ou n’a pas été exécutée. Cette unité de configuration n’a pas été exécutée car une assertion a échoué ou a la valeur false. L’unité de configuration a retourné un résultat inattendu lors de l’exécution. Cette unité de configuration n’a pas été exécutée pour une raison inconnue : {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. La valeur du champ '{0}' n’est pas valide : {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. Le champ « {0} » est manquant ou vide. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. Voir {0} de ligne, {1} de colonne dans le fichier. {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. Annulation de l’opération Installer le package stub pour AppInstaller Installer le package complet pour AppInstaller Activer les fonctionnalités étendues. Nécessite l’accès au store. L’option '--enable' et '--disable' ne peuvent pas être utilisées avec d’autres arguments. {Locked="--enable", "--disable"} Activation des fonctionnalités étendues. Nécessite l’accès au store. Les fonctionnalités étendues ne sont pas activées. Exécutez 'winget configure --enable' pour les activer. {Locked="winget configure --enable"} Les fonctionnalités étendues sont activées. Désactiver les fonctionnalités étendues. Nécessite l’accès au store. Désactivation des fonctionnalités étendues. Nécessite l’accès au store. Les fonctionnalités étendues sont désactivées. Ignore le traitement des dépendances de package et des fonctionnalités Windows Dépendances ignorées. Échec de l’actualisation de la variable PATH pour le processus. Les installations suivantes qui dépendent des modifications apportées à la variable PATH risquent d’échouer. {Locked="PATH"} Certaines unités de configuration ont échoué lors du test de leur état. Le système est dans l’état de configuration décrit. L’état de configuration n’a pas été testé. Le système n’est pas dans l’état de configuration décrit. Résultat de test inattendu : {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). Avez-vous examiné la configuration et souhaitez-vous continuer à la vérifier sur le système ? Le fichier de configuration n’est pas un fichier YAML valide. {Locked="YAML"} YAML is a file format name. Cette unité de configuration fait partie d’un cycle de dépendance. La validation n’a détecté aucun problème. L’unité de configuration n’est disponible qu’en version préliminaire, mais elle n’est pas marquée de cette façon dans la configuration. Ajoutez 'allowPrerelease: true' au 'directives'. {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. L’unité de configuration a été trouvée localement, mais elle est introuvable dans un catalogue configuré. Vérifiez qu’il est présent sur n’importe quel système avant d’appliquer la configuration. L’unité de configuration n’est pas disponible publiquement ; vérifiez que toute personne qui utilisera cette configuration y aura accès. Le module n’a pas été fourni. La spécification du module améliore les performances et empêche les collisions de noms futures. Télécharge le programme d’installation à partir du package sélectionné, trouvé en recherchant une source configurée ou directement à partir d’un manifeste. Par défaut, la requête doit correspondre, sans respect de la casse, à l’ID, au nom ou au moniker du package. D’autres champs peuvent être utilisés en passant leur option appropriée. Par défaut, la commande download télécharge le programme d’installation approprié dans le dossier Téléchargements de l’utilisateur. Télécharge le programme d’installation à partir d’un package donné Répertoire dans lequel les installateurs sont téléchargés Téléchargement des dépendances : Programme d’installation téléchargé : {0} {Locked="{0}"} Full path of the downloaded installer. Sélectionner le type de programme d’installation Téléchargements du programme d’installation Le programme d’installation ne peut pas être téléchargé pour une installation hors connexion ultérieure. « --module-path allusers » nécessite des privilèges d’administrateur pour s’exécuter. {Locked="--module-path allusers"} Spécifie l’emplacement de stockage des modules sur l’ordinateur local. %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules par défaut {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} La valeur '--module-path' doit être 'currentuser', 'allusers', 'default' ou un chemin absolu. {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Activer la configuration Gestionnaire de package Windows Récupérez des informations sur les erreurs. En fonction d’un nombre, la sortie contiendra des détails sur l’erreur, y compris le nom du symbole s’il s’agit d’une erreur spécifique à winget. En fonction d’une chaîne, les erreurs spécifiques à winget sont recherchées pour cette valeur. Obtenir des informations sur les erreurs Valeur à rechercher dans les informations sur l’erreur Le nombre donné est trop grand pour être un HRESULT. Code d’erreur inconnu Erreur interne Arguments de ligne de commande non valides Échec de l’exécution de la commande Échec de l’ouverture du manifeste Signal d’annulation reçu Échec de l’exécution de ShellExecute Impossible de traiter le manifeste. La version du manifeste est supérieure à la version prise en charge. Mettez à jour le client. Échec du téléchargement du programme d’installation Impossible d’écrire dans l’index ; il s’agit d’une version de schéma supérieure L’index est endommagé Les informations sources configurées sont endommagées Le nom de la source est déjà configuré Le type de source n’est pas valide Le fichier MSIX est un pack, et non un package Il manque des données requises par la source Aucun des installateurs n’est applicable pour le système actuel Le hachage du fichier du programme d’installation ne correspond pas au manifeste Le nom de la source n’existe pas L’emplacement source est déjà configuré sous un autre nom Aucun package n’a été trouvé Aucune source n’est configurée Plusieurs packages trouvés correspondant aux critères Aucun manifeste trouvé correspondant aux critères Échec de l’obtention du dossier public à partir du package source La commande requiert des privilèges d’administrateur pour s’exécuter L’emplacement source n’est pas sécurisé Le client Microsoft Store est bloqué par la stratégie L’application Microsoft Store est bloquée par la stratégie La fonctionnalité est en cours de développement. Il peut être activé à l’aide des paramètres winget. Échec de l’installation de l’application Microsoft Store Échec de l’exécution de la saisie semi-automatique Échec de l’initialisation de l’analyseur YAML Une clé YAML non valide a été rencontrée Une clé YAML dupliquée a été rencontrée Opération YAML non valide Échec de la génération du document YAML État de l’émetteur YAML non valide Données YAML non valides Erreur LibYAML La validation des manifestes a réussi avec des avertissements La validation du manifeste a échoué Le manifeste n’est pas valide Aucune mise à jour applicable trouvée mises à niveau WinGet – toutes terminées sur des échecs Désolé, Nous n’avons pas pu effectuer la vérification de sécurité du programme d’installation La taille du téléchargement ne correspond pas à la longueur de contenu attendue Commande de désinstallation introuvable Échec de l’exécution de la commande de désinstallation Erreur d’itérateur d’arrêt ICU Erreur ICU casemap Erreur d’expression régulière ICU Échec de l’installation d’un ou de plusieurs packages importés Impossible de trouver un ou plusieurs packages demandés Le fichier JSON n’est pas valide L’emplacement source n’est pas distant La source rest configurée n’est pas prise en charge Données non valides retournées par la source rest L’opération est bloquée par la stratégie de groupe Erreur interne de l'API Rest URL de source rest non valide Type MIME non supporté renvoyé par l'API restante Version de contrat source rest non valide Les données sources sont endommagées ou falsifiées Erreur de lecture à partir du flux Les contrats du package n’ont pas été acceptés Erreur lors de la lecture de l’entrée dans l’invite La demande de recherche n’est pas prise en charge par une ou plusieurs sources Le point de terminaison de l'API de repos est introuvable. Échec de l’ouverture de la source. Les contrats sources n’ont pas été acceptés La taille de l’en-tête dépasse la limite autorisée de 1 024 caractères. Réduisez la taille et réessayez. Fichier de ressources manquant Échec de l’exécution de l’installation de MSI Les arguments pour msiexec ne sont pas valides Échec de l’ouverture d’une ou de plusieurs sources Échec de la validation des dépendances Un ou plusieurs packages sont manquants Colonne de table non valide La version de mise à niveau n’est pas plus récente que la version installée La version de mise à niveau est inconnue et le remplacement n’est pas spécifié Erreur de conversion de l’ICU Échec de l’installation du package portable Le volume ne prend pas en charge les points d’analyse Un package portable d’une autre source existe déjà. Impossible de créer le lien symbolique. Le chemin pointe vers un répertoire. Le programme d’installation ne peut pas être exécuté à partir d’un contexte d’administrateur. Échec de la désinstallation du package portable Échec de la validation des valeurs DisplayVersion par rapport à l’index. Un ou plusieurs arguments ne sont pas pris en charge. Les caractères Null incorporés ne sont pas autorisés pour SQLite Impossible de trouver le programme d’installation imbriqué dans l’archive. Échec de l’extraction de l’archive. Chemin de fichier relatif non valide fourni pour le programme d’installation imbriqué. Le certificat de serveur ne correspond à aucune des valeurs attendues. L’emplacement d’installation doit être fourni. Échec de l’analyse des programmes malveillants d’archivage. Au moins une version du package installée a été trouvée. Un code PIN existe déjà pour le package. Il n’y a pas de code PIN pour le package. Impossible d’ouvrir la base de données des codes PIN. Une ou plusieurs applications n’ont pas pu être installées Une ou plusieurs applications n’ont pas pu être désinstallés Une ou plusieurs requêtes n’ont pas retourné exactement une correspondance Le package a une broche qui empêche la mise à niveau. Le package actuellement installé est le package stub Signal d’arrêt de l’application reçu Échec du téléchargement des dépendances de package. Échec du téléchargement du package. Le téléchargement pour une installation hors connexion est interdit. Un service requis est occupé ou indisponible. Réessayez plus tard. Le GUID fourni ne correspond pas à un état de reprise valide. La version actuelle du client ne correspondait pas à la version du client de l’état enregistré. Les données d’état de reprise ne sont pas valides. Impossible d’ouvrir la base de données de point de contrôle. Limite de reprise maximale dépassée. Informations d’authentification non valides. Méthode d’authentification non prise en charge. Échec de l'authentification. Échec de l’authentification. Authentification interactive requise. Échec de l’authentification. Annulé par l’utilisateur. Échec de l’authentification. Le compte authentifié n’est pas le compte souhaité. L’application est en cours d’exécution. Quittez l’application, puis réessayez. Une autre installation est déjà en cours. Veuillez réessayez. Un ou plusieurs fichiers sont en cours d’utilisation. Quittez l’application, puis réessayez. Ce package a une dépendance manquante dans votre système. Votre PC n’a plus d’espace disponible. Libérez de l’espace, puis réessayez. Mémoire disponible insuffisante pour l’installation. Fermez les autres applications, puis réessayez. Cette application nécessite une connectivité Internet. Connectez-vous à un réseau, puis réessayez. Cette application a rencontré une erreur lors de l’installation. Contactez le support. Redémarrez votre PC pour terminer l’installation. Échec de l’installation. Redémarrez votre PC, puis réessayez. Votre PC redémarrera pour terminer l’installation. L’installation a été annulée par l’utilisateur. Une autre version de cette application est déjà installée. Une version supérieure de cette application est déjà installée. Les stratégies d’organisation empêchent l’installation. Contactez votre administrateur. Échec de l'installation des dépendances de package. L’application est actuellement utilisée par une autre application. Paramètre non valide. Package non pris en charge par le système. Le programme d’installation ne prend pas en charge la mise à niveau d’un package existant. L'installation a échoué en raison d'une erreur du programme d'installation personnalisé. L’entrée Applications et fonctionnalités du package est introuvable. L’emplacement de l’installation n’est pas applicable. L’emplacement de l’installation est introuvable. Le hachage du fichier existant ne correspond pas. Fichier introuvable. Nous avons trouvé le fichier, mais n’avons pas vérifié le hachage. Désolé... Nous n’avons pas pu accéder au fichier. Le fichier de configuration n’est pas valide. La syntaxe YAML n’est pas valide. Un champ de configuration a un type non valide. La configuration présente une version inconnue. Une erreur s’est produite lors de l’application de la configuration. La configuration contient un identificateur en double. Il manque une dépendance à la configuration. La configuration a une dépendance non satisfaite. Échec d’une assertion pour l’unité de configuration. La configuration a été ignorée manuellement. L’utilisateur a refusé de poursuivre l’exécution. Le graphe des dépendances contient un cycle qui ne peut pas être résolu. La configuration a une valeur de champ non valide. Un champ manque à la configuration. Certaines unités de configuration ont échoué lors du test de leur état. L’état de configuration n’a pas été testé. Désolé... Nous n’avons pas pu installer l’unité de configuration. L’unité de configuration est introuvable. Plusieurs correspondances ont été trouvées pour l’unité de configuration ; spécifiez le module pour sélectionner le module approprié. Échec de l’unité de configuration lors de la tentative d’obtention de l’état actuel du système. Échec de l’unité de configuration lors de la tentative de test de l’état actuel du système. Échec de l’unité de configuration lors de la tentative d’application de l’état souhaité. Le module de l’unité de configuration est disponible dans plusieurs emplacements avec la même version. Échec du chargement du module pour l’unité de configuration. L’unité de configuration a retourné un résultat inattendu lors de l’exécution. Une unité contient un paramètre qui nécessite la racine de configuration. L’opération n’est pas prise en charge par le processeur de configuration. Non disponible Dépendances des fonctionnalités Windows activées Échec du chargement du module pour l’unité de configuration, car il nécessite des privilèges d’administrateur pour s’exécuter. Une unité contient un paramètre qui nécessite la racine de configuration. Échec du chargement du module pour l’unité de configuration, car il nécessite des privilèges d’administrateur pour s’exécuter. Reprend l’exécution d’une commande précédemment enregistrée en passant l’identificateur unique de la commande enregistrée. Permet de reprendre une commande exécutée qui a peut-être été arrêtée en raison d’un redémarrage. Reprend l’exécution d’une commande précédemment enregistrée. Identificateur unique de l’état enregistré à reprendre La reprise de l’état à partir d’une version de client différente n’est pas prise en charge : {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. L’état de reprise n’existe pas : {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. Données introuvables dans l’état de reprise. Cette commande ne prend pas en charge la reprise. Autorise un redémarrage le cas échéant Lancement du redémarrage pour terminer l’opération... Échec du lancement d’un redémarrage. L’opération de reprise dépasse la limite autorisée de {0} reprise(s). Pour reprendre manuellement, exécutez la commande '{1}'. {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. Ignorer la limite de reprise d’un état enregistré Schéma d’URI non pris en charge : {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. URI mal formé : {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. Échec de l’analyse {0} contenu des paramètres ou du contenu des paramètres de l’unité de configuration est vide. {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. l’argument requis est manquant dans l’unité de configuration {0} : {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. {0}'unité de configuration n’a pas d’argument recommandé : {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. L’unité de configuration WinGetSource est en conflit avec une source connue : {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. Assertions de l’unité de configuration WinGetSource sur une source tierce : {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage déclare UseLatest et Version. ID du package : {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} L’unité de configuration WinGetPackage déclare sur un package à partir d’une source tierce. ID du package : {0} ; Source : {1} {Locked="WinGetPackage,{0},{1}"} Le package d’unité de configuration WinGetPackage dépend d’une source tierce qui n’a pas été configurée précédemment. ID du package : {0} ; Source : {1} {Locked="WinGetPackage,{0},{1}"} Le package d’unité de configuration WinGetPackage dépend d’une source tierce. Il est recommandé de déclarer la dépendance dans la section uni dependsOn. ID du package : {0} ; Source : {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} Impossible de valider le package d’unité de configuration WinGetPackage. Échec de l’ouverture de la source. ID du package : {0} ; Source : {1} {Locked="WinGetPackage,{0},{1}"} Impossible de valider le package d’unité de configuration WinGetPackage. Package introuvable. ID du package : {0} {Locked="WinGetPackage,{0}"} Impossible de valider le package d’unité de configuration WinGetPackage. Plusieurs packages trouvés. ID du package : {0} {Locked="WinGetPackage,{0}"} Impossible de valider le package d’unité de configuration WinGetPackage. Version de package introuvable. ID du package : {0} ; {1} de version {Locked="WinGetPackage,{0},{1}"} Package d’unité de configuration WinGetPackage spécifié avec une version spécifique alors qu’une seule version du package est disponible. ID du package :{0} ; version : {1} {Locked="WinGetPackage,{0},{1}"} Impossible de valider le package d’unité de configuration WinGetPackage. ID du package : {0} {Locked="WinGetPackage,{0}"} Spécifier la préférence de fenêtre d’authentification (silent, silentPreferred ou interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. Spécifier le compte à utiliser pour l’authentification Échec de l’ajout de la source. Cette version winget ne prend pas en charge la méthode d’authentification de la source. Essayez de mettre à niveau vers la dernière version winget. {Locked="winget"} La source {0} requiert une authentification. L’invite d’authentification peut s’afficher en cas de besoin. Les informations authentifiées seront partagées avec la source pour l’autorisation d’accès. {Locked="{0}"} Répare le package sélectionné, trouvé en effectuant une recherche dans la liste des packages installés ou directement à partir d’un manifeste. Par défaut, la requête doit correspondre, sans respect de la casse, à l’ID, au nom ou au moniker du package. D’autres champs peuvent être utilisés en passant leur option appropriée. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Répare le package sélectionné La commande de réparation de ce package est introuvable. Contactez l’éditeur du package pour obtenir de l’aide. La technologie du programme d’installation utilisée ne correspond pas à la version actuellement installée. Commande de réparation introuvable. La technologie du programme d’installation en cours d’utilisation ne prend pas en charge la réparation. L’opération de réparation s’est terminée correctement. Réparation abandonnée Démarrage de la réparation du package... Échec de la réparation du package Microsoft Store. Code d'erreur : {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. Échec de l’opération de réparation. L’opération de réparation n’est pas applicable. Aucune version de package correspondante n’est disponible à partir des sources configurées. La configuration système actuelle ne prend pas en charge la réparation de ce package. La technologie de programme d’installation utilisée ne prend pas en charge la réparation. Le package installé pour l’étendue de l’utilisateur ne peut pas être réparé en cas d’exécution avec des privilèges d’administrateur. Les opérations de réparation impliquant des privilèges d’administrateur ne sont pas autorisées sur les packages installés dans l’étendue utilisateur. Échec de la réparation avec le code de sortie : {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. La connexion SQLite a été interrompue pour éviter toute altération. Désinstaller toutes les versions La version à suivre Plusieurs versions de ce paquet sont installées. Soit vous affinez la recherche, soit vous passez l'argument `--version` pour en sélectionner une, soit vous passez l'option `--all-versions` pour les désinstaller toutes. {Locked="--version,--all-versions"} Activer les options de ligne de commande du gestionnaire de paquets Windows Describes a Group Policy that can enable the use of the --proxy option to set a proxy Définir un proxy à utiliser pour cette exécution Désactiver l’utilisation du proxy pour cette exécution Impossible de réinitialiser {0}. Ce paramètre est contrôlé par la stratégie. Pour plus d’informations, contactez votre administrateur système. {Locked="{0}"} The value will be replaced with the feature name Réinitialiser les paramètres administratifs '{0}'. {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. Impossible de définir {0}. Ce paramètre est contrôlé par la stratégie. Pour plus d’informations, contactez votre administrateur système. {Locked="{0}"} The value will be replaced with the feature name Régler le paramètre d'administration '{0}' sur '{1}'. {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced Nom du paramètre à modifier Valeur à définir pour le paramètre. Rétablit la valeur par défaut d’un paramètre d’administrateur. Rétablit la valeur par défaut d’un paramètre d’administrateur. Définit la valeur d'un paramètre administratif. Définit la valeur d'un paramètre administratif. Exclut une source de la recherche, sauf indication contraire Exclut une source de la découverte (true ou false) Contenu explicite Niveau de confiance de la source (aucun ou approuvé) Niveau de confiance Échec de l'obtention du catalogue de paquets Microsoft Store. Aucun paquet Microsoft Store applicable n'a été trouvé dans le catalogue de paquets Microsoft Store. Échec de l'obtention des informations de téléchargement du paquet Microsoft Store. Aucun package Microsoft Store applicable n’a été trouvé pour le téléchargement. Échec de la récupération de la licence du paquet Microsoft Store. Aucun paquet Microsoft Store applicable n’a été trouvé. Vérification réussie du hachage du paquet Microsoft Store Mauvaise correspondance du hachage du paquet Microsoft Store Paquet Microsoft Store téléchargé : {0} {Locked="{0}"} Full path of the downloaded package. Le téléchargement du paquet Microsoft Store a échoué : {0} {Locked="{0}"} Package name. Le téléchargement du paquet Microsoft Store est terminé Téléchargement des principaux paquets à partir du Microsoft Store... Téléchargement de paquets de dépendances à partir du Microsoft Store... Récupération des informations de téléchargement des paquets Microsoft Store Échec de la récupération des informations de téléchargement du paquet Microsoft Store Récupération de la licence du paquet Microsoft Store Licence du paquet Microsoft Store sauvegardée : {0} {Locked="{0}"} License file full path. Échec de la récupération de la licence du paquet Microsoft Store Le téléchargement du paquet Microsoft Store ne prend pas en charge l'argument --rename. Le paquet Microsoft Store utilisera les noms fournis par le catalogue Microsoft Store. {Locked="--rename"} Microsoft Store téléchargement du package nécessite une authentification par ID Microsoft Entra. L’invite d’authentification peut s’afficher en cas de besoin. Les informations authentifiées seront partagées avec services Microsoft pour l’autorisation d’accès. Pour Microsoft Store licence de package, le compte d’ID Microsoft Entra doit être membre de l’administrateur général, de l’administrateur utilisateur ou de l’administrateur de licences. {Locked="Global Administrator,User Administrator,License Administrator"} Ne pas récupérer la licence hors ligne du paquet Microsoft Store Sélectionner la plate-forme cible Ajout d'un fichier de configuration : {0} {Locked="{0}"} Exportation réussie Obtenir les paramètres de configuration... Au moins --packageId et/ou --module avec --resource doit être fourni. Ou utilisez --all pour exporter toutes les configurations de package. {Locked="--packageId,--module, --resource, --all"} Vous ne pouvez pas utiliser les arguments --packageId, --module et --resource avec --all. {Locked="--packageId,--module, --resource, --all"} Exporte les ressources de configuration vers un fichier de configuration. Lorsqu’il est utilisé avec --all, exporte toutes les configurations de package. Lorsqu’il est utilisé avec --packageId, exporte une ressource WinGetPackage de l’ID de package donné. Lorsqu’il est utilisé avec --module et --resource, obtient les paramètres de la ressource et l’exporte vers le fichier de configuration. Si le fichier de configuration de sortie existe déjà, ajoute les ressources de configuration exportées. {Locked="WinGetPackage,--packageId,--module, --resource"} Exporte les ressources de configuration vers un fichier de configuration. Le module de la ressource à exporter. L'identifiant du paquet à exporter. La ressource de configuration à exporter. Permet d’exporter toutes les configurations de package. L'unité de configuration n'a pas réussi à obtenir ses propriétés. Échec de l'exportation de la configuration. Configuration de {0} {Locked="{0}"} Installer {0} {Locked="{0}"} Certaines données présentes dans le fichier de configuration ont été tronquées pour cette sortie ; inspecter le contenu du fichier pour en obtenir le contenu complet. <cette valeur a été tronquée ; consultez le contenu du fichier pour obtenir le texte complet> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. Affiche les détails de niveau supérieur pour les configurations qui ont été appliquées au système. Ces données peuvent ensuite être utilisées avec des commandes `configure` pour obtenir plus de détails. {Locked="`configure`"} Affiche l’historique de configuration On ne trouve aucune configuration dans l'historique. Sélectionner des éléments de l’historique Aucune configuration unique correspondant aux données fournies n’a été trouvée. Fournissez le nom complet ou une partie de l’identificateur qui correspond sans ambiguïté à la configuration souhaitée. Supprimer l’élément de l’historique Première application Column header for date values indicating when a configuration was first applied to the system. Identificateur Nom Origine Chemin d'accès La configuration spécifiée est introuvable. Terminé The state of processing an item. En cours The state of processing an item. En attente The state of processing an item. Inconnu The state of processing an item. Surveiller l’état de la configuration. As in "to monitor the status of a configuration being applied". Terminé The state of processing an item. En cours The state of processing an item. En attente The state of processing an item. Ignoré The state of processing an item. Inconnu The state of processing an item. Application démarrée When the configuration application started. Fin de l’application When the configuration application ended. Résultat Détails État The state of processing an item. Unité Le package n’est pas compatible avec la version ou la plateforme Windows actuelle. Supprimer l’affichage des détails de configuration initiale lorsque cela est possible Vous pouvez télécharger plusieurs packages Microsoft Store ciblant différentes plateformes et architectures. --platform, --architecture peuvent être utilisés pour filtrer les packages. {Locked="--platform--architecture"} Échec de la récupération de Microsoft Store licence de package. Le compte d’ID Microsoft Entra n’est pas membre de l’administrateur général, de l’administrateur d’utilisateurs ou de l’administrateur de licences. Utilisez --skip-license pour ignorer la récupération Microsoft Store licence de package. {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} Le package Microsoft Store ne prend pas en charge le téléchargement. Le package Microsoft Store ne prend pas en charge le téléchargement. Échec de la récupération de Microsoft Store licence de package. Le compte d’ID Microsoft Entra ne dispose pas des privilèges requis. Le paramètre ne peut pas être transmis au-delà de la limite d’intégrité. Programme d’installation zéro octet téléchargé ; vérifiez que votre connexion réseau fonctionne correctement. Programme d’installation zéro octet téléchargé ; vérifiez que votre connexion réseau fonctionne correctement. Gérer les polices Gérez les polices avec des sous-commandes. Les polices peuvent être installées, mises à niveau ou désinstallées similaires à packages. Famille Visages "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' Filtrer les résultats par nom de famille Visage "Face" represents the typeface of a font family such as 'Bold' or 'Italic' Chemins d'accès Aucune police installée ne correspond aux critères d'entrée. Répertorier les polices installées Répertoriez toutes les polices installées, tous les fichiers de police ou tous les détails d’une police spécifique. Version Modules de configuration PowerShell Modules that are used for the Configuration feature Le programme d’installation du package nécessite une authentification. L’invite d’authentification peut s’afficher en cas de besoin. Les informations authentifiées seront partagées avec l’URL de téléchargement du programme d’installation. Échec du téléchargement du programme d’installation. Cette version winget ne prend pas en charge la méthode d’authentification de téléchargement du programme d’installation. Essayez de mettre à niveau vers la dernière version winget. {Locked="winget"} Le téléchargement du programme d'installation a échoué. L'authentification a échoué. Spécifiez le chemin d’accès au processeur de configuration Commandes de ressources DSC v3 DSC stands for "Desired State Configuration". It should already have a locked translation. Les sous-commandes implémentent ici des ressources Desired State Configuration (DSC) v3 pour configurer winget et packages. Obtenir l’état de la ressource Définir l’état de la ressource Décrire les changements d’état requis Tester l’état de la ressource Supprimer l’état de la ressource Obtenir toutes les instances d’état Valider le contenu du groupe Résoudre l’état externe Exécuter l’adaptateur Obtenir le schéma de ressource Obtenir le manifeste de ressource Gérer l’état du package Gérez les packages via WinGet. Indique si un instance doit ou existe. Indique si un instance est dans l’état souhaité. Identificateur du package. Source du package. Version du package. Méthode de mise en correspondance de l’identificateur avec un package. Indiquez que la dernière version disponible du package doit être installée. Mode d’installation à utiliser si nécessaire. Étendue cible du package. Indique s’il faut accepter les contrats pour les sources et les packages. Gérer le fichier de paramètres utilisateur Gérez les paramètres utilisateur de WinGet. Contenu du fichier JSON des paramètres. Action utilisée pour appliquer les paramètres. La valeur est journalisée pour corrélation Gérer la configuration de la source Gérez les sources de WinGet. Nom à utiliser pour la source. Argument de la source. Type de la source. Niveau de confiance de la source. Indique si la source est incluse lorsque les appels ne spécifient pas de source. Application de l’unité de configuration... Exportation de l’unité de configuration... Obtention des processeurs d’unité de configuration... Vérifier le module requis pour l’exportation [{0}] {Locked="{0}"} Échec du test ou de l’acquisition du module requis. Les paramètres associés ne seront pas exportés. Exporter [{0}] {Locked="{0}"} Échec de l’exportation de la ressource. Échec de l’obtention des processeurs d’unités. Les paramètres de package individuels ne seront pas exportés. Répertoire dans lequel les résultats doivent être écrits Desired State Configuration package introuvable sur le système. Installation du package... Échec de l’installation du package Desired State Configuration. Installez le package manuellement ou indiquez le chemin d’accès à dsc.exe via --processor-path argument. {Locked="dsc.exe","--processor-path"} Objet contenant les paramètres de l’administrateur et leurs valeurs. Gérer les paramètres de l’administrateur Gérez les paramètres d’administrateur de WinGet. Réinitialise tous les paramètres d’administration Tous les paramètres d’administration ont été réinitialisés. Informations MCP MCP stands for Model Context Protocol and should probably remain as-is Informations MCP (Model Context Protocol) pour le Gestionnaire de package Windows. Pour configurer manuellement le serveur Gestionnaire de package Windows MCP avec votre client MCP, utilisez le fragment JSON suivant dans l’objet `servers` : {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. Activer le serveur MCP du Gestionnaire de package Windows Version du système d’exploitation cible Désolé, échec de l’installation d’une ou plusieurs polices. Désolé, le fichier de police n’est pas pris en charge et ne peut pas être installé. Une ou plusieurs polices du package de polices ne sont pas prises en charge et ne peuvent pas être installées. Désolé, nous n’avons pas pu effectuer l’installation; nettoyage en cours. La police est déjà installée. ID de package WinGet pris en charge Titre Fichier de police introuvable. Échec de la désinstallation de la police. La police n’est peut-être pas en bon état. Essayez de désinstaller après un redémarrage. La validation de la police a échoué. Échec de la restauration de police. La police n’est peut-être pas en bon état. Essayez de désinstaller après un redémarrage. Afficher les informations détaillées du fichier de police. Désolé, échec de la validation du package de polices. La tentative de restauration de l’installation de police ayant échoué a échoué. Un redémarrage peut être nécessaire pour désinstaller la police. Échec de la désinstallation du package de polices. Cela est souvent dû aux polices utilisées par le système ou une application. La désinstallation peut réussir après le redémarrage de votre ordinateur. OK "OK" means the font is in a good healthy state Endommagé "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. Statut Inconnu Le package de polices est déjà installé. Afficher des informations détaillées sur les packages Providing this argument causes the CLI to output additional details about installed application packages. Canal : Precedes a string value that names the delivery channel for the software package (ex. stable, beta). Identifiant local : Precedes a value that is the unique identifier for the installed package on the local system. Nom de la famille de packages : Precedes a value that is the APPX/MSIX package family name of the installed package. Code du produit : Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. Code de mise à niveau : Precedes a value that is the MSI Upgrade Code for the installed package. Étendue de l’installation : Precedes a value that is the scope of the installation of the package (ex. user, machine). Architecture installée : Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). Paramètres régionaux de l’installation : Precedes a value that is the locale of the installed package. Emplacement de l’installation : Precedes a value that is the directory path to the installed package. Source d’origine : Precedes a value that names the package source where the installed package originated from. Mises à niveau disponibles : Precedes a list of package upgrades available for the installed package. Catégorie du programme d’installation : Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). Ancienne valeur Column title for listing edit changes. Nouvelle valeur Column title for listing the new value. ================================================ FILE: Localization/Resources/it-IT/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/it-IT/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 L'alias adiacente non è un contrassegno: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Alias del contrassegno adiacente non trovato: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Sono disponibili i seguenti argomenti: Message displayed to inform the user about the available command line arguments. Sono disponibili gli alias di comando seguenti: Message displayed to inform the user about the available command line alias arguments. Sono disponibili i seguenti comandi: Title displayed to inform the user about the available commands. Disponibile As in "a new version is available to upgrade to". Sono disponibili le seguenti opzioni: Message displayed to inform the user about the available options. Sono disponibili i seguenti comandi secondari: Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} aggiornamenti disponibili. {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Usa il canale specificato; l'impostazione predefinita è General Audience (adatto a tutte le età) comando Label displayed for a command to give the software. Filtra i risultati per comando Description message displayed to inform the user about filtering the search results by a package command. Riga di comando completa per il completamento Questo comando richiede i privilegi di amministratore per l'esecuzione. Questo comando può essere utilizzato per richiedere il completamento della riga di comando sensibile al contesto. Vengono inseriti i dati della riga di comando, della posizione del cursore e della parola da completare. L'output è un insieme di valori possibili in base agli input, con un valore possibile per riga. Abilita il completamento della riga di comando con contesto Non mostra più del numero di risultati specificato (compreso tra 1 e 1000) Fatto Label displayed when an operation completes or is done executing. Trova pacchetto con corrispondenza esatta Description message displayed to inform the user about finding an application package using an exact matching criteria. Argomento sperimentale a scopo dimostrativo Questo comando è un esempio su come implementare una funzionalità sperimentale. Per attivare l'opzione “winget settings” e abilitare le funzionalità experimentalCmd o experimentalArg. {Locked="winget settings"} Esempio di funzionalità sperimentale È stato trovato un argomento posizionale quando non ne era previsto nessuno: '{0}' {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. Questa funzionalità è in fase di sviluppo e potrebbe essere modificata completamente o rimossa del tutto in futuro. Per abilitarla, modificare le impostazioni ('winget settings') per includere la funzionalità sperimentale: '{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Mostra lo stato delle funzionalità sperimentali. Le funzionalità sperimentali possono essere attivate tramite 'winget settings'. {Locked="winget settings"} Mostra lo stato delle funzionalità sperimentali Disabilitate Abilitata Funzionalità Collegamento Le seguenti funzionalità sperimentali sono in corso. Possono essere configurati tramite il file di impostazioni ' winget settings '. {Locked="winget settings"} Proprietà Stato File su cui eseguire l'hashing L'argomento del contrassegno non può contenere un valore adiacente: '{0}' {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Calcola l'hash di un file locale, appropriato per l'ingresso in un manifesto. Può anche calcolare l'hash del file della firma di un pacchetto MSIX per abilitare le installazioni di streaming multimediale. Helper per eseguire l'hashing dei file di installazione Mostra la guida per il comando selezionato Per altre informazioni su un comando specifico, passa alla Guida degli argomenti. Altre informazioni sono disponibili alla pagina: {0} {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Filtra i risultati per ID Elimina gli output di avviso Questa applicazione viene concessa in licenza dal proprietario. Microsoft non è responsabile né concede alcuna licenza a pacchetti di terze parti. Questo pacchetto è stato fornito tramite Microsoft Store. winget potrebbe dover acquisire il pacchetto da Microsoft Store per conto dell'utente corrente. {Locked="winget"} Installa il pacchetto selezionato, trovato eseguendo una ricerca in un'origine configurata o direttamente da un manifesto. Per impostazione predefinita, la query deve corrispondere senza distinzione tra maiuscole e minuscole all'id, name o moniker del pacchetto. È possibile usare altri campi passando l'opzione appropriata. Per impostazione predefinita, il comando install controllerà lo stato di installazione del pacchetto e tenterà di eseguire un aggiornamento, se applicabile. Eseguire l'override con --force per eseguire un'installazione diretta. {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Installa il pacchetto specificato Hash di installazione non corrispondente. Questo non può essere ignorato durante l'esecuzione come amministratore L’hash di installazione non corrisponde. Procedendo a causa di --ignore-security-hash {Locked="--ignore-security-hash"} Hash di installazione non corrispondente; Per eseguire l’override, seleziona use --ignore-security-hash {Locked="--ignore-security-hash"} Verifica dell'hash di installazione completata Installazione riuscita Avvio installazione pacchetto in corso... Ignora l'errore di controllo hash del programma di installazione Ignora l'analisi malware eseguita durante l'installazione di un pacchetto di tipo di archivio dal manifesto locale Richiedi l'installazione interattiva; l'input dell'utente potrebbe essere necessario Alias di argomento non riconosciuto per il comando corrente: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Identificatore di argomento non valido: '{0}' {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). Nome argomento non riconosciuto per il comando corrente: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Directory WinGet Header for a table detailing the directories Winget uses for key operations like logging and portable installs Impostazioni locali da usare (formato BCP47) {Locked="BCP47"} Contratto di licenza Collegamenti Links to different webpages Il comando list mostra i pacchetti installati nel sistema nonché la disponibilità di un aggiornamento. È possibile fornire opzioni aggiuntive per filtrare l'output, molto simili al comando search. {Locked="list","search"} Visualizza pacchetti installati Percorso di installazione (se supportato) Percorso log (se supportato) Copyright (c) Microsoft Corporation. Tutti i diritti sono riservati. Home page The primary webpage for the software Percorso del manifesto del pacchetto Convalida del manifesto non riuscita. Convalida del manifesto riuscita. La convalida del manifesto è stata effettuata con avvisi. Il valore dell'argomento necessario risulta mancante: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Filtra i risultati in base al moniker Il file di input verrà considerato come MSIX; l'hash della firma verrà fornito se firmato Non è possibile calcolare l'hash della firma MSIX. Non è stato possibile installare o aggiornare il pacchetto di Microsoft Store perché l'app specifica è bloccata dai criteri Non è stato possibile installare o aggiornare il pacchetto di Microsoft Store. Codice di errore: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Non è stato possibile installare o aggiornare il pacchetto Microsoft Store perché il client Microsoft Store è bloccato dai criteri Verifica/richiesta di acquisizione pacchetti... Trovati più pacchetti installati corrispondenti ai criteri di input. Ridefinire l'input. Multipli pacchetti hanno trovato criteri di input corrispondenti. Ridefinisci l'input. Filtra i risultati per nome Nessun programma di installazione applicabile trovato. Per ulteriori dettagli, vedere i log. Al momento non sono disponibili funzionalità sperimentali. Non è stato trovato alcun pacchetto installato corrispondente ai criteri di input. Nessun pacchetto trovato con criteri di input corrispondenti. Disabilita la visualizzazione di VirtualTerminal {Locked="VirtualTerminal"} Apri il percorso predefinito dei log opzioni Options to change how a command works Sostituisci gli argomenti da trasmettere al programma di installazione Pacchetto: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. Ci siamo dimenticati di farlo... Posizione del cursore nella riga di comando Informativa sulla privacy Query usata per cercare un'app Lo stato di avanzamento mostra un arcobaleno di colori L'argomento obbligatorio non è stato fornito: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Lo stato di avanzamento viene mostrato come il colore predefinito Esegue ricerche di pacchetti da origini configurate. Trova e mostra le informazioni di base dei pacchetti Id Abbreviation of Identifier. Corrispondenza Nome Origine voci aggiuntive troncate a causa del limite dei risultati Versione Sono stati trovati i seguenti errori durante la convalida delle impostazioni: Aprire le impostazioni nell'editor di testo json predefinito. Se nessun editor è configurato, apre le impostazioni nel blocco note. Per le impostazioni disponibili, vedi https://aka.ms/winget-settings. Questo comando può essere usato anche per impostare le impostazioni dell'amministratore, fornendo gli argomenti --enable o --disable {Locked="--enable"} {Locked="--disable"} Aprire le impostazioni o predisporre le impostazioni di amministratore Errore imprevisto durante il caricamento delle impostazioni. Verificare le impostazioni eseguendo il comando 'settings'. {Locked="'settings'"} Canale Visualizza informazioni su un pacchetto specifico. Per impostazione predefinita, la query deve essere insensitively con l'ID, il nome o il moniker del pacchetto. Altri campi possono essere utilizzati passando l'opzione appropriata. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Mostra informazioni su un pacchetto Versione Richiedi l'installazione invisibile all'utente Solo l'alias a carattere singolo può apparire dopo un singolo -: '{0}' {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. Esiste già un'origine con il nome specificato e si riferisce a un percorso diverso: Una fonte con un nome diverso fa già riferimento a questa posizione: Esiste già un'origine con il nome specificato e si riferisce allo stesso percorso: Aggiunta origine: Aggiungi una nuova origine. Un'origine fornisce i dati per individuare e installare i pacchetti. Aggiungi una nuova origine solo se la consideri attendibile. Aggiungi una nuova origine Argomento assegnato all'origine Trova il pacchetto usando l'origine specificata Gestisci le origini con i comandi secondari. Un'origine fornisce i dati per individuare e installare i pacchetti. Aggiungi una nuova origine solo se la consideri attendibile. Gestisci le origini dei pacchetti Modifica le proprietà di un'origine esistente. Un'origine fornisce i dati per l'individuazione e l'installazione dei pacchetti. Modifica proprietà di un'origine Modifica origine: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. L'origine denominata '{0}' si trova già nello stato desiderato. {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Argomento Value given to source. Elenca tutte le origini correnti o tutti i dettagli di un'origine specifica. Elenca le origini correnti Dati Data stored by the source. Campo The name of a piece of information about a source. Nome The name of the source. Identificatore The source's unique identifier. Non è stata trovata un'origine denominata: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. Nessuna origine configurata. Tipo The kind of source. Aggiornato The last time the source was updated. mai The source has never been updated. Valore The value of information about a source. Nome dell'origine Errore durante l'apertura della/e origine/i; provare il comando 'source reset' se il problema persiste. {Locked="source reset"} Non è stato possibile aprire l'origine predefinita. Informare i gestori winget {Locked="winget"} Rimozione di tutte le origini in corso... Rimuovi un'origine specifica. Rimuovi le origini correnti Rimozione origine in corso: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Reimpostazione di tutte le origini in corso... Questo comando elimina le origini esistenti, ad esclusione dei dati locali. Senza alcun argomento, eliminerà tutte le origini e aggiungerà quelle predefinite. Se viene fornito il nome di un'origine specifica, verrà eliminata solo quella. Reimposta origini Forza il reset delle origini Le origini seguenti verranno reimpostate se viene specificata l'opzione--force: {Locked="--force"} Reimpostazione origine in corso: {0} {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Tipo di origine Aggiornamento di tutte le origini... Aggiorna tutte le origini o solo un'origine specifica. Aggiorna le origini correnti Aggiornamento origine in corso: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Filtra i risultati per tag Grazie per aver usato WinGet Comunicazioni di terze parti L'utilità della riga di comando di Winget consente di installare le applicazioni e altri pacchetti dalla riga di comando. Visualizza le informazioni generali dello strumento Mostra la versione dello strumento Argomento fornito più volte di quanto consentito: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. Sono stati specificati più argomenti del comportamento di esecuzione: '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). Si è verificato un errore imprevisto durante l'esecuzione del comando: Disinstalla la versione precedente del pacchetto durante l'aggiornamento Comando non riconosciuto: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Aggiorna tutti i pacchetti installati alla versione più recente se disponibile Non è stato trovato alcun aggiornamento applicabile. Una versione più recente del pacchetto è disponibile in un'origine configurata, ma non si applica al sistema o ai requisiti. Non sono stati trovati aggiornamenti disponibili. Non sono disponibili versioni più recenti del pacchetto dalle origini configurate. Aggiorna il pacchetto selezionato, trovato eseguendo la ricerca nell'elenco dei pacchetti installati o direttamente da un manifesto. Per impostazione predefinita, la query deve essere insensitively con l'ID, il nome o il moniker del pacchetto. Altri campi possono essere utilizzati passando l'opzione appropriata. Quando non vengono forniti argomenti, mostra i pacchetti con aggiornamenti disponibili id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Mostra ed esegue gli aggiornamenti disponibili utilizzo: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Convalida un manifesto con un insieme di linee guida rigorose. Queste hanno lo scopo di consentirti di controllare il tuo manifesto prima di inviarlo a un repository. Convalida un file manifesto Il percorso del manifesto da convalidare Abilita la registrazione dettagliata per winget Verifica che il file di input sia un MSIX valido e firmato. Usa la versione specificata; la versione predefinita è quella più recente Mostra le versioni disponibili del pacchetto È richiesto il valore specificato prima del completamento Non sono state trovate versioni corrispondenti: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. Nessuna origine corrisponde al valore specificato: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. Le origini configurate sono: Non è stata definita alcuna origine. Aggiungerne una con "source add" o ripristinare i valori predefiniti con "source reset" {Locked="source add","source reset"} Trovato Il percorso è una directory: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. Il file non esiste: {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Sono forniti argomenti sia per il manifesto locale che per la query di ricerca. Log Label displayed for diagnostic files containing information about the application use. Programma di installazione bloccato dai criteri Non è stato possibile eseguire il controllo di sicurezza per il programma di installazione Un antivirus ha rilevato una infezione nel programma di installazione Non è stato possibile aggiornare l'origine: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Disinstalla il pacchetto selezionato, trovato mediante la ricerca dell'elenco dei pacchetti installati o direttamente da un manifesto. Per impostazione predefinita, la query deve essere insensitively con l'ID, il nome o il moniker del pacchetto. Altri campi possono essere utilizzati passando l'opzione appropriata. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Disinstalla il pacchetto specificato Avvio disinstallazione pacchetto in corso... Disinstallazione completata winget non riesce a individuare il comando di disinstallazione per questo pacchetto. Contattare l'autore del pacchetto per chiedere supporto. {Locked="winget"} Disinstallazione interrotta Disinstallazione non riuscita con codice di uscita: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Esporta un elenco dei pacchetti installati Installa tutti i pacchetti elencati in un file. Installa tutti i pacchetti in un file File in cui deve essere scritto il risultato File che descrive i pacchetti da installare Esporta pacchetti dall'origine specificata Scrive un elenco dei pacchetti installati in un file. I pacchetti possono quindi essere installati con il comando import. {Locked="import"} Impossibile installare uno o più pacchetti importati L'origine necessaria per l'importazione non è installata: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. Il pacchetto installato non è disponibile in alcuna origine: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. La versione installata del pacchetto non è disponibile in alcuna origine: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. Nessun pacchetto trovato nel file di importazione Il file JSON non è valido Il pacchetto è già installato: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. Ignora pacchetti non disponibili Includi versioni del pacchetto nel file di esportazione Ignora versioni del pacchetto dal file di importazione Il percorso non esiste: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. Il file JSON non specifica uno schema riconosciuto. Seleziona ambito di installazione (user o machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. Il valore specificato per l'argomento '{0}' non è valido. i valori validi sono: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. Operazione disabilitata da Criteri di gruppo: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Abilita altre origini di programma di installazione app di Windows Abilita le origini consentite del programma di installazione app di Windows Abilita origine predefinita del programma di installazione app di Windows Abilita funzionalità sperimentali del programma di installazione app di Windows Abilita l'origine Microsoft Store del programma di installazione app di Windows Abilita l'origine del carattere di Programma di installazione Windows App Abilita le impostazioni di Gestione pacchetti Windows Abilita Gestione pacchetti Windows Abilita Gestione interfacce della riga di comando dei pacchetti Windows Imposta l’intervallo di aggiornamento automatico di origine Gestione pacchetti Windows in minuti Criteri di gruppo Header for a table listing active Group Policies Abilita file manifesto locali del programma di installazione app di Windows Abilita il bypass del certificato aggiunto di origine di Windows Programma di installazione app Microsoft Store Abilita l'override dell'analisi malware dell'archivio locale di Programma di installazione app di Windows Campo: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. Formato del campo non valido. Valore campo non valido. Impostazione non valida dai criteri di gruppo. Caricamento delle impostazioni dal file di backup completato. Errore durante l'analisi del file: Valore: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. Le seguenti funzionalità sperimentali sono in corso. La configurazione è disabilitata a causa dei criteri di gruppo. L'hash del programma di installazione non corrisponde. Abilita sostituzione hash del programma di installazione app di Windows Esporta origini correnti come JSON per criteri di gruppo. Esporta le origini correnti Origine aggiuntiva An additional source required by policy. Origine consentita A source that the user is allowed to add. Il valore specificato per l'argomento '{0}' non è valido {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Annullato Esterno I pacchetti trovati in questa importazione hanno le dipendenze seguenti: Import command sentence showed before reporting dependencies Questo pacchetto richiede le dipendenze seguenti: Message shown before reporting dependencies Installazione delle dipendenze: L'origine delle dipendenze non è stata trovata La ricerca del pacchetto ha restituito più di un risultato: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. Ultima versione non trovata per il pacchetto: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. Non sono stati trovati programmi di installazione: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Versione minima richiesta non disponibile per il pacchetto: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. Nessuna corrispondenza When package search yields no matches Con ciclo Dependency graph has loop Non è stato trovato alcun programma di installazione adatto per il manifesto: {0} versione {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Errore durante l'elaborazione delle dipendenze del pacchetto. Vuoi continuare l'installazione? Prompt message shown when dependencies processing yields errors. Errore durante l'elaborazione delle dipendenze del pacchetto. Uscita in corso... Pacchetti Questo pacchetto contiene dipendenze che potrebbero non essere più necessarie: Uninstall command sentence showed before reporting dependencies Il manifesto contiene le seguenti dipendenze che non sono state convalidate; assicurarsi che siano valide: Validate command sentence showed before reporting dependencies Funzionalità Windows Raccolte di Windows Trova le dipendenze del pacchetto con l'origine specificata For getting package type dependencies when installing from a local manifest Condizioni di Windows Store Accetta tutti i contratti di licenza per i pacchetti Il pacchetto esportato richiede il contratto di licenza per l'installazione: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. L'editore richiede la visualizzazione delle informazioni precedenti e l'accettazione dei contratti prima dell'installazione. Accetti le condizioni? Contratti del pacchetto non accettati. Operazione annullata. Contratti: Autore: Descrizione: Programma di installazione: Impostazioni locali del programma di installazione: ID prodotto dello Store: SHA256 del programma di installazione: Tipo di programma di installazione: URL del programma di installazione: Licenza: URL licenza: Moniker: Home page: Editore: Tag: Versione: Dipendenze: Funzionalità Windows: Raccolte di Windows: Dipendenze dei pacchetti: Dipendenze esterne: No Non è stato possibile aprire l'origine aggiunta. Accetta tutti i contratti di origine durante le operazioni di origine Prima di usare l'origine `{0}`, è necessario visualizzare i contratti seguenti. {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. Accetti tutte le condizioni dei contratti di origine? Uno o più contratti di origine non sono stati accettati. Operazione annullata. Accettare i contratti di origine o rimuovere le origini corrispondenti. L'origine richiede che l'area geografica di 2 lettere del computer corrente venga inviata al servizio back-end per funzionare correttamente ,ad esempio "STATI Uniti". Installazione completata. Riavviare l'applicazione per completare l'aggiornamento. Intestazione HTTP di origine REST di Windows-Package-Manager facoltativa L'intestazione facoltativa verrà ignorata perché non è applicabile per questa origine. L'intestazione facoltativa non è applicabile senza specificare un'origine: '{0}' {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Data di rilascio: Distribuzione offline supportata: URL editore: URL acquisto: URL supporto editore: URL privacy: Copyright: URL copyright: Note sulla versione: URL note sulla versione: Errore durante la ricerca nell'origine; i risultati non verranno inclusi: '{0}' {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Errore durante la ricerca nell'origine: '{0}' {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Questa funzionalità deve essere abilitata dagli amministratori. Per abilitarlo, eseguire 'winget settings --enable {0}' come amministratore. {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Abilita l'impostazione amministratore specifica Disabilita l'impostazione di amministratore specifica Impostazione amministratore '{0}' abilitata. {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. Impostazione amministratore '{0}' disabilitata. {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. Impostazione amministrazione Header for a table displaying admin settings. L'applicazione è attualmente in esecuzione. Uscire dall'applicazione, quindi riprovare. I file modificati dal programma di installazione sono attualmente utilizzati da un'applicazione diversa. Chiudere le applicazioni, quindi riprovare. È già in corso un'altra installazione. Riprova più tardi. È in uso uno o più file. Uscire dall'applicazione, quindi riprovare. Nel sistema manca una dipendenza per questo pacchetto. Non c'è più spazio nel PC. Liberare spazio, quindi riprovare. Memoria insufficiente per l'installazione. Chiudi le altre applicazioni, quindi riprova. Uno dei parametri di installazione non è valido. Il log di installazione del pacchetto potrebbe contenere ulteriori dettagli. Questa applicazione richiede la connettività Internet. Connettiti a una rete e riprova. Errore dell'applicazione durante l'installazione. Contattare il supporto tecnico. Riavvia il PC per completare l'installazione.. Il PC verrà riavviato per completare l'installazione. Installazione non riuscita. Riavvia il PC e riprova. Hai annullato l’installazione. È già installata un'altra versione di questa applicazione. È già installata una versione successiva di questa applicazione. I criteri dell'organizzazione impediscono l'installazione. Contattare l'amministratore. La configurazione di sistema corrente non supporta l'installazione di questo pacchetto. L'installazione non è riuscita con un errore del programma di installazione personalizzato. Contattare il supporto per i pacchetti. Installazione interrotta Programma di installazione non riuscito con codice di uscita: '{0}' {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. Il log del programma di installazione è disponibile in: '{0}' {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. Sono stati trovati i pacchetti seguenti tra le origini di lavoro. Specificarne uno utilizzando l'opzione --source per continuare. {Locked="--source"} "working sources" as in "sources that are working correctly" Non sono stati trovati pacchetti tra le origini di lavoro. "working sources" as in "sources that are working correctly" Questa è una release stabile di Gestione pacchetti Windows. Se desideri provare funzionalità sperimentali, installa una build preliminare. Istruzioni disponibili su GitHub all'indirizzo https://github.com/microsoft/winget-cli. {Locked="https://github.com/microsoft/winget-cli"} Gestione pacchetti Windows v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. Ignora versioni del pacchetto dal file di importazione Il numero di risultati richiesto deve essere compreso tra 1 e 1000. Argomenti da passare al programma di installazione oltre ai valori predefiniti È stata trovata una versione più recente, ma la tecnologia di installazione è diversa dalla versione corrente installata. Disinstallare il pacchetto e installare la versione più recente. La tecnologia di installazione della versione più recente specificata è diversa dalla versione corrente installata. Disinstallare il pacchetto e installare la versione più recente. Gestione pacchetti Windows (Anteprima) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Selezionare l'architettura Aggiorna i pacchetti anche se non è possibile determinare la versione corrente Elenca i pacchetti anche se non è possibile determinare la versione corrente. Può essere utilizzato solo con l'argomento --upgrade-available {Locked="--upgrade-available"} Non è possibile determinare il numero di versione di questo pacchetto. Per aggiornarlo comunque, aggiungere l'argomento --include-unknown al comando precedente. {Locked="--include-unknown"} {0} pacchetti contengono numeri di versione che non possono essere determinati. Usare --include-unknown per visualizzare tutti i risultati. {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} pacchetti sono aggiunti e devono essere aggiornati in modo esplicito. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. Gli argomenti specificati possono essere usati solo con una query. Architettura di sistema: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). Conserva tutti i file e le directory creati dal pacchetto (portabile) Elimina tutti i file e le directory nella directory dei pacchetti (portabile) Premi INVIO per continuare Valore per rinominare il file eseguibile (portabile) Richiede all'utente di premere un tasto qualsiasi prima di uscire Il programma di installazione richiederà l'esecuzione come amministratore. È previsto un prompt. Impossibile eseguire il programma di installazione da un contesto di amministratore. Variabile di ambiente del percorso modificata; riavviare la shell per usare il nuovo valore. Filtri che usano il codice prodotto Esiste già un pacchetto portabile con lo stesso nome ma proveniente da un'origine diversa; procedi a causa di --force {Locked="--force"} Il volume non supporta i dati reparse point Il nome file specificato non è un nome di file valido Sovrascrittura file esistente: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. Non è stato specificato alcun argomento di selezione pacchetto; per informazioni dettagliate sulla ricerca di un pacchetto, vedere la Guida. Alias della riga di comando aggiunto: Directory collegamenti portabili (computer) Directory collegamenti portabili (utente) Radice pacchetto portabile (utente) Radice pacchetto portabile Radice pacchetto portabile (x86) Installazione portabile non riuscita; Pulizia... Il pacchetto portatile è stato modificato; procedimento causato da --force {Locked="--force"} Impossibile rimuovere il pacchetto portabile perché è stato modificato; per eseguire la sostituzione di questo controllo usare --force {Locked="--force"} I file rimangono nella directory di installazione: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Eliminazione della directory di installazione in corso... Non è possibile eliminare la directory di installazione, poiché non è stata creata da WinGet. Collegamento correlato Documentazione: Note: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Note sull'installazione: Un argomento specificato non è supportato per questo pacchetto Non è stato possibile estrarre il contenuto dell'archivio Il file del programma di installazione annidato non esiste. Assicurarsi che il percorso relativo specificato del programma di installazione annidato corrisponda a: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Il percorso relativo del file del programma di installazione annidato non è valido. il percorso punta a un percorso esterno alla directory di installazione Nessun programma di installazione annidato specificato per questo pacchetto È possibile specificare un solo programma di installazione annidato per un programma di installazione di archivio, a meno che non si tratti di un programma di installazione portatile o annidato del tipo di carattere Sono stati specificati argomenti della riga di comando incompatibili Elenca solo i pacchetti con un aggiornamento disponibile Per i pacchetti seguenti è disponibile un aggiornamento, ma è necessario un targeting esplicito per l'aggiornamento: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly Download in corso Label displayed while downloading an application installer. Il tipo di programma di installazione annidato non è supportato Questo programma di installazione riavvia il terminale o la shell Questo pacchetto richiede un percorso di installazione I programmi di installazione seguenti riavviano il terminale o la shell: I seguenti programmi di installazione richiedono un percorso di installazione: Specificare la radice di installazione: Continuare? Accordi per This will be followed by a package name, and then a list of license agreements Il percorso di installazione è richiesto dal pacchetto, ma non è stato specificato Disabilita prompt interattivi Description for a command line argument, shown next to it in the help È stato trovato un pacchetto esistente già installato. Tentativo di aggiornamento del pacchetto installato... Esegui direttamente il comando e continua con problemi non correlati alla sicurezza Description for a command line argument, shown next to it in the help Esiste già un pacchetto portabile da un'origine diversa Estrazione del file compresso completata con successo. Estrazione del file compresso L'analisi dell'archivio ha rilevato malware. Per eseguire l'override di questo controllo, usare --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} L’analisi dell'archivio ha rilevato un malware. Operazione in corso a causa di --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} Ignora l'aggiornamento se esiste già una versione installata È già installata una versione del pacchetto. Installazione annullata. Impossibile abilitare {0}. Questa impostazione è controllata dai criteri. Per ulteriori informazioni, contattare l'amministratore di sistema. {Locked="{0}"} The value will be replaced with the feature name Impossibile disabilitare {0}. Questa impostazione è controllata dai criteri. Per ulteriori informazioni, contattare l'amministratore di sistema. {Locked="{0}"} The value will be replaced with the feature name Aggiungi un nuovo PIN. Un PIN può limitare il Gestione pacchetti Windows dall'aggiornamento di un pacchetto a intervalli di versioni specifici o impedire l'aggiornamento del pacchetto. Un pacchetto aggiunto potrebbe ancora essere aggiornato autonomamente ed essere aggiornato dall'esterno del Gestione pacchetti Windows. Per impostazione predefinita, è possibile aggiornare un pacchetto aggiunto menzionandolo in modo esplicito nel comando 'upgrade' o aggiungendo il flag '--include-pinned' a 'winget upgrade --all'. {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} Aggiungi un nuovo pin Gestione dei pin dei pacchetti con i sotto-comandi. Il pin può limitare l'aggiornamento di un pacchetto da parte di Gestione pacchetti Windows a intervalli specifici di versioni o può impedire l'aggiornamento del pacchetto stesso. Un pacchetto con il pin può comunque aggiornarsi da solo ed essere aggiornato dall'esterno di Gestione pacchetti Windows. Gestisci i pin del pacchetto Elenca tutti i pin correnti o i dettagli completi di un pin specifico. Elenca i pin correnti Rimuovi un pin specifico del pacchetto. Rimuovi un pin del pacchetto Reimposta tutti i pin esistenti. Reimposta i pin Versione a cui aggiungere il pacchetto. È possibile usare il carattere jolly '*' come ultima parte della versione Blocca l'aggiornamento fino a quando il pin non viene rimosso, evitando la sostituzione degli argomenti Aggiungi una versione installata specifica Installato Value used in a table to indicate that a package comes from the list of packages installed in the machine Esporta le impostazioni come JSON Impostazioni di esportazione Impostazioni utente Label displayed for the file containing the user settings. Non è stato possibile caricare il file delle impostazioni. Utilizzo dei valori predefiniti. Selezionare il filtro dell'ambito del pacchetto installato (user o machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. Aggiunta del pin completata È già presente un PIN per il pacchetto {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. Per il pacchetto {0} esiste già un PIN. Sovrascrittura a causa dell'argomento --force. {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Per il pacchetto {0} esiste già un PIN. Utilizzare l'argomento --force per sovrascriverlo. {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Reimpostazione di tutti i pin correnti. Utilizza l'argomento --force per reimpostare tutti i pin. I pin seguenti verranno rimossi: {Locked="--force"} Tipo di pin Nessun PIN per il pacchetto {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. Nessun PIN configurato. Shown when listing or modifying existing pins if there are none. Reimpostazione dei pin completata Shown after resetting (deleting) all the pins Impossibile aprire il database pin. Error message for when we cannot open the database containing package pins. È stato specificato un argomento che può essere usato solo per un pacchetto singolo Sono stati specificati più argomenti che si escludono a vicenda: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together L'argomento {0} può essere utilizzato solo con {1} {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" Disabilitate As in enabled/disabled Abilitata As in enabled/disabled Stato Header for a table listing the state (enabled/disabled) of Group Policies and Settings Query usata per cercare un'app Pacchetto non trovato: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. Sono stati trovati più pacchetti per: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. Ricerca non riuscita per: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} pacchetti hanno un PIN che deve essere rimosso prima dell'aggiornamento {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade Non è possibile aggiornare il pacchetto con WinGet. Usare il metodo fornito dal server di pubblicazione per l'aggiornamento di questo pacchetto. Aggiorna i pacchetti anche se hanno un PIN non bloccante Elenca i pacchetti anche se hanno un PIN che impedisce l'aggiornamento. Può essere utilizzato solo con l'argomento --upgrade-available {Locked="--upgrade-available"} L'aggiunta è stata rimossa È stata trovata una versione più recente, ma il pacchetto contiene un PIN che impedisce l'aggiornamento. Il pacchetto è bloccato e non può essere aggiornato. Utilizza il comando 'winget pin' per visualizzare e modificare le scelte. Alcuni tipi di PIN possono essere ignorati con l'argomento --include-pinned. {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} pacchetti contiene pin che impediscono l'aggiornamento. Utilizza il comando 'winget pin' per visualizzare e modificare le scelte. L'utilizzo dell'argomento --include-pinned può mostrare più risultati. {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages Assicura che il sistema corrisponda allo stato desiderato, come descritto dalla configurazione fornita. Può scaricare/eseguire i processori per ottenere lo stato desiderato. La configurazione e i processori devono essere controllati per verificarne l'attendibilità prima di applicarli. Configura il sistema in uno stato desiderato Mostra i dettagli della configurazione fornita. Per impostazione predefinita, il sistema non verrà modificato, ma alcune opzioni determineranno il download e/o il caricamento dei file. Mostra i dettagli di una configurazione Controlla che il sistema corrisponda allo stato desiderato come descritto dalla configurazione fornita. Può scaricare/eseguire processori per testare lo stato desiderato. Verificare che la configurazione e i processori siano attendibili prima di eseguirli. Controlla il sistema rispetto a uno stato desiderato Convalida un file di configurazione per la correttezza. Convalida un file di configurazione Il tipo del campo '{0}' nel file di configurazione non è corretto. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. Percorso del file di configurazione Il file di configurazione non è valido. La versione del file di configurazione {0} non è nota. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. Accetta l'avviso di configurazione, impedendo un prompt interattivo Applica Indicates that this item is used to write state Asserzione Indicates that this item is used to check/assert the state rather than write to it Dipendenze:{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. Parte della configurazione non è stata applicata correttamente. Impossibile ottenere informazioni dettagliate sulla configurazione. Informare Indicates that this item is used to retrieve values for future use rather than writing them Locale Used to indicate that the item is present on the device. Modulo: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. Modulo: {0} per {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. Impostazioni: Label for the values that are used as inputs for this item when applying state La configurazione è stata applicata. Unità applicata correttamente. È in corso l'applicazione di un'altra configurazione al sistema. Questa configurazione continuerà non appena possibile... È tua responsabilità comprendere le impostazioni di configurazione che scegli di eseguire. Microsoft non è responsabile del file di configurazione creato o importato. Questa configurazione può modificare le impostazioni in Windows, installare software, modificare le impostazioni del software (incluse le impostazioni di sicurezza) e accettare contratti utente per i pacchetti e i servizi di terze parti per conto dell'utente.  Eseguendo questo file di configurazione, riconosci e accetti queste risorse e impostazioni. Tutte le applicazioni installate sono concesse in licenza dai proprietari. Microsoft non è responsabile né concede alcuna licenza a pacchetti o servizi di terze parti. Legal approved. Do not change without approval. La configurazione è stata esaminata e si desidera procedere con l'applicazione al sistema? PM approved. La configurazione è vuota. La funzionalità [{0}] non è stata trovata. {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. Non è stato possibile abilitare le dipendenze delle funzionalità di Windows. Per procedere con l'installazione, usare '--force'. {Locked="--force"} Riavvio richiesto per abilitare completamente le funzioni di Windows; per ignorare questo controllo, usare '--force'. "Windows Feature(s)" is the name of the options Windows features setting. Non è stato possibile abilitare le dipendenze delle funzionalità di Windows; in corso a causa di --force "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} Abilitazione di [{0}] in corso... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. Non è stato possibile abilitare la funzionalità [{0}]: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. È necessario riavviare il sistema per abilitare completamente le funzionalità di Windows. procedi a causa di --force "Windows Feature(s)" is the name of the options Windows features setting. In attesa del completamento di un'altra installazione/disinstallazione... Versione aggiunta Table header for the version to which a package is pinned; meaning it should not update from that version. <Per ulteriori dettagli vedi il file di log> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. Recupero dei dettagli della configurazione Inizializzazione del sistema di configurazione Lettura del file di configurazione Il sistema non si trova nello stato desiderato dichiarato dalla configurazione. L'unità di configurazione non è riuscita per un motivo sconosciuto: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. L'unità di configurazione non è riuscita a causa della configurazione: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Errore dell'unità di configurazione durante il tentativo di ottenere lo stato corrente del sistema. Errore dell'unità di configurazione durante il tentativo di applicare lo stato desiderato. Errore dell'unità di configurazione durante il tentativo di testare lo stato corrente del sistema. L'unità di configurazione non è riuscita a causa di un errore interno: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. L'unità di configurazione non è riuscita perché una condizione preliminare non è valida: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. L'unità di configurazione non è riuscita a causa dello stato del sistema: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Errore dell'unità di configurazione durante il tentativo di esecuzione: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. La configurazione contiene l'identificatore '{0}' più volte. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. La dipendenza '{0}' non è stata trovata nella configurazione. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. Questa unità di configurazione è stata ignorata manualmente. Il modulo per l'unità di configurazione è disponibile in più località con la stessa versione. Caricamento del modulo per l'unità di configurazione non riuscito. Sono state trovate più corrispondenze per l'unità di configurazione; specificare il modulo per selezionare quello corretto. Impossibile trovare l'unità di configurazione. L'unità di configurazione non si trovava nel modulo come previsto. L'unità di configurazione non è stata eseguita perché una dipendenza non è riuscita o non è stata eseguita. L'unità di configurazione non è stata eseguita perché un'asserzione non è riuscita o è false. L'unità di configurazione ha restituito un risultato imprevisto durante l'esecuzione. L'unità di configurazione non è stata eseguita per un motivo sconosciuto: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Il campo '{0}' ha un valore non valido: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. Il campo '{0}' è mancante o vuoto. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. Vedere la riga {0}, la colonna {1} nel file. {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. Annullamento dell'operazione Installa il pacchetto di stub per AppInstaller Installa il pacchetto completo per AppInstaller Abilita le funzionalità estese. Richiede l'accesso all'archivio. Non è possibile usare le opzioni "--enable" e "--disable" con altri argomenti. {Locked="--enable", "--disable"} Abilitazione delle funzionalità estese. Richiede l'accesso all'archivio. Le funzionalità estese non sono abilitate. Eseguire "winget configure --enable" per abilitarli. {Locked="winget configure --enable"} Le funzionalità estese sono abilitate. Disabilitare le funzionalità estese. Richiede l'accesso all'archivio. Disabilitazione delle funzionalità estese. Richiede l'accesso all'archivio. Le funzionalità estese sono disabilitate. Ignora l'elaborazione delle dipendenze dei pacchetti e delle funzionalità di Windows Dipendenze ignorate. Non è stato possibile aggiornare la variabile PATH per il processo. Le installazioni successive che dipendono dalle modifiche apportate alla variabile PATH potrebbero non riuscire. {Locked="PATH"} Errore di alcune unità di configurazione durante il test dello stato. Il sistema si trova nello stato di configurazione descritto. Lo stato della configurazione non è stato testato. Il sistema non si trova nello stato di configurazione descritto. Risultato del test imprevisto: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). La configurazione è stata esaminata e si desidera procedere con la verifica del sistema? Il file di configurazione non è un file di YAML valido. {Locked="YAML"} YAML is a file format name. Questa unità di configurazione fa parte di un ciclo di dipendenze. La convalida non ha rilevato problemi. L'unità di configurazione è disponibile solo come versione preliminare, ma non è contrassegnata in questo modo nella configurazione. Aggiungi 'allowPrerelease: true' alladirectives'. {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. L'unità di configurazione è stata trovata localmente, ma non è stata trovata in alcun catalogo configurato. Verifica che sia presente in qualsiasi sistema prima di applicare la configurazione. L'unità di configurazione non è disponibile pubblicamente; assicurati che chiunque usi questa configurazione abbia accesso a tale configurazione. Il modulo non è stato specificato. La specifica del modulo migliora le prestazioni e impedisce future collisioni di nomi. Scarica il programma di installazione dal pacchetto selezionato, trovato eseguendo una ricerca in un'origine configurata o direttamente da un manifesto. Per impostazione predefinita, la query deve corrispondere senza distinzione tra maiuscole e minuscole all'ID, al nome o al moniker del pacchetto. È possibile usare altri campi passando l'opzione appropriata. Per impostazione predefinita, il comando download scaricherà il programma di installazione appropriato nella cartella Download dell'utente. Scarica il programma di installazione da un pacchetto specifico Directory in cui vengono scaricati i programmi di installazione Download delle dipendenze: Programma di installazione scaricato: {0} {Locked="{0}"} Full path of the downloaded installer. Seleziona il tipo di programma di installazione Download del programma di installazione Non è consentito scaricare il programma di installazione per un'installazione offline successiva. '--module-path allusers' richiede privilegi di amministratore per l'esecuzione. {Locked="--module-path allusers"} Specifica il percorso nel computer locale in cui archiviare i moduli. %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules predefinito {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} Il valore di '--module-path' deve essere 'currentuser', 'allusers', 'default' o un percorso assoluto. {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Abilita la configurazione di Gestione pacchetti Recupera informazioni sugli errori. Dato un numero, l'output conterrà i dettagli sull'errore, incluso il nome del simbolo se si tratta di un errore specifico di WinGet. In base a una stringa, viene eseguita la ricerca di questo valore negli errori specifici di WinGet. Ottenere informazioni sugli errori Valore da cercare nelle informazioni sull'errore Il numero specificato è troppo grande per essere un HRESULT. Codice di errore sconosciuto Errore interno Argomenti della riga di comando non validi L'esecuzione del comando non è riuscita Apertura del manifesto non riuscita Segnale di annullamento ricevuto L'esecuzione di ShellExecute non è riuscita Impossibile elaborare il manifesto. La versione del manifesto è superiore a quella supportata. Aggiornare il client. Download del programma di installazione non riuscito Impossibile scrivere nell'indice; è una versione dello schema superiore L'indice è danneggiato Le informazioni di origine configurate sono danneggiate Il nome dell'origine è già configurato Il tipo di origine non è valido Il file MSIX è un bundle, non un pacchetto Mancano i dati richiesti dall'origine Nessuno dei programmi di installazione è applicabile al sistema corrente L'hash del file del programma di installazione non corrisponde al manifesto Il nome di origine non esiste Il percorso di origine è già configurato con un altro nome Nessun pacchetto trovato Nessuna origine configurata Sono stati trovati più pacchetti corrispondenti ai criteri Nessun manifesto trovato corrispondente ai criteri Non è stato possibile ottenere la cartella pubblica dal pacchetto di origine Il comando richiede i privilegi di amministratore per essere eseguito Il percorso di origine non è sicuro Il client Microsoft Store è bloccato dai criteri L'applicazione Microsoft Store è bloccata dai criteri La funzionalità è attualmente in fase di sviluppo. Può essere abilitato con le impostazioni di WinGet. Non è stato possibile installare l'app Microsoft Store Non è stato possibile eseguire il completamento automatico Non è stato possibile inizializzare il parser YAML È stata rilevata una chiave YAML non valida È stata rilevata una chiave YAML duplicata Operazione YAML non valida Non è stato possibile compilare il documento YAML Stato dell'emettitore YAML non valido Dati YAML non validi Errore LibYAML Convalida del manifesto riuscita con avviso Convalida del manifesto non riuscita Il manifesto non è valido Non è stato trovato alcun aggiornamento applicabile aggiornamento di winget -- tutto completato con errori Non è stato possibile eseguire il controllo di sicurezza per il programma di installazione La dimensione del download non corrisponde alla lunghezza prevista del contenuto Comando di disinstallazione non trovato L'esecuzione del comando di disinstallazione non è riuscita Errore iteratore di interruzione dell'ICU Errore casemap ICU Errore regex ICU Non è stato possibile installare uno o più pacchetti importati Non è stato possibile trovare uno o più pacchetti richiesti Il file Json non è valido Il percorso di origine non è remoto L'origine rest configurata non è supportata Dati non validi restituiti dall'origine rest L'operazione è bloccata da Criteri di gruppo Errore interno dell'API Rest L'URL dell'origine rest non è valido Tipo MIME non supportato restituito dall'API REST Versione del contratto di origine rest non valida I dati di origine sono danneggiati o manomessi Errore nella lettura del flusso I contratti del pacchetto non sono stati concordati Errore durante la lettura dell'input nel prompt La richiesta di ricerca non è supportata da una o più origini L'endpoint dell'API REST non è stato trovato. Impossibile aprire l'origine. Contratti di origine non accettati Le dimensioni dell'intestazione superano il limite consentito di 1024 caratteri. Ridurre le dimensioni e riprovare. File di risorse mancante L'esecuzione dell'installazione MSI non è riuscita Gli argomenti per msiexec non sono validi Non è stato possibile aprire una o più origini Non è stato possibile convalidare le dipendenze Uno o più pacchetti mancanti Colonna tabella non valida La versione di aggiornamento non è più recente della versione installata La versione dell'aggiornamento è sconosciuta e l'override non è specificato Errore di conversione ICU Non è stato possibile installare il pacchetto portabile Il volume non supporta i punti di reparse Esiste già un pacchetto portabile di un'origine diversa. Impossibile creare il collegamento simbolico. Il percorso punta a una directory. Impossibile eseguire il programma di installazione da un contesto di amministratore. Non è stato possibile disinstallare il pacchetto portabile Non è stato possibile convalidare i valori DisplayVersion rispetto all'indice. Uno o più argomenti non sono supportati. I caratteri null incorporati non sono consentiti in SQLite Impossibile trovare il programma di installazione annidato nell'archivio. Impossibile estrarre l'archivio. È stato specificato un percorso di file relativo non valido per il programma di installazione annidato. Il certificato del server non corrisponde ad alcun valore previsto. È necessario specificare il percorso di installazione. L'analisi del malware dell'archivio non è riuscita. È stata trovata almeno una versione del pacchetto installato. Esiste già un pin per il pacchetto. Nessun pin per il pacchetto. Impossibile aprire il database dei pin. Installazione di una o più applicazioni non riuscita Non è stato possibile disinstallare una o più applicazioni Una o più query non hanno restituito esattamente una corrispondenza Il pacchetto contiene un PIN che impedisce l'aggiornamento. Il pacchetto attualmente installato è il pacchetto stub Segnale di arresto dell'applicazione ricevuto Non è stato possibile scaricare le dipendenze del pacchetto. Non è stato possibile scaricare il pacchetto. Il download per l'installazione offline non è consentito. Un servizio richiesto è occupato o non disponibile. Riprova più tardi. Il GUID specificato non corrisponde a uno stato di ripresa valido. La versione corrente del client non corrisponde alla versione client dello stato salvato. I dati sullo stato di ripresa non sono validi. Impossibile aprire il database del checkpoint. È stato superato il limite massimo di ripresa. Informazioni di autenticazione non valide. Metodo di autenticazione non supportato. Autenticazione non riuscita. Impossibile eseguire l'autenticazione. È richiesta l'autenticazione interattiva. Impossibile eseguire l'autenticazione. Annullato dall'utente. Autenticazione non riuscita. L'account autenticato non è l'account desiderato. L'applicazione è attualmente in esecuzione. Uscire dall'applicazione, quindi riprovare. È già in corso un'altra installazione. Riprova più tardi. È in uso uno o più file. Uscire dall'applicazione, quindi riprovare. Nel sistema manca una dipendenza per questo pacchetto. Non c'è più spazio nel PC. Liberare spazio, quindi riprovare. Memoria insufficiente per l'installazione. Chiudi le altre applicazioni, quindi riprova. Questa applicazione richiede la connettività Internet. Connettiti a una rete e riprova. Errore dell'applicazione durante l'installazione. Contattare il supporto tecnico. Riavvia il PC per completare l'installazione.. Installazione non riuscita. Riavvia il PC e riprova. Il PC verrà riavviato per completare l'installazione. Hai annullato l’installazione. È già installata un'altra versione di questa applicazione. È già installata una versione successiva di questa applicazione. I criteri dell'organizzazione impediscono l'installazione. Contattare l'amministratore. Non è stato possibile installare le dipendenze del pacchetto. L'applicazione è attualmente utilizzata da un'altra applicazione. Parametro non valido. Pacchetto non supportato dal sistema. Il programma di installazione non supporta l'aggiornamento di un pacchetto esistente. Installazione non riuscita a causa di un errore del programma di installazione personalizzato. La voce App e funzionalità per il pacchetto non è stata trovata. Il percorso di installazione non è applicabile. Il percorso di installazione non è stato trovato. L'hash del file esistente non corrisponde. File non trovato. Il file è stato trovato ma l'hash non è stato controllato. Impossibile accedere al file. Il file di configurazione non è valido. Sintassi YAML non valida. Un campo di configurazione ha un tipo non valido. La versione della configurazione è sconosciuta. Errore durante l'applicazione della configurazione. La configurazione contiene un identificatore duplicato. Nella configurazione manca una dipendenza. La configurazione ha una dipendenza non soddisfatta. Un'asserzione per l'unità di configurazione non è riuscita. La configurazione è stata ignorata manualmente. L'utente ha rifiutato di continuare l'esecuzione. Il grafico delle dipendenze contiene un ciclo che non può essere risolto. La configurazione contiene un valore di campo non valido. Nella configurazione manca un campo. Errore di alcune unità di configurazione durante il test dello stato. Lo stato della configurazione non è stato testato. L'unità di configurazione non è stata installata. Impossibile trovare l'unità di configurazione. Sono state trovate più corrispondenze per l'unità di configurazione; specificare il modulo per selezionare quello corretto. Errore dell'unità di configurazione durante il tentativo di ottenere lo stato corrente del sistema. Errore dell'unità di configurazione durante il tentativo di testare lo stato corrente del sistema. Errore dell'unità di configurazione durante il tentativo di applicare lo stato desiderato. Il modulo per l'unità di configurazione è disponibile in più località con la stessa versione. Caricamento del modulo per l'unità di configurazione non riuscito. L'unità di configurazione ha restituito un risultato imprevisto durante l'esecuzione. Un'unità contiene un'impostazione che richiede la radice di configurazione. Operazione non supportata dal processore di configurazione. Non disponibile Le dipendenze delle funzioni di Windows sono state abilitate Il caricamento del modulo per l'unità di configurazione non è riuscito perché richiede privilegi di amministratore per l'esecuzione. Un'unità contiene un'impostazione che richiede la radice di configurazione. Il caricamento del modulo per l'unità di configurazione non è riuscito perché richiede privilegi di amministratore per l'esecuzione. Riprende l'esecuzione di un comando salvato in precedenza passando l'identificatore univoco del comando salvato. Consente di riprendere un comando eseguito che potrebbe essere stato terminato a causa di un riavvio. Riprende l'esecuzione di un comando salvato in precedenza. Identificatore univoco dello stato salvato da riprendere La ripresa dello stato da una versione client diversa non è supportata: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. Lo stato di ripresa non esiste: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. Non sono stati trovati dati nello stato di ripresa. Questo comando non supporta la ripresa. Consente un riavvio, se applicabile Avvio del riavvio per completare l'operazione... Non è stato possibile avviare un riavvio. L'operazione di ripresa supera il limite consentito di {0} curriculum. Per riprendere manualmente, eseguire il comando '{1}'. {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. Ignora il limite per la ripresa di uno stato salvato Schema Uri non supportato: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. Uri non ben formato: {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. Non è stato possibile analizzare {0} contenuto delle impostazioni dell'unità di configurazione o il contenuto delle impostazioni è vuoto. {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. argomento obbligatorio mancante nell'unità di configurazione {0}: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. {0}'unità di configurazione non contiene l'argomento consigliato: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. L'unità di configurazione WinGetSource è in conflitto con un'origine nota: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. L'unità di configurazione WinGetSource asserisce su un'origine di terze parti: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage dichiara sia UseLatest che Version. ID pacchetto: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} L'unità di configurazione WinGetPackage asserisce un pacchetto da un'origine di terze parti. ID pacchetto: {0}; Origine: {1} {Locked="WinGetPackage,{0},{1}"} Il pacchetto dell'unità di configurazione WinGetPackage dipende da un'origine di terze parti non configurata in precedenza. ID pacchetto: {0}; Origine: {1} {Locked="WinGetPackage,{0},{1}"} Il pacchetto dell'unità di configurazione WinGetPackage dipende da un'origine di terze parti. È consigliabile dichiarare la dipendenza nella sezione dependsOn uni. ID pacchetto: {0}; Origine: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} Impossibile convalidare il pacchetto di unità di configurazione WinGetPackage. Apertura dell'origine non riuscita. ID pacchetto: {0}; Origine: {1} {Locked="WinGetPackage,{0},{1}"} Impossibile convalidare il pacchetto di unità di configurazione WinGetPackage. Pacchetto non trovato. ID pacchetto: {0} {Locked="WinGetPackage,{0}"} Impossibile convalidare il pacchetto di unità di configurazione WinGetPackage. Sono stati trovati più pacchetti. ID pacchetto: {0} {Locked="WinGetPackage,{0}"} Impossibile convalidare il pacchetto di unità di configurazione WinGetPackage. La versione del pacchetto non è stata trovata. ID pacchetto: {0}; Versione {1} {Locked="WinGetPackage,{0},{1}"} Pacchetto di unità di configurazione WinGetPackage specificato con una versione specifica, ma è disponibile una sola versione del pacchetto. ID pacchetto:{0}; Versione: {1} {Locked="WinGetPackage,{0},{1}"} Impossibile convalidare il pacchetto di unità di configurazione WinGetPackage. ID pacchetto: {0} {Locked="WinGetPackage,{0}"} Specifica la preferenza per la finestra di autenticazione (silent, silentPreferred o interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. Specificare l'account da usare per l'autenticazione Non è stato possibile aggiungere l'origine. Questa versione winget non supporta il metodo di autenticazione dell'origine. Provare a eseguire l'aggiornamento alla versione più recente winget. {Locked="winget"} L'origine {0} richiede l'autenticazione. La richiesta di autenticazione può essere visualizzata quando necessario. Le informazioni autenticate verranno condivise con l'origine per l'autorizzazione di accesso. {Locked="{0}"} Ripristina il pacchetto selezionato, trovato eseguendo una ricerca nell'elenco dei pacchetti installati o direttamente da un manifesto. Per impostazione predefinita, la query deve corrispondere senza distinzione tra maiuscole e minuscole all'ID, al nome o al moniker del pacchetto. È possibile usare altri campi passando l'opzione appropriata. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Ripristina il pacchetto selezionato Impossibile trovare il comando di ripristino per questo pacchetto. Contattare l'editore del pacchetto per ottenere supporto. La tecnologia del programma di installazione in uso non corrisponde alla versione attualmente installata. Comando di ripristino non trovato. La tecnologia del programma di installazione in uso non supporta il ripristino. L'operazione di ripristino è stata completata. Riparazione abbandonata Avvio del ripristino del pacchetto in corso... Non è stato possibile riparare il pacchetto Microsoft Store. Codice di errore: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. L'operazione di ripristino non è riuscita. L'operazione di ripristino non è applicabile. Non sono disponibili versioni del pacchetto corrispondenti dalle origini configurate. La configurazione di sistema corrente non supporta il ripristino di questo pacchetto. La tecnologia del programma di installazione in uso non supporta il ripristino. Impossibile ripristinare il pacchetto installato per l'ambito utente durante l'esecuzione con privilegi di amministratore. Le operazioni di ripristino che richiedono privilegi di amministratore non sono consentite nei pacchetti installati nell'ambito utente. Ripristino non riuscito con codice di uscita: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. La connessione SQLite è stata terminata per evitare il danneggiamento. Disinstalla tutte le versioni Versione su cui intervenire Sono installate più versioni di questo pacchetto. Affinare la ricerca, passare l'argomento '--version' per selezionarne uno oppure passare il flag '--all-versions' per disinstallarli tutti. {Locked="--version,--all-versions"} Abilita le opzioni della riga di comando del proxy Gestione pacchetti Windows Describes a Group Policy that can enable the use of the --proxy option to set a proxy Imposta un proxy da usare per questa esecuzione Disabilita l'uso del proxy per questa esecuzione Impossibile reimpostare {0}. Questa impostazione è controllata dai criteri. Per ulteriori informazioni, contattare l'amministratore di sistema. {Locked="{0}"} The value will be replaced with the feature name Reimposta l'impostazione di amministrazione '{0}’. {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. Impossibile impostare {0}. Questa impostazione è controllata dai criteri. Per ulteriori informazioni, contattare l'amministratore di sistema. {Locked="{0}"} The value will be replaced with the feature name Esegui l'impostazione di amministrazione '{0}' su '{1}'. {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced Nome dell'impostazione da modificare Valore da impostare per l'impostazione. Ripristina il valore predefinito di un'impostazione di amministrazione. Ripristina il valore predefinito di un'impostazione di amministrazione. Imposta il valore di un'impostazione di amministrazione. Imposta il valore di un'impostazione di amministrazione. Esclude un'origine dall'individuazione se non specificata Esclude un'origine dall'individuazione (vero o falso) Contenuti espliciti Livello di attendibilità dell'origine (nessuno o attendibile) Livello di trust Non è possibile ottenere il catalogo pacchetti Microsoft Store. Non sono stati trovati pacchetti Microsoft Store applicabili dal catalogo pacchetti Microsoft Store. Non è possibile ottenere informazioni sul download del pacchetto Microsoft Store. Non sono stati trovati pacchetti Microsoft Store applicabili per il download. Non è stato possibile recuperare la licenza del pacchetto Microsoft Store. Non sono stati trovati pacchetti Microsoft Store applicabili. La verifica dell'hash del pacchetto Microsoft Store è stata completata Mancata corrispondenza dell'hash del pacchetto Microsoft Store Pacchetto Microsoft Store scaricato: {0} {Locked="{0}"} Full path of the downloaded package. Download del pacchetto Microsoft Store non riuscito: {0} {Locked="{0}"} Package name. Download del pacchetto Microsoft Store completato Download dei pacchetti principali da Microsoft Store... Download dei pacchetti di dipendenze da Microsoft Store... Recupero delle informazioni di download del pacchetto Microsoft Store Non è possibile recuperare le informazioni sul download del pacchetto Microsoft Store Recupero della licenza del pacchetto Microsoft Store Licenza del pacchetto Microsoft Store salvata: {0} {Locked="{0}"} License file full path. Non è possibile recuperare la licenza del pacchetto Microsoft Store Il download del pacchetto Microsoft Store non supporta l'argomento --rename. Il pacchetto Microsoft Store userà i nomi forniti dal catalogo Microsoft Store. {Locked="--rename"} Microsoft Store download del pacchetto richiede l'autenticazione con ID Microsoft Entra. La richiesta di autenticazione può essere visualizzata quando necessario. Le informazioni autenticate verranno condivise con servizi Microsoft per l'autorizzazione di accesso. Per Microsoft Store gestione delle licenze dei pacchetti, l'account ID Microsoft Entra deve essere membro dell'amministratore globale, dell'amministratore utente o dell'amministratore delle licenze. {Locked="Global Administrator,User Administrator,License Administrator"} Ignora il recupero della licenza offline del pacchetto Microsoft Store Seleziona la piattaforma di destinazione Aggiunta del file di configurazione: {0} {Locked="{0}"} Esportazione completata Recupero delle impostazioni di configurazione in corso... È necessario specificare almeno --packageId e/o --module con --resource. In alternativa, usare --all per esportare tutte le configurazioni dei pacchetti. {Locked="--packageId,--module, --resource, --all"} Gli argomenti --packageId, --module e --resource non possono essere utilizzati insieme a --all. {Locked="--packageId,--module, --resource, --all"} Esporta le risorse di configurazione in un file di configurazione. Se usato con --all, esporta tutte le configurazioni dei pacchetti. Se usato con --packageId, esporta una risorsa WinGetPackage dell'ID pacchetto specificato. Se usato con --module e --resource, ottiene le impostazioni della risorsa ed esporta la risorsa nel file di configurazione. Se il file di configurazione di output esiste già, aggiunge le risorse di configurazione esportate. {Locked="WinGetPackage,--packageId,--module, --resource"} Esporta le risorse di configurazione in un file di configurazione. Modulo della risorsa da esportare. Identificatore del pacchetto da esportare. Risorsa di configurazione da esportare. Esporta tutte le configurazioni del pacchetto. Non è possibile ottenere le proprietà dell'unità di configurazione. Esportazione della configurazione non riuscita. Configura {0} {Locked="{0}"} Installa {0} {Locked="{0}"} Alcuni dati presenti nel file di configurazione sono stati troncati per questo output; controllare il contenuto completo del file. <questo valore è stato troncato; controlla il contenuto del file per il testo completo> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. Mostra i dettagli di alto livello per le configurazioni applicate al sistema. Questi dati possono quindi essere usati con `configure` comandi per ottenere maggiori dettagli. {Locked="`configure`"} Mostra la cronologia di configurazione Non sono presenti configurazioni nella cronologia. Seleziona elementi dalla cronologia Impossibile trovare una singola configurazione corrispondente ai dati forniti. Specificare il nome completo o parte dell'identificatore che corrisponda senza ambiguità alla configurazione desiderata. Rimuovi l'elemento dalla cronologia Prima applicazione Column header for date values indicating when a configuration was first applied to the system. Identificatore Nome Origine Percorso Non è possibile trovare la configurazione specificata. Operazione completata The state of processing an item. In corso The state of processing an item. In sospeso The state of processing an item. Sconosciuto The state of processing an item. Stato di configurazione monitoraggio. As in "to monitor the status of a configuration being applied". Completato The state of processing an item. In corso The state of processing an item. In sospeso The state of processing an item. Ignorato The state of processing an item. Sconosciuto The state of processing an item. Applicazione avviata When the configuration application started. Applicazione terminata When the configuration application ended. Risultato Dettagli Stato The state of processing an item. Unità Il pacchetto non è compatibile con la versione o la piattaforma Windows corrente. Non visualizzare i dettagli della configurazione iniziale quando possibile È possibile scaricare più pacchetti Microsoft Store destinati a piattaforme e architetture diverse. --platform, --architecture può essere usato per filtrare i pacchetti. {Locked="--platform--architecture"} Non è stato possibile recuperare Microsoft Store licenza del pacchetto. L'account ID Microsoft Entra non è membro di Amministratore globale, Amministratore utenti o Amministratore licenze. Usare --skip-license per ignorare il recupero Microsoft Store licenza del pacchetto. {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} Il pacchetto Microsoft Store non supporta il download. Il pacchetto Microsoft Store non supporta il download. Non è stato possibile recuperare Microsoft Store licenza del pacchetto. L'account id Microsoft Entra non dispone dei privilegi necessari. Il parametro non può essere passato oltre il limite di integrità. Programma di installazione a zero byte scaricato; verificare che la connessione di rete funzioni correttamente. Programma di installazione a zero byte scaricato; verificare che la connessione di rete funzioni correttamente. Gestire i tipi di carattere Gestire i tipi di carattere con i sottocomandi. I tipi di carattere possono essere installati, aggiornati o disinstallati in modo simile ai pacchetti. Famiglia Facce "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' Filtra i risultati per cognome Viso "Face" represents the typeface of a font family such as 'Bold' or 'Italic' Percorsi Nessun tipo di carattere installato corrisponde ai criteri di input. Elencare i tipi di carattere installati Elenca tutti i tipi di carattere installati, tutti i file di tipi di carattere o i dettagli completi di un tipo di carattere specifico. Versione Moduli di configurazione PowerShell Modules that are used for the Configuration feature Il programma di installazione del pacchetto richiede l'autenticazione. La richiesta di autenticazione può essere visualizzata quando necessario. Le informazioni autenticate verranno condivise con l'URL di download del programma di installazione. Non è stato possibile scaricare il programma di installazione. Questa versione winget non supporta il metodo di autenticazione per il download del programma di installazione. Provare a eseguire l'aggiornamento alla versione più recente winget. {Locked="winget"} Download del programma di installazione non riuscito. Autenticazione non riuscita. Specifica il percorso del processore di configurazione Comandi delle risorse DSC v3 DSC stands for "Desired State Configuration". It should already have a locked translation. I comandi secondari implementano le risorse Desired State Configuration (DSC) v3 per la configurazione di winget e pacchetti. Recupera stato della risorsa Imposta stato della risorsa Descrivi le modifiche di stato necessarie Testa stato della risorsa Elimina stato della risorsa Recupera tutte le istanze di stato Convalida contenuti del gruppo Risolvi stato esterno Esegui scheda Recupera schema della risorsa Recupera manifesto della risorsa Gestisci stato del pacchetto Gestisci pacchetti tramite WinGet. Indica se un'istanza deve esistere o meno. Indica se un'istanza si trova nello stato desiderato. Identificatore pacchetto. Origine pacchetto. Versione del pacchetto. Metodo per la corrispondenza dell'identificatore con un pacchetto. Indica che deve essere installata la versione più recente disponibile del pacchetto. Modalità di installazione da utilizzare, se necessario. Ambito di destinazione del pacchetto. Indica se accettare contratti per origini e pacchetti. Gestisci file delle impostazioni utente Gestisci le impostazioni utente di WinGet. Contenuto del file JSON delle impostazioni. Azione utilizzata per applicare le impostazioni. Il valore è registrato per la correlazione Gestisci configurazione di origine Gestisci le origini di WinGet. Nome da utilizzare per l'origine. Argomento per l'origine. Tipo di origine. Livello di attendibilità dell'origine. Indica se l'origine è inclusa quando le chiamate non specificano un'origine. Applicazione dell'unità di configurazione in corso... Esportazione dell'unità di configurazione in corso... Recupero dei processori dell'unità di configurazione in corso... Assicurati che sia presente il modulo necessario per l'esportazione [{0}] {Locked="{0}"} Non è stato possibile testare o acquisire il modulo richiesto. Le impostazioni correlate non verranno esportate. Esporta [{0}] {Locked="{0}"} Esportazione della risorsa non riuscita. Impossibile ottenere i processori di unità. Le impostazioni dei singoli pacchetti non verranno esportate. Directory in cui devono essere scritti i risultati Pacchetto Desired State Configuration non trovato nel sistema. Installazione del pacchetto in corso... Non è stato possibile installare Desired State Configuration pacchetto. Installare il pacchetto manualmente o specificare il percorso per dsc.exe tramite --processor-path argomento. {Locked="dsc.exe","--processor-path"} Oggetto contenente le impostazioni di amministratore e i relativi valori. Gestisci impostazioni amministratore Gestisci impostazioni amministratore di WinGet. Reimposta tutte le impostazioni di amministrazione Reimpostazione di tutte le impostazioni di amministrazione. Informazioni MCP MCP stands for Model Context Protocol and should probably remain as-is Informazioni MCP (Model Context Protocol) per il Gestione pacchetti Windows. Per configurare manualmente il server MCP Gestione pacchetti Windows con il client MCP, usare il frammento JSON seguente nell'oggetto `servers`: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. Abilita Gestione pacchetti Windows server MCP Versione del sistema operativo di destinazione Impossibile installare uno o più tipi di carattere. Il file dei tipi di carattere non è supportato e non può essere installato. Uno o più tipi di carattere nel pacchetto di tipi di carattere non sono supportati e non possono essere installati. Installazione del tipo di carattere non riuscita; pulizia. Tipo di carattere già installato. ID pacchetto WinGet supportato Titolo Il file del tipo di carattere non è stato trovato. Disinstallazione del carattere non riuscita. Il tipo di carattere potrebbe non essere in uno stato valido. Provare a disinstallare dopo un riavvio. Convalida del tipo di carattere non riuscita. Rollback del tipo di carattere non riuscito. Il tipo di carattere potrebbe non essere in uno stato valido. Provare a disinstallare dopo un riavvio. Mostra informazioni dettagliate sul file del tipo di carattere. La convalida del pacchetto del tipo di carattere non è riuscita. Il tentativo di rollback per l'installazione del tipo di carattere non riuscita non è riuscito. Potrebbe essere necessario riavviare il sistema per disinstallare correttamente il tipo di carattere. Disinstallazione del pacchetto di caratteri non riuscita. Questo errore è spesso dovuto al fatto che i tipi di carattere sono in uso dal sistema o da un'applicazione. La disinstallazione potrebbe essere riuscita dopo il riavvio del computer. OK "OK" means the font is in a good healthy state Danneggiata "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. Stato Sconosciuto Il pacchetto di tipi di carattere è già installato. Visualizza informazioni dettagliate sui pacchetti Providing this argument causes the CLI to output additional details about installed application packages. Canale: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). Identificatore locale: Precedes a value that is the unique identifier for the installed package on the local system. Nome della famiglia di pacchetti: Precedes a value that is the APPX/MSIX package family name of the installed package. Codice prodotto: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. Codice aggiornamento: Precedes a value that is the MSI Upgrade Code for the installed package. Ambito installazione: Precedes a value that is the scope of the installation of the package (ex. user, machine). Architettura installata: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). Impostazioni locali installate: Precedes a value that is the locale of the installed package. Posizione installata: Precedes a value that is the directory path to the installed package. Origine: Precedes a value that names the package source where the installed package originated from. Aggiornamenti disponibili: Precedes a list of package upgrades available for the installed package. Categoria programma di installazione: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). Valore precedente Column title for listing edit changes. Nuovo valore Column title for listing the new value. ================================================ FILE: Localization/Resources/ja-JP/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/ja-JP/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 隣接エイリアスがフラグではありません: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). 隣接フラグエイリアスが見つかりません: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). 次の引数を使用できます。 Message displayed to inform the user about the available command line arguments. 次のコマンド エイリアスを使用できます: Message displayed to inform the user about the available command line alias arguments. 使用できるコマンドは次のとおりです: Title displayed to inform the user about the available commands. 利用可能 As in "a new version is available to upgrade to". 次のオプションを使用できます。 Message displayed to inform the user about the available options. 次のサブコマンドを使用できます。 Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} アップグレードを利用できます。 {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. 指定されたチャンネルを使用します。既定値は一般向けです コマンド Label displayed for a command to give the software. コマンドによる結果のフィルター処理 Description message displayed to inform the user about filtering the search results by a package command. 完了するための完全なコマンド ライン このコマンドを実行するには、管理者権限が必要です。 このコマンドは、状況依存のコマンド ラインの完了を要求するために使用できます。コマンド ライン、カーソル位置、および完了する単語が渡されます。出力は、入力に基づいた一連の潜在的な値で、1 行に 1 つの可能な値が入ります。 状況依存のコマンド ラインの完了を有効にする 指定した数を超える結果を表示しない (1 ~ 1000) 完了 Label displayed when an operation completes or is done executing. 完全一致を使用してパッケージを検索 Description message displayed to inform the user about finding an application package using an exact matching criteria. デモを目的とした試験的引数 このコマンドは、試験的な機能を実装する方法の例です。有効にするには、"winget settings"へと移動し、experimentalCmdまたはexperimentalArg機能を有効にします。 {Locked="winget settings"} 試験的な機能の例 何も指定されていないときに位置引数が見つかりました: '{0}' {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. この機能は進行中の作業であり、今後大幅に変更または完全に削除される可能性があります。これを有効にするには、設定 ('winget settings') を編集して試験的な機能を含めます: '{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. 試験的な機能の状態を表示します。試験的な機能は、"winget settings" を使用してオンにすることができます。 {Locked="winget settings"} 試験的な機能の状態を表示 無効 有効 機能 リンク 次の試験的な機能が進行中です。 これらの設定は、設定ファイル'winget settings'を使用して構成できます。 {Locked="winget settings"} プロパティ 状態 ハッシュするファイル フラグ引数に隣接する値を含めることはできません: '{0}' {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. マニフェストへのエントリに適したローカル ファイルのハッシュを計算します。また、MSIX パッケージの署名ファイルのハッシュを計算して、ストリーミング インストールを有効にすることもできます。 インストーラー ファイルをハッシュするヘルパー 選択したコマンドに関するヘルプを表示 特定のコマンドの詳細については、そのコマンドにヘルプ引数を渡します。 その他のヘルプについては、次を参照してください: {0} {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. ID で結果をフィルター処理 警告出力を非表示にする このアプリケーションは所有者からライセンス供与されます。 Microsoft はサードパーティのパッケージに対して責任を負わず、ライセンスも付与しません。 このパッケージは Microsoft Store から提供されています。winget は、現在のユーザーに代わって Microsoft Store からパッケージを取得する必要がある場合があります。 {Locked="winget"} 構成されたソースを探して見つけるかマニフェストから直接見つけて、選んだパッケージをインストールします。既定では、そのクエリはパッケージの id、name、または moniker と大文字小文字の区別なく一致する必要があります。その他のフィールドは、適切なオプションを渡すと使えます。既定では、install コマンドは、パッケージのインストール済み状態を確認し、アップグレードがあればそれを実行しようとします。直接インストールするには、--force を指定してオーバーライドします。 {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 指定されたパッケージをインストール インストーラーのハッシュが一致しません; これは、管理者として実行している場合は上書きできません インストーラーのハッシュが一致しません。--ignore-security-hash のため続行しています {Locked="--ignore-security-hash"} インストーラーのハッシュが一致しません。このチェックをオーバーライドするには、--ignore-security-hash を使用してください {Locked="--ignore-security-hash"} インストーラーハッシュが正常に検証されました インストールが完了しました パッケージのインストールを開始しています... インストーラーのハッシュのチェックが失敗しても無視します ローカル マニフェストからのアーカイブの種類のパッケージのインストールの一環として実行されたマルウェア スキャンを無視します 対話式のインストールが要求されます。ユーザーの入力が必要になる場合があります 引数のエイリアスが現在のコマンドに対して認識されませんでした: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). 無効な引数指定子: '{0}' {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). 引数名が現在のコマンドに対して認識されませんでした: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Winget ディレクトリ Header for a table detailing the directories Winget uses for key operations like logging and portable installs 使用するロケール (BCP47 形式) {Locked="BCP47"} 使用許諾契約 リンク Links to different webpages list コマンドは、システムにインストールされているパッケージと、アップグレードを適用可能かどうかを表示します。search コマンドとよく似ていて、出力をフィルター処理するためのその他のオプションを指定できます。 {Locked="list","search"} インストール済みパッケージを表示する インストール先 (サポートされている場合) ログの場所 (サポートされている場合) Copyright (c) Microsoft Corporation. All rights reserved. ホーム ページ The primary webpage for the software パッケージのマニフェストのパス マニフェストの検証に失敗しました。 マニフェストの検証は成功しました。 マニフェストの検証は成功しましたが、警告があります。 引数の値は必須ですが、見つかりませんでした: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. モニカーで結果をフィルター処理 入力ファイルは msix として扱われます。署名された場合、署名ハッシュが提供されます MSIX 署名ハッシュを計算できませんでした。 特定のアプリがポリシーによってブロックされているため、Microsoft Store パッケージをインストールまたはアップグレードできませんでした Microsoft Store パッケージをインストールまたはアップグレードできませんでした。エラー コード: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Microsoft Store クライアントがポリシーによってブロックされているため、Microsoft Store パッケージをインストールまたはアップグレードできませんでした パッケージ取得の確認/要求... 複数のインストール済みパッケージが一致する入力条件を検出しました。入力内容を修正してください。 複数のパッケージが入力条件に一致しました。入力内容を修正してください。 名前で結果をフィルター処理 該当するインストーラーが見つかりません。詳細については、ログを参照してください。 現在、試験的な機能はありません。 入力条件に一致するインストール済みのパッケージが見つかりませんでした。 入力条件に一致するパッケージが見つかりませんでした。 VirtualTerminal 表示を無効にする {Locked="VirtualTerminal"} 既定のログの場所を開く オプション Options to change how a command works インストーラーに渡される引数を上書き パッケージ: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. 申し訳ございません。これを行うことを忘れました… コマンド ライン内のカーソルの位置 プライバシーに関する声明 パッケージの検索に使用されるクエリ 進行状況は虹の色を表示します 必須の引数が指定されていません: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. 既定の色としての進行状況の表示 構成されたソースからパッケージを検索します。 アプリの基本情報を見つけて表示 ID Abbreviation of Identifier. 一致 名前 ソース 結果制限により、エントリがさらに切り捨てられました バージョン 次のエラーが検出されたため、設定を検証しています: 既定の json テキスト エディターで設定を開きます。エディターが構成されていない場合は、メモ帳で設定を開きます。使用可能な設定については、https://aka.ms/winget-settings を参照してください。このコマンドを使用して、--enable または --disable 引数を指定して管理者設定を設定することもできます。 {Locked="--enable"} {Locked="--disable"} 設定を開くか、管理者設定を設定する 設定の読み込み中に予期しないエラーが発生しました。'settings' コマンドを実行して、設定を確認してください。 {Locked="'settings'"} チャネル 特定のパッケージの情報を表示します。既定では、クエリはパッケージの ID、名前、モニカーに大文字小文字の区別なく一致する必要があります。その他のフィールドは、適切なオプションを渡すことで使用することができます。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. パッケージに関する情報を表示します バージョン サイレント インストールを要求 単一の - の後に出現できるのは、1 文字の別名のみです: '{0}' {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. 指定された名前のソースは既に存在し、別の場所を参照しています: 別の名前のソースが、既に次の場所を参照しています: 指定された名前のソースは既に存在し、同じ場所を参照しています: ソースを追加しています: 新しいソースを追加します。ソースは、パッケージを検出してインストールするためのデータを提供します。安全な場所として信頼できる場合にのみ、新しいソースを追加してください。 新しいソースを追加 ソースに与えられた引数 指定されたリソースを使用してパッケージを検索 サブコマンドでソースを管理します。ソースは、パッケージを検出してインストールするためのデータを提供します。安全な場所として信頼できる場合にのみ、新しいソースを追加してください。 パッケージのソースの管理 既存のソースのプロパティを編集します。ソースは、パッケージを検出してインストールするためのデータを提供します。 ソースのプロパティを編集します ソースの編集中: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. '{0}' という名前のソースは既に適切な状態です。 {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. 引数 Value given to source. 現在のすべてのソース、または特定のソースの完全な詳細を一覧表示します。 現在のソースを一覧表示 データ Data stored by the source. フィールド The name of a piece of information about a source. 名前 The name of the source. ID The source's unique identifier. 次の名前のソースが見つかりませんでした: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. 構成されたソースがありません。 種類 The kind of source. 更新されました The last time the source was updated. なし The source has never been updated. The value of information about a source. ソース名 ソースを開くときに失敗しました;問題が解決しない場合は、'source reset' コマンドを試してください。 {Locked="source reset"} 定義済みのソースを開けませんでした; winget メンテナに報告してください。 {Locked="winget"} すべてのソースを削除しています... 特定のソースを削除します。 現在のソースを削除 ソースを削除しています: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. すべてのソースをリセットしています... このコマンドは既存のソースを削除し、ローカル データを潜在的に残します。引数がないと、すべてのソースが削除され、既定値が追加されます。名前付きソースが提供されている場合、そのソースのみが削除されます。 ソースをリセット ソースのリセットの強制 --force オプションを指定すると、次のソースがリセットされます: {Locked="--force"} ソースをリセットしています: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. ソースのタイプ すべてのソースを更新しています... すべてのソースを更新するか、特定のソースのみを更新します。 現在のソースを更新 ソースを更新しています: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. タグで結果をフィルター処理 Winget をお使いいただきありがとうございます サード パーティに関する通知 WinGet コマンド ライン ユーティリティを使用すると、コマンド ラインからアプリケーションやその他のパッケージをインストールできます。 ツールの一般情報を表示 ツールのバージョンを表示 引数に指定された回数が許容回数を超えています: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. 複数の実行動作引数が指定されました: '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). コマンドの実行中に予期しないエラーが発生しました: アップグレード中に以前のバージョンのパッケージをアンインストールする 認識されないコマンド: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. 可能な場合はインストール済みのすべてのパッケージをアップグレードして最新状態にします 適用可能なアップグレードは見つかりませんでした。 構成されたソースでは新しいパッケージ バージョンを使用できますが、システムまたは要件には適用されません。 利用可能なアップグレードが見つかりませんでした。 構成されたソースから入手できる新しいパッケージ バージョンはありません。 インストールされたパッケージ リスト、またはマニフェストから直接検索し、選択されたパッケージをアップグレードします。既定では、クエリはパッケージの ID、名前、モニカーに大文字小文字の区別なく一致する必要があります。その他のフィールドは、適切なオプションを渡すことで使用することができます。引数が指定されていない場合は、利用可能なアップグレードを含むパッケージが表示されます id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 利用可能なアップグレードの表示と実行 使用法: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. 厳密なガイドラインのセットを使用してマニフェストを検証します。これは、リポジトリに送信する前にマニフェストを確認できるようにすることを目的としています。 マニフェスト ファイルを検証 検証対象のマニフェストのパス WinGet の詳細ログを有効にする 入力ファイルが有効な署名された MSIX であることを確認してください。 指定されたバージョンを使用します。既定値は最新バージョンです パッケージの利用可能なバージョンを表示 完了する前に提供された値が要求されます 一致するバージョンが見つかりません: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. 指定された値に一致するソースがありません: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. 構成されたソースは次のとおりです: ソースが定義されていません。[source add]で追加するか、[source reset]で規定値にリセットします {Locked="source add","source reset"} 見つかりました パスはディレクトリです: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. ファイルが存在しません: {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. ローカル マニフェストと検索クエリ引数の両方が指定されています ログ Label displayed for diagnostic files containing information about the application use. インストーラーはポリシーによってブロックされています インストーラーのセキュリティチェックに失敗しました ウイルス対策製品がインストーラーに感染を報告します ソースを更新できませんでした: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. インストールされているパッケージリスト、またはマニフェストから検索し、選択したパッケージをアンインストールします。既定では、クエリはパッケージのID、名前、モニカーと大文字と小文字の区別なく一致する必要があります。その他のフィールドは、適切なオプションを渡すことで使用することができます。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 指定されたパッケージをアンインストール パッケージのアンインストールを開始しています... 正常にアンインストールされました winget は、このパッケージのアンインストールコマンドを見つけることができません。サポートについては、パッケージの発行元に問い合わせてください。 {Locked="winget"} アンインストールが中止されました アンインストールは次の終了コードで失敗しました: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. インストールされているパッケージのリストをエクスポート ファイルに一覧表示されているすべてのパッケージをインストールします。 ファイル中のすべてのパッケージをインストール 結果が書き込まれるファイル インストールするパッケージを記述したファイル 指定したソースからパッケージをエクスポートする インストールされているパッケージの一覧をファイルに書き込みます。その後、パッケージを import コマンドを使用してインストールできます。 {Locked="import"} 1つまたは複数のインポートされたパッケージをインストールできませんでした インポートに必要なソースがインストールされていません: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. インストールされているパッケージはどのソースからも利用できません: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. インストールされているパッケージのバージョンは、どのソースからも使用できません: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. インポート ファイルにパッケージが見つかりません JSON ファイルが無効です パッケージは既にインストールされています: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. 使用できないパッケージを無視する エクスポート ファイルにパッケージ バージョンを含める インポート ファイルからパッケージ バージョンを無視する パスが存在しません: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. JSON ファイルでは、認識されるスキーマが指定されていません。 インストールの範囲 (user または machine) の選択 {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. '{0}' 引数に指定された値が無効です。有効な値: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. この操作はグループ ポリシーによって無効にされています: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. 追加の Windows アプリ インストーラー ソースを有効にする Windows アプリ インストーラーの許可されたソースを有効にする Windows アプリ インストーラーの既定のソースを有効にする Windows アプリ インストーラーの試験的な機能を有効にする Windows アプリインストーラー Microsoft Store ソースを有効にする Windows App インストーラー フォント ソースを有効にする Windows パッケージ マネージャーを有効にする Windows パッケージ マネージャーを有効にする Windows パッケージ マネージャーのコマンド ライン インターフェイスを有効にする Windows パッケージ マネージャーのソース自動更新間隔を分単位で設定する グループ ポリシー Header for a table listing active Group Policies Windows アプリ インストーラーのローカル マニフェスト ファイルを有効にする Windows アプリ インストーラーでの Microsoft Store ソースの固定証明書のバイパスを有効にする Windows アプリ インストーラー ローカル アーカイブ マルウェア スキャンの上書きを有効にする フィールド: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. フィールド形式が無効です。 無効なフィールド値です。 グループ ポリシーの設定が無効です。 バックアップ ファイルから設定を読み込みました。 ファイルの解析エラー: 値: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. 次の試験的な機能が進行中です。 グループポリシーにより、構成が無効になっています。 インストーラーのハッシュが一致しません。 Windows アプリ インストーラーのハッシュ上書きを有効にする グループポリシーの JSON として現在のソースをエクスポートします。 現在のソースをエクスポートする 追加のソース An additional source required by policy. 許可されたソース A source that the user is allowed to add. '{0}' 引数に指定された値が無効です {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. 取り消し済み 外部 このインポートで見つかったパッケージには、次の依存関係があります: Import command sentence showed before reporting dependencies このパッケージには次の依存関係が必要です: Message shown before reporting dependencies 依存関係をインストールしています 依存ソースが見つかりません パッケージ検索では、複数の結果が生成されます: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. パッケージの最新バージョンが見つかりません: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. インストーラーが見つかりません: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. パッケージに必要な最小バージョンは使用できません: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. 一致する項目はありません When package search yields no matches ループを含む Dependency graph has loop マニフェストに適したインストーラーが見つかりません: {0} バージョン {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. パッケージ依存関係の処理中にエラーが発生しました。インストールを続行しますか? Prompt message shown when dependencies processing yields errors. パッケージ依存関係の処理中にエラーが発生しました。終了しています... パッケージ このパッケージには、不要になる可能性のある依存関係があります: Uninstall command sentence showed before reporting dependencies マニフェストには、検証されていない次の依存関係があります。有効であることを確認してください: Validate command sentence showed before reporting dependencies Windows の機能 Windows ライブラリ 指定されたリソースを使用してパッケージの依存関係を検索 For getting package type dependencies when installing from a local manifest Windows ストアの使用条件 パッケージのすべての使用許諾契約に同意する エクスポートされたパッケージをインストールするには、使用許諾契約書が必要です: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. 発行元は、お客様がインストール前に上記の情報を表示し、契約に同意することを必要としています。 使用条件に同意しますか? パッケージ契約に同意しませんでした。操作が取り消されました。 契約: 作成者: 説明: インストーラー: インストーラーのロケール: ストア製品 ID: インストーラーの SHA256: インストーラーの種類: インストーラーの URL: ライセンス: ライセンス URL: モニカー: ホーム ページ: 公開元: タグ: バージョン: 依存関係: Windows の機能: Windows ライブラリ: パッケージの依存関係: 外部依存関係: いいえ はい 追加されたソースを開けませんでした。 ソース操作中にすべてのソース契約に同意する '{0}' ソースでは、使用する前に次の契約を表示する必要があります。 {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. すべてのソース契約条件に同意しますか? 1 つ以上のソース契約が同意されませんでした。操作が取り消されました。ソース契約に同意するか、対応するソースを削除してください。 ソースが正常に機能するには、現在のマシンの 2 文字の地理的リージョンをバックエンド サービスに送信する必要があります (例: "US")。 正常にインストールされました。アップグレードを完了するには、アプリケーションを再起動してください。 オプションの Windows-Package-Manager REST ソース HTTP ヘッダー 省略可能なヘッダーは、このソースには適用できないため無視されます。 省略可能なヘッダーは、ソースを指定しないと適用されません: '{0}' {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. リリース日: オフライン配信をサポート: 発行元 URL: 購入 URL: 発行元のサポート URL: プライバシー URL: 著作権: 著作権 URL: リリース ノート: リリース ノート URL: ソースの検索中にエラーが発生しました;結果は含まれません: {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. ソースの検索中に失敗しました: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. この機能は管理者が有効にする必要があります。有効にするには、'winget settings --enable {0}' を管理者として実行します。 {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. 特定の管理者設定を有効にします 特定の管理者設定を無効にします 管理者設定 '{0}' を有効にしました。 {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. 管理者設定 '{0}' を無効にしました。 {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. 管理者設定 Header for a table displaying admin settings. アプリケーションは現在実行中です。アプリケーションを終了してから、もう一度お試しください。 インストーラーによって変更されたファイルは、現在別のアプリケーションで使用されています。アプリケーションを終了してから、もう一度お試しください。 別のインストールが既に進行中です。後でもう一度お試しください。 1 つ以上のファイルが使用されています。アプリケーションを終了してから、もう一度お試しください。 このパッケージには、システムに依存関係がありません。 PC にこれ以上空き領域がありません。空き領域を確保してから、もう一度やり直してください。 インストールできるメモリが不足しています。他のアプリケーションを閉じてから、もう一度やり直してください。 インストール パラメーターの 1 つが無効です。パッケージのインストール ログに追加の詳細が含まれている可能性があります。 このアプリケーションにはインターネット接続が必要です。ネットワークに接続してから、もう一度お試しください。 このアプリケーションのインストール中にエラーが発生しました。サポートにお問い合わせください。 PC を再起動してインストールを完了します。 PC が再起動してインストールが完了します。 インストールに失敗しました。PC を再起動してから、もう一度お試しください。 ユーザーがインストールを取り消しました。 このアプリケーションの別のバージョンが既にインストールされています。 このアプリケーションの上位バージョンは既にインストールされています。 組織のポリシーが原因でインストールできません。管理者にお問い合わせください。 現在のシステム構成では、このパッケージのインストールはサポートされていません。 カスタム インストーラー エラーにより、インストールに失敗しました。パッケージ サポートにお問い合わせください。 インストールが破棄されました インストーラーが終了コードで失敗しました: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. インストーラー ログは次で入手できます: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. 作業ソースの中以下のパッケージが見つかりました。 続行するには、'--source' オプションを使用していずれかのパッケージを指定してください。 {Locked="--source"} "working sources" as in "sources that are working correctly" 作業ソースの中にパッケージが見つかりませんでした。 "working sources" as in "sources that are working correctly" これは、Windows パッケージ マネージャーの安定版リリースです。実験的な機能を試したい場合は、プレリリース ビルドをインストールしてください。手順は、GitHub (https://github.com/microsoft/winget-cli) で入手できます。 {Locked="https://github.com/microsoft/winget-cli"} v{0} の Windows パッケージ マネージャー {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. インポート ファイルのパッケージ バージョンを無視する 要求された結果の数は 1 から 1000 の間である必要があります。 既定値に加えてインストーラーに渡される引数 新しいバージョンが見つかりましたが、インストールテクノロジが現在インストールされているバージョンと異なります。パッケージをアンインストールして、新しいバージョンをインストールしてください。 指定された新しいバージョンのインストール テクノロジが、現在インストールされているバージョンと異なります。パッケージをアンインストールして、新しいバージョンをインストールしてください。 Windows パッケージ マネージャー (プレビュー) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. アーキテクチャを選択する 現在のバージョンを特定できない場合でもパッケージをアップグレードする 現在のバージョンを特定できない場合でもパッケージを一覧表示します。--upgrade-available 引数でのみ使用できます {Locked="--upgrade-available"} このパッケージのバージョン番号を特定できません。とにかくアップグレードするには、前のコマンドに引数 --include-unknown を追加します。 {Locked="--include-unknown"} {0} 個のパッケージのバージョン番号を判別できません。すべての結果を表示するには、--include-unknown を使用してください。 {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} パッケージがピン留めされているため、明示的にアップグレードする必要があります。 {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. 指定された引数はクエリでのみ使用できます。 システム アーキテクチャ: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). パッケージ (ポータブル) によって作成されたすべてのファイルとディレクトリを保持します パッケージ ディレクトリ (ポータブル) 内のすべてのファイルとディレクトリを削除します 続行するには、Enter キーを押してください . . . 実行可能ファイルの名前を変更する値 (ポータブル) 終了する前に任意のキーを押すプロンプトをユーザーに表示します インストーラーは管理者として実行するように要求します。プロンプトが表示されます。 管理者コンテキストからインストーラーを実行することはできません。 パス環境変数が変更されました; 新しい値を使用するにはシェルを再起動してください。 製品コードを使用したフィルター 同じ名前で、別のソースからのポータブル パッケージが既に存在します; --force が原因で続行しています {Locked="--force"} ボリュームは再パース ポイントをサポートしていません 指定されたファイル名は有効なファイル名ではありません 既存のファイルを上書きしています: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. パッケージ選択引数が指定されていません; パッケージの検索の詳細については、ヘルプを参照してください。 コマンド ライン エイリアスが追加されました: ポータブル リンク ディレクトリ (マシン) ポータブル リンク ディレクトリ (ユーザー) ポータブル パッケージ ルート (ユーザー) ポータブル パッケージ ルート ポータブル パッケージ ルート (x86) ポータブル インストールに失敗しました; クリーンアップしています... 移植可能パッケージは変更されています。--force が指定されているため続行しています {Locked="--force"} 移植可能パッケージは、変更されているため削除できません。このチェックをオーバーライドするには、--force を指定します {Locked="--force"} ファイルはインストール ディレクトリに残ります: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. インストール ディレクトリを破棄しています... WinGet によって作成されていないため、インストール ディレクトリを破棄することができません 関連リンク Documentation: メモ: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. インストールに関する注意: 指定された引数はこのパッケージ用にはサポートされていません アーカイブの内容を抽出できませんでした 入れ子になったインストーラー ファイルが存在しません。入れ子になったインストーラーの指定された相対パスが一致することを確認します: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. 入れ子状態インストーラーへの相対ファイル パスが正しくありません。パスでインストール ディレクトリ以外の場所が示されています このパッケージ用には入れ子状態インストーラーは指定されていません 移植可能なインストーラーまたはフォントで入れ子になったインストーラーでない限り、アーカイブ インストーラーに指定できる入れ子になったインストーラーは 1 つだけです 互換性のないコマンド ライン引数が指定されました アップグレードが利用可能なパッケージのみを一覧表示します 次のパッケージにはアップグレードを適用可能ですが、アップグレードには明示的対象化が必要です: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly ダウンロード中 Label displayed while downloading an application installer. 入れ子状態インストーラーの種類には対応していません このインストーラーではターミナルまたはシェルが再起動されることがわかっています このパッケージにはインストール先の場所が必要です 次のインストーラーではターミナルまたはシェルが再起動されることがわかっています 次のインストーラーにはインストール先の場所が必要です: インストール ルートを指定してください: 続行しますか? 契約の対象 This will be followed by a package name, and then a list of license agreements パッケージにはインストール場所が必要ですが、指定されていませんでした 対話型プロンプトを無効にします Description for a command line argument, shown next to it in the help 既存のパッケージが既にインストールされています。インストールされているパッケージ...をアップグレードしようとしています コマンドを直接実行し、セキュリティ関連でない問題の場合に続行します Description for a command line argument, shown next to it in the help 別のソースからの移植可能パッケージが既に存在します アーカイブが正常に展開されました アーカイブを展開しています... アーカイブ スキャンでマルウェアが検出されました。このチェックをオーバーライドするには、--ignore-local-archive-malware-scan を使用します {Locked="--ignore-local-archive-malware-scan"} アーカイブ スキャンでマルウェアが検出されました。--ignore-local-archive-malware-scan により続行しています {Locked="--ignore-local-archive-malware-scan"} インストール済みバージョンが既に存在する場合はアップグレードをスキップします パッケージ バージョンは既にインストールされています。インストールが取り消されました。 {0}を有効にできません。この設定はポリシーによって制御されます。詳細については、システム管理者に問い合わせてください。 {Locked="{0}"} The value will be replaced with the feature name {0}を無効にできません。この設定はポリシーによって制御されます。詳細については、システム管理者に問い合わせてください。 {Locked="{0}"} The value will be replaced with the feature name 新しいピンを追加します。ピン留めをすると、Windows パッケージ マネージャーがパッケージを特定のバージョンの範囲にアップグレードするのを制限したり、パッケージを完全にアップグレードできなくなる可能性があります。ピン留めされたパッケージは、それ自体でアップグレードされ、Windows パッケージ マネージャー外からアップグレードされる可能性があります。既定では、ピン留めされたパッケージをアップグレードするには、'upgrade' コマンドで明示的に指定するか、'--include-pinned' フラグを 'winget upgrade --all' に追加します。 {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} 新しい PIN を追加する サブコマンドを使用してパッケージのピンを管理します。ピン留めすると、Windows パッケージ マネージャーがパッケージを特定のバージョンの範囲にアップグレードするのを制限したり、パッケージを完全にアップグレードできなくなる可能性があります。ピン留めされたパッケージは、それ自体でアップグレードされ、Windows パッケージ マネージャー外からアップグレードされる可能性があります。 パッケージ ピンの管理 現在のすべてのピン、または特定のピンの完全な詳細を一覧表示します。 現在のピンを一覧表示する 特定のパッケージ ピンを削除します。 パッケージ ピンの削除 既存のすべてのピンをリセットします。 PIN をリセット パッケージをピン留めするバージョン。ワイルドカード '*' を最後のバージョン部分として使用できます ピンが削除されるまで更新をブロックし、引数をオーバーライドできないようにします インストールされている特定のバージョンをピン留めする インストール済み Value used in a table to indicate that a package comes from the list of packages installed in the machine 設定を JSON としてエクスポート 設定のエクスポート ユーザー設定 Label displayed for the file containing the user settings. 設定ファイルを読み込めませんでした。既定値を使用しています。 インストールされているパッケージ スコープ フィルター (user または machine) の選択 {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. ピンが正常に追加されました パッケージ {0}の PIN は既に存在します {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. パッケージ {0}の PIN は既に存在します。--force引数が原因で上書きしています。 {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. パッケージ {0}の PIN は既に存在します。--force引数を使用して上書きします。 {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. 現在のすべてのピンをリセットしています。 --force 引数を使用して、すべてのピンをリセットします。次のピンが削除されます: {Locked="--force"} ピンの種類 パッケージ {0} にピンがありません {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. ピンが構成されていません。 Shown when listing or modifying existing pins if there are none. ピンが正常にリセットされました Shown after resetting (deleting) all the pins ピン データベースを開けません。 Error message for when we cannot open the database containing package pins. 1 つのパッケージにのみ使用できる引数が指定されました 複数の相互排他的引数が指定されました: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together 引数 {0} は {1} でのみ使用できます {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" 無効 As in enabled/disabled 有効 As in enabled/disabled 状態 Header for a table listing the state (enabled/disabled) of Group Policies and Settings パッケージの検索に使用されるクエリ パッケージが見つかりません: {0}' {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. 複数のパッケージが見つかりました: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. 検索に失敗しました: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} 個のパッケージには、アップグレードする前に削除する必要があるピンがあります {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade winget を使用してパッケージをアップグレードすることはできません。このパッケージをアップグレードするには、発行元から提供された方法を使用してください。 ブロックでないピンを持っている場合でもパッケージをアップグレードする アップグレードを妨げるピンがある場合でもパッケージを一覧表示します。--upgrade-available 引数でのみ使用できます {Locked="--upgrade-available"} ピンが正常に削除されました 新しいバージョンが見つかりましたが、パッケージにアップグレードを止めるピンが含まれています。 パッケージはピン留めされているため、アップグレードできません。ピンを表示および編集するには、'winget pin' コマンドを使用します。一部のピンの種類は、--include-pinned 引数を使用してバイパスできます。 {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} 個のパッケージには、アップグレードを止めるピンがあります。'winget pin' コマンドを使用してピンを表示および編集します。--include-pinned 引数を使用すると、より多くの結果が表示される場合があります。 {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages 指定された構成で説明されているように、システムが適切な状態と一致することを確認します。適切な状態を達成するために、プロセッサをダウンロードまたは実行してください。構成とプロセッサを実行する前に、それらが信頼できることを確認する必要があります。 システムを適切な状態に構成します 指定された構成の詳細を表示します。既定では、システムは変更されませんが、一部のオプションによってファイルがダウンロードまたは読み込まれます。 構成の詳細を表示します 指定された構成で説明されているように、システムが適切な状態と一致することを確認します。適切な状態をテストするために、プロセッサをダウンロードまたは実行してください。構成とプロセッサを実行する前に、それらが信頼できることを確認する必要があります。 システムを適切な状態と照合します 構成ファイルの正確性を検証します。 構成ファイルを検証します 構成ファイル'{0}'フィールドの種類が正しくありません。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. 構成ファイルへのパス 構成ファイルが無効です。 構成ファイル バージョン {0} は不明です。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. 構成の警告を受け入れ、対話型プロンプトを表示できないようにします 適用 Indicates that this item is used to write state アサート Indicates that this item is used to check/assert the state rather than write to it 依存関係:{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. 一部の構成が正常に適用されませんでした。 構成に関する詳細情報を取得できませんでした。 情報 Indicates that this item is used to retrieve values for future use rather than writing them ローカル Used to indicate that the item is present on the device. モジュール: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. モジュール: {1} [{2}] による {0} {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. 設定: Label for the values that are used as inputs for this item when applying state 構成が正常に適用されました。 ユニットが正常に適用されました。 別の構成がシステムに適用されています。この構成は可能な限り早く続行... 実行を選択した構成設定について理解する責任はお客様にあります。お客様が作成またはインポートした構成ファイルについて、Microsoft は責任を負いません。この構成により、Windows の設定の変更、ソフトウェアのインストール、ソフトウェアの設定の変更 (セキュリティ設定を含む) が行われることや、サードパーティのパッケージとサービスに対するユーザー同意事項についてお客様の代理としての承諾が行われることがあります。この構成ファイルを実行することは、これらのリソースと設定を理解して同意したことの確認となります。インストールされるアプリケーションがある場合、それぞれの所有者からライセンスが付与されます。サードパーティのパッケージまたはサービスについて Microsoft は責任を負わず、ライセンスを付与することもありません。 Legal approved. Do not change without approval. 構成を確認してから、システムへの適用を続行しますか? PM approved. 構成が空です。 機能 [{0}] が見つかりませんでした。 {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. Windows 機能の依存関係を有効にできませんでした。インストールを続行するには、'--force'を使用します。 {Locked="--force"} Windows 機能を完全に有効にするには再起動が必要です。このチェックをオーバーライドするには、'--force' を使用してください。 "Windows Feature(s)" is the name of the options Windows features setting. Windows 機能の依存関係を有効にできませんでした;--forceが原因で続行しています "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} [{0}] を有効にしています... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. [{0}] 機能を有効にできませんでした: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. Windows 機能を完全に有効にするには再起動が必要です。--force が原因で続行しています "Windows Feature(s)" is the name of the options Windows features setting. 別のインストール/アンインストールが完了するのを待機しています... ピン留めされたバージョン Table header for the version to which a package is pinned; meaning it should not update from that version. <追加の詳細については、ログ ファイルを参照> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. 構成の詳細を取得しています 構成システムを初期化しています 構成ファイルを読み取っています このシステムは、構成によってアサートされた適切な状態ではありません。 この構成単位は不明な理由で失敗しました: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 構成が原因で構成単位が失敗しました: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 現在のシステム状態の取得中に構成単位が失敗しました。 必要な状態の適用中に構成単位が失敗しました。 現在のシステム状態のテスト中に構成単位が失敗しました。 内部エラーのため、構成単位が失敗しました: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 必須条件が無効なため、構成単位が失敗しました: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. システムの状態が原因で構成単位が失敗しました: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 構成単位の実行中にエラーが発生しました: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 構成に識別子 '{0}' が複数回含まれています。 {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. 依存関係 '{0}' が構成内に見つかりませんでした。 {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. この構成単位は手動でスキップされました。 構成単位のモジュールは、同じバージョンの複数の場所で使用できます。 構成単位のモジュールを読み込めませんでした。 構成単位に一致する項目が複数見つかりました。モジュールを指定して、正しいモジュールを選択してください。 構成単位が見つかりませんでした。 構成単位がモジュール内に正しく含まれていませんでした。 依存関係が失敗したか、実行されなかったため、この構成単位は実行されませんでした。 アサートが失敗したか false であったため、この構成単位は実行されませんでした。 構成単位が実行中に予期しない結果を返しました。 この構成単位は不明な理由で実行されませんでした: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. フィールド '{0}'に無効な値が含まれています: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. フィールド '{0}' が見つからないか、空です。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. ファイル内の行{0}、列{1}を参照してください。 {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. 操作を取り消しています AppInstaller のスタブ パッケージをインストールする AppInstaller のフル パッケージをインストールする 拡張機能を有効にします。ストアへのアクセスが必要です。 オプション '--enable' と '--disable' は他の引数で使用することはできません。 {Locked="--enable", "--disable"} 拡張機能を有効にしています。ストアへのアクセスが必要です。 拡張機能が有効になっていません。'winget configure --enable' を実行して有効にします。 {Locked="winget configure --enable"} 拡張機能が有効になっています。 拡張機能を無効にします。ストアへのアクセスが必要です。 拡張機能を無効にしています。ストアへのアクセスが必要です。 拡張機能は無効になっています。 パッケージの依存関係と Windows 機能の処理をスキップする 依存関係がスキップされました。 プロセスの PATH 変数を更新できませんでした。PATH 変数への変更に依存する後続のインストールは失敗する可能性があります。 {Locked="PATH"} 状態のテスト中に一部の構成ユニットが失敗しました。 システムは記述された構成状態です。 構成の状態がテストされませんでした。 システムは、記述された構成状態ではありません。 予期しないテスト結果: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). 構成を確認してから、システムに対する検証を続行しますか? 構成ファイルが有効な YAML ファイルではありません。 {Locked="YAML"} YAML is a file format name. この構成ユニットは依存関係サイクルの一部です。 検証で問題は見つかりませんでした。 構成ユニットはプレリリースとしてのみ使用できますが、構成内でそのようにマークされていません。'allowPrerelease: true' を 'directives' に追加してください。 {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. 構成単位がローカルで見つかりましたが、構成されたカタログに見つかりませんでした。構成を適用する前に、それがシステムに存在することを確認してください。 構成ユニットはパブリックに使用できません。この構成を使用するすべてのユーザーがアクセスできることを確認してください。 モジュールが指定されませんでした。モジュールを指定すると、パフォーマンスが向上し、今後の名前の競合が回避されます。 構成されたソースを探して見つけるかマニフェストから直接見つけて、選んだパッケージからインストーラをダウンロードします。既定では、そのクエリはパッケージの id、name、または moniker と大文字小文字の区別なく一致する必要があります。その他のフィールドは、適切なオプションを渡すと使えます。既定では、download コマンドによってユーザーのダウンロード フォルダーに該当するインストーラがダウンロードされます。 指定されたパッケージからインストーラをダウンロードする インストーラのダウンロード先ディレクトリ 依存関係をダウンロードしています: インストーラがダウンロードされました: {0} {Locked="{0}"} Full path of the downloaded installer. インストーラ タイプを選択する インストーラのダウンロード インストーラは、後でオフラインでインストールするためにダウンロードすることはできません。 `--module-path allusers` を実行するには管理者特権が必要です。 {Locked="--module-path allusers"} モジュールを格納するローカル コンピューター上の場所を指定します。既定の %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} `--module-path` 値は、`currentuser`、`allusers`、`default`、または絶対パスである必要があります。 {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Windows パッケージ マネージャーの構成を有効にする エラーに関する情報を取得します。指定された数値は、winget 固有のエラーの場合のシンボル名を含む、エラーに関する詳細を出力に含めます。文字列を指定すると、winget 固有のエラーがこの値で検索されます。 エラーに関する情報を取得する エラー情報内で検索する値 指定された数値が大きすぎて HRESULT にできません。 不明なエラー コード 内部エラー コマンド ラインの引数が無効です コマンドの実行に失敗しました マニフェストを開けませんでした 取り消しシグナルを受信しました ShellExecute の実行に失敗しました マニフェストを処理できません。マニフェストのバージョンがサポートされているバージョンを超えています。クライアントを更新してください。 インストーラーのダウンロードに失敗しました インデックスに書き込めません;上位のスキーマ バージョンです インデックスが壊れています 構成されたソース情報が壊れています ソース名は既に構成されています ソースの種類が無効です MSIX ファイルがパッケージではなくバンドルです ソースに必要なデータがありません 現在のシステムに適用できるインストーラーはありません インストーラー ファイルのハッシュがマニフェストと一致しません ソース名が存在しません ソースの場所は既に別の名前で構成されています パッケージが見つかりません ソースが構成されていません 条件に一致するパッケージが複数見つかりました 条件に一致するマニフェストが見つかりませんでした ソース パッケージからパブリック フォルダーを取得できませんでした コマンドを実行するには管理者権限が必要です ソースの場所がセキュリティで保護されていません Microsoft Store クライアントがポリシーによってブロックされています Microsoft Store アプリがポリシーによってブロックされています この機能は現在開発中です。winget 設定を使用して有効にできます。 Microsoft Store アプリをインストールできませんでした オート コンプリートを実行できませんでした YAML パーサーの初期化に失敗しました 無効な YAML キーが見つかりました 重複している YAML キーが見つかりました YAML 操作が無効です YAML ドキュメントをビルドできませんでした YAML エミッタの状態が無効です YAML データが無効です LibYAML エラー マニフェストの検証は成功しましたが、警告があります マニフェストの検証に失敗しました マニフェストが無効です 適用可能な更新は見つかりませんでした WinGet アップグレード -- すべて完了しましたが、エラーが発生しました インストーラーのセキュリティ チェックに失敗しました ダウンロード サイズが予期されたコンテンツの長さと一致しません アンインストール コマンドが見つかりません アンインストール コマンドの実行に失敗しました ICU ブレーク反復子エラー ICU ケースマップ エラー ICU 正規表現エラー 1 つ以上のインポートされたパッケージをインストールできませんでした 1 つ以上の要求されたパッケージが見つかりませんでした JSON ファイルが無効です ソースの場所がリモートではありません 構成された REST ソースはサポートされていません REST ソースによって無効なデータが返されました 操作はグループ ポリシーによってブロックされています Rest API 内部エラー REST ソース URL が無効です REST API によって返されたサポートされていない MIME の種類 REST ソース コントラクト バージョンが無効です ソース データが壊れているか、改ざんされています ストリームからの読み込みエラー パッケージ契約に同意しませんでした プロンプトで入力の読み取り中にエラーが発生しました 検索要求は 1 つ以上のソースでサポートされていません Rest API エンドポイントが見つかりません。 ソースを開けませんでした。 ソース契約に同意しませんでした ヘッダーのサイズが許容される上限の 1024 文字を超えています。サイズを小さくして、もう一度お試しください。 リソース ファイルがありません MSI インストールの実行に失敗しました msiexec の引数が無効です 1 つ以上のソースを開けませんでした 依存関係の検証ができませんでした 1 つ以上のパッケージが見つかりません テーブル列が無効です アップグレード バージョンが、インストールされているバージョンより新しくありません アップグレード バージョンが不明で、オーバーライドが指定されていません ICU 変換エラー ポータブル パッケージをインストールできませんでした ボリュームは再パース ポイントをサポートしていません 別のソースからのポータブル パッケージが既に存在します。 symlink を作成できません。パスはディレクトリを指しています。 管理者コンテキストからインストーラーを実行することはできません。 ポータブル パッケージをアンインストールできませんでした インデックスに対して DisplayVersion 値を検証できませんでした。 1 つ以上の引数がサポートされていません。 SQLite では埋め込み Null 文字は使用できません 入れ子になったインストーラーがアーカイブに見つかりませんでした。 アーカイブを抽出できませんでした。 指定された入れ子になったインストーラーへの相対ファイル パスが無効です。 サーバー証明書が、予期された値のいずれにも一致しませんでした。 インストールの場所を指定する必要があります。 アーカイブのマルウェア スキャンに失敗しました。 インストールされているパッケージのバージョンが少なくとも 1 つ見つかりました。 パッケージの PIN が既に存在します。 パッケージの PIN がありません。 PIN データベースを開けません。 1 つ以上のアプリケーションをインストールできませんでした 1 つ以上のアプリケーションをアンインストールできませんでした 1 つ以上のクエリで、返された結果が 1 つのみではありませんでした パッケージがピン留めされているため、アップグレードできません。 現在インストールされているパッケージはスタブ パッケージです アプリケーション シャットダウン シグナルを受信しました パッケージの依存関係をダウンロードできませんでした。 パッケージをダウンロードできませんでした。オフライン インストール用のダウンロードは禁止されています。 必要なサービスがビジー状態であるか、利用できません。後でもう一度お試しください。 指定された GUID は、有効な再開状態に対応していません。 現在のクライアント バージョンは、保存された状態のクライアント バージョンと一致しませんでした。 再開状態データが無効です。 チェックポイント データベースを開けません。 再開の上限を超えました。 認証情報が無効です。 認証方法がサポートされていません。 認証に失敗しました。 認証に失敗しました。対話型認証が必要です。 認証に失敗しました。ユーザーにより取り消されました。 認証に失敗しました。認証されたアカウントは目的のアカウントではありません。 アプリケーションは現在実行中です。アプリケーションを終了してから、もう一度お試しください。 別のインストールが既に進行中です。後でもう一度お試しください。 1 つ以上のファイルが使用されています。アプリケーションを終了してから、もう一度お試しください。 このパッケージには、システムに依存関係がありません。 PC にこれ以上空き領域がありません。空き領域を確保してから、もう一度やり直してください。 インストールできるメモリが不足しています。他のアプリケーションを閉じてから、もう一度やり直してください。 このアプリケーションにはインターネット接続が必要です。ネットワークに接続してから、もう一度お試しください。 このアプリケーションのインストール中にエラーが発生しました。サポートにお問い合わせください。 PC を再起動してインストールを完了します。 インストールに失敗しました。PC を再起動してから、もう一度お試しください。 PC が再起動してインストールが完了します。 ユーザーがインストールを取り消しました。 このアプリケーションの別のバージョンが既にインストールされています。 このアプリケーションの上位バージョンは既にインストールされています。 組織のポリシーが原因でインストールできません。管理者にお問い合わせください。 パッケージの依存関係をインストールできませんでした。 アプリケーションは現在別のアプリケーションで使用されています。 パラメーターが無効です。 パッケージはシステムでサポートされていません。 インストーラーは既存のパッケージのアップグレードをサポートしていません。 カスタム インストーラー エラーにより、インストールに失敗しました。 パッケージのアプリと機能エントリが見つかりませんでした。 インストールの場所は適用できません。 インストールの場所が見つかりませんでした。 既存のファイルのハッシュが一致しませんでした。 ファイルが見つかりませんでした。 ファイルは見つかりましたが、ハッシュはチェックされませんでした。 ファイルにアクセスできませんでした。 構成ファイルが無効です。 YAML 構文が無効です。 構成フィールドの型が無効です。 構成のバージョンが不明です。 構成の適用中にエラーが発生しました。 重複する識別子が構成に含まれています。 構成に依存関係がありません。 構成に不十分な依存関係があります。 構成単位のアサーションが失敗しました。 この構成は手動でスキップされました。 ユーザーが実行の続行を拒否しました。 依存関係グラフに、解決できない循環が含まれています。 構成に無効なフィールド値が含まれています。 構成にフィールドがありません。 状態のテスト中に一部の構成ユニットが失敗しました。 構成の状態がテストされませんでした。 構成ユニットはインストールされませんでした。 構成単位が見つかりませんでした。 構成単位に一致する項目が複数見つかりました。モジュールを指定して、正しいモジュールを選択してください。 現在のシステム状態の取得中に構成単位が失敗しました。 現在のシステム状態のテスト中に構成単位が失敗しました。 必要な状態の適用中に構成単位が失敗しました。 構成単位のモジュールは、同じバージョンの複数の場所で使用できます。 構成単位のモジュールを読み込めませんでした。 構成単位が実行中に予期しない結果を返しました。 ユニットに構成ルートを必要とする設定が含まれています。 構成プロセッサでは操作がサポートされていません。 利用不可 Windows 機能の依存関係が正常に有効になりました 構成単位のモジュールを読み込めませんでした。実行するには管理者特権が必要です。 ユニットに構成ルートを必要とする設定が含まれています。 構成単位のモジュールを読み込めませんでした。実行するには管理者特権が必要です。 保存されたコマンドの一意識別子を渡すことで、以前に保存されたコマンドの実行を再開します。これは、再起動のために終了した可能性のある、実行されたコマンドを再開するために使用されます。 以前に保存したコマンドの実行を再開します。 再開する保存された状態の一意識別子 別のクライアント バージョンからの状態の再開はサポートされていません: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. 再開状態が存在しません: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. 再開状態のデータが見つかりません。 このコマンドは再開をサポートしていません。 適用可能な場合は再起動を許可します 再起動を開始して操作を完了しています... 再起動を開始できませんでした。 再開操作が、許可されている再開 {0} 制限を超えています。手動で再開するには、コマンド '{1}' を実行してください。 {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. 保存された状態の再開の制限を無視する URI スキームはサポートされていません: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. 整形式の URL ではありません: {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. 構成単位の設定コンテンツ {0} 解析できなかったか、設定コンテンツが空です。 {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. {0} 構成単位に必要な引数がありません: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. {0} 構成単位に推奨引数がありません: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. WinGetSource の構成ユニットが既知のソースと競合しています: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetSource の構成ユニットがサード パーティのソースに対してアサートしています: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage で UseLatest と Version の両方が宣言されています。パッケージ ID: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} WinGetPackage 構成単位は、サード パーティのソースからのパッケージに対してアサートします。パッケージ ID: {0};ソース: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 構成単位パッケージは、以前に構成されていないサード パーティのソースに依存しています。パッケージ ID: {0};ソース: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 構成単位パッケージはサード パーティのソースに依存しています。uni dependsOn セクションで依存関係を宣言することをお勧めします。パッケージ ID: {0};ソース: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} WinGetPackage 構成単位パッケージを検証できません。ソースを開けませんでした。パッケージ ID: {0};ソース: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 構成単位パッケージを検証できません。パッケージが見つかりません。パッケージ ID: {0} {Locked="WinGetPackage,{0}"} WinGetPackage 構成単位パッケージを検証できません。複数のパッケージが見つかりました。パッケージ ID: {0} {Locked="WinGetPackage,{0}"} WinGetPackage 構成単位パッケージを検証できません。パッケージ バージョンが見つかりません。パッケージ ID: {0};バージョン {1} {Locked="WinGetPackage,{0},{1}"} 特定のバージョンで指定された WinGetPackage 構成ユニット パッケージ。使用可能なパッケージのバージョンは 1 つだけです。パッケージ ID: {0}; バージョン: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 構成単位パッケージを検証できません。パッケージ ID: {0} {Locked="WinGetPackage,{0}"} 認証ウィンドウの設定を指定する (silent、silentPreferred、または interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. 認証に使用するアカウントを指定します ソースを追加できませんでした。この winget バージョンは、ソースの認証方法をサポートしていません。最新の winget バージョンにアップグレードしてみてください。 {Locked="winget"} {0} ソースには認証が必要です。必要に応じて認証プロンプトが表示されることがあります。認証された情報は、アクセス承認のためにソースと共有されます。 {Locked="{0}"} インストールされたパッケージリスト、またはマニフェストから検索し、選択されたパッケージを修復します。既定では、クエリはパッケージのID、名前、モニカーに大文字小文字の区別なく一致する必要があります。その他のフィールドは、適切なオプションを渡すことで使用することができます。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 選択したパッケージを修復します このパッケージの修復コマンドが見つかりません。サポートが必要な場合は、パッケージの発行元にお問い合わせください。 使用中のインストーラー テクノロジが、現在インストールされているバージョンと一致しません。 修復コマンドが見つかりません。 使用中のインストーラー テクノロジは修復をサポートしていません。 修復操作が正常に完了しました。 修復が中止されました パッケージの修復を開始しています... Microsoft Store パッケージを修復できませんでした。エラー コード: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. 修復操作に失敗しました。 修復操作は適用できません。 構成されたソースから入手できるパッケージ バージョンの中に、一致するものはありません。 現在のシステム構成では、このパッケージの修復はサポートされていません。 使用中のインストーラー テクノロジは修復をサポートしていません。 管理者特権で実行している場合、ユーザー スコープ用にインストールされたパッケージは修復できません。 管理者特権を含む修復操作は、ユーザースコープ内にインストールされたパッケージでは許可されていません。 修復が終了コードで失敗しました: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. SQLite 接続は破損を防ぐために終了しました。 すべてのバージョンをアンインストール 処理するバージョン このパッケージの複数のバージョンがインストールされています。検索を絞り込むか、'--version' 引数を渡して 1 つ選択するか、'--all-versions' フラグを渡してすべてをアンインストールしてください。 {Locked="--version,--all-versions"} プロキシ コマンド ライン オプションWindows パッケージ マネージャー有効にする Describes a Group Policy that can enable the use of the --proxy option to set a proxy この実行に使用するプロキシを設定します この実行に対するプロキシの使用を無効にする {0} をリセットできません。この設定はポリシーによって制御されます。詳細については、システム管理者に問い合わせてください。 {Locked="{0}"} The value will be replaced with the feature name 管理者設定 '{0}' をリセットします。 {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. {0} を設定できません。この設定はポリシーによって制御されます。詳細については、システム管理者に問い合わせてください。 {Locked="{0}"} The value will be replaced with the feature name 管理者設定の '{0}' を '{1}' に設定します。 {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced 変更する設定の名前 設定に設定する値。 管理者設定を既定値にリセットします。 管理者設定を既定値にリセットします。 管理者設定の値を設定します。 管理者設定の値を設定します。 指定しない限り、ソースを検出から除外します ソースを検出から除外します (true または false) 成人指定 ソースの信頼レベル (なしまたは信頼済み) 信頼レベル Microsoft Store パッケージ カタログを取得できませんでした。 Microsoft Store パッケージ カタログから該当する Microsoft Store パッケージが見つかりません。 Microsoft Store パッケージのダウンロード情報を取得できませんでした。 ダウンロードする適用可能なMicrosoft Store パッケージが見つかりませんでした。 Microsoft Store パッケージ ライセンスを取得できませんでした。 該当する Microsoft Store パッケージが見つかりません。 Microsoft Store パッケージ ハッシュの検証に成功しました Microsoft Store パッケージ ハッシュの不一致 Microsoft Store パッケージがダウンロードされました: {0} {Locked="{0}"} Full path of the downloaded package. Microsoft Store パッケージのダウンロードに失敗しました: {0} {Locked="{0}"} Package name. Microsoft Store パッケージのダウンロードが完了しました Microsoft Store からメイン パッケージをダウンロードしています... Microsoft Store から依存パッケージをダウンロードしています... Microsoft Store パッケージのダウンロード情報を取得しています Microsoft Store パッケージのダウンロード情報を取得できませんでした Microsoft Store パッケージ ライセンスを取得しています Microsoft Store のパッケージ ライセンスを保存しました: {0} {Locked="{0}"} License file full path. Microsoft Store パッケージ ライセンスを取得できませんでした Microsoft Store パッケージのダウンロードでは、--rename 引数はサポートされていません。Microsoft Store パッケージでは、Microsoft Store カタログによって提供される名前が使用されます。 {Locked="--rename"} Microsoft Store パッケージのダウンロードにはMicrosoft Entra ID 認証が必要です。必要に応じて認証プロンプトが表示されることがあります。認証された情報は、アクセス承認のためにMicrosoft サービスと共有されます。パッケージ ライセンスMicrosoft Storeするには、Microsoft Entra ID アカウントがグローバル管理者、ユーザー管理者、またはライセンス管理者のメンバーである必要があります。 {Locked="Global Administrator,User Administrator,License Administrator"} Microsoft Store パッケージのオフライン ライセンスの取得をスキップします ターゲット プラットフォームの選択 構成ファイルの追加: {0} {Locked="{0}"} エクスポートに成功しました 構成設定を取得しています... --resource を含む --packageId または --module を少なくとも指定する必要があります。または、--all を使用して、すべてのパッケージ構成をエクスポートします。 {Locked="--packageId,--module, --resource, --all"} 引数 --packageId、--module、--resource を --all と共に使用することはできません。 {Locked="--packageId,--module, --resource, --all"} 構成リソースを構成ファイルにエクスポートします。--all と共に使用すると、すべてのパッケージ構成がエクスポートされます。--packageId と共に使用すると、指定されたパッケージ ID の WinGetPackage リソースがエクスポートされます。--module および --resource と共に使用する場合、リソースの設定を取得し、構成ファイルにエクスポートします。出力構成ファイルが既に存在する場合は、エクスポートされた構成リソースを追加します。 {Locked="WinGetPackage,--packageId,--module, --resource"} 構成リソースを構成ファイルにエクスポートします。 エクスポートするリソースのモジュール。 エクスポートするパッケージ識別子。 エクスポートする構成リソース。 すべてのパッケージ構成をエクスポートします。 構成ユニットはプロパティの取得に失敗しました。 構成をエクスポートできませんでした。 {0} の構成 {Locked="{0}"} {0} のインストール {Locked="{0}"} 構成ファイルに存在するデータの一部が、この出力に対して切り捨てられました。ファイルの内容を検査して、完全なコンテンツを確認してください。 <この値は切り捨てられています。完全なテキストについてはファイルの内容を調べてください> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. システムに適用された構成の詳細レベルを表示します。このデータは、`configure` コマンドと共に使用して、詳細を取得できます。 {Locked="`configure`"} 構成履歴の表示 履歴に構成はありません。 履歴からアイテムを選択する 指定されたデータに一致する単一の構成が見つかりませんでした。必要な構成と厳密に一致する完全な名前または識別子の一部を指定してください。 履歴からアイテムを削除する 最初に適用された日 Column header for date values indicating when a configuration was first applied to the system. ID 名前 元の場所 パス 指定された構成が見つかりませんでした。 完了済み The state of processing an item. 処理中 The state of processing an item. 保留中 The state of processing an item. 不明 The state of processing an item. 構成状態を監視します。 As in "to monitor the status of a configuration being applied". 完了済み The state of processing an item. 処理中 The state of processing an item. 保留中 The state of processing an item. スキップ済み The state of processing an item. 不明 The state of processing an item. 適用を開始しました When the configuration application started. 適用が終了しました When the configuration application ended. 結果 詳細 状態 The state of processing an item. 単位数 パッケージは現在の Windows バージョンまたはプラットフォームと互換性がありません。 可能な場合は初期設定の詳細を表示しない 異なるプラットフォームとアーキテクチャを対象に、複数のMicrosoft Store パッケージをダウンロードできます。--platform、 --architecture を使用してパッケージをフィルター処理できます。 {Locked="--platform--architecture"} Microsoft Store パッケージ ライセンスを取得できませんでした。Microsoft Entra ID アカウントは、グローバル管理者、ユーザー管理者、またはライセンス管理者のメンバーではありません。--skip-license を使用して、パッケージ ライセンスの取得Microsoft Storeスキップします。 {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} Microsoft Store パッケージはダウンロードをサポートしていません。 Microsoft Store パッケージはダウンロードをサポートしていません。 Microsoft Store パッケージ ライセンスを取得できませんでした。Microsoft Entra ID アカウントに必要な特権がありません。 整合性境界を越えてパラメーターを渡すことはできません。 ゼロ バイト インストーラーをダウンロードしました;ネットワーク接続が正しく動作していることを確認してください。 ゼロ バイト インストーラーをダウンロードしました;ネットワーク接続が正しく動作していることを確認してください。 フォントの管理 サブコマンドを使用してフォントを管理します。packages. と同様に、フォントをインストール、アップグレード、またはアンインストールできます ファミリ フェイス "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' ファミリ名で結果をフィルター処理する "Face" represents the typeface of a font family such as 'Bold' or 'Italic' パス 入力条件に一致するインストール済みのフォントが見つかりませんでした。 インストール済みフォントを一覧表示します インストールされているすべてのフォント、すべてのフォント ファイル、または特定のフォントの完全な詳細を一覧表示します。 バージョン 構成モジュール PowerShell Modules that are used for the Configuration feature パッケージ インストーラーには認証が必要です。必要に応じて認証プロンプトが表示されることがあります。認証された情報はインストーラーのダウンロード URL と共有されます。 インストーラーをダウンロードできませんでした。この winget バージョンは、インストーラーのダウンロード認証方法をサポートしていません。最新の winget バージョンにアップグレードしてみてください。 {Locked="winget"} インストーラーをダウンロードできませんでした。認証に失敗しました。 構成プロセッサへのパスを指定する DSC v3 リソース コマンド DSC stands for "Desired State Configuration". It should already have a locked translation. ここでのサブコマンドは、winget とパッケージを構成するための Desired State Configuration (DSC) v3 リソースを実装しています。 リソースの状態を取得する リソースの状態を設定する 必要な状態の変更について説明する リソースの状態をテストする リソースの状態を削除する すべての状態インスタンスを取得する グループの内容を検証する 外部状態を解決する アダプターを実行する リソース スキーマを取得する リソース マニフェストを取得する パッケージの状態を管理する winget を使用してパッケージを管理します。 インスタンスが存在するかどうかを示します。 インスタンスが目的の状態かどうかを示します。 パッケージの識別子。 パッケージのソース。 パッケージのバージョン。 識別子とパッケージを照合するためのメソッドです。 パッケージの使用可能な最新バージョンをインストールするように指示します。 必要に応じて使用するインストール モード。 パッケージのターゲット スコープ。 ソースとパッケージの契約に同意するかどうかを示します。 ユーザー設定ファイルを管理する winget のユーザー設定を管理します。 設定 JSON ファイルの内容。 設定の適用に使用されるアクション。 相関関係のために値がログに記録されます ソースの構成を管理する winget のソースを管理します。 ソースに使用する名前。 ソースの引数。 ソースの種類。 ソースの信頼レベル。 呼び出しでソースが指定されていない場合にソースを含めるかどうか。 構成単位を適用しています... 構成単位をエクスポートしています... 構成単位プロセッサを取得しています... エクスポートに必要なモジュールを確認する [{0}] {Locked="{0}"} 必要なモジュールをテストまたは取得できませんでした。関連する設定はエクスポートされません。 エクスポート [{0}] {Locked="{0}"} リソースをエクスポートできませんでした。 ユニット プロセッサを取得できませんでした。個々のパッケージ設定はエクスポートされません。 結果を書き込むディレクトリ Desired State Configuration パッケージがシステムに見つかりません。パッケージ... をインストールしています Desired State Configuration パッケージをインストールできませんでした。パッケージを手動でインストールするか、引数を使用して dsc.exe するパス --processor-path 指定してください。 {Locked="dsc.exe","--processor-path"} 管理者の設定とその値を含むオブジェクトです。 管理者設定を管理する winget の管理者設定を管理します。 すべての管理者設定をリセットする すべての管理者設定がリセットされました。 MCP 情報 MCP stands for Model Context Protocol and should probably remain as-is Windows パッケージ マネージャーの MCP (モデル コンテキスト プロトコル) 情報。 Windows パッケージ マネージャー MCP サーバーを MCP クライアントで手動で構成するには、`servers` オブジェクトで次の JSON フラグメントを使用します: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. Windows パッケージ マネージャー MCP サーバーを有効にする ターゲット オペレーティング システムのバージョン 1 つ以上のフォントのインストールに失敗しました。 フォント ファイルはサポートされていないため、インストールできません。 フォント パッケージ内の 1 つ以上のフォントがサポートされていないため、インストールできません。 フォントのインストールに失敗しました。クリーンアップしています。 フォントは既にインストールされています。 パッケージ ID WinGet がサポートされています タイトル フォント ファイルが見つかりません。 フォントのアンインストールに失敗しました。フォントが正常な状態でない可能性があります。再起動後にアンインストールしてみてください。 フォントの検証に失敗しました。 フォントのロールバックに失敗しました。フォントが正常な状態でない可能性があります。再起動後にアンインストールしてみてください。 フォント ファイルの詳細情報を表示します。 フォント パッケージの検証に失敗しました。 失敗したフォントのインストールのロールバックに失敗しました。フォントを正常にアンインストールするには、再起動が必要な場合があります。 フォント パッケージのアンインストールに失敗しました。多くの場合、フォントがシステムまたはアプリケーションによって使用されていることが原因です。コンピューターの再起動後にアンインストールが正常に完了する可能性があります。 OK "OK" means the font is in a good healthy state 破損 "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. 状態 不明 フォント パッケージは既にインストールされています。 パッケージに関する詳細情報を表示します Providing this argument causes the CLI to output additional details about installed application packages. チャネル: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). ローカル識別子: Precedes a value that is the unique identifier for the installed package on the local system. パッケージ ファミリ名: Precedes a value that is the APPX/MSIX package family name of the installed package. 製品コード: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. アップグレード コード: Precedes a value that is the MSI Upgrade Code for the installed package. インストール スコープ: Precedes a value that is the scope of the installation of the package (ex. user, machine). インストールされたアーキテクチャ: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). インストール ロケール: Precedes a value that is the locale of the installed package. インストール場所: Precedes a value that is the directory path to the installed package. ソース提供元: Precedes a value that names the package source where the installed package originated from. 利用可能なアップグレード: Precedes a list of package upgrades available for the installed package. インストーラー カテゴリ: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). 古い値 Column title for listing edit changes. 新しい値 Column title for listing the new value. ================================================ FILE: Localization/Resources/ko-KR/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/ko-KR/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 근접한 별칭이 플래그가 아님: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). 근접한 플래그 별칭 찾을 수 없음: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). 다음 인수를 사용할 수 있음 Message displayed to inform the user about the available command line arguments. 다음 명령 별칭을 사용할 수 있습니다. Message displayed to inform the user about the available command line alias arguments. 다음 명령을 사용할 수 있음 Title displayed to inform the user about the available commands. 사용 가능 As in "a new version is available to upgrade to". 다음 선택 사항을 사용할 수 있음 Message displayed to inform the user about the available options. 다음 부명령어는 사용 할 수 없음 Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} 업그레이드를 사용할 수 있습니다. {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. 지정된 채널을 사용합니다(기본값은 일반 대상). 명령 Label displayed for a command to give the software. 명령을 기준으로 결과 필터링 Description message displayed to inform the user about filtering the search results by a package command. 완료에 대한 전체 명령줄 이 명령을 실행하려면 관리자 권한이 필요합니다. 이 명령은 상황에 맞는 명령줄 완료를 요청하는 데 사용할 수 있습니다. 명령줄, 커서 위치 및 완료 할 단어가 전달됩니다. 출력은 입력을 기반으로 한 잠재적 값의 집합으로 한 줄당 하나의 값이 가능합니다. 컨텍스트에 중요한 명령줄 완료를 사용합니다. 지정된 수의 결과만 표시(1~1000 사이) 완료 Label displayed when an operation completes or is done executing. 정확한 일치를 사용하여 패키지를 찾습니다. Description message displayed to inform the user about finding an application package using an exact matching criteria. 데모용 실험 인수 이 명령은 실험적 기능을 구현하는 방법에 대한 예입니다. 'winget settings'으로 이동하여 experimentalCmd 또는 experimentalArg 기능을 사용하려면 {Locked="winget settings"} 실험적 기능 예 필요한 인수가 없을 때 위치 인수를 찾았습니다. '{0}' {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. 이 기능은 진행 중인 작업이며, 향후 크게 변경되거나 모두 제거될 수 있습니다. 사용하도록 설정하려면 설정('winget settings')을 편집하여 실험적 기능을 포함하세요. '{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. 실험적 기능의 상태를 표시합니다. 'winget settings' 통해 실험적 기능을 켤 수 있습니다. {Locked="winget settings"} 실험적 기능의 상태 표시 사용 안 함 사용 기능 링크 다음 실험적 기능이 진행 중입니다. 설정 파일 'winget settings'을 통해 설정을 구성할 수 있습니다. {Locked="winget settings"} 속성 상태 해시할 파일 플래그 인수는 근접하는 값을 포함할 수 없음: '{0}' {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. 매니페스트에 삽입하기에 적합한 로컬 파일의 해시를 계산합니다. 또한 MSIX 패키지의 서명 파일의 해시를 계산하여 스트리밍 설치를 사용하도록 할 수도 있습니다. 해시 설치 관리자 파일 도우미 선택한 명령에 대한 도움말을 표시 특정 명령에 대한 자세한 내용을 보려면 도움말 인수에 해당 명령을 전달합니다. 자세한 도움말은 다음의 위치에서 찾아볼 수 있음: {0} {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. ID를 기준으로 결과 필터링 경고 출력을 표시하지 않습니다. 이 응용 프로그램의 라이선스는 그 소유자가 사용자에게 부여했습니다. Microsoft는 타사 패키지에 대한 책임을 지지 않고 라이선스를 부여하지도 않습니다. 이 패키지는 Microsoft Store를 통해 제공됩니다. winget은 현재 사용자를 대신하여 Microsoft Store에서 패키지를 가져와야 할 수 있습니다. {Locked="winget"} 구성된 원본을 검색하여 찾거나 매니페스트에서 직접 선택한 패키지를 설치합니다. 기본적으로 쿼리는 대소문자를 구분하지 않고 패키지의 id, 이름 (name) 또는 모니커와 (moniker) 일치해야 합니다. 다른 필드는 적절한 옵션을 전달하여 사용할 수 있습니다. 기본적으로 install 명령은 패키지 설치 상태를 확인하고 해당되는 경우 업그레이드를 시도합니다. 직접 설치를 수행하려면 --force로 재정의하세요. {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 지정된 패키지를 설치합니다. 설치 관리자 해시가 일치하지 않습니다. 관리자로 실행할 경우 이 설정을 무시할 수 없습니다. 설치 프로그램 해시가 일치하지 않습니다. --ignore-security-hash로 인한 진행 {Locked="--ignore-security-hash"} 설치 프로그램 해시가 일치하지 않습니다. 이 검사를 무시하려면 --ignore-security-hash를 사용하세요. {Locked="--ignore-security-hash"} 설치 관리자 해시를 확인했습니다. 설치 성공 패키지 설치를 시작하는 중... 설치 관리자 해시 확인 실패 무시 로컬 매니페스트에서 보관 유형 패키지를 설치하는 동안 수행된 맬웨어 검색 무시 대화형 설치를 요청합니다(사용자 입력이 필요할 수 있음). 현재 명령에 대해 인수 별칭을 인식할 수 없음: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). 유효하지 않은 인수 특정자: '{0}' {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). 현재 명령에 대해 인수 이름을 인식할 수 없음: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Winget 디렉터리 Header for a table detailing the directories Winget uses for key operations like logging and portable installs 사용할 로캘(BCP47 형식) {Locked="BCP47"} 라이선스 계약 링크 Links to different webpages list 명령은 시스템에 설치된 패키지를 표시하거나 업그레이드를 사용할 수 있는지 여부를 표시합니다. search 명령과 같이 출력을 필터링하는 데 추가 옵션을 제공할 수 도 있습니다. {Locked="list","search"} 설치된 패키지 표시 설치할 위치(지원되는 경우) 로그 위치(지원되는 경우) Copyright (c) Microsoft Corporation. All rights reserved. 홈페이지 The primary webpage for the software 패키지의 매니페스트 경로 매니페스트 유효성 검사에 실패했습니다. 매니페스트 유효성 검사에 성공했습니다. 매니페스트 유효성 검사에 성공했으나 경고가 발생했습니다. 인수 값이 필수이지만 아무것도 찾지 못했음: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. 결과를 모니커로 필터링합니다. 입력 파일이 msix로 처리됩니다. (서명된 경우 서명 해시가 제공됨) MSIX 서명 해시를 계산하지 못했습니다. 정책에 의해 특정 앱이 차단되어 Microsoft Store 패키지를 설치하거나 업그레이드하지 못했습니다. Microsoft Store 패키지를 설치하거나 업그레이드하지 못했습니다. 오류 코드: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Microsoft Store 클라이언트가 정책에 의해 차단되어 Microsoft Store 패키지를 설치 또는 업그레이드하지 못했습니다. 패키지 획득을 확인/요청하는 중.. 입력 기준과 일치하는 여러 개의 패키지를 찾았습니다. 입력을 구체화하십시오. 입력 조건과 일치하는 패키지가 여러 개 있습니다. 입력을 구체화하십시오. 이름을 기준으로 결과 필터링 적용 가능한 설치 관리자를 찾을 수 없습니다. 자세한 내용은 로그를 참조하십시오. 현재 사용 가능한 실험적 기능이 없습니다. 입력 조건과 일치하는 설치된 패키지를 찾을 수 없습니다. 입력 조건과 일치하는 패키지를 찾을 수 없습니다. VirtualTerminal 표시를 사용 안 함 {Locked="VirtualTerminal"} 기본 로그 위치 열기 옵션 Options to change how a command works 설치 관리자에 전달할 인수 재정의 패키지: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. 죄송합니다 .작업 수행을 하지 못했습니다. 명령줄 내의 커서 위치 개인정보처리방침 패키지를 검색하는 데 사용되는 쿼리 여러 가지 무지개의 색깔로 진행률 표시 필수 인수가 제공 되지 않음: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. 기본 색깔로 진행률 표시 구성된 원본에서 패키지를 검색합니다. 패키지의 기본 정보를 찾아 표시 장치 ID Abbreviation of Identifier. 일치 이름 원본 결과 한도로 잘렸습니다. 버전 설정의 유효성을 확인하는 동안 다음 오류가 발견되었습니다. 기본 json 텍스트 편집기에서 설정을 엽니다. 편집기를 구성하지 않은 경우 메모장에서 설정을 엽니다. 사용 가능한 설정은 https://aka.ms/winget-settings를 참조하세요. 이 명령은 --enable 또는 --disable 함 인수를 제공하여 관리자 설정을 설정하는 데도 사용할 수 있습니다. {Locked="--enable"} {Locked="--disable"} 설정 열기 또는 관리자 설정 설정 설정을 로드하는 동안 예기치 않은 오류가 발생했습니다. 'settings' 명령을 실행하여 설정을 확인하세요. {Locked="'settings'"} 채널 특정 패키지에 대한 정보를 표시합니다. 기본적으로 쿼리는 패키지의 ID, 이름 또는 모양과 대/소문자를 구분하지 않는 다섯 번이 어려져야 합니다. 적절한 옵션을 전달하여 다른 필드를 사용할 수 있습니다. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 패키지에 대한 정보 표시 버전 무인 설치 요청 단일 문자 별칭만이 단일- 후에 나타남: '{0}' {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. 지정한 이름의 원본이 이미 있으며 다른 위치를 참조합니다. 원본이 다른 이름의 위치를 이미 참조합니다. 지정한 이름의 소스가 이미 있으며 다음과 같이 동일한 위치를 참조합니다. 원본을 추가하는 중: 새 원본을 추가합니다. 원본은 패키지를 검색하고 설치할 수 있는 데이터를 제공합니다. 새 원본을 보안 위치로 신뢰하는 경우에만 새 원본을 추가합니다. 새 원본 추가 원본에 지정된 인수 지정한 원본을 사용하여 패키지를 찾습니다. 하위 명령을 사용하여 원본을 관리합니다. 원본은 패키지를 검색하고 설치할 수 있는 데이터를 제공합니다. 새 원본을 보안 위치로 신뢰하는 경우에만 새 원본을 추가합니다. 패키지 원본 관리 기존 원본의 속성을 편집합니다. 원본은 패키지를 검색하고 설치하는 데 사용할 데이터를 제공합니다. 소스 속성 편집 소스 편집 중: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. 이름이 '{0}' 원본이 이미 원하는 상태에 있습니다. {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. 인수 Value given to source. 현재의 모든 원본 또는 특정 원본의 전체 세부 정보를 목록에 추가합니다. 현재의 원본을 목록에 추가 데이터 Data stored by the source. 필드 The name of a piece of information about a source. 이름 The name of the source. 식별자 The source's unique identifier. 다음 이름의 원본을 찾지 못함: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. 구성된 원본이 없습니다. 유형 The kind of source. 업데이트됨 The last time the source was updated. 사용 안 함 The source has never been updated. The value of information about a source. 원본의 이름 소스를 열 때 실패했습니다. 문제가 계속되면 'source reset' 명령을 사용해 보십시오. {Locked="source reset"} 미리 정의된 출처를 열지 못했습니다. 유지 관리 winget 신고하세요. {Locked="winget"} 모든 원본을 제거하는 중... 특정 원본을 제거합니다. 현재의 원본 제거 출처를 제거하는 중: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. 모든 원본... 다시 설정 이 명령은 기존 원본을 삭제하고, 잠정적으로 로컬 데이터는 그대로 둡니다. 인수가 없으면 모든 원본이 삭제되고 기본값이 추가됩니다. 명명된 원본이 제공되는 경우, 해당 원본만 삭제됩니다. 원본 재설정 원본을 강제로 초기화 --force 옵션을 지정하면 다음 원본이 다시 설정됩니다. {Locked="--force"} 원본을 다시 설정하는 중: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. 원본의 형식 모든 원본을 업데이트 중... 모든 원본 또는 특정 원본만을 업데이트합니다. 현재의 원본을 업데이트 원본 업데이트 중: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. 태그를 기준으로 결과 필터링 위젯을 이용해 주셔서 감사합니다 타사 통지 사항 원넷 명령줄 유틸리티를 사용하면 명령줄에서 응용 프로그램 및 기타 패키지를 설치할 수 있습니다. 도구의 일반 정보를 표시 도구의 버전을 표시 인수가 허용 되는 횟수보다 더 많이 제공 됨: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. 둘 이상의 실행 동작 인수가 제공되었습니다. '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). 명령을 실행하는 동안 예기치 않은 오류가 발생했습니다. 업그레이드하는 동안 이전 버전의 패키지 제거 인식할 수 없는 명령임: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. 설치된 모든 패키지를 사용 가능한 경우 최신 패키지로 업그레이드 적용 가능한 업그레이드를 찾을 수 없습니다. 구성된 원본에서 최신 패키지 버전을 사용할 수 있지만 시스템 또는 요구 사항에는 적용되지 않습니다. 사용 가능한 업그레이드를 찾을 수 없습니다. 구성된 원본에서 사용할 수 있는 최신 패키지 버전이 없습니다. 설치된 패키지 목록을 검색하거나 매니페스트에서 직접 찾은 선택한 패키지를 업그레이드합니다. 기본적으로 쿼리는 패키지의 ID, 이름 또는 모니커와 대/소문자를 구분하지 않아야 합니다. 다른 필드는 적절한 옵션을 전달하여 사용할 수 있습니다. 인수가 지정되지 않은 경우 업그레이드를 사용할 수 있는 패키지가 표시됩니다. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 사용 가능한 업그레이드를 표시하고 수행합니다. 사용 현황: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. 엄격한 지침의 집합을 사용하여 매니페스트의 유효성을 검사합니다. 이 기능은 리포지토리를 제출하기 전에 매니페스트를 확인할 수 있도록 하기 위한 목적을 가지고 있습니다. 매니페스트 파일의 유효성 검사 유효성을 검사할 매니페스트로의 경로 WinGet에 대해 자세한 로깅을 설정합니다. 입력 파일이 올바른 MSIX인지 확인하십시오. 지정된 버전을 사용합니다. 기본값은 최신 버전입니다. 패키지의 사용 가능한 버전을 표시합니다. 완료되기 전에 제공한 값이 요청되었습니다. 일치하는 버전을 찾을 수 없는 경우: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. 지정된 값과 일치하는 원본이 없는 경우: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. 구성된 원본은 다음과 같습니다. 정의된 원본이 없습니다. 'source add'를 사용해 추가하거나 'source reset'을 사용하여 기본값으로 다시 설정 {Locked="source add","source reset"} 찾음 경로가 디렉터리임: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. 파일이 없음: {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. 로컬 매니페스트 및 검색 쿼리 인수가 모두 제공되었습니다. 로그 Label displayed for diagnostic files containing information about the application use. 설치 프로그램이 정책에 의해 차단됨 설치 관리자가 보안 검사를 하지 못했습니다 바이러스 백신 제품이 설치 관리자의 감염을 보고합니다. 원본을 업데이트하지 못했습니다. {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. 설치된 패키지 목록을 검색하거나 매니페스트에서 직접 찾은 선택한 패키지를 제거합니다. 기본적으로 쿼리는 패키지의 ID, 이름 또는 모양과 대/소문자를 구분하지 않는 것이 어떻게 되아야 합니다. 적절한 옵션을 전달하여 다른 필드를 사용할 수 있습니다. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 지정된 패키지를 제거 패키지 제거를 시작하는 중... 성공적으로 제거됨 winget 이 패키지에 대한 제거 명령을 찾을 수 없습니다. 지원을 위해 패키지 게시자에게 문의하세요. {Locked="winget"} 제거가 중단됨 설치 종료 코드로 인해 제거하지 못함: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. 설치된 패키지 목록 내보내기 파일에 나열된 모든 패키지를 설치합니다. 파일에 있는 모든 패키지를 설치합니다. 결과를 기록할 파일 설치할 패키지를 설명하는 파일 지정한 원본에서 패키지 내보내기 설치된 패키지 목록을 파일에 기록합니다. 그런 다음 패키지를 import 명령과 함께 설치할 수 있습니다. {Locked="import"} 가져온 패키지를 하나 이상 설치하지 못했습니다. 가져오기에 필요한 원본이 설치되어 있지 않습니다. {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. 어떤 원본에서도 설치된 패키지를 사용할 수 없음: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. 어떤 원본에서도 설치된 패키지 버전을 사용할 수 없음: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. 가져오기 파일에 패키지가 없습니다. JSON 파일이 잘못되었습니다. 패키지가 이미 설치되어 있음: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. 사용할 수 없는 패키지 무시 내보내기 파일에 패키지 버전 포함 가져오기 파일에서 패키지 버전 무시 경로가 존재하지 않음: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. JSON 파일에서 인식되는 스키마를 지정하지 않습니다. 설치 범위 선택(user 또는 machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. '{0}' 인수에 제공된 값이 잘못되었습니다. 유효한 값: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. 그룹 정책 이 작업을 사용하지 않도록 설정했습니다. {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. 추가 Windows 앱 설치 관리자 원본 사용 Windows 앱 설치 관리자 허용된 원본 사용 Windows 앱 설치 관리자 기본 원본 사용 Windows 앱 설치 관리자 실험적 기능 사용 Windows 앱 설치 관리자 Microsoft Store 소스 사용 Windows App 설치 관리자 글꼴 원본 활성화 Windows 패키지 관리자 설정 사용 Windows 패키지 관리자 사용 Windows 패키지 관리자 명령줄 인터페이스 활성화 Windows 패키지 관리자 원본 자동 업데이트 간격(분) 설정 그룹 정책 Header for a table listing active Group Policies Windows 앱 설치 관리자 로컬 매니페스트 파일 사용 Windows 앱 설치 관리자 Microsoft Store 원본 고정 인증서 바이패스 사용 Windows 앱 설치 관리자 로컬 보관 맬웨어 검사 재정의 사용 필드: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. 잘못된 필드 형식입니다. 잘못된 필드 값 그룹 정책에서 잘못된 설정 백업 파일의 설정이 로드된 경우 오류 구문 분석 파일: 값: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. 다음 실험적 기능이 진행 중입니다. 그룹 정책으로 인해 구성을 사용할 수 없습니다. 설치 관리자 해시가 일치하지 않습니다. Windows 앱 설치 관리자 해시 재정의 사용 현재 원본을 그룹 정책에 대한 JSON으로 내보냅니다. 현재 원본 내보내기 추가 원본 An additional source required by policy. 허용된 원본 A source that the user is allowed to add. '{0}' 인수에 제공된 값이 잘못되었습니다. {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. 취소됨 외부 이 가져오기에서 찾은 패키지에는 다음과 같은 종속성이 있습니다. Import command sentence showed before reporting dependencies 이 패키지에는 다음 종속성이 필요합니다. Message shown before reporting dependencies 종속성 설치 중: 종속성 원본을 찾을 수 없음 패키지 검색 결과 두 개 이상 생성: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. 패키지에 대한 최신 버전을 찾을 수 없음: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. 설치 프로그램을 찾을 수 없음: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. 패키지에 사용할 수 없는 최소 필수 버전: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. 일치하는 항목 없음 When package search yields no matches Loop 있음 Dependency graph has loop 매니페스트에 적합한 설치 관리자를 찾을 수 없습니다. {0} 버전 {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. 패키지 종속성을 처리하는 동안 오류가 발생했습니다. 설치를 계속하시겠나요? Prompt message shown when dependencies processing yields errors. 패키지 종속성을 처리하는 동안 오류가 발생했습니다. 종료하는 중... 패키지 이 패키지에 더 이상 필요하지 않을 수 있는 종속성이 있습니다. Uninstall command sentence showed before reporting dependencies 매니페스트에 유효성이 검사되지 않은 다음 종속성이 있습니다. 올바른지 확인하세요. Validate command sentence showed before reporting dependencies Windows 기능 Windows 라이브러리 지정된 원본을 사용하여 패키지 종속성 찾기 For getting package type dependencies when installing from a local manifest Windows 스토어 사용 약관 패키지에 대한 모든 사용권 계약 수락 내보낸 패키지를 설치하려면 사용권 계약이 필요합니다. {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. 게시자는 설치하기 전에 위의 정보를 보고 계약에 동의해야 합니다. 사용 약관에 동의하십니까? 패키지 계약에 동의하지 않았습니다. 작업이 취소되었습니다. 계약: 만든 이: 설명: 설치 관리자: 설치 관리자 로캘: Store 제품 ID: 설치 관리자 SHA256: 설치 관리자 유형: 설치 관리자 URL: 라이선스: 라이선스 URL: 모니커: 홈페이지 게시자: 태그: 버전: 종속성: Windows 기능 Windows 라이브러리: 패키지 종속성: 외부 종속성: 아니요 추가된 원본을 열지 못했습니다. 원본 작업 중 모든 원본 계약 수락 '{0}' 원본을 사용하려면 다음 계약을 확인해야 합니다. {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. 모든 원본 사용 약관에 동의하십니까? 원본 계약 중 하나 이상이 동의하지 않았습니다. 작업이 취소되었습니다. 원본 계약에 동의하거나 해당 원본을 제거하세요. 원본이 제대로 작동하려면 현재 컴퓨터의 두 글자 지리적 지역을 백 엔드 서비스로 보내야 합니다(예: "미국"). 설치되었습니다. 업그레이드를 완료하려면 애플리케이션을 다시 시작하세요. Windows-Package-Manager REST 원본 HTTP 헤더(선택 사항) 선택적 헤더는 이 원본에 적용할 수 없으므로 무시합니다. 원본을 지정하지 않으면 선택적 헤더를 적용할 수 없습니다. '{0}' {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. 릴리스 날짜: 지원되는 오프라인 배포: 게시자 URL: 구매 URL: 게시자 지원 URL: 개인 정보 URL: 저작권: 저작권 URL: 릴리스 정보: 릴리스 정보 URL: 원본을 검색하는 동안 실패했습니다. 결과가 포함되지 않습니다. {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. 원본을 검색하는 동안 실패함: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. 관리자가 이 기능을 사용하도록 설정해야 합니다. 사용하도록 설정하려면 관리자로 'winget settings --enable {0}'를 실행하세요. {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. 특정 관리자 설정을 사용하도록 설정합니다. 특정 관리자 설정을 사용하지 않도록 설정합니다. 관리자 설정 '{0}'을(를) 사용하도록 설정했습니다. {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. 관리자 설정 '{0}'을(를) 사용하지 않도록 설정했습니다. {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. 관리자 설정 Header for a table displaying admin settings. 응용 프로그램이 현재 실행 중입니다. 애플리케이션을 종료한 후 다시 시도하세요. 설치 관리자가 수정한 파일을 현재 다른 응용 프로그램에서 사용하고 있습니다. 응용 프로그램을 종료한 후 다시 시도하십시오. 다른 설치가 이미 진행 중입니다. 나중에 다시 시도하세요. 하나 이상의 파일을 사용하고 있습니다. 애플리케이션을 종료한 후 다시 시도하세요. 이 패키지에 시스템에 종속성이 없습니다. PC에 더 이상 공간이 없습니다. 공간을 확보한 후 다시 시도하세요. 설치할 수 있는 메모리가 부족합니다. 다른 애플리케이션을 닫은 후 다시 시도하세요. 설치 매개 변수 중 하나가 잘못되었습니다. 패키지 설치 로그에 추가 정보가 있을 수 있습니다. 이 응용 프로그램을 사용하려면 인터넷에 연결해야 합니다. 네트워크에 연결한 후 다시 시도하세요. 설치하는 동안 이 응용 프로그램에 오류가 발생했습니다. 고객 지원에 문의하세요. PC를 다시 시작하여 설치를 완료합니다. 설치를 마치기 위해 PC가 다시 시작됩니다. 설치하지 못했습니다. PC를 다시 시작한 후 다시 시도하세요. 설치를 취소했습니다. 이 응용 프로그램의 다른 버전이 이미 설치되어 있습니다. 이 응용 프로그램의 상위 버전이 이미 설치되어 있습니다. 조직 정책으로 인해 설치할 수 없습니다. 관리자에게 문의하세요. 현재 시스템 구성에서는 이 패키지 설치를 지원하지 않습니다. 사용자 지정 설치 관리자 오류로 인해 설치하지 못했습니다. 패키지 지원에 문의하세요. 설치 중단됨 설치 관리자가 종료 코드로 인해 실패함: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. 설치 관리자 로그는 다음 위치에서 사용할 수 있음: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. 다음 패키지가 작업 원본에서 발견되었습니다. 계속하려면 --source 옵션을 사용하여 둘 중 하나를 지정하십시오. {Locked="--source"} "working sources" as in "sources that are working correctly" 작업 원본 중 패키지를 찾을 수 없습니다. "working sources" as in "sources that are working correctly" 이 릴리스는 Windows 패키지 관리자의 안정적인 릴리스입니다. 실험적인 기능을 사용해 보려면 시험판 빌드를 설치하세요. 지침은 GitHub(https://github.com/microsoft/winget-cli)에서 확인할 수 있습니다. {Locked="https://github.com/microsoft/winget-cli"} Windows 패키지 관리자 v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. 가져오기 파일의 패키지 버전 무시 요청한 결과 수는 1에서 1000 사이여야 합니다. 기본값 외에도 설치 관리자에 전달할 인수 최신 버전을 찾았지만 설치 기술이 현재 설치된 버전과 다릅니다. 패키지를 제거하고 최신 버전을 설치하세요. 지정된 최신 버전의 설치 기술이 현재 설치된 버전과 다릅니다. 패키지를 제거하고 최신 버전을 설치하세요. Windows 패키지 관리자(미리 보기) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. 아키텍처를 선택하세요 현재 버전을 확인할 수 없는 경우에도 패키지 업그레이드 현재 버전을 확인할 수 없는 경우에도 패키지를 나열합니다. --upgrade-available 인수와 함께만 사용할 수 있습니다. {Locked="--upgrade-available"} 이 패키지의 버전 번호를 확인할 수 없습니다. 그래도 업그레이드하려면 이전 명령에 인수 --include-unknown 추가하세요. {Locked="--include-unknown"} {0} 패키지에 확인할 수 없는 버전 번호가 있습니다. 모든 결과를 보려면 --include-unknown 사용하세요. {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} 패키지가 고정되어 있으므로 명시적으로 업그레이드해야 합니다. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. 제공된 인수는 쿼리에서만 사용할 수 있습니다. 시스템 아키텍처: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). 패키지에서 만든 모든 파일 및 디렉터리를 유지합니다(이식 가능). 패키지 디렉터리의 모든 파일 및 디렉터리를 삭제합니다(이식 가능). 계속하려면 Enter 키를 누르세요 . . . 실행 파일의 이름을 바꿀 값입니다(이식 가능). 종료하기 전에 아무 키나 누르라는 메시지를 사용자에게 표시합니다. 설치 관리자가 관리자 권한으로 실행을 요청합니다. 프롬프트가 표시됩니다. 관리자 컨텍스트에서 설치 관리자를 실행할 수 없습니다. 경로 환경 변수 수정됨; 셸을 다시 시작하여 새 값을 사용합니다. 제품 코드를 사용하여 필터링 이름이 같지만 다른 원본의 이식 가능한 패키지가 이미 있습니다. --force로 인해 계속 진행 중 {Locked="--force"} 볼륨에서 다시 지점 구문 분석을 지원하지 않습니다. 지정한 파일 이름이 올바른 파일 이름이 아닙니다. 기존 파일 덮어쓰기: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. 패키지 선택 인수가 제공되지 않았습니다. 패키지 찾기에 대한 자세한 내용은 도움말을 참조하세요. 명령줄 별칭이 추가되었습니다. 이식 가능한 링크 디렉터리(컴퓨터) 이식 가능한 링크 디렉터리(사용자) 이식 가능한 패키지 루트(사용자) 이식 가능한 패키지 루트 이식 가능한 패키지 루트(x86) 이식 가능한 설치에 실패했습니다. 정리 중... 휴대용 패키지가 수정되었습니다. --force로 인해 계속 진행 {Locked="--force"} 휴대용 패키지가 수정되었으므로 제거할 수 없습니다. 이 검사를 재정의하려면 --force 사용하세요. {Locked="--force"} 파일은 설치 디렉터리에 남아 있음: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. 설치 디렉터리를 제거하는 중... WinGet에서 만들지 않았으므로 설치 디렉터리를 제거할 수 없습니다. 관련 링크 설명서: 메모: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. 설치 정보: 제공된 인수는 이 패키지에 대해 지원되지 않습니다. 보관 파일의 내용을 추출하지 못했습니다. 중첩된 설치 관리자 파일이 없습니다. 중첩 설치 관리자의 지정한 상대 경로가 일치하는지 확인하세요. {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. 중첩된 설치 관리자에 대한 상대 파일 경로가 잘못되었습니다. 경로가 설치 디렉터리 외부의 위치를 가리킵니다. 이 패키지에 대해 중첩된 설치 관리자가 지정되지 않았습니다. 보관 설치 관리자의 경우, 이식 가능 또는 글꼴 중첩 설치 관리자가 아니면 중첩 설치 관리자를 하나만 지정할 수 있습니다. 제공된 호환되지 않는 명령줄 인수 업그레이드를 사용할 수 있는 패키지만 나열합니다. 다음 패키지에는 업그레이드를 사용할 수 있지만 업그레이드를 위해 명시적 대상 지정이 필요합니다. "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly 다운로드 중 Label displayed while downloading an application installer. 중첩된 설치 관리자 유형은 지원되지 않습니다. 이 설치 관리자는 터미널 또는 셸을 다시 시작하는 것으로 알려져 있습니다. 이 패키지에는 설치 위치가 필요합니다. 다음 설치 관리자는 터미널 또는 셸을 다시 시작하는 것으로 알려져 있습니다. 다음 설치 관리자에는 설치 위치가 필요합니다. 설치 루트 지정: 계속하시겠습니까? 다음에 대한 계약 This will be followed by a package name, and then a list of license agreements 설치 위치가 패키지에 필요하지만 제공되지 않았습니다. 대화형 프롬프트 사용 안 함 Description for a command line argument, shown next to it in the help 이미 설치된 기존 패키지를 찾았습니다. 설치된 패키지를 업그레이드하는 중... 명령을 직접 실행하고 보안과 관련되지 않은 문제를 계속 진행합니다. Description for a command line argument, shown next to it in the help 다른 원본의 휴대용 패키지가 이미 있습니다. 보관 파일을 추출했습니다. 보관 파일을 추출하는 중... 보관 검색에서 맬웨어가 검색되었습니다. 이 검사를 재정의하려면 --ignore-local-archive-malware-scan 사용하세요. {Locked="--ignore-local-archive-malware-scan"} 보관 검색에서 맬웨어가 검색되었습니다. --ignore-local-archive-malware-scan 때문에 계속 {Locked="--ignore-local-archive-malware-scan"} 설치된 버전이 이미 있는 경우 업그레이드를 건너뜁니다. 패키지 버전이 이미 설치되어 있습니다. 설치가 취소되었습니다. {0} 사용하도록 설정할 수 없습니다. 이 설정은 정책에 의해 제어됩니다. 자세한 내용은 시스템 관리자에게 문의하십시오. {Locked="{0}"} The value will be replaced with the feature name {0} 사용하지 않도록 설정할 수 없습니다. 이 설정은 정책에 의해 제어됩니다. 자세한 내용은 시스템 관리자에게 문의하십시오. {Locked="{0}"} The value will be replaced with the feature name 새 핀을 추가합니다. 핀을 사용하면 Windows 패키지 관리자 패키지를 특정 버전의 범위로 업그레이드하지 못하도록 제한하거나 패키지를 완전히 업그레이드하지 못할 수 있습니다. 고정된 패키지는 자체적으로 업그레이드되고 Windows 패키지 관리자 외부에서 업그레이드될 수 있습니다. 기본적으로 고정된 패키지는 'upgrade' 명령에서 명시적으로 언급하거나 'winget upgrade --all'에 '--include-pinned' 플래그를 추가하여 업그레이드할 수 있습니다. {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} 새 핀 추가 하위 명령을 사용하여 패키지 핀을 관리합니다. 핀은 Windows 패키지 관리자 패키지를 특정 버전의 범위로 업그레이드하지 못하도록 제한하거나 패키지를 완전히 업그레이드하지 못하게 할 수 있습니다. 고정된 패키지는 여전히 자체적으로 업그레이드되고 Windows 패키지 관리자 외부에서 업데이트될 수 있습니다. 패키지 핀 관리 현재의 모든 핀 또는 특정 핀의 전체 세부 정보를 목록에 추가합니다. 현재 핀 나열 특정 패키지 핀을 제거합니다. 패키지 핀 제거 모든 기존 핀을 다시 설정합니다. 핀을 다시 설정하시겠습니까? 패키지를 고정할 버전입니다. 와일드카드 '*'는 마지막 버전 부분으로 사용할 수 있습니다. PIN이 제거될 때까지 업그레이드 차단, 인수 재정의 방지 설치된 특정 버전 고정 설치됨 Value used in a table to indicate that a package comes from the list of packages installed in the machine 설정을 JSON으로 내보내기 내보내기 설정 사용자 설정 Label displayed for the file containing the user settings. 설정 파일을 로드할 수 없습니다. 기본값을 사용합니다. 설치된 패키지 범위 필터 선택(user 또는 machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. 고정이 추가됨 패키지 {0} 대한 PIN이 이미 있습니다. {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. 패키지 {0} PIN이 이미 있습니다. --force 인수로 인해 덮어씁니다. {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. 패키지 {0} 대한 PIN이 이미 있습니다. --force 인수를 사용하여 덮어쓰십시오. {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. 현재의 모든 핀을 다시 설정하는 중입니다. --force 인수를 사용하여 모든 핀을 다시 설정합니다. 다음 핀이 제거됩니다. {Locked="--force"} 고정 유형 패키지 {0}에 대한 핀이 없습니다. {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. 구성된 핀이 없습니다. Shown when listing or modifying existing pins if there are none. 고정을 다시 설정했습니다. Shown after resetting (deleting) all the pins 고정 데이터베이스를 열 수 없습니다. Error message for when we cannot open the database containing package pins. 단일 패키지에만 사용할 수 있는 인수가 제공되었습니다. 여러 상호 배타적 인수가 제공됨: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together 인수 {0} {1} 사용할 수 있습니다. {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" 사용 안 함 As in enabled/disabled 사용 As in enabled/disabled 상태 Header for a table listing the state (enabled/disabled) of Group Policies and Settings 패키지를 검색하는 데 사용되는 쿼리 패키지를 찾을 수 없음: '{0}' 패키지를 찾을 수 없습니다. {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. 다음에 대해 여러 패키지를 차음: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. 다음에 대한 검색이 실패함: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} 패키지에 업그레이드 전에 제거해야 하는 PIN이 있습니다. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade winget을 사용하여 패키지를 업그레이드할 수 없습니다. 게시자가 제공한 메서드를 사용하여 이 패키지를 업그레이드하세요. 차단되지 않는 PIN이 있는 경우에도 패키지 업그레이드 업그레이드를 방해하는 핀이 있는 경우에도 패키지를 나열합니다. --upgrade-available 인수와 함께만 사용할 수 있습니다. {Locked="--upgrade-available"} 고정을 제거했습니다. 최신 버전을 찾았지만 패키지에 업그레이드를 방지하는 핀이 있습니다. 패키지가 고정되어 있어 업그레이드할 수 없습니다. 'winget pin' 명령을 사용하여 핀을 보고 편집합니다. 일부 PIN 유형은 --include-pinned 인수를 사용하여 무시할 수 있습니다. {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} 패키지에 업그레이드를 방지하는 핀이 있습니다. 'winget pin' 명령을 사용하여 핀을 보고 편집합니다. --include-pinned 인수를 사용하면 더 많은 결과가 표시될 수 있습니다. {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages 시스템이 제공된 구성에 설명된 대로 원하는 상태와 일치하는지 확인합니다. 원하는 상태를 얻기 위해 프로세서를 다운로드/실행할 수 있습니다. 구성 및 프로세서를 적용하기 전에 신뢰할 수 있는지 확인해야 합니다. 시스템을 원하는 상태로 구성합니다. 제공된 구성의 세부 정보를 표시합니다. 기본적으로 시스템을 수정하지 않지만 일부 옵션을 사용하면 파일이 다운로드 및/또는 로드됩니다. 구성 세부 정보 표시 시스템이 제공된 구성에 설명된 대로 원하는 상태와 일치하는지 확인합니다. 원하는 상태를 테스트하기 위해 프로세서를 다운로드/실행할 수 있습니다. 구성 및 프로세서를 실행하기 전에 신뢰할 수 있는지 확인해야 합니다. 원하는 상태에 대해 시스템을 확인합니다. 구성 파일의 정확성을 검사합니다. 구성 파일의 유효성을 검사합니다. 구성 파일의 필드 '{0}' 형식이 잘못되었습니다. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. 구성 파일의 경로 구성 파일이 잘못되었습니다. 구성 파일 버전{0}을 알 수 없습니다. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. 대화형 프롬프트가 발생하지 않도록 구성 경고를 수락합니다. 적용 Indicates that this item is used to write state 어설션 Indicates that this item is used to check/assert the state rather than write to it 종속성: {0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. 일부 구성이 적용되지 않았습니다. 구성에 대한 자세한 정보를 가져오지 못했습니다. 알림 Indicates that this item is used to retrieve values for future use rather than writing them 로컬 Used to indicate that the item is present on the device. 모듈: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. 모듈: {0}, 작성자:{1}[{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. 설정: Label for the values that are used as inputs for this item when applying state 구성이 적용되었습니다. 단위를 적용했습니다. 시스템에 다른 구성이 적용되고 있습니다. 이 구성은 가능한 한 빨리... 실행하도록 선택한 구성 설정을 확실히 파악해야 합니다. Microsoft는 사용자가 작성하거나 가져온 구성 파일에 대해 책임을 지지 않습니다. 이 구성은 Windows의 설정을 변경하고, 소프트웨어를 설치하고, 소프트웨어 설정(보안 설정 포함)을 변경하고, 사용자 대신 타사 패키지 및 서비스에 대한 사용자 계약에 동의할 수 있습니다.  이 구성 파일을 실행하면 이러한 리소스 및 설정을 이해하고 동의하는 것입니다. 설치된 모든 응용 프로그램은 소유자에 의해 사용이 허가됩니다. Microsoft는 타사 패키지 또는 서비스에 대한 책임을 지지 않으며 라이선스를 부여하지도 않습니다. Legal approved. Do not change without approval. 구성을 검토했으며 계속 시스템에 적용하시겠습니까? PM approved. 구성이 비어 있습니다. [{0}] 기능을 찾을 수 없습니다. {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. Windows 기능 종속성을 사용하도록 설정하지 못했습니다. 설치를 계속하려면 '--force' 사용하세요. {Locked="--force"} Windows 기능을 완전히 사용하도록 설정하려면 다시 부팅해야 합니다. 이 검사 재정의하려면 '--force'를 사용하십시오. "Windows Feature(s)" is the name of the options Windows features setting. Windows 기능 종속성을 사용하도록 설정하지 못했습니다. --force 때문에 계속 "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} [{0} ]활성화하는 중... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. [{0}] 기능을 사용하도록 설정하지 못했습니다.{1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. Windows 기능을 완전히 사용하도록 설정하려면 다시 부팅해야 합니다. --force로 인해 계속 "Windows Feature(s)" is the name of the options Windows features setting. 다른 설치/제거가 완료되기를 기다리는 중... 고정된 버전 Table header for the version to which a package is pinned; meaning it should not update from that version. <자세한 내용은 로그 파일을 참고하세요> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. 구성 세부 정보를 검색하는 중 구성 시스템을 초기화하는 중 구성 파일을 읽는 중 시스템이 구성에서 어설션된 원하는 상태가 아닙니다. 알 수 없는 이유로 이 구성 단위가 실패했습니다. {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 구성으로 인해 구성 단위가 실패했습니다. {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 현재 시스템 상태를 가져오는 동안 구성 단위가 실패했습니다. 원하는 상태를 적용하는 동안 구성 단위가 실패했습니다. 현재 시스템 상태를 테스트하는 동안 구성 단위가 실패했습니다. 내부 오류로 인해 구성 단위가 실패했습니다. {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 사전 조건이 잘못되어 구성 단위가 실패했습니다. {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 시스템 상태로 인해 구성 단위가 실패했습니다. {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 실행하는 동안 구성 단위가 실패했습니다. {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 구성에 식별자 '{0}'가 여러 번 포함되어 있습니다. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. 구성에서 종속성 '{0}'을(를) 찾을 수 없습니다. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. 이 구성 단위는 수동으로 건너뛰었습니다. 구성 단위의 모듈은 같은 버전으로 여러 위치에서 사용할 수 있습니다. 구성 단위에 대한 모듈을 로드하지 못했습니다. 구성 단위와 일치하는 항목이 여러 개 있습니다. 모듈을 지정하여 올바른 모듈을 선택하십시오. 구성 단위를 찾을 수 없습니다. 구성 단위가 모듈에 예상대로 없습니다. 종속성이 실패했거나 실행되지 않았기 때문에 이 구성 단위가 실행되지 않았습니다. 어설션이 실패했거나 false이므로 이 구성 단위가 실행되지 않았습니다. 구성 단위가 실행 중에 예기치 않은 결과를 반환했습니다. 알 수 없는 이유로 이 구성 단위가 실행되지 않았습니다. {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 필드 '{0}' 값이 잘못되었습니다. {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. '{0}' 필드가 없거나 비어 있습니다. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. 파일의 줄 {0}, 열 {1} 참조하세요. {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. 작업 취소 중 AppInstaller용 스텁 패키지 설치 AppInstaller의 전체 패키지 설치 확장 기능을 활성화합니다. 저장소 액세스 권한이 필요합니다. 옵션 '--enable' 및 '--disable' 다른 인수와 함께 사용할 수 없습니다. {Locked="--enable", "--disable"} 확장 기능을 활성화하는 중입니다. 저장소 액세스 권한이 필요합니다. 확장 기능을 사용할 수 없습니다. 'winget configure --enable'를 실행하여 사용하도록 설정하세요. {Locked="winget configure --enable"} 확장 기능이 활성화되어 있습니다. 확장 기능을 비활성화합니다. 저장소 액세스 권한이 필요합니다. 확장 기능을 비활성화하는 중입니다. 저장소 액세스 권한이 필요합니다. 확장 기능이 비활성화되어 있습니다. 패키지 종속성 및 Windows 기능 처리를 건너뜁니다. 종속성을 건너뛰었습니다. 프로세스의 PATH 변수를 새로 고치지 못했습니다. PATH 변수 변경에 따라 후속 설치가 실패할 수 있습니다. {Locked="PATH"} 상태를 테스트하는 동안 일부 구성 단위가 실패했습니다. 시스템이 설명된 구성 상태에 있습니다. 구성 상태가 테스트되지 않았습니다. 시스템이 설명된 구성 상태가 아닙니다. 예상치 못한 테스트 결과: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). 구성을 검토했으며 시스템에 대해 계속해서 확인하시겠습니까? 구성 파일이 올바른 YAML 파일이 아닙니다. {Locked="YAML"} YAML is a file format name. 이 구성 단위는 종속성 주기의 일부입니다. 유효성 검사 결과 문제가 발견되지 않았습니다. 구성 단위는 시험판으로만 제공되지만 구성에는 그런 방식으로 표시되지 않습니다. `directives`에 `allowPrerelease: true`를 추가합니다. {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. 구성 단위를 로컬에서 찾았지만 구성된 카탈로그에서 찾을 수 없습니다. 구성을 적용하기 전에 시스템에 있는지 확인하세요. 구성 단위는 공개적으로 사용할 수 없습니다. 이 구성을 사용하는 사람은 누구나 해당 구성에 액세스할 수 있는지 확인하세요. 모듈이 제공되지 않았습니다. 모듈을 지정하면 성능이 향상되고 이후 이름 충돌이 방지됩니다. 구성된 소스를 검색하거나 매니페스트에서 직접 찾은 선택한 패키지에서 설치 관리자를 다운로드합니다. 기본적으로 쿼리는 대/소문자를 구분하지 않는 패키지의 ID, 이름 또는 모니커와 일치해야 합니다. 다른 필드는 해당 옵션을 전달하여 사용할 수 있습니다. 기본적으로 다운로드 명령은 적절한 설치 관리자를 사용자의 다운로드 폴더에 다운로드합니다. 특정 패키지에서 설치 프로그램을 다운로드합니다. 설치 프로그램이 다운로드되는 디렉터리 종속성 다운로드 중: 설치 프로그램 다운로드됨: {0} {Locked="{0}"} Full path of the downloaded installer. 설치 프로그램 유형 선택 설치 프로그램 다운로드 나중에 오프라인 설치를 위해 설치 관리자를 다운로드할 수 없습니다. '--module-path allusers'를 실행하려면 관리자 권한이 필요합니다. {Locked="--module-path allusers"} 모듈을 저장할 로컬 컴퓨터의 위치를 지정합니다. 기본값 %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} '--module-path' 값은 'currentuser', 'allusers', 'default' 또는 절대 경로여야 합니다. {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Windows 패키지 관리자 구성 사용 오류에 대한 정보를 검색합니다. 숫자로 지정하면 출력에 winget 특정 오류인 경우 기호 이름을 포함하여 오류에 대한 세부 정보가 포함됩니다. 문자열이 지정되면 winget 관련 오류가 이 값을 검색합니다. 오류에 대한 정보 가져오기 오류 정보 내에서 검색할 값입니다. 주어진 숫자가 너무 커서 HRESULT가 될 수 없습니다. 알 수 없는 오류 코드 내부 오류 잘못된 명령줄 인수 명령을 실행하지 못했습니다. 매니페스트를 열지 못했습니다. 취소 신호 수신됨 ShellExecute를 실행하지 못했습니다. 매니페스트를 처리할 수 없습니다. 매니페스트 버전이 지원되는 버전보다 높습니다. 클라이언트를 업데이트하세요. 설치 관리자를 다운로드하지 못했습니다. 인덱스에 쓸 수 없습니다. 상위 스키마 버전입니다. 인덱스가 손상되었습니다. 구성된 원본 정보가 손상되었습니다. 원본 이름이 이미 구성되어 있습니다. 원본 유형이 잘못되었습니다. MSIX 파일은 패키지가 아닌 번들입니다. 원본에 필요한 데이터가 없습니다. 현재 시스템에 적용할 수 있는 설치 관리자가 없습니다. 설치 관리자 파일의 해시가 매니페스트와 일치하지 않습니다. 원본 이름이 없습니다. 원본 위치가 이미 다른 이름으로 구성되어 있습니다. 패키지를 찾을 수 없음 원본이 구성되지 않았습니다. 조건과 일치하는 여러 패키지가 있습니다. 조건과 일치하는 매니페스트를 찾을 수 없습니다. 원본 패키지에서 공용 폴더를 가져오지 못했습니다. 명령을 실행하려면 관리자 권한이 필요합니다. 원본 위치가 안전하지 않습니다. Microsoft Store 클라이언트가 정책에 의해 차단됨 Microsoft Store 앱이 정책에 의해 차단됨 이 기능은 현재 개발 중입니다. winget 설정을 사용하여 사용하도록 설정할 수 있습니다. Microsoft Store 앱을 설치하지 못했습니다. 자동 완성을 수행하지 못했습니다. YAML 파서 초기화 실패 잘못된 YAML 키가 발견되었습니다. 중복된 YAML 키가 발견되었습니다. 잘못된 YAML 작업 YAML 문서를 빌드하지 못했습니다. 잘못된 YAML 발신기 상태 잘못된 YAML 데이터 LibYAML 오류 매니페스트 유효성 검사에 성공했으나 경고가 발생했습니다. 매니페스트 유효성 검사 실패 매니페스트가 잘못되었습니다. 적용 가능한 업데이트를 찾을 수 없음 winget 업그레이드 -- 모두 실패로 완료됨 설치 관리자에서 보안 검사 실패 다운로드 크기가 예상 콘텐츠 길이와 일치하지 않습니다. 제거 명령을 찾을 수 없음 제거 명령을 실행하지 못했습니다. ICU 중단 반복기 오류 ICU casemap 오류 ICU regex 오류 가져온 패키지를 하나 이상 설치하지 못했습니다. 요청한 패키지를 하나 이상 찾을 수 없습니다. Json 파일이 잘못되었습니다. 원본 위치가 원격이 아닙니다. 구성된 rest 원본은 지원되지 않습니다. REST 원본에서 반환된 데이터가 잘못되었습니다. 작업이 그룹 정책 의해 차단되었습니다. Rest API 내부 오류 잘못된 REST 원본 URL REST API에서 반환된 지원되지 않는 MIME 형식 잘못된 REST 원본 계약 버전 원본 데이터가 손상되었거나 변조되었습니다. 스트림에서 읽는 동안 오류가 발생했습니다. 패키지 계약이 다음에 동의하지 않았습니다. 프롬프트에서 입력을 읽는 동안 오류가 발생했습니다. 하나 이상의 원본에서 검색 요청을 지원하지 않습니다. 나머지 API 엔드포인트를 찾을 수 없습니다. 원본을 열지 못했습니다. 원본 계약이 다음에 동의하지 않았습니다. 헤더 크기가 허용되는 제한인 1024자를 초과합니다. 크기를 줄이고 다시 시도하세요. 누락된 리소스 파일 MSI 설치를 실행하지 못했습니다. msiexec에 대한 인수가 잘못되었습니다. 하나 이상의 원본을 열지 못했습니다. 종속성 유효성을 검사하지 못했습니다. 하나 이상의 패키지가 없습니다. 잘못된 테이블 열 업그레이드 버전이 설치된 버전보다 최신 버전이 아닙니다. 업그레이드 버전을 알 수 없으며 재정의가 지정되지 않았습니다. ICU 변환 오류 휴대용 패키지를 설치하지 못했습니다. 볼륨에서 다시 지점 구문 분석을 지원하지 않습니다. 다른 원본의 휴대용 패키지가 이미 있습니다. symlink를 만들 수 없습니다. 경로가 디렉터리를 가리킵니다. 관리자 컨텍스트에서 설치 관리자를 실행할 수 없습니다. 휴대용 패키지를 제거하지 못했습니다. 인덱스에 대해 DisplayVersion 값의 유효성을 검사하지 못했습니다. 하나 이상의 인수가 지원되지 않습니다. 포함된 null 문자는 SQLite에 대해 허용되지 않습니다. 보관함에서 중첩 설치 관리자를 찾지 못했습니다. 보관 파일을 추출하지 못했습니다. 제공된 중첩 설치 관리자의 상대 파일 경로가 잘못되었습니다. 서버 인증서가 필요한 값과 일치하지 않습니다. 설치 위치를 제공해야 합니다. 보관 맬웨어 검색에 실패했습니다. 설치된 패키지의 버전을 하나 이상 찾았습니다. 패키지에 대한 핀이 이미 있습니다. 패키지에 대한 핀이 없습니다. 고정 데이터베이스를 열 수 없습니다. 하나 이상의 응용 프로그램을 설치하지 못했습니다. 하나 이상의 애플리케이션을 제거하지 못했습니다. 하나 이상의 쿼리가 정확히 하나의 일치 항목을 반환하지 않았습니다. 패키지에 업그레이드를 방지하는 PIN이 있습니다. 현재 설치된 패키지는 스텁 패키지입니다. 응용 프로그램 종료 신호 수신됨 패키지 종속성을 다운로드하지 못했습니다. 패키지를 다운로드하지 못했습니다. 오프라인 설치를 위해 다운로드할 수 없습니다. 필요한 서비스가 사용 중이거나 사용할 수 없습니다. 나중에 다시 시도하세요. 제공된 GUID가 올바른 다시 시작 상태에 해당하지 않습니다. 현재 클라이언트 버전이 저장된 상태의 클라이언트 버전과 일치하지 않습니다. 다시 시작 상태 데이터가 잘못되었습니다. 검사점 데이터베이스를 열 수 없습니다. 최대 다시 시작 제한을 초과했습니다. 잘못된 인증 정보입니다. 지원되지 않는 인증 방법입니다. 인증에 실패했습니다. 인증에 실패했습니다. 대화형 인증이 필요합니다. 인증에 실패했습니다. 사용자가 취소했습니다. 인증에 실패했습니다. 인증된 계정이 원하는 계정이 아닙니다. 응용 프로그램이 현재 실행 중입니다. 애플리케이션을 종료한 후 다시 시도하세요. 다른 설치가 이미 진행 중입니다. 나중에 다시 시도하세요. 하나 이상의 파일을 사용하고 있습니다. 애플리케이션을 종료한 후 다시 시도하세요. 이 패키지에 시스템에 종속성이 없습니다. PC에 더 이상 공간이 없습니다. 공간을 확보한 후 다시 시도하세요. 설치할 수 있는 메모리가 부족합니다. 다른 애플리케이션을 닫은 후 다시 시도하세요. 이 응용 프로그램을 사용하려면 인터넷에 연결해야 합니다. 네트워크에 연결한 후 다시 시도하세요. 설치하는 동안 이 응용 프로그램에 오류가 발생했습니다. 고객 지원에 문의하세요. PC를 다시 시작하여 설치를 완료합니다. 설치하지 못했습니다. PC를 다시 시작한 후 다시 시도하세요. 설치를 마치기 위해 PC가 다시 시작됩니다. 설치를 취소했습니다. 이 응용 프로그램의 다른 버전이 이미 설치되어 있습니다. 이 응용 프로그램의 상위 버전이 이미 설치되어 있습니다. 조직 정책으로 인해 설치할 수 없습니다. 관리자에게 문의하세요. 패키지 종속성을 설치하지 못했습니다. 현재 다른 응용 프로그램에서 응용 프로그램을 사용하고 있습니다. 잘못된 매개 변수입니다. 시스템에서 패키지가 지원되지 않습니다. 설치 관리자가 기존 패키지 업그레이드를 지원하지 않습니다. 사용자 지정 설치 관리자 오류로 설치하지 못했습니다. 패키지에 대한 앱 및 기능 항목을 찾을 수 없습니다. 설치 위치를 적용할 수 없습니다. 설치 위치를 찾을 수 없습니다. 기존 파일의 해시가 일치하지 않습니다. 파일을 찾을 수 없습니다. 파일을 찾았지만 해시가 확인되지 않았습니다. 파일에 액세스할 수 없습니다. 구성 파일이 잘못되었습니다. YAML 구문이 잘못되었습니다. 구성 필드에 잘못된 형식이 있습니다. 구성에 알 수 없는 버전이 있습니다. 구성을 적용하는 동안 오류가 발생했습니다. 구성에 중복 식별자가 포함되어 있습니다. 구성에 종속성이 없습니다. 구성에 만족스럽지 않은 종속성이 있습니다. 구성 단위에 대한 어설션이 실패했습니다. 구성을 수동으로 건너뛰었습니다. 사용자가 실행을 계속하기를 거부했습니다. 종속성 그래프 확인할 수 없는 주기가 포함되어 있습니다. 구성에 잘못된 필드 값이 있습니다. 구성에 필드가 없습니다. 상태를 테스트하는 동안 일부 구성 단위가 실패했습니다. 구성 상태가 테스트되지 않았습니다. 구성 단위가 설치되지 않았습니다. 구성 단위를 찾을 수 없습니다. 구성 단위와 일치하는 항목이 여러 개 있습니다. 모듈을 지정하여 올바른 모듈을 선택하십시오. 현재 시스템 상태를 가져오는 동안 구성 단위가 실패했습니다. 현재 시스템 상태를 테스트하는 동안 구성 단위가 실패했습니다. 원하는 상태를 적용하는 동안 구성 단위가 실패했습니다. 구성 단위의 모듈은 같은 버전으로 여러 위치에서 사용할 수 있습니다. 구성 단위에 대한 모듈을 로드하지 못했습니다. 구성 단위가 실행 중에 예기치 않은 결과를 반환했습니다. 구성 루트가 필요한 설정이 단위에 포함되어 있습니다. 구성 프로세서에서 지원하지 않는 작업입니다. 사용할 수 없음 Windows 기능 종속성을 사용하도록 설정함 구성 단위를 실행하려면 관리자 권한이 필요하므로 구성 단위에 대한 모듈을 로드하지 못했습니다. 구성 루트가 필요한 설정이 단위에 포함되어 있습니다. 구성 단위를 실행하려면 관리자 권한이 필요하므로 구성 단위에 대한 모듈을 로드하지 못했습니다. 저장된 명령의 고유 식별자를 전달하여 이전에 저장한 명령의 실행을 다시 시작합니다. 다시 부팅으로 인해 종료되었을 수 있는 실행된 명령을 다시 시작하는 데 사용됩니다. 이전에 저장된 명령의 실행을 다시 시작합니다. 다시 시작할 저장된 상태의 고유 식별자입니다. 다른 클라이언트 버전에서 상태를 다시 시작하는 것은 지원되지 않습니다. {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. 다시 시작 상태가 없습니다. {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. 다시 시작 상태에서 데이터를 찾을 수 없습니다. 이 명령은 다시 시작이 지원되지 않습니다. 해당하는 경우 다시 부팅 허용 작업을 완료하기 위해 다시 부팅을 시작하는 중... 다시 부팅을 시작하지 못했습니다. 다시 시작 작업이 허용되는 {0} 다시 시작 제한을 초과합니다. 수동으로 다시 시작하려면 '{1}' 명령을 실행하십시오. {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. 저장된 상태 다시 시작 시 제한 무시 {0} URI 체계는 지원되지 않습니다. {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. URI 형식이 잘못되었습니다. {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. 구성 단위 설정 콘텐츠 또는 설정 콘텐츠가 비어 {0} 구문 분석하지 못했습니다. {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. {0} 구성 단위에 필수 인수가 없습니다. {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. {0} 구성 단위에 권장 인수가 없습니다. {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. WinGetSource 구성 단위가 알려진 원본과 충돌합니다. {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetSource 구성 단위는 타사 원본에서 어설션합니다. {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage가 UseLatest와 Version을 모두 선언합니다. 패키지 ID: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} WinGetPackage 구성 단위는 타사 소스의 패키지에 대해 어설션합니다. 패키지 ID: {0}; 원본: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 구성 단위 패키지는 이전에 구성되지 않은 타사 원본에 종속됩니다. 패키지 ID: {0}; 원본: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 구성 단위 패키지는 타사 원본에 종속됩니다. uni dependsOn 섹션에 종속성을 선언하는 것이 좋습니다. 패키지 ID: {0}; 원본: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} WinGetPackage 구성 단위 패키지의 유효성을 검사할 수 없습니다. 원본을 열지 못했습니다. 패키지 ID: {0}; 원본: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 구성 단위 패키지의 유효성을 검사할 수 없습니다. 패키지를 찾을 수 없습니다. 패키지 ID: {0} {Locked="WinGetPackage,{0}"} WinGetPackage 구성 단위 패키지의 유효성을 검사할 수 없습니다. 패키지를 두 개 이상 찾았습니다. 패키지 ID: {0} {Locked="WinGetPackage,{0}"} WinGetPackage 구성 단위 패키지의 유효성을 검사할 수 없습니다. 패키지 버전을 찾을 수 없습니다. 패키지 ID: {0}; 버전 {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 구성 단위 패키지는 특정 버전으로 지정되지만 하나의 패키지 버전만 사용할 수 있습니다. 패키지 ID: {0}, 버전: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 구성 단위 패키지의 유효성을 검사할 수 없습니다. 패키지 ID: {0} {Locked="WinGetPackage,{0}"} 인증 창 기본 설정 지정(silent, silentPreferred 또는 interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. 인증에 사용할 계정 지정 원본을 추가하지 못했습니다. 이 winget 버전에서는 원본의 인증 방법을 지원하지 않습니다. 최신 winget 버전으로 업그레이드해 보세요. {Locked="winget"} {0} 원본을 인증해야 합니다. 필요한 경우 인증 프롬프트가 표시될 수 있습니다. 인증된 정보는 액세스 권한 부여를 위해 원본과 공유됩니다. {Locked="{0}"} 설치된 패키지 목록을 검색하거나 매니페스트에서 직접 선택한 패키지를 복구합니다. 기본적으로 쿼리는 대/소문자를 구분하지 않는 패키지의 ID, 이름 또는 모니커와 일치해야 합니다. 다른 필드는 해당 옵션을 전달하여 사용할 수 있습니다. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 선택한 패키지를 복구합니다. 이 패키지에 대한 복구 명령을 찾을 수 없습니다. 지원을 받으려면 패키지 게시자에 문의하세요. 사용 중인 설치 관리자 기술이 현재 설치된 버전과 일치하지 않습니다. 복구 명령을 찾을 수 없습니다. 사용 중인 설치 관리자 기술이 복구를 지원하지 않습니다. 복구 작업이 완료되었습니다. 복구 중단됨 패키지 복구를 시작하는 중... Microsoft Store 패키지를 복구하지 못했습니다. 오류 코드: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. 복구 작업에 실패했습니다. 복구 작업은 적용할 수 없습니다. 구성된 원본에서 사용할 수 있는 일치하는 패키지 버전이 없습니다. 현재 시스템 구성은 이 패키지의 복구를 지원하지 않습니다. 사용 중인 설치 관리자 기술이 복구를 지원하지 않습니다. 관리자 권한으로 실행하는 경우 사용자 scope 위해 설치된 패키지를 복구할 수 없습니다. 사용자 scope 내에 설치된 패키지에서는 관리자 권한과 관련된 복구 작업이 허용되지 않습니다. 복구하지 못했습니다. 종료 코드: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. 손상을 방지하기 위해 SQLite 연결을 종료했습니다. 모든 버전 제거 작업할 버전 이 패키지의 여러 버전이 설치되어 있습니다. 검색을 구체화하거나,'--version' 인수를 전달하여 하나를 선택하거나, '--all-versions' 플래그를 전달하여 모두 제거하세요. {Locked="--version,--all-versions"} 프록시 명령줄 옵션 Windows 패키지 관리자 사용 Describes a Group Policy that can enable the use of the --proxy option to set a proxy 이 실행에 사용할 프록시 설정 이 실행에 프록시 사용 안 함 {0} 다시 설정할 수 없습니다. 이 설정은 정책에 의해 제어됩니다. 자세한 내용은 시스템 관리자에게 문의하십시오. {Locked="{0}"} The value will be replaced with the feature name 관리자 설정 ‘{0}’을(를) 초기화합니다. {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. {0} 설정할 수 없습니다. 이 설정은 정책에 의해 제어됩니다. 자세한 내용은 시스템 관리자에게 문의하십시오. {Locked="{0}"} The value will be replaced with the feature name 관리자 설정 '{0}'을(를) '{1}'(으)로 설정합니다. {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced 수정할 설정의 이름 설정에 대해 설정할 값입니다. 관리자 설정을 기본값으로 다시 설정합니다. 관리자 설정을 기본값으로 다시 설정합니다. 관리자 설정의 값을 설정합니다. 관리자 설정의 값을 설정합니다. 지정하지 않는 한 검색에서 원본 제외 검색에서 원본 제외(true 또는 false) 유해 콘텐츠 원본의 신뢰 수준(없음 또는 신뢰할 수 없음) 신뢰 수준 Microsoft Store 패키지 카탈로그를 가져오지 못했습니다. Microsoft Store 패키지 카탈로그에서 적용 가능한 Microsoft Store 패키지를 찾을 수 없습니다. Microsoft Store 패키지 다운로드 정보를 가져오지 못했습니다. 다운로드할 적용 가능한 Microsoft Store 패키지를 찾을 수 없습니다. Microsoft Store 패키지 라이선스를 검색하지 못했습니다. 해당하는 Microsoft Store 패키지를 찾을 수 없습니다. Microsoft Store 패키지 해시가 성공적으로 확인되었습니다. Microsoft Store 패키지 해시 불일치 Microsoft Store 패키지 다운로드됨: {0} {Locked="{0}"} Full path of the downloaded package. Microsoft Store 패키지 다운로드 실패: {0} {Locked="{0}"} Package name. Microsoft Store 패키지 다운로드 완료 Microsoft Store 기본 패키지를 다운로드하는 중... Microsoft Store에서 종속성 패키지를 다운로드하는 중... Microsoft Store 패키지 다운로드 정보 검색 Microsoft Store 패키지 다운로드 정보를 검색하지 못했습니다. Microsoft Store 패키지 라이선스 검색 Microsoft Store 패키지 라이선스 저장됨: {0} {Locked="{0}"} License file full path. Microsoft Store 패키지 라이선스를 검색하지 못했습니다. Microsoft Store 패키지 다운로드는 --rename 인수를 지원하지 않습니다. Microsoft Store 패키지는 Microsoft Store 카탈로그에서 제공하는 이름을 사용합니다. {Locked="--rename"} Microsoft Store 패키지를 다운로드하려면 Microsoft Entra ID 인증이 필요합니다. 필요한 경우 인증 프롬프트가 표시될 수 있습니다. 인증된 정보는 액세스 권한 부여를 위해 Microsoft 서비스 공유됩니다. Microsoft Store 패키지 라이선스의 경우 Microsoft Entra ID 계정은 전역 관리자, 사용자 관리자 또는 라이선스 관리자의 구성원이어야 합니다. {Locked="Global Administrator,User Administrator,License Administrator"} Microsoft Store 패키지 오프라인 라이선스 검색 건너뛰기 대상 플랫폼 선택 구성 파일 추가: {0} {Locked="{0}"} 내보냈습니다. 구성 설정을 가져오는 중... --resource가 있는 --packageId 및/또는 --module 이상을 제공해야 합니다. 또는 --all을 사용하여 모든 패키지 구성을 내보냅니다. {Locked="--packageId,--module, --resource, --all"} --packageId, --module 및 --resource 인수는 --all과 함께 사용할 수 없습니다. {Locked="--packageId,--module, --resource, --all"} 구성 리소스를 구성 파일로 내보냅니다. --all과 함께 사용하면 모든 패키지 구성을 내보냅니다. --packageId와 함께 사용하면 지정된 패키지 ID의 WinGetPackage 리소스를 내보냅니다. --module 및 --resource와 함께 사용하면 리소스의 설정을 가져오고 구성 파일로 내보냅니다. 출력 구성 파일이 이미 있으면 내보낸 구성 리소스를 추가합니다. {Locked="WinGetPackage,--packageId,--module, --resource"} 구성 리소스를 구성 파일로 내보냅니다. 내보낼 리소스의 모듈입니다. 내보낼 패키지 식별자입니다. 내보낼 구성 리소스입니다. 모든 패키지 구성을 내보냅니다. 구성 단위가 해당 속성을 가져오지 못했습니다. 구성을 내보내지 못했습니다. {0} 구성 {Locked="{0}"} {0} 설치 {Locked="{0}"} 구성 파일에 있는 일부 데이터가 이 출력에 대해 잘렸습니다. 파일 콘텐츠를 검사하여 전체 콘텐츠를 검사합니다. <이 값이 잘렸습니다. 전체 텍스트를 보려면 파일 내용을 검사합니다> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. 시스템에 적용된 구성에 대한 높은 수준의 세부 정보를 표시합니다. 그런 다음 이 데이터를 `configure` 명령과 함께 사용하여 자세한 정보를 확인할 수 있습니다. {Locked="`configure`"} 구성 기록 표시 기록에 구성이 없습니다. 기록에서 항목 선택 제공된 데이터와 일치하는 단일 구성을 찾을 수 없습니다. 원하는 구성과 명확하게 일치하는 식별자의 전체 이름 또는 일부를 제공하십시오. 기록에서 항목 제거 처음 적용됨 Column header for date values indicating when a configuration was first applied to the system. 식별자 이름 원본 경로 지정한 구성을 찾을 수 없습니다. 완료 The state of processing an item. 진행 중 The state of processing an item. 보류 중 The state of processing an item. 알 수 없음 The state of processing an item. 구성 상태를 모니터링합니다. As in "to monitor the status of a configuration being applied". 완료 The state of processing an item. 진행 중 The state of processing an item. 보류 중 The state of processing an item. 건너뜀 The state of processing an item. 알 수 없음 The state of processing an item. 적용 시작됨 When the configuration application started. 적용 종료됨 When the configuration application ended. 결과 세부 정보 상태 The state of processing an item. 단위 패키지가 현재 Windows 버전 또는 플랫폼과 호환되지 않습니다. 가능한 경우 초기 구성 세부 정보를 표시하지 않음 여러 플랫폼 및 아키텍처를 대상으로 하는 여러 Microsoft Store 패키지를 다운로드할 수 있습니다. --platform, --architecture를 사용하여 패키지를 필터링할 수 있습니다. {Locked="--platform--architecture"} Microsoft Store 패키지 라이선스를 검색하지 못했습니다. Microsoft Entra ID 계정이 전역 관리자, 사용자 관리자 또는 라이선스 관리자의 구성원이 아닙니다. --skip-license를 사용하여 Microsoft Store 패키지 라이선스 검색을 건너뜁니다. {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} Microsoft Store 패키지는 다운로드를 지원하지 않습니다. Microsoft Store 패키지는 다운로드를 지원하지 않습니다. Microsoft Store 패키지 라이선스를 검색하지 못했습니다. Microsoft Entra ID 계정에 필요한 권한이 없습니다. 무결성 경계를 넘어 매개 변수를 전달할 수 없습니다. 0 바이트 설치 관리자 다운로드됨; 네트워크 연결이 제대로 작동하는지 확인하십시오. 0 바이트 설치 관리자 다운로드됨; 네트워크 연결이 제대로 작동하는지 확인하십시오. 글꼴 관리 하위 명령으로 글꼴을 관리합니다. 글꼴은 packages와 비슷하게 설치, 업그레이드 또는 제거할 수 있습니다. 가족 얼굴 "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' 성으로 결과 필터링 얼굴 "Face" represents the typeface of a font family such as 'Bold' or 'Italic' 경로 입력 조건과 일치하는 설치된 글꼴을 찾을 수 없습니다. 설치된 글꼴 나열 설치된 모든 글꼴, 모든 글꼴 파일 또는 특정 글꼴의 전체 세부 정보를 나열합니다. 버전 구성 모듈 PowerShell Modules that are used for the Configuration feature 패키지 설치 관리자에 인증이 필요합니다. 필요한 경우 인증 프롬프트가 표시될 수 있습니다. 인증된 정보는 설치 관리자 다운로드 URL과 공유됩니다. 설치 관리자를 다운로드하지 못했습니다. 이 winget 버전에서는 설치 관리자 다운로드 인증 방법을 지원하지 않습니다. 최신 winget 버전으로 업그레이드해 보세요. {Locked="winget"} 설치 관리자를 다운로드하지 못했습니다. 인증에 실패했습니다. 구성 프로세서의 경로를 지정합니다. DSC v3 리소스 명령 DSC stands for "Desired State Configuration". It should already have a locked translation. 여기서 하위 명령은 WinGet 및 패키지를 구성하기 위한 DSC(Desired State Configuration) v3 리소스를 구현합니다. 리소스 상태 가져오기 리소스 상태 설정 필요한 상태 변경 설명하기 리소스 상태 테스트 리소스 상태 삭제 모든 상태 인스턴스 가져오기 그룹 콘텐츠 유효성 검사 외부 상태 해결 어댑터 실행 리소스 스키마 가져오기 리소스 매니페스트 가져오기 패키지 상태 관리 WinGet을 통해 패키지를 관리하세요. instance 있는지 여부를 나타냅니다. instance 원하는 상태에 있는지 여부를 나타냅니다. 패키지의 식별자입니다. 패키지의 원본입니다. 패키지 버전입니다. 식별자를 패키지와 일치시키는 메서드입니다. 사용 가능한 최신 버전의 패키지를 설치해야 합니다. 필요한 경우 사용할 설치 모드입니다. 패키지의 대상 범위입니다. 소스 및 패키지에 대한 계약에 동의할지 여부를 나타냅니다. 사용자 설정 파일 관리 WinGet의 사용자 설정을 관리합니다. 설정 JSON 파일 콘텐츠입니다. 설정을 적용하는 데 사용되는 작업입니다. 상관 관계를 위해 값이 기록되었습니다. 데이터 원본 구성 WinGet의 소스를 관리합니다. 원본에 사용할 이름입니다. 원본의 인수입니다. 원본의 유형입니다. 원본의 신뢰 수준입니다. 호출 시 원본이 지정되지 않았을 때 원본이 포함되는지 여부입니다. 구성 단위 적용 중... 단위를 내보내는 중... 구성 단위 프로세서를 가져오는 중... [{0}] 내보내기에 필요한 모듈을 확인하세요. {Locked="{0}"} 필수 모듈을 테스트하거나 가져오지 못했습니다. 관련 설정을 내보내지 않습니다. [{0}] 내보내기 {Locked="{0}"} 리소스를 내보내지 못했습니다. 단위 프로세서를 가져오지 못했습니다. 개별 패키지 설정을 내보내지 않습니다. 결과를 쓸 디렉터리 Desired State Configuration 패키지를 시스템에서 찾을 수 없습니다. 패키지... 설치 중 Desired State Configuration 패키지를 설치하지 못했습니다. 패키지를 수동으로 설치하거나 --processor-path 인수를 통해 dsc.exe 경로를 제공하십시오. {Locked="dsc.exe","--processor-path"} 관리자 설정 및 해당 값을 포함하는 개체입니다. 관리자 설정 관리 WinGet의 관리자 설정을 관리합니다. 모든 관리자 설정을 초기화합니다. 모든 관리자 설정이 초기화되었습니다. MCP 정보 MCP stands for Model Context Protocol and should probably remain as-is Windows 패키지 관리자를 위한 MCP(모델 컨텍스트 프로토콜) 정보입니다. MCP 클라이언트를 사용하여 Windows 패키지 관리자 MCP 서버를 수동으로 구성하려면 `servers` 개체에서 다음 JSON 조각을 사용하십시오. {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. WINDOWS 패키지 관리자 MCP 서버 활성화 대상 OS 버전 하나 이상의 글꼴을 설치하지 못했습니다. 글꼴 파일이 지원되지 않으므로 설치할 수 없습니다. 글꼴 패키지에 있는 하나 이상의 글꼴이 지원되지 않으므로 설치할 수 없습니다. 글꼴 설치 실패, 정리하는 중입니다. 글꼴이 이미 설치되어 있습니다. 패키지 ID WinGet 지원됨 제목 글꼴 파일을 찾을 수 없습니다. 글꼴을 제거하지 못했습니다. 글꼴 상태가 좋지 않을 수 있습니다. 다시 시작한 후 제거해 보세요. 글꼴 유효성 검사에 실패했습니다. 글꼴 롤백에 실패했습니다. 글꼴 상태가 좋지 않을 수 있습니다. 다시 시작한 후 제거해 보세요. 글꼴 파일의 자세한 정보를 표시합니다. 글꼴 패키지의 유효성을 검사하지 못했습니다. 실패한 글꼴 설치에 대한 롤백 시도가 실패했습니다. 글꼴을 제거하려면 다시 시작해야 할 수 있습니다. 글꼴 패키지를 제거하지 못했습니다. 시스템 또는 응용 프로그램에서 글꼴을 사용 중이기 때문인 경우가 많습니다. 컴퓨터를 다시 시작한 후 제거가 성공할 수 있습니다. 확인 "OK" means the font is in a good healthy state 손상됨 "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. 상태 알 수 없음 글꼴 패키지가 이미 설치되어 있습니다. 패키지에 대한 자세한 정보 표시 Providing this argument causes the CLI to output additional details about installed application packages. 채널: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). 로컬 식별자 Precedes a value that is the unique identifier for the installed package on the local system. 패키지 패밀리 이름: Precedes a value that is the APPX/MSIX package family name of the installed package. 제품 코드: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. 업그레이드 코드: Precedes a value that is the MSI Upgrade Code for the installed package. 설치된 범위: Precedes a value that is the scope of the installation of the package (ex. user, machine). 설치된 아키텍처: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). 설치된 지역: Precedes a value that is the locale of the installed package. 설치된 위치: Precedes a value that is the directory path to the installed package. 원본 소스: Precedes a value that names the package source where the installed package originated from. 사용 가능한 업그레이드: Precedes a list of package upgrades available for the installed package. 설치 관리자 범주: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). 이전 값 Column title for listing edit changes. 새 값 Column title for listing the new value. ================================================ FILE: Localization/Resources/pt-BR/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/pt-BR/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Alias adjacente não é um sinalizador: "{0}" {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Alias de sinalizador adjacente não encontrado: "{0}" {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Os seguintes argumentos estão disponíveis: Message displayed to inform the user about the available command line arguments. Os seguintes nomes alternativos de comando estão disponíveis: Message displayed to inform the user about the available command line alias arguments. Os seguintes comandos estão disponíveis: Title displayed to inform the user about the available commands. Disponível As in "a new version is available to upgrade to". As seguintes opções estão disponíveis: Message displayed to inform the user about the available options. Os seguintes subcomandos estão disponíveis: Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} atualizações disponíveis. {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Usar o canal especificado; o padrão é público geral comando Label displayed for a command to give the software. Filtrar resultados por comando Description message displayed to inform the user about filtering the search results by a package command. A linha de comando completa para conclusão Este comando requer privilégios de administrador para ser executado. Esse comando pode ser usado para solicitar a conclusão da linha de comando sensível ao contexto. A linha de comando, a posição do cursor e a palavra a ser completada são entregues. A saída é um conjunto de valores possíveis com base nas entradas, com um valor possível por linha. Habilita a conclusão da linha de comando sensível ao contexto Não mostrar mais do que o número especificado de resultados (entre 1 e 1000) Concluído Label displayed when an operation completes or is done executing. Localizar pacote usando uma correspondência exata Description message displayed to inform the user about finding an application package using an exact matching criteria. Argumento experimental para fins de demonstração Este comando é um exemplo de como implementar um recurso experimental. Para ativar, vá para 'winget settings' e habilite os recursos experimentalCmd ou experimentalArg. {Locked="winget settings"} Exemplo de recurso experimental Foi encontrado um argumento posicional quando nenhum era esperado: '{0}' {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. Este recurso é um trabalho em andamento e pode ser alterado radicalmente ou removido completamente no futuro. Para habilita-lo, edite suas configurações ('winget settings') para incluir o recurso experimental: '{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Mostra o status dos recursos experimentais. Os recursos experimentais podem ser ativados por 'winget settings'. {Locked="winget settings"} Mostra o status dos recursos experimentais Desabilitado Habilitado Recurso Link Os seguintes recursos experimentais estão em andamento. Eles podem ser configurados por meio do arquivo de configurações ' winget settings '. {Locked="winget settings"} Propriedade Status Arquivo em que será gerado o hash Argumento sinalizado não pode conter valores adjacentes: "{0}" {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Calcula o hash de um arquivo local, apropriado para entrada em um manifesto. Ele também pode calcular o hash do arquivo de assinatura de um pacote MSIX para habilitar instalações de streaming. Ajuda para arquivos do instalador de hash Mostra a ajuda sobre o comando selecionado Para obter mais detalhes sobre um comando específico, passe o argumento help. Mais tópicos de ajuda podem ser encontrados em: {0} {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Filtrar resultados por id Suprime saídas de aviso Este aplicativo é licenciado para você pelo proprietário. A Microsoft não é responsável por, nem concede licenças a pacotes de terceiros. Este pacote é fornecido por meio da Microsoft Store. O winget talvez precise adquirir o pacote da Microsoft Store em nome do usuário atual. {Locked="winget"} Instala o pacote selecionado, seja encontrado pesquisando uma origem configurada ou diretamente de um manifesto. Por padrão, a consulta deve corresponder a maiúsculas e minúsculas a id, name ou moniker do pacote. Outros campos podem ser usados passando sua opção apropriada. Por padrão, o comando install verificará o status do pacote instalado e tentará realizar uma atualização, se aplicável. Substitua por --force para executar uma instalação direta. {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Instalar um determinado pacote O hash do instalador não corresponde; Isto não pode ser substituído durante a execução como administrador O hash do instalador não corresponde; prosseguir devido à --ignore-security-hash {Locked="--ignore-security-hash"} O hash do instalador não corresponde; para substituir este uso de verificação --ignore-security-hash {Locked="--ignore-security-hash"} Hash do instalador verificado com êxito Instalado com êxito Iniciando a instalação do pacote... Ignorar a falha na verificação do código hash do instalador Ignorar a verificação de malware realizada como parte da instalação de um pacote de tipo de arquivo morto do manifesto local Solicitar instalação interativa; a entrada do usuário pode ser necessária O alias de argumento não foi reconhecido para o comando atual: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Especificador de argumento inválido: "{0}" {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). O nome do argumento não foi reconhecido para o comando atual: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Diretórios Winget Header for a table detailing the directories Winget uses for key operations like logging and portable installs Local para usar (formato BCP47) {Locked="BCP47"} Contrato de Licença Links Links to different webpages O comando list exibe os pacotes instalados no sistema, bem como se uma atualização está disponível. Opções adicionais podem ser fornecidas para filtrar a saída, assim como o comando de pesquisa (search). {Locked="list","search"} Exibir os pacotes instalados Local para instalar (se houver suporte) Local do log (se houver suporte) Copyright (c) Microsoft Corporation. Todos os direitos reservados. Home page The primary webpage for the software O caminho para o manifesto do pacote Falha na validação do manifesto. Êxito na validação do manifesto. Êxito na validação do manifesto com avisos. Valor de argumento obrigatório, mas nenhum encontrado: "{0}" {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Filtrar resultados por moniker O arquivo de entrada será tratado como msix; hash de assinatura será fornecido se assinado Falha ao calcular o hash de assinatura MSIX. Falha ao instalar ou atualizar o pacote da Microsoft Store porque o aplicativo específico está bloqueado pela política Falha ao instalar ou atualizar o pacote da Microsoft Store. Código de erro: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Falha ao instalar ou atualizar o pacote da Microsoft Store porque o cliente da Microsoft Store está bloqueado pela diretiva Verificando/solicitando aquisição de pacote... Vários pacotes instalados encontraram critérios de entrada correspondentes. Refine a entrada. Vários pacotes encontraram critérios de entrada correspondentes. Refine a entrada. Filtrar resultados por nome Nenhum instalador aplicável encontrado; consulte os logs para obter mais detalhes. Atualmente não há recursos experimentais disponíveis. Nenhum pacote instalado foi encontrado que corresponda aos critérios de entrada. Nenhum pacote encontrou os critérios de entrada correspondentes. Desabilita a exibição VirtualTerminal {Locked="VirtualTerminal"} Abrir o local padrão dos logs opções Options to change how a command works Substituir argumentos para passar para o instalador Pacote: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. Opa, esquecemos de fazer isso... A posição do cursor na linha de comando Política de Privacidade A consulta usada para pesquisar um pacote Exibição de progresso arco-íris de cores Argumento necessário não fornecido: "{0}" {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Exibição de andamento como a cor padrão Pesquisas de pacotes de fontes configuradas. Localizar e mostrar informações básicas de pacotes ID Abbreviation of Identifier. Correspondência Nome Origem entradas adicionais truncadas devido ao limite de resultado Versão As seguintes falhas foram encontradas ao validar as configurações: Abre as configurações no editor de texto json padrão. Se nenhum editor estiver configurado, abre as configurações no bloco de notas. Para obter as configurações disponíveis, consulte https://aka.ms/winget-settings Este comando também pode ser usado para definir configurações de administrador fornecendo os argumentos --enable ou --disable {Locked="--enable"} {Locked="--disable"} Abra as configurações ou defina as configurações do administrador Erro inesperado ao carregar as configurações. Verifique suas configurações executando o 'settings' comando. {Locked="'settings'"} Canal Mostra informações sobre um pacote específico. Por padrão, a consulta deve corresponder de forma insensível à ID, ao nome ou ao moniker do pacote. Outros campos podem ser usados passando a opção apropriada. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Mostra informações sobre um pacote Versão Solicitar uma instalação silenciosa Somente o alias de caractere único pode ocorrer após um único -: '{0}' {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. Uma origem com o nome fornecido já existe e se refere a um local diferente: Uma origem com um nome diferente já se refere a este local: Uma origem com o nome fornecido já existe e se refere ao mesmo local: Adicionando fonte: Adicione uma nova fonte. Uma fonte fornece dados para você descobrir e instalar pacotes. Adicione uma nova fonte apenas se você confiar nela como um local seguro. Adicionar uma nova origem Argumento fornecido à fonte Encontrar pacote usando a fonte especificada Gerencie fontes usando os subcomandos. Uma fonte fornece dados para você descobrir e instalar pacotes. Adicione uma nova fonte apenas se você confiar nela como um local seguro. Gerenciar fontes de pacotes Editar propriedades de uma fonte existente. Uma fonte fornece os dados para você descobrir e instalar pacotes. Editar propriedades da fonte Editando fonte: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. A fonte chamada '{0}' já está no estado desejado. {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Argumento Value given to source. Liste todas as fontes atuais ou todos os detalhes de uma fonte específica. Listar fontes atuais Dados Data stored by the source. Campo The name of a piece of information about a source. Nome The name of the source. Identificador The source's unique identifier. Não foi encontrada uma fonte chamada: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. Não há fontes configuradas. Digitar The kind of source. Atualizada The last time the source was updated. nunca The source has never been updated. Valor The value of information about a source. Nome da fonte Falha ao abrir a origem(s); tentar o comando 'source reset' se o problema persistir. {Locked="source reset"} Falha ao abrir a origem predefinida; Relate aos mantenedores winget. {Locked="winget"} Removendo todas as fontes... Remover uma fonte específica. Remover fontes atuais Removendo fonte: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Redefinindo todas as fontes... Esse comando descarta fontes existentes, deixando potencialmente todos os dados locais para trás. Sem nenhum argumento, ele removerá todas as fontes e adicionará os padrões. Se uma fonte nomeada for fornecida, só essa fonte será descartada. Redefinir fontes Força a redefinição das fontes As seguintes fontes serão redefinidas se a opção--force for fornecida: {Locked="--force"} Redefinindo fonte: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Tipo da fonte Atualizando todas as fontes... Atualizar todas as fontes ou apenas uma fonte específica. Atualizar fontes atuais Atualizando fonte: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Filtrar resultados por rótulo Obrigado por usar o winget Avisos de Terceiros O programa utilitário de linha de comando winget permite instalar aplicativos e outros pacotes a partir da linha de comando. Exibir informações gerais da ferramenta Exibir a versão da ferramenta Argumento fornecido mais vezes do que o permitido: "{0}" {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. Mais de um argumento de comportamento de execução fornecido: '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). Ocorreu um erro inesperado ao executar o comando: Desinstalar a versão anterior do pacote durante a atualização Comando não reconhecido: "{0}" {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Atualizar todos os pacotes instalados para o mais recente, se disponível Nenhuma atualização aplicável foi encontrada. Uma versão de pacote mais recente está disponível em uma origem configurada, mas não se aplica ao seu sistema ou requisitos. Nenhuma atualização disponível foi encontrada. Nenhuma versão de pacote mais recente está disponível nas origens configuradas. Atualiza o pacote selecionado, encontrado pesquisando a lista de pacotes instalados ou diretamente de um manifesto. Por padrão, a consulta deve corresponder, sem diferenciação entre maiúsculas e minúsculas, à id, name ou ao moniker do pacote. Outros campos podem ser usados passando a opção apropriada. Quando nenhum argumento é fornecido, mostra os pacotes com atualizações disponíveis id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Mostra e executa atualizações disponíveis uso: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Valida um manifesto usando um conjunto estrito de diretrizes. O objetivo é permitir que você verifique o manifesto antes de enviá-lo para um repositório. Valida um arquivo de manifesto O caminho para o manifesto a ser validado Habilita o registro em log detalhado para winget Verifique se o arquivo de entrada é um MSIX assinado válido. Usar a versão especificada. O padrão é a versão mais recente Mostrar versões disponíveis do pacote O valor fornecido antes da conclusão é solicitado Nenhuma versão correspondente foi encontrada: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. Nenhuma fonte corresponde ao valor fornecido: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. As fontes configuradas são: Nenhuma fonte definida; adicionar um com 'source add' ou redefinir para os padrões com 'source reset' {Locked="source add","source reset"} Encontrado O caminho é um diretório: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. O arquivo {0} não existe. {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Tanto o manifesto local quanto os argumentos de consulta de pesquisa são fornecidos Registros Label displayed for diagnostic files containing information about the application use. O instalador foi bloqueado pela política O instalador falhou na verificação de segurança Um produto antivírus relata uma infecção no instalador Falha ao tentar atualizar a origem: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Desinstala o pacote selecionado, encontrado pesquisando a lista de pacotes instalados ou diretamente de um manifesto. Por padrão, a consulta deve corresponder de forma insensível à ID, ao nome ou ao moniker do pacote. Outros campos podem ser usados passando a opção apropriada. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Desinstala um determinado pacote Iniciando a desinstalação do pacote... Desinstalado com êxito winget não é possível localizar o comando Uninstall deste pacote. Entre no fornecedor do pacote para obter suporte. {Locked="winget"} Desinstalação abandonada Falha na desinstalação com o código de saída: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Exporta uma lista dos pacotes instalados Instala todos os pacotes listados em um arquivo. Instala todos os pacotes em um arquivo Arquivo em que o resultado deve ser gravado Arquivo descrevendo os pacotes que serão instalados Exportar pacotes a partir da fonte especificada Grava uma lista de pacotes instalados em um arquivo. Os pacotes podem ser instalados com o comando import. {Locked="import"} Um ou mais pacotes importados não foram instalados A origem necessária para a importação não está instalada: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. Pacote instalado não disponível em nenhuma fonte: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. Versão instalada do pacote não disponível em nenhuma fonte: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. Não foram encontrados pacotes no arquivo de importação Arquivo JSON não é válido O pacote já está instalado: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. Ignorar os pacotes indisponíveis Incluir versões de pacote no arquivo de exportação Ignorar as versões de pacote do arquivo de importação O caminho não existe: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. O arquivo JSON não especifica um esquema reconhecido. Selecione instalar escopo (user ou machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. O valor fornecido para o argumento '{0}' é inválido; os valores válidos são: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. Esta operação foi desabilitada por Política de Grupo: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Habilitar as Fontes Adicionais do Instalador de Aplicativos do Windows Habilitar as Fontes permitidas do Instalador de Aplicativos do Windows Habilitar a Fonte Padrão do Instalador de Aplicativos do Windows Habilitar os Recursos Experimentais do Instalador de Aplicativos do Windows Habilitar a Fonte da Microsoft Store do Instalador de Aplicativos do Windows Habilitar a origem da fonte do Instalador de Windows App Habilitar as Configurações do Gerenciador de Pacotes do Windows Habilitar o Gerenciador de Pacotes do Windows Habilitar interfaces de linha de comando do Gerenciador de Pacotes do Windows Definir o Intervalo de Atualização Automática da Fonte do Gerenciador de Pacotes do Windows em Minutos Política de Grupo Header for a table listing active Group Policies Habilitar o Arquivos de Manifesto Local do Instalador de Aplicativos do Windows Habilitar Ignorar o Certificado Fixado de Origem da Microsoft Store do Instalador de Aplicativo do Windows Habilitar a Substituição de Escaneamento de Malware de Arquivo Morto Local do Instalador de aplicativo do Windows Campo: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. O formato de campo é inválido. O valor de campo é inválido. A configuração da Política de Grupo é inválida. Configurações carregadas a partir do arquivo de backup. Arquivo de análise de erros: Valor: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. Os seguintes recursos experimentais estão em andamento. A configuração está desabilitada devido à política de grupo. O hash do instalador não corresponde. Habilitar a Substituição do Hash do Instalador de Aplicativos do Windows Exportar as fontes atuais como JSON para a política de grupo. Exportar fontes atuais Fonte adicional An additional source required by policy. Fonte permitida A source that the user is allowed to add. O valor fornecido para o argumento '{0}' é inválido {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Cancelado Externo Os pacotes encontrados nesta importação têm as seguintes dependências: Import command sentence showed before reporting dependencies Este pacote requer as seguintes dependências: Message shown before reporting dependencies Instalando dependências: Origem da dependência não encontrada A pesquisa de pacote produz mais de um resultado: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. Versão mais recente não encontrada para o pacote: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. Nenhum instalador encontrado: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Versão mínima exigida não disponível para o pacote: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. Nenhuma correspondência When package search yields no matches Tem loop Dependency graph has loop Nenhum instalador adequado encontrado para o manifesto: {0} versão {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Erro ao processar as dependências do pacote. Deseja continuar com a instalação? Prompt message shown when dependencies processing yields errors. Erro ao processar as dependências do pacote. Saindo... Pacotes Este pacote tinha dependências que podem não ser mais necessárias: Uninstall command sentence showed before reporting dependencies O manifesto tem as seguintes dependências que não foram validadas; verifique se eles são válidos: Validate command sentence showed before reporting dependencies Recursos do Windows Bibliotecas do Windows Encontre as dependências de pacotes usando a origem especificada For getting package type dependencies when installing from a local manifest Termos da Windows Store Aceitar todos os contratos de licença para pacotes O pacote exportado requer o contrato de licença para instalar: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. O editor requer que você exiba as informações acima e aceite os contratos antes de instalar. Você concorda com os termos? Os contratos de pacote não foram concordados. Operação cancelada. Acordos: Autor: Descrição: Instaladora: Local do instalador: Id do Produto da Microsoft Store: Instalador SHA256: Tipo de instalador: URL do instalador: Licença: URL da licença: Moniker: Página inicial: Fornecedor: Marcas: Versão: Dependências: Recursos do Windows: Bibliotecas do Windows: Dependências do pacote: Dependências externas: Não Sim Falha ao abrir a fonte adicionada. Aceitar todos os acordos de origem durante as operações de origem A fonte '{0}' requer que você exiba os seguintes contratos antes de usar. {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. Você concorda com todos os termos dos contratos de origem? Um ou mais contratos de origem não foram concordados. Operação cancelada. Aceite os contratos de origem ou remova as fontes correspondentes. A origem requer que a região geográfica de duas letras do computador atual seja enviada ao serviço de back-end para funcionar corretamente (por exemplo, "EUA"). Instalado com êxito. Reinicie o aplicativo para concluir a atualização. Cabeçalho HTTP de origem REST opcional do Gerenciador de Pacotes do Windows Ignorando o cabeçalho opcional, pois ele não é aplicável a esta fonte. O cabeçalho opcional não é aplicável sem especificar uma fonte: '{0}' {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Data do Lançamento: Distribuição Offline Com Suporte: URL do Fornecedor: URL de compra: URL de Suporte do Fornecedor: URL de Privacidade: Copyright: Copyright URL: Notas de Versão URL de Notas de Versão: Falha ao pesquisar origem; os resultados não serão incluídos: {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Falha na pesquisa da origem: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Este recurso precisa ser habilitado pelos administradores. Para habilita-lo, execute 'winget settings --enable {0}' como administrador. {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Habilitar a configuração específica do administrador Desabilitar a configuração específica do administrador Configuração de administrador "{0}" habilitada. {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. Configuração de administrador "{0}" desabilitada. {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. Configuração do Administrador Header for a table displaying admin settings. O aplicativo está em execução no momento. Saia do aplicativo e tente novamente. Os arquivos modificados pelo instalador estão sendo usados por um aplicativo diferente. Saia dos aplicativos e tente novamente. Outra instalação já está em andamento. Tente novamente mais tarde. Um ou mais arquivos estão sendo usados. Saia do aplicativo e tente novamente. Este pacote tem uma dependência ausente do sistema. Não há mais espaço no computador. Libere espaço e tente novamente. Não há memória suficiente disponível para instalar. Fechar outros aplicativos e depois tentar novamente. Um dos parâmetros de instalação é inválido. O log de instalação do pacote pode ter detalhes adicionais. Este aplicativo requer conectividade com a Internet. Conecte-se a uma rede e tente novamente. Este aplicativo encontrou um erro durante a instalação. Entre em contato com o suporte. Reiniciar seu PC para terminar a instalação. Seu PC será reiniciado para terminar a instalação. Falha na instalação. Reinicie o computador e tente novamente. Você cancelou a instalação. Outra versão deste aplicativo já está instalada. Uma versão superior deste aplicativo já está instalada. As políticas da organização estão impedindo a instalação. Entre em contato com o administrador. A configuração atual do sistema não dá suporte à instalação deste pacote. Falha na instalação com um erro de instalador personalizado. Entre em contato com o suporte do pacote. Instalação abandonada O instalador falhou com o código de saída: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. O registro do instalador está disponível em: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. Os seguintes pacotes foram encontrados entre as fontes de trabalho. Especifique um deles usando a opção --source para continuar. {Locked="--source"} "working sources" as in "sources that are working correctly" Nenhum pacote foi encontrado entre as fontes de trabalho. "working sources" as in "sources that are working correctly" Esta é uma versão estável do Gerenciador de Pacotes do Windows. Se quiser experimentar os recursos experimentais, instale uma versão de pré-lançamento. As instruções estão disponíveis no GitHub em https://github.com/microsoft/winget-cli. {Locked="https://github.com/microsoft/winget-cli"} Gerenciador de Pacotes do Windows v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. Ignorar versões do pacote no arquivo de importação O número solicitado de resultados deve estar entre 1 e 1000. Argumentos a serem passados para o instalador, além dos padrões Uma versão mais recente foi encontrada, mas a tecnologia de instalação é diferente da versão atual instalada. Desinstale o pacote e instale a versão mais recente. A tecnologia de instalação da versão mais recente especificada é diferente da versão atual instalada. Desinstale o pacote e instale a versão mais recente. Gerenciador de Pacotes do Windows (Pré-visualização) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Selecionar a arquitetura Atualizar pacotes mesmo que a versão atual não seja determinada Liste pacotes mesmo que sua versão atual não possa ser determinada. Só pode ser usado com o argumento --upgrade-available {Locked="--upgrade-available"} Não é possível determinar o número de versão deste pacote. Para atualizá-lo mesmo assim, adicione o argumento --include-unknown ao comando anterior. {Locked="--include-unknown"} {0} pacotes têm números de versão que não podem ser determinados. Use --include-unknown para ver todos os resultados. {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} os pacotes estão fixados e precisam ser atualizados explicitamente. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. Os argumentos fornecidos só podem ser usados com uma consulta. Arquitetura do Sistema: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). Retém todos os arquivos e diretórios criados pelo pacote (portátil) Exclui todos os arquivos e diretórios no diretório do pacote (portátil) Pressione Enter para continuar . . . O valor para renomear o arquivo executável (portátil) Solicita que o usuário pressione qualquer tecla antes de sair O instalador solicitará que você execute como administrador. Espere uma solicitação. O instalador não pode ser executado de um contexto de administrador. Variável de ambiente do caminho modificada; reinicie seu shell para usar o novo valor. Filtros usando o código do produto Já existe um pacote portátil com o mesmo nome, mas de uma fonte diferente; prosseguir devido a --force {Locked="--force"} O volume não suporta pontos de nova análise O nome de arquivo especificado não é um nome de arquivo válido Substituindo arquivo existente: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. Nenhum argumento de seleção de pacote foi fornecido; consulte a ajuda para obter detalhes sobre como encontrar um pacote. O alias da linha de comando foi adicionado: Diretório de Links Portáteis (Computador) Diretório de Links Portáteis (Usuário) Raiz do Pacote Portátil (Usuário) Raiz do Pacote Portátil Raiz do Pacote Portátil (x86) Falha na instalação portátil; Limpando... O pacote portátil foi modificado; continuando devido a --force {Locked="--force"} Não é possível remover o pacote Portátil, pois ele foi modificado; para substituir essa verificação, use --force {Locked="--force"} Os arquivos permanecem no diretório de instalação: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Limpando o diretório de instalação... Não é possível limpar o diretório de instalação porque ele não foi criado pelo winget Link Relacionado Documentação: Observações: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Notas da instalação: Não há suporte para um argumento fornecido para este pacote Falha ao extrair o conteúdo do arquivo morto O arquivo do instalador aninhado não existe. Verifique se o caminho relativo especificado das correspondentes do instalador aninhado: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Caminho de arquivo relativo inválido para o instalador aninhado; caminho aponta para um local fora do diretório de instalação Nenhum instalador aninhado especificado para esse pacote Somente um instalador aninhado pode ser especificado para um instalador de arquivo morto, a menos que ele seja um instalador portátil ou aninhado de fonte Argumentos de linha de comando incompatíveis fornecidos Lista somente os pacotes que têm uma atualização disponível Os pacotes a seguir têm uma atualização disponível, mas exigem uma segmentação explícita para atualização: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly Baixando Label displayed while downloading an application installer. O tipo de instalador aninhado não é suportado Este instalador é conhecido por reiniciar o terminal ou shell Este pacote exige um local de instalação Sabe-se que os seguintes instaladores reiniciam o terminal ou o shell: Os seguintes instaladores exigem um local de instalação: Especifique a raiz da instalação: Deseja continuar? Contratos para This will be followed by a package name, and then a list of license agreements O local de instalação é exigido pelo pacote, mas não foi fornecido Desabilitar os prompts interativos Description for a command line argument, shown next to it in the help Foi encontrado um pacote existente já instalado. Tentando atualizar o pacote instalado... Execute diretamente o comando e continue com problemas não relacionados à segurança Description for a command line argument, shown next to it in the help Já existe um pacote portátil de uma fonte diferente Arquivo extraído com êxito Extraindo arquivo... Malware detectado na verificação de arquivo morto. Para substituir essa verificação, use --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} A verificação de arquivo morto detectou malware. Prosseguir devido a --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} Ignora a atualização se uma versão instalada já existir Uma versão do pacote já está instalada. Instalação cancelada. Não é possível habilitar {0}. Esta configuração é controlada pela política. Para obter mais informações, contate o administrador do sistema. {Locked="{0}"} The value will be replaced with the feature name Não é possível desabilitar {0}. Esta configuração é controlada pela política. Para obter mais informações, contate o administrador do sistema. {Locked="{0}"} The value will be replaced with the feature name Adiciona um novo marcador. Um marcador pode limitar o Gerenciador de Pacotes do Windows de atualizar um pacote para intervalos específicos de versões ou pode impedir que ele atualize completamente o pacote. Um pacote fixado ainda pode ser atualizado por conta própria e ser atualizado de fora do Gerenciador de Pacotes do Windows. Por padrão, um pacote fixado pode ser atualizado mencionando-o explicitamente no comando "upgrade" ou adicionando o sinalizador "--include-pinned" a "winget upgrade --all". {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} Adicionar um novo pin Gerencie pinos de pacote com os sub-comandos. Um PIN pode limitar o Gerenciador de Pacotes do Windows de atualizar um pacote para intervalos específicos de versões ou pode impedir que ele atualize totalmente o pacote. Um pacote fixado ainda pode ser atualizado por conta própria e ser atualizado de fora da Gerenciador de Pacotes do Windows. Gerenciar pinos de pacote Listar todos os pins atuais ou detalhes completos de um PIN específico. Listar pinos atuais Remove um pin de pacote específico. Remover um pin de pacote Redefine todos os pinos existentes. Redefinir pinos Versão na qual fixar o pacote. O curinga '*' pode ser usado como a última parte da versão Bloquear a atualização até que o pin seja removido, impedindo a substituição de argumentos Fixar uma versão instalada específica Instalado Value used in a table to indicate that a package comes from the list of packages installed in the machine Exportar configurações como JSON Exportar configurações Configurações do Usuário Label displayed for the file containing the user settings. Não foi possível carregar o arquivo de configurações. Usando valores padrão. Selecione o filtro de escopo do pacote instalado (user ou machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. Marcador adicionado com êxito Já existe um PIN para o pacote {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. Já existe um PIN para o pacote {0}. Substituindo devido ao argumento --force. {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Já existe um PIN para o pacote {0}. Use o --force para substituí-lo. {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Redefinindo todos os marcadores atuais. Use o argumento --force para redefinir todos os marcadores. Os seguintes marcadores seriam removidos: {Locked="--force"} Tipo de marcador Não há marcador para o pacote {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. Não há martcadores configurados. Shown when listing or modifying existing pins if there are none. Marcadores redefinidos com êxito Shown after resetting (deleting) all the pins Não é possível abrir o banco de dados do marcador. Error message for when we cannot open the database containing package pins. Foi fornecido um argumento que só pode ser usado para um único pacote Vários argumentos mutuamente exclusivos fornecidos: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together O {0} argumento só pode ser usado com {1} {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" Desabilitado As in enabled/disabled Habilitado As in enabled/disabled Estado Header for a table listing the state (enabled/disabled) of Group Policies and Settings A consulta usada para pesquisar um pacote Pacote não encontrado: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. Vários pacotes encontrados para: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. Falha na pesquisa por: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} pacote(s) têm um pin que precisa ser removido antes da atualização {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade O pacote não pode ser atualizado usando winget. Use o método fornecido pelo editor para atualizar este pacote. Atualizar pacotes mesmo que eles tenham um marcador sem bloqueio Liste os pacotes mesmo que eles tenham um PIN que impeça a atualização. Só pode ser usado com o argumento --upgrade-available {Locked="--upgrade-available"} Marcador removido com êxito Uma versão mais recente foi encontrada, mas o pacote tem um PIN que impede de atualize-o. O pacote está fixado e não pode ser atualizado. Use o 'winget pin' para exibir e editar pinos. Alguns tipos de pinos podem ser ignorados com o --include-pinned especificado. {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} pacotes têm pinos que impedem a atualização. Use o 'winget pin' para exibir e editar pinos. O uso do --include-pinned pode mostrar mais resultados. {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages Garante que o sistema corresponda ao estado desejado conforme descrito pela configuração fornecida. Pode baixar/executar processadores para atingir o estado desejado. A configuração e os processadores devem ser verificados para garantir que eles sejam confiáveis antes de apá-los. Configura o sistema em um estado desejado Mostra detalhes da configuração fornecida. Por padrão, não modificará o sistema, mas algumas opções fará com que os arquivos sejam baixados e/ou carregados. Mostra detalhes de uma configuração Verifica se o sistema corresponde ao estado desejado conforme descrito pela configuração fornecida. Pode baixar/executar processadores para testar o estado desejado. A configuração e os processadores devem ser verificados para garantir que eles sejam confiáveis antes de executá-los. Verifica o sistema em relação a um estado desejado Valida um arquivo de configuração para correção. Valida um arquivo de configuração O campo "{0}" no arquivo de configuração é do tipo errado. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. O caminho para o arquivo de configuração O arquivo de configuração é inválido. A versão do arquivo {0} não é conhecida. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. Aceita o aviso de configuração, impedindo um prompt interativo Aplicar Indicates that this item is used to write state Declarar Indicates that this item is used to check/assert the state rather than write to it Dependências:{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. Parte da configuração não foi aplicada com êxito. Falha ao obter informações detalhadas sobre a configuração. Informar Indicates that this item is used to retrieve values for future use rather than writing them Local Used to indicate that the item is present on the device. Módulo: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. Módulo: {0} por {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. Configurações: Label for the values that are used as inputs for this item when applying state Configuração aplicada com êxito. Unidade aplicada com êxito. Outra configuração está sendo aplicada ao sistema. Esta configuração continuará assim que possível... Você é responsável por entender as definições de configuração que você está escolhendo executar. A Microsoft não é responsável pelo arquivo de configuração que você criou ou importou. Essa configuração pode alterar as configurações no Windows, instalar software, alterar configurações de software (incluindo configurações de segurança) e aceitar contratos de usuário para pacotes e serviços de terceiros em seu nome.  Ao executar esse arquivo de configuração, você reconhece que entende e concorda com esses recursos e configurações. Todos os aplicativos instalados são licenciados para você por seus proprietários. A Microsoft não é responsável nem concede licenças a pacotes ou serviços de terceiros. Legal approved. Do not change without approval. Você releou a configuração e gostaria de continuar aplicando-a ao sistema? PM approved. A configuração está vazia. O recurso [{0}] não foi encontrado. {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. Falha ao habilitar as dependências do Recurso do Windows. Para continuar com a instalação, use '--force'. {Locked="--force"} Reinicialização necessária para habilitar totalmente o(s) Recurso(s) do Windows; para substituir este marcar use '--force'. "Windows Feature(s)" is the name of the options Windows features setting. Falha ao habilitar as dependências do Recurso do Windows; continuar devido a --force "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} Habilitando o [{0}]... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. Falha ao habilitar o recurso [{0}]: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. Reinicialização necessária para habilitar totalmente o(s) Recurso(s) do Windows; prosseguir devido a --force "Windows Feature(s)" is the name of the options Windows features setting. Aguardando a conclusão de outra instalação/desinstalação... Versão fixada Table header for the version to which a package is pinned; meaning it should not update from that version. <See the log file for additional details> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. Recuperando detalhes de configuração Inicializando o sistema de configuração Lendo arquivo de configuração O sistema não está no estado desejado declarado pela configuração. Esta unidade de configuração falhou por um motivo desconhecido: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. A unidade de configuração falhou devido à configuração: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. A unidade de configuração falhou ao tentar obter o estado atual do sistema. A unidade de configuração falhou ao tentar aplicar o estado desejado. A unidade de configuração falhou ao tentar testar o estado atual do sistema. A unidade de configuração falhou devido a um erro interno: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. A unidade de configuração falhou devido a uma pré-condição não ser válida: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. A unidade de configuração falhou devido ao estado do sistema: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. A unidade de configuração falhou ao tentar executar: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. A configuração contém o identificador "{0}" várias vezes. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. A dependência "{0}" não foi encontrada na configuração. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. Esta unidade de configuração foi ignorada manualmente. O módulo para a unidade de configuração está disponível em vários locais com a mesma versão. Falha ao carregar o módulo para a unidade de configuração. Foram encontradas várias correspondências para a unidade de configuração. Especifique o módulo para selecionar o correto. Não foi possível encontrar a unidade de configuração. A unidade de configuração não estava no módulo conforme o esperado. Esta unidade de configuração não foi executada porque uma dependência falhou ou não foi executada. Esta unidade de configuração não foi executada porque uma asserção falhou ou era falsa. A unidade de configuração retornou um resultado inesperado durante a execução. Esta unidade de configuração não foi executada por um motivo desconhecido: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. O campo "{0}" tem um valor inválido: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. O campo "{0}" está ausente ou vazio. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. Consulte a linha {0}, coluna {1} no arquivo. {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. Cancelando operação... Instalar o pacote stub para AppInstaller Instalar o pacote completo do AppInstaller Habilitar recursos avançados. Requer acesso ao repositório. A '--enable' e '--disable' não podem ser usadas com outros argumentos. {Locked="--enable", "--disable"} Habilitando recursos avançados. Requer acesso ao repositório. Os recursos estendidos não estão habilitados. Execute 'winget configure --enable' para habilite-os. {Locked="winget configure --enable"} Os recursos avançados estão habilitados. Desabilitar recursos avançados. Requer acesso ao repositório. Desabilitando recursos avançados. Requer acesso ao repositório. Os recursos avançados estão desabilitados. Ignora o processamento de dependências de pacote e recursos do Windows Dependências ignoradas. Falha ao atualizar PATH variável para o processo. As próximas instalação que dependem de alterações na PATH variável poderão falhar. {Locked="PATH"} Algumas das unidades de configuração falharam ao testar seu estado. O sistema está no estado de configuração descrito. O estado de configuração não foi testado. O sistema não está no estado de configuração descrito. Resultado de teste inesperado: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). Você releou a configuração e gostaria de continuar a verificá-la no sistema? O arquivo de configuração não é um arquivo YAML válido. {Locked="YAML"} YAML is a file format name. Esta unidade de configuração faz parte de um ciclo de dependência. A validação não encontrou problemas. A unidade de configuração só está disponível como um pré-lançamento, mas não está marcada dessa forma na configuração. Adicione 'allowPrerelease: true' ao 'directives'. {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. A unidade de configuração foi encontrada localmente, mas não pôde ser encontrada em nenhum catálogo configurado. Verifique se ele está presente em qualquer sistema antes de aplicar a configuração. A unidade de configuração não está disponível publicamente; verifique se qualquer pessoa que usará essa configuração tem acesso a ela. O módulo não foi fornecido. A especificação do módulo melhora o desempenho e impede futuras colisões de nomes. Baixa o instalador do pacote selecionado, encontrado pesquisando uma fonte configurada ou diretamente de um manifesto. Por padrão, a consulta deve corresponder sem diferenciar maiúsculas de minúsculas à ID, ao nome ou ao moniker do pacote. Outros campos podem ser usados passando a opção apropriada. Por padrão, o comando de download baixará o instalador apropriado para a pasta Downloads do usuário. Baixa o instalador de um determinado pacote Diretório para o qual os instaladores são baixados Baixando dependências: Instalador baixado: {0} {Locked="{0}"} Full path of the downloaded installer. Selecione o tipo de instalador Downloads do Instalador O instalador está proibido de ser baixado para instalação offline posterior. '--module-path allusers' requer privilégios de administrador para ser executado. {Locked="--module-path allusers"} Especifica o local no computador local para armazenar módulos. Padrão %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} O valor '--module-path' deve ser 'currentuser', 'allusers', 'default' ou um caminho absoluto. {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Habilitar as Configurações do Gerenciador de Pacotes do Windows Recuperar informações sobre erros. Dado um número, a saída conterá detalhes sobre o erro, incluindo o nome do símbolo se for um erro específico do winget. Dado uma cadeia de caracteres, os erros específicos do winget são pesquisados para esse valor. Obter informações sobre erros Um valor a ser pesquisado dentro das informações de erro O número fornecido é muito grande para ser um HRESULT. Código de erro desconhecido ERRO INTERNO Argumentos da linha de comando inválidos Falha na execução do comando Falha na abertura do manifesto Sinal de cancelamento recebido Falha na execução do ShellExecute Não é possível processar o manifesto. A versão do manifesto é maior do que a suportada. Atualize o cliente. Falha no download do instalador Não é possível gravar no índice; é uma versão de esquema superior O índice está corrompido As informações da origem configurada estão corrompidas O nome da origem já está configurado O tipo de fonte é inválido O arquivo MSIX é um pacote, não um pacote Os dados exigidos pela origem estão ausentes Nenhum dos instaladores é aplicável ao sistema atual O hash do arquivo do instalador não corresponde ao manifesto O nome da origem não existe O local de origem já está configurado com outro nome Nenhum pacote encontrado Nenhuma fonte está configurada Vários pacotes encontrados que correspondem aos critérios Nenhum manifesto encontrado que corresponda aos critérios Falha ao obter a pasta Pública do pacote de origem O comando requer privilégios de administrador para ser executado O local de origem não é seguro O cliente da Microsoft Store está bloqueado pela política O aplicativo da Microsoft Store está bloqueado pela política O recurso está atualmente em desenvolvimento. Pode ser habilitado usando configurações de winget. Falha ao instalar o aplicativo da Microsoft Store Falha ao executar a conclusão automática Falha ao inicializar o analisador YAML Encontrou uma chave YAML inválida Encontrou uma chave YAML duplicada Operação YAML inválida Falha ao criar o documento YAML O estado do emissor YAML é inválido Dados YAML inválidos Erro da LibYAML A validação do manifesto foi bem-sucedida com um aviso Falha na validação do manifesto O manifesto é inválido Nenhuma atualização aplicável foi encontrada A atualização do winget --all foi concluída com falhas Falha na verificação de segurança do instalador O tamanho do download não corresponde ao tamanho do conteúdo esperado Comando de desinstalação não encontrado Falha na execução do comando de desinstalação Erro no iterador de interrupção de ICU Erro no mapa de casos da ICU Erro de regex ICU Falha ao instalar um ou mais pacotes importados Não foi possível localizar um ou mais pacotes solicitados O arquivo Json é inválido O local de origem não é remoto Não há suporte para a origem REST configurada Dados inválidos retornados pela origem REST A operação está bloqueada pela Política de Grupo Erro interno da API Rest URL de origem REST inválida Tipo MIME sem suporte retornado pela API rest Versão inválida do contrato da origem REST Os dados de origem estão corrompidos ou adulterados Erro na leitura do fluxo Os contratos de pacotes não foram aceitos Erro na leitura da entrada no prompt Uma ou mais fontes não dão suporte à solicitação de pesquisa O ponto de extremidade da API rest não foi encontrado. Falha ao abrir a origem. Os contratos de origem não foram aceitos O tamanho do cabeçalho excede o limite permitido de 1024 caracteres. Reduza o tamanho e tente novamente. Arquivo de recurso ausente Falha na execução da instalação MSI Os argumentos para o msiexec são inválidos Falha ao abrir uma ou mais fontes Falha na validação das dependências Um ou mais pacotes estão ausentes Coluna de tabela inválida A versão de atualização não é mais recente do que a versão instalada A versão de atualização é desconhecida e a substituição não foi especificada Erro de conversão de ICU Falha ao instalar o pacote portátil Não há suporte para pontos de reparo no volume Já existe um pacote portátil de uma fonte diferente. Não é possível criar o link simbólico. O caminho aponta para um diretório. O instalador não pode ser executado de um contexto de administrador. Falha ao desinstalar o pacote portátil Falha ao validar os valores de DisplayVersion em relação ao índice. Não há suporte para um ou mais argumentos. Os caracteres nulos inseridos não são permitidos no SQLite Falha ao localizar o instalador aninhado no arquivo morto. Falha ao extrair o arquivo. Caminho de arquivo relativo inválido para o instalador aninhado fornecido. O certificado do servidor não correspondeu a nenhum dos valores esperados. O local de instalação deve ser fornecido. Falha na verificação do arquivo de malware. Encontrada pelo menos uma versão do pacote instalado. Já existe uma marcação para o pacote. Não há pino para o pacote. Não é possível abrir o banco de dados de pinos. Falha na instalação de um ou mais aplicativos Falha na desinstalação de um ou mais aplicativos Uma ou mais consultas não retornaram exatamente uma correspondência O pacote tem um PIN que impede a atualização. O pacote atualmente instalado é o pacote stub Sinal de desligamento do aplicativo recebido Falha no download das dependências do pacote. Falha ao baixar o pacote. O download para instalação offline é proibido. Um serviço necessário está ocupado ou indisponível. Tente novamente mais tarde. O guid fornecido não corresponde a um estado de retomada válido. A versão atual do cliente não correspondeu à versão do cliente do estado salvo. Os dados do estado de retomada são inválidos. Não é possível abrir o banco de dados de pontos de verificação. Excedeu o limite máximo de retomadas. Informações de autenticação inválidas. Não há suporte para o método de autenticação. Falha na autenticação. Falha na autenticação. É necessária autenticação interativa. Falha na autenticação. Usuário cancelado. Falha na autenticação. A conta autenticada não é a conta desejada. O aplicativo está em execução no momento. Saia do aplicativo e tente novamente. Outra instalação já está em andamento. Tente novamente mais tarde. Um ou mais arquivos estão sendo usados. Saia do aplicativo e tente novamente. Este pacote tem uma dependência ausente do sistema. Não há mais espaço no computador. Libere espaço e tente novamente. Não há memória suficiente disponível para instalar. Fechar outros aplicativos e depois tentar novamente. Este aplicativo requer conectividade com a Internet. Conecte-se a uma rede e tente novamente. Este aplicativo encontrou um erro durante a instalação. Entre em contato com o suporte. Reiniciar seu PC para terminar a instalação. Falha na instalação. Reinicie o computador e tente novamente. Seu PC será reiniciado para terminar a instalação. Você cancelou a instalação. Outra versão deste aplicativo já está instalada. Uma versão superior deste aplicativo já está instalada. As políticas da organização estão impedindo a instalação. Entre em contato com o administrador. Falha ao instalar as dependências do pacote. O aplicativo está sendo usado por outro aplicativo no momento. Parâmetro inválido. Não há suporte para o pacote no sistema. O instalador não oferece suporte à atualização de um pacote existente. A instalação falhou com um erro do instalador personalizado. Não foi possível encontrar a Entrada de Aplicativos e Recursos do pacote. O local de instalação não é aplicável. Não foi possível encontrar o local da instalação. O hash do arquivo existente não correspondeu. Arquivo não encontrado. O arquivo foi encontrado, mas o hash não foi verificado. O arquivo não pôde ser acessado. O arquivo de configuração é inválido. A sintaxe YAML é inválida. Um campo de configuração tem um tipo inválido. A configuração tem uma versão desconhecida. Ocorreu um erro ao aplicar a configuração. A configuração contém um identificador duplicado. Uma dependência está ausente na configuração. A configuração tem uma dependência não atendida. Falha na instrução de declaração da unidade de configuração. A configuração foi ignorada manualmente. O usuário se recusou a continuar a execução. O grafo de dependência contém um ciclo que não pode ser resolvido. A configuração tem um valor de campo inválido. A configuração está ausente em um campo. Algumas das unidades de configuração falharam ao testar seu estado. O estado de configuração não foi testado. A unidade de configuração não foi instalada. Não foi possível encontrar a unidade de configuração. Foram encontradas várias correspondências para a unidade de configuração. Especifique o módulo para selecionar o correto. A unidade de configuração falhou ao tentar obter o estado atual do sistema. A unidade de configuração falhou ao tentar testar o estado atual do sistema. A unidade de configuração falhou ao tentar aplicar o estado desejado. O módulo para a unidade de configuração está disponível em vários locais com a mesma versão. Falha ao carregar o módulo para a unidade de configuração. A unidade de configuração retornou um resultado inesperado durante a execução. Uma unidade contém uma configuração que requer a raiz de configuração. Não há suporte para a operação no processador de configuração. Não disponível Habilitação bem-sucedida das Dependências de Recursos do Windows Falha ao carregar o módulo para a unidade de configuração porque ele requer privilégios de administrador para ser executado. Uma unidade contém uma configuração que requer a raiz de configuração. Falha ao carregar o módulo para a unidade de configuração porque ele requer privilégios de administrador para ser executado. Retoma a execução de um comando salvo anteriormente passando o identificador exclusivo do comando salvo. Isso é usado para retomar um comando executado que pode ter sido encerrado devido a uma reinicialização. Retoma a execução de um comando salvo anteriormente. O identificador exclusivo do estado salvo a ser retomado Não há suporte para retomar o estado de uma versão de cliente diferente: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. O estado de retomada não existe: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. Nenhum dado encontrado no estado do retomada. Esse comando não dá suporte à retomada. Permite uma reinicialização, se aplicável Iniciando a reinicialização para concluir a operação... Falha ao iniciar uma reinicialização. A operação retomar excede o limite permitido de {0} retomada(s). Para retomar manualmente, execute o comando '{1}'. {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. Ignorar o limite para retomar um estado salvo Não há suporte para o esquema de URI: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. O Uri não está bem formado: {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. Falha ao analisar {0} conteúdo das configurações da unidade de configuração ou se o conteúdo das configurações está vazio. {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. {0} unidade de configuração não tem o argumento necessário: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. {0} unidade de configuração não tem o argumento recomendado: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. A unidade de configuração WinGetSource está em conflito com uma fonte conhecida: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. A unidade de configuração WinGetSource asser em uma fonte de terceiros: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage declara UseLatest e Version. ID do Pacote: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} A unidade de configuração WinGetPackage asser em um pacote de origem de terceiros. ID do Pacote: {0}; Origem: {1} {Locked="WinGetPackage,{0},{1}"} O pacote da unidade de configuração WinGetPackage depende de uma fonte de terceiros que não foi configurada anteriormente. ID do Pacote: {0}; Origem: {1} {Locked="WinGetPackage,{0},{1}"} O pacote da unidade de configuração WinGetPackage depende de uma fonte de terceiros. É recomendável declarar a dependência na seção uni dependsOn. ID do Pacote: {0}; Origem: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} O pacote da unidade de configuração WinGetPackage não pode ser validado. Falha ao abrir o código-fonte. ID do Pacote: {0}; Origem: {1} {Locked="WinGetPackage,{0},{1}"} O pacote da unidade de configuração WinGetPackage não pode ser validado. Pacote não encontrado. ID do Pacote: {0} {Locked="WinGetPackage,{0}"} O pacote da unidade de configuração WinGetPackage não pode ser validado. Mais de um pacote encontrado. ID do Pacote: {0} {Locked="WinGetPackage,{0}"} O pacote da unidade de configuração WinGetPackage não pode ser validado. Versão do pacote não encontrada. ID do Pacote: {0}; Versão {1} {Locked="WinGetPackage,{0},{1}"} Pacote da unidade de configuração do WinGetPackage especificado com uma versão específica enquanto apenas uma versão do pacote estiver disponível. ID do pacote: {0}; Versão: {1} {Locked="WinGetPackage,{0},{1}"} O pacote da unidade de configuração WinGetPackage não pode ser validado. ID do Pacote: {0} {Locked="WinGetPackage,{0}"} Especificar a preferência da janela de autenticação (silent, silentPreferred ou interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. Especifique a conta a ser usada para autenticação Falha ao adicionar origem. Esta winget não dá suporte ao método de autenticação da origem. Tente atualizar para a versão mais winget versão. {Locked="winget"} A fonte {0} requer autenticação. O prompt de autenticação pode aparecer quando necessário. As informações autenticadas serão compartilhadas com a origem para autorização de acesso. {Locked="{0}"} Repara o pacote selecionado, encontrado pesquisando a lista de pacotes instalados ou diretamente de um manifesto. Por padrão, a consulta deve corresponder sem diferenciar maiúsculas de minúsculas à ID, ao nome ou ao moniker do pacote. Outros campos podem ser usados passando a opção apropriada. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Repara o pacote selecionado O comando de reparo deste pacote não foi encontrado. Entre em contato com o fornecedor do pacote para obter suporte. A tecnologia do instalador em uso não corresponde à versão instalada no momento. Comando de reparo não encontrado. A tecnologia do instalador em uso não dá suporte ao reparo. A operação de reparo foi concluída com êxito. Reparo abandonado Início do reparo do pacote... Falha ao reparar o pacote da Microsoft Store. Código de erro: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. Falha na operação de reparo. A operação de reparo não é aplicável. Nenhuma versão de pacote correspondente está disponível nas origens configuradas. A configuração atual do sistema não dá suporte ao reparo deste pacote. A tecnologia do instalador em uso não dá suporte ao reparo. O pacote instalado para o escopo do usuário não pode ser reparado durante a execução com privilégios de administrador. Operações de reparo envolvendo privilégios de administrador não são permitidas em pacotes instalados no escopo do usuário. O reparo falhou com o código de saída: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. A conexão SQLite foi encerrada para evitar danos. Desinstale todas as versões A versão para agir Várias versões deste pacote estão instaladas. Refine a pesquisa, passe o argumento '--version' para selecionar um ou passe o sinalizador '--all-versions' para desinstalar todos eles. {Locked="--version,--all-versions"} Habilitar Gerenciador de Pacotes do Windows de linha de comando do proxy atual Describes a Group Policy that can enable the use of the --proxy option to set a proxy Definir um proxy a ser usado para esta execução Desabilitar o uso de proxy para esta execução Não é possível redefinir {0}. Esta configuração é controlada pela política. Para obter mais informações, contate o administrador do sistema. {Locked="{0}"} The value will be replaced with the feature name Redefinir configuração de administrador '{0}'. {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. Não é possível definir {0}. Esta configuração é controlada pela política. Para obter mais informações, contate o administrador do sistema. {Locked="{0}"} The value will be replaced with the feature name Defina a configuração de administrador '{0}' para '{1}'. {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced Nome da configuração a ser modificada Valor a ser definido para a configuração. Redefine uma configuração de administrador para seu valor padrão. Redefine uma configuração de administrador para seu valor padrão. Define o valor de uma configuração de administrador. Define o valor de uma configuração de administrador. Exclui uma fonte da descoberta, a menos que especificado Exclui uma origem da descoberta (true ou false) Explícito Nível de confiança da origem (nenhum ou confiável) Nível de Confiança Falha ao obter o catálogo do pacotes da Microsoft Store. Nenhum pacote aplicável da Microsoft Store encontrado no catálogo dos pacotes da Microsoft Store. Recuperando informações de download do pacote Microsoft Store. Nenhum pacote aplicável da Microsoft Store para download. Falha ao recuperar a licença do pacote da Microsoft Store. Nenhum pacote aplicável da Microsoft Store encontrado. Hash do pacote da Microsoft Store verificado com êxito Incompatibilidade de hash do pacote da Microsoft Store Pacote da Microsoft Store baixado: {0} {Locked="{0}"} Full path of the downloaded package. Falha no download do pacote da Microsoft Store: {0} {Locked="{0}"} Package name. Download do pacote da Microsoft Store concluído Baixando pacotes principais da Microsoft Store... Baixando pacotes de dependências da Microsoft Store... Recuperando informações de download de pacotes da Microsoft Store Falha ao recuperar informações de download do pacote da Microsoft Store Recuperando licença do pacote da Microsoft Store Licença do pacote da Microsoft Store salva: {0} {Locked="{0}"} License file full path. Falha ao recuperar a licença do pacote da Microsoft Store O download do pacote da Microsoft Store não dá suporte ao argumento --rename. O pacote da Microsoft Store usará nomes fornecidos pelo catálogo da Microsoft Store. {Locked="--rename"} Microsoft Store download do pacote requer Microsoft Entra de ID. O prompt de autenticação pode aparecer quando necessário. As informações autenticadas serão compartilhadas com serviços Microsoft autorização de acesso. Para Microsoft Store pacote de licenciamento, a Microsoft Entra ID do cliente precisa ser membro do Administrador Global, do Administrador de Usuários ou do Administrador de Licenças. {Locked="Global Administrator,User Administrator,License Administrator"} Ignora a recuperação da licença offline do pacote da Microsoft Store Selecione a plataforma de destino Adicionando arquivo de configuração: {0} {Locked="{0}"} Exportado com êxito Obtendo definições de configuração... Pelo menos --packageId e/ou --module com --resource devem ser fornecidos. Ou use --all para exportar todas as configurações de pacote. {Locked="--packageId,--module, --resource, --all"} Os argumentos --packageId, --module e --resource não podem ser usados com --all. {Locked="--packageId,--module, --resource, --all"} Exporta recursos de configuração para um arquivo de configuração. Quando usado com --all, exporta todas as configurações de pacote. Quando usado com --packageId, exporta um recurso WinGetPackage da ID do pacote fornecida. Quando usado com --module e --resource, obtém as configurações do recurso e exporta-o para o arquivo de configuração. Se o arquivo de configuração de saída já existir, anexa os recursos de configuração exportados. {Locked="WinGetPackage,--packageId,--module, --resource"} Exporta recursos de configuração para um arquivo de configuração. O módulo do recurso a ser exportado. O identificador do pacote a ser exportado. O recurso de configuração a ser exportado. Exporta todas as configurações de pacotes. Falha na unidade de configuração ao obter suas propriedades. Falha na exportação da configuração. Configurar {0} {Locked="{0}"} Instalar {0} {Locked="{0}"} Alguns dos dados presentes no arquivo de configuração foram truncados para esta saída; inspecione o conteúdo do arquivo para obter o conteúdo completo. <este valor foi truncado; inspecione o conteúdo do arquivo para obter o texto completo> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. Mostra os detalhes de alto nível das configurações que foram aplicadas ao sistema. Em seguida, os dados podem ser usados com `configure` para obter mais detalhes. {Locked="`configure`"} Mostra o histórico de configurações Não há configurações no histórico. Selecionar itens do histórico Não foi encontrada nenhuma configuração única que corresponda aos dados fornecidos. Forneça o nome completo ou parte do identificador que corresponda ineiguamente à configuração desejada. Remover o item do histórico Primeira Aplicação Column header for date values indicating when a configuration was first applied to the system. Identificador Nome Origem Caminho Não foi possível encontrar a configuração especificada. Concluídas The state of processing an item. Em andamento The state of processing an item. Pendente The state of processing an item. Desconhecido The state of processing an item. Monitorar o status da configuração. As in "to monitor the status of a configuration being applied". Concluídas The state of processing an item. Em andamento The state of processing an item. Pendente The state of processing an item. Ignorado The state of processing an item. Desconhecido The state of processing an item. Aplicação Iniciada When the configuration application started. Aplicação Encerrada When the configuration application ended. Resultado Detalhes Estado The state of processing an item. Unidade O pacote não é compatível com a versão ou plataforma atual do Windows. Suprimir a exibição dos detalhes da configuração inicial quando possível Vários Microsoft Store podem ser baixados direcionando diferentes plataformas e arquiteturas. --platform, --architecture pode ser usado para filtrar os pacotes. {Locked="--platform--architecture"} Falha ao recuperar a Microsoft Store do pacote. A Microsoft Entra ID do cliente não é membro do Administrador Global, do Administrador de Usuários ou do Administrador de Licenças. Use --skip-license para ignorar a recuperação Microsoft Store de pacote. {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} O Microsoft Store não dá suporte ao download. O Microsoft Store não dá suporte ao download. Falha ao recuperar a Microsoft Store do pacote. A Microsoft Entra de ID não tem o privilégio necessário. O parâmetro não pode ser passado através do limite de integridade. Instalador de zero bytes baixado; verifique se a conexão de rede está funcionando corretamente. Instalador de zero bytes baixado; verifique se a conexão de rede está funcionando corretamente. Gerenciar fontes Gerencie fontes com sub-comandos. As fontes podem ser instaladas, atualizadas ou desinstaladas semelhantes a packages. Família Fonte "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' Filtrar resultados por nome da família Rosto "Face" represents the typeface of a font family such as 'Bold' or 'Italic' Caminhos Nenhuma fonte instalada foi encontrada que corresponda aos critérios de entrada. Listar fontes instaladas Listar todas as fontes instaladas, todos os arquivos de fonte ou detalhes completos de uma fonte específica. Versão Módulos de Configuração PowerShell Modules that are used for the Configuration feature O instalador do pacote requer autenticação. O prompt de autenticação pode aparecer quando necessário. As informações autenticadas serão compartilhadas com a URL de download do instalador. Falha ao baixar o instalador. Esta winget não dá suporte ao método de autenticação de download do instalador. Tente atualizar para a versão mais winget versão. {Locked="winget"} Falha ao baixar o instalador. Falha na autenticação. Especificar o caminho para o processador de configuração Comandos de recurso DSC v3 DSC stands for "Desired State Configuration". It should already have a locked translation. Os sub-comandos aqui implementam recursos Desired State Configuration (DSC) v3 para configurar o winget e os pacotes. Obter o estado do recurso Definir o estado do recurso Descrever as alterações de estado necessárias Testar o estado do recurso Excluir o estado do recurso Obter todas as instâncias de estado Validar o conteúdo do grupo Resolver o estado externo Executar o adaptador Obter o esquema do recurso Obter o manifesto do recurso Gerenciar o estado do pacote Gerencie pacotes por meio do WinGet. Indica se uma instância deve ou não existir. Indica se uma instância está no estado desejado. O identificador do pacote. A origem do pacote. A versão do pacote. O método para correspondência do identificador com um pacote. Indique que a versão mais recente disponível do pacote deve ser instalada. O modo de instalação a ser usado, se necessário. O escopo de destino do pacote. Indica se os contratos de fontes e pacotes devem ser aceitos. Gerenciar o arquivo de configurações do usuário Gerencie as configurações de usuário do WinGet. O conteúdo do arquivo JSON de configurações. A ação utilizada para aplicar as configurações. O valor está registrado para correlação Gerenciar a configuração da origem Gerencie as fontes do WinGet. O nome a ser usado para a origem. O argumento para a origem. O tipo da origem. O nível de confiança da origem. Se a origem é incluída quando as chamadas não especificam uma origem. Aplicando a unidade de configuração... Exportando a unidade de configuração... Obtendo processadores de unidade de configuração... Garantir o módulo necessário para exportação [{0}] {Locked="{0}"} Falha ao testar ou adquirir o módulo necessário. As configurações relacionadas não serão exportadas. Exportar [{0}] {Locked="{0}"} Falha ao exportar o recurso. Falha ao obter processadores de unidade. As configurações de pacote individuais não serão exportadas. Diretório em que os resultados devem ser gravados Desired State Configuration pacote não encontrado no sistema. Instalando o pacote... Falha ao instalar Desired State Configuration pacote. Instale o pacote manualmente ou forneça o caminho para dsc.exe por meio --processor-path argumento. {Locked="dsc.exe","--processor-path"} Um objeto que contém as configurações de administrador e seus valores. Gerenciar as configurações de administrador Gerencie as configurações de administrador do WinGet. Redefinindo todas as configurações de administrador Todas as configurações de administrador foram redefinidas. Informações MCP MCP stands for Model Context Protocol and should probably remain as-is Informações do MCP (Protocolo de Contexto de Modelo) do Gerenciador de Pacotes do Windows. Para configurar manualmente o Gerenciador de Pacotes do Windows MCP com o cliente MCP, use o seguinte fragmento JSON no objeto `servers` servidor: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. Habilitar o servidor MCP do Gerenciador de pacotes do Windows Versão do Sistema Operacional de destino Falha ao instalar uma ou mais fontes. Não há suporte para o arquivo de fonte e ele não pode ser instalado. Não há suporte para uma ou mais fontes no pacote de fontes e ela não pode ser instalada. Falha na instalação da fonte; limpando. Fonte já instalada. ID do pacote WinGet com suporte Título Arquivo da fonte não encontrado. Falha na desinstalação da fonte. A fonte pode não estar em um bom estado. Tente desinstalar após uma reinicialização. Falha na validação da fonte. Falha na reversão da fonte. A fonte pode não estar em um bom estado. Tente desinstalar após uma reinicialização. Mostrar informações detalhadas do arquivo da fonte. Falha na validação do pacote de fontes. A tentativa de reversão da instalação de fonte com falha não foi bem-sucedida. Uma reinicialização pode ser necessária para desinstalar a fonte com êxito. Falha na desinstalação do pacote de fontes. Isso geralmente ocorre devido às fontes que estão sendo usadas pelo sistema ou por um aplicativo. A desinstalação pode ser bem-sucedida após a reinicialização do computador. OK "OK" means the font is in a good healthy state Corrompido "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. Status Desconhecido O pacote de fontes já está instalado. Mostrar informações detalhadas sobre os pacotes instalados Providing this argument causes the CLI to output additional details about installed application packages. Canal: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). Identificador Local: Precedes a value that is the unique identifier for the installed package on the local system. Nome da Família de Pacotes: Precedes a value that is the APPX/MSIX package family name of the installed package. Código do Produto: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. Código de Atualização: Precedes a value that is the MSI Upgrade Code for the installed package. Escopo da Instalação: Precedes a value that is the scope of the installation of the package (ex. user, machine). Arquitetura Instalada: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). Localidade da Instalação: Precedes a value that is the locale of the installed package. Local de Instalação: Precedes a value that is the directory path to the installed package. Fonte de Origem: Precedes a value that names the package source where the installed package originated from. Atualizações Disponíveis: Precedes a list of package upgrades available for the installed package. Categoria do Instalador: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). Valor Antigo Column title for listing edit changes. Novo Valor Column title for listing the new value. ================================================ FILE: Localization/Resources/ru-RU/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/ru-RU/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Присоединенный псевдоним не является флагом: "{0}" {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Псевдоним присоединенного флага не найден: "{0}" {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Доступны следующие аргументы: Message displayed to inform the user about the available command line arguments. Доступны следующие псевдонимы команд. Message displayed to inform the user about the available command line alias arguments. Применимы следующие команды: Title displayed to inform the user about the available commands. Доступно As in "a new version is available to upgrade to". Доступны следующие опции: Message displayed to inform the user about the available options. Доступны следующие подкоманды: Message displayed to inform the user about the available nested commands that run in context of the selected command. Доступны обновления: {0}. {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Использовать указанный канал; по умолчанию предназначено для широкой аудитории команда Label displayed for a command to give the software. Фильтровать результаты по команде Description message displayed to inform the user about filtering the search results by a package command. Полная командная строка для завершения Для выполнения этой команды требуются права администратора. Эту команду можно использовать для запроса завершения контекстной командной строки. Передается командная строка, положение курсора и слово для завершения. Выходные данные представляют собой набор потенциальных значений, основанных на входных данных, по одному возможному значению на строку. Разрешает завершение контекстной командной строки Показать не более указанного числа результатов (от 1 до 1000) Готово Label displayed when an operation completes or is done executing. Поиск пакета по точному совпадению Description message displayed to inform the user about finding an application package using an exact matching criteria. Экспериментальный аргумент для демонстрации Эта команда является примером реализации экспериментального компонента. Чтобы включить его, перейдите к файлу "winget settings" и включите компоненты experimentalCmd или experimentalArg. {Locked="winget settings"} Пример экспериментального компонента Найден позиционный аргумент, когда не ожидалось: "{0}" {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. Этот компонент находится в процессе разработки. В будущем он может быть существенно изменен или полностью удален. Чтобы включить его, измените параметры ("winget settings"), чтобы включить экспериментальный компонент: "{0}" {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Показывает состояние экспериментальных компонентов. Экспериментальные компоненты можно включить с помощью команды "winget settings". {Locked="winget settings"} Показывает состояние экспериментальных компонентов Отключено Включено Компонент Ссылка Следующие экспериментальные компоненты находятся в процессе разработки. Их можно настроить с помощью файла параметров "winget settings". {Locked="winget settings"} Свойство Состояние Файл для хэширования Аргумент флага не может содержать присоединенное значение: "{0}" {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Вычисляет хэш локального файла, подходящий для записи в манифест. Он также может вычислить хэш файла подписи для MSIX-пакета, чтобы разрешить потоковые установки. Вспомогательное приложение для хэширования файлов установщика Отображает справку по выбранной команде Для более подробной информации о конкретной команде передайте ей аргумент справки. Дополнительную справку см. по адресу: {0} {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Фильтровать результаты по идентификатору Подавляет вывод предупреждений Лицензия на это приложение предоставлена вам владельцем. Корпорация Майкрософт не несет ответственность за сторонние пакеты и не предоставляет для них никакие лицензии. Этот пакет предоставляется через Microsoft Store. Программе winget может потребоваться получить пакет в Microsoft Store от имени текущего пользователя. {Locked="winget"} Устанавливает выбранный пакет, найденный путем поиска в настроенном источнике или непосредственно из манифеста. По умолчанию запрос должен сравнить id, name или moniker пакета (без учета регистра). Можно использовать и другие поля, указав соответствующий параметр. По умолчанию команда установки проверяет состояние установки пакета и, если применимо, пытается выполнить обновление. Переопределите с помощью параметра --force для прямой установки. {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Установка указанного пакета Хэш установщика не совпадает. Невозможно переопределить этот параметр при запуске от имени администратора. Хеш установщика не совпадает. Продолжение работы, так как указан параметр --ignore-security-hash {Locked="--ignore-security-hash"} Хэш установщика не совпадает. Чтобы переопределить это ограничение, используйте параметр --ignore-security-hash {Locked="--ignore-security-hash"} Хэш установщика успешно проверен Успешно установлено Запуск установки пакета... Игнорировать ошибку проверки хэша установщика Игнорирование проверки вредоносных программ, выполняемое как часть установки пакета архивного типа из локального манифеста. Запросить интерактивную установку; может потребоваться ввод данных пользователем Псевдоним аргумента не был распознан для текущей команды: "{0}" {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Неверный указатель аргумента: "{0}" {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). Имя аргумента не распознано для текущей команды: "{0}" {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Каталоги WinGet Header for a table detailing the directories Winget uses for key operations like logging and portable installs Используемый языковой стандарт (формат BCP47) {Locked="BCP47"} Лицензионное соглашение Ссылки Links to different webpages Команда list отображает пакеты, установленные в системе, а также доступность обновления. Для фильтрации выходных данных можно указать дополнительные параметры по аналогии с командой search. {Locked="list","search"} Отображать установленные пакеты Место для установки (если поддерживается) Расположение журнала (если поддерживается) (с) Корпорация Майкрософт (Microsoft Corporation). Все права защищены. Домашняя страница The primary webpage for the software Путь к манифесту пакета Ошибка проверки манифеста. Проверка манифеста выполнена успешно. Проверка манифеста выполнена с предупреждениями. Требуется значение аргумента, но ничего не найдено: "{0}" {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Фильтровать результаты по моникеру Входной файл будет обрабатываться как формат MSIX; при наличии подписи будет предоставлен хэш подписи Не удалось вычислить хэш подписи MSIX. Не удалось установить или обновить пакет Microsoft Store, так как конкретное приложение заблокировано политикой Не удалось установить или обновить пакет Microsoft Store. Код ошибки: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Не удалось установить или обновить пакет Microsoft Store, так как клиент Microsoft Store заблокирован политикой Проверка/запрос сведений о получении пакета... Обнаружено несколько установленных пакетов, соответствующих введенным условиям. Уточните условия. Обнаружено несколько пакетов, совпадающих с введенными условиями. Уточните условия. Фильтровать результаты по имени Применимый установщик не найден. Дополнительные сведения см. в журналах. В настоящее время нет доступных экспериментальных компонентов. Не найдены установленные пакеты, соответствующие введенным условиям. Не найдены пакеты, совпадающие с введенными условиями. Отключает дисплей VirtualTerminal {Locked="VirtualTerminal"} Открыть расположение журналов по умолчанию параметры Options to change how a command works Переопределить аргументы, передаваемые установщику Пакет: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. К сожалению, мы забыли сделать это... Положение курсора в командной строке Заявление о конфиденциальности Запрос, используемый для поиска пакета Отображение хода выполнения в виде радуги цветов Обязательный аргумент не указан: "{0}" {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Отображение хода выполнения в виде стандартного цвета Поиск пакетов в настроенных источниках. Поиск и отображение базовых сведений о пакетах ИД Abbreviation of Identifier. Совпадение Имя Источник дополнительные записи усечены из-за ограничения количества результатов Версия Обнаружены следующие ошибки при проверке параметров: Открытие параметров в текстовом редакторе JSON по умолчанию. Если редактор не настроен, параметры будут открыты в Блокноте. Доступные параметры см. по адресу https://aka.ms/winget-settings С помощью этой команды также можно настраивать параметры администратора, указав аргумент --enable или --disable. {Locked="--enable"} {Locked="--disable"} Открыть параметры или настроить параметры администратора Непредвиденная ошибка при загрузке параметров. Проверьте параметры, выполнив команду 'settings'. {Locked="'settings'"} Канал Отображает сведения о конкретном пакете. По умолчанию запрос должен сравнить идентификатор, имя или моникер пакета (без учета регистра). Можно использовать и другие поля, указав соответствующий параметр. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Показывает сведения о пакете Версия Запросить автоматическую установку Только один псевдоним символа может появляться после одного символа -: "{0}" {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. Источник с указанным именем уже существует и ссылается на другое расположение: На это расположение уже ссылается источник с другим именем: Источник с указанным именем уже существует и ссылается на это же расположение: Добавление источника: Добавление нового источника. Источник предоставляет данные для обнаружения и установки пакетов. Добавляйте новый источник только в том случае, если вы доверяете ему как надежному расположению. Добавление нового источника Аргумент, предоставленный источнику Поиск пакета с помощью указанного источника Управление источниками с помощью подкоманд. Источник предоставляет данные для обнаружения и установки пакетов. Добавляйте новый источник только в том случае, если вы доверяете ему как надежному расположению. Управление источниками пакетов Изменение свойств существующего источника. Источник предоставляет данные для обнаружения и установки пакетов. Изменить свойства источника Редактирование источника: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Источник с '{0}' уже находится в нужном состоянии. {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Аргумент Value given to source. Перечисление всех текущих источников или полных сведений об определенном источнике. Перечисление текущих источников Данные Data stored by the source. Поле The name of a piece of information about a source. Имя The name of the source. Идентификатор The source's unique identifier. Не найден источник с именем: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. Нет настроенных источников. Тип The kind of source. Обновлено The last time the source was updated. никогда The source has never been updated. Значение The value of information about a source. Имя источника Сбой при открытии источников; выполните команду ''source reset'', если проблема повторится. {Locked="source reset"} Не удалось открыть предопределенный источник. Сообщите об этом специалистам по обслуживанию winget. {Locked="winget"} Удаление всех источников... Удаление определенного источника. Удаление текущих источников Удаление источника: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Сброс всех источников... Эта команда удаляет существующие источники, в результате чего могут остаться локальные данные. При отсутствии аргументов она удаляет все источники и добавляет значения по умолчанию. Если указан именованный источник, удаляется только он. Сброс источников Принудительный сброс источников Если задан параметр --force, будут сброшены следующие источники: {Locked="--force"} Сброс источника: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Тип источника Обновление всех источников... Обновление всех источников или только определенного источника. Обновление текущих источников Обновление источника: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Фильтровать результаты по тегу Благодарим за использование виджета Уведомления третьих сторон Программа командной строки winget дает возможность устанавливать приложения и другие пакеты из командной строки. Отображать общие сведения об инструменте Отобразить версию инструмента Аргумент предоставлен больше раз, чем разрешено: "{0}" {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. Указано несколько аргументов поведения выполнения: '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). При выполнении команды произошла непредвиденная ошибка: Удалить предыдущую версию пакета во время обновления Нераспознанная команда: "{0}" {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Обновить все установленные пакеты до последней версии, если она доступна Применимые обновления не найдены. В настроенном источнике доступна более новая версия пакета, но она не применяется к системе или требованиям. Доступные обновления не найдены. Более новые версии пакетов недоступны в настроенных источниках. Обновляет выбранный пакет, обнаруженный путем поиска в списке установленных пакетов или непосредственно из манифеста приложения. По умолчанию запрос должен соответствовать идентификатору, имени или пути ссылки пакета (без учета регистра). Можно использовать и другие поля, указав соответствующий параметр. Если аргументы не указаны, показывает пакеты с доступными обновлениями id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Отображает и выполняет доступные обновления использование: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Проверяет манифест с помощью строгого набора рекомендаций. Это позволяет вам проверить манифест перед отправкой в репозиторий. Утверждает файл манифеста Путь к манифесту, подлежащему утверждению Включает ведение подробного журнала для WinGet Убедитесь, что входной файл является допустимым подписанным MSIX-файлом. Использовать указанную версию; по умолчанию используется последняя версия Показать доступные версии пакета Значение, указанное до запроса завершения Не найдены совпадающие версии: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. Нет источников, совпадающих с указанным значением: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. Настроенные источники: Источники не указаны. Добавьте источник с помощью команды "source add" или восстановите значения по умолчанию с помощью "source reset" {Locked="source add","source reset"} Найдено Указан путь к каталогу: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. Файл не существует: {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Предоставляются аргументы и локального манифеста, и поискового запроса Журналы Label displayed for diagnostic files containing information about the application use. Установщик заблокирован политикой Установщик не прошел проверку безопасности Антивирусная программа сообщает о заражении в установщике Не удалось обновить источник: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Удаление выбранного пакета, обнаруженного путем поиска в списке установленных пакетов либо непосредственно из манифеста. По умолчанию запрос должен соответствовать идентификатору, имени или моникеру пакета (без учета регистра). Можно использовать и другие поля, указав соответствующий параметр. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Удаление указанного пакета Запуск удаления пакета... Удалено winget не может найти команду удаления для пакета. Обратитесь за помощью к издателю пакетов. {Locked="winget"} Удаление прекращено Не удалось удалить. Код выхода: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Экспортирует список установленных пакетов Установка всех пакетов, перечисленных в файле. Устанавливает все пакеты в файле Файл, в который будет записан результат Файл с описанием устанавливаемых пакетов Экспорт пакетов из указанного источника Записывает список установленных пакетов в файл. После этого пакеты можно установить командой import. {Locked="import"} Не удалось установить один или несколько импортированных пакетов Источник, необходимый для импорта, не установлен: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. Установленный пакет недоступен из любого источника: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. Установленная версия пакета недоступна из любого источника: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. Пакеты в файле импорта не найдены Файл JSON не является допустимым Пакет уже установлен: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. Игнорировать недоступные пакеты Включить версии пакетов в файл экспорта Игнорировать версии пакета из файла импорта Путь не существует: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. В JSON-файле не указана распознанная схема. Выберите область установки (user или machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. Для аргумента "{0}" указано недопустимое значение; допустимые значения: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. Эта операция отключена групповая политика: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Включить дополнительные источники Установщика приложений для Windows Включить разрешенные источники Установщика приложений для Windows Включить источник по умолчанию Установщика приложений для Windows Включить экспериментальные функции Установщика приложений для Windows Включить источник Microsoft Store Установщика приложений для Windows Включить источник шрифтов Установщика Windows App Включить параметры Диспетчера пакетов Windows Включить Диспетчер пакетов Windows Включить интерфейсы командной строки Диспетчера пакетов Windows Задать интервал автоматического обновления источника Диспетчера пакетов Windows в минутах Групповая политика Header for a table listing active Group Policies Включить локальные файлы манифестов Установщика приложений для Windows Включить обход закрепленного сертификата источника Microsoft Store Установщика приложений для Windows Включение переопределения сканирования вредоносных программ в локальном архиве установщика приложений Windows Поле: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. Недопустимый формат поля. Недопустимое значение поля. Недопустимый параметр из групповой политики. Загруженные параметры из файла резервной копии. Ошибка при анализе файла: Значение: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. Выполняются следующие экспериментальные функции. Конфигурация отключена из-за групповой политики. Хэш-код установщика не совпадает. Включить переопределение хэша Установщика приложений для Windows Экспорт текущих источников в формате JSON для групповой политики. Экспорт текущих источников Дополнительный источник An additional source required by policy. Разрешенный источник A source that the user is allowed to add. Для аргумента "{0}" указано недопустимое значение {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Отменено Внешние У пакетов, обнаруженных в этом импорте, следующие зависимости: Import command sentence showed before reporting dependencies Для этого пакета требуются следующие зависимости: Message shown before reporting dependencies Установка зависимостей: Источник зависимостей не найден Поиск пакетов дает несколько результатов: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. Не найдена последняя версия для пакета: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. Установщики не найдены: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Для пакета недоступна минимальная необходимая версия: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. Нет совпадений When package search yields no matches Содержит цикл Dependency graph has loop Не найден подходящий установщик для манифеста: {0} версии {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Ошибка обработки зависимостей пакета. Продолжить установку? Prompt message shown when dependencies processing yields errors. Ошибка обработки зависимостей пакета. Выход... Пакеты У этого пакета есть зависимости, которые могут больше не требоваться: Uninstall command sentence showed before reporting dependencies Манифест содержит следующие непроверенные зависимости; убедитесь, что они действительны: Validate command sentence showed before reporting dependencies Компоненты Windows Библиотеки Windows Поиск зависимостей пакета с помощью указанного источника For getting package type dependencies when installing from a local manifest Условия Windows Store Принять все лицензионные соглашения для пакетов Для установки экспортированного пакета требуется лицензионное соглашение: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. Издатель требует, чтобы вы просмотрели указанную выше информацию и приняли соглашения перед установкой. Вы согласны с условиями? Соглашения для пакетов не приняты. Операция отменена. Соглашения: Автор: Описание: Установщик: Языковой стандарт установщика: Идентификатор продукта в Store: SHA256 установщика: Тип установщика: URL-адрес установщика: Лицензия: URL-адрес лицензии: Моникер: Домашняя страница: Издатель: Теги: Версия: Зависимости: Компоненты Windows: Библиотеки Windows: Зависимости пакетов: Внешние зависимости: Нет Да Не удалось открыть добавленный источник. Принимать все соглашения источников во время операций с источниками Перед использованием источника "{0}" необходимо просмотреть следующие соглашения. {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. Вы согласны со всеми условиями исходных соглашений? Соглашения для одного или нескольких источников не приняты. Операция отменена. Примите соглашения источников или удалите эти источники. Для правильной работы источника требуется отправить во внутреннюю службу двухбуквенный код текущего региона компьютера (например, "RU"). Успешно установлено. Перезапустите приложение, чтобы завершить обновление. Необязательный HTTP-заголовок источника REST Windows-Package-Manager Необязательный заголовок игнорируется, поскольку он неприменим для этого источника. Необязательный заголовок неприменим, если не указан источник: "{0}" {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Дата выпуска: Поддерживается автономное распространение: URL-адрес издателя: URL-адрес покупки: URL-адрес службы поддержки издателя: URL-адрес заявления о конфиденциальности: Авторское право: URL-адрес заявления об авторских правах: Заметки о выпуске: URL-адрес заметок о выпуске: Сбой при поиске в источнике; результаты не будут включены: {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Сбой при поиске в источнике: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Эту функцию должен включить администратор. Чтобы включить ее, выполните команду "winget settings --enable {0}" от имени администратора. {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Включает определенный параметр администратора Отключает определенный параметр администратора Включен параметр администратора "{0}". {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. Отключен параметр администратора "{0}". {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. Параметр администратора Header for a table displaying admin settings. Приложение сейчас запущено. Выйдите из приложения и повторите попытку. Файлы, измененные установщиком, в настоящее время используются другим приложением. Выйдите из приложений и повторите попытку. Уже выполняется другая установка. Повторите попытку позже. Используется один или несколько файлов. Выйдите из приложения и повторите попытку. У этого пакета отсутствует зависимость в вашей системе. На вашем компьютере больше нет места. Освободите место и повторите попытку. Недостаточно памяти для установки. Закройте другие приложения и повторите попытку. Один из параметров установки недопустим. Дополнительные сведения могут содержаться в журнале установки пакета. Для этого приложения требуется подключение к Интернету. Подключитесь к сети и повторите попытку. Приложение обнаружило ошибку во время установки. Обратитесь в службу поддержки. Перезапустите компьютер, чтобы завершить установку. Ваш компьютер будет перезагружен, чтобы завершить установку. Сбой установки. Перезагрузите компьютер и повторите попытку. Вы отменили установку. Уже установлена другая версия этого приложения. Уже установлена более поздняя версия этого приложения. Установка запрещена политиками организации. Обратитесь к администратору. Текущая конфигурация системы не поддерживает установку этого пакета. Сбой установки из-за ошибки настраиваемого установщика. Обратитесь в службу поддержки пакета. Установка прервана Сбой установки с кодом выхода: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. Журнал установщика доступен по адресу: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. Среди рабочих источников найдены следующие пакеты. Чтобы продолжить, укажите один из них, используя параметр "--source". {Locked="--source"} "working sources" as in "sources that are working correctly" Пакеты не найдены среди рабочих источников. "working sources" as in "sources that are working correctly" Это стабильный выпуск Диспетчера пакетов Windows. Чтобы попробовать экспериментальные функции, установите предварительную сборку. Инструкции доступны в GitHub по ссылке https://github.com/microsoft/winget-cli. {Locked="https://github.com/microsoft/winget-cli"} Диспетчер пакетов Windows версии {0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. Игнорировать версии пакета в файле импорта Запрошенное число результатов должно находиться в диапазоне от 1 до 1000. Аргументы, передаваемые установщику в дополнение к параметрам по умолчанию Обнаружена более новая версия, но технология установки отличается от текущей установленной версии. Удалите пакет и установите более новую версию. Указанная технология установки более новой версии отличается от текущей установленной версии. Удалите пакет и установите более новую версию. Диспетчер пакетов Windows (предварительная версия) версии {0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Выберите архитектуру Обновить пакеты, даже если не удается определить их текущую версию Список пакетов, даже если их текущая версия не может быть определена. Можно использовать только с аргументом --upgrade-available. {Locked="--upgrade-available"} Невозможно определить номер версии этого пакета. Чтобы все равно обновить его, добавьте аргумент --include-unknown к предыдущей команде. {Locked="--include-unknown"} Несколько ({0}) пакетов содержат номера версий, которые невозможно определить. Используйте параметр --include-unknown для просмотра всех результатов. {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. Несколько ({0}) пакетов закреплены и должны быть явно обновлены. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. Указанные аргументы можно использовать только с запросом. Архитектура системы: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). Сохраняет все файлы и каталоги, созданные пакетом (переносной) Удаляет все файлы и каталоги в каталоге пакета (переносной) Для продолжения нажмите клавишу ВВОД . . . Значение для переименования исполняемого файла (переносной) Запрашивает нажатие любой клавиши пользователем перед выходом Установщик запросит запуск от имени администратора. Ожидайте запроса. Не удается запустить установщик из контекста администратора. Переменная среды пути изменена; перезапустите оболочку, чтобы использовать новое значение. Фильтры с использованием кода продукта Переносной пакет с таким именем, но из другого источника уже существует; выполняется из-за параметра --force {Locked="--force"} Том не поддерживает точки повторного анализа Указанное имя файла не является допустимым именем файла Перезапись существующего файла:{0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. Не указан аргумент выбора пакета; дополнительные сведения о поиске пакета см. в справке. Добавлен псевдоним командной строки: Каталог переносимых ссылок (компьютер) Каталог переносимых ссылок (пользователь) Корень переносимого пакета (пользователь) Корень переносимого пакета Корень переносимого пакета (x86) Сбой переносной установки; Очистка... Переносимый пакет был изменен. Продолжение работы, так как указан параметр --force {Locked="--force"} Не удалось удалить переносимый пакет, так как он был изменен. Чтобы переопределить эту проверку, используйте параметр --force {Locked="--force"} Файлы, остающиеся в каталоге установки:: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Очистка каталога установки... Не удается очистить каталог установки, так как он не был создан с помощью WinGet Связанная ссылка Документация: Заметки: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Примечания по установке: Указанный аргумент не поддерживается для этого пакета Не удалось извлечь содержимое архива Вложенный файл установщика не существует. Убедитесь, что указанный относительный путь вложенного установщика совпадает: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Недопустимый относительный путь к файлу для вложенного установщика. Путь указывает на расположение за пределами каталога установки Для этого пакета не указаны вложенные установщики Для установщика архива можно указать только один вложенный установщик, если только он не является переносимым или вложенным установщиком шрифтов Указаны несовместимые аргументы командной строки Перечисляет только пакеты с доступным обновлением Для следующих пакетов доступно обновление, но для обновления требуется явный выбор целевого объекта. "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly Скачивание Label displayed while downloading an application installer. Вложенный тип установщика не поддерживается Известно, что установщик перезапускает терминал или оболочку Для этого пакета требуется расположение установки Известно, что следующие установщики перезапускают терминал или оболочку. Для следующих установщиков требуется расположение установки. Укажите корень установки: Вы хотите продолжить? Соглашения для This will be followed by a package name, and then a list of license agreements Для пакета требуется расположение установки, но оно не было предоставлено Отключить интерактивные запросы Description for a command line argument, shown next to it in the help Найден существующий установленный пакет. Попытка обновления установленного пакета... Выполнить команду напрямую и продолжить с проблемами, не связанными с безопасностью Description for a command line argument, shown next to it in the help Переносимый пакет из другого источника уже существует Архив извлечен Извлечение архива... При проверке архива обнаружено вредоносное ПО. Чтобы переопределить эту проверку, используйте --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} При проверке архива обнаружено вредоносное ПО. Продолжено из-за --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} Пропускает обновление, если установленная версия уже существует Версия пакета уже установлена. Установка отменена. Не удается включить {0}. Этот параметр управляется политикой. Для получения дополнительных сведений обратитесь к системному администратору. {Locked="{0}"} The value will be replaced with the feature name Не удается отключить {0}. Этот параметр управляется политикой. Для получения дополнительных сведений обратитесь к системному администратору. {Locked="{0}"} The value will be replaced with the feature name Добавление нового закрепления. Закрепление может помешать Диспетчеру пакетов Windows обновить пакет до определенных диапазонов версий или предотвратить обновление пакета. Закрепленный пакет может по-прежнему обновляться сам и обновляться вне Диспетчера пакетов Windows. По умолчанию закрепленный пакет можно обновить, явно указав его в команде "upgrade" или добавив флаг "--include-pinned" в "winget upgrade --all". {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} Добавление нового закрепления Управление закреплениями пакетов с помощью подкоманд. Закрепление может помешать Диспетчеру пакетов Windows обновить пакет до определенных диапазонов версий или предотвратить обновление пакета. Закрепленный пакет может по-прежнему обновляться сам и обновляться вне Диспетчера пакетов Windows. Управление закреплениями пакетов Список всех текущих контактов или полных сведений о конкретном закреплении. Список текущих закреплений Удаление закрепления определенного пакета. Удаление закрепления пакета Сброс всех существующих закреплений. Сбросить закрепления Версия для закрепления пакета. Подстановочный знак "*" можно использовать как часть последней версии Блокировать обновление до тех пор, пока закрепление не будет удалено. Это предотвращает переопределение аргументов Закрепить определенную установленную версию Установлено Value used in a table to indicate that a package comes from the list of packages installed in the machine Экспорт параметров в формате JSON Параметры экспорта Параметры пользователя Label displayed for the file containing the user settings. Не удалось загрузить файл параметров. Используются значения по умолчанию. Выберите фильтр установленных пакетов (для user или machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. Закрепление добавлено Пакет {0} уже закреплен {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. Пакет {0} уже закреплен. Перезапись из-за аргумента --force. {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Пин-код для пакета {0}. Используйте аргумент --force, чтобы перезаписать его. {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Сброс всех текущих закреплений. Используйте аргумент --force для сброса всех закреплений. Будут удалены следующие закрепления: {Locked="--force"} Тип закрепления Нет закрепления для пакета {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. Закрепления не настроены. Shown when listing or modifying existing pins if there are none. Закрепления сброшены Shown after resetting (deleting) all the pins Не удалось открыть базу данных закреплений. Error message for when we cannot open the database containing package pins. Указан аргумент, который можно использовать только для одного пакета Указано несколько взаимоисключающих аргументов: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together Аргумент {0} может использоваться только с {1} {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" Отключено As in enabled/disabled Включено As in enabled/disabled Состояние Header for a table listing the state (enabled/disabled) of Group Policies and Settings Запрос, используемый для поиска пакета Пакет не найден: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. Найдено несколько пакетов для: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. Сбой поиска для: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. Несколько ({0}) пакетов используют закрепление, которое необходимо удалить перед обновлением {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade Невозможно обновить пакет с помощью winget. Для обновления этого пакета используйте метод, предоставленный издателем. Обновление пакетов, даже если они используют неблокирующее закрепление Список пакетов, даже если у них есть ПИН-код, который блокирует обновление. Можно использовать только с аргументом --upgrade-available. {Locked="--upgrade-available"} Закрепление удалено Обнаружена более новая версия, но пакет содержит закрепление, которое не позволяет обновить его. Пакет закреплен и не может быть обновлен. Используйте команду "winget pin" для просмотра и изменения закреплений. Некоторые типы закреплений можно обойти с помощью аргумента --include-pinned. {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned Несколько ({0}) пакетов используют закрепления, которые препятствуют обновлению. Воспользуйтесь командой "winget pin" для просмотра и изменения закреплений. Использование аргумента --include-pinned может привести к отображению дополнительных результатов. {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages Гарантирует, что система соответствует нужному состоянию, как описано в указанной конфигурации. Может скачивать/выполнять обработчики для достижения нужного состояния. Необходимо проверить надежность конфигурации и обработчиков перед их применением. Настройка системы в нужное состояние Отображает сведения об указанной конфигурации. По умолчанию система не изменяется, но некоторые параметры будут вызывать скачивание и/или загрузку файлов. Отображение сведений о конфигурации Проверяет, соответствует ли система нужному состоянию, как описано в указанной конфигурации. Может скачивать/выполнять обработчики для тестирования нужного состояния. Необходимо проверить надежность конфигурации и обработчиков перед их выполнением. Проверяет систему на соответствие нужному состоянию Проверяет правильность файла конфигурации. Проверяет файл конфигурации Недопустимый '{0}' в файле конфигурации. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. Путь к файлу конфигурации Файл конфигурации недопустим. Версия файла конфигурации {0} неизвестна. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. Принимает предупреждение конфигурации, препятствующее интерактивному запросу Применить Indicates that this item is used to write state Декларация Indicates that this item is used to check/assert the state rather than write to it Зависимости:{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. Не удалось применить часть конфигурации. Не удалось получить подробные сведения о конфигурации. Информирование Indicates that this item is used to retrieve values for future use rather than writing them Локальный Used to indicate that the item is present on the device. Модуль: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. Модуль: {0} от {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. Параметры: Label for the values that are used as inputs for this item when applying state Конфигурация применена. Элемент успешно применен. К системе применяется другая конфигурация. Эта конфигурация будет продолжена, как только это будет возможно... Вы самостоятельно отвечаете за понимание параметров конфигурации, которую собираетесь выполнить. Корпорация Майкрософт не несет ответственности за файл конфигурации, который вы создали или импортировали. Эта конфигурация может изменить параметры Windows, установить программное обеспечение, изменить параметры установленного программного обеспечения (включая параметры безопасности), а также принять от вашего имени пользовательские соглашения для сторонних пакетов программ и служб. Запуская этот файл конфигурации, вы подтверждаете, что понимаете состав этих ресурсов и параметров и соглашаетесь их применить. Любые установленные приложения лицензируются их владельцами. Майкрософт не несет ответственности за сторонние пакеты программ или службы и не предоставляет для них никаких лицензий. Legal approved. Do not change without approval. Вы проверили конфигурацию и хотите продолжить ее применение к системе? PM approved. Конфигурация пуста. Компонент [{0}] не найден. {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. Не удалось включить зависимости компонентов Windows. Чтобы продолжить установку, используйте "--force". {Locked="--force"} Для полного включения компонентов Windows требуется перезагрузка. Чтобы переопределить эту проверку, используйте "--force". "Windows Feature(s)" is the name of the options Windows features setting. Не удалось включить зависимости компонентов Windows; продолжение из-за --force "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} Включение компонента [{0}]… {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. Не удалось включить компонент [{0}]: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. Для полного включения компонентов Windows требуется перезагрузка; продолжение из-за --force "Windows Feature(s)" is the name of the options Windows features setting. Ожидание завершения другой установки или удаления... Закрепленная версия Table header for the version to which a package is pinned; meaning it should not update from that version. <Дополнительные сведения см. в файле журнала> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. Получение сведений о конфигурации Инициализация системы конфигурации Чтение файла конфигурации Система не находится в нужном состоянии, утвержденном конфигурацией. Сбой этой единицы конфигурации по неизвестной причине: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Сбой единицы конфигурации из-за конфигурации: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Сбой блока конфигурации при попытке получить текущее состояние системы. Сбой блока конфигурации при попытке применить нужное состояние. Сбой блока конфигурации при попытке проверить текущее состояние системы. Сбой единицы конфигурации из-за внутренней ошибки: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Сбой единицы конфигурации из-за недопустимого предварительного условия: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Сбой единицы конфигурации из-за состояния системы: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Сбой модуля конфигурации при попытке запуска: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Конфигурация содержит идентификатор "{0}" несколько раз. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. Зависимость "{0}" не найдена в конфигурации. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. Этот блок конфигурации был пропущен вручную. Модуль для блока конфигурации доступен в нескольких расположениях с одинаковой версией. Не удалось загрузить модуль для единицы конфигурации. Обнаружено несколько соответствий для блока конфигурации. Укажите модуль, чтобы выбрать правильный вариант. Не удалось найти блок конфигурации. Блок конфигурации отсутствовал в ожидаемом модуле. Этот блок конфигурации не был запущен из-за сбоя зависимости или невыполненного запуска зависимости. Этот блок конфигурации не был запущен из-за сбоя утверждения или неверного утверждения. Модуль конфигурации вернул непредвиденный результат во время выполнения. Эта единица конфигурации не была запущена по неизвестной причине: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. Поле '{0}' имеет недопустимое значение: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. Поле "{0}" отсутствует или пусто. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. См. {0} строку, столбец {1} в файле. {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. Выполняется отмена операции Установить пакет заглушки для AppInstaller Установить полный пакет для AppInstaller Включите расширенные функции. Требуется доступ к хранилищу. Параметры "--enable" и "--disable" нельзя использовать с другими аргументами. {Locked="--enable", "--disable"} Включение расширенных функций. Требуется доступ к хранилищу. Расширенные функции не включены. Выполните "winget configure --enable", чтобы включить их. {Locked="winget configure --enable"} Расширенные функции включены. Отключить расширенные функции. Требуется доступ к хранилищу. Отключение расширенных функций. Требуется доступ к хранилищу. Расширенные функции отключены. Пропускает обработку зависимостей пакетов и компонентов Windows Зависимости пропущены. Не удалось обновить переменную PATH для процесса. Последующие установки, зависящие от изменений переменной PATH, могут быть завершиться сбоем. {Locked="PATH"} Сбой некоторых модулей конфигурации при проверке их состояния. Система находится в описанном состоянии конфигурации. Состояние конфигурации не протестировано. Система не находится в описанном состоянии конфигурации. Непредвиденный результат теста: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). Вы просмотрели конфигурацию и хотите продолжить ее проверку в системе? Файл конфигурации не является допустимым YAML файла. {Locked="YAML"} YAML is a file format name. Этот модуль конфигурации является частью цикла зависимостей. Проверка не обнаружила проблем. Модуль конфигурации доступен только в качестве предварительной версии, но не помечен так в конфигурации. Добавьте "allowPrerelease: true" в "directives". {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. Модуль конфигурации найден локально, но не найден ни в одном из настроенных каталогов. Перед применением конфигурации убедитесь, что он присутствует в какой-либо системе. Модуль конфигурации не является общедоступным. Убедитесь, что у любого пользователя, который будет применять эту конфигурацию, есть к ней доступ. Модуль не указан. Указание модуля повышает производительность и предотвращает конфликты имен в будущем. Скачивание установщика из выбранного пакета, найденного путем поиска настроенного источника или непосредственно из манифеста. По умолчанию запрос должен без учета регистра совпадать с идентификатором, именем или моникером пакета. Другие поля можно использовать путем передачи соответствующего параметра. По умолчанию команда скачивания загрузит соответствующий установщик в папку загрузок пользователя. Скачивание установщика из указанного пакета Каталог, в который скачиваются установщики Загрузка зависимостей: Установщик скачан: {0} {Locked="{0}"} Full path of the downloaded installer. Выберите тип установщика Скачивания установщика Загрузка установщика для более поздней автономной установки запрещена. Для выполнения команды --module-path allusers требуются права администратора. {Locked="--module-path allusers"} Указывает расположение на локальном компьютере для хранения модулей. По умолчанию: %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules. {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} Для --module-path должно быть задано значение currentuser, allusers, default или абсолютный путь. {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Включить конфигурацию Диспетчера пакетов Windows Получение сведений об ошибках. Если указано число, выходные данные будут содержать сведения об ошибке, включая имя символа, если это ошибка winget. При указании строки для этого значения будут искаться ошибки, относящиеся к winget. Получить информацию об ошибках Значение для поиска в сведениях об ошибке Указанное число слишком велико для HRESULT. Неизвестный код ошибки Внутренняя ошибка Недопустимые аргументы командной строки Не удалось выполнить команду Не удалось открыть манифест Получен сигнал отмены Сбой при выполнении ShellExecute Не удается обработать манифест. Версия манифеста выше поддерживаемой. Обновите клиент. Не удалось скачать установщик Не удается выполнить запись в индекс; более поздняя версия схемы Индекс поврежден Сведения о настроенном источнике повреждены Имя источника уже настроено Недопустимый тип источника Файл MSIX является набором, а не пакетом Отсутствуют данные, требуемые источником Ни один из установщиков не подходит для текущей системы Хэш файла установщика не соответствует манифесту Имя источника не существует Исходное расположение уже настроено под другим именем Пакеты не найдены Источники не настроены Найдено несколько пакетов, соответствующих условиям Соответствующих условиям манифестов не найдено Не удалось получить общедоступную папку из исходного пакета Для выполнения команды требуются права администратора Исходное расположение не защищено Клиент Microsoft Store заблокирован политикой Приложение Microsoft Store заблокировано политикой Эта функция сейчас находится в разработке. Его можно включить с помощью параметров winget. Не удалось установить приложение Microsoft Store Не удалось выполнить автозаполнение Не удается инициализировать средство синтаксического анализа YAML Обнаружен недопустимый ключ YAML Обнаружен повторяющийся ключ YAML Недопустимая операция YAML Не удалось создать документ YAML Недопустимое состояние передатчика YAML Недопустимые данные YAML Ошибка LibYAML Проверка манифеста завершена с предупреждениями Ошибка проверки манифеста Недопустимый манифест Применимые обновления не найдены Команда winget upgrade --all завершилась с ошибками Установщик не прошел проверку безопасности Размер скачиваемого файла не соответствует ожидаемому размеру содержимого Команда удаления не найдена Не удалось выполнить команду удаления Ошибка итератора прерывания ICU Ошибка сопоставления регистра ICU Ошибка регулярного выражения ICU Не удалось установить один или несколько импортированных пакетов Не удалось найти один или несколько запрошенного пакета Недопустимый JSON-файл Расположение источника не является удаленным Настроенный источник REST не поддерживается Источник REST вернул недопустимые данные Операция заблокирована групповой политикой Внутренняя ошибка REST API Недопустимый URL-адрес источника REST REST API вернул неподдерживаемый тип MIME Недопустимая версия контракта источника REST Исходные данные повреждены или незаконно изменены Ошибка чтения из потока Соглашения для пакетов не приняты Ошибка при чтении входных данных в запросе Запрос поиска не поддерживается одним или несколькими источниками Конечная точка REST API не найдена. Не удалось открыть источник. Соглашения для источников не приняты Размер заголовка превышает допустимый предел в 1024 символа. Уменьшите размер и повторите попытку. Отсутствует файл ресурсов Не удалось выполнить установку MSI Недопустимые аргументы для msiexec Не удалось открыть один или несколько источников Не удалось проверить зависимости Отсутствует один или несколько пакетов Недопустимый столбец таблицы Версия обновления не новее установленной версии Версия обновления неизвестна, и переопределение не указано Ошибка преобразования ICU Не удалось установить переносимый пакет Том не поддерживает точки повторного анализа Переносимый пакет из другого источника уже существует. Не удалось создать symlink. Путь указывает на каталог. Не удается запустить установщик из контекста администратора. Не удалось удалить переносимый пакет Не удалось проверить значения DisplayVersion на соответствие индексу. Один или несколько аргументов не поддерживаются. Внедренные нуль-символы запрещены для SQLite Не удалось найти вложенный установщик в архиве. Не удалось извлечь из архива. Указан недопустимый относительный путь к вложенным установщикам. Сертификат сервера не соответствует ни одному из ожидаемых значений. Необходимо указать папку для установки. Не удалось проверить архив на наличие вредоносных программ. Найдена по крайней мере одна версия установленного пакета. Закрепление для пакета уже существует. Нет закрепления для пакета. Не удалось открыть базу данных закреплений. Не удалось установить одно или несколько приложений Не удалось удалить одно или несколько приложений Один или несколько запросов вернули больше одного совпадения Пакет защищен ПИН-кодом, что не позволяет выполнить обновление. Установленный пакет является заглушкой Получен сигнал завершения работы приложения Не удалось скачать зависимости пакета. Не удалось скачать пакет. Скачивание для автономной установки запрещено. Требуемая служба занята или недоступна. Повторите попытку позже. Указанный GUID не соответствует допустимому состоянию возобновления. Текущая версия клиента не соответствует версии клиента сохраненного состояния. Недопустимые данные о состоянии возобновления. Не удалось открыть базу данных контрольных точек. Превышено максимальное ограничение возобновления. Неверные сведения для проверки подлинности. Метод проверки подлинности не поддерживается. Произошел сбой проверки подлинности. Сбой проверки подлинности. Требуется интерактивная проверка подлинности. Сбой проверки подлинности. Отменено пользователем. Сбой проверки подлинности. Прошедшая проверку подлинности учетная запись не подходит. Приложение сейчас запущено. Выйдите из приложения и повторите попытку. Уже выполняется другая установка. Повторите попытку позже. Используется один или несколько файлов. Выйдите из приложения и повторите попытку. У этого пакета отсутствует зависимость в вашей системе. На вашем компьютере больше нет места. Освободите место и повторите попытку. Недостаточно памяти для установки. Закройте другие приложения и повторите попытку. Для этого приложения требуется подключение к Интернету. Подключитесь к сети и повторите попытку. Приложение обнаружило ошибку во время установки. Обратитесь в службу поддержки. Перезапустите компьютер, чтобы завершить установку. Сбой установки. Перезагрузите компьютер и повторите попытку. Ваш компьютер будет перезагружен, чтобы завершить установку. Вы отменили установку. Уже установлена другая версия этого приложения. Уже установлена более поздняя версия этого приложения. Установка запрещена политиками организации. Обратитесь к администратору. Не удалось установить зависимости пакета. Приложение сейчас используется другим приложением. Недопустимый параметр. Пакет не поддерживается системой. Установщик не поддерживает обновление существующего пакета. Установка завершилась сбоем из-за ошибки пользовательского установщика. Не удалось найти запись "Приложения и компоненты" для пакета. Расположение установки неприменимо. Не удалось найти расположение установки. Хэш существующего файла не совпадает. Файл не найден. Файл найден, но хэш не был проверен. Не удалось получить доступ к файлу. Файл конфигурации недопустим. Синтаксис YAML недопустим. Поле конфигурации относится к недопустимому типу. Версия конфигурации неизвестна. Произошла ошибка при применении конфигурации. Конфигурация содержит повторяющийся идентификатор. В конфигурации отсутствует зависимость. В конфигурации есть неудовлетворенная зависимость. Сбой утверждения для блока конфигурации. Конфигурация пропущена вручную. Пользователь отклонил продолжение выполнения. Этот граф зависимостей содержит цикл, который невозможно разрешить. В конфигурации есть недопустимое значение поля. В конфигурации отсутствует поле. Сбой некоторых модулей конфигурации при проверке их состояния. Состояние конфигурации не протестировано. Блок конфигурации не установлен. Не удалось найти блок конфигурации. Обнаружено несколько соответствий для единицы конфигурации; укажите модуль, чтобы выбрать правильный модуль. Сбой единицы конфигурации при попытке получить текущее состояние системы. Сбой единицы конфигурации при попытке проверить текущее состояние системы. Сбой блока конфигурации при попытке применить нужное состояние. Модуль для блока конфигурации доступен в нескольких расположениях с одинаковой версией. Не удалось загрузить модуль для единицы конфигурации. Модуль конфигурации вернул непредвиденный результат во время выполнения. Блок содержит параметр, требующий корня конфигурации. Операция не поддерживается обработчиком конфигурации. Недоступно Зависимости компонентов Windows включены Не удалось загрузить модуль для единицы конфигурации, так как для его запуска требуются права администратора. Блок содержит параметр, требующий корня конфигурации. Не удалось загрузить модуль для единицы конфигурации, так как для его запуска требуются права администратора. Возобновление выполнения ранее сохраненной команды путем передачи уникального идентификатора сохраненной команды. Используется для возобновления выполнения команды, которая может быть прервана из-за перезагрузки. Возобновление выполнения ранее сохраненной команды. Уникальный идентификатор сохраненного состояния для возобновления Возобновление состояния из другой версии клиента не поддерживается: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. Состояние возобновления не существует: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. Не найдены данные в состоянии возобновления. Эта команда не поддерживает возобновление. Разрешает перезагрузку, если применимо Запуск перезагрузки для завершения операции... Не удалось инициировать перезагрузку. Операция возобновления превышает допустимый предел {0} возобновления. Чтобы возобновить работу вручную, выполните команду "{1}". {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. Игнорировать ограничение на возобновление сохраненного состояния Схема URI не поддерживается: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. Неправильный формат URI: {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. Не удалось проанализировать {0} содержимое параметров единицы конфигурации или содержимое параметров пусто. {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. В блоке конфигурации {0} отсутствует требуемый аргумент: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. В блоке конфигурации {0} отсутствует рекомендуемый аргумент: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. Блок конфигурации WinGetSource конфликтует с известным источником: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. Блок конфигурации WinGetSource утверждает сторонний источник: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage объявляет UseLatest и Version. Идентификатор пакета: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} Единица конфигурации WinGetPackage утверждает пакет из стороннего источника. Идентификатор пакета: {0}; Источник: {1} {Locked="WinGetPackage,{0},{1}"} Пакет единиц конфигурации WinGetPackage зависит от стороннего источника, не настроенного ранее. Идентификатор пакета: {0}; Источник: {1} {Locked="WinGetPackage,{0},{1}"} Пакет единиц конфигурации WinGetPackage зависит от стороннего источника. Рекомендуется объявить зависимость в разделе uni dependsOn. Идентификатор пакета: {0}; Источник: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} Не удается проверить пакет единиц конфигурации WinGetPackage. Не удалось открыть источник. Идентификатор пакета: {0}; Источник: {1} {Locked="WinGetPackage,{0},{1}"} Не удается проверить пакет единиц конфигурации WinGetPackage. Пакет не найден. Идентификатор пакета: {0} {Locked="WinGetPackage,{0}"} Не удается проверить пакет единиц конфигурации WinGetPackage. Найдено несколько пакетов. Идентификатор пакета: {0} {Locked="WinGetPackage,{0}"} Не удается проверить пакет единиц конфигурации WinGetPackage. Версия пакета не найдена. Идентификатор пакета: {0}; Версия {1} {Locked="WinGetPackage,{0},{1}"} Пакет блока конфигурации WinGetPackage указан с определенной версией, но доступна только одна версия пакета. Идентификатор пакета:{0}; версия: {1} {Locked="WinGetPackage,{0},{1}"} Не удается проверить пакет единиц конфигурации WinGetPackage. Идентификатор пакета: {0} {Locked="WinGetPackage,{0}"} Укажите предпочтительный режим окна проверки подлинности (silent, silentPreferred или interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. Укажите учетную запись, которая будет использоваться для проверки подлинности Не удалось добавить источник. Эта winget не поддерживает метод проверки подлинности источника. Попробуйте обновиться до последней winget версии. {Locked="winget"} Источник {0} требует проверки подлинности. При необходимости может появиться запрос проверки подлинности. Сведения, прошедшие проверку подлинности, будут предоставлены источнику для авторизации доступа. {Locked="{0}"} Восстановление выбранного пакета путем поиска в списке установленных пакетов или непосредственно из манифеста. По умолчанию запрос должен без учета регистра совпадать с идентификатором, именем или моникером пакета. Другие поля можно использовать путем передачи соответствующего параметра. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Восстановление выбранного пакета Не удается найти команду восстановления для этого пакета. Обратитесь за поддержкой к издателю пакета. Используемая технология установщика не соответствует установленной версии. Команда восстановления не найдена. Используемая технология установщика не поддерживает восстановление. Операция восстановления успешно завершена. Восстановление прервано Запуск восстановления пакета... Не удалось восстановить пакет Microsoft Store. Код ошибки: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. Не удалось выполнить операцию восстановления. Операция восстановления неприменима. В настроенных источниках недоступны соответствующие версии пакетов. Текущая конфигурация системы не поддерживает восстановление этого пакета. Используемая технология установщика не поддерживает восстановление. Пакет, установленный для область не может быть восстановлен при выполнении с правами администратора. Операции восстановления с правами администратора не разрешены для пакетов, установленных в пользовательском область. Сбой восстановления с кодом выхода: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. Подключение SQLite было разорвано для предотвращения повреждения. Удалить все версии Версия для действий Установлено несколько версий этого пакета. Уточните условия поиска, передайте аргумент "--version", чтобы выбрать его, или передайте флаг "--all-versions", чтобы удалить все из них. {Locked="--version,--all-versions"} Включить Диспетчер пакетов Windows командной строки прокси-сервера Describes a Group Policy that can enable the use of the --proxy option to set a proxy Задать прокси-сервер для этого выполнения Отключить использование прокси-сервера для этого выполнения Не удается сбросить {0}. Этот параметр управляется политикой. Для получения дополнительных сведений обратитесь к системному администратору. {Locked="{0}"} The value will be replaced with the feature name Сброс параметра администратора "{0}". {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. Невозможно задать {0}. Этот параметр управляется политикой. Для получения дополнительных сведений обратитесь к системному администратору. {Locked="{0}"} The value will be replaced with the feature name Задайте для параметра администратора "{0}" значение "{1}". {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced Имя изменяемого параметра Значение, настраиваемое для параметра. Сбрасывает значение по умолчанию для параметра администратора. Сбрасывает значение по умолчанию для параметра администратора. Задает значение параметра администратора. Задает значение параметра администратора. Исключает источник из обнаружения, если не указано Исключить источник из обнаружения (true или false) Возрастные ограничения Уровень доверия источника (нет или недоверенный) Уровень доверия Не удалось получить каталог пакетов Microsoft Store. Применимый пакет Microsoft Store не найден в каталоге пакетов Microsoft Store. Не удалось получить сведения о скачивании пакета Microsoft Store. Не найден применимый Microsoft Store для скачивания. Не удалось получить лицензию пакета Microsoft Store. Применимый пакет Microsoft Store не найден. Хэш пакета Microsoft Store успешно проверен Несоответствие хэша пакета Microsoft Store Пакет Microsoft Store скачан: {0} {Locked="{0}"} Full path of the downloaded package. Не удалось скачать пакет Microsoft Store: {0} {Locked="{0}"} Package name. Скачивание пакета Microsoft Store завершено Скачивание основных пакетов из Microsoft Store... Скачивание пакетов зависимостей из Microsoft Store... Получение сведений о скачивании пакета Microsoft Store Не удалось получить сведения о скачивании пакета Microsoft Store Получение лицензии пакета Microsoft Store Лицензия пакета Microsoft Store сохранена: {0} {Locked="{0}"} License file full path. Не удалось получить лицензию пакета Microsoft Store Скачивание пакета Microsoft Store не поддерживает аргумент --rename. Пакет Microsoft Store будет использовать имена, предоставленные каталогом Microsoft Store. {Locked="--rename"} Microsoft Store загрузки пакета требуется Microsoft Entra проверки подлинности. При необходимости может появиться запрос проверки подлинности. Сведения, прошедшие проверку подлинности, будут предоставлены службы Майкрософт для авторизации доступа. Для Microsoft Store лицензирования пакетов учетная запись Microsoft Entra идентификатора должна быть членом глобального администратора, администратора пользователей или администратора лицензий. {Locked="Global Administrator,User Administrator,License Administrator"} Пропускает получение автономной лицензии пакета Microsoft Store Выбор целевой платформы Добавление файла конфигурации: {0} {Locked="{0}"} Экспорт успешно выполнен Получение параметров конфигурации... Необходимо указать хотя бы --packageId и (или) --module с параметром --resource. Либо используйте --all для экспорта всех конфигураций пакетов. {Locked="--packageId,--module, --resource, --all"} Аргументы --packageId, --module и --resource нельзя использовать с параметром --all. {Locked="--packageId,--module, --resource, --all"} Экспорт ресурсов конфигурации в файл конфигурации. При использовании с --all выполняется экспорт всех конфигураций пакетов. При использовании с --packageId выполняется экспорт ресурса WinGetPackage указанного идентификатора пакета. При использовании с --module и --resource обеспечивается получение параметров ресурса и его экспорт в файл конфигурации. Если выходной файл конфигурации уже существует, добавляет экспортированные ресурсы конфигурации. {Locked="WinGetPackage,--packageId,--module, --resource"} Экспорт ресурсов конфигурации в файл конфигурации. Модуль ресурса для экспорта. Идентификатор пакета для экспорта. Ресурс конфигурации для экспорта. Экспортирует все конфигурации пакетов. Единице конфигурации не удалось получить свойства. Не удалось экспортировать конфигурацию. {0}: настройка {Locked="{0}"} Установить {0} {Locked="{0}"} Некоторые данные в файле конфигурации были усечены для этого вывода; проверьте содержимое файла на наличие полного содержимого. <это значение было усечено; проверьте содержимое файла на наличие полного текста> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. Отображение сведений о конфигурациях высокого уровня, которые были применены к системе. Эти данные можно использовать с командами `configure` для получения дополнительных сведений. {Locked="`configure`"} Отображает журнал конфигурации В журнале нет конфигураций. Выбор элементов из журнала Не удалось найти одну конфигурацию, соответствующую предоставленным данным. Укажите полное имя или часть идентификатора, однозначно соответствующую требуемой конфигурации. Удалить элемент из журнала Первое применение Column header for date values indicating when a configuration was first applied to the system. Идентификатор Имя Источник Путь Не удалось найти указанную конфигурацию. Завершено The state of processing an item. Выполняется The state of processing an item. Ожидание The state of processing an item. Неизвестно The state of processing an item. Отслеживайте состояние конфигурации. As in "to monitor the status of a configuration being applied". Завершено The state of processing an item. Выполняется The state of processing an item. Ожидание The state of processing an item. Пропущено The state of processing an item. Неизвестно The state of processing an item. Применение начато When the configuration application started. Применение завершено When the configuration application ended. Результат Подробности Область/край The state of processing an item. Единица Пакет несовместим с текущей версией Windows или платформой. По возможности не отображать сведения о начальной конфигурации Можно Microsoft Store несколько пакетов, нацеленных на разные платформы и архитектуры. --platform, --architecture можно использовать для фильтрации пакетов. {Locked="--platform--architecture"} Не удалось получить Microsoft Store пакета. Учетная Microsoft Entra идентификатора не является членом группы "Глобальный администратор", "Администратор пользователей" или "Администратор лицензий". Используйте --skip-license, чтобы пропустить получение Microsoft Store пакета. {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} Пакет Microsoft Store не поддерживает скачивание. Пакет Microsoft Store не поддерживает скачивание. Не удалось получить Microsoft Store пакета. Учетная Microsoft Entra идентификатора учетной записи не имеет необходимых привилегий. Параметр нельзя передать через границу целостности. Скачан установщик нулевого байта. Убедитесь, что ваше сетевое подключение работает правильно. Скачан установщик нулевого байта. Убедитесь, что ваше сетевое подключение работает правильно. Управление шрифтами Управляйте шрифтами с помощью подкоманд. Шрифты можно устанавливать, обновлять и удалять, как пакеты. Семейство Лица "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' Фильтровать результаты по фамилии Лицо "Face" represents the typeface of a font family such as 'Bold' or 'Italic' Пути Ни один установленный шрифт не соответствует заданному критерию. Получить список установленных шрифтов Список всех установленных шрифтов, всех файлов шрифтов или полных сведений о конкретном шрифте. Версия Модули конфигурации PowerShell Modules that are used for the Configuration feature Установщику пакета требуется проверка подлинности. При необходимости может появиться запрос проверки подлинности. Сведения, прошедшие проверку подлинности, будут предоставлены с URL-адресом скачивания установщика. Не удалось скачать установщик. Эта winget не поддерживает метод проверки подлинности скачивания установщиком. Попробуйте обновиться до последней winget версии. {Locked="winget"} Не удалось скачать установщик. Проверка подлинности не пройдена. Укажите путь к процессору конфигурации Команды ресурсов DSC v3 DSC stands for "Desired State Configuration". It should already have a locked translation. Вложенные команды реализуют Desired State Configuration (DSC) версии 3 для настройки winget и пакетов. Получить состояние ресурса Задать состояние ресурса Опишите необходимые изменения состояния Проверьте состояние ресурса Удалить состояние ресурса Получить все экземпляры состояния Утвердить контент группы Разрешить внешнее состояние Запустить адаптер Получить схему ресурсов Получить манифест ресурса Управление состоянием пакета Управление пакетами через winget. Указывает, существует экземпляр или должен существовать. Указывает, находится ли экземпляр в желаемом состоянии. Идентификатор пакета. Источник пакета. Версия пакета. Метод сопоставления идентификатора с пакетом. Укажите, что следует установить последнюю доступную версию пакета. Режим установки, который следует использовать при необходимости. Целевая область пакета. Указывает, следует ли принимать соглашения для источников и пакетов. Управление файлом настроек пользователя Управление настройками пользователя winget. Содержимое файла настроек JSON. Действие, используемое для применения настроек. Значение регистрируется для корреляции Управление конфигурацией источника Управляйте источниками winget. Имя, используемое для источника. Аргумент в пользу источника. Тип источника. Уровень доверия источника. Включается ли источник, если в вызовах источник не указан. Применение единицы конфигурации... Экспорт единицы конфигурации... Получение процессоров единиц конфигурации... Обеспечьте необходимый модуль для экспорта [{0}] {Locked="{0}"} Не удалось проверить или получить требуемый модуль. Связанные параметры не будут экспортированы. Экспортировать [{0}] {Locked="{0}"} Не удалось экспортировать ресурс. Не удалось получить модульные процессоры. Отдельные параметры пакета не будут экспортированы. Директория, в которую будут записаны результаты Desired State Configuration пакет не найден в системе. Установка пакета... Не удалось установить Desired State Configuration пакета. Установите пакет вручную или укажите путь для dsc.exe с помощью --processor-path аргумента. {Locked="dsc.exe","--processor-path"} Объект, содержащий настройки администратора и их значения. Управление настройками администратора Управление параметрами администратора winget. Сбрасывает все настройки администратора Все настройки администратора сброшены. Сведения об MCP MCP stands for Model Context Protocol and should probably remain as-is Сведения MCP (протокол контекста модели) для Диспетчер пакетов Windows. Чтобы вручную настроить Диспетчер пакетов Windows MCP-сервера с клиентом MCP, используйте следующий фрагмент JSON в `servers` объекта: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. Включить сервер MCP Диспетчера пакетов Windows Целевая версия ОС Не удалось установить один или несколько шрифтов. Файл шрифта не поддерживается и не может быть установлен. Один или несколько шрифтов в пакете шрифтов не поддерживаются и не могут быть установлены. Сбой установки шрифта; выполняется очистка. Шрифт уже установлен. Идентификатор пакета Поддержка WinGet Название Файл шрифта не найден. Не удалось удалить шрифт. Возможно, шрифт находится в неудовлетворительном состоянии. Попробуйте удалить после перезапуска. Сбой проверки шрифта. Сбой отката шрифта. Возможно, шрифт поврежден. Попробуйте удалить после перезагрузки. Показать подробную информацию о файле шрифта. Сбой проверки пакета шрифтов. Не удалось выполнить откат для неудачной установки шрифта. Для успешного удаления шрифта может потребоваться перезагрузка. Не удалось удалить пакет шрифтов. Часто это связано с тем, что шрифты используются системой или приложением. Удаление может быть успешным после перезагрузки компьютера. ОК "OK" means the font is in a good healthy state Повреждено "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. Состояние Неизвестно Пакет шрифтов уже установлен. Показать подробную информацию о пакетах Providing this argument causes the CLI to output additional details about installed application packages. Канал: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). Локальный идентификатор: Precedes a value that is the unique identifier for the installed package on the local system. Имя семейства пакетов Precedes a value that is the APPX/MSIX package family name of the installed package. Код продукта: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. Код обновления: Precedes a value that is the MSI Upgrade Code for the installed package. Установленная область: Precedes a value that is the scope of the installation of the package (ex. user, machine). Установленная архитектура: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). Установленный языковой стандарт: Precedes a value that is the locale of the installed package. Установленное расположение: Precedes a value that is the directory path to the installed package. Источник пакета: Precedes a value that names the package source where the installed package originated from. Доступные обновления: Precedes a list of package upgrades available for the installed package. Категория установщика: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). Старое значение Column title for listing edit changes. Новое значение Column title for listing the new value. ================================================ FILE: Localization/Resources/zh-CN/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/zh-CN/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 邻近别名不是标记: "{0}" {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). 未找到邻近标记别名: "{0}" {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). 以下参数可用: Message displayed to inform the user about the available command line arguments. 以下命令别名可用: Message displayed to inform the user about the available command line alias arguments. 下列命令有效: Title displayed to inform the user about the available commands. 可用 As in "a new version is available to upgrade to". 下列选项可用: Message displayed to inform the user about the available options. 以下子命令可用: Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} 升级可用。 {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. 使用指定的频道;默认为普通受众 命令 Label displayed for a command to give the software. 按命令筛选结果 Description message displayed to inform the user about filtering the search results by a package command. 要补全的完整命令行 需要具有管理员权限才能执行此命令。 此命令可用于请求上下文敏感命令行补全。已传入要补全的命令行、光标位置和单词。输出是基于输入的一组可能值,其中每行一个可能的值。 启用上下文相关的命令行补全 显示介于 1 和 1000 ()之间的指定数量的结果 完成 Label displayed when an operation completes or is done executing. 使用精确匹配查找程序包 Description message displayed to inform the user about finding an application package using an exact matching criteria. 用于演示目的的实验性参数 此命令是有关如何实施实验性功能的示例。若要启用,请转到“winget settings”并启用 experimentalCmd 或 experimentalArg 功能。 {Locked="winget settings"} 实验性功能示例 在未预期的情况下找到位置参数: "{0}" {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. 此功能是一项正在进行的工作,将来可能会发生显著更改或完全删除。若要启用它,请编辑设置 ('winget settings')以包括实验性功能: '{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. 显示实验性功能的状态。可以通过'winget settings'启用实验性功能。 {Locked="winget settings"} 显示实验性功能的状态 已禁用 已启用 功能 链接 以下实验性功能正在进行中。 它们可以通过设置文件“winget settings”进行配置。 {Locked="winget settings"} 属性 状态 进行哈希处理的文件 标记参数不得含有邻近值: "{0}" {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. 计算本地文件的哈希值,适用于清单中的条目。此外,它还可以计算 MSIX 程序包的签名文件的哈希,以启用流式安装。 哈希安装程序的帮助程序 显示选定命令的帮助信息 如需特定命令的更多详细信息,请向其传递帮助参数。 可在此找到更多帮助: "{0}" {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. 按 id 筛选结果 禁止显示警告输出 此应用程序由其所有者授权给你。 Microsoft 对第三方程序包概不负责,也不向第三方程序包授予任何许可证。 此程序包是通过 Microsoft Store 提供的。winget 可能需要代表当前用户从 Microsoft Store 获取该程序包。 {Locked="winget"} 安装选定的程序包(通过搜索配置的源或直接从清单中找到)。默认情况下,查询必须以不区分大小写的方式匹配程序包的 id、name 或 moniker。可以通过传递相应的选项来使用其他字段。默认情况下,安装命令将检查包安装状态,并尝试执行升级(如果适用)。使用 --force 重写以执行直接安装。 {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 安装给定的程序包 安装程序哈希不匹配;以管理员身份运行时不能覆盖此内容 安装程序哈希不匹配;由于 --ignore-security-hash 而继续进行 {Locked="--ignore-security-hash"} 安装程序哈希不匹配;若要重写此检查,请使用 --ignore-security-hash {Locked="--ignore-security-hash"} 已成功验证安装程序哈希 已成功安装 正在启动程序包安装... 忽略安装程序哈希检查失败 忽略在从本地清单安装存档类型包时执行的恶意软件扫描 请求交互式安装;可能需要用户输入 无法识别当前命令的参数别名: "{0}" {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). 无效参数说明符: "{0}" {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). 当前命令无法识别参数名称: "{0}" {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Winget 目录 Header for a table detailing the directories Winget uses for key operations like logging and portable installs 要使用的区域设置(BCP47 格式) {Locked="BCP47"} 许可协议 链接 Links to different webpages list 命令显示系统上安装的程序包,以及是否有可用的更新。可以提供其他选项来筛选输出,这与 search 命令非常相似。 {Locked="list","search"} 显示已安装的程序包 要安装到的位置(如支持) 日志位置(如果支持) 版权所有 (C) Microsoft Corporation。保留所有权利。 主页 The primary webpage for the software 程序包清单的路径 清单验证失败。 清单验证成功。 清单验证成功,但出现警告。 所需参数值,但未找到: "{0}" {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. 按名字对象筛选结果 输入文件将被视为 msix;如果签名,将提供签名哈希 无法计算 MSIX 签名哈希。 无法安装或更新 Microsoft Store 程序包,因为策略阻止了特定应用 无法安装或更新 Microsoft Store 程序包。错误代码: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. 无法安装或更新 Microsoft Store 程序包,因为策略阻止了 Microsoft Store 客户端 验证/请求程序包获取成功... 找到多个与输入标准匹配的已安装程序包。请修改输入。 找到多个与输入条件匹配的程序包。请修改输入。 按名称筛选结果 找不到适用的安装程序;有关详细信息,请参阅日志。 当前没有实验性功能可用。 找不到与输入条件匹配的已安装程序包。 找不到与输入条件匹配的程序包。 禁用 VirtualTerminal 显示 {Locked="VirtualTerminal"} 打开默认日志位置 选项 Options to change how a command works 覆盖待传递的参数至安装程序 软件包: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. 抱歉,我们忘记了... 光标在命令行中的位置 隐私声明 用于搜索程序包的查询 彩虹色进度显示 未提供所需参数: "{0}" {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. 默认颜色进度显示 从配置的源搜索程序包。 查找并显示程序包的基本信息 ID Abbreviation of Identifier. 匹配 名称 由于结果限制而截断了其他条目 版本 发现以下故障以验证设置: 在默认 json 文本编辑器中打开设置。如果未配置任何编辑器,请在记事本中打开设置。有关可用设置,请参阅 https://aka.ms/winget-settings。此命令还可用于通过提供 --enable 或 --disable 参数来设置管理员设置 {Locked="--enable"} {Locked="--disable"} 打开设置或设置管理员设置 加载设置时出现意外错误。请运行'settings'命令来验证你的设置。 {Locked="'settings'"} 频道 显示有关特定程序包的信息。默认情况下,查询必须以不区分大小写的方式匹配程序包的 ID、名称或名字对象。可通过传递相应的选项来使用其他字段。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 显示包的相关信息 版本 请求无提示安装 只能在单个 - 后出现单个字符别名: "{0}" {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. 具有给定名称的源已存在,并且引用了其他位置: 具有不同名称的源已引用此位置: 具有给定名称的源已存在,并且引用了相同位置: 正在添加源: 添加新的来源。源提供了用于发现和安装程序包的数据。仅当将新来源视为安全位置时,才可添加。 添加新来源 指定给源的参数 使用指定的源查找程序包 使用子命令管理源。源提供了用于发现和安装程序包的数据。仅当将新来源视为安全位置时,才可添加。 管理程序包的来源 编辑现有源的属性。源提供用于发现和安装包的数据。 编辑源的属性 正在编辑源: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. 名为 '{0}' 的源已处于所需状态。 {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. 参数 Value given to source. 列出所有当前来源,或列出特定来源的完整详细信息。 列出当前源 数据 Data stored by the source. 字段 The name of a piece of information about a source. 名称 The name of the source. 标识符 The source's unique identifier. 找不到具有以下名称的源: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. 未配置源。 类型 The kind of source. 已更新 The last time the source was updated. 从不 The source has never been updated. The value of information about a source. 源名称 打开源时失败;如果问题仍然存在,请尝试"source reset"命令。 {Locked="source reset"} 打开预定义源失败; 请向 winget 维护人员报告。 {Locked="winget"} 正在删除所有源... 删除指定的源。 删除当前源 正在删除源: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. 正在重置所有源... 此命令将丢弃现有源,从而有可能留下任何本地数据。没有任何参数,它将删除所有源并添加默认值。如果提供了已命名源,则只会删除该源。 重置源 强制重置源 如果指定了--force 选项,则将重置以下源: {Locked="--force"} 正在重置源: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. 源类型 正在更新所有源... 更新所有源,或仅指定源。 更新当前源 正在更新源: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. 按标签筛选 感谢你使用 winget 第三方声明 WinGet 命令行实用工具可从命令行安装应用程序和其他程序包。 显示工具的常规信息 显示工具的版本 提供的参数超过允许的参数: "{0}" {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. 提供了多个执行行为参数: "{0}" {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). 执行此命令时发生意外错误: 升级期间卸载以前版本的程序包 无法识别的命令: "{0}" {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. 如果可用,将所有已安装的程序包更新为最新版本 找不到适用的升级。 较新的包版本在配置的源中可用,但不适用于你的系统或要求。 找不到可用的升级。 配置的源中没有可用的较新的包版本。 升级通过搜索已安装的包列表或直接从清单中找到的所选包。默认情况下,查询必须以不区分大小写方式匹配包的 ID、名称或名字对象。可通过传递相应的选项来使用其他字段。如果未提供参数,则显示具有可用升级的包 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 显示并执行可用升级 使用情况: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. 使用一组严格的准则验证清单。这是为了能够在提交到存储库之前检查清单。 验证清单文件 待验证的程序清单路径 启用 WinGet 的详细日志记录 请验证输入文件是否为有效的、已签名的 MSIX。 使用指定的版本;默认为最新版本 显示程序包的可用版本 请求补全前提供的值 找不到匹配的版本: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. 没有与给定值匹配的源: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. 配置的源为: 未定义源; 使用“source add”添加一个或使用“source reset”重置为默认值 {Locked="source add","source reset"} 已找到 路径是一个目录: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. 文件不存在: {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. 同时提供本地清单和搜索查询参数 日志 Label displayed for diagnostic files containing information about the application use. 安装程序已被策略阻止 安装程序未通过安全性检查 防病毒产品报告安装程序受感染 尝试更新源失败: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. 通过搜索已安装的程序包列表或直接从清单中卸载选择的程序包。默认情况下,查询必须 insensitively 匹配程序包的 id、名称或名字对象。可通过传递适当的选项来使用其他字段。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 卸载给定的程序包 正在启动程序包卸载... 已成功卸载 winget 找不到此程序包的卸载命令。请与程序包发布者联系以获取支持。 {Locked="winget"} 卸载已放弃 卸载失败,退出代码为: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. 导出已安装程序包的列表 安装文件中列出的所有程序包。 安装文件中的所有程序包 将在其中写入结果的文件 描述要安装的程序包的文件 从指定源导出程序包 将已安装程序包的列表写入文件。然后可以通过 import 命令安装这些包。 {Locked="import"} 无法安装一个或多个导入的程序包 未安装导入所需的源: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. 无法从任何源获得已安装的程序包: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. 无法从任何源获得已安装的程序包版本: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. 在导入文件中找不到程序包 JSON 文件无效 已安装程序包: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. 忽略不可用的程序包 在导出文件中包括包版本 忽略导入文件中的程序包版本 路径不存在: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. JSON 文件未指定可识别的架构。 选择安装范围 (user 或 machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. 为 '{0}' 参数提供的值无效;有效值为: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. 组策略禁用此操作: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. 启用其他 Windows 应用安装程序源 启用 Windows 应用安装程序允许的源 启用 Windows 应用安装程序默认源 启用 Windows 应用安装程序实验性功能 启用 Windows 应用安装程序 Microsoft Store 源 启用 Windows App 安装程序字体源 启用 Windows 程序包管理器设置 启用 Windows 程序包管理器 启用 Windows 程序包管理器命令行接口 设置 Windows 程序包管理器源自动更新间隔(分钟) 组策略 Header for a table listing active Group Policies 启用 Windows 应用安装程序本地清单文件 启用 Windows 应用安装程序 Microsoft Store 源固定证书绕过 启用 Windows 应用安装程序 本地存档恶意软件扫描替代 球场: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. 字段格式无效。 字段值无效。 组策略中的设置无效。 已从备份文件加载设置。 分析文件时出错: 值: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. 以下实验功能正在进行中。 由于组策略,配置已被禁用。 安装程序哈希不匹配。 启用 Windows 应用安装程序哈希替代 将当前源作为组策略的 JSON 导出。 导出当前源 其他源 An additional source required by policy. 允许的源 A source that the user is allowed to add. 为 '{0}' 参数提供的值无效 {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. 已取消 外部 在此导入中找到的包具有以下依赖项: Import command sentence showed before reporting dependencies 此包需要以下依赖项: Message shown before reporting dependencies 正在安装依赖项: 找不到依赖项源 包搜索生成多个结果: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. 找不到包的最新版本: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. 找不到安装程序: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. 包所需的最低版本不可用: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. 没有匹配项 When package search yields no matches 具有循环 Dependency graph has loop 找不到适用于清单的安装程序: {0} 版本 {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. 处理包依赖项时出错。是否希望继续安装? Prompt message shown when dependencies processing yields errors. 处理包依赖项时出错。正在退出... 程序包 此包具有可能不再需要的依赖项: Uninstall command sentence showed before reporting dependencies 清单具有以下未验证的依赖项;确保它们有效: Validate command sentence showed before reporting dependencies Windows 功能 Windows 库 使用指定源查找包依赖项 For getting package type dependencies when installing from a local manifest Windows 应用商店条款 接受包的所有许可协议 导出的包需要安装许可协议: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. 发行商要求你在安装前查看上述信息并接受协议。 是否同意上述条款? 未同意包协议。操作已取消。 协议: 作者: 描述: 安装: 安装程序区域设置: 应用商店产品 ID: 安装程序 SHA256: 安装程序类型: 安装程序 URL: 许可证: 许可证 URL: 绰号: 主页: 发布者: 标记: 版本: 依赖项: Windows 功能: Windows 库: 包依赖项: 外部依赖项: 无法打开添加的源。 在源操作期间接受所有源协议 “{0}”源要求在使用前查看以下协议。 {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. 是否同意所有源协议条款? 未同意一个或多个源协议。操作已取消。请接受源协议或删除相应的源。 源要求将当前计算机的 2 个字母的地理区域发送到后端服务才能正常工作,(例如"US")。 已成功安装。重启应用程序以完成升级。 可选的 Windows-Package-Manager REST 源 HTTP 标头 忽略可选标头,因为它不适用于此源。 在未指定源的情况下,可选标头不适用: "{0}" {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. 发布日期: 支持脱机分发: 发布服务器 URL: 购买 URL: 发布服务器支持 URL: 隐私 URL: 版权所有: 版权 URL: 发行说明: 发行说明 URL: 搜索源时失败;结果将不包括在内: {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. 搜索源时失败: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. 此功能需要由管理员启用。若要启用它,请以管理员身份运行 “winget settings --enable {0}”。 {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. 启用特定的管理员设置 禁用特定管理员设置 已启用管理员设置 '{0}'。 {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. 已禁用管理员设置 '{0}'。 {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. 管理员设置 Header for a table displaying admin settings. 应用程序当前正在运行。退出应用程序,然后重试。 安装程序修改的文件当前正由其他应用程序使用。退出应用程序,然后重试。 另一个安装已在进行中。请稍后再试。 正在使用一个或多个文件。退出应用程序,然后重试。 系统中缺少此包的依赖项。 你的电脑上没有更多空间。腾出空间,然后重试。 内存不足,无法安装。关闭其他应用程序,然后重试。 其中一个安装参数无效。包安装日志可能包含其他详细信息。 此应用程序需要 Internet 连接。请连接到网络,然后重试。 此应用程序在安装过程中遇到错误。请联系支持人员。 重启电脑以完成安装。 你的电脑将重启以完成安装。 安装失败。请重新启动电脑,然后重试。 你已取消安装。 已安装此应用程序的另一个版本。 已安装此应用程序的更高版本。 组织策略正在阻止安装。请与管理员联系。 当前系统配置不支持安装此包。 安装失败,出现自定义安装程序错误。联系包支持。 已放弃安装 安装程序失败,退出代码为: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. 安装程序日志在以下位置可用: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. 在工作源中找到以下包。 若要继续操作,请使用--source选项指定其中一个。 {Locked="--source"} "working sources" as in "sources that are working correctly" 在工作源中找不到任何包。 "working sources" as in "sources that are working correctly" 这是 Windows 程序包管理器的稳定版本。如果要尝试实验性功能,请安装预发布版本。请前往 https://github.com/microsoft/winget-cli 查看 GitHub 说明。 {Locked="https://github.com/microsoft/winget-cli"} Windows 程序包管理器 v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. 忽略导入文件中的程序包版本 请求的结果数必须介于 1 和 1000 之间。 除默认值外,参数传递到安装程序 找到了较新的版本,但安装技术与当前安装的版本不同。请卸载包并安装较新的版本。 指定的较新版本的安装技术与当前安装的版本不同。请卸载包并安装较新的版本。 Windows 程序包管理器(预览) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. 选择体系结构 即使无法确定其当前版本,也可升级包 列出程序包,即使无法确定其当前版本。只能与 --upgrade-available 参数一起使用 {Locked="--upgrade-available"} 无法确定此包的版本号。若要继续升级,请将参数--include-unknown添加到上一命令。 {Locked="--include-unknown"} {0} 程序包的版本号无法确定。使用 --include-unknown 查看所有结果。 {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} 程序包已固定,需要显式升级。 {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. 提供的参数只能与查询一起使用。 系统体系结构: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). 保留由包创建的所有文件和目录(可移植) 删除包目录中的所有文件和目录(可移植) 按 Enter 键继续... 要重命名可执行文件的值(可移植) 提示用户在退出前按任意键 安装程序将请求以管理员身份运行。期待提示。 无法从管理员上下文运行安装程序。 已修改路径环境变量;重启 shell 以使用新值。 使用产品代码进行筛选 具有相同名称但来自不同源的可移植包已存在;由于 --force 而继续 {Locked="--force"} 卷不支持重新分析点 指定的文件名不是有效的文件名 正在覆盖现有文件: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. 未提供包选择参数;有关查找包的详细信息,请参阅帮助。 添加了命令行别名: 可移植链接目录(计算机) 可移植链接目录(用户) 可移植包根目录(用户) 可移植包根目录 可移植包根目录 (x86) 可移植安装失败;正在清理... 已修改可移植程序包;由于 --force 而继续进行 {Locked="--force"} 无法删除可移植程序包,因为它已被修改;要替代此检查,请使用 --force {Locked="--force"} 文件保留在安装目录中: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. 正在清除安装目录... 无法清除安装目录,因为它不是由 WinGet 创建的 相关链接 文档: 注意: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. 安装说明: 此程序包不支持提供的参数 未能提取存档内容 嵌套安装程序文件不存在。确保嵌套安装程序的指定相对路径匹配: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. 嵌套安装程序的相对文件路径无效;路径指向安装目录之外的位置 没有为此程序包指定嵌套安装程序 只能为存档安装程序指定一个嵌套安装程序,除非它是可移植的或字体嵌套安装程序 提供的命令行参数不兼容 仅列出具有可用升级的包 以下程序包有可用的升级,但需要显式目标才能进行升级: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly 正在下载 Label displayed while downloading an application installer. 不支持嵌套安装程序类型 已知此安装程序重启终端或 shell 此程序包需要安装位置 已知以下安装程序重启终端或 shell: 以下安装程序需要安装位置: 指定安装根目录: 是否继续? 的协议 This will be followed by a package name, and then a list of license agreements 此程序包需要安装位置,但未提供 禁用交互式提示 Description for a command line argument, shown next to it in the help 找到已安装的现有包。正在尝试升级已安装的包... 直接运行命令并继续处理与安全无关的问题 Description for a command line argument, shown next to it in the help 已存在来自其他源的可移植包 已成功提取存档 正在提取存档... 存档扫描检测到恶意软件。若要替代此检查,请使用 --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} 存档扫描检测到恶意软件。由于 --ignore-local-archive-malware-scan 而继续 {Locked="--ignore-local-archive-malware-scan"} 如果已安装的版本已存在,则跳过升级 已安装包版本。已取消安装。 无法启用{0}。此设置由策略控制。有关详细信息,请与系统管理员联系。 {Locked="{0}"} The value will be replaced with the feature name 无法禁用{0}。此设置由策略控制。有关详细信息,请与系统管理员联系。 {Locked="{0}"} The value will be replaced with the feature name 添加新的包钉。包钉可以限制 Windows 程序包管理器将包升级到特定范围的版本,也可以阻止它对包进行任何升级。被固定的包仍然可以自行升级,并在 Windows 程序包管理器之外进行升级。默认情况下,可通过在 "upgrade" 命令中显式提及被固定的包或将 "--include-pinned" 标志添加到 "winget upgrade --all" 来升级被固定的包。 {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} 添加新的包钉 使用子命令管理包钉。包钉可以限制 Windows 程序包管理器将包升级到特定范围的版本,也可以阻止它对包进行任何升级。被固定的包仍然可以自行升级,并在 Windows 程序包管理器之外进行升级。 管理包钉 列出当前所有固定或特定 PIN 的完整详细信息。 列出当前包钉 删除特定包钉。 删除包钉 重置所有现有包钉。 重置包钉 要将包钉到的版本。通配符 "*" 可用作最后一个版本部件 阻止升级,直到移除包钉,阻止替代参数 固定特定的已安装版本 已安装 Value used in a table to indicate that a package comes from the list of packages installed in the machine 将设置导出为 JSON 导出设置 用户设置 Label displayed for the file containing the user settings. 无法加载设置文件。正在使用默认值。 选择已安装的程序包范围筛选器 (user 或 machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. 已成功添加包钉 包{0}已存在 PIN {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. 程序包 {0} 已存在包钉。由于 --force 参数而覆盖。 {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. 包{0}已存在 pin。使用--force参数覆盖它。 {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. 正在重置所有当前包钉。 使用--force参数重置所有固定。将删除以下 PIN: {Locked="--force"} 包钉类型 程序包 {0} 无包钉 {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. 没有已配置包钉。 Shown when listing or modifying existing pins if there are none. 已成功重置包钉 Shown after resetting (deleting) all the pins 无法打开包钉数据库。 Error message for when we cannot open the database containing package pins. 提供的参数只能用于单个程序包 已提供多个互相排斥的参数: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together 参数 {0} 只能与 {1} 一起使用 {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" 已禁用 As in enabled/disabled 已启用 As in enabled/disabled 状态 Header for a table listing the state (enabled/disabled) of Group Policies and Settings 用于搜索程序包的查询 未找到程序包: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. 已找到多个程序包: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. 搜索失败: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} 程序包拥有需要在升级前移除的包钉 {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade 无法使用 winget 升级包。请使用发布者提供的方法升级此包。 即使程序包拥有非阻止性包钉,也要升级程序包 列出程序包,即使它们具有阻止升级的引脚。只能与 --upgrade-available 参数一起使用 {Locked="--upgrade-available"} 已成功移除包钉 已找到较新版本,但程序包拥有阻止对它进行升级的包钉。 程序包已固定,无法进行升级。使用 'winget pin' 命令查看和编辑包钉。可以使用 --include-pinned 参数绕过一些包钉类型。 {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} 程序包拥有阻止升级的包钉。使用 'winget pin' 命令查看和编辑包钉。使用 --include-pinned 参数可能显示更多结果。 {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages 确保系统是否与提供配置所述的所需状态匹配。可以下载/执行处理器以达到所需状态。在应用配置和处理器之前,应检查它们以确保可信。 将系统配置为所需状态 显示提供的配置的详细信息。默认情况下,不会修改系统,但某些选项将导致下载和/或加载文件。 显示配置的详细信息 检查系统是否与提供配置所述的所需状态匹配。可以下载/执行处理器以测试所需状态。在执行配置和处理器之前,应检查它们以确保可信。 根据所需状态检查系统 验证配置文件是否正确。 验证配置文件 配置文件中'{0}'的字段类型错误。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. 配置文件的路径 配置文件无效。 配置文件版本 {0} 未知。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. 接受配置警告,阻止交互式提示 应用 Indicates that this item is used to write state 声明 Indicates that this item is used to check/assert the state rather than write to it 依赖项:{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. 某些配置未成功应用。 无法获取有关配置的详细信息。 通知 Indicates that this item is used to retrieve values for future use rather than writing them 本地 Used to indicate that the item is present on the device. 模块:{0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. 模块:{0} 由 {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. 设置: Label for the values that are used as inputs for this item when applying state 已成功应用配置。 已成功应用单元。 正在将另一个配置应用到系统。此配置将尽快继续... 你有了解要执行配置设置的责任。Microsoft 不对你编写或导入的配置文件负责。此配置可能会更改 Windows 中的设置、安装软件、更改软件设置(包括安全设置),并代表你接受第三方程序包和服务的用户协议。运行此配置文件即表示你确认理解并同意这些资源和设置。安装的任何应用程序均由其所有者授权给你。Microsoft 不对第三方软件包或服务负责,也不向其授予任何许可证。 Legal approved. Do not change without approval. 是否已审阅配置,是否要继续将其应用到系统? PM approved. 配置为空。 未找到功能 [{0}]。 {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. 无法启用 Windows 功能依赖项。若要继续安装,请使用'--force'。 {Locked="--force"} 需要重新启动才能完全启用 Windows 功能;重写此检查使用 “--force”。 "Windows Feature(s)" is the name of the options Windows features setting. 无法启用 Windows 功能依赖项;由于 --force 而继续 "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} 正在启用 [{0}]... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. 未能启用 [{0}] 功能: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. 需要重新启动才能完全启用 Windows 功能;由于 --force 而继续 "Windows Feature(s)" is the name of the options Windows features setting. 正在等待另一个安装/卸载完成... 固定版本 Table header for the version to which a package is pinned; meaning it should not update from that version. <查看日志文件以了解更多详细信息> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. 检索配置详细信息 正在初始化配置系统 读取配置文件 系统未处于配置声明的所需状态。 由于未知原因,此配置单元失败: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 配置单元因配置而失败: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 尝试获取当前系统状态时配置单元失败。 尝试应用所需状态时配置单元失败。 尝试测试当前系统状态时配置单元失败。 由于内部错误,配置单元失败: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 配置单元失败,因为前提条件无效: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 配置单元因系统状态而失败: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 尝试运行配置单元时失败: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 配置多次包含标识符 “{0}”。 {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. 在配置中找不到依赖项 “{0}”。 {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. 已手动跳过此配置单元。 配置单元的模块在具有相同版本的多个位置可用。 加载配置单元的模块失败。 已找到配置单元的多个匹配项;指定模块以选择正确的模块。 未找到配置单元。 配置单元未按预期位于模块中。 此配置单元未运行,因为依赖项失败或未运行。 此配置单元未运行,因为声明失败或为 False。 配置单元在执行过程中返回了意外结果。 由于未知原因,未运行此配置单元: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 字段“{0}”的值无效: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. 字段“{0}”缺失或为空。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. 请参阅文件中的行{0}、列{1}。 {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. 正在取消操作 安装 AppInstaller 的存根包 安装 AppInstaller 的完整包 请启用扩展功能。需要应用商店访问权限。 选项“--enable”和“--disable”不能与其他参数一起使用。 {Locked="--enable", "--disable"} 正在启用扩展功能。需要应用商店访问权限。 未启用扩展功能。运行 `winget configure --enable` 来启用它们。 {Locked="winget configure --enable"} 扩展功能已启用。 禁用扩展功能。需要存储权限。 正在禁用扩展功能。需要应用商店访问权限。 扩展功能已禁用。 跳过处理包依赖项和 Windows 功能 已跳过依赖项。 无法刷新进程的 PATH 变量。依赖于对 PATH 变量的更改的后续安装可能会失败。 {Locked="PATH"} 某些配置单元在测试其状态时失败。 系统处于描述的配置状态。 未测试配置状态。 系统未处于描述的配置状态。 意外的测试结果: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). 是否已审阅配置,是否要针对系统继续验证? 配置文件不是有效的 YAML 文件。 {Locked="YAML"} YAML is a file format name. 此配置单元是依赖项周期的一部分。 验证未发现任何问题。 配置单元仅作为预发行版提供,但未在配置中以这种方式标记。将“allowPrerelease: true”添加到“directives”。 {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. 已在本地找到配置单元,但在任何配置的目录中都找不到该配置单元。在应用配置之前,请确保它存在于任何系统上。 配置单元不可公开;确保将使用此配置的任何人都有权访问它。 未提供模块。指定模块可提高性能并防止将来发生名称冲突。 从所选包下载安装程序,通过搜索已配置的源找到或直接从清单中找到。默认情况下,查询必须以不区分大小写方式匹配包的 ID、名称或名字对象。可通过传递相应的选项来使用其他字段。默认情况下,下载命令会将相应的安装程序下载到用户的“下载”文件夹。 从给定的程序包下载安装程序 安装程序下载到的目录 正在下载依赖项: 已下载安装程序: {0} {Locked="{0}"} Full path of the downloaded installer. 选择安装程序类型 安装程序下载 禁止下载安装程序以供以后脱机安装。 “--module-path allusers”需要管理员权限才能执行。 {Locked="--module-path allusers"} 指定本地计算机上用于存储模块的位置。默认为 %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} `--module-path` 值必须为 `currentuser`、`allusers`、`default` 或绝对路径。 {Locked="{--module-path}, {currentuser}, {allusers}, {default}} 启用 Windows 程序包管理器配置 检索有关错误的信息。给定一个数字,输出将包含有关错误的详细信息,如果是 winget 特定的错误,则包括符号名称。给定一个字符串,将搜索 winget 特定的错误以查找此值。 获取错误相关信息 要在错误信息中搜索的值 给定的数字太大,无法为 HRESULT。 未知错误代码 内部错误 命令行参数无效 执行命令失败 打开清单失败 已收到取消信号 运行 ShellExecute 失败 无法处理清单。清单版本高于支持的版本。请更新客户端。 下载安装程序失败 无法写入索引;它是更高版本的架构 索引已损坏 配置的源信息已损坏 已配置源名称 源类型无效 MSIX 文件是捆绑包,不是程序包 缺少源所需的数据 所有安装程序都不适用于当前系统 安装程序文件的哈希与清单不匹配 源名称不存在 已使用其他名称配置源位置 找不到程序包 未配置任何源 找到多个与条件匹配的程序包 找不到与条件匹配的清单 未能从源包获取公用文件夹 需要管理员权限才能运行命令 源位置不安全 Microsoft Store 客户端遭到策略阻止 Microsoft Store 应用遭到策略阻止 该功能当前正在开发中。可以使用 winget 设置启用它。 未能安装 Microsoft Store 应用 未能执行自动完成 未能初始化 XML 分析程序 遇到无效的 YAML 密钥 遇到重复的 YAML 密钥 YAML 操作无效 未能生成 YAML 文档 YAML 发出程序状态无效 YAML 数据无效 LibYAML 错误 清单验证成功,但出现警告 清单验证失败 清单无效 找不到适用的更新 winget upgrade --all 完成,但出现故障 安装程序未通过安全性检查 下载大小与预期内容长度不匹配 找不到卸载命令 运行卸载命令失败 ICU 中断迭代器错误 ICU casemap 错误 ICU 正则表达式错误 无法安装一个或多个导入的包 找不到一个或多个请求的包 Json 文件无效 源位置不是远程位置 配置的 REST 源不受支持。 REST 源返回的数据无效 操作被组策略阻止 Rest API 内部错误 REST 源 URL 无效 REST API 返回的 MIME 类型不受支持 REST 源合同版本无效 源数据已损坏或被篡改 从流中读取时出错 未同意程序包协议 在提示中读取输入时出错 一个或多个源不支持搜索请求 找不到 Rest API 终结点。 未能打开源。 未同意源协议 标头大小超过了允许的 1024 个字符的限制。请减小大小,然后重试。 缺少资源文件 运行 MSI 安装失败 msiexec 的参数无效 未能打开一个或多个源 未能验证依赖项 缺少一个或多个程序包 表列无效 升级版本不高于已安装的版本 升级版本未知,未指定替代 ICU 转换错误 未能安装可移植包 卷不支持重新分析点 已存在来自其他源的可移植包。 无法创建符号链接。路径指向目录。 无法从管理员上下文运行安装程序。 未能卸载可移植包 未能根据索引验证 DisplayVersion 值。 一个或多个参数不受支持。 SQLite 不允许嵌入的 null 字符 在存档中找不到嵌套安装程序。 未能解压缩存档。 提供的嵌套安装程序的相对文件路径无效。 服务器证书与任何预期值都不匹配。 必须提供安装位置。 存档恶意软件扫描失败。 找到至少一个已安装的包版本。 包的 PIN 已存在。 包没有 PIN。 无法打开 PIN 数据库。 一个或多个应用程序安装失败 一个或多个应用程序未能安装 一个或多个查询并不是恰好返回一个匹配项 包具有阻止升级的 PIN。 当前安装的程序包是存根程序包 已收到应用程序关闭信号 未能下载程序包依赖项。 未能下载包。禁止下载以进行脱机安装。 所需的服务正忙或不可用。请稍后重试。 提供的 GUID 与有效的恢复状态不对应。 当前客户端版本与已保存状态的客户端版本不匹配。 恢复状态数据无效。 无法打开检查点数据库。 超过了最大恢复限制。 身份验证信息无效。 不支持的身份验证方法。 身份验证失败。 身份验证失败。需要交互式身份验证。 身份验证失败。用户已取消。 身份验证失败。经过身份验证的帐户不是所需的帐户。 应用程序当前正在运行。退出应用程序,然后重试。 另一个安装已在进行中。请稍后再试。 正在使用一个或多个文件。退出应用程序,然后重试。 系统中缺少此包的依赖项。 你的电脑上没有更多空间。腾出空间,然后重试。 内存不足,无法安装。关闭其他应用程序,然后重试。 此应用程序需要 Internet 连接。请连接到网络,然后重试。 此应用程序在安装过程中遇到错误。请联系支持人员。 重启电脑以完成安装。 安装失败。请重新启动电脑,然后重试。 你的电脑将重启以完成安装。 你已取消安装。 已安装此应用程序的另一个版本。 已安装此应用程序的更高版本。 组织策略正在阻止安装。请与管理员联系。 未能安装程序包依赖项。 应用程序当前正由另一个应用程序使用。 参数无效。 系统不支持程序包。 安装程序不支持升级现有包。 安装失败,出现自定义安装程序错误。 找不到包的“应用和功能”条目。 安装位置不适用。 找不到安装位置。 现有文件的哈希不匹配。 找不到文件。 找到文件,但未检查哈希。 无法访问该文件。 配置文件无效。 YAML 语法无效。 配置字段的类型无效。 配置具有未知版本。 应用配置时出错。 配置包含重复的标识符。 配置缺少依赖项。 配置具有未满足的依赖项。 配置单元的断言失败。 已手动跳过配置。 用户拒绝继续执行。 依赖项关系图包含无法解析的循环。 配置具有无效的字段值。 配置缺少字段。 某些配置单元在测试其状态时失败。 未测试配置状态。 未安装配置单元。 未找到配置单元。 已找到配置单元的多个匹配项;指定模块以选择正确的模块。 尝试获取当前系统状态时配置单元失败。 尝试测试当前系统状态时配置单元失败。 尝试应用所需状态时配置单元失败。 配置单元的模块在具有相同版本的多个位置可用。 加载配置单元的模块失败。 配置单元在执行过程中返回了意外结果。 单元包含需要配置根的设置。 配置处理器不支持此操作。 不可用 已成功启用 Windows 功能依赖项 加载配置单元的模块失败,因为它需要管理员权限才能运行。 单元包含需要配置根的设置。 加载配置单元的模块失败,因为它需要管理员权限才能运行。 通过传递已保存命令的唯一标识符来恢复执行以前保存的命令。这用于恢复可能因重新启动而终止的已执行命令。 继续执行以前保存的命令。 要恢复的已保存状态的唯一标识符 不支持从其他客户端版本恢复状态: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. 恢复状态不存在: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. 在恢复状态下找不到数据。 此命令不支持恢复。 如果适用,允许重启 正在发起重启以完成操作... 未能发起重启。 恢复操作超出了允许的 {0} 恢复() 限制。若要手动恢复,请运行命令 “{1}”。 {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. 忽略恢复已保存状态的限制 不支持 URI 方案: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. URI 格式不标准: {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. 无法分析 {0} 配置单元设置内容或设置内容为空。 {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. {0} 配置单元缺少必需的参数: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. {0} 配置单元缺少建议的参数: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. WinGetSource 配置单元与已知源冲突: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. 第三方源上的 WinGetSource 配置单元声明: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage 声明 UseLatest 和 Version。包 ID: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} WinGetPackage 配置单元声明来自第三方源的包。包 ID: {0};源: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 配置单元包依赖于以前未配置的第三方源。包 ID: {0};源: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 配置单元包依赖于第三方源。建议在 uni dependsOn 节中声明依赖项。包 ID: {0};源: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} 无法验证 WinGetPackage 配置单元包。源打开失败。包 ID: {0};源: {1} {Locked="WinGetPackage,{0},{1}"} 无法验证 WinGetPackage 配置单元包。找不到包。包 ID: {0} {Locked="WinGetPackage,{0}"} 无法验证 WinGetPackage 配置单元包。找到多个包。包 ID: {0} {Locked="WinGetPackage,{0}"} 无法验证 WinGetPackage 配置单元包。找不到包版本。包 ID: {0};版本 {1} {Locked="WinGetPackage,{0},{1}"} 使用特定版本指定的 WinGetPackage 配置单元包,而只有一个包版本可用。包 ID: {0};版本: {1} {Locked="WinGetPackage,{0},{1}"} 无法验证 WinGetPackage 配置单元包。包 ID: {0} {Locked="WinGetPackage,{0}"} 指定身份验证窗口首选项(silent、silentPreferred 或 interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. 指定用于身份验证的帐户 无法添加源。此 winget 版本不支持源的身份验证方法。请尝试升级到最新 winget 版本。 {Locked="winget"} {0} 源需要身份验证。必要时可能会显示身份验证提示。将与源共享经过身份验证的信息以进行访问授权。 {Locked="{0}"} 修复通过搜索已安装的包列表或直接从清单中找到的所选包。默认情况下,查询必须以不区分大小写方式匹配包的 ID、名称或名字对象。可通过传递相应的选项来使用其他字段。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 修复所选包 找不到此包的修复命令。请联系包发布者以获取支持。 正在使用的安装程序技术与当前安装的版本不匹配。 找不到修复命令。 正在使用的安装程序技术不支持修复。 修复操作成功完成。 已放弃修复 正在启动包修复... 未能修复 Microsoft Store 包。错误代码: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. 修复操作失败。 修复操作不适用。 配置的源中没有可用的匹配包版本。 当前系统配置不支持修复此包。 正在使用的安装程序技术不支持修复。 使用管理员权限运行时,无法修复为用户范围安装的包。 不允许对用户范围内安装的包执行涉及管理员特权的修复操作。 修复失败,退出代码为: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. 为了防止损坏,SQLite 连接已终止。 卸载所有版本 要执行的版本 已安装此包的多个版本。请优化搜索,传递 “--version” 参数以选择一个,或传递 “--all-versions” 标志以卸载所有这些参数。 {Locked="--version,--all-versions"} 启用Windows 程序包管理器代理命令行选项 Describes a Group Policy that can enable the use of the --proxy option to set a proxy 设置要用于此执行的代理 禁止对此执行使用代理 无法重置 {0}。此设置由策略控制。有关详细信息,请与系统管理员联系。 {Locked="{0}"} The value will be replaced with the feature name 重置管理员设置“{0}”。 {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. 无法设置 {0}。此设置由策略控制。有关详细信息,请与系统管理员联系。 {Locked="{0}"} The value will be replaced with the feature name 将管理员设置 '{0}' 设置为 '{1}'。 {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced 要修改的设置的名称 要为设置设置的值。 将管理员设置重置为其默认值。 将管理员设置重置为其默认值。 设置管理员设置的值。 设置管理员设置的值。 除非指定,否则从发现中排除源 从发现中排除源(true 或 false) 显式 源的信任级别 (无或受信任的) 信任级别 未能获取 Microsoft Store 程序包目录。 未从 Microsoft Store 程序包目录中找到适用的 Microsoft Store 程序包。 无法获取 Microsoft Store 程序包下载信息。 找不到适用于下载的Microsoft Store包。 无法检索 Microsoft Store 程序包许可证。 找不到适用的 Microsoft Store 程序包。 已成功验证 Microsoft Store 程序包哈希 Microsoft Store 程序包哈希不匹配 已下载 Microsoft Store 程序包: {0} {Locked="{0}"} Full path of the downloaded package. Microsoft Store 程序包下载失败: {0} {Locked="{0}"} Package name. 已完成 Microsoft Store 程序包下载 正在从 Microsoft Store 下载主程序包... 正在从 Microsoft Store 下载依赖项程序包... 正在检索 Microsoft Store 程序包下载信息 未能检索 Microsoft Store 程序包下载信息 正在检索 Microsoft Store 程序包许可证 已保存 Microsoft Store 程序包许可证: {0} {Locked="{0}"} License file full path. 未能检索 Microsoft Store 程序包许可证 Microsoft Store 程序包下载不支持 --rename 参数。Microsoft Store 程序包将使用 Microsoft Store 目录提供的名称。 {Locked="--rename"} Microsoft Store包下载需要Microsoft Entra ID 身份验证。必要时可能会显示身份验证提示。将与Microsoft 服务共享经过身份验证的信息以进行访问授权。对于Microsoft Store包授权,Microsoft Entra ID 帐户必须是全局管理员、用户管理员或许可证管理员的成员。 {Locked="Global Administrator,User Administrator,License Administrator"} 跳过检索 Microsoft Store 程序包脱机许可证 选择目标平台 正在添加配置文件: {0} {Locked="{0}"} 已成功导出 正在获取配置设置... 必须至少提供 --packageId 和/或 --module with --resource。或使用 --all 导出所有包配置。 {Locked="--packageId,--module, --resource, --all"} 参数 --packageId、--module 和 --resource 不能与 --all 一起使用。 {Locked="--packageId,--module, --resource, --all"} 将配置资源导出到配置文件。与 --all 一起使用时,导出所有包配置。与 --packageId 一起使用时,导出给定包 ID 的 WinGetPackage 资源。与 --module 和 --resource 一起使用时,获取资源的设置并将其导出到配置文件。如果输出配置文件已存在,则追加导出的配置资源。 {Locked="WinGetPackage,--packageId,--module, --resource"} 将配置资源导出到配置文件。 要导出的资源的模块。 要导出的程序包标识符。 要导出的配置资源。 导出所有包配置。 配置单元无法获取其属性。 导出配置失败。 配置{0} {Locked="{0}"} 安装 {0} {Locked="{0}"} 对于此输出,配置文件中存在的某些数据被截断;检查文件内容的完整内容。 <此值已被截断;检查文件内容以获取完整文本> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. 显示已应用于系统的配置的高级别详细信息。然后,此数据可与 `configure` 命令一起使用以获取更多详细信息。 {Locked="`configure`"} 显示配置历史记录 历史记录中没有配置。 从历史记录中选择项目 找不到与提供的数据匹配的单个配置。提供与所需配置明确匹配的全名或标识符的一部分。 从历史记录中移除项 首次应用 Column header for date values indicating when a configuration was first applied to the system. 标识符 名称 来源 路径 找不到指定的配置。 已完成 The state of processing an item. 正在进行 The state of processing an item. 待处理 The state of processing an item. 未知 The state of processing an item. 监视配置状态。 As in "to monitor the status of a configuration being applied". 已完成 The state of processing an item. 正在进行 The state of processing an item. 待处理 The state of processing an item. 已跳过 The state of processing an item. 未知 The state of processing an item. 应用已启动 When the configuration application started. 应用已结束 When the configuration application ended. 结果 详细信息 状态 The state of processing an item. 单位 该包与当前 Windows 版本或平台不兼容。 尽可能禁止显示初始配置详细信息 可以针对不同的平台和体系结构下载多个Microsoft Store包。--platform,--architecture 可用于筛选包。 {Locked="--platform--architecture"} 无法检索Microsoft Store包许可证。Microsoft Entra ID 帐户不是全局管理员、用户管理员或许可证管理员的成员。使用 --skip-license 跳过检索Microsoft Store包许可证。 {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} Microsoft Store包不支持下载。 Microsoft Store包不支持下载。 无法检索Microsoft Store包许可证。Microsoft Entra ID 帐户没有所需的权限。 无法跨完整性边界传递参数。 已下载零字节安装程序;请确保网络连接正常工作。 已下载零字节安装程序;请确保网络连接正常工作。 管理字体 使用子命令管理字体。可以安装、升级或卸载与 package 类似的字体。 家庭 "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' 按家庭名称筛选结果 "Face" represents the typeface of a font family such as 'Bold' or 'Italic' 路径 找不到匹配输入条件的已安装字体。 列出已安装的字体 列出所有已安装的字体、所有字体文件或特定字体的完整详细信息。 版本 配置模块 PowerShell Modules that are used for the Configuration feature 包安装程序需要身份验证。必要时可能会显示身份验证提示。将与安装程序下载 URL 共享经过身份验证的信息。 无法下载安装程序。此 winget 版本不支持安装程序下载身份验证方法。请尝试升级到最新 winget 版本。 {Locked="winget"} 未能下载安装程序。身份验证失败。 指定配置处理器的路径 DSC v3 资源命令 DSC stands for "Desired State Configuration". It should already have a locked translation. 此处的子命令实现Desired State Configuration (DSC) v3 资源来配置 winget 和包。 获取资源状态 设置资源状态 描述所需的状态更改 测试资源状态 删除资源状态 获取所有状态实例 验证组内容 解析外部状态 运行适配器 获取资源架构 获取资源清单 管理包状态 通过 winget 管理包。 指示实例是否应存在。 指示实例是否处于所需状态。 包的标识符。 包的源。 包的版本。 用于将标识符与包匹配的方法。 指示应安装该包的最新可用版本。 要使用的安装模式(如需要)。 包的目标范围。 指示是否接受源和包的协议。 管理用户设置文件 管理 winget 的用户设置。 设置 json 文件内容。 用于应用设置的操作。 已为相关性记录值 管理源配置 管理 winget 的源。 用于源的名称。 源的参数。 源的类型。 源的信任级别。 调用未指定源时是否包含源。 正在应用配置单元... 正在导出配置单元... 正在获取配置单元处理器... 确保导出所需的模块 [{0}] {Locked="{0}"} 未能测试或获取所需的模块。将不导出相关设置。 导出 [{0}] {Locked="{0}"} 无法导出资源。 未能获取单元处理器。将不导出单个包设置。 要在其中写入结果的目录 在系统上找不到Desired State Configuration包。正在安装包... 未能安装Desired State Configuration包。手动安装包或提供通过 --processor-path 参数 dsc.exe 的路径。 {Locked="dsc.exe","--processor-path"} 包含管理员设置及其值的对象。 管理管理员设置 管理 winget 的管理员设置。 重置所有管理员设置 已重置所有管理员设置。 MCP 信息 MCP stands for Model Context Protocol and should probably remain as-is MCP (模型上下文协议) Windows 程序包管理器的信息。 若要使用 MCP 客户端手动配置Windows 程序包管理器 MCP 服务器,请使用 `servers` 对象中的以下 JSON 片段: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. 启用 Windows 程序包管理器 MCP 服务器 目标 OS 版本 安装一个或多个字体失败。 字体文件不受支持,无法安装。 字体包中的一个或多个字体不受支持,无法安装。 字体安装失败; 正在清理。 已安装字体。 包 ID 支持 WinGet 标题 找不到字体文件。 字体卸载失败。字体可能未处于正常状态。请尝试在重启后卸载。 字体验证失败。 字体回滚失败。字体可能未处于正常状态。请尝试在重启后卸载。 显示字体文件详细信息。 字体包验证失败。 失败的字体安装回退尝试未成功。可能需要重新启动才能成功卸载字体。 字体包卸载失败。这通常是因为系统或应用程序正在使用字体。重新启动计算机后,卸载可能会成功。 确定 "OK" means the font is in a good healthy state 损坏 "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. 状态 未知 已安装字体包。 显示有关包的详细信息 Providing this argument causes the CLI to output additional details about installed application packages. 通道: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). 本地标识符: Precedes a value that is the unique identifier for the installed package on the local system. 包系列名称: Precedes a value that is the APPX/MSIX package family name of the installed package. 产品代码: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. 升级代码: Precedes a value that is the MSI Upgrade Code for the installed package. 已安装的范围: Precedes a value that is the scope of the installation of the package (ex. user, machine). 已安装的体系结构: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). 已安装的区域设置: Precedes a value that is the locale of the installed package. 安装位置: Precedes a value that is the directory path to the installed package. 源位置: Precedes a value that names the package source where the installed package originated from. 可用升级: Precedes a list of package upgrades available for the installed package. 安装程序类别: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). 旧值 Column title for listing edit changes. 新值 Column title for listing the new value. ================================================ FILE: Localization/Resources/zh-TW/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: Localization/Resources/zh-TW/winget.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 鄰近別名不是旗標: [{0}] {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). 找不到鄰近旗標別名: [{0}] {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). 以下是可用的引數: Message displayed to inform the user about the available command line arguments. 以下命令別名可用: Message displayed to inform the user about the available command line alias arguments. 以下是所有可用的命令: Title displayed to inform the user about the available commands. 可用 As in "a new version is available to upgrade to". 以下是可用的選項: Message displayed to inform the user about the available options. 以下是可用的子命令: Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} 個升級可供使用。 {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. 使用指定的頻道; 預設為一般觀眾 命令 Label displayed for a command to give the software. 依命令篩選結果 Description message displayed to inform the user about filtering the search results by a package command. 用於完成的完整命令列 此命令需要系統管理員許可權才能執行。 此命令可以用來要求即時線上的命令列完成。將會傳入要完成的命令列、游標位置及文字。輸出為基於輸入的一組可能值,其中每行一個可能值。 啟用即時線上的命令列完成 顯示介於 1 到 1000 個(之間的指定結果數目) 完成 Label displayed when an operation completes or is done executing. 以完全相符,來尋找套件 Description message displayed to inform the user about finding an application package using an exact matching criteria. 示範用的實驗引數 此命令是如何執行實驗功能的範例。若要開啟,請移至 ‘winget settings’ 並啟用 experimentalCmd 或 experimentalArg 功能。 {Locked="winget settings"} 實驗功能範例 在未預期的情況下找到位置引數: [{0}] {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. 此功能正在進行中,未來可能會大幅變更或全部移除。若要啟用,請編輯您的設定 ('winget settings')以包含實驗性功能: '{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. 顯次實驗功能的狀態。實驗功能可以從「winget settings」開啟。 {Locked="winget settings"} 顯示實驗功能的狀態 已停用 已啟用 功能 連結 下列實驗性功能正在運行中。 它們可以透過設定檔案的 [winget settings] 來設定。 {Locked="winget settings"} 屬性 狀態 雜湊的檔案 旗標引數不能包含鄰近值: [{0}] {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. 計算適合在資訊清單輸入的本機檔案雜湊。它也可以計算 MSIX 封裝的簽章檔案雜湊,以啟用串流安裝。 雜湊安裝程式檔案的協助程式 顯示所選命令的相關說明 如需特定命令的更多詳細資料,請向其傳遞說明引數。 若要取得更多協助,請參閱: [{0}] {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. 依識別碼篩選結果 抑制警告輸出 此應用程式已由其擁有者授權給您。 Microsoft 不負任何責任,也不會授與協力廠商封裝的任何授權。 此套件是透過 Microsoft Store 所提供。winget 可能需要代表目前的使用者從 Microsoft Store 取得套件。 {Locked="winget"} 安裝透過搜尋已設定來源或直接來自資訊清單的已選取套件。根據預設,査詢必須與套件的識別碼 (id)、名稱 (name) 或連結路徑 (moniker) 不區分大小寫。透過傳遞適當的選項,可以使用其他欄位。根據預設,install 命令將檢查套件的安裝狀態,並在適用的情况下嘗試執行升級。使用 --force 覆寫可執行直接安裝。 {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 安裝指定的套件 安裝程式雜湊不相符;以系統管理員執行時,無法覆寫此情況 安裝程式雜湊不相符; 正在繼續,因為 --ignore-security-hash {Locked="--ignore-security-hash"} 安裝程式雜湊不相符;若要覆寫此檢查,請使用 --ignore-security-hash {Locked="--ignore-security-hash"} 已成功驗證安裝程式雜湊 已成功安裝 正在啟動套件安裝... 略過安裝程式雜湊檢查失敗 從本機資訊清單安裝封存類型封裝時,略過執行的惡意程式碼掃描 要求互動式安裝; 可能需要使用者輸入 無法辨識目前命令的引數別名: [{0}] {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). 無效的引數指定元: [{0}] {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). 無法辨識目前命令的引數名稱: [{0}] {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Winget 目錄 Header for a table detailing the directories Winget uses for key operations like logging and portable installs 要使用的地區設定 (BCP47 格式) {Locked="BCP47"} 授權合約 連結 Links to different webpages list 命令會顯示已安裝在系統上的套件,以及是否有可用的更新。可以提供其他選項來篩選輸出 (這與 search 命令很相似)。 {Locked="list","search"} 顯示已安裝的套件 要安裝的位置 (如果有支援的話) 記錄位置 (如果有支援的話) Copyright (c) Microsoft Corporation. 著作權所有,並保留一切權利。 首頁 The primary webpage for the software 套件資訊清單的路徑 資訊清單驗證失敗。 資訊清單驗證成功。 資訊清單驗證成功,但出現警告。 需要引數值,但找不到: [{0}] {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. 依套件連結路徑來篩選結果 系統會將輸入檔案視為 msix; 如果簽署的話,將會提供簽章雜湊 無法計算 MSIX 簽章訊息摘要。 無法安裝或升級 Microsoft Store 套件,因為特定應用程式受原則封鎖 無法安裝或升級 Microsoft Store 套件。錯誤碼: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. 無法安裝或升級 Microsoft Store 套件,因為 Microsoft Store 用戶端受原則封鎖 正在驗證/正在要求套件取得... 找到多個符合輸入條件的已安裝套件。請精煉輸入。 找到多個符合輸入條件的套件。請精煉輸入。 依名稱篩選結果 找不到適用的安裝程式;請參閱紀錄檔以取得詳細資料。 目前沒有可用的實驗功能。 未找到符合輸入條件的已安裝套件。 未找到符合輸入條件的套件。 停用 VirtualTerminal 顯示 {Locked="VirtualTerminal"} 開啟預設記錄位置 選項 Options to change how a command works 覆寫要傳送到安裝程式的引數 包裹: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. 糟糕,我們忘了執行這項工作... 游標在命令列內的位置 隱私權聲明 用來搜尋套件的查詢 顯示各種色彩的進度 未提供必要的引數: [{0}] {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. 顯示為預設色彩的進度 從設定的來源中搜尋套件。 尋找並顯示套件的基本資訊 識別碼 Abbreviation of Identifier. 相符 名稱 來源 由於結果限制,已截斷其他項目 版本 驗證設定時發現下列失誤: 在預設 json 文字編輯器中開啟設定。如果未設定任何編輯器,則會在記事本中開啟設定。如需可用的設定,請參閱 https://aka.ms/winget-settings 此命令透過提供 --enable 或 --disable 引數,可用於設定系統管理員設定。 {Locked="--enable"} {Locked="--disable"} 開啟設定或設定系統管理員設定 載入設定時發生未預期的錯誤。請執行 'settings' 命令以驗證您的設定。 {Locked="'settings'"} 頻道 顯示特定封裝的資訊。根據預設,查詢必須 insensitively 符合封裝的識別碼、名稱或名字物件。您可以透過傳遞適當的選項來使用其他欄位。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 顯示套件相關資訊 版本 要求無訊息安裝 單一 - 後只能出現單一字元別名: [{0}] {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. 具有給定名稱的來源已存在,且參照到不同的位置: 具有不同名稱的來源已參照到此位置: 具有給定名稱的來源已存在,且參照到同樣的位置: 新增的來源: 新增來源。來源會為您提供資料,以便您探索和安裝套件。只有在您信任其為安全位置時,才會新增來源。 新增來源 指派給來源的引數 使用指定的來源尋找套件 使用子命令管理來源。來源會為您提供資料,以便您探索和安裝套件。只有在您信任其為安全位置時,才會新增來源。 管理套件來源 編輯現有來源的屬性。來源提供資料讓你發現並安裝套件。 編輯來源屬性 正在編輯來源: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. 名為 '{0}' 的來源已經處於所需的狀態。 {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. 引數 Value given to source. 列出所有目前的來源,或特定來源的完整詳細資料。 列出目前的來源 資料 Data stored by the source. 欄位 The name of a piece of information about a source. 名稱 The name of the source. 識別碼 The source's unique identifier. 找不到具有此名稱的來源: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. 沒有未設定來源。 類型 The kind of source. 已更新 The last time the source was updated. 永不 The source has never been updated. The value of information about a source. 來源的名稱 無法開啟來源;如果此問題持續發生,請嘗試使用‘source reset’命令。 {Locked="source reset"} 無法開啟預先定義的來源; 請向 winget 維護者回報。 {Locked="winget"} 正在移除所有來源... 移除特定來源。 移除目前的來源 正在移除來源: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. 正在重設所有來源... 此命令會刪除現有的來源,可能會留下任何本地資料。在沒有任何引數的情況下,系統會刪除所有的來源,並新增預設值。如果提供指定的命名來源,則只會刪除該來源。 重設來源 強制重設來源 若已給定 --force 選項,將重設下列來源: {Locked="--force"} 重設的來源: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. 來源的類型 正在更新所有來源... 更新所有來源,或只更新特定來源。 更新目前的來源 更新的來源: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. 依標籤篩選結果 感謝您使用 Winget 第三方聲明 Winget 命令列實用程式可讓您從命令列安裝應用程式和其他套件。 顯示該工具的一般資訊 顯示該工具的版本 引數提供的次數超過允許的次數: [{0}] {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. 提供了多個執行行為引數: '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). 執行命令時,發生意外的錯誤: 在升級期間卸載舊版的套件 無法辨識的命令: [{0}] {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. 將所有已安裝的套件升級至最新版本 (如果有可用更新的話) 找不到適用的升級。 已設定的來源中有較新的套件版本,但不適用於您的系統或需求。 找不到可用的升級。 沒有較新的套件版本可從設定的來源使用。 可透過搜尋已安裝的套件或直接從資訊清單中,升級已選取的套件。根據預設,查詢必須不區分大小寫地符合該套件的識別碼、名稱或連結路徑。您可以透過傳遞適當的選項,來使用其他欄位。未提供引數時,顯示具有可用升級的套件 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 顯示並執行可用的升級 使用狀況: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. 使用一套嚴格的指導方針驗證資訊清單。這是為了讓您能夠在提交報告之前檢查您的資訊清單。 驗證資訊清單檔案 要驗證的資訊清單路徑 為 winget 啟用詳細登入 請確認輸入檔是有效且已簽署的 MSIX。 使用指定的版本; 預設為最新版本 顯示該套件的可用版本 完成前提供的值已要求 找不到相符的版本: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. 沒有符合給定值的來源: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. 設定的來源為: 未定義來源;使用 [source add],以進行新增,或使用 [source reset] 將之重設成預設值 {Locked="source add","source reset"} 找到 路徑為目錄; {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. 檔案不存在: {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. 提供本地顯示和搜尋查詢引數 記錄檔 Label displayed for diagnostic files containing information about the application use. 安裝程式受到政策封鎖 安裝程式的安全性檢查失敗 防毒程式產品報告安裝程式中有感染 嘗試更新來源失敗: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. 可透過搜尋已安裝的套件或直接從資訊清單中,解除安裝已選取的套件。根據預設,查詢必須不區分大小寫地符合該套件的識別碼、名稱或連結路徑。您可以透過傳遞適當的選項,來使用其他欄位。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 解除安裝指定的套件 正在啟動套件解除安裝... 已成功解除安裝 winget 找不到此套件的解除安裝命令。請向套件發行者尋求支援。 {Locked="winget"} 已放棄解除安裝 解除安裝失敗,結束代碼: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. 匯出已安裝套件的清單 安裝檔案中列出的所有套件。 安裝檔案中的所有套件。 欲寫入結果的檔案 描述要安裝之套件的檔案 從指定來源匯出套件 將已安裝套件的清單寫入檔案。然後可以使用 import 命令來安裝封裝。 {Locked="import"} 無法安裝一或多個匯入的套件 未安裝匯入所需的來源: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. 已安裝套件無法從任何來源取得: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. 已安裝的套件版本無法從任一來源取得: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. 在匯入檔案中找不到套件 JSON 檔案無效。 已安裝的套件: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. 略過無法使用的套件 在匯出檔案中包含套件版本 略過匯入檔案的套件版本 路徑不存在: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. JSON 檔案未指定可識別的結構描述。 選取安裝範圍 (user 或 machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. `{0}` 引數提供的值無效;有效值為: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. 群組原則已停用此作業: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. 啟用其他 Windows 應用程式安裝程式來源 啟用 Windows 應用程式安裝程式允許的來源 啟用 Windows 應用程式安裝程式預設來源 啟用 Windows 應用程式安裝程式實驗性功能 啟用 Windows 應用程式安裝程式 Microsoft Store 來源 啟用 Windows App 安裝程式字型來源 啟用 Windows 封裝管理員設定 啟用 Windows 封裝管理員 啟用 Windows 封裝管理員命令列介面 設定 Windows 封裝管理員來源自動更新間隔 (分鐘) 群組原則 Header for a table listing active Group Policies 啟用 Windows 應用程式安裝程式本機資訊清單檔案 啟用 Windows 應用程式安裝程式 Microsoft Store 來源已釘選憑證旁路 啟用 Windows 應用程式安裝程式本機封存惡意程式碼掃描覆寫 欄位: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. 無效的欄位格式。 無效的欄位值。 群組原則的設定無效。 已載入備份檔案中的設定。 剖析檔案時發生錯誤: 值: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. 下列實驗性功能正在進行中。 由於群組原則的原因,已停用設定。 安裝程式雜湊不相符。 啟用 Windows 應用程式安裝程式雜湊覆寫 匯出目前來源並做為群組原則的 JSON。 匯出目前的來源 其他來源 An additional source required by policy. 允許的來源 A source that the user is allowed to add. 為 '{0}' 引數提供的值無效 {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. 已取消 外部 在此匯入中找到的套件具有下列相依性: Import command sentence showed before reporting dependencies 此套件需要下列相依性: Message shown before reporting dependencies 正在安裝相依性: 找不到相依性來源 套件搜尋產生一個以上的結果: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. 找不到套件的最新版本: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. 找不到任何安裝程式: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. 套用沒有可使用的最低必要版本: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. 沒有相符項目 When package search yields no matches 有迴圈 Dependency graph has loop 找不到適合指令清單的安裝程式: {0} 版本 {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. 處理套件相依性時發生錯誤。您要繼續安裝嗎? Prompt message shown when dependencies processing yields errors. 處理套件相依性時發生錯誤。正在結束... 套件 此套件具有可能不再需要的相依性: Uninstall command sentence showed before reporting dependencies 資訊清單具有下列未驗證的相依性;請確認它們有效: Validate command sentence showed before reporting dependencies Windows 功能 Windows 媒體櫃 使用指定的來源尋找套件相依性 For getting package type dependencies when installing from a local manifest Windows 市集條款 接受套件的所有授權合約 匯出的套件需要授權合約才能安裝: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. 發行者要求您檢視上述資訊並接受合約,然後再安裝。 是否同意這些條款? 未同意套件合約。作業已取消。 合約: 作者: 描述: 安裝程式: 安裝程式地區設定: Store 產品識別碼: 安裝程式 SHA256: 安裝程式類型: 安裝程式 URL: 授權: 授權 URL: 綽號: 首頁: 發行者: 標記: 版本: 相依性: Windows 功能: Windows 文件庫: 套件相依性: 外部相依性: 無法開啟新增的來源。 在來源作業期間接受所有來源合約 `{0}` 來源要求您必須先檢視下列合約,再使用。 {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. 是否同意所有來源合約條款? 一或多個來源合約未同意。作業已取消。請接受來源合約或移除對應的來源。 來源需要將目前電腦的 2 個字母地理區域傳輸到後端服務,才能正確(例如"US")。 已成功安裝。重新開機應用程式以完成升級。 選用的 Windows-Package-Manager REST 來源 HTTP 標頭 略過選用標頭,因為它不適用於此來源。 選擇性標頭不適用,但未指定來源: '{0}' {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. 發行日期: 支援離線散發: 發行者 URL: 購買 Url: 發行者支援 URL: 隱私權 Url: 著作權: 著作權 Url: 版本資訊: 版本資訊 Url: 搜尋來源時失敗; 將不會包含結果: {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. 搜尋來源時失敗: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. 系統管理員必須啟用此功能。若要啟用此功能,請以系統管理員的身分執行‘winget settings --enable {0}’。 {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. 啟用特定系統管理員設定 停用特定系統管理員設定 啟用管理員設定'{0}'。 {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. 關閉管理員設定 '{0}'。 {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. 管理員設定 Header for a table displaying admin settings. 應用程式目前正在執行。結束應用程式,然後再試一次。 安裝程式修改的檔案目前正被其他應用程式使用。結束應用程式,然後再試一次。 另一個安裝已在進行中。請稍後再試。 一或多個檔案正在使用中。結束應用程式,然後再試一次。 您的系統遺漏此套件的相依性。 您的電腦上已經沒有空間。請釋出空間,然後再試一次。 記憶體不足,無法安裝。請關閉其他應用程式,然後再試一次。 其中一個安裝參數無效。套裝安裝記錄可能包含其他詳細資訊。 此應用程式需要網際網路連線。請連線到網路,然後再試一次。 此應用程式在安裝期間發生錯誤。連絡客戶支援。 重新啟動您的電腦以完成安裝。 將重新開機您的電腦以完成安裝。 安裝失敗。請重新開機您的電腦,然後再試一次。 您取消了安裝。 已安裝此應用程式的另一個版本。 已安裝此應用程式的較新版本。 組織原則導致無法安裝。請連絡您的系統管理員。 目前系統設定不支援安裝此套件。 安裝失敗,發生自定義安裝程序錯誤。連絡套件支援。 已放棄安裝 安裝程式失敗,結束代碼為: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. 安裝程式記錄檔可在下列位置使用: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. 在工作來源中找到下列套件。 請使用 '--source' 選項指定其中一個以繼續。 {Locked="--source"} "working sources" as in "sources that are working correctly" 在工作來源中找不到任何套件。 "working sources" as in "sources that are working correctly" 這是 Windows 封裝管理員的穩定版本。如果您想要嘗試實驗性功能,請安裝發行前版本組建。可於 https://github.com/microsoft/winget-cli 的 GitHub 上找到指示。 {Locked="https://github.com/microsoft/winget-cli"} Windows 封裝管理員 v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. 忽略匯入檔案中的套件版本 要求的結果數目必須介於 1 到 1000 之間。 除預設外在安裝器上通過引數 找到較新的版本,但安裝技術與目前安裝的版本不同。請卸載套件並安裝較新的版本。 指定之較新版本的安裝技術與目前安裝的版本不同。請卸載套件並安裝較新的版本。 Windows 封裝管理員 (預覽) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. 選取架構 升級套件,即使無法判斷其目前版本 即使無法判斷封裝的目前版本,也要列出封裝。可以只與 --upgrade-available 引數搭配使用 {Locked="--upgrade-available"} 無法判斷此套件的版本號碼。若要繼續升級,請將引數 「--include-unknown」新增至先前的命令。 {Locked="--include-unknown"} {0} 套件具有無法確認的版本號碼。使用--include-unknown (包含未知項目) 來看到所有結果。 {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} 套件已固定且需要特別更新。 {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. 提供的引數只能與查詢一起使用。 系統架構: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). 保留套件 (可攜式) 建立的所有檔案和目錄 刪除封裝目錄中的所有檔案和目錄(可攜式) 按 Enter 以繼續. . . 對可執行檔 (可攜式) 重新命名的值 在結束之前,提示使用者按下按鍵 安裝程式會要求以系統管理員身分執行。預期提供提示。 無法從系統管理員內容執行安裝程式。 已修改路徑環境變數;重新啟動命令介面以使用新值。 使用產品代碼篩選 具有相同名稱但來源不同的可攜式套件存在;正在繼續,因為 --force {Locked="--force"} 磁碟區不支援重新分析點 指定的檔案名稱不是有效的檔案名稱 正在覆寫現有的檔案: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. 未提供套件選取引數;請參閱說明以取得有關尋找套件的詳細資訊。 新增的命令列別名: 可攜式連結目錄 (電腦) 可攜式連結目錄 (使用者) 可攜式封裝根目錄 (使用者) 可攜式封裝根目錄 可攜式封裝根目錄 (x86) 可攜式安裝失敗;正在清除... 已修改 Portable 套件: 正在繼續,因為 --force {Locked="--force"} 無法移除 Portable 套件,因為它已遭到修改;覆寫此檢查可使用 --force {Locked="--force"} 檔案仍保留在安裝目錄中: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. 正在清除安裝目錄... 無法清除安裝目錄,因為它不是由 WinGet 所建立 相關連結 文件: 筆記: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. 安裝附註: 此套件不支援提供的引數 無法擷取要封存的內容 巢狀安裝程式檔案不存在。請確保巢狀安裝程式的指定相對路徑相符: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. 巢狀安裝程式的相對檔案路徑無效;路徑指向安裝目錄之外的位置 沒有為此套件指定巢狀安裝程式 封存安裝程式只能指定一個巢狀安裝程式,除非它是可攜式或字型巢狀安裝程式 提供的命令列引數不相容 只列出有可供使用的升級的套件 以下套件有可用的升級,但需要升級的明確顯示目標: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly 正在下載 Label displayed while downloading an application installer. 不支援巢狀安裝程式類型 已知此安裝程式可以重新啟動終端機或殼層 此套件需要安裝位置 已知以下安裝程式可以重新啟動終端機或殼層: 以下安裝程式需要安裝位置: 指定安裝根目錄: 要繼續處理? 的合約 This will be followed by a package name, and then a list of license agreements 套件需要安裝位置,但未提供 停用互動式提示 Description for a command line argument, shown next to it in the help 發現已安裝的現有套件。正在嘗試升級已安裝的套件... 直接執行命令並繼續處理非安全性相關問題 Description for a command line argument, shown next to it in the help 來自不同來源的 Portable 套件已存在 已成功擷取封存 正在擷取封存... 封存掃描偵測到惡意程式碼。若要覆寫此檢查,請使用 --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} 封存掃描偵測到惡意程式碼。由於--ignore-local-archive-malware-scan,正在繼續 {Locked="--ignore-local-archive-malware-scan"} 如果已安裝版本已存在,則跳過升級 已安裝套件版本。安裝已取消。 無法啟用{0}。這個設定是由原則控制。如需詳細資訊,請連絡您的系統管理員。 {Locked="{0}"} The value will be replaced with the feature name 無法停用{0}。這個設定是由原則控制。如需詳細資訊,請連絡您的系統管理員。 {Locked="{0}"} The value will be replaced with the feature name 新增釘選。釘選可以限制 Windows 套件管理員將套件更新至指定的版本範圍,或是避免一次更新所有套件。固定的套件仍可以自行更新,且能在 Windows 套件管理員外進行更新。根據預設,固定的套件可以藉由在「更新」指令中特別指定來更新,或是利用「--include-pinned」(包含固定項目) 旗標至「winget upgrade --all」(winget 更新—全部。) {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} 新增新釘選 釘選可以限制 Windows 套件管理員將套件更新至指定的版本範圍,或是避免一次更新所有套件。固定套件仍可以自行更新,且能在 Windows 套件管理員外進行更新。 管理套件釘選 列出所有目前的釘選,或特定釘選的完整詳細資料。 列出目前的釘選項目 移除特定套件 PIN。 移除套件釘選 重設所有現有的釘選。 重設釘選? 要釘選封裝的版本。萬用字元 '*' 可以做為最後一個版本元件 直到移除釘選前,都對更新進行封鎖,以防止覆寫引數 釘選特定安裝的版本 已安裝 Value used in a table to indicate that a package comes from the list of packages installed in the machine 將設定匯出為 JSON 匯出設定 使用者設定 Label displayed for the file containing the user settings. 無法載入設定檔案。使用預設值。 選取已安裝的套件範圍篩選條件 (user 或 machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. 成功新增釘選 已經有封裝{0}的 PIN {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. 套件{0}已經有 PIN。正在覆寫,因為--force引數。 {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. 套件{0}已經有 PIN。使用--force引數覆寫。 {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. 重設目前所有釘選。 使用--force引數重設所有釘選。將移除下列 PIN: {Locked="--force"} 釘選類型 {0} 套件沒有釘選 {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. 沒有經過設定的釘選。 Shown when listing or modifying existing pins if there are none. 成功重置釘選 Shown after resetting (deleting) all the pins 無法開啟釘選資料庫 Error message for when we cannot open the database containing package pins. 提供僅能用於單一套件的引數 已提供多個互斥的引數: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together 引數 {0} 只能搭配 {1} 使用 {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" 停用 As in enabled/disabled 已啟用 As in enabled/disabled Header for a table listing the state (enabled/disabled) of Group Policies and Settings 用來搜尋套件的查詢 找不到套件: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. 找到多個套件: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. 搜尋失敗: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} 個包裹具有更新前需要先移除的釘選 {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade 無法使用 winget 升級封裝。請使用發行者提供的方法來升級此套件。 即使有非封鎖釘選仍要更新套件 即使封裝有防止升級的 PIN 碼,也要列出封裝。可以只與 --upgrade-available 引數搭配使用 {Locked="--upgrade-available"} 成功移除釘選 找到更新的版本,但套件具有防止更新的釘選。 已固定套件且無法更新。使用「winget pin」指令來檢視並編輯釘選。有些釘選類型可以透過--include-pinned (包含固定項目) 引數執行繞過。 {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} 個套件有阻止更新的釘選。請使用「winget pin」指令來檢視並編輯釘選。使用 --include-pinned (包含固定項目) 引數可能會顯示更多結果。 {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages 確保系統是否符合所提供設定所述的預期狀態。可下載/執行處理器,以獲得所需的狀態。應該先檢查設定和處理器,以確保它們在套用前值得信任。 將系統設定為所需的狀態 顯示所提供設定的詳細資料。根據預設,不會修改系統,但某些選項會造成檔案下載和/或載入。 顯示設定的詳細資料 檢查系統是否符合所提供設定所述的預期狀態。可下載/執行處理器,以測試所需的狀態。應該先檢查設定和處理器,以確保它們在執行前值得信任。 檢查系統是否處於預期狀態 驗證設定檔案的正確性。 驗證設定檔案 設定檔中的欄位'{0}'類型錯誤。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. 設定檔案的路徑 設定檔案無效。 設定檔案版本 {0} 不明。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. 接受設定警告,防止互動式提示 套用 Indicates that this item is used to write state 判斷提示 Indicates that this item is used to check/assert the state rather than write to it 相依性: {0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. 某些設定未成功套用。 無法取得設定的詳細資訊。 通知 Indicates that this item is used to retrieve values for future use rather than writing them 本機 Used to indicate that the item is present on the device. 模組: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. 模組: {0} {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. 設定: Label for the values that are used as inputs for this item when applying state 已成功套用設定。 已成功套用單位。 正在將另一個設定套用到系統。此設定會儘快繼續... 您必須瞭解您選擇要執行的組態設定。Microsoft 不負責您已撰寫或匯入的設定檔。此設定可能會變更 Windows 中的設定、安裝軟體、變更軟體設定 (包括安全性設定),以及代表您接受與協力廠商套件和服務的使用者合約。 執行此設定檔案,即表示您瞭解並同意這些資源與設定。所有安裝的應用程式均由其擁有者授權給您。Microsoft 不負責,也不會授與任何授權給協力廠商套件或服務。 Legal approved. Do not change without approval. 您是否已檢閱過設定,是否要繼續將它套用至系統? PM approved. 設定是空的。 找不到功能 [{0}]。 {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. 無法啟用 Windows 功能相依性。若要繼續安裝,請使用'--force'。 {Locked="--force"} 必須重新開機,才能完全啟用 Windows 功能;若要覆寫此檢查,請使用 [--force]。 "Windows Feature(s)" is the name of the options Windows features setting. 無法啟用 Windows 功能相依性;因為 [--force] 而繼續 "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} 正在啟用 [{0}]... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. 無法啟用 [{0}] 功能: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. 必須重新開機,才能完全啟用 Windows 功能;因為 [--force] 而繼續 "Windows Feature(s)" is the name of the options Windows features setting. 正在等候另一個安裝/卸載完成... 已釘選的版本 Table header for the version to which a package is pinned; meaning it should not update from that version. <如需其他詳細資料,請參閱記錄檔> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. 正在擷取設定詳細資料 正在初始化設定系統 讀取設定檔案 系統不是設定所宣告的預期狀態。 此設定單位失敗,原因不明: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 設定單位失敗,因為設定: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 嘗試取得目前的系統狀態時,設定單位失敗。 嘗試套用所需的狀態時,組態單位失敗。 嘗試測試目前的系統狀態時,組態單位失敗。 設定單位失敗,因為發生內部錯誤: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 設定單位失敗,因為先決條件無效: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 設定單位失敗,因為系統狀態: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 嘗試執行時設定單位失敗: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 組態包含識別碼 '{0}' 多次。 {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. 在組態中找不到相依性 '{0}'。 {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. 已手動略過此設定單位。 設定單位的模組可在多個具有相同版本的位置使用。 載入設定單位的模組失敗。 找到多個符合設定單位的項目;指定模組以選取正確的項目。 找不到設定單位。 設定單位未如預期地存在於模組中。 未執行此設定單位,因為相依性失敗或未執行。 未執行此設定單位,因為判斷提示失敗或為 False。 組態單位在執行期間傳回非預期的結果。 無法執行此設定單位,原因不明: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. 欄位'{0}'具有不正確值: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. 欄位 [{0}] 遺失或為空白。 {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. 請參閱檔案中的行 {0}、欄{1}。 {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. 正在取消操作 安裝適用於 AppInstaller 的虛設常式封裝 安裝適用於 AppInstaller 的完整封裝 啟用擴充功能。需要商店存取權。 選項 '--enable' 和 '--disable' 不能與其他引數一起使用。 {Locked="--enable", "--disable"} 正在啟用擴充功能。需要商店存取權。 未啟用擴充功能。執行「winget configure --enable」以啟用它們。 {Locked="winget configure --enable"} 擴充功能已啟用。 停用擴充功能。需要商店存取權。 正在停用擴充功能。需要商店存取權。 擴充功能已停用。 略過處理封裝相依性和 Windows 功能 已略過相依性。 無法重新整理流程的 PATH 變數。取決於變更 PATH 變數的後續安裝可能會失敗。 {Locked="PATH"} 部分設定單位在測試其狀態時失敗。 系統處於描述的組態狀態。 組態狀態未進行測試。 系統未處於描述的組態狀態。 未預期的測試結果: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). 您是否已檢閱過組態,是否要繼續針對系統繼續驗證? 組態檔不是有效的 YAML 檔案。 {Locked="YAML"} YAML is a file format name. 此組態單位是相依性循環圖的一部分。 驗證找不到問題。 設定單位只能做為發行前版本使用,但不會在組態中標示。將 'allowPrerelease: true' 新增至 'directives'。 {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. 已在本機找到組態單元,但在任何已設定的目錄中找不到。在套用設定之前,請確保在任何系統上已存有此組態。 組態單位無法公開使用; 請確認任何會使用此設定的人員都可存取此設定。 未提供模組。指定模組可改善效能並防止未來名稱衝突。 從選取的套件下載安裝程式,可能是透過搜尋已設定的來源或直接從資訊清單找到。根據預設,查詢必須不區分大小寫地符合封裝的識別碼、名稱或 Moniker。傳遞其他欄位的適當選項即可使用。根據預設,下載命令會將適當的安裝程式下載到使用者的 [下載] 資料夾。 從指定的封裝下載安裝程式 下載安裝程式的目錄 正在下載相依性: 已下載安裝程式: {0} {Locked="{0}"} Full path of the downloaded installer. 選取安裝程式類型 安裝程式下載 禁止下載安裝程式以供稍後離線安裝。 '--module-path allusers' 需要系統管理員權限才能執行。 {Locked="--module-path allusers"} 在本機電腦上指定位置以儲存模組。預設 %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} '--module-path' 值必須是 'currentuser'、'allusers'、'default' 或絕對路徑。 {Locked="{--module-path}, {currentuser}, {allusers}, {default}} 啟用 Windows 封裝管理員設定 擷取錯誤的相關信息。指定數位后,輸出將包含錯誤的詳細數據,包括符號名稱,如果是 Winget 特定的錯誤。指定字串時,會搜尋 winget 特定錯誤以尋找此值。 取得關於錯誤的資訊 要在錯誤資訊中搜尋的值 指定的數位太大,無法成為 HRESULT。 未知錯誤代碼 內部錯誤 命令列引數無效 執行命令失敗 開啟資訊清單失敗 已收到取消訊號 ShellExecute 執行失敗 無法處理指令清單。指令清單版本高於支援。請更新用戶端。 下載安裝程序失敗 無法寫入索引;這是較高的架構版本 索引已損毀 設定的來源資訊已損毀 已設定來源名稱 來源類型無效 MSIX 檔案是套件組合,而不是套件 遺失來源所需的資料 沒有任何安裝程式適用於目前的系統 安裝程式檔案的哈希不符合指令清單 來源名稱不存在 來源位置已在另一個名稱下設定 找不到封裝 未設定任何來源 找到多個符合準則的封裝 找不到符合準則的資訊清單。 無法從來源套件取得公用資料夾 命令需要系統管理員權限才能執行 來源位置不安全 原則已封鎖 Microsoft Store 用戶端 原則已封鎖 Microsoft Store 應用程式 功能目前正在開發中。您可以使用 winget 設定來啟用它。 安裝 Microsoft Store 應用程式失敗 無法執行自動完成 無法初始化 YAML 剖析器 遇到無效的 YAML 金鑰 遇到重複的 YAML 金鑰 YAML 作業無效 無法建置 YAML 文件 YAML 發射器狀態無效 無效的 YAML 資料 LibYAML 錯誤 資訊清單驗證成功,但出現警告 資訊清單驗證失敗 資訊清單無效 找不到適用的更新 winget 升級 -- 全部完成,但有失敗部分 安裝程式的安全性檢查失敗 下載大小不符合預期的內容長度 找不到解除安裝命令 執行解除安裝命令失敗 ICU 中斷列舉程式錯誤 ICU 案例對應錯誤 ICU Regex 錯誤 無法安裝一或多個匯入的套件 找不到一或多個要求的套件 無效的 Json 檔案 來源位置非遠端 不支援設定的剩餘來源 剩餘來源傳回的資料無效 [群組原則] 封鎖作業 Rest API 內部錯誤 剩餘來源 URL 無效 剩餘 API 傳回不支援的 MIME 類型 休息來源合約版本無效 來源資料已損毀或遭竄改 從資料流讀取時發生錯誤 未同意的封裝合約。 讀取輸入提示時發生錯誤 一或多個來源不支持搜尋要求 找不到其餘的 API 端點。 無法開啟來源。 未同意的來源合約 標頭大小超過允許的1024個字元限制。請縮減大小,然後再試一次。 遺失資源檔案 執行 MSI 安裝失敗 msiexec 的引數無效 無法開啟一或多個來源 無法驗證相依性 一或多個封裝遺失 無效資料行欄 升級版本不比安裝的版本新 升級版本不明,且未指定覆寫 ICU 轉換錯誤 無法安裝可攜式封裝 磁碟區不支援重新分析點 來自不同來源的可攜式封裝已經存在。 無法建立符號連結。路徑指向目錄。 無法從系統管理員內容執行安裝程式。 無法解除安裝可攜式封裝 無法根據索引驗證 DisplayVersion 值。 不支援一個或多個引數。 SQLite 不允許內嵌 Null 字元 在封存中找不到巢狀安裝程式。 無法解壓縮封存。 提供的巢狀安裝程式相對檔案路徑無效。 伺服器證書不符合任何預期值。 必須提供安裝位置。 封存惡意代碼掃描失敗。 找到至少一個已安裝的套件版本。 封裝已經有釘選。 沒有適用於封裝的釘選。 無法開啟釘選資料庫。 一個或多個應用程式無法安裝 無法解除安裝一個或多個應用程式 一或多個查詢未傳回完全相符的專案 封裝有防止升級的 PIN。 目前安裝的套件是存根套件 已收到應用程式關機訊號 無法下載封裝相依性。 無法下載套件。禁止下載以進行離線安裝。 必要服務忙碌中或無法使用。請稍後再試。 提供的 GUID 未對應到有效的繼續狀態。 目前的用戶端版本不符合儲存狀態的用戶端版本。 恢復狀態資料無效。 無法開啟檢查點資料庫。 已超過最大恢復限制。 無效驗證資訊。 不支援驗證方法。 驗證失敗。 驗證失敗。需要互動式驗證。 驗證失敗。使用者已取消。 驗證失敗。已驗證的帳戶不是所需的帳戶。 應用程式目前正在執行。結束應用程式,然後再試一次。 另一個安裝已在進行中。請稍後再試。 一或多個檔案正在使用中。結束應用程式,然後再試一次。 您的系統遺漏此套件的相依性。 您的電腦上已經沒有空間。請釋出空間,然後再試一次。 記憶體不足,無法安裝。請關閉其他應用程式,然後再試一次。 此應用程式需要因特網連線。請連線到網路,然後再試一次。 此應用程式在安裝期間發生錯誤。連絡客戶支援。 重新啟動您的電腦以完成安裝。 安裝失敗。請重新啟動您的計算機,然後再試一次。 將重新開機您的電腦以完成安裝。 您取消了安裝。 已安裝此應用程式的另一個版本。 已安裝此應用程式的較新版本。 組織原則導致無法安裝。請連絡您的系統管理員。 無法安裝封裝相依性。 應用程式目前正由另一個應用程式使用中。 參數無效。 系統不支援封裝。 安裝程式不支持升級現有的封裝。 安裝失敗,出現自訂安裝程式錯誤。 找不到套件的應用程式和功能專案。 安裝位置不適用。 找不到安裝位置。 現有檔案的哈希不符。 找不到檔案。 找到檔案,但未檢查哈希。 無法存取檔案。 設定檔案無效。 YAML 語法無效。 設定欄位的類型無效。 設定的版本不明。 在套用設定時發生錯誤。 設定包含重複的識別碼。 設定缺少相依性。 設定具有未滿足的相依性。 設定單位的聲明失敗。 已手動略過設定。 使用者拒絕繼續執行。 相依性圖形包含無法解析的迴圈。 設定具有無效的欄位值。 設定缺少一個欄位。 部分設定單位在測試其狀態時失敗。 組態狀態未進行測試。 未安裝設定單位。 找不到設定單位。 找到多個符合設定單位的項目;指定模組以選取正確的項目。 嘗試取得目前的系統狀態時,設定單位失敗。 嘗試測試目前的系統狀態時,組態單位失敗。 嘗試套用所需的狀態時,組態單位失敗。 設定單位的模組可在多個具有相同版本的位置使用。 載入設定單位的模組失敗。 組態單位在執行期間傳回非預期的結果。 單位包含需要設定根目錄的設定。 設定處理器不支援此作業。 無法使用 已成功啟用 Windows 功能相依性 載入組態單位的模組失敗,因為它需要系統管理員許可權才能執行。 單位包含需要設定根目錄的設定。 載入組態單位的模組失敗,因為它需要系統管理員許可權才能執行。 傳入儲存命令的唯一標識符,以繼續執行先前儲存的命令。這可用來繼續可能因重新啟動而終止的已執行命令。 繼續執行先前儲存的命令。 要繼續之儲存狀態的唯一標識符 不支援從不同的用戶端版本繼續狀態: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. 恢復狀態不存在: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. 在恢復狀態中找不到任何資料。 此命令不支持恢復。 如果適用,則允許重新開機 正在起始重新開機,以完成作業... 無法起始重新開機。 繼續作業超過允許的 {0} 繼續() 限制。若要手動繼續,請執行命令 '{1}'。 {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. 略過繼續儲存狀態的限制 不支援 Uri 配置: {0} {Locked="{0}"} Error message displayed when the provided uri is not supported. {0} is a placeholder replaced by the provided uri. Uri 的格式不正確: {0} {Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri. 無法剖析 {0} 組態單位設定內容或設定內容是空的。 {Locked="{0}"} {0} is a placeholder replaced by the input winget configure resource unit type. {0} 組態單位遺漏必要的自變數: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. {0} 組態單位遺漏建議的自變數: {1} {Locked="{0},{1}"} {0} is a placeholder for the input winget configure resource unit type. {1} is placeholder for the missing arg. WinGetSource 組態單位與已知來源衝突: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. 第三方來源的 WinGetSource 組態單位判斷提示: {0} {Locked="WinGetSource,{0}"} {0} is a placeholder for the input winget source in the configuration unit settings. WinGetPackage 宣告 UseLatest 和 Version。套件識別碼: {0} {Locked="WinGetPackage,UseLatest,Version,{0}"} WinGetPackage 組態單位在來自第三方來源的封裝上宣告。套件標識碼: {0};來源: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 設定單元套件相依於先前未設定的第三方來源。套件標識碼: {0};來源: {1} {Locked="WinGetPackage,{0},{1}"} WinGetPackage 設定單元套件相依於第三方來源。建議您在 uni dependsOn 區段中宣告相依性。套件標識碼: {0};來源: {1} {Locked="WinGetPackage,dependsOn,{0},{1}"} 無法驗證 WinGetPackage 設定單元套件。來源開啟失敗。套件標識碼: {0};來源: {1} {Locked="WinGetPackage,{0},{1}"} 無法驗證 WinGetPackage 設定單元套件。找不到套件。套件識別碼: {0} {Locked="WinGetPackage,{0}"} 無法驗證 WinGetPackage 設定單元套件。找到多個套件。套件識別碼: {0} {Locked="WinGetPackage,{0}"} 無法驗證 WinGetPackage 設定單元套件。找不到套件版本。套件標識碼: {0};版本 {1} {Locked="WinGetPackage,{0},{1}"} 以特定版本指定的 WinGetPackage 設定單元封裝,其中只有一個封裝版本可用。封裝識別碼: {0};版本: {1} {Locked="WinGetPackage,{0},{1}"} 無法驗證 WinGetPackage 設定單元套件。套件識別碼: {0} {Locked="WinGetPackage,{0}"} 指定驗證視窗喜好設定 (silent、silentPreferred 或 interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. 指定要用於驗證的帳戶 無法新增來源。此 winget 版本不支援來源的驗證方法。嘗試升級至最新 winget 版本。 {Locked="winget"} {0} 來源需要驗證。必要時可能會顯示驗證提示。已驗證的資訊將與來源共用以取得存取授權。 {Locked="{0}"} 修復選取的套件,您可以搜尋已安裝的套件清單,或直接從指令清單找到該套件。根據預設,查詢必須不區分大小寫地符合封裝的標識碼、名稱或Moniker。傳遞其他欄位的適當選項即可使用。 id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. 修復選取的封裝 找不到此封裝的修復命令。請連絡套件發行者以取得支援。 使用中的安裝程序技術與目前安裝的版本不符。 找不到修復命令。 使用中的安裝程序技術不支持修復。 修復作業已成功完成。 已放棄修復 正在啟動封裝修復... 無法修復 Microsoft Store 封裝。錯誤碼: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. 修復作業失敗。 修復作業不適用。 沒有相符的封裝版本可從設定的來源使用。 目前的系統設定不支援修復此封裝。 使用中的安裝程序技術不支持修復。 以系統管理員許可權執行時,無法修復為用戶範圍安裝的套件。 不允許在使用者範圍內安裝的套件上修復涉及系統管理員許可權的作業。 修復失敗,結束代碼為: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. 已終止 SQLite 連線以避免損毀。 解除安裝所有版本 要執行的版本 已安裝此套件的多個版本。請精簡搜尋、傳遞 '--version' 自變數以選取,或傳遞 '--all-versions' 旗標以卸載所有專案。 {Locked="--version,--all-versions"} 啟用Windows 封裝管理員 Proxy 命令行選項 Describes a Group Policy that can enable the use of the --proxy option to set a proxy 設定要用於此執行的 Proxy 停用此執行的 Proxy 使用 無法重設 {0}。這個設定是由原則控制。如需詳細資訊,請連絡您的系統管理員。 {Locked="{0}"} The value will be replaced with the feature name 重設系統管理員設定 '{0}'。 {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. 無法設定 {0}。這個設定是由原則控制。如需詳細資訊,請連絡您的系統管理員。 {Locked="{0}"} The value will be replaced with the feature name 將系統管理員設定 '{0}' 設為 '{1}'。 {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced 要修改之設定的名稱 要為設定設定的值。 將系統管理員設定重設為其預設值。 將系統管理員設定重設為其預設值。 設定系統管理設定的值。 設定系統管理設定的值。 除非指定,否則將來源排除在探索之外 從探索中排除來源 (true 或 false) 偏激 來源的信任層級 (無或信任) 信任層級 無法取得 Microsoft Store 封裝類別目錄。 在套件目錄 Microsoft Store 找不到適用的 Microsoft Store 封裝。 無法取得 Microsoft Store 套件下載資訊。 找不到適用於下載Microsoft Store套件。 無法擷取 Microsoft Store 套件授權。 找不到適用的 Microsoft Store 套件。 已成功驗證 Microsoft Store 套件雜湊 Microsoft Store 套件雜湊不符 已下載 Microsoft Store套件: {0} {Locked="{0}"} Full path of the downloaded package. Microsoft Store 套件下載失敗: {0} {Locked="{0}"} Package name. Microsoft Store 套件下載完成 正在從 Microsoft Store 下載主要套件... 正在從 Microsoft Store 下載相依性套件... 正在擷取 Microsoft Store 套件下載資訊 無法擷取 Microsoft Store 套件下載資訊 正在擷取 Microsoft Store 套件授權 已儲存 Microsoft Store套件授權: {0} {Locked="{0}"} License file full path. 無法擷取 Microsoft Store 套件授權 Microsoft Store 套件下載不支援 --rename 引數。Microsoft Store 封裝將使用 Microsoft Store 目錄提供的名稱。 {Locked="--rename"} Microsoft Store套件下載需要 Microsoft Entra 標識符驗證。必要時可能會顯示驗證提示。將與Microsoft 服務共享已驗證的信息,以進行存取授權。對於Microsoft Store套件授權,Microsoft Entra 標識符帳戶必須是全域管理員、用戶系統管理員或授權系統管理員的成員。 {Locked="Global Administrator,User Administrator,License Administrator"} 略過擷取 Microsoft Store 套件離線授權 選取目標平台 正在新增設定檔: {0} {Locked="{0}"} 已成功匯出 正在取得組態設定... 至少必須提供 --packageId 和/或 --module with --resource。或使用 --all 匯出所有封裝組態。 {Locked="--packageId,--module, --resource, --all"} 自變數 --packageId、--module 和 --resource 不能與 --all 一起使用。 {Locked="--packageId,--module, --resource, --all"} 將設定資源匯出至組態檔。搭配 --all 使用時,導出所有封裝組態。搭配 --packageId 使用時,導出指定套件標識符的 WinGetPackage 資源。與 --module 和 --resource 搭配使用時,取得資源的設定,並將它匯出至組態檔。如果輸出組態檔已存在,會附加導出的設定資源。 {Locked="WinGetPackage,--packageId,--module, --resource"} 將設定資源匯出至設定檔。 要匯出之資源的模組。 要匯出的套件識別碼。 要匯出的設定資源。 匯出所有套件設定。 設定單位無法取得其屬性。 無法匯出設定。 設定 {0} {Locked="{0}"} 安裝 {0} {Locked="{0}"} 組態檔中存在的部分數據已針對此輸出截斷;檢查檔案內容以取得完整內容。 <此值已被截斷; 檢查檔案內容以取得完整的文字> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. 顯示已套用至系統之設定的高層級詳細數據。此數據可與 `configure` 命令搭配使用,以取得更多詳細數據。 {Locked="`configure`"} 顯示設定歷程記錄 歷程記錄中沒有設定。 從歷程記錄選取項目 找不到符合所提供數據的單一設定。提供明確符合所需設定的完整名稱或標識碼的一部分。 從歷程記錄中移除項目 首次套用 Column header for date values indicating when a configuration was first applied to the system. 識別碼 名稱 來源 路徑 找不到指定的設定。 已完成 The state of processing an item. 進行中 The state of processing an item. 擱置中 The state of processing an item. 未知 The state of processing an item. 監視設定狀態。 As in "to monitor the status of a configuration being applied". 已完成 The state of processing an item. 進行中 The state of processing an item. 擱置中 The state of processing an item. 已略過 The state of processing an item. 未知 The state of processing an item. 已啟動套用 When the configuration application started. 已結束套用 When the configuration application ended. 結果 詳細資料 狀態 The state of processing an item. 單位 套件與目前的 Windows 版本或平台不相容。 盡可能隱藏顯示初始設定詳細資料 可能會下載多個以不同平臺和架構為目標的Microsoft Store套件。--platform, --architecture 可以用來篩選封裝。 {Locked="--platform--architecture"} 無法擷取Microsoft Store套件授權。Microsoft Entra標識子帳戶不是全域管理員、用戶系統管理員或授權管理員的成員。使用 --skip-license 略過擷取Microsoft Store套件授權。 {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} Microsoft Store套件不支持下載。 Microsoft Store套件不支持下載。 無法擷取Microsoft Store套件授權。Microsoft Entra識別子帳戶沒有所需的許可權。 參數無法通過完整性邊界。 已下載零位元組安裝程式;請確認您的網路連線正常運作。 已下載零位元組安裝程式;請確認您的網路連線正常運作。 管理字型 使用子命令管理字型。可以安裝、升級或卸載類似封裝的字型。 家庭 表情 "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' 依家庭名稱篩選結果 "Face" represents the typeface of a font family such as 'Bold' or 'Italic' 路徑 未找到符合輸入條件的已安裝字型。 列出安裝的字型 列出所有已安裝的字體、所有字體檔案或特定字體的完整詳細資訊。 版本 設定模組 PowerShell Modules that are used for the Configuration feature 套件安裝程式需要驗證。必要時可能會顯示驗證提示。已驗證的資訊將會與安裝程序下載 URL 共用。 無法下載安裝程式。此 winget 版本不支援安裝程式下載驗證方法。嘗試升級至最新 winget 版本。 {Locked="winget"} 無法下載安裝程式。驗證失敗。 指定設定處理器的路徑 DSC v3 資源命令 DSC stands for "Desired State Configuration". It should already have a locked translation. 這裡的子命令實作 Desired State Configuration (DSC) v3 資源,以設定 winget 和封裝。 取得資源狀態 設定資源狀態 描述必要的狀態變更 測試資源狀態 刪除資源狀態 取得所有狀態執行個體 驗證群組內容 解決外部狀態 執行配接器 取得資源結構描述 取得資源資訊清單 管理套件狀態 透過 winget 管理套件。 指出實例是否應存在。 指出實例是否處於預期狀態。 套件的識別碼。 套件的來源。 套件版本。 用來比對標識碼與封裝的方法。 指示應安裝最新可用的套件版本。 必要時要使用的安裝模式。 套件的目標範圍。 指出是否接受來源和套件的合約。 管理使用者設定檔案 管理 winget 的使用者設定。 設定 JSON 檔案內容。 用來套用設定的動作。 已記錄相互關聯的值 管理來源設定 管理 winget 的來源。 要用於來源的名稱。 來源的引數。 來源的類型。 來源的信任層級。 呼叫未指定來源時是否包含來源。 正在套用設定單位... 正在匯出設定單位... 正在取得設定單位處理器... 請確認匯出所需的模組[{0}] {Locked="{0}"} 無法測試或取得必要的模組。將不會匯出相關設定。 匯出[{0}] {Locked="{0}"} 無法匯出資源。 無法取得單元處理器。將不會匯出個別套件設定。 要寫入結果的目錄 在系統上找不到 Desired State Configuration 封裝。正在安裝套件... 無法安裝 Desired State Configuration 封裝。手動安裝套件,或透過 --processor-path 自變數提供 dsc.exe 路徑。 {Locked="dsc.exe","--processor-path"} 包含系統管理員設定及其值的物件。 管理系統管理員設定 管理 winget 的系統管理員設定。 重設所有系統管理員設定 已重設所有系統管理員設定。 MCP 資訊 MCP stands for Model Context Protocol and should probably remain as-is MCP (模型內容通訊協定) Windows 封裝管理員的資訊。 若要使用 MCP 用戶端手動設定 Windows 封裝管理員 MCP 伺服器,請在 `servers` 物件中使用下列 JSON 片段: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. 啟用 Windows 封裝管理員 MCP 伺服器 目標作業系統版本 無法安裝一或多種字型。 不支援字型檔案,無法安裝。 字型套件中的一或多個字型不受支援,無法安裝。 字型安裝失敗;正在清除。 已安裝字型。 套件識別碼 支援 WinGet 標題 找不到字型檔案。 字體解除安裝失敗。字型可能狀態不佳。重新啟動後嘗試解除安裝。 字型驗證失敗。 字型復原失敗。字型可能狀態不佳。重新啟動後嘗試解除安裝。 顯示字型檔案詳細資訊。 字型套件驗證失敗。 嘗試復原失敗的字型安裝失敗。可能需要重新啟動才能成功卸載字體。 字型套件解除安裝失敗。這通常是由於系統或應用程式正在使用字型。重新啟動電腦後卸載可能會成功。 確定 "OK" means the font is in a good healthy state 損毀 "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. 狀態 未知 已安裝字型套件。 顯示套件的詳細資訊 Providing this argument causes the CLI to output additional details about installed application packages. 管道: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). 本機識別碼: Precedes a value that is the unique identifier for the installed package on the local system. 套件系列名稱: Precedes a value that is the APPX/MSIX package family name of the installed package. 產品碼: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. 升級代碼: Precedes a value that is the MSI Upgrade Code for the installed package. 已安裝範圍: Precedes a value that is the scope of the installation of the package (ex. user, machine). 安裝結構: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). 安裝地區設定: Precedes a value that is the locale of the installed package. 安裝位置: Precedes a value that is the directory path to the installed package. 原始來源: Precedes a value that names the package source where the installed package originated from. 可用的升級: Precedes a list of package upgrades available for the installed package. 安裝程式類別: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). 舊值 Column title for listing edit changes. 新值 Column title for listing the new value. ================================================ FILE: Localization/Settings/LocConfig.xml ================================================  ================================================ FILE: NOTICE ================================================ NOTICES AND INFORMATION Do Not Translate or Localize This software incorporates material from third parties. Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, platform, and version number, to: Source Code Compliance Team Microsoft Corporation One Microsoft Way Redmond, WA 98052 USA Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License. --------------------------------------------------------- Castle.Core 5.1.0 - Apache-2.0 (c) 2004-2022 Castle Project - http://www.castleproject.org Copyright 2004-2021 Castle Project - http://www.castleproject.org Copyright (c) 2004-2022 Castle Project - http://www.castleproject.org Copyright 2004-2021 Castle Project - http://www.castleproject.org/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --------------------------------------------------------- --------------------------------------------------------- NuGet.Frameworks 5.11.0 - Apache-2.0 (c) Microsoft Corporation. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --------------------------------------------------------- --------------------------------------------------------- openssl/openssl 01d5e2318405362b4de5e670c90d9b40a351d053 - Apache-2.0 (c) 2005 WISeKey SA1 Copyright 2005 Nokia Copyright 2021 UnionTech Copyright 2021- IBM Inc. Copyright IBM Corp. 2018 Copyright IBM Corp. 2019 Copyright Nokia 2007-2018 Copyright Nokia 2007-2019 Copyright Nokia 2007-2020 Copyright Siemens AG 2020 Copyright 2011 Google Inc. Copyright 2017 Ribose Inc. (c) 2013 ATT Wi-Fi Services Copyright 2017 BaishanCloud Copyright 2013 M. J. Dominus Copyright 2019 Red Hat, Inc. Copyright IBM Corp. 2018-2019 Copyright Patrick Powell 1995 Copyright (c) 2011, RTFM, Inc. Copyright Siemens AG 2015-2019 Copyright Siemens AG 2015-2020 Copyright Siemens AG 2015-2022 Copyright Siemens AG 2018-2020 Copyright Siemens AG 2019-2022 Copyright 2016 VMS Software, Inc. Copyright (c) 2004, EdelKey Project Copyright (c) 2015 CloudFlare, Inc. Copyright (c) 2015, CloudFlare, Inc. Copyright (c) 2005 WISeKey SA1 InterU Copyright (c) 2012, Intel Corporation Copyright (c) 2014, Intel Corporation Copyright (c) 2020, Intel Corporation Copyright (c) 2021, Intel Corporation Copyright (c) 2002 The OpenTSA Project Copyright 1998-$YEAR The OpenSSL Authors Copyright 2004-2014, Akamai Technologies Copyright 2023The OpenSSL Project Authors Copyright (c) 2020-2021, Intel Corporation Copyright 2014 Cryptography Research, Inc. Copyright 2015 Cryptography Research, Inc. Copyright 2016 Cryptography Research, Inc. Copyright 2016 The OpenSSL Project Authors Copyright 2017 The OpenSSL Project Authors Copyright 2018 The OpenSSL Project Authors Copyright 2019 The OpenSSL Project Authors Copyright 2020 The OpenSSL Project Authors Copyright 2021 The OpenSSL Project Authors Copyright 2022 The OpenSSL Project Authors Copyright 2023 The OpenSSL Project Authors Copyright (c) 1998-2023 The OpenSSL Project Copyright (c) 2006, Network Resonance, Inc. Copyright (c) 2012-2014 Daniel J. Bernstein copyrighted by the Free Software Foundation Copyright (c) 2012-2016 Jean-Philippe Aumasson Copyright 1995-2016 The OpenSSL Project Authors Copyright 1995-2017 The OpenSSL Project Authors Copyright 1995-2018 The OpenSSL Project Authors Copyright 1995-2019 The OpenSSL Project Authors Copyright 1995-2020 The OpenSSL Project Authors Copyright 1995-2021 The OpenSSL Project Authors Copyright 1995-2022 The OpenSSL Project Authors Copyright 1995-2023 The OpenSSL Project Authors Copyright 1998-2016 The OpenSSL Project Authors Copyright 1998-2017 The OpenSSL Project Authors Copyright 1998-2020 The OpenSSL Project Authors Copyright 1998-2021 The OpenSSL Project Authors Copyright 1998-2022 The OpenSSL Project Authors Copyright 1998-2023 The OpenSSL Project Authors Copyright 1999-2016 The OpenSSL Project Authors Copyright 1999-2018 The OpenSSL Project Authors Copyright 1999-2020 The OpenSSL Project Authors Copyright 1999-2021 The OpenSSL Project Authors Copyright 1999-2022 The OpenSSL Project Authors Copyright 1999-2023 The OpenSSL Project Authors Copyright 2000-2016 The OpenSSL Project Authors Copyright 2000-2017 The OpenSSL Project Authors Copyright 2000-2018 The OpenSSL Project Authors Copyright 2000-2019 The OpenSSL Project Authors Copyright 2000-2020 The OpenSSL Project Authors Copyright 2000-2021 The OpenSSL Project Authors Copyright 2000-2022 The OpenSSL Project Authors Copyright 2000-2023 The OpenSSL Project Authors Copyright 2001-2016 The OpenSSL Project Authors Copyright 2001-2017 The OpenSSL Project Authors Copyright 2001-2018 The OpenSSL Project Authors Copyright 2001-2020 The OpenSSL Project Authors Copyright 2001-2021 The OpenSSL Project Authors Copyright 2001-2022 The OpenSSL Project Authors Copyright 2001-2023 The OpenSSL Project Authors Copyright 2002-2016 The OpenSSL Project Authors Copyright 2002-2018 The OpenSSL Project Authors Copyright 2002-2020 The OpenSSL Project Authors Copyright 2002-2021 The OpenSSL Project Authors Copyright 2002-2022 The OpenSSL Project Authors Copyright 2002-2023 The OpenSSL Project Authors Copyright 2003-2021 The OpenSSL Project Authors Copyright 2003-2022 The OpenSSL Project Authors Copyright 2003-2023 The OpenSSL Project Authors Copyright 2004-2016 The OpenSSL Project Authors Copyright 2004-2017 The OpenSSL Project Authors Copyright 2004-2018 The OpenSSL Project Authors Copyright 2004-2020 The OpenSSL Project Authors Copyright 2004-2021 The OpenSSL Project Authors Copyright 2004-2022 The OpenSSL Project Authors Copyright 2004-2023 The OpenSSL Project Authors Copyright 2005-2016 The OpenSSL Project Authors Copyright 2005-2018 The OpenSSL Project Authors Copyright 2005-2020 The OpenSSL Project Authors Copyright 2005-2021 The OpenSSL Project Authors Copyright 2005-2022 The OpenSSL Project Authors Copyright 2005-2023 The OpenSSL Project Authors Copyright 2006-2016 The OpenSSL Project Authors Copyright 2006-2017 The OpenSSL Project Authors Copyright 2006-2018 The OpenSSL Project Authors Copyright 2006-2020 The OpenSSL Project Authors Copyright 2006-2021 The OpenSSL Project Authors Copyright 2006-2022 The OpenSSL Project Authors Copyright 2006-2023 The OpenSSL Project Authors Copyright 2007-2016 The OpenSSL Project Authors Copyright 2007-2018 The OpenSSL Project Authors Copyright 2007-2019 The OpenSSL Project Authors Copyright 2007-2020 The OpenSSL Project Authors Copyright 2007-2021 The OpenSSL Project Authors Copyright 2007-2022 The OpenSSL Project Authors Copyright 2007-2023 The OpenSSL Project Authors Copyright 2008-2016 The OpenSSL Project Authors Copyright 2008-2018 The OpenSSL Project Authors Copyright 2008-2020 The OpenSSL Project Authors Copyright 2008-2021 The OpenSSL Project Authors Copyright 2008-2022 The OpenSSL Project Authors Copyright 2008-2023 The OpenSSL Project Authors Copyright 2009-2020 The OpenSSL Project Authors Copyright 2009-2021 The OpenSSL Project Authors Copyright 2009-2022 The OpenSSL Project Authors Copyright 2009-2023 The OpenSSL Project Authors Copyright 2010-2016 The OpenSSL Project Authors Copyright 2010-2020 The OpenSSL Project Authors Copyright 2010-2021 The OpenSSL Project Authors Copyright 2010-2022 The OpenSSL Project Authors Copyright 2010-2023 The OpenSSL Project Authors Copyright 2011-2016 The OpenSSL Project Authors Copyright 2011-2020 The OpenSSL Project Authors Copyright 2011-2021 The OpenSSL Project Authors Copyright 2011-2022 The OpenSSL Project Authors Copyright 2011-2023 The OpenSSL Project Authors Copyright 2012, Samuel Neves Copyright 2012-2016 The OpenSSL Project Authors Copyright 2012-2020 The OpenSSL Project Authors Copyright 2012-2021 The OpenSSL Project Authors Copyright 2012-2022 The OpenSSL Project Authors Copyright 2012-2023 The OpenSSL Project Authors Copyright 2013-2017 The OpenSSL Project Authors Copyright 2013-2018 The OpenSSL Project Authors Copyright 2013-2020 The OpenSSL Project Authors Copyright 2013-2021 The OpenSSL Project Authors Copyright 2013-2022 The OpenSSL Project Authors Copyright 2013-2023 The OpenSSL Project Authors Copyright 2014-2016 Cryptography Research, Inc. Copyright 2014-2016 The OpenSSL Project Authors Copyright 2014-2017 The OpenSSL Project Authors Copyright 2014-2018 The OpenSSL Project Authors Copyright 2014-2020 The OpenSSL Project Authors Copyright 2014-2021 The OpenSSL Project Authors Copyright 2014-2022 The OpenSSL Project Authors Copyright 2014-2023 The OpenSSL Project Authors Copyright 2015-2016 Cryptography Research, Inc. Copyright 2015-2016 The OpenSSL Project Authors Copyright 2015-2017 The OpenSSL Project Authors Copyright 2015-2018 The OpenSSL Project Authors Copyright 2015-2020 The OpenSSL Project Authors Copyright 2015-2021 The OpenSSL Project Authors Copyright 2015-2022 The OpenSSL Project Authors Copyright 2015-2023 The OpenSSL Project Authors Copyright 2016-2016 The OpenSSL Project Authors Copyright 2016-2017 The OpenSSL Project Authors Copyright 2016-2018 The OpenSSL Project Authors Copyright 2016-2019 The OpenSSL Project Authors Copyright 2016-2020 The OpenSSL Project Authors Copyright 2016-2021 The OpenSSL Project Authors Copyright 2016-2022 The OpenSSL Project Authors Copyright 2016-2023 The OpenSSL Project Authors Copyright 2017-2018 The OpenSSL Project Authors Copyright 2017-2019 The OpenSSL Project Authors Copyright 2017-2020 The OpenSSL Project Authors Copyright 2017-2021 The OpenSSL Project Authors Copyright 2017-2022 The OpenSSL Project Authors Copyright 2017-2023 The OpenSSL Project Authors Copyright 2018-2019 The OpenSSL Project Authors Copyright 2018-2020 The OpenSSL Project Authors Copyright 2018-2021 The OpenSSL Project Authors Copyright 2018-2022 The OpenSSL Project Authors Copyright 2018-2023 The OpenSSL Project Authors Copyright 2019-2020 The OpenSSL Project Authors Copyright 2019-2021 The OpenSSL Project Authors Copyright 2019-2022 The OpenSSL Project Authors Copyright 2019-2023 The OpenSSL Project Authors Copyright 2020-2021 The OpenSSL Project Authors Copyright 2020-2022 The OpenSSL Project Authors Copyright 2020-2023 The OpenSSL Project Authors Copyright 2021-2022 The OpenSSL Project Authors Copyright 2021-2023 The OpenSSL Project Authors Copyright 2022-2023 The OpenSSL Project Authors Copyright 20xx-20yy The OpenSSL Project Authors Copyright (c) 2002, Oracle and/or its affiliates Copyright (c) 2017, Oracle and/or its affiliates Copyright (c) 2018, Oracle and/or its affiliates Copyright (c) 2019, Oracle and/or its affiliates Copyright 1995-$YEAR The OpenSSL Project Authors Copyright 1998-$YEAR The OpenSSL Project Authors Copyright 1999-$YEAR The OpenSSL Project Authors Copyright 2000-$YEAR The OpenSSL Project Authors Copyright 2020-$YEAR The OpenSSL Project Authors Copyright (c) 1989 Free Software Foundation, Inc. Copyright 2017 Ribose Inc. (https://www.ribose.com) Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson Copyright (c) 2008 Andy Polyakov Copyright 2021 UnionTech (https://www.uniontech.com) Copyright (c) 2018-2019, Oracle and/or its affiliates Copyright (c) 2018-2020, Oracle and/or its affiliates Copyright (c) 2019-2020, Oracle and/or its affiliates Copyright (c) 2013 by Mark Jason Dominus Copyright (c) 2017 National Security Research Institute copyright (c) 2013 by Mark Jason Dominus Copyright (c) 2004, Richard Levitte Copyright (c) 2013-2014 Timo Teras Copyright (c) 2007 KISA(Korea Information Security Agency) Copyright (c) 2004, 2018, Richard Levitte Copyright (c) 2016 Viktor Dukhovni Copyright 2006 NTT (Nippon Telegraph and Telephone Corporation) Copyright (c) 2005 WISeKey SA1 Internat(onal1)0 WISeKey CertifyID Advanced G1 CA0 Copyright (c) 2005 WISeKey SA1 International1 0 WISeKey CertifyID Advanced G1 CA0 Copyright (c) 2004 Kungliga Tekniska Hogskolan (Royal Institute of Technology, Stockholm, Sweden) Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS --------------------------------------------------------- --------------------------------------------------------- StyleCop.Analyzers 1.1.118 - Apache-2.0 AND MIT copyright company PlaceholderCompany Copyright (c) Copyright (c) 2015 Dennis Fischer Copyright (c) 2017 Marcos Lopez C. Copyright 2014 Giovanni Bassi and Elemar Jr Copyright (c) Tunnel Vision Laboratories, LLC. Copyright Tunnel Vision Laboratories, LLC 2015 copyright tag should contain a non-empty company Copyright 2015 Tunnel Vision Laboratories, LLC StyleCop DotNetAnalyzers Roslyn Diagnostic Copyright (c) Tunnel Vision Laboratories, LLC. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use these files except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --- This project uses other open source projects, which are used under the terms of the following license(s). .NET Compiler Platform ("Roslyn") Copyright Microsoft. Licensed under the Apache License, Version 2.0 (the "License"); you may not use these files except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Code Cracker Copyright 2014 Giovanni Bassi and Elemar Jr. Licensed under the Apache License, Version 2.0 (the "License"); you may not use these files except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. LightJson Copyright (c) 2017 Marcos López C. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Markdig.Signed 0.33.0 - BSD-2-Clause Copyright (c) . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Web.WebView2 1.0.3485.44 - BSD-3-Clause (c) MEE. (c) (c) S (c) Gyw A (c) Ono Q (c) 3Y Thaeh (c) Dyma (c) (c) Microsoft 2025 (c) Microsoft Corporation Copyright Sam Harwell 2011 Copyright Microsoft Corporation Copyright (c) Microsoft Corporation Copyright (c) 2011 The ANTLR Project Copyright (C) Microsoft Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of Microsoft Corporation, or the names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------- --------------------------------------------------------- Moq 4.18.2 - BSD-3-Clause Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors Copyright (c) . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------- --------------------------------------------------------- curl/curl 83bedbd730d62b83744cc26fa0433d3f6e2e4cd6 - BSD-3-Clause AND BSD-4-Clause-UC AND ISC AND curl Copyright (C) David Shaw Copyright (C) Howard Chu Copyright (C) Max Dymond (c) ! ISCNTRL (c) ISSPACE Copyright (C) Jeroen Ooms Copyright (C) Mark Gaiser Copyright (c) Evgeny Grin Daniel Stenberg, , et al. Copyright (C) Dan Fandrich Copyright (C) Dorian Craps Copyright (C) Jan Venekamp Copyright (c) Dan Fandrich Copyright (C) Dmitry Karpov Copyright (C) Jay Satiro, . Copyright (C) John Malmberg Copyright (C) Red Hat, Inc. Copyright (c) John Malmberg Copyright (c) Red Hat, Inc. copyright When contributing Copyright (C) Björn Stenberg Copyright (C) Michael Forney Copyright (C) Steve Holme, . Copyright (C) Viktor Szakats Copyright (c) Viktor Szakats Copyright (C) Daniel Stenberg Copyright (c) Daniel Fandrich Copyright (c) Daniel Stenberg Copyright (C) Nicolas Sterchele Copyright (C) " CURL_COPYRIGHT "\0" Copyright (C) Jacob Hoffman-Andrews Copyright (C) 2006-2022 wolfSSL Inc. Copyright (C) James Fuller, , et al. Copyright (C) Linus Nielsen Feltzing Copyright (c) 2001 Alexander Peslyak Copyright (c) 2006-2022 wolfSSL Inc. Copyright (C) Daniel Fandrich, et al. Copyright (C) Vijay Panghal, , et al. Copyright (C) " LIBCURL_COPYRIGHT "\0" Copyright (C) Daniel Fandrich, , et al. Copyright (C) Daniel Stenberg, , et al. Copyright (C) Simon Josefsson, , et al. Copyright (C) Evgeny Grin (Karlson2k), . 2022 Free Software Foundation Europe e.V. Copyright (c) Internet Software Consortium. Copyright Daniel Stenberg, Copyright (c) Howard Chu, Copyright (c) Mandy Wu, Copyright (C) Bill Nagel , Exacq Technologies Copyright (c) Bjorn Stenberg, Copyright (c) Jan Venekamp, Copyright (c) Mark Gaiser, Copyright (c) Daniel Stenberg, Copyright (c) Howard Chu, Copyright (c) Jay Satiro, Copyright (c) David Shaw Copyright (c) Jeroen Ooms Copyright (c) Hoi-Ho Chan, Copyright (c) Nick Zitzmann, Copyright (C) EdelWeb for EdelKey and OpenEvidence Copyright (c) EdelWeb for EdelKey and OpenEvidence Copyright (c) James Fuller, Copyright (c) Dmitry Karpov Copyright (c) Michael Forney, Copyright (c) 1996-2022 Internet Software Consortium Copyright (c) 1998, 1999 Kungliga Tekniska Hogskolan Copyright (c) Linus Nielsen Feltzing Copyright (c) Marc Hoersken, Copyright (c) Max Dymond, Copyright (c) Simon Josefsson, Copyright (c) Steve Holme, Copyright (C) 1996-2022 Internet Software Consortium. Copyright (c) Linus Nielsen Feltzing, Copyright (c) Mark Salisbury, Copyright (c) Vijay Panghal, Copyright (c) 2001-2004 Damien Miller Copyright (c) Daniel Fandrich, Copyright (c) Florin Petriuc, Copyright (c) Nicolas Sterchele, Copyright (c) 1983, Regents of the University of California Copyright (c) Dorian Craps, Copyright (c) Markus Moeller, Copyright (c) Jacob Hoffman-Andrews, Copyright (c) Bill Nagel , Exacq Technologies Copyright various years The Regents of the University of California Copyright (C) Daniel Stenberg et al. OS/400 version by P. Monnerat")' Copyright 2022 Free Software Foundation Europe e.V. Copyright (c) Daniel Stenberg, , and many contributors Copyright (c) 1996 - 2024, Daniel Stenberg, , and many contributors Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Hogskolan (Royal Institute of Technology, Stockholm, Sweden) COPYRIGHT AND PERMISSION NOTICE Copyright (c) 1996 - 2024, Daniel Stenberg, , and many contributors, see the THANKS file. All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. --------------------------------------------------------- --------------------------------------------------------- c-ares/c-ares fddf01938d3789e06cc1c3774e4cd0c7d2a89976 - HPND Copyright 2008 Google Inc. Copyright 2005, Google Inc. Copyright 2006, Google Inc. Copyright 2007, Google Inc. Copyright 2008, Google Inc. Copyright 2013, Google Inc. Copyright 2015, Google Inc. Copyright (c) 2012 Xan Lopez Copyright (c) 2021 Permission Copyright (c) 2012 Dan Winship Copyright 2005 Dominick Meglio Copyright (c) 2012 Paolo Borelli Copyright (c) 2021 by Brad House Copyright 1998 by Daniel Stenberg Copyright 2004 by Daniel Stenberg Copyright 2005 by Dominick Meglio Copyright (c) 2012 Christian Persch Copyright (c) 2017 by John Schember Copyright (c) 2014, 2015 Google Inc. Copyright (c) 2004 by Daniel Stenberg Copyright (c) 2005 by Dominick Meglio Copyright (c) 2008 by Daniel Stenberg Copyright (c) 2013 by Daniel Stenberg Copyright (c) 2016 by Daniel Stenberg Copyright (c) 2019 by Andrew Selivanov Copyright (c) 2012, 2016 Philip Withnall Copyright (c) 2015,2018 Bastien ROUCARIES Copyright (c) 2004-2009 by Daniel Stenberg Copyright (c) 2004-2010 by Daniel Stenberg Copyright (c) 2004-2011 by Daniel Stenberg Copyright (c) 2004-2017 by Daniel Stenberg Copyright (c) 2005 - 2010, Daniel Stenberg Copyright (c) 2005-2013 by Daniel Stenberg Copyright (c) 2007 - 2018, Daniel Stenberg Copyright (c) 2007-2013 by Daniel Stenberg Copyright (c) 2008-2010 by Daniel Stenberg Copyright (c) 2008-2013 by Daniel Stenberg Copyright (c) 2009-2013 by Daniel Stenberg Copyright (c) 2009-2021 by Daniel Stenberg Copyright (c) 2010-2012 by Daniel Stenberg Copyright (c) 2010-2013 by Daniel Stenberg Copyright (c) 2005, 2013 by Dominick Meglio Copyright (c) 2004 - 2011 by Daniel Stenberg Copyright (c) 2004 - 2012 by Daniel Stenberg Copyright (c) 2004 - 2013 by Daniel Stenberg Copyright (c) 2008 - 2009 by Daniel Stenberg Copyright (c) 2008 - 2012 by Daniel Stenberg Copyright (c) 2008 - 2013 by Daniel Stenberg Copyright (c) 2009 - 2021 by Daniel Stenberg Copyright (c) 2017 - 2018 by Christian Ammer Copyright (c) 2010 Jeremy Lal Copyright (c) 2012 Marko Kreen Copyright (c) 2012 Zack Weinberg Copyright (c) 2018 The Android Open Source Project Copyright 2020 by Copyright (c) 2011 Daniel Stenberg Copyright (c) 2013 Daniel Stenberg Copyright (c) 2008 Benjamin Kosnik Copyright (c) 1995, 1996, 1997, and 1998 WIDE Project Copyright (c) 2014 Mike Frysinger Copyright (c) 2008 Tom Howard Copyright (c) 2009 Tom Howard Copyright 2010 by Ben Greear Copyright 2020 Danny Sonnenschein Copyright (c) 1996,1999 by Internet Software Consortium Copyright (c) 1996-1999 by Internet Software Consortium Copyright (c) 2004 by Internet Systems Consortium, Inc. Copyright (c) 2009 by Jakub Hrozek Copyright (c) 2011 Daniel Richard G. Copyright (c) 2009 Allan Caffee Copyright (c) 2013 Roy Stogner Copyright (c) 2017 by John Schember Copyright (c) 2018 by John Schember Copyright (c) 2008 Steven G. Johnson Copyright 1998 by the Massachusetts Institute of Technology Copyright 2000 by the Massachusetts Institute of Technology Copyright 1998, 2000 by the Massachusetts Institute of Technology Copyright 1998, 2011 by the Massachusetts Institute of Technology Copyright (c) 1987-2001 The Regents of the University of California Copyright (c) 2008 John Darrington Copyright (c) 2015 Enrico M. Crisostomo Copyright 1998, 2011, 2013 by the Massachusetts Institute of Technology # c-ares license Copyright (c) 2007 - 2018, Daniel Stenberg with many contributors, see AUTHORS file. Copyright 1998 by the Massachusetts Institute of Technology. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. --------------------------------------------------------- --------------------------------------------------------- microsoft/xlang cfe510d0d2b07484fea2c6d77163de017738c100 - LicenseRef-scancode-generic-cla AND MIT (c) Microsoft Corporation Copyright 2017 Two Blue Cubes Ltd. Copyright (c) Microsoft Corporation Copyright (c) 2019 Two Blue Cubes Ltd. MIT License Copyright (c) Microsoft Corporation. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --------------------------------------------------------- --------------------------------------------------------- Microsoft.Windows.WDK.Win32Metadata 0.12.8-experimental - LicenseRef-scancode-ms-windows-sdk-win10-net-6 (c) Microsoft 2024 (c) Microsoft Corporation LicenseRef-scancode-ms-windows-sdk-win10-net-6 --------------------------------------------------------- --------------------------------------------------------- coverlet.collector 3.1.2 - MIT Copyright 2008 - 2018 Jb Evain Copyright Microsoft Corporation Copyright James Newton-King 2008 MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Humanizer.Core 2.14.1 - MIT Copyright .NET Foundation and Contributors Copyright (c) .NET Foundation and Contributors MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Json.More.Net 2.0.1.2 - MIT Copyright (c) 2024 Greg Dennis MIT License Copyright (c) 2024 Greg Dennis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- JsonPointer.Net 5.0.0 - MIT Copyright (c) 2024 Greg Dennis MIT License Copyright (c) 2024 Greg Dennis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- JsonSchema.Net 7.0.4 - MIT MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.ApplicationInsights 2.21.0 - MIT (c) Microsoft Corporation MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Bcl.AsyncInterfaces 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.CodeAnalysis.Analyzers 3.3.4 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation Copyright (c) 2013 Scott Kirkland Copyright (c) 2012-2014 Mehdi Khalili Copyright (c) 2013-2014 Omar Khudeira (http://omar.io) MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.CodeAnalysis.Common 4.9.2 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.CodeAnalysis.CSharp 4.9.2 - MIT (c) Microsoft Corporation Copyright (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors Copyright (c) Microsoft Corporation. Alle Rechte MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.AI.Abstractions 9.7.1 - MIT (c) Microsoft Corporation MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Configuration 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Configuration.Abstractions 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Configuration.Binder 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Configuration.CommandLine 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Configuration.EnvironmentVariables 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Configuration.FileExtensions 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Configuration.Json 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Configuration.UserSecrets 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.DependencyInjection 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.DependencyInjection.Abstractions 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Diagnostics 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Diagnostics.Abstractions 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.FileProviders.Abstractions 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.FileProviders.Physical 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.FileSystemGlobbing 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Hosting 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Hosting.Abstractions 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Logging 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Logging.Abstractions 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Logging.Configuration 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Logging.Console 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Logging.Debug 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Logging.EventLog 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Logging.EventSource 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.ObjectPool 8.0.10 - MIT Copyright Jorn Zaefferer (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright (c) 2015, Google Inc. Copyright (c) 2019 David Fowler Copyright (c) HTML5 Boilerplate Copyright 2019 The gRPC Authors Copyright (c) 2016 Richard Morris Copyright (c) 1998 John D. Polstra Copyright (c) 2017 Yoshifumi Kawai Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2013 - 2018 AngleSharp Copyright (c) 2000-2013 Julian Seward Copyright (c) 2011-2021 Twitter, Inc. Copyright (c) 2014-2018 Michael Daines Copyright (c) 1996-1998 John D. Polstra Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) .NET Foundation Contributors Copyright (c) 2011-2021 The Bootstrap Authors Copyright (c) 2019-2023 The Bootstrap Authors Copyright (c) .NET Foundation and Contributors Copyright (c) 2019-2020 West Wind Technologies Copyright (c) 2007 John Birrell (jb@freebsd.org) Copyright (c) 2011 Alex MacCaw (info@eribium.org) Copyright (c) Nicolas Gallagher and Jonathan Neal Copyright (c) 2010-2019 Google LLC. http://angular.io/license Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com) Copyright (c) 1989, 1993 The Regents of the University of California Copyright (c) 1990, 1993 The Regents of the University of California Copyright OpenJS Foundation and other contributors, https://openjsf.org Copyright (c) Sindre Sorhus (https://sindresorhus.com) MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Options 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Options.ConfigurationExtensions 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Extensions.Primitives 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Management.Infrastructure 3.0.0 - MIT (c) Microsoft Corporation MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Management.Infrastructure.CimCmdlets 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. CPowerShell's Microsoft.Management.Infrastructure.CimCmdlets project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Management.Infrastructure.Runtime.Unix 3.0.0 - MIT (c) Microsoft Corporation MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Msix.Utils 2.1.1 - MIT MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.NET.ILLink.Tasks 8.0.20 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright 2008 - 2018 Jb Evain Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.NETCore.Platforms 5.0.0 - MIT (c) Microsoft Corporation. Copyright (c) Andrew Arnott Copyright 2018 Daniel Lemire Copyright 2012 the V8 project Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. Copyright (c) 1998 Microsoft. To (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2017 Yoshifumi Kawai Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2012-2014, Yann Collet Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2018 Alexander Chermyanin Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) The Internet Society 1997. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.Commands.Diagnostics 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. PowerShell's Microsoft.PowerShell.Commands.Diagnostics project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.Commands.Management 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. PowerShell's Microsoft.PowerShell.Commands.Management project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.Commands.Utility 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. :PowerShell's Microsoft.PowerShell.Commands.Utility project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.ConsoleHost 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. PowerShell Host MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.CoreCLR.Eventing 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. :PowerShell's Microsoft.PowerShell.CoreCLR.Eventing project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.MarkdownRender 7.2.1 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. PowerShell's Markdown Rendering project PowerShell Markdown Renderer MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.Native 7.4.0 - MIT (c) Microsoft Corporation Copyright (c) by P.J. Plauger MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.SDK 7.4.6 - MIT (c) Microsoft Corporation Copyright (c) Microsoft Corporation (c) Microsoft Corporation. PowerShell SDK (c) Microsoft Corporation. PowerShell Host (c) Microsoft Corporation. 1PowerShell's System.Management.Automation project (c) Microsoft Corporation. :PowerShell's Microsoft.PowerShell.Commands.Utility project (c) Microsoft Corporation. PowerShell's Microsoft.PowerShell.Commands.Management project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.PowerShell.Security 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. 2PowerShell's Microsoft.PowerShell.Security project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Security.Extensions 1.2.0 - MIT MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Win32.Registry 4.7.0 - MIT (c) Microsoft Corporation. Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2007 James Newton-King Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Win32.Registry 5.0.0 - MIT (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2017 Yoshifumi Kawai Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2012-2014, Yann Collet Copyright (c) 1991-2020 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Win32.Registry.AccessControl 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Win32.SystemEvents 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Win32.SystemEvents 9.0.0-preview.6.24327.7 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Windows.Compatibility 8.0.10 - MIT (c) Microsoft Corporation MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Windows.CppWinRT 2.0.210503.1 - MIT (c) Microsoft Corporation. Copyright (c) Microsoft Corporation. MIT License Copyright (c) Microsoft Corporation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --------------------------------------------------------- --------------------------------------------------------- Microsoft.Windows.CppWinRT 2.0.240405.15 - MIT (c) Microsoft 2024 (c) Microsoft Corporation Copyright (c) Microsoft Corporation MIT License Copyright (c) Microsoft Corporation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --------------------------------------------------------- --------------------------------------------------------- Microsoft.Windows.CppWinRT 2.0.250303.1 - MIT (c) Microsoft 2025 (c) Microsoft Corporation Copyright (c) Microsoft Corporation MIT License Copyright (c) Microsoft Corporation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --------------------------------------------------------- --------------------------------------------------------- Microsoft.Windows.CsWin32 0.3.183 - MIT MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Windows.ImplementationLibrary 1.0.250325.1 - MIT (c) Microsoft 2025 Copyright (c) Microsoft (c) Microsoft Corporation Copyright (c) Microsoft Corporation Copyright (c) 2009-2014 by the contributors MIT License Copyright (c) Microsoft Corporation. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --------------------------------------------------------- --------------------------------------------------------- Microsoft.WindowsPackageManager.ComInterop 1.8.1911 - MIT (c) Microsoft Corporation MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.WSMan.Management 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. PowerShell's Microsoft.WSMan.Management project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.WSMan.Runtime 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. ,PowerShell's Microsoft.WSMan.Runtime project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- microsoft/correlationvector-cpp cf38d2b44baaf352509ad9980786bc49554c32e4 - MIT Copyright (c) Microsoft Corporation. MIT License Copyright (c) Microsoft Corporation. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --------------------------------------------------------- --------------------------------------------------------- microsoft/cpprestsdk 411a109150b270f23c8c97fa4ec9a0a4a98cdecf - MIT Copyright (c) Microsoft Copyright (c) 2014, Peter Thorson Copyright (c) Microsoft Corporation Copyright (c) 2011, Micael Hildenborg Copyright (c) 2004-2008 Rene Nyffenegger Copyright (c) 1999, 2002 Aladdin Enterprises Portions Copyright (c) Microsoft Corporation Copyright (c) 2006 Noel Llopis and Charles Nicholson Copyright (c) 2008-2009 Bjoern Hoehrmann C++ REST SDK The MIT License (MIT) Copyright (c) Microsoft Corporation All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- microsoft/sfs-client ff315ecfa2ef2953d8a808e51e8a61a4e0759180 - MIT Copyright (c) Microsoft Corporation Copyright (c) 2013-2022 Niels Lohmann Copyright (c) 2007 - 2023 Daniel Stenberg Copyright (c) 1998 Massachusetts Institute of Technology Copyright (c) 1996 - 2024, Daniel Stenberg, , and many contributors MIT License Copyright (c) Microsoft Corporation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --------------------------------------------------------- --------------------------------------------------------- ModelContextProtocol 0.3.0-preview.3 - MIT (c) Anthropic and Contributors MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- ModelContextProtocol.Core 0.3.0-preview.3 - MIT (c) Anthropic and Contributors MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- NETStandard.Library 2.0.0 - MIT copyright Unmanaged32Bit Required32Bit Copyright (c) .NET Foundation and Contributors The MIT License (MIT) Copyright (c) .NET Foundation and Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- NETStandard.Library 2.0.3 - MIT copyright Unmanaged32Bit Required32Bit Copyright (c) .NET Foundation and Contributors The MIT License (MIT) Copyright (c) .NET Foundation and Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Newtonsoft.Json 13.0.3 - MIT Copyright James Newton-King 2008 Copyright (c) 2007 James Newton-King Copyright (c) James Newton-King 2008 Copyright James Newton-King 2008 Json.NET The MIT License (MIT) Copyright (c) 2007 James Newton-King Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Newtonsoft.Json.Bson 1.0.1 - MIT Copyright James Newton-King 2017 Copyright (c) James Newton-King 2017 MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- nlohmann/json 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 - MIT (c) 2016 2012 Erik Edlund 2017 Georg Sauthoff 2018 Vitaliy Manushkin 2013-2022 Niels Lohmann 2013-2023 Niels Lohmann 2015-2017 Niels Lohmann 2016-2021 Evan Nemerson 2018 The Abseil Authors 2003-2022, LLVM Project. 2016-2021 Viktor Kirilov (c) 2013-2022 Niels Lohmann Copyright 2003-2022, LLVM Project Copyright 2015-2017 Niels Lohmann Copyright 2018 The Abseil Authors Copyright (c) 2009 Florian Loitsch Copyright 2016-2021 Viktor Kirilov Copyright (c) 2013-2022 Niels Lohmann Copyright (c) 2015-2017 Niels Lohmann copyright (c) 2013-2022 Niels Lohmann copyright (c) 2013-2023 Niels Lohmann Copyright (c) 2016-2023 Viktor Kirilov Copyright 2017 Georg Sauthoff copyright Niels Lohmann Copyright 2012 Erik Edlund Copyright Copyright (c) 2013 - 2023 Niels Lohmann Copyright 2018 Vitaliy Manushkin Copyright 2016-2021 Evan Nemerson Copyright (c) 2012, Erik Edlund Copyright 2013-2022 Niels Lohmann Copyright 2013-2023 Niels Lohmann Copyright 2020 Hannes Domani Copyright 2008-2009 Bjorn Hoehrmann Copyright (c) 2013-2019 Niels Lohmann Copyright (c) 2013-2022 Niels Lohmann Copyright (c) 2013-2022 Niels Lohmann (https://nlohmann.me) Copyright (c) 2020 Hannes Domani (https://github.com/ssbssa) Copyright 2009 Florian Loitsch Copyright (c) 2009 Florian Loitsch (https://florian.loitsch.com/) Copyright (c) 2007 Free Software Foundation, Inc. Copyright (c) 2008-2009 Bjorn Hoehrmann (http://bjoern.hoehrmann.de/) Copyright (c) 2008-2009 Bjorn Hoehrmann (https://bjoern.hoehrmann.de/) Copyright (c) 2008-2009 Bjoern Hoehrmann @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa MIT License Copyright (c) 2013-2022 Niels Lohmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- NUnit 3.12.0 - MIT (c) Microsoft 2024 copyright in NUnit Console (c) 2019 Charlie Poole, Rob Prouse Copyright (c) 2002-2014 Charlie Poole Copyright (c) 2000-2002 Philip A. Craig Copyright (c) 2019 Charlie Poole, Rob Prouse Copyright (c) 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov Copyright (c) 2019 Charlie Poole, Rob Prouse Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- NUnit3TestAdapter 3.15.1 - MIT (c) Microsoft 2025 Copyright 2008 - 2015 Jb Evain Copyright 2008 - 2018 Jb Evain (c) 2019 Charlie Poole, Rob Prouse Copyright (c) 2019 Charlie Poole, Rob Prouse Copyright 2011-2019 Charlie Poole, 2014-2019 Terje Sandstrom Copyright (c) 2011-2019 Charlie Poole, 2014-2019 Terje Sandstrom Copyright (c) 2011-2019 Charlie Poole, 2014-2019 Terje Sandstrom Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Octokit 4.0.3 - MIT Copyright GitHub 2017 Copyright GitHub 2017 GitHub API Octokit MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- PowerShellStandard.Library 5.1.1 - MIT (c) Microsoft Corporation. MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- ronomon/pure fd54913e65338e678440ae66b3b5022ab23b761b - MIT Copyright (c) 2003 Mark Adler Copyright (c) 2020 Ronomon CC Copyright 1995-2017 Mark Adler Copyright (c) 2003 Cosmin Truta Copyright (c) 1990-2000 Info-ZIP. Copyright (c) 1998 by Bob Dellaca Copyright (c) 1995-2003 Mark Adler Copyright (c) 1995-2008 Mark Adler Copyright (c) 1995-2016 Mark Adler Copyright (c) 1995-2017 Mark Adler Copyright (c) 2002-2013 Mark Adler Copyright (c) 2003 by Cosmin Truta Copyright (c) 2003-2010 Mark Adler Copyright (c) 2004-2017 Mark Adler Copyright (c) 1996 L. Peter Deutsch Copyright (c) 1997,99 Borland Corp. Copyright (c) 2003, 2012 Mark Adler Copyright (c) 2004, 2005 Mark Adler Copyright (c) 2004, 2010 Mark Adler Copyright (c) 2005, 2012 Mark Adler Copyright (c) 2011, 2016 Mark Adler Copyright (c) 2007-2008 Even Rouault Copyright (c) 1998-2005 Gilles Vollant Copyright (c) 2004, 2005 by Mark Adler Copyright (c) 1995-1998 Jean-loup Gailly Copyright (c) 1995-2003 Jean-loup Gailly Copyright (c) 1995-2003, 2010 Mark Adler Copyright (c) 1995-2005, 2010 Mark Adler Copyright (c) 1995-2011, 2016 Mark Adler Copyright (c) 1995-2016 Jean-loup Gailly Copyright (c) 1995-2017 Jean-loup Gailly Copyright (c) 1997,99 Borland Corporation Copyright (c) 1998 by Andreas R. Kleinert Copyright (c) 2002-2003 Dmitriy Anisimkov Copyright (c) 2002-2004 Dmitriy Anisimkov Copyright (c) 2003, 2012, 2013 Mark Adler Copyright (c) 2004, 2005, 2012 Mark Adler Copyright (c) 2004, 2008, 2012 Mark Adler Copyright (c) 2007, 2008, 2012 Mark Adler Copyright (c) 1998 by Jacques Nomssi Nzali (c) 1995-2017 Jean-loup Gailly & Mark Adler Copyright (c) 1995-2003 by Jean-loup Gailly Copyright (c) 1998-2010 - by Gilles Vollant (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2004, 2008, 2012, 2016 Mark Adler Copyright 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 1995-2006, 2011, 2016 Jean-loup Gailly Copyright (c) 1995-2016 Jean-loup Gailly, Mark Adler Copyright (c) 1995-2017 Jean-Loup Gailly, Mark Adler Copyright (c) 1995-2017 Jean-loup Gailly, Mark Adler Copyright (c) 1998,1999,2000 by Jacques Nomssi Nzali Copyright (c) 2003, 2005, 2008, 2010, 2012 Mark Adler Copyright (c) 2003 Chris Anderson Copyright (c) 1995-2003 Jean-loup Gailly and Mark Adler Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 1996 L. Peter Deutsch and Jean-Loup Gailly Copyright (c) 1998 Brian Raiter Copyright (c) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler Copyright (c) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly Copyright (c) 2009-2010 Mathias Svensson http://result42.com Copyright (c) 1998, 2007 Brian Raiter Copyright (c) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler Copyright (c) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler Copyright Jean-loup Gailly Osma Ahvenlampi Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll Copyright (c) 1997 Christian Michelsen Research AS Advanced Computing Copyright (c) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler Copyright (c) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson Copyright (c) 1995-1996 Jean-loup Gailly, Brian Raiter and Gilles Vollant Copyright (c) 1995-2010 Jean-loup Gailly, Brian Raiter and Gilles Vollant Copyright (c) 1998-2010 Gilles Vollant (minizip) http://www.winimage.com/zLibDll/minizip.html The MIT License (MIT) Copyright (c) 2020 Ronomon CC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- runtime.linux-arm.runtime.native.System.IO.Ports 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- runtime.linux-arm64.runtime.native.System.IO.Ports 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- runtime.linux-x64.runtime.native.System.IO.Ports 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- runtime.native.System.Data.SqlClient.sni 4.7.0 - MIT (c) Microsoft Corporation. Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2007 James Newton-King Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- runtime.native.System.IO.Ports 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- runtime.osx-arm64.runtime.native.System.IO.Ports 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- runtime.osx-x64.runtime.native.System.IO.Ports 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Semver 2.3.0 - MIT Copyright 2013 Max Hauser, Jeff Walker Copyright (c) 2013 Max Hauser, Jeff Walker Copyright 2013 Max Hauser, Jeff Walker VA SemVer MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Buffers 4.5.1 - MIT (c) Microsoft Corporation Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2015 The Chromium Authors Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.CodeDom 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Collections.Immutable 8.0.0 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ComponentModel.Composition 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ComponentModel.Composition.Registration 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Configuration.ConfigurationManager 8.0.1 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Data.Odbc 8.0.1 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Data.OleDb 8.0.1 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Data.SqlClient 4.8.6 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson Copyright (c) 2007 James Newton-King Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Diagnostics.DiagnosticSource 5.0.0 - MIT (c) Microsoft Corporation. Copyright (c) Andrew Arnott Copyright 2018 Daniel Lemire Copyright 2012 the V8 project Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. Copyright (c) 1998 Microsoft. To (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2017 Yoshifumi Kawai Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2012-2014, Yann Collet Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2018 Alexander Chermyanin Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) The Internet Society 1997. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Diagnostics.DiagnosticSource 8.0.1 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Diagnostics.DiagnosticSource 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Diagnostics.EventLog 6.0.0 - MIT (c) Microsoft Corporation. Copyright (c) Andrew Arnott Copyright 2018 Daniel Lemire Copyright 2012 the V8 project Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. Copyright (c) 1998 Microsoft. To (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2017 Yoshifumi Kawai Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2012-2014, Yann Collet Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2018 Alexander Chermyanin Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) The Internet Society 1997. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Diagnostics.EventLog 8.0.1 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Diagnostics.EventLog 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Diagnostics.PerformanceCounter 8.0.1 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.DirectoryServices 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.DirectoryServices.AccountManagement 8.0.1 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.DirectoryServices.Protocols 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Drawing.Common 8.0.19 - MIT (c) Microsoft Corporation Copyright (c) Sven Groot (Ookii.org) 2009 Copyright (c) .NET Foundation and Contributors The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Drawing.Common 9.0.0-preview.6.24327.6 - MIT (c) Microsoft Corporation Copyright (c) Sven Groot (Ookii.org) 2009 Copyright (c) .NET Foundation and Contributors The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Formats.Asn1 8.0.1 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.IO.Packaging 8.0.1 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.IO.Pipelines 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.IO.Ports 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Management 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Management.Automation 7.4.6 - MIT (c) Microsoft Corporation (c) Microsoft Corporation. 1PowerShell's System.Management.Automation project MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Memory 4.5.4 - MIT (c) Microsoft Corporation Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2015 The Chromium Authors Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Net.Http.WinHttpHandler 8.0.2 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Net.ServerSentEvents 10.0.0-preview.4.25258.110 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2024 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Numerics.Vectors 4.4.0 - MIT (c) Microsoft Corporation. (c) 1997-2005 Sean Eron Anderson. Copyright (c) 1991-2017 Unicode, Inc. Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Numerics.Vectors 4.5.0 - MIT (c) 2023 GitHub, Inc. (c) Microsoft Corporation Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2015 The Chromium Authors Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Private.ServiceModel 4.10.3 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Reflection.Context 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Reflection.DispatchProxy 4.7.1 - MIT (c) Microsoft Corporation. Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2007 James Newton-King Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Reflection.Metadata 1.6.0 - MIT (c) 2023 GitHub, Inc. (c) Microsoft Corporation Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2015 The Chromium Authors Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Reflection.Metadata 8.0.0 - MIT Copyright (c) 2021 Copyright (c) Six Labors Gets the Copyright Table (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Runtime.Caching 8.0.1 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Runtime.CompilerServices.Unsafe 4.5.3 - MIT (c) Microsoft Corporation. Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson. Copyright (c) 1991-2017 Unicode, Inc. Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Runtime.CompilerServices.Unsafe 6.0.0 - MIT (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2005-2020 Rich Felker Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2012-2014, Yann Collet Copyright (c) 1991-2020 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Runtime.WindowsRuntime 4.6.0 - MIT (c) Microsoft Corporation. Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2007 James Newton-King Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Runtime.WindowsRuntime.UI.Xaml 4.6.0 - MIT (c) Microsoft Corporation. Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2007 James Newton-King Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Security.AccessControl 5.0.0 - MIT (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2017 Yoshifumi Kawai Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2012-2014, Yann Collet Copyright (c) 1991-2020 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Security.AccessControl 6.0.1 - MIT (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2005-2020 Rich Felker Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2012-2014, Yann Collet Copyright (c) 1991-2020 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Security.Cryptography.Pkcs 8.0.1 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Security.Cryptography.ProtectedData 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Security.Cryptography.Xml 8.0.2 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Security.Permissions 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Security.Principal.Windows 5.0.0 - MIT (c) Microsoft Corporation. Copyright (c) Andrew Arnott Copyright 2018 Daniel Lemire Copyright 2012 the V8 project Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. Copyright (c) 1998 Microsoft. To (c) 1997-2005 Sean Eron Anderson. Copyright (c) 2017 Yoshifumi Kawai Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 2012-2014, Yann Collet Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2018 Alexander Chermyanin Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) The Internet Society 1997. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ServiceModel.Duplex 4.10.3 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ServiceModel.Http 4.10.3 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ServiceModel.NetTcp 4.10.3 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ServiceModel.Primitives 4.10.3 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ServiceModel.Security 4.10.3 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ServiceModel.Syndication 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.ServiceProcess.ServiceController 8.0.1 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Speech 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Text.Encoding.CodePages 8.0.0 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Text.Encodings.Web 8.0.0 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Text.Encodings.Web 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Text.Json 9.0.6 - MIT Copyright (c) 2021 Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright (c) 1998 Microsoft Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Portions (c) International Organization for Standardization 1986 Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Threading.AccessControl 9.0.0-preview.6.24327.7 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) 2022 FormatJS Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2015 Andrew Gallant Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2018 Nemanja Mijailovic Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2015-2018, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Web.Services.Description 4.10.3 - MIT (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- System.Windows.Extensions 8.0.0 - MIT Copyright (c) Six Labors (c) Microsoft Corporation Copyright (c) Andrew Arnott Copyright 2019 LLVM Project Copyright 2018 Daniel Lemire Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. Copyright (c) 2020 Dan Shechter (c) 1997-2005 Sean Eron Anderson Copyright (c) 1998 Microsoft. To Copyright (c) 2022, Wojciech Mula Copyright (c) 2017 Yoshifumi Kawai Copyright (c) 2022, Geoff Langdale Copyright (c) 2005-2020 Rich Felker Copyright (c) 2012-2021 Yann Collet Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright 2012 the V8 project authors Copyright (c) 1999 Lucent Technologies Copyright (c) 2008-2016, Wojciech Mula Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath Copyright (c) 2015 The Chromium Authors Copyright (c) 2018 Alexander Chermyanin Copyright (c) The Internet Society 1997 Portions (c) International Organization Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2011-2015 Intel Corporation Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2006 Jb Evain (jbevain@gmail.com) Copyright (c) 2008-2020 Advanced Micro Devices, Inc. Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip Copyright (c) 1980, 1986, 1993 The Regents of the University of California Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- xunit.runner.visualstudio 2.4.5 - MIT Copyright (c) .NET Foundation Copyright (c) 2015 .NET Foundation Copyright (c) Outercurve Foundation Copyright (c) .NET Foundation and Contributors Copyright (c) .NET Foundation xUnit.net Runner Utility Copyright (c) .NET Foundation ,xUnit.net Runner Utility Copyright (c) .NET Foundation 1xUnit.net Runner Utility Copyright (c) .NET Foundation xUnit.net Runner Reporters Copyright (c) .NET Foundation .xUnit.net Runner Reporters Copyright (c) Outercurve Foundation WrapNonExceptionThrows RSDS Copyright (c) .NET Foundation and Contributors. Visual Studio 2019 Unless otherwise noted, the source code here is covered by the following license: Copyright (c) .NET Foundation and Contributors All Rights Reserved Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ----------------------- The code in src/xunit.runner.visualstudio/Utility/AssemblyResolution/Microsoft.DotNet.PlatformAbstractions was imported from: https://github.com/dotnet/core-setup/tree/v2.0.1/src/managed/Microsoft.DotNet.PlatformAbstractions The code in src/xunit.runner.visualstudio/Utility/AssemblyResolution/Microsoft.DotNet.PlatformAbstractions was imported from: https://github.com/dotnet/core-setup/tree/v2.0.1/src/managed/Microsoft.Extensions.DependencyModel Both sets of code are covered by the following license: The MIT License (MIT) Copyright (c) 2015 .NET Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- yaml/libyaml 840b65c40675e2d06bf40405ad3f12dec7f35923 - MIT Copyright (c) 2017-2020 Ingy dot Net Copyright (c) 2006-2016 Kirill Simonov Copyright (c) 2017-2020 Ingy dot Net Copyright (c) 2006-2016 Kirill Simonov Copyright (c) 2017-2020 Ingy döt Net Copyright (c) 2006-2016 Kirill Simonov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- YamlDotNet 16.3.0 - MIT Copyright (c) Antoine Aubry and contributors (c) Antoine Aubry and contributors 2008 - 2019 Copyright (c) Antoine Aubry and contributors 2008 - 2019 MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- Microsoft.Management.Infrastructure.Runtime.Win 3.0.0 (c) Microsoft 2024 (c) Microsoft Corporation Copyright (c) Microsoft Corporation MICROSOFT SOFTWARE LICENSE TERMS Microsoft.Management.Infrastructure.dll Microsoft.Management.Infrastructure.Native.dll Microsoft.Management.Infrastructure.Unmanaged.dll Mi.dll Miutils.dll ________________________________________ IF YOU LIVE IN (OR ARE A BUSINESS WITH A PRINCIPAL PLACE OF BUSINESS IN) THE UNITED STATES, PLEASE READ THE “BINDING ARBITRATION AND CLASS ACTION WAIVER” SECTION BELOW. IT AFFECTS HOW DISPUTES ARE RESOLVED. ________________________________________ These license terms are an agreement between you and Microsoft Corporation (or one of its affiliates). They apply to the software named above and any Microsoft services or software updates (except to the extent such services or updates are accompanied by new or additional terms, in which case those different terms apply prospectively and do not alter your or Microsoft’s rights relating to pre-updated software or services). IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. 1. INSTALLATION AND USE RIGHTS. a) General. You may install and use any number of copies of the software solely for use with Microsoft PowerShell. b) Third Party Software. The software may include third party applications that Microsoft, not the third party, licenses to you under this agreement. Any included notices for third party applications are for your information only. 2. DATA COLLECTION. The software may collect information about you and your use of the software and send that to Microsoft. Microsoft may use this information to provide services and improve Microsoft’s products and services. Your opt-out rights, if any, are described in the product documentation. Some features in the software may enable collection of data from users of your applications that access or use the software. If you use these features to enable data collection in your applications, you must comply with applicable law, including getting any required user consent, and maintain a prominent privacy policy that accurately informs users about how you use, collect, and share their data. You can learn more about Microsoft’s data collection and use in the product documentation and the Microsoft Privacy Statement at aka.ms/privacy. You agree to comply with all applicable provisions of the Microsoft Privacy Statement. a. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at http://go.microsoft.com/?linkid=9840733. 3. SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you will not (and have no right to): a) work around any technical limitations in the software that only allow you to use it in certain ways; b) reverse engineer, decompile or disassemble the software; c) remove, minimize, block, or modify any notices of Microsoft or its suppliers in the software; d) use the software in any way that is against the law or to create or propagate malware; or e) share, publish, distribute, or lend the software, provide the software as a stand-alone hosted solution for others to use, or transfer the software or this agreement to any third party. 4. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit http://aka.ms/exporting. 5. SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any support services for the software. Any support provided is “as is”, “with all faults”, and without warranty of any kind. 6. UPDATES. The software may periodically check for updates, and download and install them for you. You may obtain updates only from Microsoft or authorized sources. Microsoft may need to update your system to provide you with updates. You agree to receive these automatic updates without any additional notice. Updates may not include or support all existing software features, services, or peripheral devices. 7. BINDING ARBITRATION AND CLASS ACTION WAIVER. This Section applies if you live in (or, if a business, your principal place of business is in) the United States. If you and Microsoft have a dispute, you and Microsoft agree to try for 60 days to resolve it informally. If you and Microsoft can’t, you and Microsoft agree to binding individual arbitration before the American Arbitration Association under the Federal Arbitration Act (“FAA”), and not to sue in court in front of a judge or jury. Instead, a neutral arbitrator will decide. Class action lawsuits, class-wide arbitrations, private attorney-general actions, and any other proceeding where someone acts in a representative capacity are not allowed; nor is combining individual proceedings without the consent of all parties. The complete Arbitration Agreement contains more terms and is at http://aka.ms/arb-agreement-1. You and Microsoft agree to these terms. 8. TERMINATION. Without prejudice to any other rights, Microsoft may terminate this agreement if you fail to comply with any of its terms or conditions. In such event, you must destroy all copies of the software and all of its component parts. 9. ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for supplements, updates, or third-party applications, is the entire agreement for the software. 10. APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in the United States or Canada, the laws of the state or province where you live (or, if a business, where your principal place of business is located) govern the interpretation of this agreement, claims for its breach, and all other claims (including consumer protection, unfair competition, and tort claims), regardless of conflict of laws principles, except that the FAA governs everything related to arbitration. If you acquired the software in any other country, its laws apply, except that the FAA governs everything related to arbitration. If U.S. federal jurisdiction exists, you and Microsoft consent to exclusive jurisdiction and venue in the federal court in King County, Washington for all disputes heard in court (excluding arbitration). If not, you and Microsoft consent to exclusive jurisdiction and venue in the Superior Court of King County, Washington for all disputes heard in court (excluding arbitration). 11. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state, province, or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state, province, or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: a) Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. b) Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software. c) Germany and Austria. i. Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. ii. Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable according to the statutory law. Subject to the foregoing clause ii., Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. 12. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. TO THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. 13. LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES. This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, warranty, guarantee, or condition; strict liability, negligence, or other tort; or any other claim; in each case to the extent permitted by applicable law. It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your state, province, or country may not allow the exclusion or limitation of incidental, consequential, or other damages. Please note: As this software is distributed in Canada, some of the clauses in this agreement are provided below in French. Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français. EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues. LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. Cette limitation concerne: • tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers; et • les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur. Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus ne s’appliquera pas à votre égard. EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas. --------------------------------------------------------- --------------------------------------------------------- Microsoft.NETCore.Platforms 1.1.1 (c) Microsoft Corporation Copyright (c) .NET Foundation and Contributors --------------------------------------------------------- --------------------------------------------------------- madler/zlib 51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf - Zlib Copyright (c) 2003 Mark Adler Copyright (c) 2018 Mark Adler (c) Copyright Henrik Ravn 2004 Copyright (c) Henrik Ravn 2004 Copyright 1995-2024 Mark Adler Copyright (c) 2003 Cosmin Truta Copyright (c) 1990-2000 Info-ZIP. Copyright (c) 1998 by Bob Dellaca Copyright (c) 2004 by Henrik Ravn Copyright (c) 1995-2003 Mark Adler Copyright (c) 1995-2008 Mark Adler Copyright (c) 1995-2017 Mark Adler Copyright (c) 1995-2019 Mark Adler Copyright (c) 1995-2022 Mark Adler Copyright (c) 1995-2024 Mark Adler Copyright (c) 2002-2013 Mark Adler Copyright (c) 2003 by Cosmin Truta Copyright (c) 2003-2010 Mark Adler Copyright (c) 2004-2017 Mark Adler Copyright (c) 2004-2019 Mark Adler Copyright (c) 2004-2023 Mark Adler Copyright (c) 2004-2024 Mark Adler Copyright (c) 1996 L. Peter Deutsch Copyright (c) 1997,99 Borland Corp. Copyright (c) 2003, 2012 Mark Adler Copyright (c) 2004, 2010 Mark Adler Copyright (c) 2011, 2016 Mark Adler Copyright (c) 2007-2008 Even Rouault Copyright (c) 1998-2005 Gilles Vollant Copyright (c) 1995-1998 Jean-loup Gailly Copyright (c) 1995-2003 Jean-loup Gailly Copyright (c) 1995-2003, 2010 Mark Adler Copyright (c) 1995-2005, 2010 Mark Adler Copyright (c) 1995-2011, 2016 Mark Adler Copyright (c) 1995-2017 Jean-loup Gailly Copyright (c) 1995-2024 Jean-loup Gailly Copyright (c) 1997,99 Borland Corporation Copyright (c) 1998 by Andreas R. Kleinert Copyright (c) 2002-2003 Dmitriy Anisimkov Copyright (c) 2002-2004 Dmitriy Anisimkov Copyright (c) 2003, 2012, 2013 Mark Adler Copyright (c) 2004, 2005, 2012 Mark Adler Copyright (c) 2004, 2008, 2012 Mark Adler Copyright (c) 1998 by Jacques Nomssi Nzali (c) 1995-2022 Jean-loup Gailly & Mark Adler (c) 1995-2024 Jean-loup Gailly & Mark Adler Copyright (c) 1995-2003 by Jean-loup Gailly Copyright (c) 1998-2010 - by Gilles Vollant (c) 1995-2017 Jean-loup Gailly and Mark Adler (c) 1995-2022 Jean-loup Gailly and Mark Adler (c) 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 2005, 2012, 2018, 2023 Mark Adler Copyright (c) 2007, 2008, 2012, 2018 Mark Adler Copyright 1995-2024 Jean-loup Gailly and Mark Adler Copyright (c) 1995-2006, 2011, 2016 Jean-loup Gailly Copyright (c) 1995-2017 Jean-Loup Gailly, Mark Adler Copyright (c) 1995-2024 Jean-loup Gailly, Mark Adler Copyright (c) 1998,1999,2000 by Jacques Nomssi Nzali Copyright (c) 2003, 2005, 2008, 2010, 2012 Mark Adler Copyright (c) 2004, 2008, 2012, 2016, 2019 Mark Adler Copyright (c) 1995-2003 Jean-loup Gailly and Mark Adler Copyright (c) 1995-2024 Jean-loup Gailly and Mark Adler copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 1996 L. Peter Deutsch and Jean-Loup Gailly Copyright (c) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly Copyright (c) 2009-2010 Mathias Svensson http://result42.com Copyright (c) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler Copyright Jean-loup Gailly Osma Ahvenlampi Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll Copyright (c) 1997 Christian Michelsen Research AS Advanced Computing Copyright (c) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler Copyright (c) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson Copyright (c) 1995-2010 Jean-loup Gailly, Brian Raiter and Gilles Vollant Copyright (c) 1998-2010 Gilles Vollant (minizip) http://www.winimage.com/zLibDll/minizip.html Copyright notice: (C) 1995-2022 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu --------------------------------------------------------- --------------------------------------------------------- tristanpenman/valijson 0b4771e273a065d437814baf426bcfcafec0f434 - BSD-2-Clause Copyright (c) 2021 Tristan Penman Copyright (c) 2016, Tristan Penman Copyright (c) The Internet Society (2005) Copyright (c) The Internet Society (2006) Copyright (c) 2011 - 2012 Andrzej Krzemienski Copyright (c) 2016, Akamai Technologies, Inc. Copyright (c) 2016 Akamai Technologies Polymorphic Copyright (c) 2010 IETF Trust and the persons identified as the document authors Copyright (c) 2012 IETF Trust and the persons identified as the document authors Copyright (c) 2013 IETF Trust and the persons identified as the document authors Copyright (c) 2016, Tristan Penman Copyright (c) 2016, Akamai Technologies, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------- --------------------------------------------------------- open-source-parsers/jsoncpp 9be589598595963f94ba264d7b416d0533421106 - MIT OR OTHER Copyright (c) 2016 InfoTeCS JSC. Copyright 2007-2010 The JsonCpp Authors Copyright 2007-2019 The JsonCpp Authors Copyright 2007 Baptiste Lepilleur and The JsonCpp Authors Copyright 2009 Baptiste Lepilleur and The JsonCpp Authors Copyright 2010 Baptiste Lepilleur and The JsonCpp Authors Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors Copyright (c) 2007-2010 by Baptiste Lepilleur and The JsonCpp Authors The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following conditions... Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and The JsonCpp Authors, and is released under the terms of the MIT License (see below). In jurisdictions which recognize Public Domain property, the user of this software may choose to accept it either as 1) Public Domain, 2) under the conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License The full text of the MIT License follows: ======================================================================== Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ======================================================================== (END LICENSE TEXT) The MIT license is compatible with both the GPL and commercial software, affording one all of the rights of Public Domain with the minor nuisance of being required to keep the above copyright notice and license text in the source code. Note also that by accepting the Public Domain "license" you can re-license your copy using whatever license you like. --------------------------------------------------------- ================================================ FILE: PRIVACY.md ================================================ # Data Collection The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft's privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. ================================================ FILE: README.md ================================================ # ![WinGet Icon](.github/images/WindowsPackageManager_Assets/ICO/PNG/_40.png) Windows Package Manager ## WinGet Client ![winget install wingetcreate](.github/images/WingetInstall.gif) If you are new to the Windows Package Manager, you might want to [Explore the Windows Package Manager tool](https://docs.microsoft.com/learn/modules/explore-windows-package-manager-tool/?WT.mc_id=AZ-MVP-5004737). The client has access to packages from two default sources. The first is "msstore" the Microsoft Store (free Apps rated "e" for everyone). The second is "winget" the [WinGet community repository](https://github.com/microsoft/winget-pkgs). > [!NOTE] > Group policy may be configured and modify configured sources. Run `winget --info` to see any configured policies. ## Installing The Client > [!NOTE] > The client requires Windows 10 1809 (build 17763) or later at this time. Windows Server 2019 is not supported as the Microsoft Store is not available nor are updated dependencies. It may be possible to install on Windows Server 2022, this should be considered experimental (not supported) and requires dependencies to be manually installed as well. ### Microsoft Store [Recommended] The client is distributed within the [App Installer](https://apps.microsoft.com/detail/9nblggh4nns1) package. ### Development Releases There are a few methods to get development releases: * Install a [Windows 10 or Windows 11 Insider](https://insider.windows.com/) build. * Manually update using a development build from our [Releases](https://github.com/microsoft/winget-cli/releases) page. * Use the `Repair-WinGetPackageManager` cmdlet from the [Microsoft.WinGet.Client](https://www.powershellgallery.com/packages/Microsoft.WinGet.Client/) PowerShell module and use the `-IncludePrerelease` parameter. > [!NOTE] > If you decide to install the latest release from GitHub, and you have successfully joined the insider program, you will receive updates when the next development release has been published in the Microsoft Store. Once you have received the updated App Installer from the Microsoft Store you should be able to execute `winget features` to see experimental features. Some users have reported [issues](https://github.com/microsoft/winget-cli/issues/210) with the client not being on their PATH. ### Manually Update The same Microsoft Store package will be made available via our [Releases](https://github.com/microsoft/winget-cli/releases). Note that installing this package will give you the WinGet client, but it will not enable automatic updates from the Microsoft Store if you have not joined the Windows Package Manager Insider program. > [!NOTE] > You may need to install the [VC++ v14 Desktop Framework Package](https://docs.microsoft.com/troubleshoot/cpp/c-runtime-packages-desktop-bridge#how-to-install-and-update-desktop-framework-packages). > This should only be necessary on older builds of Windows 10 and only if you get an error about missing framework packages. ### Troubleshooting Please read our [troubleshooting guide](/doc/troubleshooting/README.md). ## Administrator Considerations Installer behavior can be different depending on whether you are running **WinGet** with administrator privileges. * When running **WinGet** without administrator privileges, some applications may [require elevation](https://docs.microsoft.com/windows/security/identity-protection/user-account-control/how-user-account-control-works) to install. When the installer runs, Windows will prompt you to [elevate](https://docs.microsoft.com/windows/security/identity-protection/user-account-control/how-user-account-control-works#the-uac-user-experience). If you choose not to elevate, the application will fail to install. * When running **WinGet** in an Administrator Command Prompt, you will not see [elevation prompts](https://docs.microsoft.com/windows/security/identity-protection/user-account-control/how-user-account-control-works#the-uac-user-experience) if the application requires it. Always use caution when running your command prompt as an administrator, and only install applications you trust. ### Build your own You can also [build the client yourself](#building-the-client). While the client should be perfectly functional, we are not ready to provide full support for clients running outside of the official distribution mechanisms yet. Feel free to file an [Issue](https://github.com/microsoft/winget-cli/issues/new/choose), but know that it may get lower prioritization. ## Build Status [![Build Status](https://dev.azure.com/shine-oss/winget-cli/_apis/build/status/winget-cli%20Build_Test?branchName=master&label=Main%20Branch%20(Including%20PRs))](https://dev.azure.com/shine-oss/winget-cli/_build/latest?definitionId=10&branchName=master) ## Windows Package Manager Release Roadmap The plan for delivering the next Windows Package Manager release is described and included in our [discussions](https://github.com/microsoft/winget-cli/discussions/2063), and will be updated as the project proceeds. ## Overview of the Windows Package Manager The **Windows Package Manager** is a tool designed to help you quickly and easily discover and install those packages that make your PC environment special. By using the **Windows Package Manager**, from one command, you can install your favorite packages: `winget install ` ## Overview ### Client Repository This winget-cli repository includes the source code designed to build the client. You are encouraged to participate in the development of this client. We have plenty of backlog features in our [Issues](https://github.com/microsoft/winget-cli/issues?q=is%3Aopen+is%3Aissue+milestone%3ABacklog-Client). You can upvote the ones you want, add more, or even [get started on one.](https://github.com/orgs/microsoft/projects/137) ### Sources The client is built around the concept of sources; a set of packages effectively. Sources provide the ability to discover and retrieve the metadata about the packages so that the client can act on it. * The default "winget" source includes packages in the [Windows Package Manager Community Repository](https://github.com/microsoft/winget-pkgs). * The default "msstore" source includes packages in the Microsoft Store. * It is also possible to host your own private [REST-based](https://github.com/microsoft/winget-cli-restsource) source. ## Building the client Please follow our [developer guidance](/doc/Developing.md) to build, run & test the client. ## Credit We would like to thank [Keivan Beigi (@kayone)](https://github.com/kayone) for his work on AppGet which helped us with the initial project direction for Windows Package Manager. ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and do, actually grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. More information is available in our [CONTRIBUTING.md](/CONTRIBUTING.md) file. When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, please refer to the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ## Data/Telemetry The winget.exe client is instrumented to collect usage and diagnostic (error) data and sends it to Microsoft to help improve the product. If you build the client yourself the instrumentation will not be enabled and no data will be sent to Microsoft. The winget.exe client respects machine-wide privacy settings and users can opt out on their device, as documented in the [Microsoft Windows privacy statement](https://support.microsoft.com/help/4468236/diagnostics-feedback-and-privacy-in-windows-10-microsoft-privacy). In addition, you may also explicitly block telemetry using [settings](https://docs.microsoft.com/windows/package-manager/winget/settings) In short, to opt out, do one of the following: **Windows 11**: Go to `Start`, then select `Settings` > `Privacy & Security` > `Diagnostics & feedback` > `Diagnostic data` and unselect `Send optional diagnostic data`. **Windows 10**: Go to `Start`, then select `Settings` > `Privacy` > `Diagnostics & feedback`, and select `Required diagnostic data`. See the [privacy statement](PRIVACY.md) for more details. ================================================ FILE: SECURITY.md ================================================ ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. ## Preferred Languages We prefer all communications to be in English. ## Policy Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). ================================================ FILE: SUPPORT.md ================================================ # Support ## How to file issues and get help This project uses [GitHub issues][gh-issue] to [track bugs][gh-bug] and [feature requests][gh-feature]. Please search the existing issues before filing new issues to avoid duplicates. For new topics, file your bug or feature request as a new issue. For help and questions about using this project, please look at the [docs site for Windows Package Manager][docs] and our [Contributor's Guide][contributor] if you want to work on WinGet. ## Microsoft Support Policy Support for Windows Package Manager is limited to the resources listed above. [gh-issue]: https://github.com/microsoft/winget-cli/issues/new/choose [gh-bug]: https://github.com/microsoft/winget-cli/issues/new?assignees=&labels=Issue-Bug&projects=&template=Bug_Report.yml [gh-feature]: https://github.com/microsoft/winget-cli/issues/new?assignees=&labels=Issue-Feature&projects=&template=Feature_Request.yml [docs]: https://docs.microsoft.com/windows/package-manager [contributor]: https://github.com/microsoft/winget-cli/blob/master/CONTRIBUTING.md ================================================ FILE: azure-pipelines.loc.yml ================================================ # # Localization # This pipeline uploads English strings files to the localization service, downloads any translated # files which are available, and checks them into git. This pipeline relies on Microsoft-internal # resources to run. # # Expects a variable called LocServiceKey to contain the OAuth client secret for Touchdown. trigger: none pr: none name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) jobs: - job: Localize pool: vmImage: windows-latest variables: skipComponentGovernanceDetection: true tdbuildTeamId: 8343 steps: # Upload client resources - task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@5 displayName: Send resources to Touchdown Build inputs: teamId: $(tdbuildTeamId) authType: FederatedIdentity FederatedIdentityServiceConnection: AppInstallerTDBuild isPreview: false relativePathRoot: src\AppInstallerCLIPackage\Shared\Strings\en-us resourceFilePath: '*.resw' outputDirectoryRoot: localization/Resources/ # Upload Group Policy ADML # Do it as a separate step as we need the result in a different location - task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@5 displayName: Send ADML to Touchdown Build inputs: teamId: $(tdbuildTeamId) authType: FederatedIdentity FederatedIdentityServiceConnection: AppInstallerTDBuild isPreview: false relativePathRoot: doc\admx\en-US resourceFilePath: '*.adml' outputDirectoryRoot: Localization\Policies\ - script: | cd $(Build.SourcesDirectory) git add -A git diff --cached --exit-code echo ##vso[task.setvariable variable=hasChanges]%errorlevel% git diff --cached > $(Build.ArtifactStagingDirectory)\LocalizedStrings.patch displayName: Check for changes and create patch file - task: PublishPipelineArtifact@0 displayName: Publish patch file as artifact condition: eq(variables['hasChanges'], '1') inputs: artifactName: Patch targetPath: $(Build.ArtifactStagingDirectory) ================================================ FILE: azure-pipelines.yml ================================================ # Commit triggers trigger: - master # PR triggers pr: branches: include: - master paths: include: - azure-pipelines.yml - templates/* - src/* - schemas/JSON/manifests/* pool: vmImage: 'windows-2025' variables: solution: 'src\AppInstallerCLI.sln' EnableDetectorVcpkg: true # Do not set the build version for a PR build. jobs: - job: 'GetReleaseTag' condition: not(eq(variables['Build.Reason'], 'PullRequest')) variables: runCodesignValidationInjection: ${{ false }} skipComponentGovernanceDetection: ${{ true }} steps: - task: PowerShell@2 name: 'GetTag' displayName: Get Release Tag inputs: filePath: 'src\binver\Update-BinVer.ps1' arguments: '-OutVar' workingDirectory: 'src' # Build job creates artifacts for use in test jobs - job: 'Build' timeoutInMinutes: 120 dependsOn: 'GetReleaseTag' condition: always() strategy: matrix: x86_release: buildConfiguration: 'Release' buildPlatform: 'x86' artifactIdentifier: 'x86release' x64_release: buildConfiguration: 'Release' buildPlatform: 'x64' artifactIdentifier: 'x64release' variables: BuildVer: $[counter(dependencies.GetReleaseTag.outputs['GetTag.tag'], 1)] buildOutDir: $(Build.SourcesDirectory)\src\$(buildPlatform)\$(buildConfiguration) buildOutDirAnyCpu: $(Build.SourcesDirectory)\src\AnyCPU\$(buildConfiguration) artifactsDir: $(Build.ArtifactStagingDirectory)\$(buildPlatform) appxPackageDir: $(Build.ArtifactStagingDirectory)\$(buildPlatform)\AppxPackages steps: - task: NuGetToolInstaller@1 displayName: Install Nuget # Restores all projects, including native (vcxproj) projects - task: NuGetCommand@2 displayName: Restore Solution inputs: restoreSolution: '$(solution)' # Restore these UAP packages as https://github.com/NuGet/Home/issues/7796 leads to all UAP packages being skipped for restore. # Even though they don't need any actual restore action, they need the project.assets.json file to be created and a direct restore does that. - task: NuGetCommand@2 displayName: Restore AppInstallerCLIPackage inputs: restoreSolution: 'src\AppInstallerCLIPackage\AppInstallerCLIPackage.wapproj' - task: NuGetCommand@2 displayName: Restore AppInstallerTestMsixInstaller inputs: restoreSolution: 'src\AppInstallerTestMsixInstaller\AppInstallerTestMsixInstaller.wapproj' # Restores only .NET core projects, but is still necessary, as without this the IndexCreationTool and LocalhostWebServer projects fail to build - task: DotNetCoreCLI@2 displayName: DotNet Restore inputs: command: 'restore' projects: '**/*.csproj' - task: CmdLine@2 displayName: Enable Vcpkg Install inputs: script: | $(VCPKG_INSTALLATION_ROOT)\vcpkg.exe integrate install workingDirectory: '$(VCPKG_INSTALLATION_ROOT)' - task: PowerShell@2 displayName: Update Binary Version condition: not(eq(variables['Build.Reason'], 'PullRequest')) inputs: filePath: 'src\binver\Update-BinVer.ps1' arguments: '-TargetFile binver\binver\version.h -BuildVersion $(BuildVer)' workingDirectory: 'src' # Build all solutions in the root directory. - task: VSBuild@1 displayName: Build Solution inputs: platform: '$(buildPlatform)' solution: '$(solution)' configuration: '$(buildConfiguration)' msbuildArgs: '/bl:$(artifactsDir)\msbuild.binlog /p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=SideloadOnly /p:WingetCleanIntermediateFiles=true' maximumCpuCount: true - task: MSBuild@1 displayName: Build MSIX Test Installer File inputs: platform: '$(buildPlatform)' solution: 'src\AppInstallerTestMsixInstaller\AppInstallerTestMsixInstaller.wapproj' configuration: '$(buildConfiguration)' msbuildArguments: '/p:AppxPackageOutput="$(Build.ArtifactStagingDirectory)\AppInstallerTestMsixInstaller.msix" /p:AppxBundle=Never /p:UapAppxPackageBuildMode=SideLoadOnly /p:AppxPackageSigningEnabled=false' maximumCpuCount: true - task: CopyFiles@2 displayName: Copy vcpkg logs inputs: SourceFolder: $(VCPKG_INSTALLATION_ROOT)\buildtrees Contents: '**\*.log' TargetFolder: '$(artifactsDir)\vcpkgLogs' condition: succeededOrFailed() - task: CopyFiles@2 displayName: 'Copy specific build artifacts' inputs: Contents: | $(buildOutDir)\WinGetUtil\WinGetUtil.dll $(buildOutDir)\WinGetUtil\WinGetUtil.pdb TargetFolder: '$(artifactsDir)' - task: PowerShell@2 displayName: Create Package Layout inputs: filePath: 'src\AppInstallerCLIPackage\Execute-AppxRecipe.ps1' arguments: '-AppxRecipePath AppInstallerCLIPackage\bin\$(buildPlatform)\$(buildConfiguration)\AppInstallerCLIPackage.build.appxrecipe -LayoutPath $(artifactsDir)\DevPackage -Force -Verbose' workingDirectory: 'src' - task: CopyFiles@2 displayName: 'Copy native binaries for Microsoft.WinGet.Client (net8)' inputs: SourceFolder: $(buildOutDir) Contents: | Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.dll Microsoft.Management.Deployment\Microsoft.Management.Deployment.winmd WindowsPackageManager\WindowsPackageManager.dll UndockedRegFreeWinRT\winrtact.dll TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Client\net8.0-windows10.0.26100.0\SharedDependencies\$(BuildPlatform) flattenFolders: true - task: CopyFiles@2 displayName: 'Copy native binaries for Microsoft.WinGet.Client (net48)' inputs: SourceFolder: $(buildOutDir) Contents: | Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.dll Microsoft.Management.Deployment\Microsoft.Management.Deployment.winmd WindowsPackageManager\WindowsPackageManager.dll UndockedRegFreeWinRT\winrtact.dll TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Client\net48\SharedDependencies\$(BuildPlatform) flattenFolders: true - task: CopyFiles@2 displayName: 'Copy native binaries for Microsoft.WinGet.Configuration' inputs: SourceFolder: $(buildOutDir) Contents: | Microsoft.Management.Configuration\Microsoft.Management.Configuration.dll TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Configuration\SharedDependencies\$(BuildPlatform) flattenFolders: true - task: CopyFiles@2 displayName: 'Copy managed binaries for Microsoft.WinGet.Configuration in arch specific' inputs: SourceFolder: $(buildOutDirAnyCpu) Contents: | Microsoft.Management.Configuration.Projection\net8.0-windows10.0.26100.0\Microsoft.Management.Configuration.Projection.dll TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Configuration\SharedDependencies\$(BuildPlatform) flattenFolders: true - task: CopyFiles@2 displayName: 'Copy Microsoft.WinGet.UnitTests AnyCPU Files' inputs: SourceFolder: '$(buildOutDirAnyCpu)\Microsoft.WinGet.UnitTests\net8.0-windows10.0.26100.0' TargetFolder: '$(artifactsDir)\Microsoft.WinGet.UnitTests\' CleanTargetFolder: true OverWrite: true - task: CopyFiles@2 displayName: 'Copy PowerShell AnyCPU Module Files' inputs: SourceFolder: '$(buildOutDirAnyCpu)\PowerShell' TargetFolder: '$(artifactsDir)\PowerShell' - task: CopyFiles@2 displayName: 'Copy binaries' inputs: SourceFolder: '$(buildOutDir)' TargetFolder: '$(artifactsDir)' Contents: | AppInstallerCLIE2ETests\** AppInstallerCLITests\** ComInprocTestbed\** Microsoft.Management.Configuration\** Microsoft.Management.Configuration.UnitTests\** Microsoft.Management.Configuration.OutOfProc\** - task: CopyFiles@2 displayName: 'Copy Files: WinGetUtilInterop.UnitTests' inputs: SourceFolder: '$(Build.SourcesDirectory)\src\WinGetUtilInterop.UnitTests\bin\$(buildPlatform)\$(BuildConfiguration)\net8.0' TargetFolder: '$(artifactsDir)\WinGetUtilInterop.UnitTests\' CleanTargetFolder: true OverWrite: true - task: CopyFiles@2 displayName: 'Copy WinGetUtil to WinGetUtilInterop.UnitTests folder' inputs: Contents: | $(buildOutDir)\WinGetUtil\WinGetUtil.dll TargetFolder: '$(artifactsDir)\WinGetUtilInterop.UnitTests\' flattenFolders: true - task: CopyFiles@2 displayName: 'Copy LocalhostWebServer to E2ETests' inputs: SourceFolder: '$(buildOutDir)\LocalhostWebServer' TargetFolder: '$(artifactsDir)\E2ETests\LocalhostWebServer' # Invoke E2E setup to generate the TestLocalIndex; could optimize out some of its steps if needed - template: templates/e2e-setup.yml parameters: sourceDir: $(Build.SourcesDirectory) localhostWebServerArgs: '-BuildRoot $(artifactsDir)\E2ETests\LocalhostWebServer -StaticFileRoot $(Agent.TempDirectory)\TestLocalIndex -LocalSourceJson $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\localsource.json -TestDataPath $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData -ExitBeforeRun' signingCertOutDir: $(artifactsDir)\E2ETests - task: CopyFiles@2 displayName: 'Copy TestLocalIndex' inputs: SourceFolder: '$(Agent.TempDirectory)\TestLocalIndex' TargetFolder: '$(artifactsDir)\E2ETests\TestLocalIndex' - task: CopyFiles@2 displayName: 'Copy TestData' inputs: SourceFolder: '$(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\' TargetFolder: '$(artifactsDir)\E2ETests\TestData' - task: CopyFiles@2 displayName: 'Copy Dev Package Dependencies' inputs: SourceFolder: '$(appxPackageDir)\AppInstallerCLIPackage_0.0.2.0_Test\Dependencies\$(buildPlatform)\' TargetFolder: '$(artifactsDir)\E2ETests\DevPackageDependencies' - task: CopyFiles@2 displayName: 'Copy test scripts to artifacts' inputs: Contents: | $(Build.SourcesDirectory)\src\PowerShell\scripts\Execute-WinGetTests.ps1 $(Build.SourcesDirectory)\src\PowerShell\tests\** $(Build.SourcesDirectory)\src\LocalhostWebServer\Run-LocalhostWebServer.ps1 TargetFolder: '$(artifactsDir)\E2ETests\Scripts' flattenFolders: true - task: PublishPipelineArtifact@1 displayName: Publish Pipeline Artifacts inputs: targetPath: '$(artifactsDir)' artifact: 'Build.$(artifactIdentifier)' condition: succeededOrFailed() - task: ComponentGovernanceComponentDetection@0 displayName: Component Governance inputs: scanType: 'Register' verbosity: 'Verbose' alertWarningLevel: 'High' # Test job runs tests using build artifacts - job: 'Test' timeoutInMinutes: 120 dependsOn: 'Build' condition: succeeded('Build') strategy: matrix: x86_release: buildConfiguration: 'Release' buildPlatform: 'x86' artifactIdentifier: 'x86release' x64_release: buildConfiguration: 'Release' buildPlatform: 'x64' artifactIdentifier: 'x64release' variables: buildOutDir: $(Pipeline.Workspace)\Build.$(artifactIdentifier) artifactsDir: $(Build.ArtifactStagingDirectory) packageLayoutDir: $(Pipeline.Workspace)\Build.$(artifactIdentifier)\DevPackage steps: - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifacts' inputs: artifact: 'Build.$(artifactIdentifier)' path: '$(buildOutDir)' - task: PowerShell@2 displayName: Install Tests Dependencies inputs: targetType: 'inline' script: | Get-ChildItem $(buildOutDir)\E2ETests\DevPackageDependencies -Filter *.appx | %{ Add-AppxPackage $_.FullName } - task: VisualStudioTestPlatformInstaller@1 displayName: Prepare VSTest for E2E Tests inputs: packageFeedSelector: 'nugetOrg' - task: CmdLine@2 displayName: Start HAM trace condition: and(succeededOrFailed(), eq(variables['System.debug'], true)) inputs: script: 'wpr -start $(Build.SourcesDirectory)\tools\HAMTrace\WER.HostActivityManager.wprp -filemode' - powershell: | Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force Install-Module Microsoft.WinGet.Client -Repository PSGallery -Force try { Repair-WingetPackageManager -Latest -Verbose } catch { $_.Exception } Install-WinGetPackage -Id Microsoft.Sysinternals.PsTools -Source winget displayName: Install Sysinternals PsTools Using Winget condition: succeededOrFailed() - task: CmdLine@2 displayName: Complete HAM trace condition: and(succeededOrFailed(), eq(variables['System.debug'], true)) inputs: script: 'wpr -stop "$(artifactsDir)\HamTrace.etl"' - pwsh: | $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") PsExec -accepteula -s -i $(buildOutDir)\AppInstallerCLITests\AppInstallerCLITests.exe -logto $(artifactsDir)\AICLI-Unpackaged-System.log -mdmpto $(artifactsDir)\AICLI-Unpackaged-System.mdmp -s -r junit -o $(artifactsDir)\TEST-AppInstallerCLI-Unpackaged-System.xml displayName: Run Unit Tests Unpackaged Under System Context workingDirectory: '$(buildOutDir)\AppInstallerCLITests' condition: succeededOrFailed() - powershell: | Uninstall-WinGetPackage -Id Microsoft.Sysinternals.PsTools -Source winget displayName: Clean up Sysinternals PsTools condition: succeededOrFailed() - task: PowerShell@2 displayName: Run Unit Tests Packaged inputs: filePath: 'src\AppInstallerCLITests\Run-TestsInPackage.ps1' arguments: '-Args "~[pips]" -BuildRoot $(buildOutDir) -PackageRoot $(packageLayoutDir) -LogTarget $(artifactsDir)\AICLI-Packaged.log -MdmpTarget $(artifactsDir)\AICLI-Packaged.mdmp -TestResultsTarget $(artifactsDir)\TEST-AppInstallerCLI-Packaged.xml -ScriptWait' workingDirectory: 'src' condition: succeededOrFailed() - task: PublishTestResults@2 displayName: Publish Unit Test Results inputs: testResultsFormat: 'JUnit' testResultsFiles: '$(artifactsDir)\TEST-*.xml' failTaskOnFailedTests: true condition: succeededOrFailed() - task: PowerShell@2 displayName: 'Set program files directory' inputs: targetType: 'inline' script: | if ("$(buildPlatform)" -eq "x86") { Write-Host "##vso[task.setvariable variable=platformProgramFiles;]${env:ProgramFiles(x86)}" } else { Write-Host "##vso[task.setvariable variable=platformProgramFiles;]${env:ProgramFiles}" } condition: succeededOrFailed() # Resolves resource strings utilized by InProc E2E tests. - task: CopyFiles@2 displayName: 'Copy resources.pri to dotnet directory' inputs: SourceFolder: '$(buildOutDir)\DevPackage' TargetFolder: '$(platformProgramFiles)\dotnet' Contents: resources.pri condition: succeededOrFailed() # Winmd accessed by test runner process (dotnet.exe) - task: CopyFiles@2 displayName: 'Copy winmd to dotnet directory' inputs: SourceFolder: '$(buildOutDir)\DevPackage' TargetFolder: '$(platformProgramFiles)\dotnet' Contents: Microsoft.Management.Deployment.winmd condition: succeededOrFailed() - template: templates/e2e-setup.yml parameters: sourceDir: $(Build.SourcesDirectory) localhostWebServerArgs: '-BuildRoot $(buildOutDir)\E2ETests\LocalhostWebServer -StaticFileRoot $(buildOutDir)\E2ETests\TestLocalIndex -SourceCert $(buildOutDir)\E2ETests\TestSigningCert.cer' - template: templates/e2e-test.template.yml parameters: title: "E2E Tests Packaged" isPackaged: true filter: "TestCategory!=InProcess&TestCategory!=OutOfProcess" - template: templates/e2e-test.template.yml parameters: title: "Microsoft.Management.Deployment E2E Tests (In-process)" isPackaged: false filter: "TestCategory=InProcess" - template: templates/e2e-test.template.yml parameters: title: "Microsoft.Management.Deployment E2E Tests (Out-of-process)" isPackaged: true filter: "TestCategory=OutOfProcess" - task: CopyFiles@2 displayName: 'Copy E2E Tests Package Log to artifacts folder' inputs: SourceFolder: '$(temp)\E2ETestLogs' TargetFolder: '$(artifactsDir)\PackagedLog' condition: succeededOrFailed() - task: VSTest@2 displayName: 'Run tests: Microsoft.WinGet.UnitTests' inputs: testSelector: 'testAssemblies' testAssemblyVer2: 'Microsoft.WinGet.UnitTests.dll' searchFolder: '$(buildOutDir)\Microsoft.WinGet.UnitTests' codeCoverageEnabled: true platform: '$(buildPlatform)' configuration: '$(BuildConfiguration)' diagnosticsEnabled: true condition: succeededOrFailed() - task: VSTest@2 displayName: 'Run tests: WinGetUtilInterop.UnitTests' inputs: testSelector: 'testAssemblies' testAssemblyVer2: 'WinGetUtilInterop.UnitTests.dll' searchFolder: '$(buildOutDir)\WinGetUtilInterop.UnitTests' codeCoverageEnabled: true platform: '$(buildPlatform)' configuration: '$(BuildConfiguration)' diagnosticsEnabled: true condition: succeededOrFailed() - task: VSTest@2 displayName: 'Run tests: Microsoft.Management.Configuration.UnitTests (InProc)' inputs: testRunTitle: Microsoft.Management.Configuration.UnitTests (InProc) testSelector: 'testAssemblies' testAssemblyVer2: '**\Microsoft.Management.Configuration.UnitTests.dll' searchFolder: '$(buildOutDir)\Microsoft.Management.Configuration.UnitTests' testFiltercriteria: 'Category=InProc' codeCoverageEnabled: false platform: '$(buildPlatform)' configuration: '$(BuildConfiguration)' diagnosticsEnabled: true condition: succeededOrFailed() - task: PowerShell@2 displayName: Prepare for Microsoft.Management.Configuration.UnitTests (OutOfProc) inputs: filePath: 'src\Microsoft.Management.Configuration.OutOfProc\Prepare-ConfigurationOOPTests.ps1' arguments: '-BuildOutputPath $(buildOutDir) -PackageLayoutPath $(packageLayoutDir)' condition: succeededOrFailed() - task: VSTest@2 displayName: 'Run tests: Microsoft.Management.Configuration.UnitTests (OutOfProc)' inputs: testRunTitle: Microsoft.Management.Configuration.UnitTests (OutOfProc) testSelector: 'testAssemblies' testAssemblyVer2: '**\Microsoft.Management.Configuration.UnitTests.dll' searchFolder: '$(buildOutDir)\Microsoft.Management.Configuration.UnitTests' testFiltercriteria: 'Category=OutOfProc' codeCoverageEnabled: true platform: '$(buildPlatform)' configuration: '$(BuildConfiguration)' condition: succeededOrFailed() - task: PowerShell@2 displayName: Collect logs for Microsoft.Management.Configuration.UnitTests (OutOfProc) inputs: filePath: 'src\Microsoft.Management.Configuration.OutOfProc\Collect-ConfigurationOOPTests.ps1' arguments: '-TargetLocation $(artifactsDir)\ConfigOOPTestsLog' condition: succeededOrFailed() - powershell: Get-Process LocalhostWebServer | Stop-Process displayName: Stop LocalhostWebServer condition: succeededOrFailed() - task: PowerShell@2 displayName: 'Copy GA WinGet Log to artifacts folder' inputs: targetType: 'inline' script: | $source = "$env:LocalAppData\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir" $destination = "$(artifactsDir)\GA_WinGet_Logs" if (Test-Path $source) { Copy-Item -Path $source -Destination $destination -Recurse -Force } else { Write-Host "WinGet logs not found at $source" } condition: succeededOrFailed() - task: PowerShell@2 displayName: 'Copy Application Event Logs to Artifacts' inputs: targetType: 'inline' script: | $source = "$env:SystemRoot\System32\winevt\Logs\Application.evtx" $destination = "$(artifactsDir)\Application.evtx" if (Test-Path $source) { Copy-Item -Path $source -Destination $destination -Force } else { Write-Host "Application event log not found at $source" } condition: succeededOrFailed() - task: PublishPipelineArtifact@1 displayName: Publish Pipeline Artifacts inputs: targetPath: '$(artifactsDir)' artifact: 'Test.$(artifactIdentifier).$(System.JobAttempt)' condition: succeededOrFailed() # Build and test PowerShell module - job: 'BuildPowerShellModule' timeoutInMinutes: 120 dependsOn: 'Build' condition: succeeded('Build') variables: buildOutDir: $(Pipeline.Workspace)\Build.x64Release steps: - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifacts' - task: CopyFiles@2 displayName: 'Copy x64 PowerShell Binaries to Output' inputs: SourceFolder: '$(buildOutDir)\PowerShell' Contents: '**\*' TargetFolder: '$(Build.ArtifactStagingDirectory)' - task: CopyFiles@2 displayName: 'Copy x86 PowerShell Binaries to Output' inputs: SourceFolder: '$(Pipeline.Workspace)\Build.x86release\PowerShell' Contents: '**\*' TargetFolder: '$(Build.ArtifactStagingDirectory)' - task: PowerShell@2 displayName: Generate Microsoft.WinGet.Client Help Documentation inputs: pwsh: true targetType: inline script: | Install-Module -Name platyPS -Force Import-Module platyPS New-ExternalHelp -Path '$(Build.SourcesDirectory)\src\PowerShell\Help\Microsoft.WinGet.Client' -OutputPath '$(Build.ArtifactStagingDirectory)\Microsoft.WinGet.Client' - task: CopyFiles@2 displayName: 'Copy Microsoft.WinGet.DSC module to staging directory' inputs: SourceFolder: '$(Build.SourcesDirectory)\src\PowerShell\Microsoft.WinGet.DSC' Contents: '**\*' TargetFolder: '$(Build.ArtifactStagingDirectory)\Microsoft.WinGet.DSC' - task: PowerShell@2 displayName: Install Tests Dependencies inputs: targetType: 'inline' script: | Get-ChildItem AppxPackages\AppInstallerCLIPackage_0.0.2.0_Test\Dependencies\x64 -Filter *.appx | %{ Add-AppxPackage $_.FullName } workingDirectory: $(buildOutDir) - template: templates/e2e-setup.yml parameters: sourceDir: $(Build.SourcesDirectory) localhostWebServerArgs: '-BuildRoot $(buildOutDir)\E2ETests\LocalhostWebServer -StaticFileRoot $(buildOutDir)\E2ETests\TestLocalIndex -SourceCert $(buildOutDir)\E2ETests\TestSigningCert.cer' - pwsh: .\RunTests.ps1 -testModulesPath $(Build.ArtifactStagingDirectory) -outputPath $(Pipeline.Workspace)\PesterTest -packageLayoutPath $(buildOutDir)\DevPackage workingDirectory: $(Build.SourcesDirectory)\src\PowerShell\tests\ displayName: Run PowerShell 7 Tests - powershell: .\RunTests.ps1 -testModulesPath $(Build.ArtifactStagingDirectory) -outputPath $(Pipeline.Workspace)\WPPesterTest workingDirectory: $(Build.SourcesDirectory)\src\PowerShell\tests\ displayName: Run Windows PowerShell Tests condition: succeededOrFailed() - powershell: Get-Process LocalhostWebServer | Stop-Process displayName: Stop LocalhostWebServer condition: succeededOrFailed() - task: PublishTestResults@2 displayName: Publish Pester Test Results PowerShell 7 inputs: testResultsFormat: 'NUnit' testResultsFiles: '$(Pipeline.Workspace)\PesterTest\Test*.xml' failTaskOnFailedTests: true condition: succeededOrFailed() - task: PublishTestResults@2 displayName: Publish Pester Test Results Windows PowerShell inputs: testResultsFormat: 'NUnit' testResultsFiles: '$(Pipeline.Workspace)\WPPesterTest\Test*.xml' failTaskOnFailedTests: true condition: succeededOrFailed() - task: PowerShell@2 displayName: Copy WinGet Logs inputs: pwsh: true targetType: inline script: | $sourceDir = Join-Path $env:LocalAppData Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\DiagOutputDir $destinationDir = Join-Path $(Build.ArtifactStagingDirectory) WinGetLogs Copy-Item -Path $sourceDir -Destination $destinationDir -Recurse -Force condition: succeededOrFailed() - task: PublishPipelineArtifact@1 displayName: Publish PowerShell Module Artifacts inputs: targetPath: '$(Build.ArtifactStagingDirectory)' condition: succeededOrFailed() - job: 'Fuzzing' timeoutInMinutes: 60 condition: not(eq(variables['Build.Reason'], 'PullRequest')) strategy: matrix: x64: buildConfiguration: 'Fuzzing' buildPlatform: 'x64' variables: buildOutDir: $(Build.SourcesDirectory)\src\$(buildPlatform)\$(buildConfiguration) artifactsDir: $(Build.ArtifactStagingDirectory)\$(buildPlatform) steps: - task: NuGetToolInstaller@1 displayName: Install Nuget - task: NuGetCommand@2 displayName: Restore Solution inputs: restoreSolution: '$(solution)' - task: CmdLine@2 displayName: Enable Vcpkg Install inputs: script: | $(VCPKG_INSTALLATION_ROOT)\vcpkg.exe integrate install workingDirectory: '$(VCPKG_INSTALLATION_ROOT)' - task: VSBuild@1 displayName: Build Fuzzing Artifacts inputs: platform: '$(buildPlatform)' solution: '$(solution)' configuration: '$(buildConfiguration)' msbuildArgs: '/bl:$(artifactsDir)\msbuild.binlog' maximumCpuCount: true - task: CopyFiles@2 displayName: Copy vcpkg logs inputs: SourceFolder: $(VCPKG_INSTALLATION_ROOT)\buildtrees Contents: '**\*.log' TargetFolder: '$(artifactsDir)\vcpkgLogs' condition: succeededOrFailed() - task: CopyFiles@2 displayName: Copy Fuzzing Artifacts for Publishing inputs: SourceFolder: '$(buildOutDir)\WinGetYamlFuzzing' Contents: '**' TargetFolder: '$(artifactsDir)' - task: PublishPipelineArtifact@1 displayName: Publish Fuzzing Artifacts inputs: targetPath: '$(artifactsDir)' condition: succeededOrFailed() - task: onefuzz-task@0 inputs: onefuzzOSes: 'Windows' env: onefuzzDropDirectory: '$(buildOutDir)\WinGetYamlFuzzing' SYSTEM_ACCESSTOKEN: $(System.AccessToken) ================================================ FILE: cgmanifest.json ================================================ { "$schema": "https://json.schemastore.org/component-detection-manifest.json", "Registrations": [ { "component": { "type": "git", "git": { "repositoryUrl": "https://github.com/microsoft/cpprestsdk.git", "commitHash": "411a109150b270f23c8c97fa4ec9a0a4a98cdecf" } } }, { "component": { "type": "git", "git": { "repositoryUrl": "https://github.com/ronomon/pure.git", "commitHash": "fd54913e65338e678440ae66b3b5022ab23b761b" } } }, { "component": { "type": "git", "git": { "repositoryUrl": "https://github.com/microsoft/xlang", "commitHash": "cfe510d0d2b07484fea2c6d77163de017738c100" } } }, { "component": { "type": "git", "git": { "repositoryUrl": "https://github.com/microsoft/sfs-client.git", "commitHash": "ff315ecfa2ef2953d8a808e51e8a61a4e0759180" } } }, { "component": { "type": "git", "git": { "repositoryUrl": "https://github.com/yaml/libyaml.git", "commitHash": "840b65c40675e2d06bf40405ad3f12dec7f35923" } } } ], "Version": 1 } ================================================ FILE: doc/Completion.md ================================================ # WinGet Command Line Tab Completion WinGet offers a `complete` command that can be leveraged by your shell to provide context sensitive tab completion. It allows for completion of command names, argument names, and argument values, dependent on the current command line state. > Note, this feature was released in [v0.1.42241 Preview](https://github.com/microsoft/winget-cli/releases/tag/v0.1.42241-preview). Please update if you are on an older build. ## Examples > These examples assume that the tab completion in your shell works similar to PowerShell; repeated presses of tab (`⇥`) will result in cycling through the possible values. Input | Result | Reason --- | --- | --- `winget ⇥` | `winget install` | `install` is the first command below the root `winget sh⇥` | `winget show` | `show` is the first command that starts with `sh` `winget source l⇥` | `winget source list` | `list` is the first sub-command of source that starts with `l` `winget -⇥` | `winget --version` | `--version` is the first argument defined for the root `winget install power⇥` | `winget install "Power Toys"` | `"Power Toys"` is the first package whose Id, Name, or Moniker starts with `power` `winget install "Power Toys" --version ⇥` | `winget install "Power Toys" --version 0.19.2` | `0.19.2` is the highest version of Power Toys at the time of writing ## PowerShell You can add the argument completer to your `$PROFILE`, which will enable it in all subsequent PowerShell sessions. For more information, see [How to create your profile](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_profiles#how-to-create-a-profile) and [Profiles and execution policy](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_profiles#profiles-and-execution-policy). Here is the PowerShell command to add to your `$PROFILE`: ```PowerShell Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) [Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new() $Local:word = $wordToComplete.Replace('"', '""') $Local:ast = $commandAst.ToString().Replace('"', '""') winget complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } ``` ## Command Reference The complete command takes 3 required arguments: Argument | Description --- | --- `--word` | The current word that is being completed; the token that the cursor is located within. Can be empty to indicate no current value at the cursor, but if provided, it must appear as a substring in the command line. `--commandline` | The entire current command line, including `winget`. See the examples above; everything but the tab character (`⇥`) should be provided to this argument. `--position` | The current position of the cursor in the command line. Can be greater than the length of the command line string to indicate at the end. When a word value is provided, the completion operates in replacement mode. It will suggest completions that would fit correctly at this location that also start with the given word value. When a word value is not provided (an empty value is provided for word, ex. `--word=`), the completion operates in insertion mode. It will suggest completions that would fit as a new value in the cursor's location. Based on the arguments, the completions suggested can be one of: 1. A sub command :: The cursor is located just after a command and there are sub commands available. 2. An argument specifier :: The cursor is not positioned after an argument specifier that expects a value, and there are arguments available. 3. An argument value :: The cursor is positioned after an argument specifier that expects a value, or a positional argument is expected. After evaluating all of these cases, the potential completions are output, one on each line. If the completion string contains a space, it is wrapped in quotations. ================================================ FILE: doc/Developing.md ================================================ # Developer guidance ## Prerequisites * Windows 10 1809 (17763) or later * [Developer Mode enabled](https://docs.microsoft.com/windows/uwp/get-started/enable-your-device-for-development) * [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) * Or use WinGet to install it ;) (although you may need to adjust the workloads via Tools->Get Tools and Features...) * The following workloads: * .NET Desktop Development * Desktop Development with C++ * Universal Windows Platform Development * Check [.vsconfig file](../.vsconfig) for full components list * [Windows SDK for Windows 11 (10.0.26100)](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/) > [!NOTE] > You can also get it through `winget install Microsoft.WindowsSDK.10.0.26100` or via Visual Studio > Get Tools and Features > Individual Components > Windows 10 SDK (10.0.26100.0) * The following extensions: * [Microsoft Visual Studio Installer Projects](https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects) ## Building the client 1. Clone the repository 2. Configure your system using the [configuration file](../.config/configuration.winget) in the repository. Run one of the following configurations from the project root so relative paths resolve correctly: - For VS Community: `winget configure .config/configuration.winget` - For VS Professional: `winget configure .config/configuration.vsProfessional.winget` - For VS Enterprise: `winget configure .config/configuration.vsEnterprise.winget` 3. Run `vcpkg integrate install` from the Developer Command Prompt / Developer PowerShell for VS 2022. This is a one-time setup step until the configuration file in step 2 is updated to work with vcpkg setup. Open `winget-cli\src\AppInstallerCLI.sln` in Visual Studio and build. We currently only build using the solution; command-line methods of building a VS solution should work as well. ## Running and Debugging After the build finishes, deploy the solution from Build > Deploy Solution. You can then run the client from the command line using `wingetdev`. To enable step-through debugging, right click on `AppInstallerCLIPackage` in the Solution Explorer, select Properties, and navigate to the Debug tab. In the Debugger type selection, change "Application process" and "Background task process" to "Native Only". This will allow you to add breakpoints and step through the code. The main entry point for the client is in `src/AppInstallerCLI/main.cpp` The best way to debug the client is to select `Do not launch, but debug my code when it starts` in the `Debug` tab and start the debugging session with F5. You can then use the `wingetdev` command in a terminal session, or any PowerShell code for COM API interaction, which will get picked up by the debugger. ## Running Unit Tests The unit tests are located inside the `AppInstallerCLITests` project. When the solution is built, all tests are compiled under `src///AppInstallerCLITests`. An executable `AppInstallerCLITests.exe` is generated in this directory to run the tests. Run `AppInstallerCLITests.exe` from the command line to execute the tests. To see all available options, run `AppInstallerCLITests.exe --help`. > [!TIP] > If you just want to run a particular test, you can specify the test name as an argument to the executable. For example, `AppInstallerCLITests.exe EnsureSortedErrorList`. ================================================ FILE: doc/ReleaseNotes.md ================================================ ## New in v1.29 # New Feature: Source Priority > [!NOTE] > Experimental under `sourcePriority`; defaulted to disabled. With this feature, one can assign a numerical priority to sources when added or later through the `source edit` command. Sources with higher priority are sorted first in the list of sources, which results in them getting put first in the results if other things are equal. > [!TIP] > Search result ordering in winget is currently based on these values in this order: > 1. Match quality (how well a valid field matches the search request) > 2. Match field (which field was matched against the search request) > 3. Source order (was always relevant, but with priority you can more easily affect this) Beyond the ability to slightly affect the result ordering, commands that primarily target available packages (largely `install`) will now prefer to use a single result from a source with higher priority rather than prompting for disambiguation from the user. Said another way, if multiple sources return results but only one of those sources has the highest priority value (and it returned only one result) then that package will be used rather than giving a "multiple packages were found" error. This has been applied to both winget CLI and PowerShell module commands. ### REST result match criteria update Along with the source priority change, the results from REST sources (like `msstore`) now attempt to correctly set the match criteria that factor into the result ordering. This will prevent them from being sorted to the top automatically. ## Minor Features ### --no-progress flag Added a new `--no-progress` command-line flag that disables all progress reporting (progress bars and spinners). This flag is universally available on all commands and takes precedence over the `visual.progressBar` setting. Useful for automation scenarios or when running WinGet in environments where progress output is undesirable. ### Authenticated GitHub API requests in PowerShell module The PowerShell module now automatically uses `GH_TOKEN` or `GITHUB_TOKEN` environment variables to authenticate GitHub API requests. This significantly increases the GitHub API rate limit, preventing failures in CI/CD pipelines. Use `-Verbose` to see which token is being used. ## Bug Fixes ================================================ FILE: doc/Settings.md ================================================ # WinGet CLI Settings You can configure WinGet by editing the `settings.json` file. Running `winget settings` will open the file in the default json editor; if no editor is configured, Windows will prompt for you to select an editor, and Notepad is a sensible option if you have no other preference. ## File Location Settings file is located in %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json If you are using the non-packaged WinGet version by building it from source code, the file will be located under %LOCALAPPDATA%\Microsoft\WinGet\Settings\settings.json If you are creating a settings file from scratch, make sure that the line `"$schema": "https://aka.ms/winget-settings.schema.json",` is in it. ## Source The `source` settings involve configuration to the WinGet source. ```json "source": { "autoUpdateIntervalInMinutes": 3 }, ``` ### autoUpdateIntervalInMinutes A positive integer represents the update interval in minutes. The check for updates only happens when a source is used. A zero will disable the check for updates to a source. Any other values are invalid. - Disable: 0 - Default: 15 To manually update the source use `winget source update` ## Visual The `visual` settings involve visual elements that are displayed by WinGet ### progressBar Style of the progress spinner and bar that WinGet displays when not specified by arguments. In addition, all options except `disabled` send [Virtual Terminal progress](https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC) updates that any supporting terminal may display visually. > [!NOTE] > You can also disable progress output for a single command using the `--no-progress` flag, which takes precedence over this setting. |Value|Description|Release| |---|---|---| |`accent` (default)|Use the [Windows Accent color](https://support.microsoft.com/en-us/windows/change-colors-in-windows-d26ef4d6-819a-581c-1581-493cfcc005fe)|1.0| |`retro`|Use the current foreground terminal color|1.0| |`rainbow`|Progress through a rainbow of colors|1.0| |`sixel`|Use sixel images; requires a terminal that supports displaying sixels, such as [Windows Terminal](https://github.com/microsoft/terminal/releases) 1.22.2362 or later|1.9| |`disabled`|No progress will be displayed|1.9| ```json "visual": { "progressBar": "accent" }, ``` ### anonymizeDisplayedPaths Replaces some known folder paths with their respective environment variable. Defaults to true. ```json "visual": { "anonymizeDisplayedPaths": true }, ``` ### enableSixels Enables output of sixel images in certain contexts. Defaults to false. ```json "visual": { "enableSixels": true }, ``` ## Install Behavior The `installBehavior` settings affect the default behavior of installing and upgrading (where applicable) packages. ### Disable Install Notes The `disableInstallNotes` behavior affects whether installation notes are shown after a successful install. Defaults to `false` if value is not set or is invalid. ```json "installBehavior": { "disableInstallNotes": true }, ``` ### Portable Package User Root The `portablePackageUserRoot` setting affects the default root directory where packages are installed to under `User` scope. This setting only applies to packages with the `portable` installer type. Defaults to `%LOCALAPPDATA%/Microsoft/WinGet/Packages/` if value is not set or is invalid. > Note: This setting value must be an absolute path. ```json "installBehavior": { "portablePackageUserRoot": "C:/Users/FooBar/Packages" }, ``` ### Portable Package Machine Root The `portablePackageMachineRoot` setting affects the default root directory where packages are installed to under `Machine` scope. This setting only applies to packages with the `portable` installer type. Defaults to `%PROGRAMFILES%/WinGet/Packages/` if value is not set or is invalid. > Note: This setting value must be an absolute path. ```json "installBehavior": { "portablePackageMachineRoot": "C:/Program Files/Packages/Portable" }, ``` ### Skip Dependencies The 'skipDependencies' behavior affects whether dependencies are installed for a given package. Defaults to 'false' if value is not set or is invalid. ```json "installBehavior": { "skipDependencies": true }, ``` ### Archive Extraction Method The `archiveExtractionMethod` behavior affects how installer archives are extracted. Currently there are two supported values: `Tar` or `ShellApi`. `Tar` indicates that the archive should be extracted using the tar executable ('tar.exe') while `shellApi` indicates using the Windows Shell API. Defaults to `shellApi` if value is not set or is invalid. ```json "installBehavior": { "archiveExtractionMethod": "tar" | "shellApi" }, ``` ### Preferences and Requirements Some of the settings are duplicated under `preferences` and `requirements`. `preferences` affect how the various available options are sorted when choosing the one to act on. For instance, the default scope of package installs is for the current user, but if that is not an option then a machine level installer will be chosen. `requirements` filter the options, potentially resulting in an empty list and a failure to install. In the previous example, a user scope requirement would result in no applicable installers and an error. Any arguments passed on the command line will effectively override the matching `requirement` setting for the duration of that command. > [!NOTE] > > - These settings are only applied for the `winget install` command. > - Other commands like `winget configure` are not affected by these settings. ### Scope The `scope` behavior affects the choice between installing a package for the current user or for the entire machine. The matching parameter is `--scope`, and uses the same values (`user` or `machine`). ```json "installBehavior": { "preferences": { "scope": "user" } }, ``` ### Locale The `locale` behavior affects the choice of installer based on installer locale. The matching parameter is `--locale`, and uses bcp47 language tag. ```json "installBehavior": { "preferences": { "locale": [ "en-US", "fr-FR" ] } }, ``` ### Architectures The `architectures` behavior affects what architectures will be selected when installing a package. The matching parameter is `--architecture`. Note that only architectures compatible with your system can be selected. ```json "installBehavior": { "preferences": { "architectures": ["x64", "arm64"] } }, ``` ### Installer Types The `installerTypes` behavior affects what installer types will be selected when installing a package. It can also determine which type to install by default if a manifest has multiple types: The list is in priority order, with the first listed type being preferred over the others, and so on. This is convenient for users who for instance prefer portable packages or MSIX/AppX installations. The matching parameter is `--installer-type`, which will override the settings. Allowed values as of version 1.12.470 include: `appx`, `burn`, `exe`, `font`, `inno`, `msi`, `msix`, `msstore`, `nullsoft`, `portable`, `wix`, `zip` By default, and with all other properties being equal, WinGet defaults to the installer type that is listed first in the manifest's installer YAML if the package has not been installed yet. If it is already installed, the same installer type will be required to ensure a proper upgrade. ```json "installBehavior": { "preferences": { "installerTypes": ["msi", "msix"] } }, ``` ### Default install root The `defaultInstallRoot` affects the install location when a package requires one. This can be overridden by the `--location` parameter. This setting is only used when a package manifest includes `InstallLocationRequired`, and the actual location is obtained by appending the package ID to the root. ```json "installBehavior": { "defaultInstallRoot": "C:/installRoot" }, ``` ### Maximum resumes The `maxResumes` setting determines the maximum number of times that a command may be resumed automatically. The default value is 3. ```json "installBehavior": { "maxResumes": 3 }, ``` > Note: [The resume behavior is an experimental feature.](#resume) ## Uninstall Behavior The `uninstallBehavior` settings affect the default behavior of uninstalling (where applicable) packages. ### Purge Portable Package The `purgePortablePackage` behavior affects the default behavior for uninstalling a portable package. If set to `true`, uninstall will remove all files and directories relevant to the `portable` package. This setting only applies to packages with the `portable` installer type. Defaults to `false` if value is not set or is invalid. ```json "uninstallBehavior": { "purgePortablePackage": true }, ``` ## Configure Behavior The `configureBehavior` settings affect the default behavior of applying a configuration. ### Default Module Root The `defaultModuleRoot` behavior affects the default root directory where modules are installed to. Defaults to `%LOCALAPPDATA%/Microsoft/WinGet/Configuration/Modules` if value is not set or is invalid. > Note: This setting value must be an absolute path. ```json "configureBehavior": { "defaultModuleRoot": "C:/Program Files/Modules/" }, ``` ## Telemetry The `telemetry` settings control whether winget writes ETW events that may be sent to Microsoft on a default installation of Windows. See [details on telemetry](../README.md#datatelemetry), and our [primary privacy statement](../PRIVACY.md). ### disable ```json "telemetry": { "disable": true }, ``` If set to true, the `telemetry.disable` setting will prevent any event from being written by the program. ## Logging The `logging` settings control the level of detail in log files. ### level `--verbose-logs` will override this setting and always creates a verbose log. Defaults to `info` if value is not set or is invalid. ```json "logging": { "level": "verbose" | "info" | "warning" | "error" | "critical" }, ``` ### channels The valid values in this array are defined in the function `GetChannelFromName` in the [logging code](../src/AppInstallerSharedLib/AppInstallerLogging.cpp). These align with the ***channel identifier*** found in the log files. For example, ***`CORE`*** in: ```plaintext 2023-12-06 19:17:07.988 [CORE] WinGet, version [1.7.0-preview], activity [{24A91EA8-46BE-47A1-B65C-CEBCE90B8675}] ``` In addition, there are special values that cover multiple channels. `default` is the default set of channels, while `all` is all of the channels. Invalid values are ignored. ```json "logging": { "channels": ["default"] }, ``` ### file The `file` settings control the log files generated by winget during operation. These settings apply to the automatic cleanup that happens whenever a Windows Package Manager process is run. They only apply to the default log location, which contains winget logs, AppInstaller logs (the MSIX install UI), and is the default location where installer logs are placed. The automatic cleanup happens at the beginning of the process, so the log file(s) generated by the current process will not be considered in the limits. |Setting|Description|Default|Note| |---|---|---|---| |`ageLimitInDays`|The maximum age, in days, of files in the log directory; older files are deleted.|7 (days)|Set to 0 to disable this limit.| |`totalSizeLimitInMB`|The maximum size, in megabytes, of all files in the log directory; the oldest files are deleted first.|128 (MB)|Set to 0 to disable this limit.| |`countLimit`|The maximum number of files in the log directory; the oldest files are deleted first.|0|Set to 0 (the default) to disable this limit.| These settings apply to the log files that winget writes, only as they are being written. They do not apply to files written by installers or the AppInstaller UI. |Setting|Description|Default|Note| |---|---|---|---| |`individualSizeLimitInMB`|The maximum size, in megabytes, of an individual log file. If a file would exceed this limit, the logs will wrap. Note that this limit is approximate and the actual files may exceed it by a few bytes.|16 (MB)|Set to 0 to disable this limit.| ```json "logging": { "file": { "ageLimitInDays": 7, "totalSizeLimitInMB": 128, "countLimit": 0, "individualSizeLimitInMB": 16, } }, ``` ## Network The `network` settings influence how winget uses the network to retrieve packages and metadata. ### Downloader The `downloader` setting controls which code is used when downloading packages. The default is `default`, which may be any of the options based on our determination. `wininet` uses the [WinINet](https://docs.microsoft.com/windows/win32/wininet/about-wininet) APIs, while `do` uses the [Delivery Optimization](https://support.microsoft.com/windows/delivery-optimization-in-windows-10-0656e53c-15f2-90de-a87a-a2172c94cf6d) service. The `doProgressTimeoutInSeconds` setting updates the number of seconds to wait without progress before fallback. The default number of seconds is 60, minimum is 1 and the maximum is 600. ```json "network": { "downloader": "do", "doProgressTimeoutInSeconds": 60 } ``` ## Interactivity The `interactivity` settings control whether winget may show interactive prompts during execution. Note that this refers only to prompts shown by winget itself and not to those shown by package installers. ### disable ```json "interactivity": { "disable": true }, ``` If set to true, the `interactivity.disable` setting will prevent any interactive prompt from being shown. ## Experimental Features To allow work to be done and distributed to early adopters for feedback, settings can be used to enable "experimental" features. The `experimentalFeatures` settings involve the configuration of these "experimental" features. Individual features can be enabled under this node. The example below shows sample experimental features. ```json "experimentalFeatures": { "experimentalCmd": true, "experimentalArg": false }, ``` ### directMSI This feature enables the Windows Package Manager to directly install MSI packages with the MSI APIs rather than through msiexec. Note that when silent installation is used this is already in affect, as MSI packages that require elevation will fail in that scenario without it. You can enable the feature as shown below. ```json "experimentalFeatures": { "directMSI": true }, ``` ### resume This feature enables support for some commands to resume. You can enable the feature as shown below. ```json "experimentalFeatures": { "resume": true }, ``` ### fonts This feature enables support for fonts via `winget settings`. The `winget font list` command will list installed font families and the number of installed font faces. ```json "experimentalFeatures": { "fonts": true }, ``` ### sourcePriority This feature enables sources to have a priority value assigned. Sources with a higher priority will appear earlier in search results and will be selected for installing new packages when multiple sources have a matching package. Note that search result ordering is dependent on several factors, and source priority is the lowest field in that currently (match quality and field are more important). ```json "experimentalFeatures": { "sourcePriority": true }, ``` ================================================ FILE: doc/admx/DesktopAppInstaller.admx ================================================ ================================================ FILE: doc/admx/en-US/DesktopAppInstaller.adml ================================================ App Installer App Installer Desktop App Installer Enable Windows Package Manager This policy controls whether the Windows Package Manager can be used by users. If you enable or do not configure this setting, users will be able to use the Windows Package Manager. If you disable this setting, users will not be able to use the Windows Package Manager. Enable Windows Package Manager Settings This policy controls whether users can change their settings. If you enable or do not configure this setting, users will be able to change settings for the Windows Package Manager. If you disable this setting, users will not be able to change settings for the Windows Package Manager. Enable Windows Package Manager Experimental Features This policy controls whether users can enable experimental features in the Windows Package Manager. If you enable or do not configure this setting, users will be able to enable experimental features for the Windows Package Manager. If you disable this setting, users will not be able to enable experimental features for the Windows Package Manager. Enable Windows Package Manager Local Manifest Files This policy controls whether users can install packages with local manifest files. If you enable or do not configure this setting, users will be able to install packages with local manifests using the Windows Package Manager. If you disable this setting, users will not be able to install packages with local manifests using the Windows Package Manager. Enable Windows Package Manager Microsoft Store Source Certificate Validation Bypass This policy controls whether the Windows Package Manager will validate the Microsoft Store certificate hash matches to a known Microsoft Store certificate when initiating a connection to the Microsoft Store Source. If you enable this policy, the Windows Package Manager will bypass the Microsoft Store certificate validation. If you disable this policy, the Windows Package Manager will validate the Microsoft Store certificate used is valid and belongs to the Microsoft Store before communicating with the Microsoft Store source. If you do not configure this policy, the Windows Package Manager administrator settings will be adhered to. Enable Windows Package Manager Hash Override This policy controls whether or not the Windows Package Manager can be configured to enable the ability override the SHA256 security validation in settings. If you enable or do not configure this policy, users will be able to enable the ability override the SHA256 security validation in the Windows Package Manager settings. If you disable this policy, users will not be able to enable the ability override the SHA256 security validation in the Windows Package Manager settings. Enable Windows Package Manager Local Archive Malware Scan Override This policy controls the ability to override malware vulnerability scans when installing an archive file using a local manifest using the command line arguments. If you enable this policy, users can override the malware scan when performing a local manifest install of an archive file. If you disable this policy, users will be unable to override the malware scan of an archive file when installing using a local manifest. If you do not configure this policy, the Windows Package Manager administrator settings will be adhered to. Enable Windows Package Manager Default Source This policy controls the default source included with the Windows Package Manager. If you do not configure this setting, the default source for the Windows Package Manager will be available and can be removed. If you enable this setting, the default source for the Windows Package Manager will be available and cannot be removed. If you disable this setting the default source for the Windows Package Manager will not be available. Enable Windows Package Manager Microsoft Store Source This policy controls the Microsoft Store source included with the Windows Package Manager. If you do not configure this setting, the Microsoft Store source for the Windows Package manager will be available and can be removed. If you enable this setting, the Microsoft Store source for the Windows Package Manager will be available and cannot be removed. If you disable this setting the Microsoft Store source for the Windows Package Manager will not be available. Enable Windows Package Manager Font Source This policy controls the Font source included with the Windows Package Manager. If you do not configure this setting, the Font source for the Windows Package manager will be available and can be removed. If you enable this setting, the Font source for the Windows Package Manager will be available and cannot be removed. If you disable this setting the Font source for the Windows Package Manager will not be available. Set Windows Package Manager Source Auto Update Interval In Minutes This policy controls the auto-update interval for package-based sources. The default source for Windows Package Manager is configured such that an index of the packages is cached on the local machine. The index is downloaded when a user invokes a command, and the interval has passed. If you disable or do not configure this setting, the default interval or the value specified in the Windows Package Manager settings will be used. If you enable this setting, the number of minutes specified will be used by the Windows Package Manager. Enable Windows Package Manager Additional Sources This policy controls additional sources provided by the enterprise IT administrator. If you do not configure this policy, no additional sources will be configured for the Windows Package Manager. If you enable this policy, the additional sources will be added to the Windows Package Manager and cannot be removed. The representation for each additional source can be obtained from installed sources using 'winget source export'. If you disable this policy, no additional sources can be configured for the Windows Package Manager. Enable Windows Package Manager Allowed Sources This policy controls additional sources allowed by the enterprise IT administrator. If you do not configure this policy, users will be able to add or remove additional sources other than those configured by policy. If you enable this policy, only the sources specified can be added or removed from the Windows Package Manager. The representation for each allowed source can be obtained from installed sources using 'winget source export'. If you disable this policy, no additional sources can be configured for the Windows Package Manager. Enable App Installer ms-appinstaller protocol This policy controls whether users can install packages from a website that is using the ms-appinstaller protocol. If you enable this setting, users will be able to install packages from websites that use this protocol. If you disable or do not configure this setting, users will not be able to install packages from websites that use this protocol. Enable Windows Package Manager command line interfaces This policy determines if a user can perform an action using the Windows Package Manager through a command line interface (WinGet CLI, or WinGet PowerShell). If you disable this policy, users will not be able execute the Windows Package Manager CLI, and PowerShell cmdlets. If you enable, or do not configure this policy, users will be able to execute the Windows Package Manager CLI commands, and PowerShell cmdlets. (Provided “Enable App Installer” policy is not disabled). This policy does not override the “Enable App Installer” policy. Enable Windows Package Manager Configuration This policy controls whether the Windows Package Manager configuration feature can be used by users. If you enable or do not configure this setting, users will be able to use the Windows Package Manager configuration feature. If you disable this setting, users will not be able to use the Windows Package Manager configuration feature. Enable Windows Package Manager Proxy command line options This policy controls whether the Windows Package Manager usage of proxy can be configured by users through the command line. If you enable this setting, users will be able to configure the Windows Package Manager's use of proxy through the command line. If you disable or do not configure this setting, users will not be able to to configure the Windows Package Manager's use of proxy through the command line. Enable MCP Server for Windows Package Manager This policy controls whether the Windows Package Manager Model Context Protocol (MCP) server can be used. If you enable or do not configure this setting, users will be able to use the Windows Package Manager's MCP server. If you disable this setting, users will not be able to to use the Windows Package Manager's MCP server. Set Windows Package Manager Default Proxy This policy controls the default proxy used by the Windows Package Manager. If you disable or do not configure this setting, no proxy will be used by default. If you enable this setting, the specified proxy will be used by default. Enable App Installer Allowed Zones for MSIX Packages This policy controls whether App Installer allows installing packages originating from specific URL Zones. A package's origin is determined by its URI and whether a Mart-of-the-Web (MotW) is present. If multiple URIs are involved, all of them are considered; for example, when using a .appinstaller file that involves redirection. If you enable this policy, users will be able to install MSIX packages according to the configuration for each zone. If you disable or do not configure this policy, users will be able to install MSIX packages from any zone except for Untrusted. Allow Block Enable Microsoft SmartScreen checks for MSIX Packages This policy controls whether App Installer performs Microsoft SmartScreen checks when installing MSIX packages. If you enable or do not configure this policy, the package URI will be evaluated with Microsoft SmartScreen before installation. This check is only done for packages that come from the internet. If you disable, Microsoft SmartScreen will not be consulted before installing a package. Source Auto Update Interval In Minutes Additional Sources: Allowed Sources: Local Machine Intranet Trusted Sites Internet Untrusted Sites ================================================ FILE: doc/specs/#1012 - Show dependencies.md ================================================ --- author: Florencia Zanollo fzanollo/t-fzanollo@microsoft.com created on: 2021-05-28 last updated: 2021-05-28 issue id: 1012 --- # Show dependencies For [#1012](https://github.com/microsoft/winget-cli/issues/1012) ## Abstract Several packages require other packages as dependencies. The Windows Package Manager should be able to support declared dependencies and, as a first step to manage them, inform the user about any required ones. ## Solution Design The Windows Package Manager should be able to report package dependency information for each of the four different types of dependencies declared in the [v1.0 manifest schemas](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.0.0/). * Windows Features * Windows Libraries * Package Dependencies (same source) * External Dependencies Only dependencies declared in the manifest/installer will be shown, not the entire dependency graph. ### install The install command will enumerate the dependencies of the package version being installed as follows: ``` > winget install Notepad++ Found Notepad++ [Notepad++.Notepad++] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. This package requires the following dependencies: - Windows Feature: Hyper-V - Package: Microsoft.WindowsTerminal Downloading https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v7.9.5/npp.7.9.5.Installer.x64.exe Successfully verified installer hash Starting package install... ``` ### show The show command will enumerate the dependencies of the package as follows: ``` > winget show Notepad++ Found Notepad++ [Notepad++.Notepad++] Version: 7.9.5 Publisher: Notepad++ Team Author: Don Ho Moniker: notepad++ Description: Notepad++ is a free (as in “free speech” and also as in “free beer”) source code editor and Notepad replacement that supports several languages. Running in the MS Windows environment, its use is governed by GNU General Public License. Homepage: https://notepad-plus-plus.org/ License: GPL-2.0-only License Url: https://raw.githubusercontent.com/notepad-plus-plus/notepad-plus-plus/v7.9.5/LICENSE Installer: Type: Nullsoft Locale: en-US Download Url: https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v7.9.5/npp.7.9.5.Installer.x64.exe SHA256: 4881548cd86491b453520e83c19292c93b9c6ce485a1f9eb9301e3913a9baced Dependencies: - Windows Feature: Hyper-V - Package: Microsoft.WindowsTerminal ``` ### upgrade The upgrade command will enumerate the dependencies of the package as follows: ``` > winget upgrade Notepad++ Found Notepad++ [Notepad++.Notepad++] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. This package requires the following dependencies: - Windows Feature: Hyper-V - Package: Microsoft.WindowsTerminal Successfully verified installer hash Starting package install... ``` As of now, it will not try to validate nor install any of the dependencies for any package version. ### uninstall Uninstall needs more work as we don't have the actual installer to get the dependencies from. This will not be added in this step. ### validate Will gather and report dependencies for all of the installers found. Will not check if they are valid nor if there are duplicates. ``` Manifest has the following dependencies that were not validated; ensure that they are valid: - Windows Feature: Hyper-V - Package: Microsoft.WindowsTerminal Manifest validation succeeded. ``` ### import Will gather all the dependencies from the packages included in the import and show them together before starting. ``` The packages found in this import have the following dependencies: - Windows Feature: Hyper-V Containers - Windows Libraries: Microsoft.WinJS - Package: Microsoft.WindowsTerminal - External: JDK-11.0.10 Found [Notepad++.Notepad++] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Downloading https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8/npp.8.0.Installer.x64.exe Successfully verified installer hash Starting package install... Successfully installed Found [plex.Plex] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Successfully verified installer hash Starting package install... Successfully installed ``` ## Capabilities It's only an informational feature, will not check if the dependency is a valid one, nor if the source is available. If a dependency is declared more than once (for example when gathering all dependencies in an import) it will only show the highest minimum version needed. Keep in mind dependencies can be declared on the root manifest and on each of the installers. If they happen to be declared in both, installer's dependencies will override those of the manifest. With the manifest's dependencies working as a default whenever installer's dependencies are not declared. ## Future considerations It may be able to enable/disable this feature using extra options for the command. ================================================ FILE: doc/specs/#1155 - Enhancements to list command.md ================================================ --- author: Demitrius Nelon @denelon created on: 2021-06-11 last updated: 2021-06-11 issue id: 1155 --- # Enhancements to List Command For [#1155](https://github.com/microsoft/winget-cli/issues/1155). ## Abstract The `winget list` command was designed to display all packages installed on a users Windows 10 machine in Add / Remove Programs (ARP). The command would also show which Apps with possible upgrades were available from sources configured in the Windows Package Manager. This specification proposes enhancements to the output provided by the list command. The first enhancement is adding the values for "Available" and "Source" for every package in configured sources rather that just packages with upgrades available. The second enhancement is reducing the set of packages to the source specified when the source is passed as an argument to the list command. The third enhancement is adding an argument to provide the list of Apps with no matching package in configured sources. ## Inspiration Several suggestions have been made on how to improve user experience. Recently, many users have been attempting to identify Apps on their system with no corresponding package in the Microsoft Community Repository. ## Solution Design Modify the output behavior for `winget list` to display available version from any source. When a package is available via more than once source, the first configured source should be displayed. >Note: Users will be able to see if packages are available from individual sources by specifying the source as an argument to the list command. Modify the output behavior for `winget list -s ` to display only packages available from the specified source. Add a new argument `-u, --unavailable` to the list command to display Apps installed in ARP not matching any configured source. ## UI/UX Design The UI would remain consistent with the current tabular output. ## Capabilities ### Accessibility This should not change impact accessibility for users of screen readers, assistive input devices, etc. ### Security This should not change or impact security? ### Reliability This should not impact reliability. ### Compatibility This will change existing behaviors. The `winget list` output currently displays available versions and sources in addition to all other installed Apps. The `winget upgrade` command should be used instead to view available upgrades. ### Performance, Power, and Efficiency ## Potential Issues This may impact performance, but it should be minor. Another Issue [#964](https://github.com/microsoft/winget-cli/issues/964) "Improvements to the list command" was created to address performance issues related to the `winget list` command. ## Future considerations This feature may simplify the process of identifying packages missing from source repositories. ## Resources Issue [#977](https://github.com/microsoft/winget-cli/issues/977) "`winget list` should be able to show hidden apps" is also related to the `winget list` command. ================================================ FILE: doc/specs/#1287 - Management of package type dependencies.md ================================================ --- author: Florencia Zanollo fzanollo/t-fzanollo@microsoft.com created on: 2021-07-15 last updated: 2021-07-15 issue id: 1287 --- # Management of package type dependencies For [#1287](https://github.com/microsoft/winget-cli/issues/1287) ## Abstract As a new step in the pursue of dependency management, the Windows Package Manager will take care of one of the four types of dependencies (Package) for most of the commands. The underlying logic of the code will prove useful when implementing the rest of the types and commands; since it will manage dependencies' graph building and validation, as well as the correct order of installation. ## Solution Design The Windows Package Manager will build the dependencies' graph and corroborate there's not a cyclic dependency, among other validations (depending on the command). ### Install: Install command will build the dependency graph at runtime, from installer information. It will report on the other three types of dependencies and manage package type installation/validation (for new/installed dependencies respectively). As a best effort, in case a cyclic dependency exists, Windows Package Manager will inform the user but try to install in some order; this is because most of the dependencies are at run time so there shouldn't present an issue on install. While building the graph, install command will verify: * Availability: the package declared as dependency will need to be an existing one. * Installed version: it will check for existing versions of the dependency and update if the minimum required version is bigger than the installed. * Version: minimum required version will need to be less or equal to the latest available one. Information will be shown about failures, existence or installation progress for each of the dependencies required. When installing from a local manifest, a dependency source will be required (using --dependency-source parameter), but only if the target has at least one package type dependency declared. ``` > winget install Notepad++ Found Notepad++ [Notepad++.Notepad++] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. This package requires the following dependencies: - Windows Feature: Hyper-V - Package: Microsoft.WindowsTerminal Building package type dependencies' graph: No errors or cyclic dependencies found. Installing package type dependencies: Found WindowsTerminal [Microsoft.WindowsTerminal] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Downloading (...) Successfully verified installer hash Starting package install... Downloading https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v7.9.5/npp.7.9.5.Installer.x64.exe Successfully verified installer hash Starting package install... ``` ### Import: Import command will report all first level dependencies beforehand and after will work as install command for each of the packages found, iteratively. Ex. if packages *foo* and *foo1* are part of the import, dependencies for *foo* will be fetch and installed before continuing with *foo1*. ``` The packages found in this import have the following dependencies: - Windows Feature: Hyper-V Containers - Windows Libraries: Microsoft.WinJS - Package: Microsoft.WindowsTerminal - External: JDK-11.0.10 Found [Notepad++.Notepad++] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Building package type dependencies' graph: No errors or cyclic dependencies found. Installing package type dependencies: Found WindowsTerminal [Microsoft.WindowsTerminal] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Downloading (...) Successfully verified installer hash Starting package install... Found [plex.Plex] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. (...) ``` ### Update: * Update one: will also work as install command, we need to check again for dependencies as the new installer can have new ones or bigger minimum required versions. * Update many: will work iteratively (as import). ``` > winget upgrade Notepad++ Found Notepad++ [Notepad++.Notepad++] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. This package requires the following dependencies: - Windows Feature: Hyper-V - Package: Microsoft.WindowsTerminal Building package type dependencies' graph: No errors or cyclic dependencies found. Installing package type dependencies: Found WindowsTerminal [Microsoft.WindowsTerminal] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Downloading (...) Successfully verified installer hash Starting package install... Successfully verified installer hash Starting package install... ``` ## Capabilities Will manage package type dependencies installation for install, import and update commands. Will not manage or try to install any of the other types of dependencies (Windows Features, Windows Libraries, External). ================================================ FILE: doc/specs/#140 - ZIP Support.md ================================================ --- author: Ryan Fu @ryfu-msft created on: 2022-05-24 last updated: 2022-12-13 issue id: 140 --- # Support ZIP/Archive InstallerTypes For [#140](https://github.com/microsoft/winget-cli/issues/140) ## Abstract This spec outlines the design for supporting the installation of packages using archive files. The initial implementation will aim to support only ZIP files. This spec will be updated if support for other archive types such as .tar.gz or .cab files are added. ## Design Flow/Implementation Because ZIP archive files are essentially a compressed directory containing the relevant installer files, the install flow will need to add the following steps: 1. Perform checks on the downloaded ZIP archive for potential malware or threats. 2. Extract the contents of the ZIP archive to a temporary location. 3. Execute the appropriate install flow using the extracted installer based on the installer type derived from the manifest. >NOTE: The initial implementation will first support installing ZIPs that contain only basic installers (msix, msi, or exe), then portable apps. ## Manifest Changes: Addition of `NestedInstallerType`: - Enumeration of supported nested installerTypes contained inside an archive file. Addition of `NestedInstallerFile` - Object containing metadata about a nested installer file contained inside an archive. - Properties: - `RelativeFilePath`: The relative path to a nested installer file contained inside an archive. - `PortableCommandAlias`: The command alias to be used for calling the package. Only applies to a nested portable package. These changes would be added to the `Installer` object with `NestedInstallerFiles` representing an array of `NestedInstallerFile`. ### Manifest Validation: - Exactly one `NestedInstallerFile` entry must be specified for a non-portable nested installer type. - Multiple `NestedInstallerFile` entries can be specified for portable nested installer type to support a suite of portable exes that would all be installed under the same package. - If a ZIP `InstallerType` is specified, a `NestedInstallerType` is required to be specified. ## Supported Install Scenarios: The initial implementation of this feature will only support the following installation scenarios: - Single base installer (exe, msi, msix) contained inside an archive file. - Single or multiple portable exes bundled as a suite inside an archive file. ## ZIP Extraction The extraction of ZIPs will be done using Windows Shell APIs. ZIP files can be represented as a [ShellFolder](https://docs.microsoft.com/windows/win32/api/shobjidl_core/nn-shobjidl_core-ishellfolder) object that can be used to manage the contents of the ZIP file. File contents are represented as [ShellItems](https://docs.microsoft.com/windows/win32/api/shobjidl_core/nn-shobjidl_core-ishellitem), which can be handled using the methods exposed by the [IFileOperation interface](https://docs.microsoft.com/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifileoperation). In our initial implementation, we will only support extracting the top level archive to a temporary location. This means that the `NestedInstallerFile` must not be contained in any nested archives. The appropriate install flow will proceed based on the specified `NestedInstallerType`. > If the community presents enough use cases that require decompressing additional layers, then we will consider extending this functionality and adding a separate manifest entry to override the default behavior of only unzipping the top level archive. > Since we are utilizing Windows Shell APIs, we need to ensure that we are not invoking a UI during the extraction. This can be done by [setting the operation flag to not display any UI](https://docs.microsoft.com/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileoperation-setoperationflags). > During implementation, we will also need to ensure that this process can work under SYSTEM context. ## ZIP Threat Detection ZIP and other archive file types are known threat vectors for malware in the form of ZIP compression bombs. Two of the most common types of compression bombs are multi-layered (recursive) and single-layered (non-recursive). ZIP bombs rely on a repetition of identical files that have extremely large compression ratios. Some examples include the following: 1. **Multi-layered (recursive)**: A ZIP file containing multiple layers of nested ZIP files that achieve high compression ratios when decompressed recursively. This technique is often used to bypass compression ratio checks performed by ZIP parsers for a single layer. 2. **Single-Layered (non-recursive)** A ZIP bomb that expands fully after a single round of decompression. The extremely high compression ratio of the ZIP bomb is achieved by overlapping files within the ZIP container. In order to protect our users from these possible threats, we will need to scan the ZIP file for malware using [Pure, a static analysis file format checker](https://github.com/ronomon/pure). > Pure is licensed under the [MIT license](https://github.com/ronomon/pure/blob/master/LICENSE). If the Pure library functions that are called on a given ZIP file detect malware, the process will terminate and a warning will be displayed to the user. The user can include `--ignore-local-archive-malware-scan` to bypass this check and continue installation at their own risk. To minimize unnecessary costs on performance during installation, these checks will only be applied when any of the following conditions are met: - Hash mismatch was overridden - Untrusted source (i.e. installing from a local manifest, private source or third-party REST sources) ## Supporting Nested Portable(s) in an Archive Currently, information regarding a single installed portable is stored in the ARP entry. In order to support installing a single or multiple portables contained inside an archive file, we will need to create a separate record that will be stored with the extracted files. This record will contain a table capturing the list of files that were created and placed down as well as various metadata to enable us to verify whether they have been modified. This table should replace most of the uninstall-related information that is stored in ARP for a given portable package. The table will contain the following information for each item that we create or place down during installation. | Metadata | Description | | ----------- | ----------- | | Path | Path to the item | | Flag | Flag to indicate what type of file was placed down (directory, symlink, exe) | | Hash | Hash for files with contents (exe) | | SymlinkTarget | Target exe for the created symlink (only applies to symlink files) | ================================================ FILE: doc/specs/#148 - Repair Support.md ================================================ --- author: Madhusudhan Gumbalapura Sudarshan @Madhusudhan-MSFT created on: 2023-12-01 last updated: 2024-08-27 issue id: 148 --- # Repair Feature for Windows Package Manager "For [#148](https://github.com/microsoft/winget-cli/issues/148)" ## Abstract This specification outlines the design and implementation of a repair feature for the Windows Package Manager.The repair feature aims to provide a convenient and reliable way for users to fix any issues that may arise with their installed applications, such as corrupted files, missing dependencies, or broken registry entries.The initial implementation will support repair for main installer types, such as Msi, Wix, Msix and MSStore installer types, which have native repair capabilities from their respective frameworks. Additionally, other installer types such as Burn/Exe/Nullsoft/Inno that can specify a custom repair switch in their YAML manifest files can also use the repair feature.The document also outlines the future plans for extending the repair feature to other installer types Portables that do not have a standard repair mechanism. ## Inspiration The motivation for creating a repair feature in the Windows Package Manager is to provide users with a convenient and consistent way to fix their malfunctioning applications, regardless of the installer type. Currently, users have to rely on different methods to repair their applications, such as using the Windows Settings app, the Control Panel, the command line, or the application's own repair tool. These methods are not always available, accessible, or intuitive, and they may vary depending on the application and the installer type. The repair feature aims to provide a unified experience for users to repair their applications, regardless of the installer type. ## Solution Design ### Repair Command Syntax `winget repair [[-q] ...] []` will initiate the repair of the specified package. The command will display an error message if the application is not installed. The command will attempt to repair the application if it is installed. The command will display a success message if the repair succeeds. The command will display an error message if the repair fails. #### Arguments | Argument | Description | |-------------|-------------| | **-q,--query** | The query used to search for an app. | #### Optional Arguments | Option | Description | |--------|-------------| | **-m, --manifest** | Must be followed by the path to the manifest (YAML) file.You can use the manifest to run the repair experience from a [local manifest file](#local-repair).| | **--id** | Limits the repair to the specified ID of the application.| | **--name** | Limits the repair to the specified name of the application.| | **--moniker** | Limits the repair to the specified moniker of the application.| | **-v --version** | Limits the repair to the specified version of the application.If not specified, the repair will be applied to the latest version of the application.| | **--architectures** | Select the architecture | | **-s --source** | Limits the search to specified source(s).Must be followed by the source name.| | **-o, --log** | Directs the logging to a log file. You must provide a path to a file that you have the write rights to. | | **-i --interactive** | Runs the repair in interactive mode.| | **-h --silent** | Runs the repair in silent mode.| | **-?, --help** | Get additional help on this command. | | **--accept-source-agreements** | Accept all source agreements during source operations | | **--logs, --open-logs** | Open the default logs location. | | **--locale** | Sets the locale.| ### Repair Feature for different Installer Types with Native Repair Capabilities - Msi, Wix : The repair command will use the built-in repair features of the MSI installer type. It will run the msiexec command with the default [repair options](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/msiexec#repair-options) to repair the application using a ShellExecute call. - Msix : The repair command will use the built-in repair features of the MSIX installer type. It will make an MSIX API call to register the application package, which will attempt to repair the package. > We can't do the same thing as setting app repair for MSIX app right now because we can't use the private APIs that it uses to do repair operations with winget. - MSStore : The repair command will make an MSStore API [StartProductInstallAsync](https://learn.microsoft.com/en-us/uwp/api/windows.applicationmodel.store.preview.installcontrol.appinstallmanager.startproductinstallasync?view=winrt-22621) call to repair the application with 'Repair' property of AppInstallOption set to true. ### Repair Feature for Installer Types that require Custom Repair Switch - Burn, Exe, Nullsoft & Inno : The custom switch for repair in the YAML manifest file will be used to perform the repair. The repair command will run the installer with the custom switch to repair the application. To have enough flexibility, different options are possible depending on the installer source used for repair. - Installed Source: - If the YAML manifest file specifies the `Repair` switch and `Modify` as the `RepairBehavior`, the repair command will use the modify command in the ARP `ModifyPath` registry key, along with the repair switch, through a ShellExecute call, as long as `NoModify` and `NoRepair` ARP registry flags are not set to 1. - If the YAML manifest file specifies the `Repair` switch and `Uninstaller` as the RepairBehavior, the repair command will use the uninstall command in the ARP `UninstallString` registry key, along with the repair switch, through a ShellExecute call, as long as `NoRepair` APR registry flag is not set to 1. - Remote Source: If the YAML manifest file specifies the `Repair` switch and `Installer` value for the RepairBehavior, the repair command will obtain the matching installer from the remote source and use the repair switch on the downloaded installer through a ShellExecute call.. > If neither switch is specified, the repair command will display an error message. > Note: The initial implementation will not support repair for Portables installer type. Based on feedback from the community, we may add the repair feature for these installer type in a future release. ## Manifest Changes Addition of `Repair` property to InstallerSwitch - The `Repair` property is used to set the custom repair option that works with `RepairBehavior` field that controls the different repair behavior. Addition of `RepairBehavior` enumerable property to Installer Object - With the `RepairBehavior` switch, we can adjust the repair behavior by choosing the installer source (Installed/local or remote) and making sure that the proper ARP registry entries are applied to identify the local installer type when carrying out a repair operation using a local installer source. - The permitted initial values for the `RepairBehavior` switch include: - Performing a repair using a Installed/Local Installer Source: - `Modify`: if this option is specified, the repair switch will be applied to the `ModifyPath` ARP command entry, as long as `NoModify` and `NoRepair` ARP registry flags are not set to 1. - `Uninstaller` : if this option is specified, the repair switch will be applied to the `UninstallString` ARP command entry, as long as `NoRepair` APR registry flag is note set to 1. - Performing a repair using a Remote Installer Source: - `Installer` : If this option is specified, the repair switch will be applied to the appropriate installer obtained from the remote installer source. ## Manifest Validation - Specifying `Repair` switch without `RepairBehavior` switch will result in an error. - Specifying `RepairBehavior` switch without `Repair` switch will result in an error. - `Repair` switch can't be empty when specified. - `RepairBehavior` switch can't be empty when specified. ## Handling Elevation Requirement - The design presumes that the elevation requirements for modification/repair are consistent with those for installation, much like uninstallation. - The expectations are: - If a non-elevated session tries to modify a package installed for machine scope, the installer should prompt for elevation, as observed in sample runs. - If a package installed for user scope attempts a repair operation in an admin context, it is restricted due to possible security risks. ## Supported Repair Scenarios - Repair for installed applications of Msi, Wix, Msix and MSStore installer types. - Repair for the application using the custom repair switch specified in the YAML manifest file for Burn/Exe/Nullsoft/Inno/Wix/Msi installer types. - The appropriate repair behavior is determined by the combination of the `Repair` switch and the `RepairBehavior` value in the YAML manifest. ## Potential Issues - For Msi based installer types - To repair an Msi-based installer using msiexec platform support, the original installer must be present at the location registered as 'InstallSource' in the ARP registry. If the original installer is moved or renamed, the repair operation will fail, which is consistent with the modify repair through the settings app. This issue is more likely with installers installed via the winget install command, as it removes the installer right after installation to reduce disk footprint by design, making the 'InstallSource' path invalid. However, this issue does not apply if the installer can store the installer in a package cache path for future use and registers that path in 'InstallSource' in the ARP registry at the time of installation. - For Burn/Exe/Nullsoft/Inno installer types - the repair command will not work if the installer does not have a custom repair switch specified in the YAML manifest file. - the repair command will not work if the installed Burn/Exe/Nullsoft/inno installer type doesn't correlate to the remote installer type that has a custom repair switch specified in the YAML manifest file. ## Future considerations - Repair for Portables installer types. The possible options are: - Download matching installer , uninstall & Install OR - Download matching installer & re Install to overwrite existing files. - Repair for Burn/Exe/Nullsoft/Inno installer types without custom repair switch. The possible options are: - Download matching installer , uninstall & Install OR - Download matching installer & re Install to overwrite existing files. ## Resources - https://learn.microsoft.com/en-us/windows/msix/desktop/managing-your-msix-reset-and-repair - https://learn.microsoft.com/en-us/windows/win32/msi/reinstalling-a-feature-or-application - https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/msiexec#repair-options - https://learn.microsoft.com/en-us/uwp/api/windows.applicationmodel.store.preview.installcontrol.appinstallmanager.startproductinstallasync?view=winrt-22621 - https://learn.microsoft.com/en-us/windows/win32/api/msi/nf-msi-msireinstallproductw ================================================ FILE: doc/specs/#163 - Dependencies.md ================================================ --- author: Demitrius Nelon denelon/denelon@microsoft.com created on: 2021-05-06 last updated: 2021-05-06 issue id: 163 --- # Dependencies "For [#163](https://github.com/microsoft/winget-cli/issues/163)" ## Abstract Several packages require other packages as dependencies. The Windows Package Manager should be able to support declared dependencies. In the best case scenario the Windows Package Manager should be able to install any necessary dependencies a package needs. In the worst case scenario the Windows Package manger should be able to inform a user about any required dependencies not able to be installed automatically. ## Inspiration Many other package mangers have this capability. It's frustrating to be required to figure out what other dependencies are needed for a package, and have to deal with their dependencies as well. ## Solution Design The Windows Package Manager manifest v1.0 schema has provided keys for four different types of dependencies. @Conan-Kudo [suggested](https://github.com/microsoft/winget-cli/issues/163#issuecomment-631091560) evaluating [openSUSE/libsolv](https://github.com/openSUSE/libsolv). Four different types of dependencies have been declared in the [v1.0 manifest schemas](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.0.0/). * Windows Features * Windows Libraries * Package Dependencies (same source) * External Dependencies Each of these dependencies must be declared in the manifest. In the case of MSIX packages, the dependencies are not required to be declared in the manifest. The implementation for dependency support should initially be enabled as an experimental feature. Each of the dependency types should be handled the same way for the minimum viable approach. A warning should be presented to the user with the list of dependencies specified in the manifest. ``` warning: This package requires the following dependencies: : Do you have these dependencies installed [y/n]? ``` If the user chooses "yes", the installation will proceed. Note: This is essentially the complete implementation for External Dependencies. It is not required at this stage to deal with nested dependencies. In addition, the consumes and provides concept is not in scope with this implementation. ### Windows Features These include items like .NET Frameworks, Internet Information Services, and Windows Subsystem for Linux. In some cases, turning these features on may require a reboot. ### Windows Libraries These include items like Microsoft.WinJS or Visual C++ Redistributable libraries. ### Package Dependencies These include other packages. The restriction on these dependencies is that they come from the same source for the package to be installed. These include programming languages, runtime environments, or drivers. ### External Dependencies These include dependencies from outside of the source the original package is distributed. In some cases suitable items may exist in the same source, but for licensing or personal preference, no explicit package should be required. One example is Java. Many vendors offer JREs and JDKs so it may be more reasonable to have a user informed, and allow the user to confirm the presence of a dependency. As a first step the Windows Package Manager should show dependency information to the user, see issue [1012](https://github.com/microsoft/winget-cli/issues/1012) with [spec](./#1012%20-%20Show%20dependencies.md). ## UI/UX Design @dustojnikhummer [suggested](https://github.com/microsoft/winget-cli/issues/163#issuecomment-633901489) a syntax like: `winget install package -y` to install all dependencies and then the package and `winget install package` to prompt the user for each required dependency before installing it. ## Capabilities Several different package installers exist and treat dependencies differently. MSIX installers have an internal mechanism to identify dependencies. MSI and .exe installers may include dependencies. ### Accessibility The Windows Package Manager has been built in such a way that screen readers will still provide audible output as the command is executed keeping the user informed of progress, warnings, and errors. This should have no direct impact on accessibility. ### Security There should be no security impact directly, although we must remember that different sources may not guarantee the safety of packages. The Windows Package Manager community repository performs static and dynamic analysis, and in some cases additional manual validation before accepting a package. ### Reliability The Windows Package Manager community repository does not directly host packages that may be called as dependencies. These packages may be removed or become unavailable if the site they are hosted on encounters a failure (or no network access is available). ### Compatibility No current implementation for dependencies exists, so no compatibility issues are expected to occur as a result of dependency support. ### Performance, Power, and Efficiency The time needed to download and install a package and it's dependencies is directly related to network speed and the size of any packages. ## Potential Issues Not all dependencies are identified using [semantic versioning](https://semver.org/). This may cause complications for packages depending on a range of versions. It is possible a breaking change is introduced in a newer version of a dependency the Windows Package Manager cannot heuristically reason about. Some systems have limited storage, and may not have suitable space to install a package and all of it's dependencies. ## Future considerations The import command may be able to take advantage of determining all dependencies for all packages, and avoid repeatedly installing the same dependencies multiple times. ## Resources [RPM Dependencies](https://jfearn.fedorapeople.org/en-US/RPM/4/html/RPM_Guide/ch-advanced-packaging.html) [Node.js package.json configuration](https://docs.microsoft.com/visualstudio/javascript/configure-packages-with-package-json?view=vs-2019#:~:text=package.json%20configuration%201%20In%20a%20major%20version%20update%2C,fixes%20are%20included.%20Bug%20fixes%20are%20always%20backwards-compatible.) [R Package metadata](https://r-pkgs.org/description.html) [Debian package relationships](https://www.debian.org/doc/debian-policy/ch-relationships.html) [Brew Untangling Dependencies](https://blog.jpalardy.com/posts/untangling-your-homebrew-dependencies/) [Scoop Dependencies](https://scoop.netlify.app/concepts/#dependencies) [Chocolatey Create Packages](https://docs.chocolatey.org/en-us/create/create-packages) ================================================ FILE: doc/specs/#164 - PWA Support.md ================================================ --- author: Amrutha Srinivasan @amrutha95 created on: 2020-08-19 last updated: 2020-08-19 issue id: 164 --- # Spec Title For [#164](https://github.com/microsoft/winget-cli/issues/164) ## Abstract This feature adds PWA (Progressive Web Application) support to winget. The goal is to allow users to install and maintain PWAs just like they would any other type of application on winget. As a first step, this would be available as an "experimental" feature to make it available to the community and receive feedback. ## Inspiration PWA adoption is extremely important in the Windows app ecosystem. It is also crucial that they are treated as first class citizens in the app ecosystem, just like any native application. Providing PWA support for winget is key to achieving this goal. ## Solution Design ### System requirements Windows 10 v2004 or newer Edge latest Canary build ### PWA YAML manifest The "InstallerType" field in the YAML manifest file specifies the type of the application. A new InstallerType "PWA" will be added to show that a given application is a PWA. Each PWA would have its own manifest YAML file. A sample manifest file would look like this: ``` Id: FinancialTimesLimited.FinTimes Version: 1.0.0.0 Name: AppInstaller FinTimes Publisher: Financial Times Limited AppMoniker: fintimes License: Test InstallerType: PWA MinOSVersion: 10.0.19041.0 Installers: - Arch: x64 Url: https://app.ft.com InstallerType: PWA ManifestVersion: 0.1.0 ``` ### Package generation A POST call is made to a web service (https://pwabuilder-win-chromium-platform.centralus.cloudapp.azure.com) with a JSON body containing the URL to the PWA. The web service packages the PWA and its assets into an unsigned MSIX. An example of the JSON body would be : ``` { "url": "https://app.ft.com" } ``` There are many more optional parameters that can be passed to this service. For more information, refer to the test website linked in the Resources section. ### Package installation The unsigned MSIX package and any other dependency packages are added for the current user and the application is silently installed on the user's system. The installation is done by calling the Windows API AddPackageByUriAsync() using the specific deployment options that allow unsigned packages. ### Flow of the install process 1. User types the install command, eg. `winget install fintimes` (For the experimental version, the manifest will be passed as an argument) 2. The URL from the manifest YAML file of the Financial Times PWA will be sent as a POST request to an internal web service that will generate an unsigned MSIX package. This MSIX package is downloaded onto the user's system. 3. The generated package will then be silently installed as a Hosted App (Refer to resources for more on the Hostel App Model). 4. The MSIX file is removed from the user's system. ## UI/UX Design Installing a PWA will be similar to installing other application types currently supported by winget, using the install command. The only difference in experience might be if the user passes a -i flag to get a more interactive experience. When this flag is passed, the user will be prompted to allow the PWA to be launched on install. This feature is provided because in order to register a PWA with Edge, it has to be launched once. An example of the interactive install would look like this: ``` >winget install -i fintimes Found Financial Times [PWAtest.FinTimes] This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Starting package install... Successfully installed! Launching app now. This is a necessary step to complete installation. (App launches on Edge) ``` ## Capabilities ### Accessibility This should have no direct impact on accessibility. ### Security No direct impact on security. ### Reliability This is not expected to impact reliability. ### Compatibility This is an additional functionality built on top of the existing implementation to allow the install of PWAs. Therefore we don't anticipate any breaking changes to the existing implementation. ### Performance, Power, and Efficiency ## Potential Issues If the user navigates to the site before launching the installed PWA at least once (i.e. before registration with Edge), Edge might prompt them to install the PWA again. ## Future considerations 1. We are looking into where the catalog of PWAs should exist, and the pros and cons of maintaining a separate catalog vs including PWAs on the existing catalog. 2. We're testing this new integration technology with Edge first in 2020 and hope to have this debugged and contributed to the Chromium project early 2021. 3. Sometimes there may be multiple versions of a PWA available. In such scenarios, we may allow a "force" option in the case where a user wants all of them to be installed. 4. There will be a service developed in the future that updates all the PWA manifests whenever there is a breaking change in Edge or in the web service that generates MSIX packages. ## Resources Hosted App Model : https://blogs.windows.com/windowsdeveloper/2020/03/19/hosted-app-model/ Test Website for generating MSIX for PWA : https://pwabuilder-win-chromium-platform.centralus.cloudapp.azure.com/ ================================================ FILE: doc/specs/#182 - Support for installation of portable standalone apps.md ================================================ --- author: Demitrius Nelon @denelon created on: 2022-03-09 last updated: 2022-04-07 issue id: 182 --- # Portable/Standalone applications "For [#182](https://github.com/microsoft/winget-cli/issues/182)" ## Abstract Several packages do not have an installer. They are simply a binary executable. This specification describes how these types of programs will be treated as "packages" by the Windows Package Manager. ## Inspiration Just because an installer doesn't exist for a particular package doesn't mean you shouldn't be able to install it with the Windows Package Manager. ## Solution Design The installer type “portable” will be added to the enumerated list of installer types to support these types of packages. When one of these packages is encountered, there are several aspects to “installing” these packages and supporting the “upgrade” behavior. Notable concerns include the location of the package, creating or updating an entry in Windows Apps & Features. ### Command Line Arguments Users should be able to specify the location for where the program is “installed” on their machine with the “--location” argument. Some packages like GitLab Runner have a file name including extra metadata (gitlab-runner-windows-386.exe). The installation instructions suggest renaming the file after it has been downloaded. In that vein, a "--rename" argument should be added so the user can choose a value they prefer. To have the upgrade scenario honor the custom name, this information should be recorded in the installed packages data store. Portable / standalone executables should have their “command” value specified so the Windows Package Manager can determine the default value to use when creating a new entry in the "App Paths" registry during installation. If no command value is provided, then the entry will use the filename of the exe. Some portable applications generate or consume other files. An additional argument "--purge" will be added for the uninstall scenario to remove all files and subsequently the directory if the user wishes. The corollary argument "--preserve" will be used to preserve files if the default setting has been modified. ### Settings Users should have settings to be able to specify a new default location other than the system default for the Windows Package Manager. The default path for installing these packages is "%LOCALAPPDATA%/Microsoft/WinGet/Packages/" for user based installs. The default path for installing these packages is "Program Files/WinGet/Packages/" for machine wide x64 installs and "Program Files (x86)/WinGet/Packages/" for machine wide x86 installs. The corresponding settings are "PortablePackageUserRoot" and "PortablePackageMachineRoot". >Note: The "packageIdentifier" will be used to generate subdirectories for these binaries to be installed to. Using GitLab.gitlab runner as an example would result in the .exe being placed in "%LOCALAPPDATA%/Microsoft/WinGet/Packages/GitLab.gitlab-runner/" If a user has configured a path to be used for portable applications, that path should be honored. The user should also be able to specify if they want all portable packages placed in the same directory, or in a directory per package. A related setting for the uninstall scenario will be able to specify the default behavior for either "--clean" or "--purge". ### Install When the portable application is being installed by the Windows Package Manager, an entry will be created in Windows Apps & Features so the user will be able to see that the application is installed. Once the portable application is copied to the appropriate install location based on the preferences of the user, a symlink will be created that points to the portable application. The locations where these symlinks will be stored are "%LOCALAPPDATA%/Microsoft/WinGet/Links/" for user based installs, "Program Files/WinGet/Links/" for machine wide x64 installs and "Program Files (x86)/WinGet/Links/" for machine wide x86 installs. We will then append these paths to the PATH environment variable if they do not exist already. The filename of the symlink file determines the command alias that will be used when being executed in the command prompt. By default, the symlink filename we be the same as the filename of the portable exe. If a command value is specified in the manifest, then the symlink filename will be renamed to match the command value. If the rename argument is provided, then both the symlink filename and the portable exe filename will be renamed to match the rename argument value. >Note: In our initial design, we had opted to only writing to the "AppPath" registry subkey to install/register the portable application. However, we determined that this is not sufficient to support command-line execution, which does not look in the "AppPath" registry when locating executables. The data from “AppsAndFeaturesEntry” in the manifest will be used to specify the name of the package in Windows Apps & Features. Constraints for manifests with portable packages are covered below in the section on validating manifests. If the “AppsAndFeaturesEntry” doesn’t have a “DisplayName” value, then we will use the “PackageName”. The Product Code will be populated with the "PackageIdentifier" if it is not specified in the “AppsAndFeaturesEntry”. No shortcuts or icons will be created for the package. >Note: Some portable programs may require dependencies. Dependencies are not covered in this specification and are not yet supported as of the writing of this specification. #### Scope Behavior By default, portable apps will be installed with the "User" scope unless specified otherwise by the user through the "--scope" argument or in their settings. This also means that the "scope" field in the manifest will be ignored when installing portable apps. Since portable apps are unique in that they are simply standalone executables that are copied to a specific install location based on scope, it does not seem reasonable to have the manifest enforce an install behavior that is not required. Manifest validation would also need to be updated to show an error to the user that the "scope" field does not apply if a portable installer type is specified. #### Installation from multiple sources: If the user chooses to install the same package but from a secondary source, the Windows Package Manager will append the source name to subdirectory. For example, if GitLabRunner is installed a second time but from the msstore, then the full path would be "%LOCALAPPDATA%/Microsoft/WinGet/Packages/GitLab.GitLabRunner_msstore/". The same behavior will be applied when creating a symlink in order to avoid overwriting an existing symlink of the same package but from a different source. Using the same example, the generated symlink for GitLabRunner from the msstore will have a full path of "%LOCALAPPDATA%/Microsoft/WinGet/Links/GitLab.GitLabRunner_msstore.exe/" ### Upgrade The package is upgraded in the same path as the installed version. The first step the Windows Package Manager will perform is to download the executable to a temporary location, and attempt to copy the exe to the specified install location. If an exe with the same name already exists, the Windows Package Manager will attempt to overwrite the file. If that process fails because the file is currently in use, the user will be informed the package is running so they can shut it down. Optionally, the user may specify "--force" to forcefully shut the application down for upgrade. Once the exe has successfully been copied to the specified install location, the entry in "Apps & Features" will be updated accordingly and the symlink will be overwritten to point to the latest portable exe. If the "UninstallPrevious" field is specified in the manifest, then the Windows Package Manager will perform an uninstall of the previous version of the package prior to installing the newer version. A new Windows Apps & Features entry is created to correctly report the upgraded version for future potential upgrades. There will be no support for installing multiple “side by side” versions of portable packages. ### Uninstall The executable and the symlink should be removed along with the entry in Apps & Features. The user should also be able to uninstall the package from Apps & Features. >Note: The default behavior for uninstalling a portable application with Windows Apps & Features will be to execute uninstall without "--purge" so any files created by the portable application will be remain if they are located in the portable applications directory. An additional argument for "--wait" will be added that will prompt the user to press any key to exit. This is intended to support this scenario so that the user is aware of any remaining files if they choose to uninstall through Windows Apps & Features. If the directory is empty after removing the portable application, the directory should also be deleted. If the directory is not empty, the user should be informed that other files exist in the directory so it will not be removed. ### Manifest Validation Portable package manifests will only support zero or one "command" value to be specified. In the absence of the "--rename" argument, the value specified in "command" will define the default value for naming the symlink file that is created to point to the portable application. If a value for "scope" is specified for a portable installer, the user should be shown an error that the "scope" field is not supported for portable installers. Only zero or one entry for "Apps & Features" can be specified for a portable installer. ## UI/UX Design ### Installing a portable package ```text winget install Microsoft.NuGet Found NuGet [Microsoft.NuGet] Version 6.0 This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Downloading https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe ██████████████████████████████ 0.1 MB / 0.1 MB Successfully verified installer hash Starting package install... Successfully installed ``` ### Upgrading a portable package ```text winget upgrade Microsoft.NuGet Found NuGet [Microsoft.NuGet] Version 7.0 This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Downloading https://dist.nuget.org/win-x86-commandline/v7.0.0/nuget.exe ██████████████████████████████ 0.1 MB / 0.1 MB Successfully verified installer hash Starting package install... Successfully installed ``` If the portable application is running when the user performs an upgrade, the user will be informed. ```text winget upgrade Microsoft.NuGet Found NuGet [Microsoft.NuGet] Version 7.0 This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Downloading https://dist.nuget.org/win-x86-commandline/v7.0.0/nuget.exe ██████████████████████████████ 0.1 MB / 0.1 MB Successfully verified installer hash Starting package install... Failed to install NuGet [Microsoft.Nuget]. Package in use. Either exit the program or use "--force" to upgrade. ``` ### Uninstalling a portable package ```text winget uninstall NuGet Found NuGet [Microsoft.NuGet] Starting package uninstall... Successfully uninstalled ``` If the portable application created files in the portable application's directory, the user will be informed. ```text winget uninstall NuGet Found NuGet [Microsoft.NuGet] Starting package uninstall... The "--purge" argument was not specified. Files still exist in "%LOCALAPPDATA%/WinGet_Packages/User/Microsoft.NuGet/". Successfully uninstalled ``` ## Capabilities This will allow users to manage portable applications via the Windows Package Manager. ### Accessibility Any description or output text that is added by consequence of this feature will need to be localized. Moreover, this feature allows a mechanism to add accessibility settings in the future. ### Security This feature will not add any new security risks. All risks associated with this feature have been resolved with the core install, import, upgrade, and uninstall features. >Note: We recommend users leverage the Windows Sandbox when testing manifests on their local machines as the programs may not have been validated from a security perspective prior to being submitted to the Windows Package Manager Community App Repository. ### Reliability This should not introduce any new reliability concerns. ### Compatibility Previous versions of the Windows Package Manager will not cease to function, but they will not be able to apply arguments or settings related to this feature. They will not be able to install portable applications. An update to the Windows Package Manager is required to use this feature with the related arguments and settings. ### Performance, Power, and Efficiency This should not introduce any new performance, power, or efficiency concerns in the Windows Package Manager. ## Potential Issues Users may not be aware that there is no support for multiple versions of the same package on their system, so performing an upgrade may not provide they experience they were expecting. Users may not be aware that the uninstall behavior by default may leave files on their systems if they were created by a portable application. If a user performs uninstall via Windows Apps & Features the default behavior will be to run `winget uninstall package --purge`. If a user removes the "App Installer" they would not be able to perform uninstall. Displaying a path may be improved with detecting the shell the Windows Package Manager is being executed in. An [Issue](https://github.com/microsoft/winget-cli/issues/1977) was already raised covering the differences in how CMD and PowerShell render or support file system paths. ## Future considerations Some portable applications have icons. The options for creating shortcuts with icons could be considered in the future. Support for channels has not yet been implemented. Some portable applications may have "beta" and "stable" releases. Users may want to have support for both in the future. ## Resources [Portable / Standalone Executables · Discussion #1887 · microsoft/winget-cli (github.com)](https://github.com/microsoft/winget-cli/discussions/1887) [Application Registration - Win32 Apps](https://docs.microsoft.com/windows/win32/shell/app-registration) ================================================ FILE: doc/specs/#190 - Proxy Support.md ================================================ --- author: Flor Chacon @florelis created on: 2024-02-07 last updated: 2024-02-07 issue id: 190 --- # Proxy support For [#190](https://github.com/microsoft/winget-cli/issues/190) ## Abstract This spec describes a feature to specify a proxy for winget to use when connecting to the internet. ## Solution Design A new command line argument will be added to specify a proxy to use during a particular invocation of winget. This functionality will first need to be enabled through an admin setting, similar to local manifests or hash override. An option to set a default proxy to use on every flow will be added, but it will require administrator permissions to be set. New Group Policy will also be added for IT admins to control the use of proxies. The policies will be similar to those we already have for sources, so that a specific proxy can be required or only a predefined set of proxies can be allowed. Proxies will not be used for the configuration features for now. Proxy settings in winget will not affect the behavior of installers themselves, so an installation may still generate traffic outside of the proxy. The proxy will be used to download the installers and access the sources. Since the APIs used for Delivery Optimization and MSIX deployment do not provide a way to specify a custom proxy, if a proxy is specified, we will change the use of those APIs to accomodate the proxy. For Delivery Optimization, a proxy will force use of WinINet instead. For MSIX deployment, the packages will be fully downloaded before deploying; this will require more network traffic than a streaming install of only the required bits. ## UI/UX Design We will add a command line argument taking the URI to the proxy. A separate argument will be available to disable the use of proxy if there is a default set. Both of these arguments will be disabled by default and require admin privileges to enable. ``` > winget settings --enable ProxyCommandLineArgument > winget install Contoso.App --proxy https://127.0.0.1:2345 > winget install Contoso.App --no-proxy ``` To configure the default proxy, new `set` and `reset` subcommands will be added to the `settings` command. This will require admin privileges and does not require `ProxyCommandLineArgument` to be enabled. ``` > winget settings set DefaultProxy https://127.0.0.1:2345 > winget settings reset DefaultProxy ``` The current default proxy will be added to the output `winget --info`. ## Capabilities ### Accessibility This should have no direct impact on accessibility. ### Security There is a possibility of an attacker using a malicious proxy to tamper with the data received from the source, or with the contents of the installer file. This is not much different from the risks of using a public network. The following mitigating factors will be in place: * (New) The ability to set a default proxy will be restricted to administrators, to prevent attackers from adding a proxy without the user realizing. * (New) A Group Policy will be available to block the use of proxies, require the use of a specific proxy, or limit them to an approved list. * Pre-indexed sources need to be signed, and the publisher is required to match during source update. When initially adding the source, administrator privileges are already required to limit misuse. * Pre-indexed sources include manifest hashes in the local database, to ensure that the manifest downloaded later is as expected. * For the Microsoft Store source, we use certificate pinning to ensure we are talking to the right server. * When communicating with REST sources, the certificate used by the source for HTTPS needs to match the domain. * Manifests include a hash of the installer that is validated before executing it. The ability to ignore installer hash mismatches is disabled by default, and enabling it requires administrator privileges. ### Compatibility No breaking changes to existing behavior. ### Performance, Power, and Efficiency There should not be any notable performance changes. ## Potential Issues A faulty or misconfigured proxy could impact most of winget's functionality, but it can be worked around by disabling the use of the proxy. ## Future considerations Things we may want to consider in the future: * Extend support for proxies to the Configuration feature * Add proxy support to the COM API * Add support for proxies that require authentication * Add the ability for admins to set multiple allowed proxies that a user can use * Add the ability to specify a different default proxy for each source * Use proxies with Delivery Optimization. This requires changes to the Delivery Optimization APIs. ================================================ FILE: doc/specs/#292 - winget should install an app if there is an exact match.md ================================================ --- author: Demitrius Nelon @denelon created on: <2020-05-28> last updated: <2020-05-28> issue id: 292 --- # Package matching bug fix For [#292](https://github.com/microsoft/winget-cli/issues/292) ## Abstract The winget.exe client attempts to be generous with the `search` command, but is a bit too generous with `install`. The *id* should be the unique key to identifying a package (other than the package version). It should also be case-insensitive from the perspective of command execution, but it should be case-sensitive in terms of the displayed value. If a manifest was created with the *id* "Git.Git" then that is what would be displayed in the client output. Any combination of case in the `install` command should match. If multiple versions of the package have been created, and they differ in the casing for the *id* then the most recent version of the manifest should be used for displaying the characters in the search output. The latest version of a package is also the version that should be displayed during `search`, and subsequently installed. ## Inspiration `winget install git.git` should work. ## Solution Design Installing a package by using the *Id* as the package name should not require disambiguation. The package *Id* should perform a case-insensitive match. If the given *Id* is exact, it should not be confused with a longer *Id* `winget install git.git` should install that package The `git.gitLFS` should not cause ambiguity If the value passed as a package *Id* is a substring, or there is still ambiguity, the help text should provide an example. ## UI/UX Design Executing `winget install git.git` will install the Git package. Executing `winget install git.g` will provide additional guidance: Multiple apps found matching input criteria. Please refine the input. Try providing the Id: winget install git.git For the example above, the help could default to the Id for the top listed package from the search result. ## Capabilities This should help reduce the friction for users trying to install a package when there is ambiguity. ### Accessibility This may have an impact on users with screen readers. The goal is to provide additional context for this scenario. ### Security This should not introduce any _new_ security concerns. ### Reliability This is not expected to impact reliability. ### Compatibility This changes existing behavior with `install`, but is expected to be an improvement. There are no known unintended consequences. ### Performance, Power, and Efficiency ## Potential Issues Users may not realize another package exists with a longer name, and may install a program other than what they intended. If "git.git" and "git.gity" both existed and the user were to press enter before typing the final "y", the git.git package would be installed rather than what they intended. ## Future considerations There are changes to how results should displayed to reduce the likelihood of a user mistakenly assuming the "Name" is a key value for a package. The client commands should be case-insensitive, but the display should still be case-sensitive to support ease of reading. Long names can be easier to understand when presented in camel case, pascal case, or with a branded letter casing. The client also needs a mechanism to display all of the available versions of a package. ## Resources N/A ================================================ FILE: doc/specs/#3026 - Bypass Microsoft Store blocked for COM API.md ================================================ --- author: Roy MacLachlan RDMaclachlan/roy.maclachlan@microsoft.com created on: 2023-02-09 last updated: 2023-02-09 issue id: 3025 --- # Bypass Microsoft Store blocked for COM API "For [#3025](https://github.com/microsoft/winget-cli/issues/3025)" ## Abstract This spec describes allowing WinGet install COM APIs to install applications from the Microsoft Store when the Store application has been disabled through group policy object or SLAPI policies. ## Inspiration This is inspired by * Enterprise customer feedback requiring the ability to disable the Microsoft Store on Enterprise devices but capable of delivering Microsoft Store apps through deployment technologies (Example: Microsoft Intune). * Education customers feedback requiring the ability to control what a student can install on education devices, while continuing to leverage deployment technologies (Example: Microsoft Intune). ## Solution Design The Windows Package Manager will allow the WinGet COM APIs to perform Microsoft Store application installations that will bypass the check on Microsoft Store is blocked by policy (IsStoreBlockedByPolicyAsync). Both the WinGet command line interface (CLI) and WinGet PowerShell commands will continue to block application installations from the Store when the Microsoft Store is blocked by policy. The checks on Microsoft Store blocked by policy will allow calls through COM, with the exception of PowerShell and CLI. ## UI/UX Design None. ## Capabilities ### Accessibility None. ### Security None. ### Reliability None. ### Compatibility This proposed change will allow IT Administrators to allow the installation of Microsoft Store applications using the WinGet while preventing users from using the Microsoft Store application to search and install applications. This setting is an override that will be disabled by default. ### Performance, Power, and Efficiency ## Potential Issues None. ## Future considerations None. ## Resources N/A ================================================ FILE: doc/specs/#364 - Feature Toggle.md ================================================ --- author: John McPherson @JohnMcPMS created on: 2020-06-04 last updated: 2020-06-04 issue id: 364 --- # Feature Toggle For [#364](https://github.com/microsoft/winget-cli/issues/364) ## Abstract As features are implemented within winget, they may cause disruption to users oblivious to the fact that they are in progress. In order to allow work to be done in master, and distributed to early adopters for their feedback, this spec suggests that settings should be used to control "experimental" features. We use this term [as others have](https://github.com/PowerShell/PowerShell-RFC/blob/master/Final/RFC0029-Support-Experimental-Features.md) to mean work in progress that is not yet ready for release to the general audience. ## Inspiration We realize that we need to be able to: 1. deliver work in progress to users so that we can receive feedback 2. work in master without creating multiple, divergent branches 3. not disrupt users who do not wish to investigate unfinished features Thank you to @megamorf for the link to the PowerShell Experimental Features RFC. ## Solution Design A new settings block will be added to the settings file for experimental features to be toggled. By default, all experimental features will be disabled. A hidden command will be added to the root: ``` >winget features The following experimental features are in progress. They can be configured through the settings file (winget settings). Feature Status Link -------------------------------------------------------------------- Example1 Enabled https://github.com/microsoft/winget-cli/issues/364 Example2 Disabled https://github.com/microsoft/winget-cli/issues/396 ``` When run, this command will give the status of each feature (enabled/disabled), as well as a link to the Issue or Spec. Internally, a single flag enum will be used to reference experimental features. All command line parsing objects will have a field added for the feature(s) to which they belong. Any internal behavioral changes will be made based on a check using this enum. In this way, transitioning a feature either to release or remove can be done by finding by a single identifier. A feature is released by removing the feature enum value in code, and all related references. The value in settings will become ignored, and we can keep a list in a comment to prevent future reuse of settings names. ## Settings File To enable experimental features a user will need to modify the settings file. The settings file can be opened via the settings command. ``` "experimentalFeatures": { "example1": true, "example2": false, "example3": true } ``` ## UI/UX Design Experimental features can impact the winget user interface in 2 ways; new commands and options. Both will be added to the parse tree, but hidden from help view if disabled. When enabled, they will be labeled as experimental to reiterate this to the user. If a user attempts to use an experimental command or option that is not enabled, a special error will be presented to indicate that this feature is experimental and must be enabled via settings. If the feature needs settings after release, they should be added as part of the feature work, and can be tagged with the enum value as well. ## Capabilities ### Accessibility This should have no direct impact on accessibility. ### Security There should be no security impact directly, although we must remember that any medium IL process will be able to enable any feature by writing to the settings file. But any experimental feature should be created with the intention of becoming released, and so should have its own security consideration. ### Reliability One of the goals is to increase reliability for unaware users, so that they do not accidentally stumble into an incomplete feature. ### Compatibility No breaking changes to existing behavior. ### Performance, Power, and Efficiency ## Potential Issues No known issues. ## Future considerations This feature enables all larger features in the future to have a phased rollout. ## Resources [PowerShell Experimental Features RFC](https://github.com/PowerShell/PowerShell-RFC/blob/master/Final/RFC0029-Support-Experimental-Features.md) ================================================ FILE: doc/specs/#396 - Settings command.md ================================================ --- author: Ruben Guerrero @msftrubengu created on: 2020-06-03 last updated: 2021-07-14 issue id: 396 --- # Settings command For [#396](https://github.com/microsoft/winget-cli/issues/396) ## Abstract The winget.exe client must support having a feature that allows users to specify settings. This will be in the form of an editable file. The file must be in a location where a user can easily access it and modify it. ## Inspiration Add ability for user to set their own preferences. ## Solution Design ### Format The WinGet settings file needs to be in a readable format for users. We considered using other options, like the registry, but having a file makes it more accessible to users. #### Option 1: YAML WinGet already knows how to handle YAML files via yaml-cpp. To follow the manifest style, the properties will be PascalCased. #### Option 2: JSON JSON is a popular format that most applications tend to use. It is also possible to parse JSON with yaml-cpp as YAML is a superset of JSON, but a proper JSON parser might be needed depending on how well yaml-cpp handles it. Options are third party JSON parsers or Microsoft cpp winrt implementation `winrt::Windows::Data::Json::JsonObject`. Properties will be camelCased. There is also the concern about comments because JSON doesn't support them. There are different approaches we could take. - Go with JSON standard and support properties named `__comments` which will be ignored when parsing the file. - Use jsoncpp and allow C type comments. - Use yaml-cpp and allow YAML comments. Based on this information we are going to use JSON and jsoncpp as a parser. For the comments issue, we are having a discussion here [#416](https://github.com/microsoft/winget-cli/issues/416) ### Location WinGet can either run in package context or not. That means the location of the settings file will be determined depending on the context. Package Context: %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json. For more about UWP file system see [this](https://docs.microsoft.com/windows/uwp/get-started/fileio-learning-track#access-the-file-system) Non-package context: %LOCALAPPDATA%\Microsoft\Winget\settings.json ### Command A WinGet command will be added to support settings. Options: - settings (preferred) - config - configure A command is a better option than having an argument, for example `winget --settings`, because it let us add more commands into it in the future such as set and unset. The expectation is that when the user enters `winget settings` the settings file will be opened in the user's default text editor via ShellExecute. If the user doesn't have any file type association with `.json`, the default will be to open it with notepad.exe There will also be a telemetry point added into the command as the other commands have. ### Backup Settings File A user might make a mistake that could make the settings file unparsable. To protect against this there must be a backup settings file with the latest known good settings file. The flow is the following: 1. `winget settings` 2. If settings file can be parsed copy to settings.json.backup 3. Open settings file 4. Wait for next command 5. If settings file can be parsed use it; otherwise, use settings.json.backup and warn user. This mechanism allows the settings file to be resilient against mistakes. ### Creating file A settings file will only be created if the user runs `winget settings` and one of the following occurs: 1. First time use: Files settings.json or settings.json.backup don't exist. Winget will create both settings.json and settings.json.backup files using the default settings text. Winget will open settings.json in an editor. 2. settings.json deleted: If the settings file doesn't exist but settings.json.backup exists, the settings file will be created using settings.json.backup. Winget will open settings.json in an editor. If the user intended to remove their settings the recommendation should be an empty JSON file, not deleting the file. 3. settings.json and settings.json.backup deleted: Scenario is identical to 1. ### Loading settings Since the settings file doesn't exist at this time, we can't force the creation of it at WinGet install time. Moreover, the file will only be created at `winget settings` time, so any other command executed before it must work as it does right now. This means that winget must work without the existence of the file, which are the default settings. These leave us with three different sources of settings in order of importance: 1. settings.json 2. settings.json.backup 3. Default settings Setting will be loaded as following: ``` if settings exists and valid load settings else if backup exists and valid load settings backup else use default settings ``` Where valid means that syntax and semantic checks pass. For now, semantics checks will be part of the validation. If one setting is semantically incorrect and we fallback to backup proves to be annoying to users, checks can be relaxed so that only syntax failures are fatal and semantic errors are warnings. We could also in the future add a `winget settings validate` to improve the experience. We cannot force the user to upgrade, so it is possible for someone to add a setting for a future version that is not supported. There is not an easy way to detect it which means that loading the settings will warn of an unknown property. The user will need to verify the documentation and the version of winget that is running via `winget --info`. #### Errors and Warnings Loading settings will never fail, but warnings might happen. In addition to warnings regarding syntax and semantic validation, a warning will be printed if settings.json.backup is being used. If both settings and backup files failed to load and they exist another warning will be printed. There is no warning if the files don't exist. All these warnings must be localized as other text used in the project. - Settings Warning: Settings file failed loading. Using backup file. - Settings Backup Warning: Settings backup failed loading. Using default settings. ### Documentation All settings must be documented in the winget-cli repository in doc\Settings.md. The settings file will need to have a link to this file or some other Microsoft documentation site for reference. ### Version property A version property can be added to the settings, such as the manifest has, to have a more structured validation. I am currently opposed to the idea because, for future settings, we will need to always bump up the version and force the users to modify two pieces: the settings they want to use and the version property. I can also see settings becoming more dynamic and bumping the version per addition/removal seems like an overkill. ### Settings Two settings will be implemented for the sake of the discussion and to not provide a setting feature without actual settings. ### Progress Bar Visual Style This setting will specify the color of the progress bar that WinGet displays. It will only support the current visual style that WinGet already supports and no new ones will be added for now. Possible values: 1. accent (default) 2. retro 3. rainbow ``` "Visual": { "ProgressBar": "accent" } ``` #### Override The user must be able to keep using the preferred visual style via arguments as it is now. This has more priority than the style defined in the settings file, because is saying it want that style for the current run. #### Special request The author of this document firmly believes that `--plain` must be replaced with `--retro`. ### Source auto update Currently, WinGet updates the source after 5 minutes. This setting will enable users to set the timeout in minutes. Value must be integers with a minimum of 0. An arbitrary limit can be set but is not strictly necessary. A value of 0 indicates no update. ``` "Source": { "AutoUpdateIntervalInMinutes": 5 } ``` Having the unit defined in the property makes it self documented and avoids the pain of opening the settings file to see the unit. When the value is set to 0 it is the user responsibility to update the source if needed via `winget source update` ## UI/UX Design Executing `winget settings` will open settings file in the default text editor the user has for the file extension. ## Capabilities It will allow winget to set and define any new settings in the future. ### Accessibility Any description or output text that is added by consequence of this feature will need to be localized. Moreover, this feature allows a mechanism to add accessibility settings in the future. ### Security This should not introduce any _new_ security concerns. ### Reliability This is not expected to impact reliability. Settings will not be transferred if the user changes the context in which WinGet runs. That is, if the user has the Microsoft provided WinGet package and uninstalls it and runs a non-packaged WinGet, the settings are gone. This is by design. ### Compatibility Previous versions winget will not be able to read a setting file. An update to winget is required. ### Performance, Power, and Efficiency This introduce a read file for every command being run. However this shouldn't affect the performance of WinGet. ## Potential Issues If the file isn't intuitive users will experience difficulties setting what they need. ## Future considerations This feature allows the ability to expand the customization of winget for any user. It is also designed to be expanded with future settings commands that don't fit the scope of this feature. For example, one could use `winget settings set SOME_SETTING on` to modify the settings without the need of editing a file. ## References @JohnMcPMS for telling me what to type. ================================================ FILE: doc/specs/#476 - Package Pinning.md ================================================ --- author: Yao Sun @yao-msft created on: 2022-10-12 last updated: 2022-10-12 issue id: 476 --- # Package Pinning For [#476](https://github.com/microsoft/winget-cli/issues/476) ## Abstract This spec describes the functionality and high level implementation design of Package Pinning feature. ## Inspiration This is inspired by functionalities in other package managers, as well as community feedback. - Packages may introduce breaking changes that users may not want integrate into their workflow quite yet. - Packages may update themselves so that it will be duplicate effort for winget to try to update them. - User may want to maintain some of the packages through other channels outside of winget, or prefer one source over others within winget. - User may want some of the packages to stay in some major versions but allow minor version changes during upgrade. ## Solution Design #### Package Pinning types To achieve goals listed above, winget will support 3 types of Package Pinning: - **Blocking:** The package is blocked from `winget upgrade --all` or `winget upgrade `, user has to unblock the package to let winget perform upgrade. - **Pinning:** The package is excluded from `winget upgrade --all` but allowed in `winget upgrade `, a new argument `--include-pinned` will be introduced to let `winget upgrade --all` to include pinned packages. - **Gating:** The package is pinned to specific version(s). For example, if a package is pinned to version `1.2.*`, any version between `1.2.0` to `1.2.` is considered valid. To allow user override, `--force` can be used with `winget upgrade ` to override some of the pinning created above. #### Package Pinning Configuration Storage A separate sqlite db (other than the existing tracking catalog) will be created to store the package pinning configurations from user. ```text PackageIdentifier SourceIdentifier Version PinningType ---------------------------------------------------------------------------- Microsoft.TestApp winget 1.2.* Gating ``` **Notes:** For this iteration, winget will only support pinning packages that are locally installed and correlatable with at least one of the remote sources. Winget will record a pinned package by the PackageIdentifier and SourceIdentifier. There can only be one pinning configuration for a specific package. In the future, winget may consider pinning packages from installed packages (upon improving the installed package's PackageIdentifier logic). ## UI/UX Design #### winget pin commands A new `winget pin` command with 3 sub-commands will be introduced. - Add package pinning configuration: `winget pin add [--version ] [--source ] [--force] [--blocking]` - Remove package pinning configuration: `winget pin remove [--source ] [--force]` - List package pinning configuration: `winget pin list [--source ]` for a specific package or `winget pin list` to list all #### Blocking To block a package from upgrade, use `winget pin add --blocking` ```text cmd> winget pin Microsoft.TestApp --blocking ``` Now the pinning configuration is recorded as ```text PackageIdentifier SourceIdentifier Version PinningType ---------------------------------------------------------------------------- Microsoft.TestApp winget Blocking Microsoft.TestAppStore msstore Blocking ``` **Note:** by default packages correlated from all sources are blocked, user can pass in `--source` to block for a specific source Corresponding upgrade behavior ```text cmd> winget upgrade -all Microsoft TestApp is blocked from upgrade and skipped cmd> winget upgrade Microsoft.TestApp Microsoft TestApp is blocked from upgrade cmd> winget upgrade Microsoft.TestApp --force Success ``` #### Pinning To pin a package from `winget upgrade --all`, use `winget pin add ` ```text cmd> winget pin Microsoft.TestApp ``` Now the pinning configuration is recorded as ```text PackageIdentifier SourceIdentifier Version PinningType ---------------------------------------------------------------------------- Microsoft.TestApp winget Pinning Microsoft.TestAppStore msstore Pinning ``` **Note:** by default packages correlated from all sources are pinned, user can pass in `--source` to pin for a specific source Corresponding upgrade behavior ```text cmd> winget upgrade -all Microsoft TestApp is pinned from upgrade and skipped cmd> winget upgrade Microsoft.TestApp Success ``` #### Gating To gate a package to some specific version, use `winget pin add --version ` ```text cmd> winget pin Microsoft.TestApp --version 1.2.* ``` Now the pinning configuration is recorded as ```text PackageIdentifier SourceIdentifier Version PinningType ---------------------------------------------------------------------------- Microsoft.TestApp winget 1.2.* Gating Microsoft.TestAppStore msstore 1.2.* Gating ``` **Note:** by default packages correlated from all sources are gated, user can pass in `--source` to gate for a specific source Corresponding upgrade behavior ```text cmd> winget upgrade -all Success // If the available versions for upgrade are: 1.2.3 and 1.3.0, the selected version for upgrade is 1.2.3 cmd> winget upgrade Microsoft.TestApp Success // If the available versions for upgrade are: 1.2.3 and 1.3.0, the selected version for upgrade is 1.2.3 cmd> winget upgrade Microsoft.TestApp --version 1.3.0 Microsoft TestApp is gated to version 1.2.* Override with --force cmd> winget upgrade Microsoft.TestApp --version 1.3.0 --force Success ``` **Note:** Regarding gated version syntax, it will be mostly same as what current winget version supports, except with special `.*` in the end as wild card matching any remaining version parts if there are any. Example: When `.*` in the end is detected: Gate version `1.0.*` matches Version `1.0.1` Gate version `1.0.*` matches Version `1.0` Gate version `1.0.*` matches Version `1` Gate version `1.0.*` matches Version `1.0.alpha` Gate version `1.0.*` matches Version `1.0.1.2.3` Gate version `1.0.*` matches Version `1.0.*` Gate version `1.0.*` does not match Version `1.1.1` In rare cases where `*` is actually part of a version, only the last `.*` is considered wild card: Gate version `1.*.*` matches Version `1.*.1` Gate version `1.*.*` matches Version `1.*.*` Gate version `1.*.*` does not match Version `1.1.1` If no `.*` in the end is detected, the gate version gates to the specific version: Gate version `1.0.1` matches Version `1.0.1` Gate version `1.0.1` does not match Version `1.1.1` ## Capabilities ### Accessibility Accessibility should not be impacted by this change. There will be a few more tables printed to the terminal in certain cases, but they should use the current table implementation used by `winget upgrade` and `winget list`. ### Security Security of the Windows Package Manager should not be impacted by this change. However, security of user's software may be, as if they pin a insecure version of a package, it will not be upgraded by Winget unless explicitly requested by user. ### Reliability The change will improve reliability, as users will be able to have fine-grained control of the Windows Package Manager's upgrade functionality to ensure their workflow is not disrupted. ### Compatibility There should not be any breaking changes to the code. Although there could be a mild breaking change to the behavior of `upgrade --all` (not all packages are upgraded anymore since pinned ones are skipped), this is purely opt-in from the user's perspective at this time (if they do not pin software, there should not be a change). ### Performance, Power, and Efficiency There should not be any notable performance changes. ## Potential Issues - Installation/Upgrades from Com Apis may be impacted by user's package pinning configuration. It could be mitigated by returning a specific error code and the caller retrying with Force option. - Package dependencies resolution may be impacted by user's package pinning configuration. - Package imports may be impacted by user's package pinning configuration. ## Future considerations - Implementation in this spec only supports pinning from remote sources, so all installed versions from same package share the same pinning configuration. Winget could better support side by side installations by introducing package pinning from installed source. - Package pinning from user and from manifest are stored separately, we may integrate the `winget pin` commands to control package pinning from manifests. - A couple UI integrations can be made to `winget upgrade` and `winget list` to show pinned status during listing. - Dependencies flow can be improved to first check pinned status of each dependent package before trying to install all dependencies. - Support setting pinned state right after installation/upgrades like `winget install foo --pin`. - Improvements to import export commands to work seamlessly with existing package pinning configurations. ## Resources - [Brew - How do I stop certain formulae from being upgraded?](https://docs.brew.sh/FAQ#how-do-i-stop-certain-formulae-from-being-updated) - [NPM - package.json dependencies](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#dependencies) - [APT - Introduction to Holding Packages](https://help.ubuntu.com/community/PinningHowto#Introduction_to_Holding_Packages) - [Chocolatey - pin a package](https://docs.chocolatey.org/en-us/choco/commands/pin) Special thanks to [@jedieaston](https://github.com/jedieaston) for coming up with the initial draft of Package Pinning spec at [#1894](https://github.com/microsoft/winget-cli/pull/1894/). A lot has been discussed and this spec is much inspired from the draft. ================================================ FILE: doc/specs/#5949 - Authenticated GitHub API Requests for PowerShell Module.md ================================================ --- author: Melvin Wang @wmmc88 created on: 2026-02-09 last updated: 2026-02-26 issue id: 5949 --- # Authenticated GitHub API Requests for PowerShell Module For [#5949](https://github.com/microsoft/winget-cli/issues/5949). ## Abstract This spec describes adding support for authenticated GitHub API requests in the WinGet PowerShell module. The module's `GitHubClient` helper will automatically detect `GH_TOKEN` or `GITHUB_TOKEN` environment variables and use them to authenticate Octokit API calls, significantly increasing the GitHub API rate limit. ## Inspiration Users running `Repair-WinGetPackageManager` in GitHub Actions pipelines hit unauthenticated rate limits (60 requests/hour). Authenticated requests allow 5,000 requests/hour. The GitHub CLI (`gh`) already uses `GH_TOKEN` and `GITHUB_TOKEN` for the same purpose, and GitHub Actions automatically provides `GITHUB_TOKEN`. See: https://github.com/microsoft/windows-drivers-rs/actions/runs/20531244312/job/58982795057#step:3:43 ## Solution Design The `GitHubClient` class in `Microsoft.WinGet.Client.Engine` is updated to: 1. Read all known token environment variables (`GH_TOKEN`, `GITHUB_TOKEN`) on construction. 2. Log the presence or absence of each token via `StreamType.Verbose`. 3. Select the token to use based on precedence (`GH_TOKEN` > `GITHUB_TOKEN`), matching GitHub CLI behavior. 4. Log which token source is being used, or that no token was found. 5. Set `Octokit.GitHubClient.Credentials` if a token is available. Token resolution is extracted into a static `ResolveGitHubToken` method for testability. ### Token Precedence `GH_TOKEN` takes precedence over `GITHUB_TOKEN`, matching the [GitHub CLI convention](https://cli.github.com/manual/gh_help_environment). This is because `GH_TOKEN` is explicitly set by users, while `GITHUB_TOKEN` is automatically provided by GitHub Actions and may have more restricted permissions. ### Logging All logging uses `StreamType.Verbose` via the existing `PowerShellCmdlet.Write` pattern, visible when users pass `-Verbose` to cmdlets. Example output: ``` VERBOSE: GH_TOKEN environment variable: not found VERBOSE: GITHUB_TOKEN environment variable: found VERBOSE: Using authenticated GitHub API requests via GITHUB_TOKEN environment variable. ``` ### Files Changed - `src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/GitHubClient.cs` — Token resolution logic and logging. - `src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/AssemblyInfo.cs` — `InternalsVisibleTo` for unit tests. - `src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/WinGetPackageManagerCommand.cs` — Pass `PowerShellCmdlet` to `GitHubClient` constructor. - `src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/AppxModuleHelper.cs` — Pass `PowerShellCmdlet` to `GitHubClient` constructor. - `src/PowerShell/Microsoft.WinGet.UnitTests/GitHubClientTests.cs` — Unit tests for token resolution. ## UI/UX Design No new command-line arguments or user-facing changes. The feature is automatic: if `GH_TOKEN` or `GITHUB_TOKEN` is set in the environment, authenticated requests are used. Users can see which token is being used by running any repair/assert cmdlet with `-Verbose`. ### Accessibility No impact on accessibility. ### Security - Tokens are never logged or written to output; only the environment variable name is logged. - Token values are read from environment variables which are a standard secure mechanism for passing secrets in CI/CD environments. - Whitespace-only token values are treated as unset to prevent accidental empty-credential authentication. ### Reliability Improves reliability by reducing GitHub API rate limit failures in CI/CD pipelines. Unauthenticated requests are still used as a fallback when no tokens are set. ### Compatibility Fully backward compatible. When no token environment variables are set, behavior is identical to the previous implementation. ### Performance, Power, and Efficiency No measurable impact. A single environment variable read per `GitHubClient` construction. ## Potential Issues - If a user has an expired or revoked token in `GH_TOKEN`/`GITHUB_TOKEN`, API calls will fail with a 401 rather than falling back to unauthenticated. This matches GitHub CLI behavior and is the expected outcome. ## Future considerations - Support for additional token sources (e.g., `gh auth token` integration, Windows Credential Manager). - Applying authenticated requests to other parts of the WinGet client beyond the PowerShell module. ## Resources - [GitHub API rate limits](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api) - [GitHub CLI environment variables](https://cli.github.com/manual/gh_help_environment) - [Octokit.NET authenticated access](https://octokitnet.readthedocs.io/en/latest/getting-started/#authenticated-access) - [Issue #5949](https://github.com/microsoft/winget-cli/issues/5949) ================================================ FILE: doc/specs/#658 - WinGet Download.md ================================================ --- author: Roy MacLachlan @RDMaclachlan, Ryan Fu @ryfu-msft created on: 2023-02-09 last updated: 2024-02-15 issue id: 658 --- # `Download` command "For [#658](https://github.com/microsoft/winget-cli/issues/658)" ## Abstract This spec describes the functionality and high-level implementation design for downloading package installers using the Windows Package Manager. ## Inspiration This is inspired by customer feedback, and a need for broader application deployments: * Customers want to share the installer with an offline device. ## Solution Design The `download` command will provide users with the ability to download any installer from a single package. The following command options are available: ``` -d,--download-directory Directory where the installers are downloaded to -m,--manifest The path to the manifest of the package --id Filter results by id --name Filter results by name --moniker Filter results by moniker -v,--version Use the specified version; default is the latest version -s,--source Find package using the specified source --scope Select install scope (user or machine) -a,--architecture Select the architecture --installer-type Select the installer type -e,--exact Find package using exact match --locale Locale to use (BCP47 format) --ignore-security-hash Ignore the installer hash check failure --skip-dependencies Skips processing package dependencies and Windows features --header Optional Windows-Package-Manager REST source HTTP header --authentication-mode Specify authentication window preference (silent, silentPreferred or interactive) --authentication-account Specify the account to be used for authentication --accept-package-agreements Accept all license agreements for packages --accept-source-agreements Accept all source agreements during source operations -?,--help Shows help about the selected command --wait Prompts the user to press any key before exiting --logs,--open-logs Open the default logs location --verbose,--verbose-logs Enables verbose logging for winget --disable-interactivity Disable interactive prompts ``` ### Selecting the installer A new command argument for `--installer-type` has been added to support selecting a specific installer type to download. A package installer should also be able to be selected by `--scope`, `--architecture`, and `--locale`. ### Downloading the installer Downloading the package's installer will still require that the package's installer hash be verified before becoming available to the user to interact with. By default, installers will be downloaded to a unique folder name located in the `%USERPROFILE%/Downloads` directory. The default download directory can be modified in the user's settings. The unique folder name is comprised of the package identifier and package version. The installer will be comprised of the package identifier, package version, scope, architecture, and locale. This naming pattern ensures that the installer is unique and identifiable based on the installer filters applied: > Example installer download path name: `%USER_PROFILE%\Downloads\Microsoft.PowerToys_0.78.0\PowerToys (Preview)_0.78.0_User_X64_burn_en-US.exe` When downloading the package's installer, if a file with the same name exists the new download will overwrite the existing file. ### Downloading the manifest Along with downloading the installer, a merged manifest will be generated and outputted in the same installer download directory. The naming of the file will be exactly the same as the installer except for the extension which will be `.yaml`. The manifest is useful for providing information about the installer such as scope, product code, installer switches, etc. ## UI/UX Design ### WinGet Command Line Downloading an installer will output information relative to each step performed. Informing the user of any license agreements that must be accepted prior to download. Acceptance of license agreements will trigger the download to begin, displaying a progress bar that shows the download status. Upon download, the user will then be informed of the file hash validation status before being notified of the download status. The following is representative of the user experience. ```PowerShell PS C:\> WinGet download --id Microsoft.VisualStudioCode Found Microsoft Visual Studio Code [Microsoft.VisualStudioCode] Version 1.73.1 This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Starting package download... \ Successfully verified installer hash Installer downloaded:%USERPROFILE%\Downloads\Microsoft.VisualStudioCode_1.86.1\Microsoft Visual Studio Code_1.86.1_User_X64_inno_en-US.exe ``` When the user runs `download` command with the `--help` argument, the following information will be provided: ``` PS C:\> WinGet Download --help Windows Package Manager v1.1.1 Copyright (c) Microsoft Corporation. All rights reserved. Downloads the installer from the selected package, either found by searching a configured source or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. By default, download command will download the appropriate installer to the user's Downloads folder. usage: winget download [[-q] ] [] ``` ### WinGet Setting - Default Download Output The following items will be included in the WinGet Settings Schema ```json "DownloadBehavior": { "defaultDownloadDirectory":"%USERPROFILE%/Downloads/" } ``` The "defaultDownloadDirectory" setting will be used as the default folder where the package installer and manifest is downloaded to. ### WinGet PowerShell Cmdlet WinGet PowerShell cmdlet will download the identified package's installer and manifest based on the user specified parameters. While downloading the package's installer, PowerShell will show a progress bar displaying the progress. Once the download is complete, the status of the download will be shown to the user, along with the id, name, and source of the package. ``` C:\> Export-WinGetPackage -? NAME Export-WinGetPackage SYNTAX Export-WinGetPackage [[-Query] ] [-DownloadDirectory ] [-AllowHashMismatch] [-Architecture {Default | X86 | Arm | X64 | Arm64}] [-InstallerType {Default | Inno | Wix | Msi | Nullsoft | Zip | Msix | Exe | Burn | MSStore | Portable}] [-Locale ] [-Scope {Any | User | System | UserOrUnknown | SystemOrUnknown}] [-SkipDependencies] [-Version ] [-Id ] [-Name ] [-Moniker ] [-Source ] [-MatchOption {Equals | EqualsCaseInsensitive | StartsWithCaseInsensitive | ContainsCaseInsensitive}] [-WhatIf] [-Confirm] [] Export-WinGetPackage [[-PSCatalogPackage] ] [-DownloadDirectory ] [-AllowHashMismatch] [-Architecture {Default | X86 | Arm | X64 | Arm64}] [-InstallerType {Default | Inno | Wix | Msi | Nullsoft | Zip | Msix | Exe | Burn | MSStore | Portable}] [-Locale ] [-Scope {Any | User | System | UserOrUnknown | SystemOrUnknown}] [-SkipDependencies] [-Version ] [-WhatIf] [-Confirm] [] ALIASES None REMARKS None ``` ## Capabilities ### Accessibility Accessibility should not be impacted by this change. There will be a new column in WinGet search that will appear if an application is downloadable. ### Security Security of the Windows Package Manager should not be impacted by this change. ### Reliability There will be no change to the reliability of the Windows Package Manager. ### Compatibility There will be no breaking changes to the code. A subsection of the WinGet Install functionality will be leveraged for this new functionality. ### Performance, Power, and Efficiency ## Potential Issues ## Future considerations * AAD Authentication ## Resources ================================================ FILE: doc/specs/#888 - Com Api.md ================================================ # 1. Background The Windows Package Manager currently exposes a command line interface to search for packages, install them, view progress, and more. This API is designed to provide another way for callers to make use of that functionality. The API will be preferred by callers that want to receive progress and completion events, and UWP packages that do not have permission to launch command line processes. The goal for this api is to provide the full set of install functionality possible using the Windows Package Manager command line. The command line is documented at https://docs.microsoft.com/windows/package-manager/winget/ # 2. Description Windows Package Manager is a package manager for windows applications. It comes with a predefined repository of applications and users can add new repositories using the winget command line. This API allows packaged apps with the packageManagement capability and other higher privilege processes to start, manage, and monitor installation of packages that are listed in Windows Package Manager repositories. # 3. Examples Sample member values for the following examples: m_installAppId = L"Microsoft.VisualStudioCode"; ## 3.1. Create objects Creation of objects has to be done through CoCreateInstance rather than normal winrt initialization since it's hosted by an out of proc com server. These helper methods will be used in the rest of the examples. ```c++ (C++ish pseudocode) AppInstaller CreateAppInstaller() { return winrt::create_instance(CLSID_AppInstaller, CLSCTX_ALL); } InstallOptions CreateInstallOptions() { return winrt::create_instance(CLSID_InstallOptions, CLSCTX_ALL); } FindPackagesOptions CreateFindPackagesOptions() { return winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); } CreateCompositeAppCatalogOptions CreateCreateCompositeAppCatalogOptions() { return winrt::create_instance(CLSID_CreateCompositeAppCatalogOptions, CLSCTX_ALL); } PackageMatchFilter CreatePackageMatchFilter() { return winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); } ``` ## 3.2. Search The api can be used to search for packages in a catalog known to Windows Package Manager. This can be used to get availability information or start an install. ```c++ (C++ish pseudocode) // Sample of using synchronous methods on background thread. CatalogPackage MainPage::FindPackageOnBackgroundThread() { PackageManager packageManager = CreatePackageManager(); PackageCatalogReference catalogRef{ packageManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog) }; ConnectResult connectResult = catalogRef.Connect(); if (connectResult.Status() != ConnectResultStatus::Ok) { return nullptr; } PackageCatalog catalog = connectResult.PackageCatalog(); FindPackagesOptions findPackagesOptions = CreateFindPackagesOptions(); PackageMatchFilter filter = CreatePackageMatchFilter(); filter.Field(PackageMatchField::Id); filter.Option(PackageFieldMatchOption::Equals); filter.Value(m_installAppId); findPackagesOptions.Filters().Append(filter); // We've already switched to a background thread, so do everything synchronously. FindPackagesResult findPackagesResult{ catalog.FindPackages(findPackagesOptions) }; winrt::IVectorView matches = findPackagesResult.Matches(); if (matches.Size() == 0) { return nullptr; } return matches.GetAt(0).CatalogPackage(); } // Sample of using async methods. IAsyncOperation MainPage::FindPackageInCatalogAsync(PackageCatalog catalog, std::wstring packageId) { FindPackagesOptions findPackagesOptions = CreateFindPackagesOptions(); PackageMatchFilter filter = CreatePackageMatchFilter(); filter.Field(PackageMatchField::Id); filter.Option(PackageFieldMatchOption::Equals); filter.Value(packageId); findPackagesOptions.Filters().Append(filter); FindPackagesResult findPackagesResult{ co_await catalog.FindPackagesAsync(findPackagesOptions) }; winrt::IVectorView matches = findPackagesResult.Matches(); if (matches.Size() == 0) { co_return nullptr; } co_return matches.GetAt(0).CatalogPackage(); } IAsyncOperation MainPage::FindPackageAsync() { PackageManager packageManager = CreatePackageManager(); PackageCatalogReference catalogRef{ packageManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog) }; ConnectResult connectResult = catalogRef.Connect(); if (connectResult.Status() != ConnectResultStatus::Ok) { co_return nullptr; } PackageCatalog catalog = connectResult.PackageCatalog(); co_return FindPackageInCatalogAsync(catalog, m_installAppId).get(); } ``` ## 3.3. Install ```c++ (C++ish pseudocode) IAsyncOperationWithProgress MainPage::InstallPackage(CatalogPackage package) { PackageManager packageManager = CreatePackageManager(); InstallOptions installOptions = CreateInstallOptions(); installOptions.PackageInstallScope(PackageInstallScope::Any); return packageManager.InstallPackageAsync(package, installOptions); } IAsyncAction UpdateUIProgress( InstallProgress progress, winrt::Windows::UI::Xaml::Controls::ProgressBar progressBar, winrt::Windows::UI::Xaml::Controls::TextBlock statusText) { co_await winrt::resume_foreground(progressBar.Dispatcher()); progressBar.Value(progress.DownloadProgress*100); std::wstring downloadText{ L"Downloading. " }; switch (progress.State) { case PackageInstallProgressState::Queued: statusText.Text(L"Queued"); break; case PackageInstallProgressState::Downloading: downloadText += std::to_wstring(progress.BytesDownloaded) + L" bytes of " + std::to_wstring(progress.BytesRequired); statusText.Text(downloadText); break; case PackageInstallProgressState::Installing: statusText.Text(L"Installing"); progressBar.IsIndeterminate(true); break; case PackageInstallProgressState::PostInstall: statusText.Text(L"Finishing install"); break; case PackageInstallProgressState::Finished: statusText.Text(L"Finished install."); progressBar.IsIndeterminate(false); break; default: statusText.Text(L""); } co_return; } // This method is called from a background thread. IAsyncAction UpdateUIForInstall( IAsyncOperationWithProgress installPackageOperation, winrt::Windows::UI::Xaml::Controls::Button installButton, winrt::Windows::UI::Xaml::Controls::Button cancelButton, winrt::Windows::UI::Xaml::Controls::ProgressBar progressBar, winrt::Windows::UI::Xaml::Controls::TextBlock statusText) { if (installPackageOperation) { installPackageOperation.Progress([=]( IAsyncOperationWithProgress const& /* sender */, InstallProgress const& progress) { UpdateUIProgress(progressBar, statusText, 50, stateStr).get(); }); winrt::hresult installOperationHr = S_OK; std::wstring errorMessage{ L"Unknown Error" }; InstallResult installResult{ nullptr }; try { installResult = co_await installPackageOperation; } catch (hresult_canceled const&) { errorMessage = L"Cancelled"; OutputDebugString(L"Operation was cancelled"); } catch (...) { // Operation failed // Example: HRESULT_FROM_WIN32(ERROR_DISK_FULL). installOperationHr = winrt::to_hresult(); // Example: "There is not enough space on the disk." errorMessage = winrt::to_message(); OutputDebugString(L"Operation failed"); } // Switch back to ui thread context. co_await winrt::resume_foreground(progressBar.Dispatcher()); cancelButton.IsEnabled(false); installButton.IsEnabled(true); progressBar.IsIndeterminate(false); if (installPackageOperation.Status() == AsyncStatus::Canceled) { installButton.Content(box_value(L"Retry")); statusText.Text(L"Install cancelled."); } if (installPackageOperation.Status() == AsyncStatus::Error || installResult == nullptr) { installButton.Content(box_value(L"Retry")); statusText.Text(errorMessage); } else if (installResult.RebootRequired()) { installButton.Content(box_value(L"Install")); statusText.Text(L"Reboot to finish installation."); } else if (installResult.Status() == InstallResultStatus::Ok) { installButton.Content(box_value(L"Install")); statusText.Text(L"Finished."); } else { installButton.Content(box_value(L"Install")); statusText.Text(L"Install failed."); } } } IAsyncAction MainPage::StartInstall( winrt::Windows::UI::Xaml::Controls::Button installButton, winrt::Windows::UI::Xaml::Controls::Button cancelButton, winrt::Windows::UI::Xaml::Controls::ProgressBar progressBar, winrt::Windows::UI::Xaml::Controls::TextBlock statusText) { installButton.IsEnabled(false); cancelButton.IsEnabled(true); co_await winrt::resume_background(); PackageManager packageManager = CreatePackageManager(); PackageCatalogReference catalogRef{ packageManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog) }; ConnectResult connectResult = catalogRef.Connect(); if (connectResult.Status() != ConnectResultStatus::Ok) { co_await winrt::resume_foreground(progressBar.Dispatcher()); statusText.Text(L"Connecting to catalog failed."); co_return; } PackageCatalog catalog = connectResult.PackageCatalog(); FindPackagesResult findPackagesResult{ FindPackageOnBackgroundThread(catalog, m_installAppId) }; winrt::IVectorView matches = findPackagesResult.Matches(); if (matches.Size() > 0) { m_installPackageOperation = InstallPackage(matches.GetAt(0).CatalogPackage()); UpdateUIForInstall(m_installPackageOperation, installButton, cancelButton, progressBar, statusText); } else { co_await winrt::resume_foreground(progressBar.Dispatcher()); statusText.Text(L"Could not find package."); co_return; } } ``` ## 3.4.1 Cancel The async operation can be stored, or the install code can wait on an event that can be triggered. ```c++ (C++ish pseudocode) void MainPage::CancelButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { if (m_installPackageOperation) { m_installPackageOperation.Cancel(); } } ``` ## 3.5. Open a catalog by name Open a catalog known to the caller. There is no way to use the api to add a catalog, that must be done on the command line. ```c++ (C++ish pseudocode) IAsyncOperation MainPage::FindSourceAsync(std::wstring packageSource) { PackageManager packageManager = CreatePackageManager(); PackageCatalogReference catalogRef{ packageManager.GetPackageCatalogByName(packageSource) }; if (catalogRef) { ConnectResult connectResult{ co_await catalogRef.ConnectAsync() }; // PackageCatalog will be null if connectResult.ErrorCode() is a failure PackageCatalog catalog = connectResult.PackageCatalog(); co_return catalog; } } ``` # 4 Remarks Notes have been added inline throughout the api details. For this api there are multiple similar apis that are relevant with regard to naming and consistency. There is the Windows Package Manager command line which uses "source" to describe the various repositories that can host packages and "search" to describe looking up an app. https://docs.microsoft.com/windows/package-manager/winget/ There is the Windows::ApplicationModel::PackageCatalog which exists as a Windows API for installing packages and monitoring their installation progress. https://docs.microsoft.com/uwp/api/windows.applicationmodel.packagecatalog?view=winrt-19041 And there is Windows.Management.Deployment.PackageManager which allows packages with the packageManagement capability to install msix apps and uses "Find" to describe looking up an app https://docs.microsoft.com/uwp/api/windows.management.deployment.packagemanager?view=winrt-19041 This API has aligned with those Windows APIs in using \*Catalog and Find. # 5 API Details ```c# (but really MIDL3) namespace Microsoft.Management.Deployment { [contractversion(1)] apicontract WindowsPackageManagerContract{}; /// State of the install. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageInstallProgressState { /// The install is queued but not yet active. Cancellation of the IAsyncOperationWithProgress in this /// state will prevent the package from downloading or installing. Queued, /// The installer is downloading. Cancellation of the IAsyncOperationWithProgress in this state will /// end the download and prevent the package from installing. Downloading, /// The install is in progress. Cancellation of the IAsyncOperationWithProgress in this state will not /// stop the installation or the post install cleanup. Installing, /// The installer has completed and cleanup actions are in progress. Cancellation of the /// IAsyncOperationWithProgress in this state will not stop cleanup or roll back the install. PostInstall, /// The operation has completed. Finished, }; /// Progress object for the install /// DESIGN NOTE: percentage for the install as a whole is purposefully not included as there is no way to /// estimate progress when the installer is running. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] struct InstallProgress { /// State of the install PackageInstallProgressState State; /// DESIGN NOTE: BytesDownloaded may only be available for downloads done by Windows Package Manager itself. /// Number of bytes downloaded if known UInt64 BytesDownloaded; /// DESIGN NOTE: BytesRequired may only be available for downloads done by Windows Package Manager itself. /// Number of bytes required if known UInt64 BytesRequired; /// Download percentage completed Double DownloadProgress; /// Install percentage if known. Double InstallationProgress; }; /// Status of the Install call /// Implementation Note: Errors mapped from AppInstallerErrors.h [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum InstallResultStatus { Ok, BlockedByPolicy, CatalogError, InternalError, InvalidOptions, DownloadError, InstallError, ManifestError, NoApplicableInstallers, }; /// Result of the install [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass InstallResult { /// Used by a caller to correlate the install with a caller's data. String CorrelationData{ get; }; /// Whether a restart is required to complete the install. Boolean RebootRequired{ get; }; /// Batched error code, example APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED InstallResultStatus Status{ get; }; /// Specific error if known, from downloader or installer itself, example ERROR_INSTALL_PACKAGE_REJECTED HRESULT ExtendedErrorCode{ get; }; } /// IMPLEMENTATION NOTE: SourceOrigin from AppInstallerRepositorySource.h /// Defines the origin of the package catalog details. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageCatalogOrigin { /// Predefined means it came as part of the Windows Package Manager package and cannot be removed. Predefined, /// User means it was added by the user and could be removed. User, }; /// IMPLEMENTATION NOTE: SourceTrustLevel from AppInstallerRepositorySource.h /// Defines the trust level of the package catalog. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageCatalogTrustLevel { None, Trusted, }; /// IMPLEMENTATION NOTE: SourceDetails from AppInstallerRepositorySource.h /// Interface for retrieving information about an package catalog without acting on it. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageCatalogInfo { /// The package catalog's unique identifier. /// SAMPLE VALUES: For OpenWindowsCatalog "Microsoft.Winget.Source_8wekyb3d8bbwe" /// For contoso sample on msdn "contoso" String Id { get; }; /// The name of the package catalog. /// SAMPLE VALUES: For OpenWindowsCatalog "winget". /// For contoso sample on msdn "contoso" String Name { get; }; /// The type of the package catalog. /// ALLOWED VALUES: "Microsoft.Rest", "Microsoft.PreIndexed.Package" /// SAMPLE VALUES: For OpenWindowsCatalog "Microsoft.PreIndexed.Package". /// For contoso sample on msdn "Microsoft.PreIndexed.Package" String Type { get; }; /// The argument used when adding the package catalog. /// SAMPLE VALUES: For OpenWindowsCatalog "https://winget.azureedge.net/cache" /// For contoso sample on msdn "https://pkgmgr-int.azureedge.net/cache" String Argument { get; }; /// The last time that this package catalog was updated. Windows.Foundation.DateTime LastUpdateTime { get; }; /// The origin of the package catalog. PackageCatalogOrigin Origin { get; }; /// The trust level of the package catalog PackageCatalogTrustLevel TrustLevel { get; }; } /// A metadata item of a package version. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageVersionMetadataField { /// The InstallerType of an installed package InstallerType, /// The Scope of an installed package InstalledScope, /// The system path where the package is installed InstalledLocation, /// The standard uninstall command; which may be interactive StandardUninstallCommand, /// An uninstall command that should be non-interactive SilentUninstallCommand, /// The publisher of the package PublisherDisplayName, }; /// IMPLEMENTATION NOTE: IPackageVersion from AppInstallerRepositorySearch.h /// A single package version. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageVersionInfo { /// IMPLEMENTATION NOTE: PackageVersionMetadata fields from AppInstallerRepositorySearch.h /// Gets any metadata associated with this package version. /// Primarily stores data on installed packages. /// Metadata fields may have no value (e.g. packages that aren't installed will not have an InstalledLocation). String GetMetadata(PackageVersionMetadataField metadataField); /// IMPLEMENTATION NOTE: PackageVersionProperty fields from AppInstallerRepositorySearch.h String Id { get; }; String DisplayName { get; }; String Version { get; }; String Channel { get; }; /// DESIGN NOTE: RelativePath from AppInstallerRepositorySearch.h is excluded as not needed. /// String RelativePath; /// IMPLEMENTATION NOTE: PackageVersionMultiProperty fields from AppInstallerRepositorySearch.h /// PackageFamilyName and ProductCode can have multiple values. Windows.Foundation.Collections.IVectorView PackageFamilyNames { get; }; Windows.Foundation.Collections.IVectorView ProductCodes { get; }; /// Gets the package catalog where this package version is from. PackageCatalog PackageCatalog { get; }; /// DESIGN NOTE: /// GetManifest from IPackageVersion in AppInstallerRepositorySearch is not implemented in V1. That class has /// a lot of fields and no one requesting it. /// Gets the manifest of this package version. /// virtual Manifest::Manifest GetManifest() = 0; } /// IMPLEMENTATION NOTE: PackageVersionKey from AppInstallerRepositorySearch.h /// A key to identify a package version within a package. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageVersionId { /// The package catalog id that this version came from. String PackageCatalogId { get; }; /// The version. String Version { get; }; /// The channel. String Channel { get; }; }; /// IMPLEMENTATION NOTE: IPackage from AppInstallerRepositorySearch.h /// A package, potentially containing information about it's local state and the available versions. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass CatalogPackage { /// IMPLEMENTATION NOTE: PackageProperty fields from AppInstallerRepositorySearch.h /// Gets a property of this package. String Id { get; }; String Name { get; }; /// Gets the installed package information if the package is installed. PackageVersionInfo InstalledVersion{ get; }; /// Gets all available versions of this package. Ordering is not guaranteed. Windows.Foundation.Collections.IVectorView AvailableVersions { get; }; /// Gets the version of this package that will be installed if version is not set in InstallOptions. PackageVersionInfo DefaultInstallVersion { get; }; /// Gets a specific version of this package. PackageVersionInfo GetPackageVersionInfo(PackageVersionId versionKey); /// Gets a value indicating whether an available version is newer than the installed version. Boolean IsUpdateAvailable { get; }; /// DESIGN NOTE: /// IsSame from IPackage in AppInstallerRepositorySearch is not implemented in V1. /// Determines if the given IPackage refers to the same package as this one. /// virtual bool IsSame(const IPackage*) const = 0; } /// IMPLEMENTATION NOTE: CompositeSearchBehavior from AppInstallerRepositorySource.h /// Search behavior for composite catalogs. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum CompositeSearchBehavior { /// Search local catalogs only LocalCatalogs, /// Search remote catalogs only, don't check local catalogs for InstalledVersion RemotePackagesFromRemoteCatalogs, /// Search remote catalogs, and check local catalogs for InstalledVersion RemotePackagesFromAllCatalogs, /// Search both local and remote catalogs. AllCatalogs, }; /// IMPLEMENTATION NOTE: PackageFieldMatchOption from AppInstallerRepositorySearch.h [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageFieldMatchOption { Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive, }; /// IMPLEMENTATION NOTE: PackageFieldMatchOption from AppInstallerRepositorySearch.h /// The field to match on. /// The values must be declared in order of preference in search results. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageMatchField { CatalogDefault, Id, Name, Moniker, Command, Tag, /// DESIGN NOTE: The following PackageFieldMatchOption from AppInstallerRepositorySearch.h are not implemented in V1. /// PackageFamilyName, /// ProductCode, /// NormalizedNameAndPublisher, }; /// IMPLEMENTATION NOTE: PackageMatchFilter from AppInstallerRepositorySearch.h [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageMatchFilter { PackageMatchFilter(); /// The type of string comparison for matching PackageFieldMatchOption Option; /// The field to search PackageMatchField Field; /// The value to match String Value; /// DESIGN NOTE: "Additional" from RequestMatch AppInstallerRepositorySearch.h is not implemented here. } /// IMPLEMENTATION NOTE: MatchResult from AppInstallerRepositorySearch.h /// A single result from the search. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass MatchResult { /// The package found by the search request. CatalogPackage CatalogPackage { get; }; /// The highest order field on which the package matched the search. PackageMatchFilter MatchCriteria { get; }; } /// Status of the FindPackages call [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum FindPackagesResultStatus { Ok, BlockedByPolicy, CatalogError, InternalError, InvalidOptions }; /// IMPLEMENTATION NOTE: SearchResult from AppInstallerRepositorySearch.h /// Search result data returned from FindPackages [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass FindPackagesResult { /// Error codes FindPackagesResultStatus Status{ get; }; /// The full set of results from the search. Windows.Foundation.Collections.IVectorView Matches { get; }; /// If true, the results were truncated by the given ResultLimit /// USAGE NOTE: Windows Package Manager does not support result pagination, there is no way to continue /// getting more results. Boolean WasLimitExceeded{ get; }; } /// Options for FindPackages [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass FindPackagesOptions { FindPackagesOptions(); /// DESIGN NOTE: /// This class maps to SearchRequest from AppInstallerRepositorySearch.h /// That class is a container for data used to filter the available manifests in an package catalog. /// Its properties can be thought of as: /// (Query || Selectors...) && Filters... /// If Query and Selectors are both empty, the starting data set will be the entire database. /// Everything && Filters... /// Query is PackageMatchField::CatalogDefault and in the Selector list. /// USAGE NOTE: Only one selector with PackageMatchField::CatalogDefault is allowed. /// Selectors = you have to match at least one selector (if there are no selectors, then nothing is selected) Windows.Foundation.Collections.IVector Selectors { get; }; /// Filters = you have to match all filters(if there are no filters, then there is no filtering of selected items) Windows.Foundation.Collections.IVector Filters{ get; }; /// Restricts the length of the returned results to the specified count. UInt32 ResultLimit; } /// IMPLEMENTATION NOTE: ISource from AppInstallerRepositorySource.h /// A catalog for searching for packages [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageCatalog { /// Gets a value indicating whether this package catalog is a composite of other package catalogs, /// and thus the packages may come from disparate package catalogs as well. Boolean IsComposite { get; }; /// The details of the package catalog if it is not a composite. PackageCatalogInfo Info { get; }; /// Searches for Packages in the catalog. Windows.Foundation.IAsyncOperation FindPackagesAsync(FindPackagesOptions options); FindPackagesResult FindPackages(FindPackagesOptions options); } /// Status of the Connect call [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum ConnectResultStatus { Ok, CatalogError, }; /// Result of the Connect call [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass ConnectResult { /// Error codes ConnectResultStatus Status{ get; }; PackageCatalog PackageCatalog { get; }; } /// A reference to a catalog that callers can try to Connect. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageCatalogReference { /// Gets a value indicating whether this package catalog is a composite of other package catalogs, /// and thus the packages may come from disparate package catalogs as well. Boolean IsComposite { get; }; /// The details of the package catalog if it is not a composite. PackageCatalogInfo Info { get; }; /// Opens a catalog. Required before searching. For remote catalogs (i.e. not Installed and Installing) this /// may require downloading information from a server. Windows.Foundation.IAsyncOperation ConnectAsync(); ConnectResult Connect(); } /// Catalogs with PackageCatalogOrigin Predefined [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PredefinedPackageCatalog { OpenWindowsCatalog, }; /// Local Catalogs with PackageCatalogOrigin Predefined [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum LocalPackageCatalog { InstalledPackages, }; /// Options for creating a composite catalog. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass CreateCompositePackageCatalogOptions { CreateCompositePackageCatalogOptions(); /// Create a composite catalog to allow searching a user defined or pre defined source /// and a local source (Installed packages) together IVector Catalogs { get; }; /// Sets the default search behavior if the catalog is a composite catalog. CompositeSearchBehavior CompositeSearchBehavior; } /// Required install scope for the package. If the package does not have an installer that /// supports the specified scope the Install call will fail with InstallResultStatus.NoApplicableInstallers [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageInstallScope { /// An installer with any install scope is valid. Any, /// Only User install scope installers are valid User, /// Only System installers will be valid System, }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageInstallMode { /// The default experience for the installer. Installer may show some UI. Default, /// Runs the installer in silent mode. This suppresses the installer's UI to the extent /// possible (installer may still show some required UI). Silent, /// Runs the installer in interactive mode. Interactive, }; /// Options when installing a package. /// Intended to allow full compatibility with the "winget install" command line interface. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass InstallOptions { InstallOptions(); /// Optionally specifies the version from the package to install. If unspecified the version matching /// CatalogPackage.GetLatestVersion() is used. PackageVersionId PackageVersionId; /// Specifies alternate location to install package (if supported). String PreferredInstallLocation; /// User or Machine. PackageInstallScope PackageInstallScope; /// Silent, Interactive, or Default PackageInstallMode PackageInstallMode; /// Directs the logging to a log file. If provided, the installer must have write access to the file String LogOutputPath; /// Continues the install even if the hash in the catalog does not match the linked installer. Boolean AllowHashMismatch; /// Allows Store installs when Store Client is disabled. Boolean BypassIsStoreClientBlockedPolicyCheck; /// A string that will be passed to the installer. /// IMPLEMENTATION NOTE: maps to "--override" in the winget cmd line String ReplacementInstallerArguments; /// Used by a caller to correlate the install with a caller's data. /// The string must be JSON encoded. String CorrelationData; /// A string that will be passed to the source server if using a REST source String AdditionalPackageCatalogArguments; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageManager { PackageManager(); /// Get the available catalogs. Each source will have a separate catalog. /// This does not open the catalog. These catalogs can be used individually or merged with CreateCompositePackageCatalogAsync. /// IMPLEMENTATION NOTE: This is a list of sources returned by Windows Package Manager source list Windows.Foundation.Collections.IVectorView GetPackageCatalogs(); /// Get a built in catalog PackageCatalogReference GetPredefinedPackageCatalog(PredefinedPackageCatalog predefinedPackageCatalog); /// Get a built in catalog PackageCatalogReference GetLocalPackageCatalog(LocalPackageCatalog localPackageCatalog); /// Get a catalog by a known name PackageCatalogReference GetPackageCatalogByName(String catalogName); /// Get a composite catalog to allow searching a user defined or pre defined source and a local source /// (Installing, Installed) together at the same time. PackageCatalogReference CreateCompositePackageCatalog(CreateCompositePackageCatalogOptions options); /// Install the specified package Windows.Foundation.IAsyncOperationWithProgress InstallPackageAsync(CatalogPackage package, InstallOptions options); } /// Force midl3 to generate vector marshalling info. declare { interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; } } ``` # Appendix ================================================ FILE: doc/specs/#893 - Support for arbitrary HTTP header for Rest sources.md ================================================ --- author: Ashwini Patil @ashpatil created on: 2021-08-11 last updated: 2021-09-01 issue id: 893 --- # Support for arbitrary HTTP header for Rest sources For [#893](https://github.com/microsoft/winget-cli/issues/893). ## Abstract There is a need for some REST sources to support custom behaviors when interacting with Windows Package Manager Client. The client should be able to pass data to a REST source via an HTTP header provided by the user. This could be used for any appropriate scenario. One example might be passing data to a source to indicate a specific behavior for the source to act on. For example, if you want the source to return an invalid response to test client behavior. The optional HTTP header can be passed in as a command-line argument with winget commands and a specific Rest source option. If the source specified is not a Rest source, the header will be ignored and a warning message will be displayed. ## Inspiration An ability for the REST source to define custom behavior based on data provided in the HTTP header. ## Solution Design An optional command-line argument named `--header` will be exposed for the commands that interact with Rest source. The header argument should be accompanied with a source argument that specifies which source should the custom header should be sent to. If the source specified is something other than a Rest source in the command, the header will be ignored and a warning message will be displayed. If no source is specified, an error message will be displayed with more information. The header can have a maximum length of 1024 characters. If the length of the header input exceeds this, an error message will be displayed with information. The client will send the header as a value of HTTP header named `Windows-Package-Manager`. The source may use it any way the source sees fit. ## UI/UX Design An optional command-line argument named `--header` will be exposed on the winget commands `source add, search, install, uninstall, list, upgrade and show`. Support for `import/export` commands with header will be added when we get more information on how custom header will be used in those commands. ### Accessibility This should not change impact accessibility for users of screen readers, assistive input devices, etc. ### Security ### Reliability This should not impact reliability. ### Compatibility ### Performance, Power, and Efficiency ## Potential Issues ## Future considerations ## Resources Issue [#893](https://github.com/microsoft/winget-cli/issues/893) Add support for an arbitrary HTTP header value in REST API. ================================================ FILE: doc/specs/#929 #2334 - Improvements and behavior changes to winget install and winget upgrade flow.md ================================================ --- author: Yao Sun @yao-msft created on: 2022-09-19 last updated: 2022-09-19 issue id: 929, 2334 --- # Improvements and behavior changes to winget install and winget upgrade flow For [#929](https://github.com/microsoft/winget-cli/issues/929), [#2334](https://github.com/microsoft/winget-cli/issues/2334) ## Abstract This is a mini spec for describing upcoming behavior changes and improvements to `winget install` and `winget upgrade` workflows. ## Solution Design ### Winget Install flow will look for installed packages and act accordingly **Existing Behavior**: `winget install` is a light-weight command that just installs the found package with latest version without any installed package detection and applicable version selection. **New Behavior**: By default, `winget install` will check for installed package after a package is found in a source. If an installed package is found, `winget install` will inform user in bold text and try to do a `winget upgrade` workflow instead. User will get `No applicable upgrade` if upgradable version not available. If an installed package is not found, `winget install` will try to search through all available package versions and find the latest that's applicable, in hope that installation success will be higher with less `No applicable installer`. **Note**: To better accommodate various user needs, the existing behavior will be preserved and can be invoked with `--force` argument. ### Winget Upgrade flow will try to select installer that better matches installed package **Existing Behavior**: `winget upgrade` does not try to select installer by installed package's architecture, locale. **New Behavior**: - `architecture` and `locale` arguments will be added to `winget upgrade` command - winget will try to record selected installer's architecture and locale for installation through winget - winget will record architecture or locale from command line arguments as user intent. i.e. `winget install foo --architecture x86 --locale en-US` - During upgrade flow installer selection, installer architecture or locale from previous installation will be treated as preference. Installer architecture or locale from user intent will be treated as requirement (i.e. the upgrade will fail if architecture or locale requirement cannot be met). **Note**: This improvement only works for installations through winget. Due to current limitations of winget tracking implementation, side by side installations may not work perfectly, as winget will only honor metadata from last installation for the same package. User would need to provide `--architecture` or `--locale` to override the last installation metadata when side by side scenarios fail to work as expected. ### `--force` argument separation from Override Hash Mismatch **Existing Behavior**: Currently, the `--force` argument is overloaded with overriding installer hash mismatch, overriding conflicting portable package, and potentially overriding the new `winget install` behavior. **New Behavior**: Since hash mismatch overriding is security related, it warrants a dedicated argument. `--ignore-security-hash`(name suggested in [#715](https://github.com/microsoft/winget-cli/issues/715)) will be introduced to represent installer hash mismatch overriding. `--force` argument will be kept for generic workflow behavior overriding. ================================================ FILE: doc/specs/#980 - Apps and Features entries version mapping.md ================================================ --- author: Yao Sun @yao-msft created on: 2022-05-09 last updated: 2022-05-15 issue id: 980 --- # Mapping of Apps and Features version and Winget package version For [#980](https://github.com/microsoft/winget-cli/issues/980) ## Abstract Some Winget packages may have the concept of marketing version and internal version. So they may have different version values in Winget manifest and in Apps and Features registry entry. Usually, the marketing version will be in the Winget manifest and internal version will be written in the Apps and Features registry. In this doc, Winget manifest version will be referred to as Winget version and Apps and Features version in registry will be referred to as ARP version. ## Inspiration Winget should try to map or correlate the ARP version of an installed package to its Winget version. This will help Winget List/Upgrade better determine if there's an upgrade available. ## Solution Design ### Version parsing and comparison logic in Winget Versions are parsed by: 1. Splitting the string based on the split character (`.`) 2. Parsing a leading, positive integer from each split part 3. Saving any remaining, non-digits as a supplemental value 4. If a version part's value is 0 and it does not have supplemental value(non-digits), the version part is dropped(i.e. `1.0.0` will be parsed internally as version with only one part with value 1) Versions are compared by:  for each part in each version   if both sides have no more parts, return equal   else if one side has no more parts, it is less   else if integers not equal, return comparison of integers   else if only one side has a non-empty string part, it is less   else if string parts not equal, return comparison of strings For example: Version `1` is less than version `2` Version `1.0.0` is less than version `2.0.0` Version `0.0.1-alpha` is less than version `0.0.2-alpha` Version `0.0.1-beta` is less than version `0.0.2-alpha` Version `0.0.1-alpha` is less than version `0.0.1-beta` Version `0.0.1-alpha` is less than version `0.0.1` Version `13.9.8` is less than version `14.0` Version `1.0` is equal to version `1.0.0` Both Winget version and ARP version will use the above parsing and comparison logic. ### How Winget collects and stores ARP version The `DisplayVersion` field under `AppsAndFeaturesEntries` will be treated as ARP versions of the package. ```YAML Installers: AppsAndFeaturesEntries: DisplayVersion: # Used as ARP version for version comparison if the key is present ``` Some packages may assign different build numbers to their internal versions for different installers. Winget will treat the ARP versions as a version range. For example, for below manifest, the ARP version range will be [10.0.0.1, 10.0.0.4], any ARP version between 10.0.0.1 and 10.0.0.4(both inclusive) will be treated as a match(i.e. mapped to Winget version 1.0.0). ```YAML PackageVersion: 1.0.0 Installers: - Architecture: x86 AppsAndFeaturesEntries: DisplayVersion: 10.0.0.1 - Architecture: x64 AppsAndFeaturesEntries: DisplayVersion: 10.0.0.2 - Architecture: arm AppsAndFeaturesEntries: DisplayVersion: 10.0.0.3 - Architecture: arm64 AppsAndFeaturesEntries: DisplayVersion: 10.0.0.4 ``` For manifest with only 1 ARP version, the only ARP version will be both the minimum and maximum version of the ARP version range. The Winget index will record the ARP version range(if present in the manifest) in its internal VersionTable. During indexing, Winget will make sure ARP version range does not collide with each other within a same package. ### How Winget maps ARP version to Winget version Winget will create a map of Winget version and ARP version range after all available packages info and installed package info are ready. The map will be sorted by Winget version. Conceptually below: ```text Winget version: 1.0.0 2.0.0 3.0.0 ARP version range: [10.0.0.1, 10.0.0.4] [11.0.0.1, 11.0.0.4] [12.0.0.1, 12.0.0.4] ``` #### No version mapping performed For packages without ARP version ranges provided, or if all ARP version ranges provided are same as the Winget version, no ARP version and Winget version mapping will be performed. For example: ```text Winget version: 1.0.0 2.0.0 3.0.0 ARP version range: [1.0.0, 1.0.0] [Not provided] [3.0.0, 3.0.0] ``` #### Only mapped if installed ARP version fall within one of the ARP version range For packages with ARP version unordered, or ordered in the opposite order of Winget version, mapping will be performed only when installed ARP version fall within one of the ARP version range. Otherwise the installed version will be set to Unknown. For example: ```text Winget version: 1.0.0 2.0.0 3.0.0 ARP version range: [10.0, 10.5] [7.0, 7.5] [13.0, 13.7] ``` ARP version `10.4` will be mapped as Winget version `1.0.0`. ARP version `13.4` will be mapped as Winget version `3.0.0`. ARP version `9.4` will be mapped as Winget version `Unknown`. #### Full version mapping performed For packages with ARP version ordered in the same order of Winget version, full mapping will be performed. For example: ```text Winget version: 1.0.0 2.0.0 3.0.0 4.0.0 ARP version range: [10.0, 10.5] [Not provided] [12.0, 12.5] [13.0, 13.5] ``` Winget will perform following mapping: 1. Check if ARP version fall within one of the ARP version range 2. Try to find the closest ARP version range it's less than, this is higher priority than greater than mapping because this mapping will mostly be used for package upgrade applicability check. 3. Try to find the closes ARP version range it's greater than A special "less than"("< ") and "greater than"("> ") version concept will be used. This is to indicate the version is less than or greater than the closest version Winget is known of. UI/UX change to the output is described in later UI/UX section. For above version mapping: ARP version `10.0` will be mapped as Winget version `1.0.0`. ARP version `11.7` will be mapped as less than Winget version 3.0.0(`< 3.0.0`). ARP version `12.7` will be mapped as less than Winget version 4.0.0(`< 4.0.0`). ARP version `14.0` will be mapped as greater than Winget version 4.0.0(`> 4.0.0`). ARP version `2.0.0` will be mapped as less than Winget version 1.0.0(`< 1.0.0`)(Once ARP version mapping logic is applied, Winget will only look at ARP version range, though `2.0.0` exactly matches one Winget version). For version comparison with special "less than" or "greater than", the "less than" or "greater than" only applies when compared to the specific version, for comparing to other versions, "less than" or "greater than" could be considered as ignored. For example: Version `< 3.0` is less than version `3.0` Version `< 3.0` is greater than version `2.9`(because `3.0` is greater than `2.9`) Version `< 3.0` is less than version `4.0` Version `< 3.0` is less than version `> 3.0` Version `> 3.0` is greater than version `3.0` Version `> 3.0` is less than version `3.1` (because `3.0` is less than `3.1`) Version `> 3.0` is greater than version `2.9` **Note:** It is recommended for package authors to update ARP version info for all package versions of a package for better version mapping if this feature is to be used for a package. **Note:** Given the limited support for multiple component packages by Winget as of the writing, if a package has multiple components to be installed, it is recommended for package authors to only list the `DisplayName`s of the primary component for better version mapping results. ## UI/UX Design For packages matched to a particular Winget version, there'll be no UI/UX change. For packages not matched to a particular Winget version, "> " will be used to indicate the version is greater than a closest version known to Winget. ```text Name Id Version Available Source ------------------------------------------------------------ Git Git.Git > 2.36.0 winget GitHub Desktop GitHub.GitHubDesktop 2.9.15 3.0.0 winget ``` "< " will be used to indicate the version is less than a closest version known to Winget. ```text Name Id Version Available Source ------------------------------------------------------------ Git Git.Git < 2.36.0 2.36.0 winget GitHub Desktop GitHub.GitHubDesktop 2.9.15 3.0.0 winget ``` ## Capabilities This should improve Winget upgrade applicability check and lead to a better result for Winget list/upgrade. ### Accessibility This is not expected to impact accessibility. ### Security This should not introduce any _new_ security concerns. ### Reliability This is not expected to impact reliability. ### Compatibility This changes existing behavior with Winget installed packages version detection, but is expected to be an improvement. ### Performance, Power, and Efficiency All the new ARP versions info will be indexed so this should have little impact to performance. ## Potential Issues ## Future considerations ## Resources ================================================ FILE: doc/specs/Configuration-COM-API.md ================================================ Microsoft.Management.Configuration API === # Background This API is being added to enable the Developer+ configuration scenarios. It enables interacting with configuration sets in three contexts: 1. Loading an existing configuration set from a stream 2. Loading previously applied configuration sets from the local history 3. Authoring a new/editing an existing configuration These configuration sets are composed of configuration units, which describe the individual configurable items and the values to configure. Configuration actions consist of: 1. Test :: Determining whether the system state matches the described state 2. Get :: Extracting the current system state with respect to the configuration scope 3. Set :: Applying the described state to the system This API is also intended to support multiple processes watching for state changes, both for the configuration set lifetimes and the individual configuration unit states. # Conceptual pages (How To) _(Add conceptual documentation that will go to docs.microsoft.com "how to" page if needed)_ # API Pages ## ConfigurationSetState enumeration The state of a configuration set in the configuration history. | Name | Description | |-|-| | Unknown | Primarily used for a configuration set that has not been applied. | | Pending | The configuration set has been recorded into the history, but has not yet begun applying. | | InProgress | The configuration set has begun being applied to the system. | | Completed | The configuration set has completed being applied. | ## ConfigurationUnitState enumeration The state of a configuration unit in the configuration history. | Name | Description | |-|-| | Unknown | Primarily used for a configuration unit that has not been applied. | | Pending | The configuration unit has been recorded into the history, but has not yet begun applying. | | InProgress | The configuration unit has begun being applied to the system. | | Completed | The configuration unit has completed being applied; the result information will contain additional details. | | Skipped | The configuration unit was skipped; the result information will contain additional details on the reason. | ## ConfigurationUnitDetailLevel enumeration Defines the level of detail probing that is allowed about a configuration unit. | Name | Description | |-|-| | Local | Only reads details from local data. | | Catalog | Will query the catalog information for details, but will not download any modules. | | Download | Will download modules, but not load them. | | Load | Will download and load modules for details. | ## ConfigurationUnitResultInformation class Information on a result for a single unit of configuration. The class is used both in reporting results through the `ConfigurationSet.ConfigurationSetChange` event as they occur and in viewing past results via the `ConfigurationUnit.ResultInformation` property on a historical record. ## IConfigurationUnitSettingDetails interface Provides information for a specific configuration unit setting. The properties on this interface are useful for creating a rich authoring experience. ## IConfigurationUnitSettingDetails.Semantics schema > _TODO: Define the meaning/schema for this value_ ## IConfigurationUnitProcessorDetails interface Provides information for a specific configuration unit within the runtime. The properties on this interface are useful for informing the user about the provenance of the code that is responsible for processing the configuration unit. ## ConfigurationUnit class A single unit of configuration. Represents the smallest actionable configuration element. ## ConfigurationUnit constructor Creates an empty configuration unit for authoring purposes. ```C# ConfigurationUnit(); ``` ## ConfigurationUnit properties | Name | Description | |-|-| | UnitName | The name of the unit being configured; not a name for this instance. | | InstanceIdentifier | An identifier used to uniquely identify the instance of a configuration unit on the system. | | Identifier | The identifier name of this instance within the set. This value is referenced by other unit's `Dependencies`. | | Dependencies | The identifiers of the configuration units that this unit depends on. | | Directives | Contains the values that are for use by the configuration system, related to this unit. | | Settings | Contains the values that are for use by the configuration unit itself. | | Details | Contains information on the origin of the configuration unit. You must call `ConfigurationProcessor.Get*DetailsAsync` to populate this value. | | State | The current state of the configuration unit. | | ResultInformation | Contains information on the result of the latest attempt to apply the configuration unit. | | ShouldApply | Allows for control over whether this unit should be applied when the set containing it is applied. | ## ConfigurationUnit.Directives known values > _TODO: List of well known directives_ ## ConfigurationSetChangeEventType enumeration The change event type that has occurred for a configuration set change. | Name | Description | |-|-| | Unknown | For future use if the caller is not aware of newer change types. | | SetStateChanged | The state of the configuration set has changed. | | UnitStateChanged | The state of a configuration unit has changed. | ## ConfigurationSetChangeData class The change data sent about changes to a specific set. This class is sent to subscribers of the `ConfigurationSet.ConfigurationSetChange` event, containing information about the specific change that occurred. ## ConfigurationSet class A configuration set contains a collection of configuration units and details about the set. Represents a self contained group of configuration units that are operated on together. ## ConfigurationSet constructors Creates an empty configuration set for authoring purposes. ```C# ConfigurationSet(); ``` Loads a configuration set from the given stream. ```C# ConfigurationSet(Windows.Storage.Streams.IInputStream stream); ``` ## ConfigurationSet.ConfigurationSetChange event State changes for this set and it's units are sent to subscribers of this event. ```C# event Windows.Foundation.TypedEventHandler ConfigurationSetChange; ``` ## ConfigurationSet.Serialize method Serializes the configuration set to the given output stream. ```C# void Serialize(Windows.Storage.Streams.IOutputStream stream); ``` ## ConfigurationSet.Remove method Removes the configuration set from the recorded history, if present. ```C# void Remove(); ``` You can use this method to remove a configuration set that is no longer relevant. For example, it may have been for a repository that is no longer being used by the user and future conflicts with it's configuration are not important. ## ConfigurationSet properties | Name | Description | |-|-| | Name | The name of the set; if from a file this could be the file name. | | Origin | The origin of the set; if it came from a repository it could be the remote URL (ex. https://github.com/microsoft/winget-cli.git). | | InstanceIdentifier | An identifier used to uniquely identify the instance of a configuration set on the system. | | State | The state that the set is in. | | InitialIntent | The time that this set was recorded with intent to apply. | | ApplyBegun | The time that this set was last started to be applied. | | ApplyEnded | The time that this set was last finished being applied (does not indicate success). | | ConfigurationUnits | The configuration units that are part of this set. | ## IConfigurationUnitProcessor interface Provides access to a specific configuration unit within the runtime. This interface is the primary mechanism used to actually read and write configuration to the system, but it is not expected that you would use this directly as a consumer of Microsoft.Management.Configuration. ## IConfigurationSetProcessor interface Contains the lifetime of the processing action for a configuration set. This interface is used to contain the lifetime of a processing action, but it is not expected that you would use this directly as a consumer of Microsoft.Management.Configuration. ## IConfigurationProcessorFactory interface Allows different runtimes to provide specialized handling of configuration processing. It is not expected that you would use this interface directly, but rather the `ConfigurationProcessor` class. _Spec note: A separate binary (written by us) will contain the implementation(s) of this interface._ ## DiagnosticLevel enumeration Indicates the importance of diagnostic information. | Name | Description | |-|-| | Verbose | Most useful for debugging scenarios; likely too much for general use. | | Informational | Details that can be useful for understanding what is happening. | | Warning | Indicates some abnormal condition, but that is not expected to impact functionality. | | Error | An error has occurred, but this does not necessarily mean that it will halt the operation. | | Critical | A serious, fatal condition has been encountered. | ## DiagnosticInformation class Contains diagnostic information from the configuration system that can be passed along to the user or log files. This is not intended as primary information, and is thus not localized. ## ConfigurationConflictType enumeration The type of conflict between configuration sets that was detected. | Name | Description | |-|-| | Unknown | For future use if the caller is not aware of newer conflict types. | | MatchingOrigin | Indicates that the first configuration set has a matching name and origin to the second, which has already been applied. | | IdenticalSetApplied | Indicates that the first configuration set is identical to the second, which has already been applied. | | SettingsConflict | Indicates a conflict between the settings of two configuration units. | ## ConfigurationConflictSetting class Describes a conflict between a setting of two configuration units. ## ConfigurationConflict class Describes a conflict between two configuration sets. ## ApplyConfigurationSetFlags enumeration Flags to control how a configuration set should be applied to the system. | Name | Description | |-|-| | None | The configuration set should be applied in the default manner. | | DoNotOverwriteMatchingOriginSet | Forces a new configuration set instance to be recorded when the set being applied matches a previous set's origin. The default behavior is to assume that the incoming set is an update to the existing set and overwrite it. | ## ConfigurationChangeEventType enumeration The configuration set change event type that has occurred. | Name | Description | |-|-| | Unknown | For future use if the caller is not aware of newer change types. | | SetAdded | A new configuration set was recorded in the history with the intent to be applied. | | SetStateChanged | A configuration set has changed state. | | SetRemoved | A configuration set has been removed from the history. | ## ConfigurationChangeEventType class The change data sent about changes to sets. ## ConfigurationProcessor class The configuration processor is responsible for the interactions with the system. You must use this class to do anything beyond reading configuration sets. It is the entrypoint for all actions that will interact with the actual system configuration. ## ConfigurationProcessor constructor Creates a configuration processor using the given configuration factory. ```C# ConfigurationProcessor(IConfigurationProcessorFactory factory); ``` > _TODO: Add details on the mechanics of creating the `IConfigurationProcessorFactory` objects that we provide._ ## ConfigurationProcessor.CheckForConflicts(Async) method Checks for conflicts amongst the configuration sets provided, optionally including the configuration sets already applied to the system. ```C# Windows.Foundation.Collections.IVectorView CheckForConflicts(Windows.Foundation.Collections.IVectorView configurationSets, Boolean includeConfigurationHistory); Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVectorView > CheckForConflictsAsync(Windows.Foundation.Collections.IVectorView configurationSets, Boolean includeConfigurationHistory); ``` This method should be used on any configuration set that is opened in order to determine if it would cause a conflict with previously applied configurations. It should be called *after* setting the `Name` and `Origin` in order to determine if it is a potential update. ## ConfigurationProcessor.GetSetDetails(Async) method Gets the details for all configuration units in a set. ```C# void GetSetDetails(ConfigurationSet configurationSet, ConfigurationUnitDetailLevel detailLevel); Windows.Foundation.IAsyncAction GetSetDetailsAsync(ConfigurationSet configurationSet, ConfigurationUnitDetailLevel detailLevel); ``` This is a convenience/optimization method that will do the same thing as calling `GetUnitDetails(Async)` on each configuration unit in the set. See `GetUnitDetails(Async)` for more information on what it will do. ## ConfigurationProcessor.GetUnitDetails(Async) method Gets the details for all configuration units in a set. ```C# void GetUnitDetails(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel); Windows.Foundation.IAsyncAction GetUnitDetailsAsync(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel); ``` This method will get the details about a specific configuration unit and make them available via `ConfigurationUnit.Details`. The `detailLevel` parameter allows control over how deeply to probe for details. It is an analog for the amount of trust to place in the configuration unit processor. ## ConfigurationProcessor.ApplySet(Async) method Applies the configuration set state to the system. ```C# ApplyConfigurationSetResult ApplySet(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags); Windows.Foundation.IAsyncOperationWithProgress ApplySetAsync(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags); ``` Using the async method and it's progress is more efficient than subscribing to the `ConfigurationSetChange` event before calling this method. ## ConfigurationProcessor.TestSet(Async) method Tests if the system state matches the state described by the configuration set. ```C# TestConfigurationSetResult TestSet(ConfigurationSet configurationSet); Windows.Foundation.IAsyncOperationWithProgress TestSetAsync(ConfigurationSet configurationSet); ``` ## ConfigurationProcessor.GetSettings(Async) method Gets the current configuration unit settings from the system state. ```C# GetConfigurationUnitSettingsResult GetSettings(ConfigurationUnit unit); Windows.Foundation.IAsyncOperation GetSettingsAsync(ConfigurationUnit unit); ``` ## ConfigurationProcessor.Diagnostics event Enables listening to internal diagnostics events for logging purposes. ## ConfigurationProcessor.ConfigurationChange event Signals changes to the set of configuration sets in the history, as well as changes to the state of configuration sets in the history. ## ConfigurationProcessor.GetConfigurationHistory method Gets the configuration sets from the recorded history. ```C# Windows.Foundation.Collections.IVectorView GetConfigurationHistory(); ``` Gets the configuration sets that have already been applied or those recorded with the intent to be applied. This may include in progress sets or those that are waiting to be applied. # API Details [Link to the MIDL3 file.](../../src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl) # Appendix # Sample This sample illustrates some of the expected usage patterns. ```C# using Microsoft.Management.Configuration; using System; using System.Collections.Generic; using System.IO; using System.Reflection; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.Storage; using Windows.Storage.Streams; namespace ConfigurationSample { internal static class Helpers { internal static IConfigurationProcessorFactory CreateIConfigurationProcessorFactory() { throw new NotImplementedException(); } internal static ConfigurationSet OpenConfigurationSet(string filePath, ConfigurationProcessor processor) { var fileOperation = FileRandomAccessStream.OpenAsync(filePath, FileAccessMode.Read); fileOperation.AsTask().Wait(); var file = fileOperation.GetResults(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(file); if (result.Set != null) { return result.Set; } Console.WriteLine($"Failed opening configuration set: 0x{result.ResultCode:X} at {result.Field}"); return null; } internal static void SetWatcher(ConfigurationSet set, ConfigurationSetChangeData data) { Console.WriteLine($" - Set: {set.Name} [{set.InstanceIdentifier}]"); Console.WriteLine($" Change: {data.Change}"); Console.WriteLine($" Set State: {data.SetState}"); switch (data.Change) { case ConfigurationSetChangeEventType.UnitStateChanged: Console.WriteLine($" Unit: {data.Unit.UnitName} [{data.Unit.InstanceIdentifier}]"); Console.WriteLine($" Unit State: {data.UnitState}"); if (data.UnitState == ConfigurationUnitState.Completed && data.ResultInformation.ResultCode != null) { Console.WriteLine($" Failure: {data.ResultInformation.Description} [{data.ResultInformation.ResultCode.HResult}]"); } break; } } } internal class ApplyProgressWatcher { private bool isFirstProgress = true; internal void Watcher(IAsyncOperationWithProgress operation, ConfigurationSetChangeData data) { if (isFirstProgress) { isFirstProgress = false; // If our first progress callback contains partial results, output them as if they had been called through progress ApplyConfigurationSetResult partialResult = operation.GetResults(); foreach (ApplyConfigurationUnitResult unitResult in partialResult.UnitResults) { HandleUnitProgress(unitResult.Unit, unitResult.State, unitResult.ResultInformation); } } switch (data.Change) { case ConfigurationSetChangeEventType.SetStateChanged: Console.WriteLine($" - Set State: {data.SetState}"); break; case ConfigurationSetChangeEventType.UnitStateChanged: HandleUnitProgress(data.Unit, data.UnitState, data.ResultInformation); break; } } private void HandleUnitProgress(ConfigurationUnit unit, ConfigurationUnitState state, ConfigurationUnitResultInformation resultInformation) { switch (state) { case ConfigurationUnitState.Pending: break; case ConfigurationUnitState.InProgress: case ConfigurationUnitState.Completed: case ConfigurationUnitState.Skipped: Console.WriteLine($" - Unit: {unit.UnitName} [{unit.InstanceIdentifier}]"); Console.WriteLine($" Unit State: {state}"); if (resultInformation.ResultCode != null) { Console.WriteLine($" HRESULT: [0x{resultInformation.ResultCode.HResult:X8}]"); Console.WriteLine($" Reason: {resultInformation.Description}"); } break; case ConfigurationUnitState.Unknown: break; } } } internal class Program { static void LoadAndOutput(string[] args) { ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory()); // Open the given configuration file ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor); if (configSet == null) { return; } // Output some of the information from the set Console.WriteLine($"Configuration Set: {args[1]}"); foreach (ConfigurationUnit unit in configSet.ConfigurationUnits) { Console.WriteLine($" - Configuration Unit: {unit.UnitName}"); if (!string.IsNullOrEmpty(unit.Identifier)) { Console.WriteLine($" Identifier: {unit.Identifier}"); } Console.WriteLine($" Intent: {unit.Intent}"); IReadOnlyList dependencies = unit.Dependencies; if (dependencies.Count > 0) { Console.WriteLine(" Dependencies:"); foreach (string dependency in dependencies) { Console.WriteLine($" {dependency}"); } } ValueSet directives = unit.Directives; if (directives.Count > 0) { Console.WriteLine(" Directives:"); foreach (var directive in unit.Directives) { Console.WriteLine($" {directive.Key}: {directive.Value}"); } } ValueSet settings = unit.Settings; if (settings.Count > 0) { Console.WriteLine(" Settings:"); foreach (var setting in unit.Settings) { Console.WriteLine($" {setting.Key}: {setting.Value}"); } } } } static void LoadAndCheckConflicts(string[] args) { // Create the factory and processor ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory()); // Open the given configuration file ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor); if (configSet == null) { return; } // Set a name and origin for this set so that we can see it in the conflict info configSet.Name = Path.GetFileName(args[1]); configSet.Origin = args[1]; // Check for conflicts with existing configurations List configSets = new List() { configSet }; IList conflicts = processor.CheckForConflicts(configSets, true); Console.WriteLine($"Conflicts with Configuration Set: {args[1]}"); foreach (ConfigurationConflict conflict in conflicts) { Console.WriteLine($" - Conflict: {conflict.Conflict}"); Console.WriteLine($" First Set: {conflict.FirstSet.Name} [{conflict.FirstSet.Origin}]"); Console.WriteLine($" Second Set: {conflict.SecondSet.Name} [{conflict.SecondSet.Origin}]"); if (conflict.Conflict == ConfigurationConflictType.SettingsConflict) { Console.WriteLine($" First Unit: {conflict.FirstUnit.UnitName} [{conflict.FirstUnit.InstanceIdentifier}]"); Console.WriteLine($" Second Unit: {conflict.SecondUnit.UnitName} [{conflict.SecondUnit.InstanceIdentifier}]"); foreach (ConfigurationConflictSetting setting in conflict.Settings) { Console.WriteLine($" - Setting: {setting.Name}"); Console.WriteLine($" First Value: {setting.FirstValue}"); Console.WriteLine($" Second Value: {setting.SecondValue}"); } } } } static void LoadAndApply(string[] args) { // Create the factory and processor ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory()); // Open the given configuration file ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor); if (configSet == null) { return; } Console.WriteLine($"Applying Configuration Set: {args[1]}"); ApplyProgressWatcher watcher = new ApplyProgressWatcher(); var operation = processor.ApplySetAsync(configSet, ApplyConfigurationSetFlags.None); operation.Progress = watcher.Watcher; operation.AsTask().Wait(); ApplyConfigurationSetResult result = operation.GetResults(); Console.WriteLine($" - Done: {result.ResultCode.HResult}"); } static void GetHistoryAndWatchEverything(string[] args) { Console.WriteLine("Watching all configuration [press Enter to stop]:"); // Create the factory and processor ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory()); List list = new List(); // Attach to the top level change event processor.ConfigurationChange += (ConfigurationSet incomingSet, ConfigurationChangeData data) => { int existingSetIndex = -1; lock (list) { for (int i = 0; i < list.Count; ++i) { if (list[i].InstanceIdentifier == data.InstanceIdentifier) { existingSetIndex = i; break; } } if (data.Change == ConfigurationChangeEventType.SetAdded || data.Change == ConfigurationChangeEventType.SetStateChanged) { if (existingSetIndex == -1) { incomingSet.ConfigurationSetChange += Helpers.SetWatcher; list.Add(incomingSet); } } else // Removed { if (existingSetIndex != -1) { list[existingSetIndex].ConfigurationSetChange -= Helpers.SetWatcher; list.RemoveAt(existingSetIndex); } } } Console.WriteLine($" - Set: {data.InstanceIdentifier}"); Console.WriteLine($" Change: {data.Change}"); }; foreach (ConfigurationSet set in processor.GetConfigurationHistory()) { int existingSetIndex = -1; lock (list) { for (int i = 0; i < list.Count; ++i) { if (list[i].InstanceIdentifier == set.InstanceIdentifier) { existingSetIndex = i; break; } } if (existingSetIndex == -1) { set.ConfigurationSetChange += Helpers.SetWatcher; list.Add(set); } } if (existingSetIndex == -1) { Console.WriteLine($" - Set: {set.Name} [{set.InstanceIdentifier}]"); Console.WriteLine($" State: {set.State}"); } } // Wait for user to press enter Console.ReadLine(); } static void Main(string[] args) { var method = typeof(Program).GetMethod(args[0], BindingFlags.NonPublic | BindingFlags.Static); if (method != null) { method.Invoke(null, new object[]{ args }); } else { Console.WriteLine($"{args[0]} is not a sample"); } } } } ``` ================================================ FILE: doc/specs/spec-template.md ================================================ --- author: / created on: last updated: issue id: --- # Spec Title [comment]: # Link to issue: "For [#1](https://github.com/microsoft/winget-cli/issues/1)" ## Abstract [comment]: # Outline what this spec describes ## Inspiration [comment]: # What were the drivers/inspiration behind the creation of this spec. ## Solution Design [comment]: # Outline the design of the solution. Feel free to include ASCII-art diagrams, etc. ## UI/UX Design [comment]: # What will this fix/feature look like? How will it affect the end user? ## Capabilities [comment]: # Discuss how the proposed fixes/features impact the following key considerations: ### Accessibility [comment]: # How will the proposed change impact accessibility for users of screen readers, assistive input devices, etc. ### Security [comment]: # How will the proposed change impact security? ### Reliability [comment]: # Will the proposed change improve reliability? If not, why make the change? ### Compatibility [comment]: # Will the proposed change break existing code/behaviors? If so, how, and is the breaking change "worth it"? ### Performance, Power, and Efficiency ## Potential Issues [comment]: # What are some of the things that might cause problems with the fixes/features proposed? Consider how the user might be negatively impacted. ## Future considerations [comment]: # What are some of the things that the fixes/features might unlock in the future? Does the implementation of this spec enable scenarios? ## Resources [comment]: # Be sure to add links to references, resources, footnotes, etc. ================================================ FILE: doc/troubleshooting/README.md ================================================ # Troubleshooting ## How do I get the Windows Package Manager? ### Prerequisites The first thing to check is which version of Windows 10 you have. [Check your version of Windows](https://support.microsoft.com/windows/see-which-version-of-windows-10-you-have-12d35019-4da9-0cb1-ba47-f8b031b712ad). The Windows Package Manager requires at least Version 1809 (October 2018 Update). The next requirement is ensuring you have the [App Installer](https://apps.microsoft.com/detail/9nblggh4nns1) from the Microsoft Store. The Windows Package Manager is delivered as an MSIX package. The App Installer is required to install MSIX packages on Windows 10. >Note: The Windows Package Manager is shipped with later versions of the App Installer. ### Stable Releases The first stable release of the Windows Package Manager was v1.0.11451. This release was **not** published as an automatic update to all Windows 10 users on a supported version of Windows 10. This was an intentional decision made to provide enterprise customers (IT Professionals) sufficient time to configure and deploy Group Policy for the Windows Package Manager. Customers may install the [latest stable release](https://github.com/microsoft/winget-cli/releases/latest/) directly from the GitHub repository. These packages are signed, and customers will receive automatic updates if IT Policy does not block the Microsoft Store. ### Developer Releases (Pre-Release) >Note: There is a known problem restoring the client to the latest stable App Installer release. We will be distributing the latest stable builds and providing instructions once they are made available. During the initial Windows Package Manager Preview period, releases were distributed to all Windows Insider channels. Customers who [sign up](http://aka.ms/winget-InsiderProgram) to become members of the Windows Package Manager Insider program also receive pre-release builds. The final process for inclusion into the program requires manual steps, so the App Installer update may not be available for a few days **after** receiving their e-mail notification. Customers may install any [release](https://github.com/microsoft/winget-cli/releases/) including pre-release builds directly from the GitHub repository. These packages are signed, and customers will receive automatic updates if IT Policy does not block the Microsoft Store. >Note: Insiders will receive updates to the latest build (stable or pre-release) if IT Policy does not block the Microsoft Store. Other customers will receive updates to the latest stable build once a newer stable version is published if IT Policy does not block the Microsoft Store. Only the Windows Insider DEV channel will continue receiving pre-release builds of the Windows Package Manager after v1.0.11451. Other Windows Insider channels will only receive stable release candidates or updated versions of the Windows Package Manager with critical bug fixes. ### Machine-wide Provisioning The Windows Package Manager can be provisioned machine-wide or for each new user. The following PowerShell cmdlet can be used to provision the package machine-wide. The latest Windows Package Manager release and license can be downloaded directly from the GitHub repository. It is dependent on the [Microsoft.VCLibs](https://docs.microsoft.com/troubleshoot/cpp/c-runtime-packages-desktop-bridge) desktop framework package, which needs to be downloaded and specified in the dependency path option in the cmdlet. >`Add-AppxProvisionedPackage -online -PackagePath -LicensePath -DependencyPackagePath ` After the package is provisioned, the users need to log into their Windows account to get the package registered and use it. ## Common Issues ### Executing `winget` exits with no message If no output is displayed, it is likely that the version of WinGet on your system is using a retired Content Delivery Network (CDN). You can check which version of WinGet is on your machine using `winget --info`. If the version is lower than `1.6.3482`, take the following troubleshooting steps. 1. Install the latest version of WinGet using one of the below methods * a. Through the Microsoft Store by installing the latest version of [App Installer](https://apps.microsoft.com/detail/9NBLGGH4NNS1) * b. Through installing the MSIX package found in the [GitHub releases](https://github.com/microsoft/winget-cli/releases) * c. Through installing the MSIX package from https://aka.ms/getwinget 2. Force a source update using `winget source update` If the above guidelines do not resolve the problem, please open an issue with details of the Windows version and App Installer version you are using. ### Executing `winget` doesn't display help The following errors are displayed when executed in CMD. `The system cannot execute the specified program.` or ``` 'winget' is not recognized as an internal or external command, operable program or batch file. ``` The following errors are displayed when executed in PowerShell. ``` winget : The term 'winget' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:1 + winget + ~~~~~~ + CategoryInfo : ObjectNotFound: (winget:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException ``` or ``` Program 'winget.exe' failed to run: The file cannot be accessed by the systemAt line:1 char:1 + winget + ~~~~~~. At line:1 char:1 + winget + ~~~~~~ + CategoryInfo : ResourceUnavailable: (:) [], ApplicationFailedException + FullyQualifiedErrorId : NativeCommandFailed ``` The following error is displayed when executed in Windows Terminal's Powershell profile. ``` winget : The term 'winget' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:1 + winget + ~~~~~~ + CategoryInfo : ObjectNotFound: (winget:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException ``` These errors most commonly occur for one of following reasons. Please try out the following troubleshooting steps. 1. The App Installer does not contain the Windows Package Manager. You should check to ensure the version of App Installer is greater than 1.11.11451. You can check by executing the following command in PowerShell: >`Get-AppxPackage microsoft.desktopappinstaller` 2. The App Execution Alias for the Windows Package Manager is disabled. You should enable the App Execution Alias for the Windows Package Manager. Go to `App execution aliases` option in `Apps & features Settings` to enable it. 3. The App Installer did not automatically add the PATH environment variable. You should add the path environment variable. The value to add is "%userprofile%\AppData\Local\Microsoft\WindowsApps". You can verify this by running `%LOCALAPPDATA%\Microsoft\WindowsApps\winget` from a command-prompt or `& "$env:LOCALAPPDATA\Microsoft\WindowsApps\winget"` from a powershell. If the command runs then, but not before, then you very likely have a missing PATH environment component. 4. Apps deployed on the machine are registered per user by default. If App Installer was installed on a different user account than the one you are trying to run it on, you will have to reinstall it again on this account and try again: 1. Get the `PackageFullName` of your installed `App Installer` package (PowerShell): `Get-AppxPackage Microsoft.DesktopAppInstaller | Select Name, PackageFullName`. 2. `Add-AppxPackage -register "C:\Program Files\WindowsApps\{PackageFullName}\appxmanifest.xml" -DisableDevelopmentMode` (where `{PackageFullName}` is the info from the previous point). 3. Toggle the App Execution Alias for winget, again (see above). If the above guidelines do not resolve the problem, please open an issue with details of the Windows version and App Installer version you are using. ## Common Errors #### Error 0x80072efd This error is related to networking and maps to "ERROR_INTERNET_CANNOT_CONNECT". It could be related to TLS (Transport Layer Security). This issue may be resolved by enabling TLS 1.2. It may also be resolved by flushing your DNS cache. Instructions are available at [Microsoft Learn](https://learn.microsoft.com/windows-server/administration/windows-commands/ipconfig). #### Error 0x801901a0 This error is related to Delivery Optimization (DO). You may configure the Windows Package Manager settings to use the standard `WININET` library. Add the following network setting: >`"network": {"downloader": "wininet"}` #### Error 0x80d03002 This error is related to Delivery Optimization (DO). You may configure the Windows Package Manager settings to use the standard `WININET` library. Add the following network setting: >`"network": {"downloader": "wininet"}` #### Error 0x80070490 The following error is displayed when trying to install some appxbundles. ``` Install failed: error 0x80070490: Opening the package from location appxbundle_Name.appxbundle failed. 0x80070490 : Element not found. ``` A possible troubleshooting step is to install the [KB5005565](https://support.microsoft.com/topic/september-14-2021-kb5005565-os-builds-19041-1237-19042-1237-and-19043-1237-292cf8ed-f97b-4cd8-9883-32b71e3e6b44) update, reboot your machine and try installing the appxbundle again. #### Updating Microsoft.DesktopAppInstaller from version 1.0.42251.0 doesn't render package name on Windows 11 Pro image in Azure Launching the Microsoft.DesktopAppInstaller update on Windows 11 Pro image in Azure to update from version 1.0.42251.0 might not render the correct package name title. The workaround to this issue is to install the latest Microsoft.DesktopAppInstaller with Windows Package Manager. #### Failed in attempting to update the source: winget The WinGet Community repository (a.k.a. "winget" source) requires network connectivity to https://cdn.winget.microsoft.com/cache. Depending on the version of WinGet, either the source.msix or source2.msix is downloaded and installed. This pre-indexed package contains the local copy of metadata queried by WinGet. If running `winget source update` doesn't resolve the problem and you are on WinGet 1.10 or newer, you may be able to correct the problem by uninstalling the current source "Windows Package Manager Source (winget) V2" and running another winget command, or manually install the source from: https://cdn.winget.microsoft.com/cache/source2.msix (You may need to download this using "In Private" if you encounter a 404). ================================================ FILE: doc/windows/package-manager/index.md ================================================ --- title: Windows Package Manager description: Windows Package Manager is a comprehensive package manager solution that consists of a command line tool and set of services for installing applications on Windows 10. ms.date: 05/03/2020 ms.topic: overview ms.localizationpriority: medium --- # Windows Package Manager Windows Package Manager is a comprehensive [package manager solution](#understanding-package-managers) that consists of a command line tool and set of services for installing applications on Windows 10 and Windows 11. ## Windows Package Manager for developers Developers use the **winget** command line tool to discover, install, upgrade, remove and configure a curated set of applications. After it is installed, developers can access **winget** via the Windows Terminal, PowerShell, or the Command Prompt. For more information, see [Use the winget tool to install and manage applications](winget/index.md). ## Windows Package Manager for ISVs Independent Software Vendors (ISVs) can use Windows Package Manager as a distribution channel for software packages containing their tools and applications. To submit software packages (containing .msix, .msi, or .exe installers) to Windows Package Manager, we provide the open source **Microsoft Community Package Manifest Repository** on GitHub where ISVs can upload [package manifests](package/manifest.md) to have their software packages considered for inclusion with Windows Package Manager. Manifests are automatically validated and may also be reviewed manually. For more information, see [Submit packages to Windows Package Manager](package/repository.md). ## Understanding package managers A package manager is a system or set of tools used to automate installing, upgrading, configuring and using software. Most package managers are designed for discovering and installing developer tools. Ideally, developers use a package manager to specify the prerequisites for the tools they need to develop solutions for a given project. The package manager then follows the declarative instructions to install and configure the tools. The package manager reduces the time spent getting an environment ready, and it helps ensure the same versions of packages are installed on their machine. Third party package managers can leverage the [Microsoft Community Package Manifest Repository](package/repository.md) to increase the size of their software catalog. ## Related topics * [Use the winget tool to install and manage software packages](winget/index.md) * [Submit packages to Windows Package Manager](package/index.md) ================================================ FILE: doc/windows/package-manager/package/binary-validation-errors.md ================================================ # Binary-Validation-Error The Windows Package Manager goes to great lengths to create an excellent user experience when installing applications. In order to do this, we must ensure that all applications install on PCs without errors regardless of environment. To that end, a key test we use for the Windows Package Manager is to ensure that all installers will install without warnings on a variety of popular antivirus configurations. While Windows provides Defender as a built-in antivirus program, many enterprise customers and users employ a wide range of antivirus software. Therefore, each submission to the Windows Package Manager will be run through several antivirus programs. These programs all have different virus detection algorithms for identifying [Potentially unwanted application (PUA)](https://docs.microsoft.com/windows/security/threat-protection/intelligence/criteria) and malware. ## Application failures If an application fails validation, Microsoft will first attempt to verify that the flagged software is not a false positive with the antivirus vendors. In many cases, after notification and validation, the antivirus vendor will update their algorithm and the application will pass. In some cases, however, the code anomaly detected is not able to be determined to be a false positive by the antivirus vendors. In this case the application cannot be added to the Windows Package Manager repository, and the Pull Request will be rejected with a **Binary-Validation-Error** label. ## Responding to Binary-Validation-Error A previously mentioned, the Windows Package Manager repository is not allowed applications that fail with a **Binary-Validation-Error**. The next step is for the ISV to update their software to remove the code detected as PUA. ### What if I cannot remove that code? Occasionally, genuine tools used for debugging and low-level activities, will appear as PUA to the antivirus vendors. This is because the code necessary to do the debugging will have a similar signature to unwanted software. Even though this is a legitimate use of that coding practice, unfortunately we are unable to allow those applications into the Windows Package Manager repository. ================================================ FILE: doc/windows/package-manager/package/index.md ================================================ --- title: Submit packages to Windows Package Manager description: You can use Windows Package Manager as a distribution channel for software packages containing your applications. ms.date: 04/29/2020 ms.topic: overview ms.localizationpriority: medium --- # Submit packages to Windows Package Manager ## Independent Software Vendor (ISV) or Publisher If you are an ISV or Publisher, you can use Windows Package Manager as a distribution channel for software packages containing your applications. Windows Package Manager currently supports installers in the following formats: MSIX, MSI, and EXE. To submit software packages to Windows Package Manager, follow these steps: 1. [Create a package manifest that provides information about your application](manifest.md). Manifests are YAML files that follow the Windows Package Manager schema. 2. [Submit your manifest to the Windows Package Manager repository](repository.md). This is an open source repository on GitHub that contains a collection of manifests that the **winget** tool can access. ## Community Member If you are a GitHub community member, you may also submit packages to Windows Package Manager following the steps above. Optionally, you may also request help to have a package added to the [community repository](https://github.com/microsoft/winget-pkgs). To do so, create a new [Package Request/Submission](https://github.com/microsoft/winget-pkgs/issues/new/choose) Issue. ## Related topics * [Use the winget tool](../winget/index.md) * [Create your package manifest](manifest.md) * [Submit your manifest to the repository](repository.md) ================================================ FILE: doc/windows/package-manager/package/manifest.md ================================================ --- title: Create your package manifest description: If you want to submit a software package to the Windows Package Manager repository, start by creating a package manifest. ms.date: 04/29/2020 ms.topic: article ms.localizationpriority: medium --- # Create your package manifest If you want to submit a software package to the [Windows Package Manager Community Repository](repository.md), start by creating a package manifest. The manifest is a YAML file that describes the application to be installed. You may either use the [Windows Package Manager Manifest Creator](https://github.com/microsoft/winget-create), the [YAMLCreate](#using-the-yamlcreateps1) PowerShell script, or you can craft a manifest manually following the instructions below. ### Using WinGetCreate Utility You can install `wingetcreate` utility using the command below. ```powershell winget install wingetcreate ``` After installation, you can run `wingetcreate new` to create a new package and fill in the prompts. The last option **WinGetCreate** will offer is for you to submit the manifest to the packages repository. If you choose yes, you will automatically submit your Pull Request (PR) to the [Windows Package Manager Community Repository](https://github.com/microsoft/winget-pkgs). ### Using the YAMLCreate.ps1 To help author manifest files, we have provided a YAMLCreate.ps1 powershell script located in the Tools folder on the [Windows Package Manager Community Repository](https://github.com/microsoft/winget-pkgs). You can use the script by cloning the [Windows Package Manager Community Repository](https://github.com/microsoft/winget-pkgs) on your PC and run the script directly from the **Tools** folder. The script will prompt you for the URL to the installer, then will prompt you to fill in metadata. Like **WinGetCreate**, this script will also offer you to submit your manifest automatically. ## YAML basics The YAML format was chosen for package manifests because of its relative ease of human readability and consistency with other Microsoft development tools. If you are not familiar with YAML syntax, you can learn the basics at [Learn YAML in Y Minutes](https://learnxinyminutes.com/docs/yaml/). > Manifests for Windows Package Manager currently do not support all YAML features. Unsupported YAML features include anchors, complex keys, and sets. ## Conventions These conventions are used in this article: * To the left of `:` is a literal keyword used in manifest definitions. * To the right of `:` is a data type. The data type can be a primitive type like **string** or a reference to a rich structure defined elsewhere in this article. * The notation `[` *datatype* `]` indicates an array of the mentioned data type. For example, `[ string ]` is an array of strings. * The notation `{` *datatype* `:` *datatype* `}` indicates a mapping of one data type to another. For example, `{ string: string }` is a mapping of strings to strings. ## Manifest contents A package manifest must include a set of required items, and can also include further optional items that can help improve the customer experience of installing your software. This section provides brief summaries of the required manifest schema and complete manifest schemas, and examples of each. Each field in the manifest file must be Pascal-cased and cannot be duplicated. For a complete list and descriptions of items in a manifest, see the [manifest specification](https://github.com/microsoft/winget-pkgs/tree/master/doc/manifest/schema) in the [https://github.com/microsoft/winget-pkgs](https://github.com/microsoft/winget-pkgs) repository. ### Minimal required schema #### [Minimal required schema](#tab/minschema/) As specified in the [singleton JSON schema](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.4.0/manifest.singleton.1.4.0.json), only a number of fields are required. The minimal supported YAML file would look like the example below. The singleton format is only valid for packages containing a single installer and a single locale. If more than one installer or locale is provided, the multiple YAML file format and schema must be used. The partitioning scheme was added to help with GitHub's UX. Folders with thousands of children do not render well in the browser. ```YAML PackageIdentifier: # Publisher.package format. PackageVersion: # Version numbering format. PackageLocale: # BCP 47 format (e.g. en-US) Publisher: # The name of the publisher. PackageName: # The name of the application. License: # The license of the application. ShortDescription: # The description of the application. Installers: - Architecture: # Enumeration of supported architectures. InstallerType: # Enumeration of supported installer types (exe, msi, msix, inno, wix, nullsoft, appx). InstallerUrl: # Path to download installation file. InstallerSha256: # SHA256 calculated from installer. ManifestType: # The manifest file type ManifestVersion: 1.4.0 ``` #### [Example](#tab/minexample/) Path: manifests / m / Microsoft / WindowsTerminal / 1.6.10571.0 / Microsoft.WindowsTerminal.yaml ```YAML PackageIdentifier: Microsoft.WindowsTerminal PackageVersion: 1.6.10571.0 PackageLocale: en-US Publisher: Microsoft PackageName: Windows Terminal License: MIT ShortDescription: The new Windows Terminal, a tabbed command line experience for Windows. Installers: - Architecture: x64 InstallerType: msix InstallerUrl: https://github.com/microsoft/terminal/releases/download/v1.6.10571.0/Microsoft.WindowsTerminal_1.6.10571.0_8wekyb3d8bbwe.msixbundle InstallerSha256: 092aa89b1881e058d31b1a8d88f31bb298b5810afbba25c5cb341cfa4904d843 SignatureSha256: e53f48473621390c8243ada6345826af7c713cf1f4bbbf0d030599d1e4c175ee ManifestType: singleton ManifestVersion: 1.4.0 ``` #### Multiple File Example In order to provide the best user experience, manifests should contain as much meta-data as possible. In order to separate concerns for validating installers and providing localized meta-data manifests will be split into multiple files. The minimum number of YAML files for this kind of manifest is three. Additional locales should also be provided. * A [version](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.4.0/manifest.version.1.4.0.json) file * The [default locale](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.4.0/manifest.defaultLocale.1.4.0.json) file * An [installer](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.4.0/manifest.installer.1.4.0.json) file * Additional [locale](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.4.0/manifest.locale.1.4.0.json) files The example below shows many optional meta-data fields and multiple locales. Note the default locale has more requirements than additional locales. In the show command, any required fields that aren't provided for additional locales will display fields from the default locale. Path: manifests / m / Microsoft / WindowsTerminal / 1.6.10571.0 / Microsoft.WindowsTerminal.yaml ```YAML PackageIdentifier: Microsoft.WindowsTerminal PackageVersion: 1.6.10571.0 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.4.0 ``` Path: manifests / m / Microsoft / WindowsTerminal / 1.6.10571.0 / Microsoft.WindowsTerminal.locale.en-US.yaml ```YAML PackageIdentifier: Microsoft.WindowsTerminal PackageVersion: 1.6.10571.0 PackageLocale: en-US Publisher: Microsoft PublisherURL: https://www.microsoft.com/ PrivacyURL: https://privacy.microsoft.com/ PackageName: Windows Terminal PackageURL: https://docs.microsoft.com/windows/terminal/ License: MIT LicenseURL: https://github.com/microsoft/terminal/blob/master/LICENSE ShortDescription: The new Windows Terminal, a tabbed command line experience for Windows. Tags: - console - command-line - shell - command-prompt - powershell - wsl - developer-tools - utilities - cli - cmd - ps - terminal ManifestType: defaultLocale ManifestVersion: 1.4.0 ``` Path: manifests / m / Microsoft / WindowsTerminal / 1.6.10571.0 / Microsoft.WindowsTerminal.locale.fr-FR.yaml ```YAML PackageIdentifier: Microsoft.WindowsTerminal PackageVersion: 1.6.10571.0 PackageLocale: fr-FR Publisher: Microsoft ShortDescription: Le nouveau terminal Windows, une expérience de ligne de commande à onglets pour Windows. ManifestType: locale ManifestVersion: 1.4.0 ``` Path: manifests / m / Microsoft / WindowsTerminal / 1.6.10571.0 / Microsoft.WindowsTerminal.installer.yaml ```YAML PackageIdentifier: Microsoft.WindowsTerminal PackageVersion: 1.6.10571.0 Platform: - Windows.Desktop MinimumOSVersion: 10.0.18362.0 InstallerType: msix InstallModes: - silent PackageFamilyName: Microsoft.WindowsTerminal_8wekyb3d8bbwe Installers: - Architecture: x64 InstallerUrl: https://github.com/microsoft/terminal/releases/download/v1.6.10571.0/Microsoft.WindowsTerminal_1.6.10571.0_8wekyb3d8bbwe.msixbundle InstallerSha256: 092aa89b1881e058d31b1a8d88f31bb298b5810afbba25c5cb341cfa4904d843 SignatureSha256: e53f48473621390c8243ada6345826af7c713cf1f4bbbf0d030599d1e4c175ee - Architecture: arm64 InstallerUrl: https://github.com/microsoft/terminal/releases/download/v1.6.10571.0/Microsoft.WindowsTerminal_1.6.10571.0_8wekyb3d8bbwe.msixbundle InstallerSha256: 092aa89b1881e058d31b1a8d88f31bb298b5810afbba25c5cb341cfa4904d843 SignatureSha256: e53f48473621390c8243ada6345826af7c713cf1f4bbbf0d030599d1e4c175ee - Architecture: x86 InstallerUrl: https://github.com/microsoft/terminal/releases/download/v1.6.10571.0/Microsoft.WindowsTerminal_1.6.10571.0_8wekyb3d8bbwe.msixbundle InstallerSha256: 092aa89b1881e058d31b1a8d88f31bb298b5810afbba25c5cb341cfa4904d843 SignatureSha256: e53f48473621390c8243ada6345826af7c713cf1f4bbbf0d030599d1e4c175ee ManifestType: installer ManifestVersion: 1.4.0 ``` * * * > If your installer is an .exe and it was built using Nullsoft or Inno, you may specify those values instead. When Nullsoft or Inno are specified, the client will automatically set the silent and silent with progress install behaviors for the installer. ## Installer switches You can often figure out what silent `Switches` are available for an installer by passing in a `-?` to the installer from the command line. Here are some common silent `Switches` that can be used for different installer types. | Installer | Command | Documentation | | :--- | :-- | :--- | | MSI | `/q` | [MSI Command-Line Options](https://docs.microsoft.com/windows/win32/msi/command-line-options) | | InstallShield | `/s` | [InstallShield Command-Line Parameters](https://docs.flexera.com/installshield19helplib/helplibrary/IHelpSetup_EXECmdLine.htm) | | Inno Setup | `/SILENT or /VERYSILENT` | [Inno Setup documentation](https://jrsoftware.org/ishelp/) | | Nullsoft | `/S` | [Nullsoft Silent Installers/Uninstallers](https://nsis.sourceforge.io/Docs/Chapter4.html#silent) | ## Tips and best practices * The package identifier must be unique. You cannot have multiple submissions with the same package identifier. Only one pull request per package version is allowed. * Avoid creating multiple publisher folders. For example, do not create "Contoso Ltd." if there is already a "Contoso" folder. * All tools must support a silent install. If you have an executable that does not support a silent install, then we cannot provide that tool at this time. * Provide as many fields as possible. The more meta-data you provide the better the user experience will be. In some cases, the fields may not yet be supported by the Windows Package Manager client (winget.exe). For example, the `Moniker` field is optional. However, if you include this field, customers will see results associated with the `Moniker` value when performing the [search](../winget/search.md) command (for example, **vscode** for **Visual Studio Code**). If there is only one app with the specified `Moniker` value, customers can install your application by specifying the moniker rather than the fully qualified package identifier. * The length of strings in this specification should be limited to 100 characters before a line break. * The "PackageName" and "Publisher" should match the entry made in Add / Remove Programs to help the correlation with manifests to support **export**, and **upgrade**. * Package installers in MSI format use [Product Codes](https://docs.microsoft.com/windows/win32/msi/product-codes) to uniquely identify applications. The product code for a given version of a package should be included in the manifest to help ensure the best **upgrade** experience. * Limit the length of strings in your manifest to 100 characters before a line break. * When more than one installer type exists for the specified version of the package, an instance of `InstallerType` can be placed under each of the `Installers`. ================================================ FILE: doc/windows/package-manager/package/repository.md ================================================ --- title: Submit your manifest to the repository description: After you create a package manifest that describes your application, you're ready to submit your manifest to the Windows Package Manager repository. ms.date: 04/29/2020 ms.topic: article ms.localizationpriority: medium --- # Submit your manifest to the repository After you create a [package manifest](manifest.md) that describes your application, you're ready to submit your manifest to the Windows Package Manager repository. This a public-facing repository that contains a collection of manifests that the **winget** tool can access. To submit your manifest, you'll upload it to the open source [https://github.com/microsoft/winget-pkgs](https://github.com/microsoft/winget-pkgs) repository on GitHub. After you submit a **pull request** to add a new manifest to the GitHub repository, an automated process will validate your manifest file and check to make sure the package complies with the [Windows Package Manager polices](windows-package-manager-policies.md) and is not known to be malicious. If this validation is successful, your package will be added to the public-facing Windows Package Manager repository so it can be discovered by the **winget** client tool. Note the distinction between the manifests in the open source GitHub repository and the public-facing Windows Package Manager repository. > **Important**: Microsoft reserves the right to refuse a submission for any reason. ## Third-party repositories There are currently no known third party repositories. Microsoft is working with multiple partners to develop protocols or an API to enable third party repositories. ## Manifest validation When you submit a manifest to the [https://github.com/microsoft/winget-pkgs](https://github.com/microsoft/winget-pkgs) repository on GitHub, your manifest will be automatically validated and evaluated for the safety of the Windows ecosystem. Manifests will be reviewed manually by [Community Moderators](https://github.com/microsoft/winget-pkgs/issues/15674). For information on the validation process, see [Windows Package Manager validation](winget-validation.md) ## How to submit your manifest To submit a manifest to the repository, follow these steps. ### Step 1: Validate your manifest The **winget** tool provides the [validate](../winget/validate.md) command to confirm that you have created your manifest correctly. To validate your manifest, use this command. ```CMD winget validate ``` If your validation fails, use the errors to locate the line number and make a correction. After your manifest is validated, you can submit it to the repository. ### Step 2: Clone the repository Next, create a fork of the repository and clone it. 1. Go to [https://github.com/microsoft/winget-pkgs](https://github.com/microsoft/winget-pkgs) in your browser and click **Fork**. ![picture of fork](images/fork.png) 2. From a command line environment such as the Windows Command Prompt or PowerShell, use the following command to clone your fork. ```CMD git clone ``` 3. If you are making multiple submissions, make a branch instead of a fork. We currently allow only one manifest file per PR. ```CMD git checkout -b ``` ### Step 3: Add your manifest to the local repository You must add your manifest files to the repository in the following folder structure: **manifests** / **letter** / **publisher** / **application** / **version** * The **manifests** folder is the root folder for all manifests in the repository. * The **letter** folder is the first letter of the publisher name in the lower case. For example, **m** of the publisher **Microsoft**. * The **publisher** folder is the name of the company that publishes the software. For example, **Microsoft**. * The **application** folder is the name of the application or tool. For example, **VSCode**. * The **version** folder is the version of the application or tool. For example, **1.0.0**. The `PackageIdentifier` and the `PackageVersion` values in the manifest must match the publisher, application names and version in the manifest folder path. For more information, see [Create your package manifest](manifest.md#tips-and-best-practices). ### Step 4: Submit your manifest to the remote repository You're now ready to push your new manifest to the remote repository. 1. Use the `commit` command to add files and commit the change and provide information on the submission. ```CMD git commit -m "Submitting ContosoApp version 1.0.0" --all ``` 3. Use the `push` command to push the changes to the remote repository. ```CMD git push ``` ### Step 5: Create a pull request After you push your changes, return to [https://github.com/microsoft/winget-pkgs](https://github.com/microsoft/winget-pkgs) and create a **pull request** to merge your fork or branch to the main branch. ![picture of pull request tab](images/pull-request.png) ## Submission process When you create a **pull request**, this will start an automated process that validates the manifests and verifies your **pull request**. During this process we will run tests against the installer and installed binaries to validate the submission. We add labels to your **pull request** so you can track its progress. For more information on labels and the process see [Windows Package Manager validation](winget-validation.md). Once complete, your submission will be manually reviewed by a moderator, and after it is approved, your application will be added to the Windows Package Manager catalog. If there is ever an error during the process, you will be notified and our labels and bot will assist you in fixing your submission. For the list of common errors, see [Windows Package Manager validation](winget-validation.md). ================================================ FILE: doc/windows/package-manager/package/windows-package-manager-policies-change-history.md ================================================ # Change history for Microsoft Store Policies | Date | Document Version | Change Description | |------|------------------|--------------------| | 5/25/2021 | 1.0 | Initial publishing of Windows Package Manager Policies| ================================================ FILE: doc/windows/package-manager/package/windows-package-manager-policies.md ================================================ # Windows Package Manager Repository Policies **Document version: 1.0** **Document date: May 22, 2021** **Effective date: May 22, 2021** Thank you for your interest in providing a Product to the Windows Package Manager repository. "Product" means content in whatever form including, but not limited to, apps, games, titles, and any additional content sold or offered from within a Product. "Submission" means [**Pull Request**](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) of manifest files and includes but is not limited to the "Product" and metadata about the "Product". A few principles to get you started: - Offer unique and distinct value within your Submission. Provide a compelling reason to download the Product from [Windows Package Manager repository](https://www.github.com/microsoft/winget-pkgs). - Don’t mislead our customers about what your Submission can do, who is offering it, etc. - Don’t attempt to cheat customers, the system or the ecosystem. There is no place in the repository for any kind of fraud, be it ratings and review manipulation, credit card fraud or other fraudulent activity. Adhering to these policies should help you make choices that enhance your Submission’s appeal and audience. Your Submissions are crucial to the experience of hundreds of millions of customers. We can’t wait to see what you create and are thrilled to help deliver your Submissions to the world. If you have feedback on the policies or the Windows Package Manager, please let us know by commenting in our [GitHub issues forum](https://www.github.com/microsoft/winget-cli/issues) ## Table of Contents **Product Policies:** - [1.1 Distinct Function & Value; Accurate Representation](#11-distinct-function--value-accurate-representation) - [1.2 Security](#12-security) - [1.3 Product is Testable](#13-product-is-testable) - [1.4 Usability](#14-usability) - [1.5 Personal Information](#15-personal-information) - [1.6 Capabilities](#16-capabilities) - [1.7 Localization](#17-localization) - [1.8 Financial Transactions](#18-financial-transactions) - [1.9 Notifications](#19-notifications) - [1.10 Advertising Conduct and Content](#110-advertising-conduct-and-content) **Content Policies:** - [2.1 General Content Requirements](#21-general-content-requirements) - [2.2 Content Including Names, Logos, Original and Third Party](#22-content-including-names-logos-original-and-third-party) - [2.3 Risk of Harm](#23-risk-of-harm) - [2.4 Defamatory, Libelous, Slanderous and Threatening](#24-defamatory-libelous-slanderous-and-threatening) - [2.5 Offensive Content](#25-offensive-content) - [2.6 Alcohol, Tobacco, Weapons and Drugs](#26-alcohol-tobacco-weapons-and-drugs) - [2.7 Adult Content](#27-adult-content) - [2.8 Illegal Activity](#28-illegal-activity) - [2.9 Excessive Profanity and Inappropriate Content](#29-excessive-profanity-and-inappropriate-content) - [2.10 Country/Region Specific Requirements](#210-countryregion-specific-requirements) - [2.11 Age Ratings](#211-age-ratings) - [2.12 User Generated Content](#212-user-generated-content) ## Product Policies ### 1.1 Distinct Function & Value; Accurate Representation The Product and its associated metadata, including but not limited to the app title, description, screenshots, trailers, content rating and Product category, must accurately and clearly reflect the source, functionality, and features of the Product. ### 1.1.1 All aspects of the Product should accurately describe the functions, features and any important limitations of the Product. ### 1.1.2 [Tags](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.4.0/manifest.defaultLocale.1.4.0.json) may not exceed 16 unique tags and should be relevant to the Product. ### 1.1.3 The Product must have distinct and informative metadata and must provide a valuable and quality user experience. ### 1.1.4 The [InstallerUrl](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.4.0/manifest.defaultLocale.1.4.0.json) must be the ISVs release location for the Product. Products from download websites will not be allowed. ### 1.2 Security The Product must not jeopardize or compromise user security, or the security or functionality of the device, system or related systems. ### 1.2.1 The Product must not attempt to change or extend its described functionality through any form of dynamic inclusion of code that is in violation of Windows Package Manager Policies. The Product should not, for example, download a remote script and subsequently execute that script in a manner that is not consistent with the described functionality. ### 1.2.2 The Product must not contain or enable malware as defined by the Microsoft criteria for [Unwanted and Malicious Software](https://docs.microsoft.com/windows/security/threat-protection/intelligence/criteria). ### 1.2.3 The Product may contain fully integrated middleware (such as third-party cross-platform engines and third-party analytics services). The Product may depend on non-integrated software (such as another Product, module, or service) to deliver its primary functionality, subject to the following requirements: ### 1.3 Product is Testable The Product must be testable. If it is not possible to test your submitted Product for any reason your Product may fail this requirement. ### 1.4 Usability The Product should meet usability standards, including, but not limited to, those listed in the subsections below. ### 1.4.1 The Product should support the devices and platforms on which they are downloaded, including compatibility with the software, hardware and screen resolution requirements specified by the Product. If the Product is downloaded on a device with which it is not compatible, it should detect that at launch and display a message to the customer detailing the requirements. ### 1.4.2 The Product should continue to run and remain responsive to user input. Products should shut down gracefully and not close unexpectedly. The Product should handle exceptions raised by any of the managed or native system APIs and remain responsive to user input after the exception is handled. ### 1.4.3 The Product should start up promptly and must stay responsive to user input. ### 1.5 Personal Information The following requirements apply to Products that access Personal Information. Personal Information includes all information or data that identifies or could be used to identify a person, or that is associated with such information or data. ### 1.5.1 If the Product accesses, collects or transmits Personal Information, or if otherwise required by law, it should maintain a privacy policy. The submission, should include the [PrivacyUrl](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/manifests/v1.4.0/manifest.defaultLocale.1.4.0.json) which links to the privacy policy of the Product. ### 1.5.2 If the Product publishes the Personal Information of customers of the Product to an outside service or third party, the Product should only do so after obtaining opt-in consent from those customers. Opt-in consent means the customer gives their express permission in the Product user interface for the requested activity, after the Product has: - described to the customer how the information will be accessed, used or shared, indicating the types of parties to whom it is disclosed, and - provided the customer a mechanism in the Product user interface through which they can later rescind this permission and opt-out. ### 1.5.3 If the Product publishes a person’s Personal Information to an outside service or third party through the Product or its metadata, but the person whose information is being shared is not a customer of the Product, the Product must obtain express written consent to publish that Personal Information, and must permit the person whose information is shared to withdraw that consent at any time. If the Product provides a customer with access to another person’s Personal Information, this requirement would also apply. ### 1.5.4 If the Product collects, stores or transmits Personal Information, it must do so securely, by using modern cryptography methods. ### 1.5.5 The Product must not collect, store or transmit highly sensitive personal information, such as health or financial data, unless the information is related to the Product’s functionality. The Product must also obtain express user consent before collecting, storing or transmitting such information. The Product’s privacy policy must clearly tell the user when and why it is collecting Personal Information and how it will be used. ### 1.5.6 If the Product supports Microsoft identity authentication it must do so only by using Microsoft-approved methods. ### 1.5.7 Products that receive device location must provide settings that allow the user to enable and disable the Product's access to and use of location from the Location Service API. ### 1.6 Capabilities If the Product declares the use of capabilities, then the capabilities the Product declares must legitimately relate to the functions of the Product. The Product must not circumvent operating system checks for capability usage. ### 1.7 Localization If the Product you should provide localized all languages that it supports. The experience provided by a product must be reasonably similar in all languages that it supports. ### 1.8 Financial Transactions If your product includes in-product purchase, subscriptions, virtual currency, billing functionality or captures financial information, the following requirements apply: ### 1.8.1 In-product offerings sold in your product cannot be converted to any legally valid currency (for example, USD, Euro, etc.) or any physical goods or services. ### 1.8.2 The Product must use a secure purchase API for purchases of physical goods or services, and a secure purchase API for payments made in connection with real world gambling or charitable contributions. If the Product is used to facilitate or collect charitable contributions or to conduct a promotional sweepstakes or contest, it must do so in compliance with applicable law. The Product must also state clearly that Microsoft is not the fundraiser or sponsor of the promotion. The Product must use a secure purchase API to receive voluntary donations from users. The following requirements apply to your use of a secure purchase API: - At the time of the transaction or when the Product collects any payment or financial information from the customer, the Product must identify the commerce transaction provider, authenticate the user, and obtain user confirmation for the transaction. - The product can offer the user the ability to save this authentication, but the user must have the ability to either require an authentication on every transaction or to turn off in-product transactions. - If the product collects credit card information or uses a third-party payment processor that collects credit card information, the payment processing must meet the current PCI Data Security Standard (PCI DSS). ### 1.8.3 The product and its associated metadata must provide information about the types of in-product purchases offered and the range of prices. The Product not mislead customers and must be clear about the nature of the in-product promotions and offerings including the scope and terms of any trial experiences. If the Product restricts access to user-created content during or after a trial, it must notify users in advance. In addition, the Product must make it clear to users that they are initiating a purchase option in the Product. If your game offers “loot boxes” or other mechanisms that provide randomized virtual items, then you must disclose the odds of receiving each item to customers prior to purchase. These disclosures may appear: in-product, such as in an in-app store, on the Microsoft Store Product Description Page (PDP), and/or on a developer or publisher website, with a link from the Store Product Description Page (PDP) and/or in-app. ### 10.8.4 All pricing, including sales or discounting, for your digital products or services shall comply with all applicable laws, regulations and regulatory guidelines, including without limitation, the Federal Trade Commission [Guides Against Deceptive Pricing](https://www.ecfr.gov/cgi-bin/text-idx?SID=676bd39fe43a808fcb417973b3d0247e&mc=true&tpl=/ecfrbrowse/Title16/16cfr233_main_02.tpl). ### 1.9 Notifications If the Product supports notifications, then the Product must respect system settings for notifications and remain functional when they are disabled. This includes the presentation of ads and notifications to the customer, which must also be consistent with the customer’s preferences, whether the notifications are provided by the Microsoft Push Notification Service (MPNS), Windows Push Notification Service (WNS) or any other service. If the customer disables notifications, either on an Product-specific or system-wide basis, the Product must remain functional. ### 1.10 Advertising Conduct and Content For all advertising related activities, the following requirements apply: ### 1.10.1 - The primary purpose of the Product should not be to get users to click ads. - The Product may not do anything that interferes with or diminishes the visibility, value, or quality of any ads it displays. - The Product must respect advertising ID settings that the user has selected. - All advertising must be truthful, non-misleading and comply with all applicable laws, regulations, and regulatory guidelines. ## Content Policies The following policies apply to content and metadata (including publisher name, Product name, Product icon, Product description, Product screenshots, Product trailers and trailer thumbnails, and any other Product metadata) offered for distribution in the Windows Package Manager repository. Content means the Product name, publisher name, Product icon, Product description, the images, sounds, videos and text contained in the Product, the tiles, notifications, error messages or ads exposed through the Product, and anything that’s delivered from a server or that the Product connects to. Because Product and the Windows Package Manager repository are used around the world, these requirements will be interpreted and applied in the context of regional and cultural norms. ### 2.1 General Content Requirements Metadata and other content you submit to accompany your submission may contain only content that would merit a rating of PEGI 12, ESRB EVERYONE 10+, or lower. ### 2.2 Content Including Names, Logos, Original and Third Party All content in the Product and associated metadata must be either originally created by the application provider, appropriately licensed from the third-party rights holder, used as permitted by the rights holder, or used as otherwise permitted by law. ### 2.3 Risk of Harm ### 2.3.1 The Product must not contain any content that facilitates or glamorizes the following real world activities: (a) extreme or gratuitous violence; (b) human rights violations; (c) the creation of illegal weapons; or (d) the use of weapons against a person, animal, or real or personal property. ### 2.3.2 The Product must not: (a) pose a safety risk to, nor result in discomfort, injury or any other harm to end users or to any other person or animal; or (b) pose a risk of or result in damage to real or personal property. You are solely responsible for all Product safety testing, certificate acquisition, and implementation of any appropriate feature safeguards. You will not disable any platform safety or comfort features, and you must include all legally required and industry-standard warnings, notices, and disclaimers in the Product. ### 2.4 Defamatory, Libelous, Slanderous and Threatening The Product must not contain any content that is defamatory, libelous, slanderous, or threatening. ### 2.5 Offensive Content The Product and associated metadata must not contain potentially sensitive or offensive content. Content may be considered sensitive or offensive in certain countries/regions because of local laws or cultural norms. In addition, the Product and associated metadata must not contain content that advocates discrimination, hatred, or violence based on considerations of race, ethnicity, national origin, language, gender, age, disability, religion, sexual orientation, status as a veteran, or membership in any other social group. ### 2.6 Alcohol, Tobacco, Weapons and Drugs The Product must not contain any content that facilitates or glamorizes excessive or irresponsible use of alcohol or tobacco Products, drugs, or weapons. ### 2.7 Adult Content The Product must not contain or display content that a reasonable person would consider pornographic or sexually explicit. ### 2.8 Illegal Activity The Product must not contain content or functionality that encourages, facilitates or glamorizes illegal activity in the real world. ### 2.9 Excessive Profanity and Inappropriate Content - The Product must not contain excessive or gratuitous profanity. - The Product must not contain or display content that a reasonable person would consider to be obscene. ### 2.10 Country/Region Specific Requirements Content that is offensive in any country/region to which the Product is targeted is not allowed. Content may be considered offensive in certain countries/regions because of local laws or cultural norms. Examples of potentially offensive content in certain countries/regions include the following: China - Prohibited sexual content - Disputed territory or region references - Providing or enabling access to content or services that are illegal under applicable local law ### 2.11 Age Ratings The Product should have a age rating that would merit a rating of PEGI 12, ESRB EVERYONE 10+, or lower. ### 2.11.1 If the Product provides content (such as user-generated, retail or other web-based content) that might be appropriate for a higher age rating than its assigned rating, you must enable users to opt in to receiving such content by using a content filter or by signing in with a preexisting account. ### 2.12 User Generated Content User Generated Content (UGC) is content that users contribute to an app or Product and which can be viewed or accessed by other users in an online state. If the Product contains UGC, the Product should: - Publish and make available to users a Product terms of service and/or content guidelines for User Generated Content either in Product or on the Product website. - Provide a means for users to report inappropriate content within the Product to the developer for review and removal/disablement if in violation of content guidelines and/or implement a method for proactive detection of inappropriate or harmful UGC. - Remove or disable UGC when requested by Microsoft. ### See also - [Change history for Windows Package Manager Policy History](windows-package-manager-policies-change-history.md) - [Windows Package Manager Code of Conduct](https://github.com/microsoft/winget-pkgs/blob/master/CODE_OF_CONDUCT.md) - [Windows Package Manager Contributing requirements](https://github.com/microsoft/winget-pkgs/blob/master/README.md) ================================================ FILE: doc/windows/package-manager/package/winget-validation-troubleshooter.md ================================================ # Change history for Microsoft Store Policies | Date | Document Version | Change Description | |------|------------------|--------------------| | 5/25/2021 | 1.0 | Initial publishing of Windows Package Manager Policies| ================================================ FILE: doc/windows/package-manager/package/winget-validation.md ================================================ ## Validation process When you create a pull request, this will start an automation process that validates the manifest and processes your pull request. GitHub labels are used to share progress and allow you to communicate with us. ## Submission expectations All application submissions to the Windows Package Manager repository should be well-behaved and adhere to the [Windows Package Manager policies](./windows-package-manager-policies.md). Here are some expectations for submissions: - The manifest complies with the [schema requirements](https://learn.microsoft.com/en-us/windows/package-manager/package/manifest?tabs=minschema%2Cversion-example#minimal-required-schema). - All URLs in the manifest lead to safe websites. - The installer and application are virus free. The package may be identified as malware by mistake. If you believe it is a false positive you can [submit the installer to the defender team for analysis](https://www.microsoft.com/wdsi/filesubmission). - The application installs and uninstalls correctly for both administrators and non-administrators. - The installer supports non-interactive modes. - All manifest entries are accurate and not misleading. - The installer comes directly from the publisher\'s website. Please see [Windows Package Manager policies](windows-package-manager-policies.md) for a complete list of the policies. ## Pull request labels During validation, we apply a series of labels to our pull request to communicate progress. Some labels will direct the ISV to take action, while others will be directed to the Package Manager developers. ### Status Labels The following table describes the possible **status labels** you will encounter: | **Label** | **Details** | |--------------|-------------| | | | | **Azure-Pipeline-Passed** | The manifest has completed the test pass. It is waiting for approval. If no issues are encountered during the test pass it will automatically be approved. If a test fails, it may be flagged for manual review.| | **Blocking-Issue** | This label indicates that the **Pull Request** cannot be approved because there is a blocking issue. You can often tell what the blocking issue is by the included error label as well. | | **Needs-Attention** | This label indicates that the **Pull Request** needs to be investigated by the Windows Package Manager development team. This is either due to a test failure that needs manual review, or a comment added to the **Pull Request** by the community. | | **Needs-Author-Feedback** | Indicates there is a failure with the submission. We will reassign **Pull Request** back to you. If you do not address the issue within 10 days, the bot will close the **pull request**. **Needs-Author-Feedback** labels are typically added when there was a failure with the Pull Request that should be updated, or if the person reviewing the Pull Request has a question. | | **Validation-Completed** | Indicates that the test pass has been completed successfully and your **Pull Request** will be merged.| ### Error Labels The following table describes the possible **error labels** that will be encountered. Not all of the error cases will be assigned to the ISV immediately. Some may trigger manual validation. | **Label** | **Details** | |--------------|-------------| ||| | **Binary-Validation-Error** | The application included in this **Pull Request** failed to pass the **Installers Scan** test. This test is designed to ensure that the application installs on all environments without warnings. For further details on this error, see [binary validation errors](binary-validation-errors.md). | | **Error-Analysis-Timeout** | This label indicates that the **Binary-Validation-Test** test timed out. The **Pull Request** will get assigned to a Windows Package Manager developer to look at it. | | **Error-Hash-Mismatch** | The submitted manifest could not be processed because the **InstallerSha256** hash provided for the **InstallerURL** did not match. Update the **InstallerSha256** in the **Pull Request** and try again. | | **Error-Installer-Availability** | The validation service was unable to download the installer. This may be related to Azure IP ranges being blocked, or the installer URL may be incorrect. Check that the **InstallerURL** is correct and try again. If you feel this has failed in error, please add a comment and the **Pull Request** will get assigned to a Windows Package Manager developer to look investigate. | | **Manifest-Path-Error** | The manifest files must be put into a specific folder structure. This label indicates a problem with the path of your submission. For example, the folder structure does not have the [required format](https://docs.microsoft.com/windows/package-manager/package/manifest?tabs=minschema%2Ccompschema). Update your manifest and path resubmit your **Pull Request**. | | **Manifest-Validation-Error** | The submitted manifest contains a syntax error. Address the syntax issue with the manifest and re-submit. For details on the manifest format and schema see: [required format](https://docs.microsoft.com/windows/package-manager/package/manifest?tabs=minschema%2Ccompschema). | | **PullRequest-Error** | The pull request is invalid because not all files submitted are under manifest folder or there is more than one package or version in the **Pull Request**. Update your **Pull Request** to address the issue and try again. | | **URL-Validation-Error** | The **URLs Validation Test** could not locate the URL and responded with a [HTTP error status code](https://docs.microsoft.com/troubleshoot/iis/http-status-code) (403 or 404), or the URL reputation test failed. You can identify which URL is in question by looking at the [Pull Request check details](winget-validation-troubleshooter.md). To address this issue, update the URLs in question to resolve the [HTTP error status code](https://docs.microsoft.com/troubleshoot/iis/http-status-code). If the issue is not due to [HTTP error status code](https://docs.microsoft.com/troubleshoot/iis/http-status-code) then you can [submit the URL for review](https://www.microsoft.com/wdsi/filesubmission/) to avoid the reputation failure. | | **Validation-Defender-Error** | During dynamic testing, Defender reported a problem. To reproduce this problem, install your application, then run a Defender full scan. If you can reproduce the problem, either fix the binary, or submit to this URL for false positive assistance. As stated in the following article, [Address false positives/negatives in Microsoft Defender for Endpoint Microsoft Docs](https://docs.microsoft.com/microsoft-365/security/defender-endpoint/defender-endpoint-false-positives-negatives?view=o365-worldwide), you can submit your binary for analysis to the [defender analysis web page](https://docs.microsoft.com/microsoft-365/security/defender-endpoint/defender-endpoint-false-positives-negatives?view=o365-worldwide#part-4-submit-a-file-for-analysis). If you are unable to reproduce, add a comment to get the Windows Package Manager developers to look at it. | | **Validation-Domain** | The test has determined the domain if the **InstallerURL** does not match the domain expected. The Windows Package Manager policies requires that the [InstallerUrl](https://docs.microsoft.com/windows/package-manager/package/manifest?tabs=minschema%2Ccompschema) comes directly from the ISVs release location. If you believe this is a false detection, add a comment to the **Pull Request** to get the Windows Package Manager developers to look at it. | | **Validation-Error** | Validation of the Windows Package Manager failed during manual approval. Look at the accompanying comment for next steps. | | **Validation-Executable-Error** | During installation testing, the test was unable to locate the primary application. Make sure the application installs correctly on all platforms. If your application does not install an application, but should still be included in the repository, add a comment to the **Pull Request** to get the Windows Package Manager developers to look at it.| | **Validation-Hash-Verification-Failed** | During installation testing, the application fails to install because the **InstallerSha256** no longer matches the **InstallerURL** hash. This can occur if the application is behind a vanity URL and the installer was updated without updating the **InstallerSha256**. To address this issue, update the **InstallerSha256** associated with the **InstallerURL** and submit again. | | **Validation-HTTP-Error** | The URL used for the installer does not use the HTTPs protocol. Please update the **InstallerURL** to use HTTPS and resubmit the **Pull Request.** | | **Validation-Indirect-URL** | The URL is not coming directly from the ISVs server. Testing has determined a redirector has been used. This is not allowed because the Windows Package Manager policies require that the [InstallerUrl](https://docs.microsoft.com/windows/package-manager/package/manifest?tabs=minschema%2Ccompschema) comes directly from the ISVs release location. Remove the redirection and resubmit. | **Validation-Installation-Error** | During manual validation of this package, there was a general error. Look at the accompanying comment for next steps.| | **Validation-Merge-Conflict** | This package could not be validated due to a merge conflict. Please address the merge conflict and resubmit your **Pull Request.** | **Validation-MSIX-Dependency** | The MSIX package has a dependency on package that could not be resolved. Update the package to include the missing components or add the dependency to the manifest file and resubmit the **Pull Request.**| | **Validation-Unapproved-URL** | The test has determined the domain if the **InstallerURL** does not match the domain expected. The Windows Package Manager policies requires that the [InstallerUrl](https://docs.microsoft.com/windows/package-manager/package/manifest?tabs=minschema%2Ccompschema) comes directly from the ISVs release location. | | **Validation-Unattended-Failed** | During installation, the test timed out.This most likely is due to the application not installing silently. It could also be due to some other error being encountered and stopping the test. Verify that you can install your manifest without user input. If you need assistance, add a comment to the **Pull Request** and the Windows Package Manager developers will look at it. | | **Validation-Uninstall-Error** | During uninstall testing, the application did not clean up completely following uninstall. Look at the accompanying comment for more details.| | **Validation-VCRuntime-Dependency** | The package has a dependency on the C++ runtime that could not be resolved. Update the package to include the missing components or add the dependency to the manifest file and resubmit the **Pull Request**. | ### Content Policy Labels The following table lists **content policy labels**. If one of the following labels is added, then something in the manifest metadata triggered additional manual content review to ensure that the metadata is following the [Windows Package Manager policies](windows-package-manager-policies.md). | **Label** | **Details** | |--------------|-------------| ||| | **Policy-Test-2.1** |Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#21-general-content-requirements) | | **Policy-Test-2.2** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#22-content-including-names-logos-original-and-third-party) | | **Policy-Test-2.3** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#23-risk-of-harm) | | **Policy-Test-2.4** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#24-defamatory-libelous-slanderous-and-threatening) | | **Policy-Test-2.5** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#25-offensive-content) | | **Policy-Test-2.6** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#26-alcohol-tobacco-weapons-and-drugs) | | **Policy-Test-2.7** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#27-adult-content) | | **Policy-Test-2.8** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#28-illegal-activity) | | **Policy-Test-2.9** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#29-excessive-profanity-and-inappropriate-content) | | **Policy-Test-2.10** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#210-countryregion-specific-requirements) | | **Policy-Test-2.11** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#211-age-ratings) | | **Policy-Test-2.12** | Manual review triggered see [Windows Package Manager Policies](windows-package-manager-policies.md#212-user-generated-content) | ### Internal Labels The following table lists the **internal errors**. When internal errors are encountered the **Pull Request** will be assigned to the Windows Package Manager developers to investigate: | **Label** | **Details** | |--------------|-------------| ||| |**Internal-Error-Domain**|During the domain validation of the URL, the test encountered an issue. A Windows Package Manager developer will take a look at it.| |**Internal-Error-Dynamic-Scan**|During the validation of the installed binaries, the test encountered an issue. A Windows Package Manager developer will take a look at it.| |**Internal-Error-Keyword-Policy**| During the validation of the manifest, the test encountered an issue. A Windows Package Manager developer will take a look at it.| |**Internal-Error-Manifest**| During the validation of the manifest, the test encountered an issue. A Windows Package Manager developer will take a look at it.| |**Internal-Error-NoArchitectures**| Testing encountered and issue where the test could not determine the architecture if the application. A Windows Package Manager developer will take a look at it.| |**Internal-Error-NoSupportedArchitectures**| Testing encountered and issue where the current architecture is not supported. A Windows Package Manager developer will take a look at it.| |**Internal-Error-PR**| An error occurred during the processing of the PR. A Windows Package Manager developer will take a look at it.| |**Internal-Error-Static-Scan**| During static analysis of the installers, the test encountered an issue. A Windows Package Manager developer will take a look at it.| |**Internal-Error-URL**| During reputation validation of the installers, the test encountered an issue. A Windows Package Manager developer will take a look at it.| |**Internal-Error**| This indicates a generic failure or unknown error was encountered during the test pass. A Windows Package Manager developer will take a look at it.| ================================================ FILE: doc/windows/package-manager/winget/export.md ================================================ --- title: export Command description: exports the list of installed applications. ms.date: 05/02/2021 ms.topic: overview ms.localizationpriority: medium --- # export command (winget) The **export** command of the [winget](index.md) tool exports a JSON file of apps to a specified file. The **export** command users JSON as the format. See [the JSON schema used by **winget**](https://aka.ms/winget-packages.schema.1.0.json). The **export** combined with the [**import**](import.md) command allows you to batch install applications on your PC. The **export** command is often used to create a file that you can share with other developers, or for use when restoring your build environment. ## Usage `winget export [-o] []` ![export](images/export.png) ## Arguments The following arguments are available. | Argument | Description | |-------------|-------------| | **-o,--output** | Path to the JSON file to be created ## Options The options allow you to customize the export experience to meet your needs. | Option | Description | |--------|-------------| | **-s, --source** | [optional] Specifies a source to export files from. Use this option when you only want files from a specific source. | | **--include-versions** | [optional] Includes the version of the app currently installed. Use this option if you want a specific version. By default, unless specified, [**import**](import.md) will use latest. | | **--accept-source-agreements** | Accept all source agreements during source operations | | **-?, --help** | Shows help about the selected command | | **--wait** | Prompts the user to press any key before exiting | | **--logs, --open-logs** | Open the default logs location | | **--verbose, --verbose-logs** | Enables verbose logging for winget | | **--disable-interactivity** | Disable interactive prompts | ## JSON Schema The driving force behind the **export** command is the JSON file. As mentioned, you can see the [schema for the JSON file](https://aka.ms/winget-packages.schema.1.0.json). The JSON file includes the following hierarchy: | Entry | Description | |-------------|-------------| | **Sources** | The sources application manifests come from. | | **Packages** | The collection of packages to install. | | **PackageIdentifier** | The Windows Package Manager package identifier used to specify the package. | | **Version** | [Optional] The specific version of the package to install. | ## exporting files When the Windows Package Manager exports the JSON file, it attempts to export all the applications installed on the PC. If the **winget export** command is not able to match an application to an application from an available **source**, the export command will show a warning. Note: matching an application depends on metadata in the manifest from a configured source, and metadata in Add / Remove Programs in Windows based on the package installer. In the example below, you will see warnings for **WhatsApp Desktop** and **7-Zip**. ![export](images/export-command.png) Once the export is complete, you can edit the resulting JSON file in your favorite editor. You can remove apps you do not wish to import in the future. ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/features.md ================================================ --- title: features Command description: Displays the list of experimental features available and the state. ms.date: 05/5/2021 ms.topic: overview ms.localizationpriority: medium --- # features command (winget) The **features** command of the [winget](index.md) tool displays a list of the experimental features available with your version of the Windows Package Manager. Each feature can be turned on individually by enabling the features through [**settings**](settings.md). You can find the latest up to date information on the [experimental features](../../../Settings.md#experimental-features) web page. ## Usage ![features command](images/features.png) Notice above that the status of each feature is listed. If the feature is **disabled** you will not be able to use it. If the feature is **enabled** you will notice that the command will be available to you through winget. To enabled any disabled features, go to **settings** and enable the feature. Note: features may be managed by group policy. You can use the **winget --info** command to view any policies in effect on your system. ================================================ FILE: doc/windows/package-manager/winget/hash.md ================================================ --- title: winget hash command description: Generates the SHA256 hash for an installer. ms.date: 04/28/2020 ms.topic: article ms.localizationpriority: medium --- # hash command (winget) The **hash** command of the [winget](index.md) tool generates the SHA256 hash for an installer. This command is used if you need to create a [manifest file](../package/manifest.md) for submitting software to the **Microsoft Community Package Manifest Repository** on GitHub. In addition, the **hash** command also supports generating a SHA256 certificate hash for MSIX files. ## Usage `winget hash [--file] []` ![hash](images/hash.png) The **hash** sub-command can only run on a local file. To use the **hash** sub-command, download your installer to a known location. Then pass in the file path as an argument to the **hash** sub-command. ## Arguments The following arguments are available: | Argument | Description | |--------------|-------------| | **-f, --file** | The path to the file to be hashed. | ## Options The following options are available: | Option | Description | |--------|-------------| | **-m, --msix** | Specifies that the hash command will also create the SHA-256 SignatureSha256 for use with MSIX installers. | | **-?, --help** | Gets additional help on this command. | | **--wait** | Prompts the user to press any key before exiting | | **--logs, --open-logs** | Open the default logs location | | **--verbose, --verbose-logs** | Enables verbose logging for winget | | **--disable-interactivity** | Disable interactive prompts | ## Related topics * [Use the winget tool to install and manage applications](index.md) * [Submit packages to Windows Package Manager](../package/index.md) ================================================ FILE: doc/windows/package-manager/winget/help.md ================================================ --- title: winget help Command description: Displays help for all the supported commands. ms.date: 04/28/2020 ms.topic: article ms.localizationpriority: medium --- # help command (winget) The **help** command of the [winget](index.md) tool displays help for all the supported commands and sub commands. In addition, you can pass the **--help** argument to any other command to get details about all additional command options. ## Usage * Display help for all commands: `winget --help` or `winget -?` * View options for a command: `winget --help` or `winget -?` ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/import.md ================================================ --- title: import Command description: imports the list of installed applications. ms.date: 05/02/2021 ms.topic: overview ms.localizationpriority: medium --- # import command (winget) The **import** command of the [winget](index.md) tool imports a JSON file of apps to install. The **import** command combined with the [**export**](export.md) command allows you to batch install applications on your PC. The **import** command is often used to share your developer environment or build up your PC image with your favorite apps. ## Usage `winget import [-i] []` ![import](images/import.png) ## Arguments The following arguments are available. | Argument | Description | |-------------|-------------| | **-i, --import-file** | JSON file describing the packages to install ## Options The options allow you to customize the import experience to meet your needs. | Option | Description | |-------------|-------------| | **--ignore-unavailable** | Suppresses errors if the app requested is unavailable | | **--ignore-versions** | Ignores versions specified in the JSON file and installs the latest available version | | **--no-upgrade** | Skips upgrade if an installed version already exists | | **--accept-package-agreements** | Accept all license agreements for packages | | **--accept-source-agreements** | Accept all source agreements during source operations | | **-?, --help** | Shows help about the selected command | | **--wait** | Prompts the user to press any key before exiting | | **--logs, --open-logs** | Open the default logs location | | **--verbose, --verbose-logs** | Enables verbose logging for winget | | **--disable-interactivity** | Disable interactive prompts | ## JSON Schema The driving force behind the **import** command is the JSON file. You can see the [schema for the JSON file](https://aka.ms/winget-packages.schema.1.0.json). The JSON file includes the following hierarchy: | Entry | Description | |-------------|-------------| | **Sources** | The sources application manifests come from. | | **Packages** | The collection of packages to install. | | **PackageIdentifier** | The Windows Package Manager package identifier used to specify the package. | | **Version** | [optional] The specific version of the package to install. | ## Importing files When the Windows Package Manager imports the JSON file, it attempts to install the specified applications in a serial fashion. If the application is not available or the application is already installed, it will notify the user of that case. ![import](images/import-command.png) You will notice in the example above, **Microsoft.VisualStudioCode** and **JanDeDobbeleer.OhMyPosh** were already installed. Therefore the import command skipped the installation. ================================================ FILE: doc/windows/package-manager/winget/index.md ================================================ --- title: Use the winget tool to install and manage applications description: The winget command line tool enables developers to discover, install, upgrade, remove and configure applications on Windows 10 computers. ms.date: 10/22/2020 ms.topic: overview ms.localizationpriority: medium --- # Use the winget tool to install and manage applications The **winget** command line tool enables users to discover, install, upgrade, remove and configure applications on Windows 10 and Windows 11 computers. This tool is the client interface to the Windows Package Manager service. ## Install winget There are several ways to install the **winget** tool: * The **winget** tool is included in the flight or preview version of [Windows App Installer](https://apps.microsoft.com/detail/9nblggh4nns1?ocid=9nblggh4nns1_ORSEARCH_Bing&rtc=1&activetab=pivot:overviewtab). You must install the preview version of **App Installer** to use **winget**. To gain early access, submit your request to the [Windows Package Manager Insiders Program](https://aka.ms/AppInstaller_InsiderProgram). Participating in the flight ring will guarantee you see the latest preview updates. * Participate in the [Windows Insider Dev Channel](https://insider.windows.com/understand-flighting). * Install the Windows Desktop App Installer package located on the [Releases page for the winget repository](https://github.com/microsoft/winget-cli/releases). > The **winget** tool is supported on Windows 10, version 1809 (build 17763) and above. ## Administrator considerations Installer behavior can be different depending on whether you are running **winget** with administrator privileges. * When running **winget** without administrator privileges, some applications may [require elevation](https://docs.microsoft.com/windows/security/identity-protection/user-account-control/how-user-account-control-works) to install. When the installer runs, Windows will prompt you to [elevate](https://docs.microsoft.com/windows/security/identity-protection/user-account-control/how-user-account-control-works). If you choose not to elevate, the application will fail to install. * When running **winget** in an Administrator Command Prompt, you will not see [elevation prompts](https://docs.microsoft.com/windows/security/identity-protection/user-account-control/how-user-account-control-works) if the application requires it. Always use caution when running your command prompt as an administrator, and only install applications you trust. ## Use winget After **App Installer** is installed, you can run **winget** by typing 'winget' from a Command Prompt. One of the most common usage scenarios is to search for and install a favorite tool. 1. To [search](search.md) for a tool, type `winget search `. 2. After you have confirmed that the tool you want is available, you can [install](install.md) the tool by typing `winget install `. The **winget** tool will launch the installer and install the application on your PC. ![winget commandline](images/install.png) 3. In addition to install and search, **winget** provides a number of other commands that enable you to [show details](show.md) on applications, [change sources](source.md), and [validate packages](validate.md). To get a complete list of commands, type: `winget --help`. ![winget help](images/help.png) ### Commands The current preview of the **winget** tool supports the following commands. | Command | Description | |---------|-------------| | [install](install.md) | Installs the specified application. | | [show](show.md) | Displays details for the specified application. | | [source](source.md) | Adds, removes, and updates the Windows Package Manager repositories accessed by the **winget** tool. | | [search](search.md) | Searches for an application. | | [list](list.md) | Display installed packages. | | [upgrade](upgrade.md) | Upgrades the given package. | | [uninstall](uninstall.md) | Uninstalls the given package. | | [hash](hash.md) | Generates the SHA256 hash for the installer. | | [validate](validate.md) | Validates a manifest file for submission to the Windows Package Manager repository. | | [settings](settings.md) | Open settings. | | [features](features.md) | Shows the status of experimental features. | | [export](export.md) | Exports a list of the installed packages. | | [import](import.md) | Installs all the packages in a file. | | pin | Manage package pins. | | configure | Configures the system into a desired state. | | download | Downloads the installer from a given package. | ### Options The current version of the **winget** tool supports the following options. | Option | Description | |--------------|-------------| | **-v, --version** | Returns the current version of winget. | | **--info** | Provides you with all detailed information on winget, including the links to the license, privacy statement, and configured group policies. | | **-?, --help** | Shows additional help for winget. | | **--wait** | Waits for user input upon command completion. | | **--logs,--open-logs** | Open the default logs location | | **--verbose,--verbose-logs** | Enables verbose logging for winget | | **--disable-interactivity** | Disable interactive prompts | ## Supported installer formats The current version of the **winget** tool supports the following types of installers: * EXE (with **Silent** and **SilentWithProgress** flags) * INNO * NULLSOFT * MSI * APPX * MSIX * BURN * PORTABLE * ZIP ## Scripting winget You can author batch scripts and PowerShell scripts to install multiple applications. ```CMD @echo off Echo Install Powertoys and Terminal REM Powertoys winget install Microsoft.Powertoys if %ERRORLEVEL% EQU 0 Echo Powertoys installed successfully. REM Terminal winget install Microsoft.WindowsTerminal if %ERRORLEVEL% EQU 0 Echo Terminal installed successfully. %ERRORLEVEL% ``` > When scripted, **winget** will launch the applications in the specified order. When an installer returns success or failure, **winget** will launch the next installer. If an installer launches another process, it is possible that it will return to **winget** prematurely. This will cause **winget** to install the next installer before the previous installer has completed. ## Missing tools If the [community repository](../package/repository.md) does not include your tool or application, please submit a package to our [repository](https://github.com/microsoft/winget-pkgs). By adding your favorite tool, it will be available to you and everyone else. ## Customize winget settings You can configure the **winget** command line experience by modifying the **settings.json** file. For more information, see [https://aka.ms/winget-settings](https://aka.ms/winget-settings). Note that the settings are still in an experimental state and not yet finalized for the preview version of the tool. ## Open source details The **winget** tool is open source software available on GitHub in the repo [https://github.com/microsoft/winget-cli/](https://github.com/microsoft/winget-cli/). The source for building the client is located in the [src folder](https://github.com/microsoft/winget-cli/tree/master/src). The source for **winget** is contained in a Visual Studio solution. To build the solution correctly, install the latest [Visual Studio with the C++ workload](https://visualstudio.microsoft.com/downloads/). We encourage you to contribute to the **winget** source on GitHub. You must first agree to and sign the Microsoft CLA. ================================================ FILE: doc/windows/package-manager/winget/install.md ================================================ --- title: install Command description: Installs the specified application. ms.date: 04/28/2020 ms.topic: overview ms.localizationpriority: medium --- # install command (winget) The **install** command of the [winget](index.md) tool installs the specified application. Use the [**search**](search.md) command to identify the application you want to install. The **install** command requires that you specify the exact string to install. If there is any ambiguity, you will be prompted to further filter the **install** command to an exact application. ## Usage `winget install [[-q] ] []` The following command aliases are available: \ `add` ![search command](images/install.png) ## Arguments The following arguments are available. | Argument | Description | |-------------|-------------| | **-q, --query** | The query used to search for an app. | ## Options The options allow you to customize the install experience to meet your needs. | Option | Description | |-------------|-------------| | **-m, --manifest** | Must be followed by the path to the manifest (YAML) file. You can use the manifest to run the install experience from a [local YAML file](#local-install). | | **--id** | Limits the install to the ID of the application. | | **--name** | Limits the search to the name of the application. | | **--moniker** | Limits the search to the moniker listed for the application. | | **-v, --version** | Enables you to specify an exact version to install. If not specified, latest will install the highest versioned application. | | **-s, --source** | Restricts the search to the source name provided. Must be followed by the source name. | | **--scope** | Select install scope (user or machine) | | **-e, --exact** | Uses the exact string in the query, including checking for case-sensitivity. It will not use the default behavior of a substring. | | **-i, --interactive** | Runs the installer in interactive mode. The default experience shows installer progress. | | **-h, --silent** | Runs the installer in silent mode. This suppresses all UI. The default experience shows installer progress. | | **-o, --log** | Directs the logging to a log file. You must provide a path to a file that you have the write rights to. | | **--override** | A string that will be passed directly to the installer. | | **-l, --location** | Location to install to (if supported). | | **--force** | Override the installer hash check. | | **-a, --architecture** | Select the architecture | | **--installer-type** | Select the installer type | | **--locale** | Locale to use (BCP47 format) | | **--custom** | Arguments to be passed on to the installer in addition to the defaults | | **--ignore-security-hash** | Ignore the installer hash check failure | | **--skip-dependencies** | Skip processing package dependencies and Windows features | | **--ignore-local-archive-malware-scan** | Ignore the malware scan performed as part of installing an archive-type package from a local manifest | | **--dependency-source** | Find package dependencies using the specified source | | **--accept-package-agreements** | Accept all license agreements for packages | | **--no-upgrade** | Skip upgrade if an installed version already exists | | **--header** | Optional Windows-Package-Manager REST source HTTP header | | **--accept-source-agreements** | Accept all source agreements during source operations | | **-r, --rename** | The value to rename the executable file (portable) | | **--uninstall-previous** | Uninstall the previous version of the package during the upgrade | | **--ignore-interactivity** | Disable interactive prompts | | **-?, --help** | Get additional help on this command. | | **--wait** | Prompts the user to press any key before exiting | | **--logs, --open-logs** | Open the default logs location | | **--verbose, --verbose-logs** | Enables verbose logging for winget | | **--disable-interactivity** | Disable interactive prompts | ### Example queries The following example installs a specific version of an application. ```CMD winget install powertoys --version 0.15.2 ``` The following example installs an application from its **Package Identifier**. ```CMD winget install --id Microsoft.PowerToys ``` The following example installs an application by version and ID. ```CMD winget install --id Microsoft.PowerToys --version 0.15.2 ``` ## Multiple selections If the query provided to **winget** does not result in a single application, then **winget** will display the results of the search. This will provide you with the additional data necessary to refine the search for a correct install. The best way to limit the selection to one file is to use the **id** of the application combined with the **exact** query option. For example: ```CMD winget install --id Git.Git -e ``` If multiple sources are configured, it is possible to have duplicate entries. Specifying a source is required to further disambiguate. ```CMD winget install --id Git.Git -e --source winget ``` ## Local install The **manifest** option enables you to install an application by passing in a YAML file directly to the client. If the manifest is a multi file manifest, the directory containing the files must be used. The **manifest** option has the following usage. Usage: `winget install --manifest ` | Option | Description | |---------|-------------| | **-m, --manifest** | The path to the manifest of the application to install. | ### Log files The log files for winget unless redirected, will be located in the following folder: **\%temp%\\AICLI\\*.log** ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/list.md ================================================ --- title: features Command description: Displays the list of listed apps and if an update is available. ms.date: 05/5/2021 ms.topic: overview ms.localizationpriority: medium --- # list command (winget) The **list** command of the [winget](index.md) tool displays a list of the applications currently installed on your computer. The list command will show apps that were installed through the Windows Package Manager as well as apps that were installed by other means. In addition, the **list** command will also display if an update is available for an app, and you can use the [**upgrade**](upgrade.md) command to update the app. The **list** command also supports filters which can be used to limit your list query. ## Usage `winget list [[-q] ] []` The following command aliases are available: \ `ls` ![list help command](images/list.png) ## Arguments The following arguments are available. | Argument | Description | |-------------|-------------| | **-q,--query** | The query used to search for an app. | ## Options The options allow you to customize the list experience to meet your needs. | Option | Description | |--------|-------------| | **--id** | Limits the list to the ID of the application. | | **--name** | Limits the list to the name of the application. | | **--moniker** | Limits the list to the moniker listed for the application. | | **-s, --source** | Restricts the list to the source name provided. Must be followed by the source name. | | **--tag** | Filters results by tags. | | **--command** | Filters results by command specified by the application. | | **-n, --count** | Limits the number of apps displayed in one query. | | **-e, --exact** | Uses the exact string in the list query, including checking for case-sensitivity. It will not use the default behavior of a substring. | | **--scope** | Select installed package scope filter (user or machine). | | **--header** | Optional Windows-Package-Manager REST source HTTP header. | | **--accept-source-agreements** | Accept all source agreements during source operations. | | **--upgrade-available** | Lists only packages which have an upgrade available. | | **-u,--unknown,--include-unknown** | List packages even if their current version cannot be determined. Can only be used with the --upgrade-available argument. | | **--pinned,--include-pinned** | List packages even if they have a pin that prevents upgrade. Can only be used with the --upgrade-available argument. | | **-?,--help** | Get additional help on this command. | | **--wait** | Prompts the user to press any key before exiting. | | **--logs,--open-logs** | Open the default logs location. | | **--verbose,--verbose-logs** | Enables verbose logging for winget. | | **--disable-interactivity** | Disable interactive prompts. | ### Example queries The following example lists a specific version of an application. ![list name command](images/list-name.png) The following example lists all application by ID from a specific source. ![list id with source command](images/list-id-source.png) The following example limits the output of list to 9 apps. ![list count command](images/list-count.png) ## List with Update As stated above, the **list** command allows you to see what apps you have installed that have updates available. In the image below, you will notice the current version of **Google Chrome** has an update available. ![list update command](images/list-update.png) The **list** command will show not only the update version available, but the source that the update is available from. ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/returnCodes.md ================================================ --- title: Exit Codes description: WinGet return codes and their meanings ms.date: 05/02/2023 ms.topic: article ms.localizationpriority: medium --- # Return Codes ## General Errors | Hex | Decimal | Symbol | Description | |-------------|-------------|-------------|-------------| | 0x8A150001 | -1978335231 | APPINSTALLER_CLI_ERROR_INTERNAL_ERROR | Internal Error | | 0x8A150002 | -1978335230 | APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS | Invalid command line arguments | | 0x8A150003 | -1978335229 | APPINSTALLER_CLI_ERROR_COMMAND_FAILED | Executing command failed | | 0x8A150004 | -1978335228 | APPINSTALLER_CLI_ERROR_MANIFEST_FAILED | Opening manifest failed | | 0x8A150005 | -1978335227 | APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED | Cancellation signal received | | 0x8A150006 | -1978335226 | APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED | Running ShellExecute failed | | 0x8A150007 | -1978335225 | APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION | Cannot process manifest. The manifest version is higher than supported. Please update the client. | | 0x8A150008 | -1978335224 | APPINSTALLER_CLI_ERROR_DOWNLOAD_FAILED | Downloading installer failed | | 0x8A150009 | -1978335223 | APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX | Cannot write to index; it is a higher schema version | | 0x8A15000A | -1978335222 | APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED | The index is corrupt | | 0x8A15000B | -1978335221 | APPINSTALLER_CLI_ERROR_SOURCES_INVALID | The configured source information is corrupt | | 0x8A15000C | -1978335220 | APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS | The source name is already configured | | 0x8A15000D | -1978335219 | APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE | The source type is invalid | | 0x8A15000E | -1978335218 | APPINSTALLER_CLI_ERROR_PACKAGE_IS_BUNDLE | The MSIX file is a bundle, not a package | | 0x8A15000F | -1978335217 | APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING | Data required by the source is missing | | 0x8A150010 | -1978335216 | APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER | None of the installers are applicable for the current system | | 0x8A150011 | -1978335215 | APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH | The installer file's hash does not match the manifest | | 0x8A150012 | -1978335214 | APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST | The source name does not exist | | 0x8A150013 | -1978335213 | APPINSTALLER_CLI_ERROR_SOURCE_ARG_ALREADY_EXISTS | The source location is already configured under another name | | 0x8A150014 | -1978335212 | APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND | No packages found | | 0x8A150015 | -1978335211 | APPINSTALLER_CLI_ERROR_NO_SOURCES_DEFINED | No sources are configured | | 0x8A150016 | -1978335210 | APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND | Multiple packages found matching the criteria | | 0x8A150017 | -1978335209 | APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND | No manifest found matching the criteria | | 0x8A150018 | -1978335208 | APPINSTALLER_CLI_ERROR_EXTENSION_PUBLIC_FAILED | Failed to get Public folder from source package | | 0x8A150019 | -1978335207 | APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN | Command requires administrator privileges to run | | 0x8A15001A | -1978335206 | APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE | The source location is not secure | | 0x8A15001B | -1978335205 | APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY | The Microsoft Store client is blocked by policy | | 0x8A15001C | -1978335204 | APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY | The Microsoft Store app is blocked by policy | | 0x8A15001D | -1978335203 | APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED | The feature is currently under development. It can be enabled using winget settings. | | 0x8A15001E | -1978335202 | APPINSTALLER_CLI_ERROR_MSSTORE_INSTALL_FAILED | Failed to install the Microsoft Store app | | 0x8A15001F | -1978335201 | APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD | Failed to perform auto complete | | 0x8A150020 | -1978335200 | APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED | Failed to initialize YAML parser | | 0x8A150021 | -1978335199 | APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY | Encountered an invalid YAML key | | 0x8A150022 | -1978335198 | APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY | Encountered a duplicate YAML key | | 0x8A150023 | -1978335197 | APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION | Invalid YAML operation | | 0x8A150024 | -1978335196 | APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED | Failed to build YAML doc | | 0x8A150025 | -1978335195 | APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE | Invalid YAML emitter state | | 0x8A150026 | -1978335194 | APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA | Invalid YAML data | | 0x8A150027 | -1978335193 | APPINSTALLER_CLI_ERROR_LIBYAML_ERROR | LibYAML error | | 0x8A150028 | -1978335192 | APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_WARNING | Manifest validation succeeded with warning | | 0x8A150029 | -1978335191 | APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_FAILURE | Manifest validation failed | | 0x8A15002A | -1978335190 | APPINSTALLER_CLI_ERROR_INVALID_MANIFEST | Manifest is invalid | | 0x8A15002B | -1978335189 | APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE | No applicable update found | | 0x8A15002C | -1978335188 | APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE | winget upgrade --all completed with failures | | 0x8A15002D | -1978335187 | APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED | Installer failed security check | | 0x8A15002E | -1978335186 | APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH | Download size does not match expected content length | | 0x8A15002F | -1978335185 | APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND | Uninstall command not found | | 0x8A150030 | -1978335184 | APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED | Running uninstall command failed | | 0x8A150031 | -1978335183 | APPINSTALLER_CLI_ERROR_ICU_BREAK_ITERATOR_ERROR | ICU break iterator error | | 0x8A150032 | -1978335182 | APPINSTALLER_CLI_ERROR_ICU_CASEMAP_ERROR | ICU casemap error | | 0x8A150033 | -1978335181 | APPINSTALLER_CLI_ERROR_ICU_REGEX_ERROR | ICU regex error | | 0x8A150034 | -1978335180 | APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED | Failed to install one or more imported packages | | 0x8A150035 | -1978335179 | APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND | Could not find one or more requested packages | | 0x8A150036 | -1978335178 | APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE | Json file is invalid | | 0x8A150037 | -1978335177 | APPINSTALLER_CLI_ERROR_SOURCE_NOT_REMOTE | The source location is not remote | | 0x8A150038 | -1978335176 | APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE | The configured rest source is not supported | | 0x8A150039 | -1978335175 | APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA | Invalid data returned by rest source | | 0x8A15003A | -1978335174 | APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY | Operation is blocked by Group Policy | | 0x8A15003B | -1978335173 | APPINSTALLER_CLI_ERROR_RESTAPI_INTERNAL_ERROR | Rest API internal error | | 0x8A15003C | -1978335172 | APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL | Invalid rest source url | | 0x8A15003D | -1978335171 | APPINSTALLER_CLI_ERROR_RESTAPI_UNSUPPORTED_MIME_TYPE | Unsupported MIME type returned by rest API | | 0x8A15003E | -1978335170 | APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION | Invalid rest source contract version | | 0x8A15003F | -1978335169 | APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE | The source data is corrupted or tampered | | 0x8A150040 | -1978335168 | APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE | Error reading from the stream | | 0x8A150041 | -1978335167 | APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED | Package agreements were not agreed to | | 0x8A150042 | -1978335166 | APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR | Error reading input in prompt | | 0x8A150043 | -1978335165 | APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST | The search request is not supported by one or more sources | | 0x8A150044 | -1978335164 | APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND | The rest API endpoint is not found. | | 0x8A150045 | -1978335163 | APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED | Failed to open the source. | | 0x8A150046 | -1978335162 | APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED | Source agreements were not agreed to | | 0x8A150047 | -1978335161 | APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH | Header size exceeds the allowable limit of 1024 characters. Please reduce the size and try again. | | 0x8A150048 | -1978335160 | APPINSTALLER_CLI_ERROR_MISSING_RESOURCE_FILE | Missing resource file | | 0x8A150049 | -1978335159 | APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED | Running MSI install failed | | 0x8A15004A | -1978335158 | APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT | Arguments for msiexec are invalid | | 0x8A15004B | -1978335157 | APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES | Failed to open one or more sources | | 0x8A15004C | -1978335156 | APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED | Failed to validate dependencies | | 0x8A15004D | -1978335155 | APPINSTALLER_CLI_ERROR_MISSING_PACKAGE | One or more package is missing | | 0x8A15004E | -1978335154 | APPINSTALLER_CLI_ERROR_INVALID_TABLE_COLUMN | Invalid table column | | 0x8A15004F | -1978335153 | APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER | The upgrade version is not newer than the installed version | | 0x8A150050 | -1978335152 | APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN | Upgrade version is unknown and override is not specified | | 0x8A150051 | -1978335151 | APPINSTALLER_CLI_ERROR_ICU_CONVERSION_ERROR | ICU conversion error | | 0x8A150052 | -1978335150 | APPINSTALLER_CLI_ERROR_PORTABLE_INSTALL_FAILED | Failed to install portable package | | 0x8A150053 | -1978335149 | APPINSTALLER_CLI_ERROR_PORTABLE_REPARSE_POINT_NOT_SUPPORTED | Volume does not support reparse points. | | 0x8A150054 | -1978335148 | APPINSTALLER_CLI_ERROR_PORTABLE_PACKAGE_ALREADY_EXISTS | Portable package from a different source already exists. | | 0x8A150055 | -1978335147 | APPINSTALLER_CLI_ERROR_PORTABLE_SYMLINK_PATH_IS_DIRECTORY | Unable to create symlink, path points to a directory. | | 0x8A150056 | -1978335146 | APPINSTALLER_CLI_ERROR_INSTALLER_PROHIBITS_ELEVATION | The installer cannot be run from an administrator context. | | 0x8A150057 | -1978335145 | APPINSTALLER_CLI_ERROR_PORTABLE_UNINSTALL_FAILED | Failed to uninstall portable package | | 0x8A150058 | -1978335144 | APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED | Failed to validate DisplayVersion values against index. | | 0x8A150059 | -1978335143 | APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT | One or more arguments are not supported. | | 0x8A15005A | -1978335142 | APPINSTALLER_CLI_ERROR_BIND_WITH_EMBEDDED_NULL | Embedded null characters are disallowed for SQLite | | 0x8A15005B | -1978335141 | APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND | Failed to find the nested installer in the archive. | | 0x8A15005C | -1978335140 | APPINSTALLER_CLI_ERROR_EXTRACT_ARCHIVE_FAILED | Failed to extract archive. | | 0x8A15005D | -1978335139 | APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_INVALID_PATH | Invalid relative file path to nested installer provided. | | 0x8A15005E | -1978335138 | APPINSTALLER_CLI_ERROR_PINNED_CERTIFICATE_MISMATCH | The server certificate did not match any of the expected values. | | 0x8A15005F | -1978335137 | APPINSTALLER_CLI_ERROR_INSTALL_LOCATION_REQUIRED | Install location must be provided. | | 0x8A150060 | -1978335136 | APPINSTALLER_CLI_ERROR_ARCHIVE_SCAN_FAILED | Archive malware scan failed. | | 0x8A150061 | -1978335135 | APPINSTALLER_CLI_ERROR_PACKAGE_ALREADY_INSTALLED | Found at least one version of the package installed. | | 0x8A150062 | -1978335134 | APPINSTALLER_CLI_ERROR_PIN_ALREADY_EXISTS | A pin already exists for the package. | | 0x8A150063 | -1978335133 | APPINSTALLER_CLI_ERROR_PIN_DOES_NOT_EXIST | There is no pin for the package. | | 0x8A150064 | -1978335132 | APPINSTALLER_CLI_ERROR_CANNOT_OPEN_PINNING_INDEX | Unable to open the pin database. | | 0x8A150065 | -1978335131 | APPINSTALLER_CLI_ERROR_MULTIPLE_INSTALL_FAILED | One or more applications failed to install | | 0x8A150066 | -1978335130 | APPINSTALLER_CLI_ERROR_MULTIPLE_UNINSTALL_FAILED | One or more applications failed to uninstall | | 0x8A150067 | -1978335129 | APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE | One or more queries did not return exactly one match | | 0x8A150068 | -1978335128 | APPINSTALLER_CLI_ERROR_PACKAGE_IS_PINNED | The package has a pin that prevents upgrade. | | 0x8A150069 | -1978335127 | APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB | The package currently installed is the stub package | | 0x8A15006A | -1978335126 | APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED | Application shutdown signal received | | 0x8A15006B | -1978335125 | APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES | Failed to download package dependencies. | | 0x8A15006C | -1978335124 | APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED | Failed to download package. Download for offline installation is prohibited. | | 0x8A15006D | -1978335123 | APPINSTALLER_CLI_ERROR_SERVICE_UNAVAILABLE | A required service is busy or unavailable. Try again later. | | 0x8A15006E | -1978335122 | APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND | The guid provided does not correspond to a valid resume state. | | 0x8A15006F | -1978335121 | APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH | The current client version did not match the client version of the saved state. | | 0x8A150070 | -1978335120 | APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE | The resume state data is invalid. | | 0x8A150071 | -1978335119 | APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX | Unable to open the checkpoint database. | | 0x8A150072 | -1978335118 | APPINSTALLER_CLI_ERROR_RESUME_LIMIT_EXCEEDED | Exceeded max resume limit. | | 0x8A150073 | -1978335117 | APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO | Invalid authentication info. | | 0x8A150074 | -1978335116 | APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED | Authentication method not supported. | | 0x8A150075 | -1978335115 | APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED | Authentication failed. | | 0x8A150076 | -1978335114 | APPINSTALLER_CLI_ERROR_AUTHENTICATION_INTERACTIVE_REQUIRED | Authentication failed. Interactive authentication required. | | 0x8A150077 | -1978335113 | APPINSTALLER_CLI_ERROR_AUTHENTICATION_CANCELLED_BY_USER | Authentication failed. User cancelled. | | 0x8A150078 | -1978335112 | APPINSTALLER_CLI_ERROR_AUTHENTICATION_INCORRECT_ACCOUNT | Authentication failed. Authenticated account is not the desired account. | | 0x8A150079 | -1978335111 | APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND | Repair command not found. | | 0x8A15007A | -1978335110 | APPINSTALLER_CLI_ERROR_REPAIR_NOT_APPLICABLE | Repair operation is not applicable. | | 0x8A15007B | -1978335109 | APPINSTALLER_CLI_ERROR_EXEC_REPAIR_FAILED | Repair operation failed. | | 0x8A15007C | -1978335108 | APPINSTALLER_CLI_ERROR_REPAIR_NOT_SUPPORTED | The installer technology in use doesn't support repair. | | 0x8A15007D | -1978335107 | APPINSTALLER_CLI_ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED | Repair operations involving administrator privileges are not permitted on packages installed within the user scope. | | 0x8A15007E | -1978335106 | APPINSTALLER_CLI_ERROR_SQLITE_CONNECTION_TERMINATED | The SQLite connection was terminated to prevent corruption. | | 0x8A15007F | -1978335105 | APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED | Failed to get Microsoft Store package catalog. | | 0x8A150080 | -1978335104 | APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE | No applicable Microsoft Store package found from Microsoft Store package catalog. | | 0x8A150081 | -1978335103 | APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED | Failed to get Microsoft Store package download information. | | 0x8A150082 | -1978335102 | APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE | No applicable Microsoft Store package download information found. | | 0x8A150083 | -1978335101 | APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED | Failed to retrieve Microsoft Store package license. | | 0x8A150084 | -1978335100 | APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED | The Microsoft Store package does not support download command. | | 0x8A150085 | -1978335099 | APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN | Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account does not have required privilege. | | 0x8A150086 | -1978335098 | APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE | Downloaded zero byte installer; ensure that your network connection is working properly. | | 0x8A150087 | -1979335097 | APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED | Failed installing one or more fonts. | | 0x8A150088 | -1979335096 | APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_SUPPORTED | Font file is not supported and cannot be installed. | | 0x8A150089 | -1979335095 | APPINSTALLER_CLI_ERROR_FONT_ALREADY_INSTALLED | Font package is already installed. | | 0x8A15008A | -1979335094 | APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_FOUND | Font file not found. | | 0x8A15008B | -1979335093 | APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED | Font uninstall failed. The font may not be in a good state. Try uninstalling after a restart. | | 0x8A15008C | -1979335092 | APPINSTALLER_CLI_ERROR_FONT_VALIDATION_FAILED | Font validation failed. | | 0x8A15008D | -1979335091 | APPINSTALLER_CLI_ERROR_FONT_ROLLBACK_FAILED | Font rollback failed. The font may not be in a good state. Try uninstalling after a restart. | ## Install errors. | Hex | Decimal | Symbol | Description | |-------------|-------------|-------------|-------------| | 0x8A150101 | -1978334975 | APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE | Application is currently running. Exit the application then try again. | | 0x8A150102 | -1978334974 | APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS | Another installation is already in progress. Try again later. | | 0x8A150103 | -1978334973 | APPINSTALLER_CLI_ERROR_INSTALL_FILE_IN_USE | One or more file is being used. Exit the application then try again. | | 0x8A150104 | -1978334972 | APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY | This package has a dependency missing from your system. | | 0x8A150105 | -1978334971 | APPINSTALLER_CLI_ERROR_INSTALL_DISK_FULL | There's no more space on your PC. Make space, then try again. | | 0x8A150106 | -1978334970 | APPINSTALLER_CLI_ERROR_INSTALL_INSUFFICIENT_MEMORY | There's not enough memory available to install. Close other applications then try again. | | 0x8A150107 | -1978334969 | APPINSTALLER_CLI_ERROR_INSTALL_NO_NETWORK | This application requires internet connectivity. Connect to a network then try again. | | 0x8A150108 | -1978334968 | APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT | This application encountered an error during installation. Contact support. | | 0x8A150109 | -1978334967 | APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH | Restart your PC to finish installation. | | 0x8A15010A | -1978334966 | APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_INSTALL | Installation failed. Restart your PC then try again. | | 0x8A15010B | -1978334965 | APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_INITIATED | Your PC will restart to finish installation. | | 0x8A15010C | -1978334964 | APPINSTALLER_CLI_ERROR_INSTALL_CANCELLED_BY_USER | You cancelled the installation. | | 0x8A15010D | -1978334963 | APPINSTALLER_CLI_ERROR_INSTALL_ALREADY_INSTALLED | Another version of this application is already installed. | | 0x8A15010E | -1978334962 | APPINSTALLER_CLI_ERROR_INSTALL_DOWNGRADE | A higher version of this application is already installed. | | 0x8A15010F | -1978334961 | APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY | Organization policies are preventing installation. Contact your admin. | | 0x8A150110 | -1978334960 | APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES | Failed to install package dependencies. | | 0x8A150111 | -1978334959 | APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE_BY_APPLICATION | Application is currently in use by another application. | | 0x8A150112 | -1978334958 | APPINSTALLER_CLI_ERROR_INSTALL_INVALID_PARAMETER | Invalid parameter. | | 0x8A150113 | -1978334957 | APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED | Package not supported by the system. | | 0x8A150114 | -1978334956 | APPINSTALLER_CLI_ERROR_INSTALL_UPGRADE_NOT_SUPPORTED | The installer does not support upgrading an existing package. | | 0x8A150115 | -1978334955 | APPINSTALLER_CLI_ERROR_INSTALL_CUSTOM_ERROR | Installation failed with installer custom error. | ## Check for package installed status | Hex | Decimal | Symbol | Description | |-------------|-------------|-------------|-------------| | 0x8A150201 | -1978334719 | WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND | The Apps and Features Entry for the package could not be found. | | 0x8A150202 | -1978334718 | WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE | The install location is not applicable. | | 0x8A150203 | -1978334717 | WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND | The install location could not be found. | | 0x8A150204 | -1978334716 | WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH | The hash of the existing file did not match. | | 0x8A150205 | -1978334715 | WINGET_INSTALLED_STATUS_FILE_NOT_FOUND | File not found. | | 0x8A150206 | -1978334714 | WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK | The file was found but the hash was not checked. | | 0x8A150207 | -1978334713 | WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR | The file could not be accessed. | ## Configuration Errors | Hex | Decimal | Symbol | Description | |-------------|-------------|-------------|-------------| | 0x8A15C001 | -1978286079 | WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE | The configuration file is invalid. | | 0x8A15C002 | -1978286078 | WINGET_CONFIG_ERROR_INVALID_YAML | The YAML syntax is invalid. | | 0x8A15C003 | -1978286077 | WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE | A configuration field has an invalid type. | | 0x8A15C004 | -1978286076 | WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION | The configuration has an unknown version. | | 0x8A15C005 | -1978286075 | WINGET_CONFIG_ERROR_SET_APPLY_FAILED | An error occurred while applying the configuration. | | 0x8A15C006 | -1978286074 | WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER | The configuration contains a duplicate identifier. | | 0x8A15C007 | -1978286073 | WINGET_CONFIG_ERROR_MISSING_DEPENDENCY | The configuration is missing a dependency. | | 0x8A15C008 | -1978286072 | WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED | The configuration has an unsatisfied dependency. | | 0x8A15C009 | -1978286071 | WINGET_CONFIG_ERROR_ASSERTION_FAILED | An assertion for the configuration unit failed. | | 0x8A15C00A | -1978286070 | WINGET_CONFIG_ERROR_MANUALLY_SKIPPED | The configuration was manually skipped. | | 0x8A15C00B | -1978286069 | WINGET_CONFIG_ERROR_WARNING_NOT_ACCEPTED | A warning was thrown and the user declined to continue execution. | | 0x8A15C00C | -1978286068 | WINGET_CONFIG_ERROR_SET_DEPENDENCY_CYCLE | The dependency graph contains a cycle which cannot be resolved. | | 0x8A15C00D | -1978286067 | WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE | The configuration has an invalid field value. | | 0x8A15C00E | -1978286066 | WINGET_CONFIG_ERROR_MISSING_FIELD | The configuration is missing a field. | | 0x8A15C00F | -1978286065 | WINGET_CONFIG_ERROR_TEST_FAILED | Some of the configuration units failed while testing their state. | | 0x8A15C010 | -1978286064 | WINGET_CONFIG_ERROR_TEST_NOT_RUN | Configuration state was not tested. | | 0x8A15C011 | -1978286063 | WINGET_CONFIG_ERROR_GET_FAILED | The configuration unit failed getting its properties. | | 0x8A15C012 | -1978286062 | WINGET_CONFIG_ERROR_HISTORY_ITEM_NOT_FOUND | The specified configuration could not be found. | | 0x8A15C013 | -1978286061 | WINGET_CONFIG_ERROR_PARAMETER_INTEGRITY_BOUNDARY | Parameter cannot be passed across integrity boundary. | ## Configuration Processor Errors | Hex | Decimal | Symbol | Description | |-------------|-------------|-------------|-------------| | 0x8A15C101 | -1978285823 | WINGET_CONFIG_ERROR_UNIT_NOT_INSTALLED | The configuration unit was not installed. | | 0x8A15C102 | -1978285822 | WINGET_CONFIG_ERROR_UNIT_NOT_FOUND_REPOSITORY | The configuration unit could not be found. | | 0x8A15C103 | -1978285821 | WINGET_CONFIG_ERROR_UNIT_MULTIPLE_MATCHES | Multiple matches were found for the configuration unit specify the module to select the correct one. | | 0x8A15C104 | -1978285820 | WINGET_CONFIG_ERROR_UNIT_INVOKE_GET | The configuration unit failed while attempting to get the current system state. | | 0x8A15C105 | -1978285819 | WINGET_CONFIG_ERROR_UNIT_INVOKE_TEST | The configuration unit failed while attempting to test the current system state. | | 0x8A15C106 | -1978285818 | WINGET_CONFIG_ERROR_UNIT_INVOKE_SET | The configuration unit failed while attempting to apply the desired state. | | 0x8A15C107 | -1978285817 | WINGET_CONFIG_ERROR_UNIT_MODULE_CONFLICT | The module for the configuration unit is available in multiple locations with the same version. | | 0x8A15C108 | -1978285816 | WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE | Loading the module for the configuration unit failed. | | 0x8A15C109 | -1978285815 | WINGET_CONFIG_ERROR_UNIT_INVOKE_INVALID_RESULT | The configuration unit returned an unexpected result during execution. | | 0x8A15C110 | -1978285814 | WINGET_CONFIG_ERROR_UNIT_SETTING_CONFIG_ROOT | A unit contains a setting that requires the config root. | | 0x8A15C111 | -1978285813 | WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE_ADMIN | Loading the module for the configuration unit failed because it requires administrator privileges to run. | | 0x8A15C112 | -1978285812 | WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR | Operation is not supported by the configuration processor. | ================================================ FILE: doc/windows/package-manager/winget/search.md ================================================ --- title: search Command description: Queries the sources for available applications that can be installed ms.date: 04/28/2020 ms.topic: overview ms.localizationpriority: medium --- # search command (winget) The **search** command of the [winget](index.md) tool queries the sources for available applications that can be installed. The **search** command can show all applications available, or it can be filtered down to a specific application. The **search** command is used typically to identify the string to use to install a specific application. ## Usage `winget search [[-q] ] []` The following command aliases are available: \ `find` ![Screenshot of the Windows Power Shell window displaying the results of the winget search.](images/search.png) ## Arguments The following arguments are available. | Argument | Description | --------------|-------------| | **-q, --query** | The query used to search for an app. | ## Show all If the search command includes no filters or options, it will display all available applications in the default source. You can also search for all applications in another source if you pass in just the **source** option. ## Search strings Search strings can be filtered with the following options. | Option | Description | --------------|-------------| | **--id** | Limits the search to the ID of the application. The ID includes the publisher and the application name. | | **--name** | Limits the search to the name of the application. | | **--moniker** | Limits the search to the moniker specified. | | **--tag** | Limits the search to the tags listed for the application. | | **--cmd, --command** | Limits the search to the commands listed for the application. | | **-s, --source** | Find package using the specified source. | | **-n, --count** | Show no more than specified number of results (between 1 and 1000). | | **-e, --exact** | Find package using exact match. | | **--header** | Optional Windows-Package-Manager REST source HTTP header. | | **--accept-source-agreements** | Accept all source agreements during source operations. | | **--versions** | Show available versions of the package. | | **-?, --help** | Gets additional help on this command. | | **--wait** | Prompts the user to press any key before exiting. | | **--logs, --open-logs** | Open the default logs location. | | **--verbose, --verbose-logs** | Enables verbose logging for winget. | | **--disable-interactivity** | Disable interactive prompts. | The string will be treated as a substring. The search by default is also case-insensitive. For example, `winget search micro` could return the following: * Microsoft * Microscope * MyMicro ## Search options The search commands supports a number of options or filters to help limit the results. | Option | Description | --------------|-------------| | **-e, --exact** | Uses the exact string in the query, including checking for case-sensitivity. It will not use the default behavior of a substring. | | **-n, --count** | Restricts the output of the display to the specified count. | | **-s, --source** | Restricts the search to the specified [source](source.md) name. | ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/settings.md ================================================ --- title: winget settings command description: Provides customizations for the Windows Package Manager. ms.date: 05/05/2021 ms.topic: article ms.localizationpriority: medium --- # settings command (winget) The **settings** command of the [winget](index.md) tool allows you to customize your Windows Package Manager client experience. You can change defaults and try out experimental features that are enabled in your client. The **settings** command will launch your default JSON editor. Windows by default will launch Notepad as an option. We recommend using a tool like [Visual Studio code](https://code.visualstudio.com/). > You can easily install Visual Studio Code, by typing `winget install Microsoft.VisualStudioCode` ## Usage Launch your default JSON editing tool: `winget settings` ![Screenshot of the Windows Package Manager Settings.](images/settings.png) When you launch the settings for the first time, there will be no settings specified. At the top of the JSON we provide a link to [https://aka.ms/winget-settings](https://aka.ms/winget-settings) where you can discover the latest experimental features and settings. We have also defined a schema for the settings file. This allows you to use TAB to discover settings and syntax if your JSON editor supports JSON schemas. ## Updating Settings The following settings are available for the 1.0 release of the Windows Package Manager. ### Source The `source` settings involve configuration to the WinGet source. ```json "source": { "autoUpdateIntervalInMinutes": 3 }, ``` #### autoUpdateIntervalInMinutes A positive integer represents the update interval in minutes. The check for updates only happens when a source is used. A zero will disable the check for updates to a source. Any other values are invalid. - Disable: 0 - Default: 5 To manually update the source use `winget source update` ### Visual The `visual` settings involve visual elements that are displayed by WinGet ```json "visual": { "progressBar": "accent" }, ``` #### progressBar Color of the progress bar that WinGet displays when not specified by arguments. - accent (default) - retro - rainbow ### Install Behavior The `installBehavior` settings affect the default behavior of installing and upgrading (where applicable) packages. #### Preferences and Requirements Some of the settings are duplicated under `preferences` and `requirements`. `preferences` affect how the various available options are sorted when choosing the one to act on. For instance, the default scope of package installs is for the current user, but if that is not an option then a machine level installer will be chosen. `requirements` filter the options, potentially resulting in an empty list and a failure to install. In the previous example, a user scope requirement would result in no applicable installers and an error. Any arguments passed on the command line will effectively override the matching `requirement` setting for the duration of that command. #### Scope The `scope` behavior affects the choice between installing a package for the current user or for the entire machine. The matching parameter is `--scope`, and uses the same values (`user` or `machine`). ```json "installBehavior": { "preferences": { "scope": "user" } }, ``` #### Locale The `locale` behavior affects the choice of installer based on installer locale. The matching parameter is `--locale`, and uses bcp47 language tag. ```json "installBehavior": { "preferences": { "locale": [ "en-US", "fr-FR" ] } }, ``` ### Telemetry The `telemetry` settings control whether winget writes ETW events that may be sent to Microsoft on a default installation of Windows. See [details on telemetry](https://github.com/microsoft/winget-cli/blob/master/README.md#datatelemetry), and our [primary privacy statement](https://github.com/microsoft/winget-cli/blob/master/PRIVACY.md). #### disable ```json "telemetry": { "disable": true }, ``` If set to true, the `telemetry.disable` setting will prevent any event from being written by the program. ### Network The `network` settings influence how winget uses the network to retrieve packages and metadata. #### Downloader The `downloader` setting controls which code is used when downloading packages. The default is `default`, which may be any of the options based on our determination. `wininet` uses the [WinINet](https://docs.microsoft.com/windows/win32/wininet/about-wininet) APIs, while `do` uses the [Delivery Optimization](https://support.microsoft.com/windows/delivery-optimization-in-windows-10-0656e53c-15f2-90de-a87a-a2172c94cf6d) service. ```json "network": { "downloader": "do" } ``` ## Enabling Experimental features To discover which experimental features are available, go to [https://aka.ms/winget-settings](https://aka.ms/winget-settings) where you can see the experimental features available to you. ================================================ FILE: doc/windows/package-manager/winget/show.md ================================================ --- title: show Command description: Displays details for the specified application, including details on the source of the application as well as the metadata associated with the application. ms.date: 04/28/2020 ms.topic: overview ms.localizationpriority: medium --- # show command (winget) The **show** command of the [winget](index.md) tool displays details for the specified application, including details on the source of the application as well as the metadata associated with the application. The **show** command only shows metadata that was submitted with the application. If the submitted application excludes some metadata, then the data will not be displayed. ## Usage `winget show [[-q] ] []` The following command aliases are available: \ `view` ![show command](images/show.png) ## Arguments The following arguments are available. | Argument | Description | |--------------|-------------| | **-q, --query** | The query used to search for an application. | ## Options The following options are available. | Option | Description | |--------------|-------------| | **-m,--manifest** | The path to the manifest of the application to install. | | **--id** | Filter results by ID. | | **--name** | Filter results by name. | | **--moniker** | Filter results by application moniker. | | **-v,--version** | Use the specified version. The default is the latest version. | | **-s,--source** | Find the application using the specified [source](source.md). | | **-e,--exact** | Find the application using exact match. | | **--versions** | Show available versions of the application. | | **--scope** | Select install scope (user or machine). | | **-a, --architecture** | Select the architecture. | | **--installer-type** | Select the installer type. | | **--locale** | Locale to use (BCP47 format). | | **--header** | Optional Windows-Package-Manager REST source HTTP header. | | **--accept-source-agreements** | Accept all source agreements during source operations. | | **-?, --help** | Gets additional help on this command. | | **--wait** | Prompts the user to press any key before exiting. | | **--logs, --open-logs** | Open the default logs location. | | **--verbose, --verbose-logs** | Enables verbose logging for winget. | | **--disable-interactivity** | Disable interactive prompts. | ## Multiple selections If the query provided to **winget** does not result in a single application, then **winget** will display the results of the search. This will provide you with the additional data necessary to refine the search. ## Results of show If a single application is detected, the following data will be displayed. ### Metadata | Value | Description | |--------------|-------------| | **Version** | Version of the application. | | **Publisher** | Publisher of the application. | | **Moniker** | AppMoniker of the application. | | **Description** | Description of the application. | | **Homepage** | Homepage of the application. | | **License** | License of the application. | | **LicenseUrl** | The URL to the license file of the application. | ### Installer details | Value | Description | |--------------|-------------| | **Type** | The type of installer. | | **Download Url** | The Url of the installer. | | **SHA256** | The Sha-256 of the installer. | ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/source.md ================================================ --- title: source Command description: Manages the repositories accessed by Windows Package Manager. ms.date: 04/28/2020 ms.topic: overview ms.localizationpriority: medium --- # source command (winget) > The **source** command is currently for internal use only. Additional sources are not supported at this time. The **source** command of the [winget](index.md) tool manages the repositories accessed by Windows Package Manager. With the **source** command you can **add**, **remove**, **list**, and **update** the repositories. A source provides the data for you to discover and install applications. Only add a new source if you trust it as a secure location. ## Usage `winget source [] []` ![Source image](images/source.png) ## Arguments The following arguments are available. | Argument | Description | |--------------|-------------| | **-?, --help** | Gets additional help on this command. | ## Sub-commands Source supports the following sub-commands for manipulating the sources. | Sub-command | Description | |--------------|-------------| | **add** | Adds a new source. | | **list** | Enumerates the list of enabled sources. | | **update** | Updates a source. | | **remove** | Removes a source. | | **reset** | Resets **winget** back to the initial configuration. | | **export** | Export current sources | For more details on a specific command, pass it the help argument. [-?] ## Options The **source** command supports the following options. | Option | Description | |--------------|-------------| | **-n, --name** | The name to identify the source by. | | **-a, --arg** | The URL or UNC of the source. | | **-t, --type** | The type of source. | | **-?, --help** | Gets additional help on this command. | | **--wait** | Prompts the user to press any key before exiting. | | **--logs, --open-logs** | Open the default logs location. | | **--verbose, --verbose-logs** | Enables verbose logging for winget. | | **--disable-interactivity** | Disable interactive prompts. | ## add The **add** sub-command adds a new source. This sub-command requires the **--name** option and the **name** argument. Usage: `winget source add [-n, --name] [-a] [[-t] ]` Example: `winget source add --name Contoso https://www.contoso.com/cache` The **add** sub-command also supports the optional **type** parameter. The **type** parameter communicates to the client what type of repository it is connecting to. The following types are supported. | Type | Description | |--------------|-------------| | **Microsoft.PreIndexed.Package** | The type of source \. | | **Microsoft.Rest** | A Microsoft REST API source. | ## list the **list** sub-command enumerates the currently enabled sources. This sub-command also provides details on a specific source. Usage: `winget source list [-n, --name] ` ### list all The **list** sub-command by itself will reveal the complete list of supported sources. For example: ![Source list image](images/source-list.png) ### list source details In order to get complete details on the source, pass in the name used to identify the source. For example: ![Source list winget image](images/source-list-winget.png) **Name** displays the name to identify the source by. **Type** displays the type of repo. **Arg** displays the URL or path used by the source. **Data** displays the optional package name used if appropriate. **Updated** displays the last date and time the source was updated. ## update The **update** sub-command forces an update to an individual source or for all. usage: `winget source update [-n, --name] ` ### update all The **update** sub-command by itself will request and update to each repo. For example: `C:\winget update` ### update source The **update** sub-command combined with the **--name** option can direct and update to an individual source. For example: `C:\winget source update --name winget` ## remove The **remove** sub-command removes a source. This sub-command requires the **--name** option and **name argument** in order to identify the source. Usage: `winget source remove [-n, --name] ` For example: `winget source remove --name Contoso` ## reset The **reset** sub-command resets the client back to its original configuration. The **reset** sub-command removes all sources and sets the source to the default. This sub-command should only be used in rare cases. Usage: `winget source reset` ## Default repository Windows Package Manager specifies a default repository. You can identify the repository by using the **list** command. For example: `winget source list` ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/uninstall.md ================================================ --- title: uninstall Command description: uninstalls the specified application. ms.date: 05/05/2021 ms.topic: overview ms.localizationpriority: medium --- # uninstall command (winget) The **uninstall** command of the [winget](index.md) tool uninstalls the specified application. The **uninstall** command requires that you specify the exact string to uninstall. If there is any ambiguity, you will be prompted to further filter the **uninstall** command to an exact application. ## Usage `winget uninstall [[-q] ...] []` The following command aliases are available: \ `remove` \ `rm` ![uninstall command](images/uninstall.png) ## Arguments The following arguments are available. | Argument | Description | |-------------|-------------| | **-q,--query** | The query used to search for an app. | ## Options The options allow you to customize the uninstall experience to meet your needs. | Option | Description | |--------|-------------| | **-m, --manifest** | Must be followed by the path to the manifest (YAML) file. You can use the manifest to run the uninstall experience from a [local YAML file](#local-uninstall). | | **--id** | Limits the uninstall to the ID of the application. | | **--name** | Limits the search to the name of the application. | | **--moniker** | Limits the search to the moniker listed for the application. | | **-v, --version** | Enables you to specify an exact version to uninstall. If not specified, the latest will uninstall the highest versioned application. | | **-s, --source** | Restricts the search to the source name provided. Must be followed by the source name. | | **-e, --exact** | Uses the exact string in the query, including checking for case-sensitivity. It will not use the default behavior of a substring. | | **-i, --interactive** | Runs the uninstaller in interactive mode. The default experience shows uninstaller progress. | | **-h, --silent** | Runs the uninstaller in silent mode. This suppresses all UI. The default experience shows uninstaller progress. | | **-o, --log** | Directs the logging to a log file. You must provide a path to a file that you have the write rights to. | | **--product-code** | Filters using the product code. | | **--scope** | Select installed package scope filter (user or machine). | | **--force** | Directly run the command and continue with non-security-related issues. | | **--purge** | Deletes all files and directories in the package directory (portable). | | **--preserve** | Retains all files and directories created by the package (portable). | | **--header** | Optional Windows-Package-Manager REST source HTTP header. | | **--accept-source-agreements** | Accept all source agreements during source operations. | | **-?, --help** | Get additional help on this command. | | **--wait** | Prompts the user to press any key before exiting. | | **--logs, --open-logs** | Open the default logs location. | | **--verbose, --verbose-logs** | Enables verbose logging for winget. | | **--disable-interactivity** | Disable interactive prompts. | Once you have successfully identified the application intended to uninstall, winget will execute the uninstall command. In the example below, the **name** 'orca' and the **id** was passed in. ![uninstall command](images/uninstall-execute.png) ### Example queries The following example uninstalls a specific version of an application. ```CMD winget uninstall --name powertoys --version 0.15.2 ``` The following example uninstalls an application using its ID. ```CMD winget uninstall --id "{24559D0F-481C-F3BE-8DD0-D908923A38F8}" ``` ## Multiple selections If the query provided to **winget** does not result in a single application to uninstall, then **winget** will display multiple results. You can then use additional filters to refine the search for a correct application. ![uninstall command](images/uninstall-multiple.png) ## Uninstalling apps not installed with Windows Package Manager As mentioned in [**list**](list.md), the **winget list** command will display more than just apps installed with the **winget**. Therefore you can use these commands to quickly and easily remove apps from your PC. In this example, **list** was used to find the application, and then the **id** was passed in as part of uninstall. ![uninstall with list command](images/uninstall-with-list.png) ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/upgrade.md ================================================ --- title: upgrade Command description: upgrades the specified application. ms.date: 05/05/2021 ms.topic: overview ms.localizationpriority: medium --- # upgrade command (winget) The **upgrade** command of the [winget](index.md) tool upgrades the specified application. Optionally, you may not specify an application, this will list all available upgrades instead. The **upgrade** command requires that you specify the exact string to upgrade. If there is any ambiguity, you will be prompted to further filter the **upgrade** command to an exact application. ## Usage `winget upgrade [[-q] ...] []` The following command aliases are available: \ `update` ![upgrade command](images/upgrade.png) ## Options The options allow you to customize the upgrade experience to meet your needs. | Option | Description | |-----------------------------------|------------------------------------------------------------------------------------------------------------------------------| | **-m, --manifest** | Must be followed by the path to the manifest (YAML) file. You can use the manifest to run the upgrade experience from a [local YAML file](#local-upgrade). | | **--id** | Limits the upgrade to the ID of the application. | | **--name** | Limits the search to the name of the application. | | **--moniker** | Limits the search to the moniker listed for the application. | | **-v, --version** | Enables you to specify an exact version to upgrade. If not specified, the latest will upgrade the highest versioned application. | | **-s, --source** | Restricts the search to the source name provided. Must be followed by the source name. | | **-e, --exact** | Uses the exact string in the query, including checking for case-sensitivity. It will not use the default behavior of a substring. | | **-i, --interactive** | Runs the installer in interactive mode. The default experience shows installer progress. | | **-h, --silent** | Runs the installer in silent mode. This suppresses all UI. The default experience shows installer progress. | | **-o, --log** | Directs the logging to a log file. You must provide a path to a file that you have the write rights to. | | **--override** | A string that will be passed directly to the installer. | | **-l, --location** | Location to upgrade to (if supported). | | **--force** | When a hash mismatch is discovered will ignore the error and attempt to install the package. | | **--all** | Updates all available packages to the latest application. | | **--include-unknown** | Attempt to upgrade a package even if the package's current version is unknown. | | **--purge** | Deletes all files and directories in the package directory (portable). | | **--custom** | Arguments to be passed on to the installer in addition to the defaults. | | **--scope** | Select installed package scope filter (user or machine). | | **-a, --architecture** | Select the architecture to install. | | **--locale** | Locale to use (BCP47 format). | | **--ignore-security-hash** | Ignore the installer hash check failure. | | **--ignore-local-archive-malware-scan** | Ignore the malware scan performed as part of installing an archive-type package from a local manifest. | | **--accept-package-agreements** | Accept all license agreements for packages. | | **--accept-source-agreements** | Accept all source agreements during source operations. | | **--header** | Optional Windows-Package-Manager REST source HTTP header. | | **-r, --recurse, --all** | Upgrade all installed packages to the latest version if available. | | **--pinned,--include-pinned** | Upgrade packages even if they have a non-blocking pin. | | **--uninstall-previous** | Uninstall the previous version of the package during the upgrade. | | **--wait** | Prompts the user to press any key before exiting. | | **--logs,--open-logs** | Open the default logs location. | | **--verbose,--verbose-logs** | Enable verbose logging for winget. | | **--disable-interactivity** | Disable interactive prompts. | | **--installer-type** | Select the installer type | **--skip-dependencies** | Skips processing package dependencies and Windows features | ### Example queries The following example upgrades a specific version of an application. ```CMD winget upgrade powertoys --version 0.15.2 ``` The following example upgrades an application from its ID. ```CMD winget upgrade --id Microsoft.PowerToys ``` The following example shows upgrading all apps ```CMD winget upgrade --all ``` ## Using **list** and **upgrade** It is common to use the [**list**](list.md) command to identify apps in need of an update, and then to use **upgrade** to install the latest. In the example below you will see [**list**](list.md) identifies that an update is available for **JetBrains.Toolbox**, and then the user uses **upgrade** to update the application. ![upgrade command usage](images/upgrade.gif) ## **upgrade** --all **upgrade --all** will identify all the applications with upgrades available. When you run **winget upgrade --all** the Windows Package Manager will look for all applications that have updates available and attempt to install the upgrade. ## Related topics * [Use the winget tool to install and manage applications](index.md) ================================================ FILE: doc/windows/package-manager/winget/validate.md ================================================ --- title: winget validate Command description: Validates a manifest file for submitting software to the Microsoft Community Package Manifest Repository on GitHub. ms.date: 04/28/2020 ms.topic: article ms.localizationpriority: medium --- # validate command (winget) The **validate** command of the [winget](index.md) tool validates a [manifest](../package/manifest.md) for submitting software to the **Microsoft Community Package Manifest Repository** on GitHub. The manifest must be a YAML file that follows the [specification](https://github.com/microsoft/winget-pkgs/blob/master/doc/manifest/README.md). ## Usage `winget validate [--manifest] []` ## Arguments The following arguments are available. | Argument | Description | |--------------|-------------| | **--manifest** | The path to the manifest to be validated. | ## Options The options allow you to customize the export experience to meet your needs. | Option | Description | |--------|-------------| | **-?, --help** | Get additional help on this command. | | **--wait** | Prompts the user to press any key before exiting. | | **--logs, --open-logs** | Open the default logs location. | | **--verbose, --verbose-logs** | Enables verbose logging for winget. | | **--disable-interactivity** | Disable interactive prompts. | ## Related topics * [Use the winget tool to install and manage applications](index.md) * [Submit packages to Windows Package Manager](../package/index.md) ================================================ FILE: doc/windows-package-manager-release-roadmap.md ================================================ # Windows Package Manager Release Roadmap ## Overview This document outlines our aspirational roadmap to deliver next release of Windows Package Manager. We anticipate substantial feedback from the community, and as such, this plan is subject to change. ## Milestones The Windows Package Manager project is engineered and delivered as described below. ### Activities #### Dev Work * Fixes / Features for Windows Package Manager * Fixes / Features for future Windows Releases #### Quality & Stability * Bug Fixes * Performance & Stability * Globalization, Localization, Internationalization, Accessibility * Tests #### Release * Available from to Windows Insiders through the [Microsoft Store](https://apps.microsoft.com/detail/9nblggh4nns1) & [GitHub Releases](https://github.com/microsoft/winget-cli/releases) * Release Notes & Announcement Blog published * Engineering System Maintenance * Community Engagement * [Docs](https://docs.microsoft.com/windows/package-manager/) * Future Milestone Planning ### Releases Releases will be available here on GitHub first. We will release to the App Installer for Insiders as frequently as the releases have met our quality bars. ## GitHub Milestones We use [GitHub Projects](https://github.com/orgs/microsoft/projects/137) to broadly organize what we intend to work on. Issues will be added to milestones when we're reasonably confident they will land in the next milestone. Dates in [GitHub milestones](https://github.com/microsoft/winget-cli/milestones) are only used for sequencing and do not represent a commitment. | Milestone | Description | Status | | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | ------------------ | | [v1.12-Client](https://github.com/microsoft/winget-cli/milestone/47) | Work Targeted for v1.12 | :white_check_mark: | | [v1.28-Client](https://github.com/microsoft/winget-cli/milestone/48) | Work Targeted for v1.28 | :construction: | | [v1.29-Client](https://github.com/microsoft/winget-cli/milestone/49) | Work Targeted for v1.29 | :construction: | | [v2.0-Client](https://github.com/microsoft/winget-cli/milestone/41) | Work Targeted for breaking-change Release | :rocket: | | [Backlog-Client](https://github.com/microsoft/winget-cli/issues?q=is%3Aissue%20state%3Aopen%20no%3Amilestone) | Work not yet assigned to a milestone or release | N/A | Older milestones can be found in the [list of closed milestones](https://github.com/microsoft/winget-cli/milestones?state=closed). ## Issue Triage & Prioritization Incoming issues/asks/etc. are triaged several times a week, labelled appropriately, and assigned to a milestone in priority order: * P0 (serious crashes, data loss, etc.) issues are scheduled to be dealt with ASAP. * P1/2 issues/features/asks assigned to the current or future milestone. * Issues/features/asks not on our list of the features of next release is assigned to the [Windows Package Manager Backlog](https://github.com/microsoft/winget-cli/milestone/2) for subsequent triage, prioritization & scheduling. #### Feature Priorities will be influenced by community feedback on issues. ================================================ FILE: samples/MinimalCallers/C#/Configuration-InProc/C#_Configuration_InProc.csproj ================================================ Exe net8.0-windows10.0.22000.0 Configuration_InProc enable enable x64;x86;ARM64 ================================================ FILE: samples/MinimalCallers/C#/Configuration-InProc/Program.cs ================================================ // Shows the contents of a configuration yml file using Windows.Storage; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor; if (args.Length != 1) { Console.WriteLine("Usage: Configuration-InProc "); return; } var configStatics = new ConfigurationStaticFunctions(); if (!configStatics.IsConfigurationAvailable) { throw new Exception("Configuration is not available"); } // Differs from the OutOfProc version in that we create the factory object directly var factory = new PowerShellConfigurationSetProcessorFactory(); var processor = configStatics.CreateConfigurationProcessor(factory); var file = await StorageFile.GetFileFromPathAsync(args[0]); var fileStream = await file.OpenStreamForReadAsync(); var openResult = processor.OpenConfigurationSet(fileStream.AsInputStream()); var configSet = openResult.Set; Console.WriteLine("Configuration set:"); configSet.Serialize(Console.OpenStandardOutput().AsOutputStream()); ================================================ FILE: samples/MinimalCallers/C#/Configuration-OutOfProc/C#_Configuration_OutOfProc.csproj ================================================ Exe net8.0-windows10.0.22000.0 Configuration_OutOfProc enable enable x64;x86;ARM64 ================================================ FILE: samples/MinimalCallers/C#/Configuration-OutOfProc/Program.cs ================================================ // Shows the contents of a configuration yml file using Windows.Storage; using Microsoft.Management.Configuration; if (args.Length != 1) { Console.WriteLine("Usage: Configuration-InProc "); return; } var configStatics = new ConfigurationStaticFunctions(); if (!configStatics.IsConfigurationAvailable) { throw new Exception("Configuration is not available"); } var factory = await configStatics.CreateConfigurationSetProcessorFactoryAsync("pwsh"); var processor = configStatics.CreateConfigurationProcessor(factory); var file = await StorageFile.GetFileFromPathAsync(args[0]); var fileStream = await file.OpenStreamForReadAsync(); var openResult = processor.OpenConfigurationSet(fileStream.AsInputStream()); var configSet = openResult.Set; Console.WriteLine("Configuration set:"); configSet.Serialize(Console.OpenStandardOutput().AsOutputStream()); ================================================ FILE: samples/MinimalCallers/C#/WinGet-InProc/C#_WinGet_InProc.csproj ================================================ Exe net8.0-windows10.0.22000.0 WinGet_InProc enable enable x64;x86;ARM64 NU1701 true none 10.0.19041.0 Microsoft.Management.Deployment ================================================ FILE: samples/MinimalCallers/C#/WinGet-InProc/Program.cs ================================================ // Lists all installed packages using Microsoft.Management.Deployment; var packageManager = new PackageManager(); var installedCatalogConnectResult = packageManager.GetLocalPackageCatalog(LocalPackageCatalog.InstalledPackages).Connect(); if (installedCatalogConnectResult.Status != ConnectResultStatus.Ok) { throw new Exception("Error connecting to catalog"); } var installedCatalog = installedCatalogConnectResult.PackageCatalog; var findOptions = new FindPackagesOptions(); var searchResult = installedCatalog.FindPackages(findOptions); if (searchResult.Status != FindPackagesResultStatus.Ok) { throw new Exception("Error finding packages"); } // Can't use foreach due to C#/WinRT limitations for (int i = 0; i < searchResult.Matches.Count; ++i) { Console.WriteLine("Package found: " + searchResult.Matches[i].CatalogPackage.Id); } ================================================ FILE: samples/MinimalCallers/C#/WinGet-OutOfProc/C#_WinGet_OutOfProc.csproj ================================================ Exe net8.0-windows10.0.22000.0 WinGet_OutOfProc enable enable x64;ARM64;x86 static ================================================ FILE: samples/MinimalCallers/C#/WinGet-OutOfProc/Program.cs ================================================ // Lists all installed packages using Microsoft.Management.Deployment; var packageManager = new PackageManager(); var installedCatalogConnectResult = packageManager.GetLocalPackageCatalog(LocalPackageCatalog.InstalledPackages).Connect(); if (installedCatalogConnectResult.Status != ConnectResultStatus.Ok) { throw new Exception("Error connecting to catalog"); } var installedCatalog = installedCatalogConnectResult.PackageCatalog; var findOptions = new FindPackagesOptions(); var searchResult = installedCatalog.FindPackages(findOptions); if (searchResult.Status != FindPackagesResultStatus.Ok) { throw new Exception("Error finding packages"); } // Can't use foreach due to C#/WinRT limitations for (int i = 0; i < searchResult.Matches.Count; ++i) { Console.WriteLine("Package found: " + searchResult.Matches[i].CatalogPackage.Id); } ================================================ FILE: samples/MinimalCallers/C++/WinGet-InProc/WinGet-InProc.cpp ================================================ #include #include #include #include using namespace winrt::Microsoft::Management::Deployment; const CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 const CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A // Simple RAII for COM initialization struct ComInitialization { ComInitialization() { winrt::check_hresult(CoInitialize(nullptr)); } ~ComInitialization() { CoUninitialize(); } }; // Lists all installed packages int main() { ComInitialization comInitialization; auto packageManager = winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); auto installedCatalogConnectResult = packageManager.GetLocalPackageCatalog(LocalPackageCatalog::InstalledPackages).Connect(); if (installedCatalogConnectResult.Status() != ConnectResultStatus::Ok) { std::wcerr << L"Error connecting to catalog" << std::endl; return E_FAIL; } auto installedCatalog = installedCatalogConnectResult.PackageCatalog(); auto findOptions = winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); auto searchResult = installedCatalog.FindPackages(findOptions); if (searchResult.Status() != FindPackagesResultStatus::Ok) { std::wcerr << L"Error finding packages" << std::endl; return E_FAIL; } for (auto package : searchResult.Matches()) { std::wcout << L"Found package: " << package.CatalogPackage().Id().c_str() << std::endl; } } ================================================ FILE: samples/MinimalCallers/C++/WinGet-InProc/WinGet-InProc.vcxproj ================================================ Debug ARM64 Debug Win32 Release ARM64 Release Win32 Debug x64 Release x64 17.0 Win32Proj {925454b7-a3fa-40c9-806a-4b64d5764157} WinGetInProc 10.0 C++_WinGet_InProc Application true false true Level3 true _CONSOLE;%(PreprocessorDefinitions) true Console true WIN32;%(PreprocessorDefinitions) _DEBUG;%(PreprocessorDefinitions) true true NDEBUG;%(PreprocessorDefinitions) true true ..\..\packages\Microsoft.WindowsPackageManager.InProcCom.1.10.340\lib\Microsoft.Management.Deployment.winmd true This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: samples/MinimalCallers/C++/WinGet-InProc/WinGet-InProc.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files ================================================ FILE: samples/MinimalCallers/C++/WinGet-InProc/packages.config ================================================  ================================================ FILE: samples/MinimalCallers/C++/WinGet-OutOfProc/WinGet-OutOfProc.cpp ================================================ #include #include #include #include using namespace winrt::Microsoft::Management::Deployment; const CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 const CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A // Simple RAII for COM initialization struct ComInitialization { ComInitialization() { winrt::check_hresult(CoInitialize(nullptr)); } ~ComInitialization() { CoUninitialize(); } }; // Lists all installed packages int main() { ComInitialization comInitialization; auto packageManager = winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); auto installedCatalogConnectResult = packageManager.GetLocalPackageCatalog(LocalPackageCatalog::InstalledPackages).Connect(); if (installedCatalogConnectResult.Status() != ConnectResultStatus::Ok) { std::wcerr << L"Error connecting to catalog" << std::endl; return E_FAIL; } auto installedCatalog = installedCatalogConnectResult.PackageCatalog(); auto findOptions = winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); auto searchResult = installedCatalog.FindPackages(findOptions); if (searchResult.Status() != FindPackagesResultStatus::Ok) { std::wcerr << L"Error finding packages" << std::endl; return E_FAIL; } for (auto package : searchResult.Matches()) { std::wcout << L"Found package: " << package.CatalogPackage().Id().c_str() << std::endl; } } ================================================ FILE: samples/MinimalCallers/C++/WinGet-OutOfProc/WinGet-OutOfProc.vcxproj ================================================ Debug ARM64 Debug Win32 Release ARM64 Release Win32 Debug x64 Release x64 17.0 Win32Proj {01cb5485-d1f3-421f-8e8a-7bc04b9ca037} WinGetOutOfProc 10.0 C++_WinGet_OutOfProc Application true false true Level3 true _CONSOLE;%(PreprocessorDefinitions) true Console true WIN32;%(PreprocessorDefinitions) _DEBUG;%(PreprocessorDefinitions) true true NDEBUG;%(PreprocessorDefinitions) true true static This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: samples/MinimalCallers/C++/WinGet-OutOfProc/WinGet-OutOfProc.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files ================================================ FILE: samples/MinimalCallers/C++/WinGet-OutOfProc/packages.config ================================================  ================================================ FILE: samples/MinimalCallers/MinimalCallers.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.12.35527.113 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ABB7C5E7-2192-4D33-9130-BBB951BF9284}" ProjectSection(SolutionItems) = preProject README.md = README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "C#", "C#", "{FD31D4E6-4934-4273-B5A3-C89CF7197E93}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C#_WinGet_OutOfProc", "C#\WinGet-OutOfProc\C#_WinGet_OutOfProc.csproj", "{A7CC8A07-02FC-4035-848F-1EBECCCE9C07}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C#_WinGet_InProc", "C#\WinGet-InProc\C#_WinGet_InProc.csproj", "{DD80F640-5071-4200-9BDE-620FBDFF1725}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C#_Configuration_InProc", "C#\Configuration-InProc\C#_Configuration_InProc.csproj", "{B44C18DE-FE76-4D30-B740-25A5AD99257F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C#_Configuration_OutOfProc", "C#\Configuration-OutOfProc\C#_Configuration_OutOfProc.csproj", "{93B5C28B-04AC-4B35-9253-81C1405D9A72}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "C++", "C++", "{41D22958-B935-4B37-A3BA-9E60441CE3E9}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "C++_WinGet_InProc", "C++\WinGet-InProc\WinGet-InProc.vcxproj", "{925454B7-A3FA-40C9-806A-4B64D5764157}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "C++_WinGet_OutOfProc", "C++\WinGet-OutOfProc\WinGet-OutOfProc.vcxproj", "{01CB5485-D1F3-421F-8E8A-7BC04B9CA037}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Debug|ARM64.ActiveCfg = Debug|ARM64 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Debug|ARM64.Build.0 = Debug|ARM64 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Debug|x64.ActiveCfg = Debug|x64 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Debug|x64.Build.0 = Debug|x64 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Debug|x86.ActiveCfg = Debug|x86 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Debug|x86.Build.0 = Debug|x86 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Release|ARM64.ActiveCfg = Release|ARM64 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Release|ARM64.Build.0 = Release|ARM64 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Release|x64.ActiveCfg = Release|x64 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Release|x64.Build.0 = Release|x64 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Release|x86.ActiveCfg = Release|x86 {A7CC8A07-02FC-4035-848F-1EBECCCE9C07}.Release|x86.Build.0 = Release|x86 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Debug|ARM64.ActiveCfg = Debug|ARM64 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Debug|ARM64.Build.0 = Debug|ARM64 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Debug|x64.ActiveCfg = Debug|x64 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Debug|x64.Build.0 = Debug|x64 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Debug|x86.ActiveCfg = Debug|x86 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Debug|x86.Build.0 = Debug|x86 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Release|ARM64.ActiveCfg = Release|ARM64 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Release|ARM64.Build.0 = Release|ARM64 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Release|x64.ActiveCfg = Release|x64 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Release|x64.Build.0 = Release|x64 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Release|x86.ActiveCfg = Release|x86 {DD80F640-5071-4200-9BDE-620FBDFF1725}.Release|x86.Build.0 = Release|x86 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Debug|ARM64.ActiveCfg = Debug|ARM64 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Debug|ARM64.Build.0 = Debug|ARM64 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Debug|x64.ActiveCfg = Debug|x64 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Debug|x64.Build.0 = Debug|x64 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Debug|x86.ActiveCfg = Debug|x86 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Debug|x86.Build.0 = Debug|x86 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Release|ARM64.ActiveCfg = Release|ARM64 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Release|ARM64.Build.0 = Release|ARM64 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Release|x64.ActiveCfg = Release|x64 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Release|x64.Build.0 = Release|x64 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Release|x86.ActiveCfg = Release|x86 {B44C18DE-FE76-4D30-B740-25A5AD99257F}.Release|x86.Build.0 = Release|x86 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Debug|ARM64.ActiveCfg = Debug|ARM64 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Debug|ARM64.Build.0 = Debug|ARM64 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Debug|x64.ActiveCfg = Debug|x64 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Debug|x64.Build.0 = Debug|x64 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Debug|x86.ActiveCfg = Debug|x86 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Debug|x86.Build.0 = Debug|x86 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Release|ARM64.ActiveCfg = Release|ARM64 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Release|ARM64.Build.0 = Release|ARM64 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Release|x64.ActiveCfg = Release|x64 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Release|x64.Build.0 = Release|x64 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Release|x86.ActiveCfg = Release|x86 {93B5C28B-04AC-4B35-9253-81C1405D9A72}.Release|x86.Build.0 = Release|x86 {925454B7-A3FA-40C9-806A-4B64D5764157}.Debug|ARM64.ActiveCfg = Debug|ARM64 {925454B7-A3FA-40C9-806A-4B64D5764157}.Debug|ARM64.Build.0 = Debug|ARM64 {925454B7-A3FA-40C9-806A-4B64D5764157}.Debug|x64.ActiveCfg = Debug|x64 {925454B7-A3FA-40C9-806A-4B64D5764157}.Debug|x64.Build.0 = Debug|x64 {925454B7-A3FA-40C9-806A-4B64D5764157}.Debug|x86.ActiveCfg = Debug|Win32 {925454B7-A3FA-40C9-806A-4B64D5764157}.Debug|x86.Build.0 = Debug|Win32 {925454B7-A3FA-40C9-806A-4B64D5764157}.Release|ARM64.ActiveCfg = Release|ARM64 {925454B7-A3FA-40C9-806A-4B64D5764157}.Release|ARM64.Build.0 = Release|ARM64 {925454B7-A3FA-40C9-806A-4B64D5764157}.Release|x64.ActiveCfg = Release|x64 {925454B7-A3FA-40C9-806A-4B64D5764157}.Release|x64.Build.0 = Release|x64 {925454B7-A3FA-40C9-806A-4B64D5764157}.Release|x86.ActiveCfg = Release|Win32 {925454B7-A3FA-40C9-806A-4B64D5764157}.Release|x86.Build.0 = Release|Win32 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Debug|ARM64.ActiveCfg = Debug|ARM64 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Debug|ARM64.Build.0 = Debug|ARM64 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Debug|x64.ActiveCfg = Debug|x64 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Debug|x64.Build.0 = Debug|x64 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Debug|x86.ActiveCfg = Debug|Win32 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Debug|x86.Build.0 = Debug|Win32 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Release|ARM64.ActiveCfg = Release|ARM64 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Release|ARM64.Build.0 = Release|ARM64 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Release|x64.ActiveCfg = Release|x64 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Release|x64.Build.0 = Release|x64 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Release|x86.ActiveCfg = Release|Win32 {01CB5485-D1F3-421F-8E8A-7BC04B9CA037}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {A7CC8A07-02FC-4035-848F-1EBECCCE9C07} = {FD31D4E6-4934-4273-B5A3-C89CF7197E93} {DD80F640-5071-4200-9BDE-620FBDFF1725} = {FD31D4E6-4934-4273-B5A3-C89CF7197E93} {B44C18DE-FE76-4D30-B740-25A5AD99257F} = {FD31D4E6-4934-4273-B5A3-C89CF7197E93} {93B5C28B-04AC-4B35-9253-81C1405D9A72} = {FD31D4E6-4934-4273-B5A3-C89CF7197E93} {925454B7-A3FA-40C9-806A-4B64D5764157} = {41D22958-B935-4B37-A3BA-9E60441CE3E9} {01CB5485-D1F3-421F-8E8A-7BC04B9CA037} = {41D22958-B935-4B37-A3BA-9E60441CE3E9} EndGlobalSection EndGlobal ================================================ FILE: samples/MinimalCallers/README.md ================================================ # Sample projects for COM APIs These are minimal samples for using the winget COM APIs in C++ and C#. The samples are mostly centered around the required project settings. For a more fleshed out example, see [`/Samples/WinGetUWPCaller`](https://github.com/microsoft/winget-cli/tree/master/samples/WinGetUWPCaller). ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/ActivePackageView.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ActivePackageView.h" #include "ActivePackageView.g.cpp" #include namespace winrt::WinGetUWPCaller::implementation { Microsoft::Management::Deployment::CatalogPackage ActivePackageView::Package() { return m_package; } void ActivePackageView::Package(Microsoft::Management::Deployment::CatalogPackage const& value) { m_package = value; } ActivePackageView::AsyncOperation_t ActivePackageView::AsyncOperation() { return m_asyncOperation; } Windows::Foundation::IAsyncAction UpdateUIProgress( Microsoft::Management::Deployment::InstallProgress progress, WinGetUWPCaller::ActivePackageView view) { co_await resume_foreground(view.Dispatcher()); view.Progress(progress.DownloadProgress * 100); } void ActivePackageView::AsyncOperation(ActivePackageView::AsyncOperation_t const& value) { m_asyncOperation = value; m_asyncOperation.Progress([=]( ActivePackageView::AsyncOperation_t const& /* sender */, Microsoft::Management::Deployment::InstallProgress const& progress) { UpdateUIProgress(progress, *this); }); } double ActivePackageView::Progress() { return m_progress; } void ActivePackageView::Progress(double value) { if (m_progress != value) { m_progress = value; m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Progress" }); } } hstring ActivePackageView::StatusText() { return m_text; } void ActivePackageView::StatusText(hstring const& value) { m_text = value; } Windows::UI::Core::CoreDispatcher ActivePackageView::Dispatcher() { return m_dispatcher; } void ActivePackageView::Dispatcher(Windows::UI::Core::CoreDispatcher const& value) { m_dispatcher = value; } event_token ActivePackageView::PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return m_propertyChanged.add(handler); } void ActivePackageView::PropertyChanged(event_token const& token) { m_propertyChanged.remove(token); } } ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/ActivePackageView.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ActivePackageView.g.h" namespace winrt::WinGetUWPCaller::implementation { struct ActivePackageView : ActivePackageViewT { using AsyncOperation_t = Windows::Foundation::IAsyncOperationWithProgress; ActivePackageView() = default; Microsoft::Management::Deployment::CatalogPackage Package(); void Package(Microsoft::Management::Deployment::CatalogPackage const& value); AsyncOperation_t AsyncOperation(); void AsyncOperation(AsyncOperation_t const& value); double Progress(); void Progress(double value); hstring StatusText(); void StatusText(hstring const& value); Windows::UI::Core::CoreDispatcher Dispatcher(); void Dispatcher(Windows::UI::Core::CoreDispatcher const& value); event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& value); void PropertyChanged(event_token const& token); private: Microsoft::Management::Deployment::CatalogPackage m_package{ nullptr }; AsyncOperation_t m_asyncOperation{ nullptr }; double m_progress = 0; hstring m_text; Windows::UI::Core::CoreDispatcher m_dispatcher{ nullptr }; event m_propertyChanged; }; } namespace winrt::WinGetUWPCaller::factory_implementation { struct ActivePackageView : ActivePackageViewT { }; } ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/App.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "App.h" #include "MainPage.h" using namespace winrt; using namespace Windows::ApplicationModel; using namespace Windows::ApplicationModel::Activation; using namespace Windows::Foundation; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Navigation; using namespace WinGetUWPCaller; using namespace WinGetUWPCaller::implementation; /// /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// App::App() { InitializeComponent(); Suspending({ this, &App::OnSuspending }); #if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e) { if (IsDebuggerPresent()) { auto errorMessage = e.Message(); __debugbreak(); } }); #endif } /// /// Invoked when the application is launched normally by the end user. Other entry points /// will be used such as when the application is launched to open a specific file. /// /// Details about the launch request and process. void App::OnLaunched(LaunchActivatedEventArgs const& e) { Frame rootFrame{ nullptr }; auto content = Window::Current().Content(); if (content) { rootFrame = content.try_as(); } // Do not repeat app initialization when the Window already has content, // just ensure that the window is active if (rootFrame == nullptr) { // Create a Frame to act as the navigation context and associate it with // a SuspensionManager key rootFrame = Frame(); rootFrame.NavigationFailed({ this, &App::OnNavigationFailed }); if (e.PreviousExecutionState() == ApplicationExecutionState::Terminated) { // Restore the saved session state only when appropriate, scheduling the // final launch steps after the restore is complete } if (e.PrelaunchActivated() == false) { if (rootFrame.Content() == nullptr) { // When the navigation stack isn't restored navigate to the first page, // configuring the new page by passing required information as a navigation // parameter rootFrame.Navigate(xaml_typename(), box_value(e.Arguments())); } // Place the frame in the current Window Window::Current().Content(rootFrame); // Ensure the current window is active Window::Current().Activate(); } } else { if (e.PrelaunchActivated() == false) { if (rootFrame.Content() == nullptr) { // When the navigation stack isn't restored navigate to the first page, // configuring the new page by passing required information as a navigation // parameter rootFrame.Navigate(xaml_typename(), box_value(e.Arguments())); } // Ensure the current window is active Window::Current().Activate(); } } } /// /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents /// of memory still intact. /// /// The source of the suspend request. /// Details about the suspend request. void App::OnSuspending([[maybe_unused]] IInspectable const& sender, [[maybe_unused]] SuspendingEventArgs const& e) { // Save application state and stop any background activity } /// /// Invoked when Navigation to a certain page fails /// /// The Frame which failed navigation /// Details about the navigation failure void App::OnNavigationFailed(IInspectable const&, NavigationFailedEventArgs const& e) { throw hresult_error(E_FAIL, hstring(L"Failed to load Page ") + e.SourcePageType().Name); } ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/App.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "App.xaml.g.h" namespace winrt::WinGetUWPCaller::implementation { struct App : AppT { App(); void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs const&); void OnSuspending(IInspectable const&, Windows::ApplicationModel::SuspendingEventArgs const&); void OnNavigationFailed(IInspectable const&, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs const&); }; } ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/App.idl ================================================ namespace WinGetUWPCaller { } ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/App.xaml ================================================ ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.cpp ================================================ #include "pch.h" #include "MainPage.h" #include "MainPage.g.cpp" #include using namespace std::chrono_literals; using namespace std::string_view_literals; using namespace winrt::Microsoft::Management::Deployment; namespace winrt { using namespace Windows::UI::Xaml; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; } namespace winrt::WinGetUWPCaller::implementation { namespace { std::wstring ConvertExceptionToStatusString(std::wstring_view context, std::exception_ptr exceptionPtr) { std::wostringstream result; try { std::rethrow_exception(exceptionPtr); } catch (const winrt::hresult_error& error) { result << context << L" :: " << L"0x" << std::hex << std::setw(8) << std::setfill(L'0') << error.code() << L": " << static_cast(error.message()); } catch (const std::exception& exception) { result << context << L" :: " << exception.what(); } catch (...) { result << context << L" :: Unknown exception"; } return std::move(result).str(); } template std::wstring RunAndReturnStatus(std::wstring_view context, Operation&& operation) { try { operation(); } catch (...) { return ConvertExceptionToStatusString(context, std::current_exception()); } return {}; } // Helper object to control button states and status text. struct BackgroundActionData { // This object should be constructed on the foreground thread. // disabledButtons will be disabled during the operation. // enabledButtons will be enabled during the operation. // statusText will be updated with the result. BackgroundActionData( std::initializer_list disabledButtons, Windows::UI::Xaml::Controls::TextBlock statusText) : m_disabledButtons(disabledButtons), m_statusText(statusText) { if (m_disabledButtons.empty()) { throw std::exception("Must specify at least one disabled button."); } for (const auto& button : m_disabledButtons) { button.IsEnabled(false); } m_statusText.Text(L""); } BackgroundActionData( std::initializer_list disabledButtons, std::initializer_list enabledButtons, Windows::UI::Xaml::Controls::TextBlock statusText) : BackgroundActionData(disabledButtons, statusText) { m_enabledButtons = enabledButtons; for (const auto& button : m_enabledButtons) { button.IsEnabled(true); } } // This should be run on the foreground thread. void Finalize() const { for (const auto& button : m_disabledButtons) { button.IsEnabled(true); } for (const auto& button : m_enabledButtons) { button.IsEnabled(false); } m_statusText.Text(m_status); } template void RunAndCatchStatus(std::wstring_view context, Operation&& operation) { if (m_status.empty()) { m_status = RunAndReturnStatus(context, operation); } } void Status(std::wstring&& value) { m_status = std::move(value); } bool Successful() const { return m_status.empty(); } Windows::UI::Core::CoreDispatcher Dispatcher() const { return m_disabledButtons[0].Dispatcher(); } private: std::vector m_disabledButtons; std::vector m_enabledButtons; Windows::UI::Xaml::Controls::TextBlock m_statusText; std::wstring m_status; }; std::wstring MakeCompactByteString(uint64_t bytes) { static constexpr std::array s_sizeStrings = { L"B"sv, L"KB"sv, L"MB"sv, L"GB"sv }; static constexpr size_t s_sizeIncrement = 1000; size_t sizeIndex = 0; while (sizeIndex < s_sizeStrings.size() && bytes > s_sizeIncrement) { sizeIndex += 1; bytes /= s_sizeIncrement; } return std::to_wstring(bytes).append(L" ").append(s_sizeStrings[sizeIndex]); } } MainPage::MainPage() { InitializeComponent(); m_packageCatalogs = winrt::single_threaded_observable_vector(); m_installedPackages = winrt::single_threaded_observable_vector(); m_activePackageViews = winrt::single_threaded_observable_vector(); } Windows::Foundation::Collections::IObservableVector MainPage::PackageCatalogs() { return m_packageCatalogs; } Windows::Foundation::Collections::IObservableVector MainPage::InstalledPackages() { return m_installedPackages; } Windows::Foundation::Collections::IObservableVector MainPage::ActivePackages() { return m_activePackageViews; } void MainPage::LoadCatalogsButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { LoadCatalogsAsync(); } void MainPage::CatalogSelectionChangedHandler(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&) { m_catalog = nullptr; } void MainPage::SearchButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { FindPackageAsync(); } void MainPage::InstallButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { if (m_packageOperation == nullptr || m_packageOperation.Status() != AsyncStatus::Started) { InstallOrUpgradeAsync(false); } } void MainPage::UpgradeButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { if (m_packageOperation == nullptr || m_packageOperation.Status() != AsyncStatus::Started) { InstallOrUpgradeAsync(true); } } void MainPage::DownloadButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { if (m_packageOperation == nullptr || m_packageOperation.Status() != AsyncStatus::Started) { DownloadAsync(); } } void MainPage::CancelButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { if (m_packageOperation && m_packageOperation.Status() == AsyncStatus::Started) { m_packageOperation.Cancel(); } } void MainPage::RefreshInstalledButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { GetInstalledPackagesAsync(); } void MainPage::UninstallButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { UninstallAsync(); } void MainPage::RefreshActiveButtonClickHandler(IInspectable const&, RoutedEventArgs const&) { GetActivePackagesAsync(); } std::wstring MainPage::EnsurePackageManager(bool forceRecreate) { std::lock_guard lock{ m_packageManagerMutex }; std::wstring result; if (!m_packageManager || forceRecreate) { result = RunAndReturnStatus(L"Create PackageManager", [&]() { m_packageManager = PackageManager{}; }); } return result; } IAsyncAction MainPage::LoadCatalogsAsync() { BackgroundActionData actionData{ { loadCatalogsButton() }, catalogStatusText() }; co_await winrt::resume_background(); actionData.Status(EnsurePackageManager(true)); decltype(m_packageManager.GetPackageCatalogs()) catalogs{ nullptr }; actionData.RunAndCatchStatus(L"Load Catalogs", [&]() { catalogs = m_packageManager.GetPackageCatalogs(); }); co_await winrt::resume_foreground(actionData.Dispatcher()); m_packageCatalogs.Clear(); if (catalogs) { for (auto const catalog : catalogs) { m_packageCatalogs.Append(catalog); } } actionData.Finalize(); } IAsyncAction MainPage::FindPackageAsync() { hstring queryInput = queryTextBox().Text(); auto selectedItems = catalogsListBox().SelectedItems(); int32_t searchType = searchField().SelectedIndex(); PackageCatalog catalog = m_catalog; BackgroundActionData actionData{ { searchButton() }, operationStatusText() }; co_await winrt::resume_background(); actionData.Status(EnsurePackageManager()); if (!catalog) { actionData.RunAndCatchStatus(L"Connect catalog(s)", [&]() { PackageCatalogReference catalogReference{ nullptr }; if (selectedItems.Size() == 0) { // If no items are selected, we use all implicit catalogs. CreateCompositePackageCatalogOptions createCompositePackageCatalogOptions; createCompositePackageCatalogOptions.CompositeSearchBehavior(CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs); for (const auto& item : m_packageManager.GetPackageCatalogs()) { if (!item.Info().Explicit()) { createCompositePackageCatalogOptions.Catalogs().Append(item); } } catalogReference = m_packageManager.CreateCompositePackageCatalog(createCompositePackageCatalogOptions); } else if (selectedItems.Size() == 1) { // If one items is selected, we can directly use this catalog. catalogReference = selectedItems.GetAt(0).as(); } else { // If multiple items are selected, we create a composite catalog using those catalogs. CreateCompositePackageCatalogOptions createCompositePackageCatalogOptions; createCompositePackageCatalogOptions.CompositeSearchBehavior(CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs); for (const auto& item : selectedItems) { createCompositePackageCatalogOptions.Catalogs().Append(item.as()); } catalogReference = m_packageManager.CreateCompositePackageCatalog(createCompositePackageCatalogOptions); } ConnectResult connectResult{ catalogReference.Connect() }; switch (connectResult.Status()) { case ConnectResultStatus::Ok: break; case ConnectResultStatus::CatalogError: throw std::exception{ "Catalog connection error." }; case ConnectResultStatus::SourceAgreementsNotAccepted: throw std::exception{ "Required catalog agreements not accepted." }; } catalog = connectResult.PackageCatalog(); }); } CatalogPackage package{ nullptr }; actionData.RunAndCatchStatus(L"Find package", [&]() { FindPackagesOptions findPackagesOptions; PackageMatchFilter filter; switch (searchType) { case 0: // Generic query filter.Field(PackageMatchField::CatalogDefault); filter.Option(PackageFieldMatchOption::ContainsCaseInsensitive); break; case 1: // Identifier (case-insensitive) filter.Field(PackageMatchField::Id); filter.Option(PackageFieldMatchOption::EqualsCaseInsensitive); break; case 2: // Name (substring) filter.Field(PackageMatchField::Name); filter.Option(PackageFieldMatchOption::ContainsCaseInsensitive); break; } filter.Value(queryInput); findPackagesOptions.Selectors().Append(filter); FindPackagesResult findPackagesResult = catalog.FindPackages(findPackagesOptions); winrt::IVectorView matches = findPackagesResult.Matches(); if (matches.Size() == 0) { throw std::exception{ "No package found matching input" }; } else if (matches.Size() > 1) { throw std::exception{ "Multiple packages found matching input; refine query." }; } else { package = matches.GetAt(0).CatalogPackage(); } }); if (package) { // Display the package name using the user's default localization information. std::wostringstream stream; stream << L"Found package: " << static_cast(package.DefaultInstallVersion().GetCatalogPackageMetadata().PackageName()) << L" [" << static_cast(package.Id()) << L"]"; actionData.Status(std::move(stream).str()); } co_await winrt::resume_foreground(actionData.Dispatcher()); m_catalog = catalog; m_package = package; bool operationButtonsEnabled = static_cast(m_package); installButton().IsEnabled(operationButtonsEnabled); upgradeButton().IsEnabled(operationButtonsEnabled); downloadButton().IsEnabled(operationButtonsEnabled); actionData.Finalize(); } IAsyncAction MainPage::InstallOrUpgradeAsync(bool upgrade) { PackageManager packageManager = m_packageManager; CatalogPackage package = m_package; auto progressBar = operationProgressBar(); auto statusText = operationStatusText(); BackgroundActionData actionData{ { installButton(), upgradeButton(), downloadButton() }, { cancelButton() }, statusText }; co_await winrt::resume_background(); Windows::Foundation::IAsyncOperationWithProgress packageOperation; actionData.RunAndCatchStatus(L"Begin install", [&]() { InstallOptions installOptions; // Passing PackageInstallScope::User causes the install to fail if there's no installer that supports that. installOptions.PackageInstallScope(PackageInstallScope::Any); installOptions.PackageInstallMode(PackageInstallMode::Silent); if (upgrade) { packageOperation = packageManager.UpgradePackageAsync(package, installOptions); } else { packageOperation = packageManager.InstallPackageAsync(package, installOptions); } }); actionData.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [packageOperation, this]() { m_packageOperation = packageOperation; }); actionData.RunAndCatchStatus(L"Set progress handler", [&]() { packageOperation.Progress([&]( IAsyncOperationWithProgress const& /* sender */, InstallProgress const& progress) { actionData.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [progressBar, statusText, progress]() { progressBar.Value(progress.DownloadProgress * 100); switch (progress.State) { case PackageInstallProgressState::Queued: statusText.Text(L"Queued"); break; case PackageInstallProgressState::Downloading: { std::wstring downloadText{ L"Downloaded " }; downloadText += MakeCompactByteString(progress.BytesDownloaded) + L" of " + MakeCompactByteString(progress.BytesRequired); statusText.Text(downloadText); } break; case PackageInstallProgressState::Installing: statusText.Text(L"Installer running"); progressBar.IsIndeterminate(true); break; case PackageInstallProgressState::PostInstall: statusText.Text(L"Post install bookkeeping"); break; case PackageInstallProgressState::Finished: statusText.Text(L"Done"); progressBar.IsIndeterminate(false); break; default: statusText.Text(L""); } }); }); }); InstallResult installResult{ nullptr }; actionData.RunAndCatchStatus(L"Install", [&]() { installResult = packageOperation.get(); }); if (packageOperation && packageOperation.Status() == AsyncStatus::Canceled) { actionData.Status(L"Cancelled"); } else if (installResult) { // Error handling for the installResult is done by first examining the Status. // Any status value other than Ok will have additional error detail in the // ExtendedErrorCode property. This HRESULT value will typically (but not always) // have the Windows Package Manager facility value (0xA15). The symbolic names and // meanings of these error codes can be found at: // https://github.com/microsoft/winget-cli/blob/master/doc/windows/package-manager/winget/returnCodes.md // or by using the winget CLI: // > winget error 0x8A150049 // > winget error -- -2146762487 switch (installResult.Status()) { case InstallResultStatus::Ok: actionData.Status(installResult.RebootRequired() ? L"Reboot required" : L"Done"); break; case InstallResultStatus::BlockedByPolicy: // See installResult.ExtendedErrorCode for more detail. // This is typically caused by system configuration applied by policy. actionData.Status(L"Blocked by policy"); break; case InstallResultStatus::CatalogError: // See installResult.ExtendedErrorCode for more detail. // This is typically an issue with an external service. actionData.Status(L"Catalog error"); break; case InstallResultStatus::InternalError: // See installResult.ExtendedErrorCode for more detail. // This is typically an issue with the Windows Package Manager code. actionData.Status(L"Internal error"); break; case InstallResultStatus::InvalidOptions: // See installResult.ExtendedErrorCode for more detail. // This is caused by invalid input combinations. actionData.Status(L"Invalid options"); break; case InstallResultStatus::DownloadError: // See installResult.ExtendedErrorCode for more detail. // This is typically a transient network error. actionData.Status(L"Download error"); break; case InstallResultStatus::InstallError: // See installResult.ExtendedErrorCode and installResult.InstallerErrorCode for more detail. // This is caused by an error in the installer or an issue with the system state. // InstallerErrorCode is the value returned by the installer technology in use for the install // attempt and may or may not be an HRESULT. actionData.Status(L"Installation error"); break; case InstallResultStatus::ManifestError: // See installResult.ExtendedErrorCode for more detail. // This is an issue with the catalog providing the package. actionData.Status(L"Manifest error"); break; case InstallResultStatus::NoApplicableInstallers: // No applicable installers were available due the combination of the current system, // user settings, and parameters provided to the install request. actionData.Status(L"No applicable installers"); break; case InstallResultStatus::NoApplicableUpgrade: // No upgrade was available due the combination of available versions, the current system, // user settings, and parameters provided to the upgrade request. actionData.Status(L"No applicable upgrade"); break; case InstallResultStatus::PackageAgreementsNotAccepted: // The user has not accepted the agreements required by the package. actionData.Status(L"Package agreements not accepted"); break; } } // Switch back to ui thread context. co_await winrt::resume_foreground(actionData.Dispatcher()); progressBar.IsIndeterminate(false); actionData.Finalize(); } IAsyncAction MainPage::DownloadAsync() { hstring downloadDirectory = downloadDirectoryTextBox().Text(); PackageManager packageManager = m_packageManager; CatalogPackage package = m_package; auto progressBar = operationProgressBar(); auto statusText = operationStatusText(); BackgroundActionData actionData{ { installButton(), upgradeButton(), downloadButton() }, { cancelButton() }, statusText}; co_await winrt::resume_background(); Windows::Foundation::IAsyncOperationWithProgress packageOperation; actionData.RunAndCatchStatus(L"Begin download", [&]() { DownloadOptions downloadOptions; if (!downloadDirectory.empty()) { downloadOptions.DownloadDirectory(downloadDirectory); } packageOperation = packageManager.DownloadPackageAsync(package, downloadOptions); }); actionData.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [packageOperation, this]() { m_packageOperation = packageOperation; }); actionData.RunAndCatchStatus(L"Set progress handler", [&]() { packageOperation.Progress([&]( IAsyncOperationWithProgress const& /* sender */, PackageDownloadProgress const& progress) { actionData.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [progressBar, statusText, progress]() { progressBar.Value(progress.DownloadProgress * 100); switch (progress.State) { case PackageDownloadProgressState::Queued: statusText.Text(L"Queued"); break; case PackageDownloadProgressState::Downloading: { std::wstring downloadText{ L"Downloaded " }; downloadText += MakeCompactByteString(progress.BytesDownloaded) + L" of " + MakeCompactByteString(progress.BytesRequired); statusText.Text(downloadText); } break; case PackageDownloadProgressState::Finished: statusText.Text(L"Done"); progressBar.IsIndeterminate(false); break; default: statusText.Text(L""); } }); }); }); DownloadResult downloadResult{ nullptr }; actionData.RunAndCatchStatus(L"Download", [&]() { downloadResult = packageOperation.get(); }); if (packageOperation && packageOperation.Status() == AsyncStatus::Canceled) { actionData.Status(L"Cancelled"); } else if (downloadResult) { // Error handling for the downloadResult is done by first examining the Status. // Any status value other than Ok will have additional error detail in the // ExtendedErrorCode property. This HRESULT value will typically (but not always) // have the Windows Package Manager facility value (0xA15). The symbolic names and // meanings of these error codes can be found at: // https://github.com/microsoft/winget-cli/blob/master/doc/windows/package-manager/winget/returnCodes.md // or by using the winget CLI: // > winget error 0x8A150049 // > winget error -- -2146762487 switch (downloadResult.Status()) { case DownloadResultStatus::Ok: actionData.Status(L"Done"); break; case DownloadResultStatus::BlockedByPolicy: // See installResult.ExtendedErrorCode for more detail. // This is typically caused by system configuration applied by policy. actionData.Status(L"Blocked by policy"); break; case DownloadResultStatus::CatalogError: // See installResult.ExtendedErrorCode for more detail. // This is typically an issue with an external service. actionData.Status(L"Catalog error"); break; case DownloadResultStatus::InternalError: // See installResult.ExtendedErrorCode for more detail. // This is typically an issue with the Windows Package Manager code. actionData.Status(L"Internal error"); break; case DownloadResultStatus::InvalidOptions: // See installResult.ExtendedErrorCode for more detail. // This is caused by invalid input combinations. actionData.Status(L"Invalid options"); break; case DownloadResultStatus::DownloadError: // See installResult.ExtendedErrorCode for more detail. // This is typically a transient network error. actionData.Status(L"Download error"); break; case DownloadResultStatus::ManifestError: // See installResult.ExtendedErrorCode for more detail. // This is an issue with the catalog providing the package. actionData.Status(L"Manifest error"); break; case DownloadResultStatus::NoApplicableInstallers: // No applicable installers were available due the combination of the current system, // user settings, and parameters provided to the install request. actionData.Status(L"No applicable installers"); break; case DownloadResultStatus::PackageAgreementsNotAccepted: // The user has not accepted the agreements required by the package. actionData.Status(L"Package agreements not accepted"); break; } } // Switch back to ui thread context. co_await winrt::resume_foreground(actionData.Dispatcher()); progressBar.IsIndeterminate(false); actionData.Finalize(); } IAsyncAction MainPage::GetInstalledPackagesAsync() { BackgroundActionData actionData{ { refreshInstalledButton() }, installedStatusText() }; co_await winrt::resume_background(); actionData.Status(EnsurePackageManager()); PackageCatalog catalog{ nullptr }; actionData.RunAndCatchStatus(L"Connect installed catalog", [&]() { PackageCatalogReference catalogReference = m_packageManager.GetLocalPackageCatalog(LocalPackageCatalog::InstalledPackages); ConnectResult connectResult{ catalogReference.Connect() }; switch (connectResult.Status()) { case ConnectResultStatus::Ok: break; case ConnectResultStatus::CatalogError: throw std::exception{ "Catalog connection error." }; } catalog = connectResult.PackageCatalog(); }); winrt::IVectorView matches; actionData.RunAndCatchStatus(L"Find package", [&]() { FindPackagesOptions findPackagesOptions; FindPackagesResult findPackagesResult = catalog.FindPackages(findPackagesOptions); matches = findPackagesResult.Matches(); }); co_await winrt::resume_foreground(actionData.Dispatcher()); m_installedPackages.Clear(); if (matches) { for (auto const& match : matches) { m_installedPackages.Append(match.CatalogPackage()); } } actionData.Finalize(); } IAsyncAction MainPage::UninstallAsync() { PackageManager packageManager = m_packageManager; auto progressBar = uninstallProgressBar(); auto statusText = uninstallStatusText(); IInspectable selectedValue = installedListBox().SelectedValue(); CatalogPackage package{ nullptr }; if (selectedValue) { package = selectedValue.as(); } else { statusText.Text(L"Select a package to uninstall"); co_return; } BackgroundActionData actionData{ { uninstallButton() }, statusText }; co_await winrt::resume_background(); Windows::Foundation::IAsyncOperationWithProgress packageOperation; actionData.RunAndCatchStatus(L"Begin uninstall", [&]() { UninstallOptions uninstallOptions; uninstallOptions.PackageUninstallScope(PackageUninstallScope::Any); uninstallOptions.PackageUninstallMode(PackageUninstallMode::Silent); packageOperation = packageManager.UninstallPackageAsync(package, uninstallOptions); }); actionData.RunAndCatchStatus(L"Set progress handler", [&]() { packageOperation.Progress([&]( IAsyncOperationWithProgress const& /* sender */, UninstallProgress const& progress) { actionData.Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [progressBar, statusText, progress]() { progressBar.Value(progress.UninstallationProgress * 100); switch (progress.State) { case PackageUninstallProgressState::Queued: statusText.Text(L"Queued"); break; case PackageUninstallProgressState::Uninstalling: statusText.Text(L"Uninstaller running"); progressBar.IsIndeterminate(true); break; case PackageUninstallProgressState::PostUninstall: statusText.Text(L"Post uninstall bookkeeping"); break; case PackageUninstallProgressState::Finished: statusText.Text(L"Done"); progressBar.IsIndeterminate(false); break; default: statusText.Text(L""); } }); }); }); UninstallResult uninstallResult{ nullptr }; actionData.RunAndCatchStatus(L"Uninstall", [&]() { uninstallResult = packageOperation.get(); }); if (packageOperation && packageOperation.Status() == AsyncStatus::Canceled) { actionData.Status(L"Cancelled"); } else if (uninstallResult) { // Error handling for the installResult is done by first examining the Status. // Any status value other than Ok will have additional error detail in the // ExtendedErrorCode property. This HRESULT value will typically (but not always) // have the Windows Package Manager facility value (0xA15). The symbolic names and // meanings of these error codes can be found at: // https://github.com/microsoft/winget-cli/blob/master/doc/windows/package-manager/winget/returnCodes.md // or by using the winget CLI: // > winget error 0x8A150049 // > winget error -- -2146762487 switch (uninstallResult.Status()) { case UninstallResultStatus::Ok: actionData.Status(uninstallResult.RebootRequired() ? L"Reboot required" : L"Done"); break; case UninstallResultStatus::BlockedByPolicy: // See installResult.ExtendedErrorCode for more detail. // This is typically caused by system configuration applied by policy. actionData.Status(L"Blocked by policy"); break; case UninstallResultStatus::CatalogError: // See installResult.ExtendedErrorCode for more detail. // This is typically an issue with an external service. actionData.Status(L"Catalog error"); break; case UninstallResultStatus::InternalError: // See installResult.ExtendedErrorCode for more detail. // This is typically an issue with the Windows Package Manager code. actionData.Status(L"Internal error"); break; case UninstallResultStatus::InvalidOptions: // See installResult.ExtendedErrorCode for more detail. // This is caused by invalid input combinations. actionData.Status(L"Invalid options"); break; case UninstallResultStatus::UninstallError: // See installResult.ExtendedErrorCode and installResult.UninstallerErrorCode for more detail. // This is caused by an error in the uninstaller or an issue with the system state. // UninstallerErrorCode is the value returned by the uninstaller technology in use for the uninstall // attempt and may or may not be an HRESULT. actionData.Status(L"Uninstallation error"); break; case UninstallResultStatus::ManifestError: // See installResult.ExtendedErrorCode for more detail. // This is an issue with the catalog providing the package. actionData.Status(L"Manifest error"); break; } } // Switch back to ui thread context. co_await winrt::resume_foreground(actionData.Dispatcher()); progressBar.IsIndeterminate(false); actionData.Finalize(); } IAsyncAction MainPage::GetActivePackagesAsync() { BackgroundActionData actionData{ { activeRefreshButton() }, activeStatusText() }; co_await winrt::resume_background(); actionData.Status(EnsurePackageManager()); PackageCatalog catalog{ nullptr }; actionData.RunAndCatchStatus(L"Connect installed catalog", [&]() { PackageCatalogReference catalogReference = m_packageManager.GetLocalPackageCatalog(LocalPackageCatalog::InstallingPackages); ConnectResult connectResult{ catalogReference.Connect() }; switch (connectResult.Status()) { case ConnectResultStatus::Ok: break; case ConnectResultStatus::CatalogError: throw std::exception{ "Catalog connection error." }; } catalog = connectResult.PackageCatalog(); }); winrt::IVectorView matches; actionData.RunAndCatchStatus(L"Find package", [&]() { FindPackagesOptions findPackagesOptions; FindPackagesResult findPackagesResult = catalog.FindPackages(findPackagesOptions); matches = findPackagesResult.Matches(); }); co_await winrt::resume_foreground(actionData.Dispatcher()); m_activePackageViews.Clear(); if (matches) { for (auto const& match : matches) { WinGetUWPCaller::ActivePackageView activeView; activeView.Package(match.CatalogPackage()); auto installOperation = m_packageManager.GetInstallProgress(activeView.Package(), nullptr); if (installOperation) { activeView.Dispatcher(actionData.Dispatcher()); activeView.AsyncOperation(installOperation); m_activePackageViews.Append(activeView); } } } actionData.Finalize(); } } ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "MainPage.g.h" #include "ActivePackageView.h" #include #include namespace Deployment = winrt::Microsoft::Management::Deployment; namespace winrt::WinGetUWPCaller::implementation { struct MainPage : MainPageT { MainPage(); Windows::Foundation::Collections::IObservableVector PackageCatalogs(); Windows::Foundation::Collections::IObservableVector InstalledPackages(); Windows::Foundation::Collections::IObservableVector ActivePackages(); // Select Catalog(s) section void LoadCatalogsButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void CatalogSelectionChangedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); // Package Operations section void SearchButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void InstallButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void UpgradeButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void DownloadButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void CancelButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); // Installed Packages section void RefreshInstalledButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void UninstallButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); // Active Operations section void RefreshActiveButtonClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); private: // Ensures that the package manager object exists; potentially recreating it if requested. // Should be called from a background thread. // Is thread-safe. // Returns an error string if it fails. std::wstring EnsurePackageManager(bool forceRecreate = false); // Select Catalog(s) section Windows::Foundation::IAsyncAction LoadCatalogsAsync(); // Package Operations section Windows::Foundation::IAsyncAction FindPackageAsync(); Windows::Foundation::IAsyncAction InstallOrUpgradeAsync(bool upgrade); Windows::Foundation::IAsyncAction DownloadAsync(); // Installed Packages section Windows::Foundation::IAsyncAction GetInstalledPackagesAsync(); Windows::Foundation::IAsyncAction UninstallAsync(); // Active Operations section Windows::Foundation::IAsyncAction GetActivePackagesAsync(); // Member fields Windows::Foundation::Collections::IObservableVector m_packageCatalogs; Windows::Foundation::Collections::IObservableVector m_installedPackages; Windows::Foundation::Collections::IObservableVector m_activePackageViews; std::mutex m_packageManagerMutex; Deployment::PackageManager m_packageManager{ nullptr }; Deployment::PackageCatalog m_catalog{ nullptr }; Deployment::CatalogPackage m_package{ nullptr }; Windows::Foundation::IAsyncInfo m_packageOperation; }; } namespace winrt::WinGetUWPCaller::factory_implementation { struct MainPage : MainPageT { }; } ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.idl ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace WinGetUWPCaller { [default_interface] runtimeclass ActivePackageView : Windows.UI.Xaml.Data.INotifyPropertyChanged { ActivePackageView(); Microsoft.Management.Deployment.CatalogPackage Package; Windows.Foundation.IAsyncOperationWithProgress AsyncOperation; Double Progress; String StatusText; Windows.UI.Core.CoreDispatcher Dispatcher; } [default_interface] runtimeclass MainPage : Windows.UI.Xaml.Controls.Page { MainPage(); Windows.Foundation.Collections.IObservableVector PackageCatalogs{ get; }; Windows.Foundation.Collections.IObservableVector InstalledPackages{ get; }; Windows.Foundation.Collections.IObservableVector ActivePackages{ get; }; } declare { interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; } } ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/MainPage.xaml ================================================ Select Catalog(s) Select catalog(s) to use Package Operations Search for a package from the selected catalog(s) and take action on it. Installed Packages View installed package information Active Operations View active package install/upgrade operations ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/Package.appxmanifest ================================================  Sample WinGet Caller Microsoft Corporation Assets\StoreLogo.png ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/WinGetUWPCaller.vcxproj ================================================  true true true true {37f1fd2a-4d63-45a0-82aa-66ef126cb322} WinGetUWPCaller WinGetUWPCaller en-US 15.0 true Windows Store 10.0 10.0.22000.0 10.0.17763.0 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Application true true false true false False SHA256 True True x64 0 Always Use pch.h $(IntDir)pch.pch Level4 %(AdditionalOptions) /bigobj /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) false _DEBUG;%(PreprocessorDefinitions) NDEBUG;%(PreprocessorDefinitions) true true App.xaml MainPage.xaml Designer Designer Designer Create App.xaml MainPage.xaml App.xaml MainPage.xaml win10-x86 win10-x64 win10-arm64 Microsoft.Management.Deployment.dll ..\packages\Microsoft.WindowsPackageManager.ComInterop.1.8.1911\lib\Microsoft.Management.Deployment.winmd true Microsoft.Management.Deployment.dll This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/WinGetUWPCaller.vcxproj.filters ================================================  Assets Assets Assets Assets Assets Assets Assets {adeebf4e-70b7-41ae-936b-ded00e6e6777} ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/packages.config ================================================  ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ================================================ FILE: samples/WinGetUWPCaller/WinGetUWPCaller.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30704.19 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinGetUWPCaller", "WinGetUWPCaller\WinGetUWPCaller.vcxproj", "{37F1FD2A-4D63-45A0-82AA-66EF126CB322}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|ARM64.ActiveCfg = Debug|ARM64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|ARM64.Build.0 = Debug|ARM64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|ARM64.Deploy.0 = Debug|ARM64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|x64.ActiveCfg = Debug|x64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|x64.Build.0 = Debug|x64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|x64.Deploy.0 = Debug|x64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|x86.ActiveCfg = Debug|Win32 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|x86.Build.0 = Debug|Win32 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Debug|x86.Deploy.0 = Debug|Win32 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|ARM64.ActiveCfg = Release|ARM64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|ARM64.Build.0 = Release|ARM64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|ARM64.Deploy.0 = Release|ARM64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|x64.ActiveCfg = Release|x64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|x64.Build.0 = Release|x64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|x64.Deploy.0 = Release|x64 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|x86.ActiveCfg = Release|Win32 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|x86.Build.0 = Release|Win32 {37F1FD2A-4D63-45A0-82AA-66EF126CB322}.Release|x86.Deploy.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D7D1DE2E-A7C1-4FB3-A191-C848562A3EBD} EndGlobalSection EndGlobal ================================================ FILE: schemas/JSON/configuration/configuration.schema.0.1.json ================================================ { "$id": "https://aka.ms/schemas/dsc/configuration.schema.0.1.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "description": "A representation of a configuration used by an orchestrator for WinDSC.", "type": "object", "properties": { "properties": { "type": "object", "properties": { "assertions": { "type": "array", "items": { "$ref": "#/$defs/resource" } }, "resources": { "type": "array", "items": { "$ref": "#/$defs/resource" } }, "parameters": { "type": "array", "items": { "$ref": "#/$defs/resource" }, "description": "Resources that retrieve information via a 'get' operation." }, "configurationVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "maxLength": 128, "default": "0.1.0", "description": "The configuration syntax version." } }, "required": ["configurationVersion"] } }, "$defs": { "resource": { "type": "object", "properties": { "resource": { "type": "string", "maxLength": 128, "description": "The name of the resource." }, "id": { "type": "string", "maxLength": 128, "description": "The identifier of this item." }, "dependsOn": { "type": [ "array", "null" ], "items": { "type": "string" }, "uniqueItems": true, "description": "The list of resource ids identifying dependencies." }, "directives": { "type": "object", "properties": { "module": { "type": "string", "maxLength": 128, "description": "The name of the module." }, "description": { "type": "string", "maxLength": 512, "description": "The description of the desired state." } }, "additionalProperties": true }, "settings": { "type": "object" } }, "required": ["resource"] } } } ================================================ FILE: schemas/JSON/configuration/configuration.schema.0.2.json ================================================ { "$id": "https://aka.ms/schemas/dsc/configuration.schema.0.2.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "description": "Description of what these resources do to apply the desired state in the configuration.", "type": "object", "properties": { "properties": { "type": "object", "properties": { "assertions": { "type": "array", "items": { "$ref": "#/$defs/resource" }, "description": "The preconditions required to run the configuration." }, "resources": { "type": "array", "items": { "$ref": "#/$defs/resource" }, "description": "A list of resources (software, tools, packages, settings, etc.) to be included in the configuration." }, "parameters": { "type": "array", "items": { "$ref": "#/$defs/resource" }, "description": "Resources that retrieve information via a 'get' operation." }, "configurationVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "maxLength": 128, "default": "0.2.0", "description": "The configuration syntax version." } }, "required": ["configurationVersion"], "description": "The properties of the configuration." } }, "$defs": { "resource": { "type": "object", "properties": { "resource": { "type": "string", "maxLength": 128, "description": "The name of the resource. Optionally specify module and resource as module/resource." }, "id": { "type": "string", "maxLength": 128, "description": "A unique identifier for this resource." }, "dependsOn": { "type": [ "array", "null" ], "items": { "type": "string" }, "uniqueItems": true, "description": "The list of resource ids identifying dependencies." }, "directives": { "type": "object", "properties": { "module": { "type": "string", "maxLength": 128, "description": "The name of the module." }, "description": { "type": "string", "maxLength": 512, "description": "The description of the desired state." }, "allowPrerelease": { "type": "boolean", "description": "Enable using prerelease modules." }, "securityContext": { "type": "string", "enum": [ "current", "restricted", "elevated" ], "description": "Provides an indication of the security context in which the configuration unit should be run." } }, "additionalProperties": true, "description": "Information about the module and/or resource." }, "settings": { "type": "object", "description": "Parameters as key-value pairs to be passed to the resource." } }, "required": ["resource"] } } } ================================================ FILE: schemas/JSON/manifests/latest/manifest.defaultLocale.latest.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.28.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.28.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.28.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/latest/manifest.installer.latest.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.28.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.28.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable", "font" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable", "font" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair." } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "ArchiveBinariesDependOnPath": { "type": [ "boolean", "null" ], "description": "Indicates whether the install location should be added directly to the PATH environment variable. Only applies to an archive containing portable packages." }, "Authentication": { "type": [ "object", "null" ], "properties": { "AuthenticationType": { "type": "string", "enum": [ "none", "microsoftEntraId", "microsoftEntraIdForAzureBlobStorage" ], "description": "The authentication type" }, "MicrosoftEntraIdAuthenticationInfo": { "type": [ "object", "null" ], "properties": { "Resource": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The resource value for Microsoft Entra Id authentication." }, "Scope": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The scope value for Microsoft Entra Id authentication." } }, "description": "The Microsoft Entra Id authentication info" } }, "required": [ "AuthenticationType" ], "description": "The authentication requirement for downloading the installer." }, "DesiredStateConfiguration": { "type": [ "object", "null" ], "description": "References to desired state configuration (DSC) resources that are related to the package.", "properties": { "PowerShell": { "type": [ "array", "null" ], "description": "Contains data about DSC resources that are contained in PowerShell modules.", "uniqueItems": true, "maxItems": 16, "items": { "type": "object", "title": "PowerShell DSC Module Item", "properties": { "RepositoryUrl": { "$ref": "#/definitions/Url" }, "ModuleName": { "type": "string", "description": "The name of the module containing resources.", "$comment": "From nuget package id, although PowerShell convention is slightly more strict: https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1017", "pattern": "^\\w+([.-]\\w+)*$", "maxLength": 100 }, "Resources": { "type": "array", "description": "The resources contained within the module.", "maxItems": 64, "items": { "type": "object", "title": "PowerShell DSC Resource Item", "properties": { "Name": { "type": "string", "description": "The name of the resource.", "$comment": "Needs to be an identifier in the various languages (MOF, PS class name), could not find any direct description.", "pattern": "^[A-Za-z][-_A-Za-z0-9]*$", "maxLength": 100 } } } } }, "required": [ "RepositoryUrl", "ModuleName", "Resources" ] } }, "DSCv3": { "type": [ "object", "null" ], "description": "Contains data about DSC resources that are contained in the package using the DSC v3 specification.", "properties": { "Resources": { "type": "array", "description": "The resources contained within the package.", "maxItems": 128, "items": { "type": "object", "title": "DSCv3 Resource Item", "properties": { "Type": { "type": "string", "description": "The name of the resource.", "$comment": "Pulled from DSCv3 definition; matches `Publisher.Product.Component/ResourceName` where the Product and Component are optional.", "pattern": "^\\w+(\\.\\w+){0,2}\\/\\w+$", "maxLength": 256 } } } } }, "required": [ "Resources" ] } } }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" }, "DesiredStateConfiguration": { "$ref": "#/definitions/DesiredStateConfiguration" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" }, "DesiredStateConfiguration": { "$ref": "#/definitions/DesiredStateConfiguration" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.28.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/latest/manifest.locale.latest.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.28.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.28.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.28.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/latest/manifest.singleton.latest.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.28.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.28.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable", "font" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable", "font" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "ArchiveBinariesDependOnPath": { "type": [ "boolean", "null" ], "description": "Indicates whether the install location should be added directly to the PATH environment variable. Only applies to an archive containing portable packages." }, "Authentication": { "type": [ "object", "null" ], "properties": { "AuthenticationType": { "type": "string", "enum": [ "none", "microsoftEntraId", "microsoftEntraIdForAzureBlobStorage" ], "description": "The authentication type" }, "MicrosoftEntraIdAuthenticationInfo": { "type": [ "object", "null" ], "properties": { "Resource": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The resource value for Microsoft Entra Id authentication." }, "Scope": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The scope value for Microsoft Entra Id authentication." } }, "description": "The Microsoft Entra Id authentication info" } }, "required": [ "AuthenticationType" ], "description": "The authentication requirement for downloading the installer." }, "DesiredStateConfiguration": { "type": [ "object", "null" ], "description": "References to desired state configuration (DSC) resources that are related to the package.", "properties": { "PowerShell": { "type": [ "array", "null" ], "description": "Contains data about DSC resources that are contained in PowerShell modules.", "uniqueItems": true, "maxItems": 16, "items": { "type": "object", "title": "PowerShell DSC Module Item", "properties": { "RepositoryUrl": { "$ref": "#/definitions/Url" }, "ModuleName": { "type": "string", "description": "The name of the module containing resources.", "$comment": "From nuget package id, although PowerShell convention is slightly more strict: https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1017", "pattern": "^\\w+([.-]\\w+)*$", "maxLength": 100 }, "Resources": { "type": "array", "description": "The resources contained within the module.", "maxItems": 64, "items": { "type": "object", "title": "PowerShell DSC Resource Item", "properties": { "Name": { "type": "string", "description": "The name of the resource.", "$comment": "Needs to be an identifier in the various languages (MOF, PS class name), could not find any direct description.", "pattern": "^[A-Za-z][-_A-Za-z0-9]*$", "maxLength": 100 } } } } }, "required": [ "RepositoryUrl", "ModuleName", "Resources" ] } }, "DSCv3": { "type": [ "object", "null" ], "description": "Contains data about DSC resources that are contained in the package using the DSC v3 specification.", "properties": { "Resources": { "type": "array", "description": "The resources contained within the package.", "maxItems": 128, "items": { "type": "object", "title": "DSCv3 Resource Item", "properties": { "Type": { "type": "string", "description": "The name of the resource.", "$comment": "Pulled from DSCv3 definition; matches `Publisher.Product.Component/ResourceName` where the Product and Component are optional.", "pattern": "^\\w+(\\.\\w+){0,2}\\/\\w+$", "maxLength": 256 } } } } }, "required": [ "Resources" ] } } }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" }, "DesiredStateConfiguration": { "$ref": "#/definitions/DesiredStateConfiguration" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" }, "DesiredStateConfiguration": { "$ref": "#/definitions/DesiredStateConfiguration" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.28.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/latest/manifest.version.latest.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.28.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.28.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.28.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/preview/manifest.0.1.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.0.1.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A single-file manifest representing a package in winget community repo. v0.1.0 Preview", "definitions": { "InstallerType": { "type": [ "string", "null" ], "pattern": "^(([Ee][Xx][Ee])|([Mm][Ss][Ii])|([Mm][Ss][Ii][Xx])|([Ii][Nn][Nn][Oo])|([Ww][Ii][Xx])|([Nn][Uu][Ll][Ll][Ss][Oo][Ff][Tt])|([Aa][Pp][Pp][Xx])|([Zz][Ii][Pp])|([Bb][Uu][Rr][Nn]))$", "description": "InstallerType is required under Installer node if it's not defined in root" }, "UpdateBehavior": { "type": [ "string", "null" ], "pattern": "^(([Ii][Nn][Ss][Tt][Aa][Ll][Ll])|([Uu][Nn][Ii][Nn][Ss][Tt][Aa][Ll][Ll][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]))$", "description": "UpdateBehavior is used to specify desired action during package upgrade" }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Description": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "Description of the package" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048 }, "Homepage": { "$ref": "#/definitions/Url", "description": "Homepage is a Url where the user can find more information about the package" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "LicenseUrl provides a link to the license for the user to read" }, "InstallerSwitches": { "type": [ "object", "null" ], "properties": { "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "Language": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Some installers include all localized resources. By specifying a Language switch, winget will pass the value of Language to the installer. This is not yet supported in Preview releases" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Update": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Update is the value that should be passed to the installer when user chooses an upgrade" } } }, "Installer": { "type": "object", "properties": { "Arch": { "type": "string", "pattern": "^(([Aa][Rr][Mm])|([Xx]86)|([Xx]64)|([Aa][Rr][Mm]64)|([Nn][Ee][Uu][Tt][Rr][Aa][Ll]))$", "description": "Arch is required. The installer architecture" }, "Url": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url is required. The path to the installer." }, "Sha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "Language": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 20, "description": "Language is the specific language of the installer. Language must follow IETF language tag guidelines" }, "Scope": { "type": [ "string", "null" ], "pattern": "^(([Uu][Ss][Ee][Rr])|([Mm][Aa][Cc][Hh][Ii][Nn][Ee]))$", "description": "Scope indicates if the installer is per user or per machine. Scope is not yet supported in Preview releases" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "UpdateBehavior": { "$ref": "#/definitions/UpdateBehavior" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Switches": { "$ref": "#/definitions/InstallerSwitches" } }, "required": [ "Arch", "Url", "Sha256" ] }, "ManifestLocalization": { "type": "object", "properties": { "Language": { "type": "string", "minLength": 2, "maxLength": 20, "description": "Language is the specific language of the localization. Language must follow IETF language tag guidelines" }, "Description": { "$ref": "#/definitions/Description" }, "Homepage": { "$ref": "#/definitions/Homepage" }, "LicenseUrl": { "$ref": "#/definitions/LicenseUrl" } }, "required": [ "Language" ] } }, "type": "object", "properties": { "ManifestVersion": { "type": "string", "default": "0.1.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" }, "Id": { "type": "string", "pattern": "^[^\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+\\.[^\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 255, "description": "Id is a required field. It MUST include the publisher name and package name separated by a period. For example: Publisher.Package" }, "Name": { "type": "string", "minLength": 1, "maxLength": 128, "description": "Name is a required field. The name of the package" }, "Version": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "minLength": 1, "description": "Version is a required field. The version of the package" }, "Publisher": { "type": "string", "minLength": 1, "maxLength": 128, "description": "Publisher is a required field. The legal publisher name" }, "AppMoniker": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "AppMoniker is the common name someone may use to search for the package" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Channel a string representing the flight ring. For example: stable, beta, canary" }, "Author": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The person or company responsible for authoring the package" }, "License": { "type": "string", "minLength": 1, "maxLength": 1000, "description": "License is a required field. License provides the type of license the package is provided under" }, "MinOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "MinOSVersion uses the Windows version to limit installations on unsupported platforms" }, "Tags": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 1000, "description": "Tags is a comma separated list. They represent strings that user may use to search for the package" }, "Commands": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 1000, "description": "Commands is a comma separated list. They are the common executable or alias that user might type trying to run the package" }, "Protocols": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 1000, "description": "Protocols is a comma separated list. Protocols provides the list of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 1000, "description": "FileExtensions is a comma separated list. FileExtensions provides the list of extensions the package can support" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "UpdateBehavior": { "$ref": "#/definitions/UpdateBehavior" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Description": { "$ref": "#/definitions/Description" }, "Homepage": { "$ref": "#/definitions/Homepage" }, "LicenseUrl": { "$ref": "#/definitions/LicenseUrl" }, "Switches": { "$ref": "#/definitions/InstallerSwitches" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "uniqueItems": true }, "Localization": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/ManifestLocalization" } } }, "required": [ "Id", "Name", "Version", "Publisher", "License", "Installers" ] } ================================================ FILE: schemas/JSON/manifests/v1.0.0/manifest.defaultLocale.1.0.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.0.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.0.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.0.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.0.0/manifest.installer.1.0.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.0.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.0.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "pwa" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295 }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[a-z][-a-z0-9\\.\\+]*$", "maxLength": 2048 }, "maxItems": 16, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 256, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 128 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.0.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.0.0/manifest.locale.1.0.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.0.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.0.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.0.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.0.0/manifest.singleton.1.0.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.0.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.0.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "pwa" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295 }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[a-z][-a-z0-9\\.\\+]*$", "maxLength": 2048 }, "maxItems": 16, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 256, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.0.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.0.0/manifest.version.1.0.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.0.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.0.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.0.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.1.0/manifest.defaultLocale.1.1.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.1.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.1.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.1.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.1.0/manifest.installer.1.1.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.1.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.1.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "pwa" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy" ] } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[a-z][-a-z0-9\\.\\+]*$", "maxLength": 2048 }, "maxItems": 16, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 256, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.1.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.1.0/manifest.locale.1.1.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.1.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.1.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.1.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.1.0/manifest.singleton.1.1.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.1.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.1.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "pwa" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy" ] } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[a-z][-a-z0-9\\.\\+]*$", "maxLength": 2048 }, "maxItems": 16, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 256, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.1.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.1.0/manifest.version.1.1.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.1.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.1.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.1.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.10.0/manifest.defaultLocale.1.10.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.10.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.10.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.10.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.10.0/manifest.installer.1.10.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.10.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.10.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair." } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "ArchiveBinariesDependOnPath": { "type": [ "boolean", "null" ], "description": "Indicates whether the install location should be added directly to the PATH environment variable. Only applies to an archive containing portable packages." }, "Authentication": { "type": [ "object", "null" ], "properties": { "AuthenticationType": { "type": "string", "enum": [ "none", "microsoftEntraId", "microsoftEntraIdForAzureBlobStorage" ], "description": "The authentication type" }, "MicrosoftEntraIdAuthenticationInfo": { "type": [ "object", "null" ], "properties": { "Resource": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The resource value for Microsoft Entra Id authentication." }, "Scope": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The scope value for Microsoft Entra Id authentication." } }, "description": "The Microsoft Entra Id authentication info" } }, "required": [ "AuthenticationType" ], "description": "The authentication requirement for downloading the installer." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.10.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.10.0/manifest.locale.1.10.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.10.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.10.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.10.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.10.0/manifest.singleton.1.10.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.10.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.10.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "ArchiveBinariesDependOnPath": { "type": [ "boolean", "null" ], "description": "Indicates whether the install location should be added directly to the PATH environment variable. Only applies to an archive containing portable packages." }, "Authentication": { "type": [ "object", "null" ], "properties": { "AuthenticationType": { "type": "string", "enum": [ "none", "microsoftEntraId", "microsoftEntraIdForAzureBlobStorage" ], "description": "The authentication type" }, "MicrosoftEntraIdAuthenticationInfo": { "type": [ "object", "null" ], "properties": { "Resource": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The resource value for Microsoft Entra Id authentication." }, "Scope": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The scope value for Microsoft Entra Id authentication." } }, "description": "The Microsoft Entra Id authentication info" } }, "required": [ "AuthenticationType" ], "description": "The authentication requirement for downloading the installer." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.10.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.10.0/manifest.version.1.10.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.10.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.10.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.10.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.12.0/manifest.defaultLocale.1.12.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.12.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.12.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.12.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.12.0/manifest.installer.1.12.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.12.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.12.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable", "font" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable", "font" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair." } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "ArchiveBinariesDependOnPath": { "type": [ "boolean", "null" ], "description": "Indicates whether the install location should be added directly to the PATH environment variable. Only applies to an archive containing portable packages." }, "Authentication": { "type": [ "object", "null" ], "properties": { "AuthenticationType": { "type": "string", "enum": [ "none", "microsoftEntraId", "microsoftEntraIdForAzureBlobStorage" ], "description": "The authentication type" }, "MicrosoftEntraIdAuthenticationInfo": { "type": [ "object", "null" ], "properties": { "Resource": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The resource value for Microsoft Entra Id authentication." }, "Scope": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The scope value for Microsoft Entra Id authentication." } }, "description": "The Microsoft Entra Id authentication info" } }, "required": [ "AuthenticationType" ], "description": "The authentication requirement for downloading the installer." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.12.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.12.0/manifest.locale.1.12.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.12.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.12.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.12.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.12.0/manifest.singleton.1.12.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.12.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.12.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable", "font" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable", "font" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "ArchiveBinariesDependOnPath": { "type": [ "boolean", "null" ], "description": "Indicates whether the install location should be added directly to the PATH environment variable. Only applies to an archive containing portable packages." }, "Authentication": { "type": [ "object", "null" ], "properties": { "AuthenticationType": { "type": "string", "enum": [ "none", "microsoftEntraId", "microsoftEntraIdForAzureBlobStorage" ], "description": "The authentication type" }, "MicrosoftEntraIdAuthenticationInfo": { "type": [ "object", "null" ], "properties": { "Resource": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The resource value for Microsoft Entra Id authentication." }, "Scope": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The scope value for Microsoft Entra Id authentication." } }, "description": "The Microsoft Entra Id authentication info" } }, "required": [ "AuthenticationType" ], "description": "The authentication requirement for downloading the installer." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Authentication": { "$ref": "#/definitions/Authentication" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.12.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.12.0/manifest.version.1.12.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.12.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.12.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.12.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.2.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.2.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.2.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.2.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.2.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 16, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.2.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.2.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.2.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.2.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.2.0/manifest.singleton.1.2.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.2.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.2.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 16, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.2.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.2.0/manifest.version.1.2.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.2.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.2.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,3}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.2.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.4.0/manifest.defaultLocale.1.4.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.4.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.4.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.4.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.4.0/manifest.installer.1.4.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.4.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.4.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.4.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.4.0/manifest.locale.1.4.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.4.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.4.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.4.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.4.0/manifest.singleton.1.4.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.4.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.4.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.4.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.4.0/manifest.version.1.4.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.4.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.4.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.4.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.5.0/manifest.defaultLocale.1.5.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.5.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.5.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.5.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.5.0/manifest.installer.1.5.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.5.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.5.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.5.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.5.0/manifest.locale.1.5.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.5.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.5.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.5.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.5.0/manifest.singleton.1.5.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.5.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.5.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.5.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.5.0/manifest.version.1.5.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.5.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.5.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.5.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.6.0/manifest.defaultLocale.1.6.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.6.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.6.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.6.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.6.0/manifest.installer.1.6.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.6.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.6.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.6.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.6.0/manifest.locale.1.6.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.6.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.6.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.6.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.6.0/manifest.singleton.1.6.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.6.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.6.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.6.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.6.0/manifest.version.1.6.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.6.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.6.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.6.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.7.0/manifest.defaultLocale.1.7.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.7.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.7.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.7.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.7.0/manifest.installer.1.7.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.7.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.7.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair" : { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair." } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.7.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.7.0/manifest.locale.1.7.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.7.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.7.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.7.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.7.0/manifest.singleton.1.7.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.7.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.7.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.7.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.7.0/manifest.version.1.7.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.7.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.7.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.7.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.9.0/manifest.defaultLocale.1.9.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.defaultlocale.1.9.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.9.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "defaultLocale", "const": "defaultLocale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.9.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.9.0/manifest.installer.1.9.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.installer.1.9.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.9.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The installer meta-data locale" }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Url type" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair." } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "uniqueItems": true, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "ArchiveBinariesDependOnPath": { "type": [ "boolean", "null" ], "description": "Indicates whether the install location should be added directly to the PATH environment variable. Only applies to an archive containing portable packages." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "installer", "const": "installer", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.9.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.9.0/manifest.locale.1.9.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.locale.1.9.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.9.0", "definitions": { "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] } }, "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "PackageLocale": { "type": "string", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Publisher": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "ManifestType": { "type": "string", "default": "locale", "const": "locale", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.9.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.9.0/manifest.singleton.1.9.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.singleton.1.9.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a single-file manifest representing an app in the OWC. v1.9.0", "definitions": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "Locale": { "type": [ "string", "null" ], "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The package meta-data locale" }, "Url": { "type": [ "string", "null" ], "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "Optional Url type" }, "Tag": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "Package moniker or tag" }, "Agreement": { "type": "object", "properties": { "AgreementLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the Agreement. i.e. EULA, AgeRating, etc. This field should be localized. Either Agreement or AgreementUrl is required. When we show the agreements, we would Bold the AgreementLabel" }, "Agreement": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The agreement text content." }, "AgreementUrl": { "$ref": "#/definitions/Url", "description": "The agreement URL." } } }, "Documentation": { "type": "object", "properties": { "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", "description": "The documentation URL." } } }, "Icon": { "type": "object", "properties": { "IconUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The url of the hosted icon file" }, "IconFileType": { "type": "string", "enum": [ "png", "jpeg", "ico" ], "description": "The icon file type" }, "IconResolution": { "type": [ "string", "null" ], "enum": [ "custom", "16x16", "20x20", "24x24", "30x30", "32x32", "36x36", "40x40", "48x48", "60x60", "64x64", "72x72", "80x80", "96x96", "256x256" ], "description": "Optional icon resolution" }, "IconTheme": { "type": [ "string", "null" ], "enum": [ "default", "light", "dark", "highContrast" ], "description": "Optional icon theme" }, "IconSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the icon file" } }, "required": [ "IconUrl", "IconFileType" ] }, "Channel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 16, "description": "The distribution channel" }, "Platform": { "type": [ "array", "null" ], "items": { "title": "Platform", "type": "string", "enum": [ "Windows.Desktop", "Windows.Universal" ] }, "maxItems": 2, "uniqueItems": true, "description": "The installer supported operating system" }, "MinimumOSVersion": { "type": [ "string", "null" ], "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$", "description": "The installer minimum operating system version" }, "InstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "zip", "inno", "nullsoft", "wix", "burn", "pwa", "portable" ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, "NestedInstallerType": { "type": [ "string", "null" ], "enum": [ "msix", "msi", "appx", "exe", "inno", "nullsoft", "wix", "burn", "portable" ], "description": "Enumeration of supported nested installer types contained inside an archive file" }, "NestedInstallerFiles": { "type": [ "array", "null" ], "items": { "type": "object", "title": "NestedInstallerFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 512, "description": "The relative path to the nested installer file" }, "PortableCommandAlias": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 40, "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, "description": "List of nested installer files contained inside an archive" }, "Architecture": { "type": "string", "enum": [ "x86", "x64", "arm", "arm64", "neutral" ], "description": "The installer target architecture" }, "Scope": { "type": [ "string", "null" ], "enum": [ "user", "machine" ], "description": "Scope indicates if the installer is per user or per machine" }, "InstallModes": { "type": [ "array", "null" ], "items": { "title": "InstallModes", "type": "string", "enum": [ "interactive", "silent", "silentWithProgress" ] }, "maxItems": 3, "uniqueItems": true, "description": "List of supported installer modes" }, "InstallerSwitches": { "type": "object", "properties": { "Silent": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Silent is the value that should be passed to the installer when user chooses a silent or quiet install" }, "SilentWithProgress": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "SilentWithProgress is the value that should be passed to the installer when user chooses a non-interactive install" }, "Interactive": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Interactive is the value that should be passed to the installer when user chooses an interactive install" }, "InstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "InstallLocation is the value passed to the installer for custom install location. token can be included in the switch value so that winget will replace the token with user provided path" }, "Log": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Log is the value passed to the installer for custom log file path. token can be included in the switch value so that winget will replace the token with user provided path" }, "Upgrade": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "Upgrade is the value that should be passed to the installer when user chooses an upgrade" }, "Custom": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Custom switches will be passed directly to the installer by winget" }, "Repair": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 512, "description": "The 'Repair' value must be passed to the installer, ModifyPath ARP command, or uninstaller ARP command when the user opts for a repair" } } }, "InstallerReturnCode": { "type": "integer", "format": "long", "not": { "enum": [ 0 ] }, "minimum": -2147483648, "maximum": 4294967295, "description": "An exit code that can be returned by the installer after execution" }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, "ExpectedReturnCodes": { "type": [ "array", "null" ], "items": { "type": "object", "title": "ExpectedReturnCode", "properties": { "InstallerReturnCode": { "$ref": "#/definitions/InstallerReturnCode" }, "ReturnResponse": { "type": "string", "enum": [ "packageInUse", "packageInUseByApplication", "installInProgress", "fileInUse", "missingDependency", "diskFull", "insufficientMemory", "invalidParameter", "noNetwork", "contactSupport", "rebootRequiredToFinish", "rebootRequiredForInstall", "rebootInitiated", "cancelledByUser", "alreadyInstalled", "downgrade", "blockedByPolicy", "systemNotSupported", "custom" ] }, "ReturnResponseUrl": { "$ref": "#/definitions/Url", "description": "The return response url to provide additional guidance for expected return codes" } }, "required": [ "InstallerReturnCode", "ReturnResponse" ] }, "maxItems": 128, "description": "Installer exit codes for common errors" }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ "install", "uninstallPrevious", "deny" ], "description": "The upgrade method" }, "Commands": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 16, "uniqueItems": true, "description": "List of commands or aliases to run the package" }, "Protocols": { "type": [ "array", "null" ], "items": { "type": "string", "maxLength": 2048 }, "maxItems": 64, "uniqueItems": true, "description": "List of protocols the package provides a handler for" }, "FileExtensions": { "type": [ "array", "null" ], "items": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, "Dependencies": { "type": [ "object", "null" ], "properties": { "WindowsFeatures": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows feature dependencies" }, "WindowsLibraries": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of Windows library dependencies" }, "PackageDependencies": { "type": [ "array", "null" ], "items": { "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" } }, "required": [ "PackageIdentifier" ] }, "maxItems": 16, "description": "List of package dependencies from current source" }, "ExternalDependencies": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 128 }, "maxItems": 16, "uniqueItems": true, "description": "List of external package dependencies" } } }, "PackageFamilyName": { "type": [ "string", "null" ], "pattern": "^[A-Za-z0-9][-\\.A-Za-z0-9]+_[A-Za-z0-9]{13}$", "maxLength": 255, "description": "PackageFamilyName for appx or msix installer. Could be used for correlation of packages across sources" }, "ProductCode": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 255, "description": "ProductCode could be used for correlation of packages across sources" }, "Capabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer capabilities" }, "RestrictedCapabilities": { "type": [ "array", "null" ], "items": { "type": "string", "minLength": 1, "maxLength": 40 }, "maxItems": 1000, "uniqueItems": true, "description": "List of appx or msix installer restricted capabilities" }, "Market": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "The installer target market" }, "MarketArray": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 256, "items": { "$ref": "#/definitions/Market" }, "description": "Array of markets" }, "Markets": { "description": "The installer markets", "type": [ "object", "null" ], "oneOf": [ { "properties": { "AllowedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "AllowedMarkets" ] }, { "properties": { "ExcludedMarkets": { "$ref": "#/definitions/MarketArray" } }, "required": [ "ExcludedMarkets" ] } ] }, "InstallerAbortsTerminal": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer will abort terminal. Default is false" }, "ReleaseDate": { "type": [ "string", "null" ], "format": "date", "description": "The installer release date" }, "InstallLocationRequired": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer requires an install location provided" }, "RequireExplicitUpgrade": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, "DisplayInstallWarnings": { "type": [ "boolean", "null" ], "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedOSArchitecture", "enum": [ "x86", "x64", "arm", "arm64" ] }, "description": "List of OS architectures the installer does not support" }, "UnsupportedArguments": { "type": [ "array", "null" ], "uniqueItems": true, "items": { "type": "string", "title": "UnsupportedArgument", "enum": [ "log", "location" ] }, "description": "List of winget arguments the installer does not support" }, "AppsAndFeaturesEntry": { "type": "object", "properties": { "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The DisplayName registry value" }, "Publisher": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "The Publisher registry value" }, "DisplayVersion": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 128, "description": "The DisplayVersion registry value" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "UpgradeCode": { "$ref": "#/definitions/ProductCode" }, "InstallerType": { "$ref": "#/definitions/InstallerType" } }, "description": "Various key values under installer's ARP entry" }, "AppsAndFeaturesEntries": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 128, "items": { "$ref": "#/definitions/AppsAndFeaturesEntry" }, "description": "List of ARP entries." }, "ElevationRequirement": { "type": [ "string", "null" ], "enum": [ "elevationRequired", "elevationProhibited", "elevatesSelf" ], "description": "The installer's elevation requirement" }, "InstallationMetadata": { "type": "object", "title": "InstallationMetadata", "properties": { "DefaultInstallLocation": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Represents the default installed package location. Used for deeper installation detection." }, "Files": { "type": [ "array", "null" ], "uniqueItems": true, "maxItems": 2048, "items": { "type": "object", "title": "InstalledFile", "properties": { "RelativeFilePath": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The relative path to the installed file." }, "FileSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "Optional Sha256 of the installed file." }, "FileType": { "type": [ "string", "null" ], "enum": [ "launch", "uninstall", "other" ], "description": "The optional installed file type. If not specified, the file is treated as other." }, "InvocationParameter": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 2048, "description": "Optional parameter for invocable files." }, "DisplayName": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 256, "description": "Optional display name for invocable files." } }, "required": [ "RelativeFilePath" ], "description": "Represents an installed file." }, "description": "List of installed files." } }, "description": "Details about the installation. Used for deeper installation detection." }, "DownloadCommandProhibited": { "type": [ "boolean", "null" ], "description": "Indicates whether the installer is prohibited from being downloaded for offline installation." }, "RepairBehavior": { "type": [ "string", "null" ], "enum": [ "modify", "uninstaller", "installer" ], "description": "The repair method" }, "ArchiveBinariesDependOnPath": { "type": [ "boolean", "null" ], "description": "Indicates whether the install location should be added directly to the PATH environment variable. Only applies to an archive containing portable packages." }, "Installer": { "type": "object", "properties": { "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallerUrl": { "type": "string", "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$", "description": "Sha256 is required. Sha256 of the installer" }, "SignatureSha256": { "type": [ "string", "null" ], "pattern": "^[A-Fa-f0-9]{64}$", "description": "SignatureSha256 is recommended for appx or msix. It is the sha256 of signature file inside appx or msix. Could be used during streaming install if applicable" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" } }, "required": [ "Architecture", "InstallerUrl", "InstallerSha256" ] } }, "type": "object", "properties": { "PackageIdentifier": { "$ref": "#/definitions/PackageIdentifier" }, "PackageVersion": { "$ref": "#/definitions/PackageVersion" }, "PackageLocale": { "$ref": "#/definitions/Locale" }, "Publisher": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The publisher name" }, "PublisherUrl": { "$ref": "#/definitions/Url", "description": "The publisher home page" }, "PublisherSupportUrl": { "$ref": "#/definitions/Url", "description": "The publisher support page" }, "PrivacyUrl": { "$ref": "#/definitions/Url", "description": "The publisher privacy page or the package privacy page" }, "Author": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 256, "description": "The package author" }, "PackageName": { "type": "string", "minLength": 2, "maxLength": 256, "description": "The package name" }, "PackageUrl": { "$ref": "#/definitions/Url", "description": "The package home page" }, "License": { "type": "string", "minLength": 3, "maxLength": 512, "description": "The package license" }, "LicenseUrl": { "$ref": "#/definitions/Url", "description": "The license page" }, "Copyright": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 512, "description": "The package copyright" }, "CopyrightUrl": { "$ref": "#/definitions/Url", "description": "The package copyright page" }, "ShortDescription": { "type": "string", "minLength": 3, "maxLength": 256, "description": "The short package description" }, "Description": { "type": [ "string", "null" ], "minLength": 3, "maxLength": 10000, "description": "The full package description" }, "Moniker": { "$ref": "#/definitions/Tag", "description": "The most common package term" }, "Tags": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Tag" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional package search terms" }, "Agreements": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Agreement" }, "maxItems": 128 }, "ReleaseNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The package release notes" }, "ReleaseNotesUrl": { "$ref": "#/definitions/Url", "description": "The package release notes url" }, "PurchaseUrl": { "$ref": "#/definitions/Url", "description": "The purchase url for acquiring entitlement for the package." }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 10000, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Documentation" }, "maxItems": 256 }, "Icons": { "type": [ "array", "null" ], "items": { "$ref": "#/definitions/Icon" }, "maxItems": 1024 }, "Channel": { "$ref": "#/definitions/Channel" }, "InstallerLocale": { "$ref": "#/definitions/Locale" }, "Platform": { "$ref": "#/definitions/Platform" }, "MinimumOSVersion": { "$ref": "#/definitions/MinimumOSVersion" }, "InstallerType": { "$ref": "#/definitions/InstallerType" }, "NestedInstallerType": { "$ref": "#/definitions/NestedInstallerType" }, "NestedInstallerFiles": { "$ref": "#/definitions/NestedInstallerFiles" }, "Scope": { "$ref": "#/definitions/Scope" }, "InstallModes": { "$ref": "#/definitions/InstallModes" }, "InstallerSwitches": { "$ref": "#/definitions/InstallerSwitches" }, "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, "ExpectedReturnCodes": { "$ref": "#/definitions/ExpectedReturnCodes" }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, "Commands": { "$ref": "#/definitions/Commands" }, "Protocols": { "$ref": "#/definitions/Protocols" }, "FileExtensions": { "$ref": "#/definitions/FileExtensions" }, "Dependencies": { "$ref": "#/definitions/Dependencies" }, "PackageFamilyName": { "$ref": "#/definitions/PackageFamilyName" }, "ProductCode": { "$ref": "#/definitions/ProductCode" }, "Capabilities": { "$ref": "#/definitions/Capabilities" }, "RestrictedCapabilities": { "$ref": "#/definitions/RestrictedCapabilities" }, "Markets": { "$ref": "#/definitions/Markets" }, "InstallerAbortsTerminal": { "$ref": "#/definitions/InstallerAbortsTerminal" }, "ReleaseDate": { "$ref": "#/definitions/ReleaseDate" }, "InstallLocationRequired": { "$ref": "#/definitions/InstallLocationRequired" }, "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, "DisplayInstallWarnings": { "$ref": "#/definitions/DisplayInstallWarnings" }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, "UnsupportedArguments": { "$ref": "#/definitions/UnsupportedArguments" }, "AppsAndFeaturesEntries": { "$ref": "#/definitions/AppsAndFeaturesEntries" }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, "InstallationMetadata": { "$ref": "#/definitions/InstallationMetadata" }, "DownloadCommandProhibited": { "$ref": "#/definitions/DownloadCommandProhibited" }, "RepairBehavior": { "$ref": "#/definitions/RepairBehavior" }, "ArchiveBinariesDependOnPath": { "$ref": "#/definitions/ArchiveBinariesDependOnPath" }, "Installers": { "type": "array", "items": { "$ref": "#/definitions/Installer" }, "minItems": 1, "maxItems": 1 }, "ManifestType": { "type": "string", "default": "singleton", "const": "singleton", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.9.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "PackageLocale", "Publisher", "PackageName", "License", "ShortDescription", "Installers", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/manifests/v1.9.0/manifest.version.1.9.0.json ================================================ { "$id": "https://aka.ms/winget-manifest.version.1.9.0.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.9.0", "type": "object", "properties": { "PackageIdentifier": { "type": "string", "pattern": "^[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}(\\.[^\\.\\s\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]{1,32}){1,7}$", "maxLength": 128, "description": "The package unique identifier" }, "PackageVersion": { "type": "string", "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 128, "description": "The package version" }, "DefaultLocale": { "type": "string", "default": "en-US", "pattern": "^([a-zA-Z]{2,3}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20, "description": "The default package meta-data locale" }, "ManifestType": { "type": "string", "default": "version", "const": "version", "description": "The manifest type" }, "ManifestVersion": { "type": "string", "default": "1.9.0", "pattern": "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$", "description": "The manifest syntax version" } }, "required": [ "PackageIdentifier", "PackageVersion", "DefaultLocale", "ManifestType", "ManifestVersion" ] } ================================================ FILE: schemas/JSON/settings/settings.export.schema.0.1.json ================================================ { "$id": "https://aka.ms/winget-settings-export.schema.json", "$schema": "https://json-schema.org/draft/2019-09/schema#", "title": "Microsoft's Windows Package Manager Settings Export Schema", "definitions": { "AdminSettings": { "description": "Administrator settings", "type": "object", "properties": { "BypassCertificatePinningForMicrosoftStore": { "description": "Bypass Certificate Pinning For Microsoft Store", "type": "boolean", "default": false }, "LocalManifestFiles": { "description": "Enable installing local manifests.", "type": "boolean", "default": false }, "InstallerHashOverride": { "description": "Enable overriding installer hash validation.", "type": "boolean", "default": false }, "LocalArchiveMalwareScanOverride": { "description": "Enable overriding malware scan for local archives.", "type": "boolean", "default": false }, "ProxyCommandLineOptions": { "description": "Enable using command line options for proxy.", "type": "boolean", "default": false }, "DefaultProxy": { "description": "Default proxy.", "type": "string" } } }, "UserSettingsFile": { "description": "Path for the winget's user settings file.", "type": "string", "maxLength": 32767 } }, "allOf": [ { "properties": { "adminSettings": { "$ref": "#/definitions/AdminSettings" } }, "additionalItems": true }, { "properties": { "userSettingsFile": { "$ref": "#/definitions/UserSettingsFile" } }, "additionalItems": true } ], "additionalProperties": true } ================================================ FILE: schemas/JSON/settings/settings.schema.0.2.json ================================================ { "$id": "https://aka.ms/winget-settings.schema.json", "$schema": "https://json-schema.org/draft/2019-09/schema#", "title": "Microsoft's Windows Package Manager Settings Profile Schema", "definitions": { "Source": { "description": "Source repository settings", "type": "object", "properties": { "autoUpdateIntervalInMinutes": { "description": "Number of minutes before source update", "type": "integer", "default": 5, "minimum": 0, "maximum": 43200 } } }, "Visual": { "description": "Visual settings", "type": "object", "properties": { "progressBar": { "description": "Progress bar display style", "type": "string", "enum": [ "accent", "rainbow", "retro", "sixel", "disabled" ] }, "anonymizeDisplayedPaths": { "description": "Replaces some known folder paths with their respective environment variable", "type": "boolean", "default": true }, "enableSixels": { "description": "Enables output of sixel images in certain contexts", "type": "boolean", "default": false } } }, "Logging": { "description": "Logging settings", "type": "object", "properties": { "level": { "description": "Preferred logging level", "type": "string", "enum": [ "verbose", "info", "warning", "error", "critical" ] }, "channels": { "description": "The logging channels to enable", "type": "array", "items": { "uniqueItems": true, "type": "string", "enum": [ "fail", "cli", "sql", "repo", "yaml", "core", "test", "config", "default", "all" ], "maxLength": 20 }, "minItems": 0, "maxItems": 20 }, "file": { "description": "The file settings control the log files generated by winget during operation.", "type": "object", "properties": { "ageLimitInDays": { "description": "The maximum age, in days, of a log file before it is deleted. Set to 0 to disable automatic deletion based on age.", "type": "integer", "default": 7, "minimum": 0 }, "totalSizeLimitInMB": { "description": "The maximum total size, in megabytes, of all log files. If the total size exceeds this limit, the oldest files will be deleted first. Set to 0 to disable this limit.", "type": "integer", "default": 128, "minimum": 0 }, "countLimit": { "description": "The maximum number of log files to retain. If the number of log files exceeds this limit, the oldest files will be deleted first. Set to 0 (the default) to disable this limit.", "type": "integer", "default": 0, "minimum": 0 }, "individualSizeLimitInMB": { "description": "The maximum size, in megabytes, of an individual log file. If a file would exceed this limit, new log lines will overwrite the file from the beginning. Set to 0 to disable this limit.", "type": "integer", "default": 16, "minimum": 0 } } } } }, "InstallPrefReq": { "description": "Shared schema for preferences and requirements", "type": "object", "properties": { "scope": { "description": "The scope of a package install", "type": "string", "enum": [ "user", "machine" ], "default": "user" }, "locale": { "description": "The locales of a package install", "type": "array", "items": { "type": "string", "pattern": "^([a-zA-Z]{2}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$", "maxLength": 20 }, "minItems": 1, "maxItems": 10 }, "architectures": { "description": "The architecture(s) to use for a package install", "type": "array", "items": { "uniqueItems": true, "type": "string", "enum": [ "neutral", "x64", "x86", "arm64", "arm" ], "minItems": 1, "maxItems": 4 } }, "installerTypes": { "description": "The installerType(s) to use for a package install", "type": "array", "items": { "uniqueItems": true, "type": "string", "enum": [ "inno", "wix", "msi", "nullsoft", "zip", "msix", "exe", "burn", "msstore", "portable" ], "minItems": 1, "maxItems": 9 } } } }, "InstallBehavior": { "description": "Install settings", "type": "object", "properties": { "preferences": { "$ref": "#/definitions/InstallPrefReq" }, "requirements": { "$ref": "#/definitions/InstallPrefReq" }, "skipDependencies": { "description": "Controls whether package dependencies and Windows Features are skipped during installation", "type": "boolean", "default": false }, "disableInstallNotes": { "description": "Controls whether installation notes are shown after a successful install", "type": "boolean", "default": false }, "portablePackageUserRoot": { "description": "The default root directory where packages are installed to under User scope. Applies to the portable installer type.", "type": "string", "default": "%LOCALAPPDATA%/Microsoft/WinGet/Packages/" }, "portablePackageMachineRoot": { "description": "The default root directory where packages are installed to under Machine scope. Applies to the portable installer type.", "type": "string", "default": "%PROGRAMFILES%/WinGet/Packages/" }, "defaultInstallRoot": { "description": "Default install location to use for packages that require it when not specified", "type": "string", "maxLength": 32767 }, "maxResumes": { "description": "The maximum number of resumes allowed for a single resume id. This is to prevent continuous reboots if an install that requires a reboot is not properly detected.", "type": "integer", "default": 3, "minimum": 1 }, "archiveExtractionMethod": { "description": "Controls the behavior how the installer extracts archives. The current two supported values are 'shellApi' and 'tar'. 'shellApi' uses the Windows Shell API to extract archives. 'tar' uses the tar command to extract archives.", "type": "string", "enum": [ "shellApi", "tar" ], "default": "shellApi" } } }, "UninstallBehavior": { "description": "Uninstall settings", "type": "object", "properties": { "purgePortablePackage": { "description": "Controls whether the default behavior for uninstall removes all files and directories relevant to this package. Only applies to the portable installerType.", "type": "boolean", "default": false } } }, "ConfigureBehavior": { "description": "Configure settings", "type": "object", "properties": { "defaultModuleRoot": { "description": "The default root directory where PowerShell modules are installed to when applying a configuration.", "type": "string", "maxLength": 32767, "default": "%LOCALAPPDATA%/Microsoft/WinGet/Configuration/Modules/" } } }, "DownloadBehavior": { "description": "Download settings", "type": "object", "properties": { "defaultDownloadDirectory": { "description": "The default directory where installers are downloaded to.", "type": "string", "default": "%USERPROFILE%/Downloads/" } } }, "Telemetry": { "description": "Telemetry settings", "type": "object", "properties": { "disable": { "description": "Controls whether telemetry events are written", "type": "boolean", "default": false } } }, "Network": { "description": "Network settings", "type": "object", "properties": { "downloader": { "description": "Control which download code is used for packages", "type": "string", "enum": [ "default", "wininet", "do" ], "default": "default" }, "doProgressTimeoutInSeconds": { "description": "Number of seconds to wait without progress before fallback", "type": "integer", "default": 60, "minimum": 1, "maximum": 600 } } }, "Interactivity": { "description": "Interactivity settings", "type": "object", "properties": { "disable": { "description": "Controls whether interactive prompts are shown by the Windows Package Manager client", "type": "boolean", "default": false } } }, "Experimental": { "description": "Experimental Features", "type": "object", "properties": { "experimentalCMD": { "description": "Reference implementation for an experimental command", "type": "boolean", "default": false }, "experimentalARG": { "description": "Reference implementation for an experimental argument", "type": "boolean", "default": false }, "directMSI": { "description": "Enable use of MSI APIs rather than msiexec for MSI installs", "type": "boolean", "default": false }, "fonts": { "description": "Enable support for managing fonts", "type": "boolean", "default": false }, "sourcePriority": { "description": "Enable source priority feature", "type": "boolean", "default": false } } } }, "allOf": [ { "properties": { "source": { "$ref": "#/definitions/Source" } }, "additionalItems": true }, { "properties": { "visual": { "$ref": "#/definitions/Visual" } }, "additionalItems": true }, { "properties": { "logging": { "$ref": "#/definitions/Logging" } }, "additionalItems": true }, { "properties": { "installPrefReq": { "$ref": "#/definitions/InstallPrefReq" } }, "additionalItems": true }, { "properties": { "installBehavior": { "$ref": "#/definitions/InstallBehavior" } }, "additionalItems": true }, { "properties": { "uninstallBehavior": { "$ref": "#/definitions/UninstallBehavior" } }, "additionalItems": true }, { "properties": { "configureBehavior": { "$ref": "#/definitions/ConfigureBehavior" } }, "additionalItems": true }, { "properties": { "downloadBehavior": { "$ref": "#/definitions/DownloadBehavior" } }, "additionalItems": true }, { "properties": { "telemetry": { "$ref": "#/definitions/Telemetry" } }, "additionalItems": true }, { "properties": { "network": { "$ref": "#/definitions/Network" } }, "additionalItems": true }, { "properties": { "interactivity": { "$ref": "#/definitions/Interactivity" } }, "additionalItems": true }, { "properties": { "experimentalFeatures": { "$ref": "#/definitions/Experimental" } }, "additionalItems": true } ], "additionalProperties": true } ================================================ FILE: src/AppInstallerCLI/AppInstallerCLI.vcxproj ================================================ true true true 15.0 {5b6f90df-fd19-4bae-83d9-24dad128e777} Win32Proj AppInstallerCLI 10.0.26100.0 10.0.17763.0 true Debug ARM64 Debug Win32 Release ARM64 Release Win32 Debug x64 Release x64 Application true true false true false Spectre Spectre Spectre true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset winget true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset winget true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset winget false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset winget false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset winget false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset winget NotUsing pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /bigobj Disabled _DEBUG;%(PreprocessorDefinitions) $(ProjectDir);$(ProjectDir)..\WindowsPackageManager\;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)..\WindowsPackageManager\;%(AdditionalIncludeDirectories) true true false false stdcpp17 stdcpp17 true true true true false false Console false %(AdditionalDependencies) true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) WIN32;%(PreprocessorDefinitions) $(ProjectDir);$(ProjectDir)..\WindowsPackageManager\;%(AdditionalIncludeDirectories) true false stdcpp17 true true false $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) true MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) $(ProjectDir);$(ProjectDir)..\WindowsPackageManager\;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)..\WindowsPackageManager\;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)..\WindowsPackageManager\;%(AdditionalIncludeDirectories) true true true Guard Guard Guard stdcpp17 stdcpp17 stdcpp17 true true true false false false false false false Console true true false %(AdditionalDependencies) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) true true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) {2046b5af-666d-4ce8-8d3e-c32c57908a56} This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/AppInstallerCLI/AppInstallerCLI.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files ================================================ FILE: src/AppInstallerCLI/PropertySheet.props ================================================ ================================================ FILE: src/AppInstallerCLI/main.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #define WIN32_LEAN_AND_MEAN #include #include int wmain(int argc, wchar_t const** argv) { return WindowsPackageManagerCLIMain(argc, argv); } ================================================ FILE: src/AppInstallerCLI/packages.config ================================================  ================================================ FILE: src/AppInstallerCLI.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.14.36915.13 d17.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "AppInstallerCLIPackage", "AppInstallerCLIPackage\AppInstallerCLIPackage.wapproj", "{6AA3791A-0713-4548-A357-87A323E7AC3A}" ProjectSection(ProjectDependencies) = postProject {0BA531C8-CF0C-405B-8221-0FE51BA529D1} = {0BA531C8-CF0C-405B-8221-0FE51BA529D1} {1CC41A9A-AE66-459D-9210-1E572DD7BE69} = {1CC41A9A-AE66-459D-9210-1E572DD7BE69} {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} = {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} {33745E4A-39E2-676F-7E23-50FB43848D25} = {33745E4A-39E2-676F-7E23-50FB43848D25} {5B6F90DF-FD19-4BAE-83D9-24DAD128E777} = {5B6F90DF-FD19-4BAE-83D9-24DAD128E777} {6597EB04-D105-49A7-A5A3-D27FE1DF895E} = {6597EB04-D105-49A7-A5A3-D27FE1DF895E} {CA460806-5E41-4E97-9A3D-1D74B433B663} = {CA460806-5E41-4E97-9A3D-1D74B433B663} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerCLI", "AppInstallerCLI\AppInstallerCLI.vcxproj", "{5B6F90DF-FD19-4BAE-83D9-24DAD128E777}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerCLICore", "AppInstallerCLICore\AppInstallerCLICore.vcxproj", "{1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}" ProjectSection(ProjectDependencies) = postProject {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4} = {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerCLITests", "AppInstallerCLITests\AppInstallerCLITests.vcxproj", "{89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}" ProjectSection(ProjectDependencies) = postProject {6AA3791A-0713-4548-A357-87A323E7AC3A} = {6AA3791A-0713-4548-A357-87A323E7AC3A} {6CB84692-5994-407D-B9BD-9216AF77FE83} = {6CB84692-5994-407D-B9BD-9216AF77FE83} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "External - Do Not Modify", "External - Do Not Modify", "{60618CAC-2995-4DF9-9914-45C6FC02C995}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerRepositoryCore", "AppInstallerRepositoryCore\AppInstallerRepositoryCore.vcxproj", "{5EB88068-5FB9-4E69-89B2-72DBC5E068F9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project", "Project", "{8D53D749-D51C-46F8-A162-9371AAA6C2E7}" ProjectSection(SolutionItems) = preProject ..\azure-pipelines.loc.yml = ..\azure-pipelines.loc.yml ..\azure-pipelines.nuget.yml = ..\azure-pipelines.nuget.yml ..\azure-pipelines.yml = ..\azure-pipelines.yml ..\cgmanifest.json = ..\cgmanifest.json Get-VcxprojNugetPackageVersions.ps1 = Get-VcxprojNugetPackageVersions.ps1 ..\README.md = ..\README.md ..\doc\ReleaseNotes.md = ..\doc\ReleaseNotes.md ..\doc\Settings.md = ..\doc\Settings.md Update-VcxprojNugetPackageVersions.ps1 = Update-VcxprojNugetPackageVersions.ps1 ..\WinGetInProcCom.nuspec = ..\WinGetInProcCom.nuspec ..\WinGetUtil.nuspec = ..\WinGetUtil.nuspec EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerCommonCore", "AppInstallerCommonCore\AppInstallerCommonCore.vcxproj", "{5890D6ED-7C3B-40F3-B436-B54F640D9E65}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinGetUtil", "WinGetUtil\WinGetUtil.vcxproj", "{FB313532-38B0-4676-9303-AB200AA13576}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerTestExeInstaller", "AppInstallerTestExeInstaller\AppInstallerTestExeInstaller.vcxproj", "{6CB84692-5994-407D-B9BD-9216AF77FE83}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "binver", "binver\binver.vcxitems", "{6E36DDD7-1602-474E-B1D7-D0A7E1D5AD86}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppInstallerCLIE2ETests", "AppInstallerCLIE2ETests\AppInstallerCLIE2ETests.csproj", "{3C0269FA-E582-4CA7-9E33-3881A005CA0C}" ProjectSection(ProjectDependencies) = postProject {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA} = {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA} {6CB84692-5994-407D-B9BD-9216AF77FE83} = {6CB84692-5994-407D-B9BD-9216AF77FE83} {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8} = {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8} EndProjectSection EndProject Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "AppInstallerTestMsixInstaller", "AppInstallerTestMsixInstaller\AppInstallerTestMsixInstaller.wapproj", "{3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}" EndProject Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "AppInstallerTestMsiInstaller", "AppInstallerTestMsiInstaller\AppInstallerTestMsiInstaller.vdproj", "{C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tool", "Tool", "{EA8CD934-0702-4911-A2C5-A40600E616DE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IndexCreationTool", "IndexCreationTool\IndexCreationTool.csproj", "{3B8466CF-4FDD-4329-9C80-91321C4AAC99}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Fuzzing", "Fuzzing", "{6D7776A8-42FE-46DD-B0F8-712F35EA0C79}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinGetYamlFuzzing", "WinGetYamlFuzzing\WinGetYamlFuzzing.vcxproj", "{1622DA16-914F-4F57-A259-D5169003CC8C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalhostWebServer", "LocalhostWebServer\LocalhostWebServer.csproj", "{3BAF989F-7F65-465B-ACE8-BAFE42D1017E}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ManifestSchema", "ManifestSchema\ManifestSchema.vcxitems", "{7D05F64D-CE5A-42AA-A2C1-E91458F061CF}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinGetSchemas", "WinGetSchemas\WinGetSchemas.vcxitems", "{952B513F-8A00-4D74-9271-925AFB3C6252}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spelling", "spelling", "{2ACDE176-F13F-42FA-8159-C34FA3D37837}" ProjectSection(SolutionItems) = preProject ..\.github\actions\spelling\allow.txt = ..\.github\actions\spelling\allow.txt ..\.github\actions\spelling\excludes.txt = ..\.github\actions\spelling\excludes.txt ..\.github\actions\spelling\expect.txt = ..\.github\actions\spelling\expect.txt ..\.github\actions\spelling\patterns.txt = ..\.github\actions\spelling\patterns.txt EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "policy", "policy", "{1A47951F-5C7A-4D6D-BB5F-D77484437940}" ProjectSection(SolutionItems) = preProject ..\doc\admx\en-US\DesktopAppInstaller.adml = ..\doc\admx\en-US\DesktopAppInstaller.adml ..\doc\admx\DesktopAppInstaller.admx = ..\doc\admx\DesktopAppInstaller.admx EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1A5D7A7D-5CB2-47D5-B40D-4E61CAEDC798}" ProjectSection(SolutionItems) = preProject CodeAnalysis.ruleset = CodeAnalysis.ruleset Directory.Build.props = Directory.Build.props Directory.Packages.props = Directory.Packages.props Directory.Solution.props = Directory.Solution.props nuget.config = nuget.config stylecop.json = stylecop.json vcpkg.json = vcpkg.json vcpkg.props = vcpkg.props EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Deployment", "Microsoft.Management.Deployment\Microsoft.Management.Deployment.vcxproj", "{1CC41A9A-AE66-459D-9210-1E572DD7BE69}" ProjectSection(ProjectDependencies) = postProject {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D} = {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D} {5890D6ED-7C3B-40F3-B436-B54F640D9E65} = {5890D6ED-7C3B-40F3-B436-B54F640D9E65} {5EB88068-5FB9-4E69-89B2-72DBC5E068F9} = {5EB88068-5FB9-4E69-89B2-72DBC5E068F9} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinGetServer", "WinGetServer\WinGetServer.vcxproj", "{2B00D362-AC92-41F3-A8D2-5B1599BDCA01}" ProjectSection(ProjectDependencies) = postProject {1CC41A9A-AE66-459D-9210-1E572DD7BE69} = {1CC41A9A-AE66-459D-9210-1E572DD7BE69} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinGetUtilInterop", "WinGetUtilInterop\WinGetUtilInterop.csproj", "{846FB88B-BF1B-4F33-9883-E589CEC99739}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinGetUtilInterop.UnitTests", "WinGetUtilInterop.UnitTests\WinGetUtilInterop.UnitTests.csproj", "{68808357-902B-406C-8C19-E8E26A69DE8A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsPackageManager", "WindowsPackageManager\WindowsPackageManager.vcxproj", "{2046B5AF-666D-4CE8-8D3E-C32C57908A56}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "COMServer", "COMServer\COMServer.vcxitems", "{409CD681-22A4-469D-88AE-CB5E4836E07A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Deployment.InProc", "Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.InProc.vcxproj", "{9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CertificateResources", "CertificateResources\CertificateResources.vcxitems", "{B0BBBD92-943B-408F-B2B2-DBBAB4A22D23}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PowerShell", "PowerShell", "{7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WinGet.Client.Cmdlets", "PowerShell\Microsoft.WinGet.Client.Cmdlets\Microsoft.WinGet.Client.Cmdlets.csproj", "{463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Deployment.Projection", "Microsoft.Management.Deployment.Projection\Microsoft.Management.Deployment.Projection.csproj", "{0B104762-5CD8-47EE-A904-71C1C3F84DCD}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UndockedRegFreeWinRT", "Xlang\UndockedRegFreeWinRT\src\UndockedRegFreeWinRT\UndockedRegFreeWinRT\UndockedRegFreeWinRT.vcxproj", "{31ED69A8-5310-45A9-953F-56C351D2C3E1}" ProjectSection(ProjectDependencies) = postProject {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} = {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} EndProjectSection 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 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerSharedLib", "AppInstallerSharedLib\AppInstallerSharedLib.vcxproj", "{F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Configuration", "Microsoft.Management.Configuration\Microsoft.Management.Configuration.vcxproj", "{CA460806-5E41-4E97-9A3D-1D74B433B663}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Configuration.Projection", "Microsoft.Management.Configuration.Projection\Microsoft.Management.Configuration.Projection.csproj", "{E8454BF1-2068-4513-A525-ABF55CC8742C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Configuration.UnitTests", "Microsoft.Management.Configuration.UnitTests\Microsoft.Management.Configuration.UnitTests.csproj", "{EE43C990-7789-4A60-B077-BF0ED3D093A1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Configuration.Processor", "Microsoft.Management.Configuration.Processor\Microsoft.Management.Configuration.Processor.csproj", "{71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConfigurationRemotingServer", "ConfigurationRemotingServer\ConfigurationRemotingServer.csproj", "{6597EB04-D105-49A7-A5A3-D27FE1DF895E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WinGet.Client.Engine", "PowerShell\Microsoft.WinGet.Client.Engine\Microsoft.WinGet.Client.Engine.csproj", "{1F56BECB-D65D-4BBA-8788-6671B251392A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WinGet.Configuration.Cmdlets", "PowerShell\Microsoft.WinGet.Configuration.Cmdlets\Microsoft.WinGet.Configuration.Cmdlets.csproj", "{167F634B-A3AD-494E-8E67-B888103E35FF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WinGet.Configuration.Engine", "PowerShell\Microsoft.WinGet.Configuration.Engine\Microsoft.WinGet.Configuration.Engine.csproj", "{C54F80ED-B736-49B0-9BD3-662F57024D01}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Configuration.OutOfProc", "Microsoft.Management.Configuration.OutOfProc\Microsoft.Management.Configuration.OutOfProc.vcxproj", "{2268D5AD-7F2A-485A-8C4B-C574497514C9}" ProjectSection(ProjectDependencies) = postProject {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} = {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinGetSourceCreator", "WinGetSourceCreator\WinGetSourceCreator.csproj", "{52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WinGet.SharedLib", "PowerShell\Microsoft.WinGet.SharedLib\Microsoft.WinGet.SharedLib.csproj", "{272B2B0E-40D4-4F0F-B187-519A6EF89B10}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{5A52D9FC-0059-4A4A-8196-427A7AA0D1C5}" ProjectSection(SolutionItems) = preProject PowerShell\tests\Microsoft.WinGet.Client.Tests.ps1 = PowerShell\tests\Microsoft.WinGet.Client.Tests.ps1 PowerShell\tests\Microsoft.WinGet.Configuration.Tests.ps1 = PowerShell\tests\Microsoft.WinGet.Configuration.Tests.ps1 PowerShell\tests\Microsoft.WinGet.DSC.Tests.ps1 = PowerShell\tests\Microsoft.WinGet.DSC.Tests.ps1 PowerShell\tests\RunTests.ps1 = PowerShell\tests\RunTests.ps1 EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SfsClient", "SfsClient\SfsClient.vcxproj", "{1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VcpkgCustomTriplets", "VcpkgCustomTriplets", "{76B26B2C-602A-4AD0-9736-4162D3FCA92A}" ProjectSection(SolutionItems) = preProject VcpkgCustomTriplets\arm64-release-static.cmake = VcpkgCustomTriplets\arm64-release-static.cmake VcpkgCustomTriplets\arm64-release.cmake = VcpkgCustomTriplets\arm64-release.cmake VcpkgCustomTriplets\arm64.cmake = VcpkgCustomTriplets\arm64.cmake VcpkgCustomTriplets\common.cmake = VcpkgCustomTriplets\common.cmake VcpkgCustomTriplets\fuzzing.cmake = VcpkgCustomTriplets\fuzzing.cmake VcpkgCustomTriplets\x64-fuzzing.cmake = VcpkgCustomTriplets\x64-fuzzing.cmake VcpkgCustomTriplets\x64-release-static.cmake = VcpkgCustomTriplets\x64-release-static.cmake VcpkgCustomTriplets\x64-release.cmake = VcpkgCustomTriplets\x64-release.cmake VcpkgCustomTriplets\x64.cmake = VcpkgCustomTriplets\x64.cmake VcpkgCustomTriplets\x86-fuzzing.cmake = VcpkgCustomTriplets\x86-fuzzing.cmake VcpkgCustomTriplets\x86-release-static.cmake = VcpkgCustomTriplets\x86-release-static.cmake VcpkgCustomTriplets\x86-release.cmake = VcpkgCustomTriplets\x86-release.cmake VcpkgCustomTriplets\x86.cmake = VcpkgCustomTriplets\x86.cmake EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Deployment.OutOfProc", "Microsoft.Management.Deployment.OutOfProc\Microsoft.Management.Deployment.OutOfProc.vcxproj", "{0BA531C8-CF0C-405B-8221-0FE51BA529D1}" ProjectSection(ProjectDependencies) = postProject {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} = {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Deployment.CsWinRTProjection", "Microsoft.Management.Deployment.CsWinRTProjection\Microsoft.Management.Deployment.CsWinRTProjection.csproj", "{9406322E-6272-487E-902A-9953889719EA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "targets", "targets", "{A0B4F808-B190-41C4-97CB-C8EA1932F84F}" ProjectSection(SolutionItems) = preProject targets\EmbeddedCsWinRT.targets = targets\EmbeddedCsWinRT.targets targets\ReferenceEmbeddedCsWinRTProject.targets = targets\ReferenceEmbeddedCsWinRTProject.targets EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PureLib", "PureLib\PureLib.vcxitems", "{A33223D2-550B-4D99-A53D-488B1F68683E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinGetTestCommon", "WinGetTestCommon\WinGetTestCommon.csproj", "{7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinGetMCPServer", "WinGetMCPServer\WinGetMCPServer.csproj", "{33745E4A-39E2-676F-7E23-50FB43848D25}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{F49C4C89-447E-4D15-B38B-5A8DCFB134AF}" ProjectSection(SolutionItems) = preProject PowerShell\scripts\Execute-WinGetTests.ps1 = PowerShell\scripts\Execute-WinGetTests.ps1 PowerShell\scripts\Initialize-LocalWinGetModules.ps1 = PowerShell\scripts\Initialize-LocalWinGetModules.ps1 EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ComInprocTestbed", "ComInprocTestbed\ComInprocTestbed.vcxproj", "{E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DSC", "DSC", "{40D7CA7F-EB86-4345-9641-AD27180C559D}" ProjectSection(SolutionItems) = preProject PowerShell\Microsoft.WinGet.DSC\Microsoft.WinGet.DSC.psd1 = PowerShell\Microsoft.WinGet.DSC\Microsoft.WinGet.DSC.psd1 PowerShell\Microsoft.WinGet.DSC\Microsoft.WinGet.DSC.psm1 = PowerShell\Microsoft.WinGet.DSC\Microsoft.WinGet.DSC.psm1 EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.WinGet.UnitTests", "PowerShell\Microsoft.WinGet.UnitTests\Microsoft.WinGet.UnitTests.csproj", "{5421394F-5619-4E4B-8923-F3FB30D5EFAD}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "copilot", "copilot", "{B978E358-D2BE-4FA7-A21A-6661F3744DD7}" ProjectSection(SolutionItems) = preProject ..\.github\copilot-instructions.md = ..\.github\copilot-instructions.md EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Fuzzing|ARM64 = Fuzzing|ARM64 Fuzzing|x64 = Fuzzing|x64 Fuzzing|x86 = Fuzzing|x86 Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 ReleaseStatic|ARM64 = ReleaseStatic|ARM64 ReleaseStatic|x64 = ReleaseStatic|x64 ReleaseStatic|x86 = ReleaseStatic|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM64.ActiveCfg = Debug|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM64.Build.0 = Debug|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM64.Deploy.0 = Debug|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x64.ActiveCfg = Debug|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x64.Build.0 = Debug|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x64.Deploy.0 = Debug|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x86.ActiveCfg = Debug|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x86.Build.0 = Debug|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x86.Deploy.0 = Debug|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|x64.ActiveCfg = Release|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|x86.ActiveCfg = Release|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM64.ActiveCfg = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM64.Build.0 = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM64.Deploy.0 = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x64.ActiveCfg = Release|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x64.Build.0 = Release|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x64.Deploy.0 = Release|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x86.ActiveCfg = Release|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x86.Build.0 = Release|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x86.Deploy.0 = Release|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.ReleaseStatic|ARM64.ActiveCfg = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.ReleaseStatic|x64.ActiveCfg = Release|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.ReleaseStatic|x86.ActiveCfg = Release|x86 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|ARM64.Build.0 = Debug|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|x64.ActiveCfg = Debug|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|x64.Build.0 = Debug|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|x86.ActiveCfg = Debug|Win32 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|x86.Build.0 = Debug|Win32 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|x64.ActiveCfg = Release|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|x86.ActiveCfg = Release|Win32 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|ARM64.ActiveCfg = Release|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|ARM64.Build.0 = Release|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|x64.ActiveCfg = Release|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|x64.Build.0 = Release|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|x86.ActiveCfg = Release|Win32 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|x86.Build.0 = Release|Win32 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.ReleaseStatic|ARM64.ActiveCfg = Release|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.ReleaseStatic|x64.ActiveCfg = Release|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.ReleaseStatic|x86.ActiveCfg = Release|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|ARM64.ActiveCfg = Debug|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|ARM64.Build.0 = Debug|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|x64.ActiveCfg = Debug|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|x64.Build.0 = Debug|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|x86.ActiveCfg = Debug|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|x86.Build.0 = Debug|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|x64.ActiveCfg = Release|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|x86.ActiveCfg = Release|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|ARM64.ActiveCfg = Release|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|ARM64.Build.0 = Release|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|x64.ActiveCfg = Release|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|x64.Build.0 = Release|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|x86.ActiveCfg = Release|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|x86.Build.0 = Release|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|ARM64.ActiveCfg = Debug|ARM64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|ARM64.Build.0 = Debug|ARM64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|x64.ActiveCfg = Debug|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|x64.Build.0 = Debug|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|x86.ActiveCfg = Debug|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|x86.Build.0 = Debug|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|ARM64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|x64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|x86.ActiveCfg = Release|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|ARM64.ActiveCfg = Release|ARM64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|ARM64.Build.0 = Release|ARM64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|x64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|x64.Build.0 = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|x86.ActiveCfg = Release|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|x86.Build.0 = Release|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.ReleaseStatic|ARM64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.ReleaseStatic|x64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.ReleaseStatic|x86.ActiveCfg = Release|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|ARM64.Build.0 = Debug|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|x64.ActiveCfg = Debug|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|x64.Build.0 = Debug|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|x86.ActiveCfg = Debug|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|x86.Build.0 = Debug|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|x64.ActiveCfg = Release|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|x86.ActiveCfg = Release|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|ARM64.ActiveCfg = Release|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|ARM64.Build.0 = Release|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|x64.ActiveCfg = Release|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|x64.Build.0 = Release|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|x86.ActiveCfg = Release|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|x86.Build.0 = Release|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|ARM64.Build.0 = Debug|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|x64.ActiveCfg = Debug|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|x64.Build.0 = Debug|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|x86.ActiveCfg = Debug|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|x86.Build.0 = Debug|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|x64.Build.0 = Fuzzing|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|x86.Build.0 = Fuzzing|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|ARM64.ActiveCfg = Release|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|ARM64.Build.0 = Release|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|x64.ActiveCfg = Release|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|x64.Build.0 = Release|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|x86.ActiveCfg = Release|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|x86.Build.0 = Release|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|ARM64.ActiveCfg = Debug|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|ARM64.Build.0 = Debug|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|x64.ActiveCfg = Debug|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|x64.Build.0 = Debug|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|x86.ActiveCfg = Debug|Win32 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|x86.Build.0 = Debug|Win32 {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|x64.ActiveCfg = Release|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|x86.ActiveCfg = Release|Win32 {FB313532-38B0-4676-9303-AB200AA13576}.Release|ARM64.ActiveCfg = Release|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Release|ARM64.Build.0 = Release|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Release|x64.ActiveCfg = Release|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Release|x64.Build.0 = Release|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Release|x86.ActiveCfg = Release|Win32 {FB313532-38B0-4676-9303-AB200AA13576}.Release|x86.Build.0 = Release|Win32 {FB313532-38B0-4676-9303-AB200AA13576}.ReleaseStatic|ARM64.ActiveCfg = Release|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.ReleaseStatic|x64.ActiveCfg = Release|x64 {FB313532-38B0-4676-9303-AB200AA13576}.ReleaseStatic|x86.ActiveCfg = Release|Win32 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|ARM64.ActiveCfg = Debug|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|ARM64.Build.0 = Debug|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|x64.ActiveCfg = Debug|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|x64.Build.0 = Debug|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|x86.ActiveCfg = Debug|Win32 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|x86.Build.0 = Debug|Win32 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|x64.ActiveCfg = Release|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|x86.ActiveCfg = Release|Win32 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|ARM64.ActiveCfg = Release|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|ARM64.Build.0 = Release|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|x64.ActiveCfg = Release|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|x64.Build.0 = Release|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|x86.ActiveCfg = Release|Win32 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|x86.Build.0 = Release|Win32 {6CB84692-5994-407D-B9BD-9216AF77FE83}.ReleaseStatic|ARM64.ActiveCfg = Release|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.ReleaseStatic|x64.ActiveCfg = Release|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.ReleaseStatic|x86.ActiveCfg = Release|Win32 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|ARM64.ActiveCfg = Debug|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|x64.ActiveCfg = Debug|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|x64.Build.0 = Debug|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|x86.ActiveCfg = Debug|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|x86.Build.0 = Debug|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|ARM64.ActiveCfg = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|x64.ActiveCfg = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|x86.ActiveCfg = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|ARM64.ActiveCfg = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|x64.ActiveCfg = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|x64.Build.0 = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|x86.ActiveCfg = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|x86.Build.0 = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.ReleaseStatic|ARM64.ActiveCfg = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.ReleaseStatic|x64.ActiveCfg = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.ReleaseStatic|x86.ActiveCfg = Release|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM64.ActiveCfg = Debug|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM64.Build.0 = Debug|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM64.Deploy.0 = Debug|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x64.ActiveCfg = Debug|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x64.Build.0 = Debug|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x64.Deploy.0 = Debug|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x86.ActiveCfg = Debug|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x86.Build.0 = Debug|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x86.Deploy.0 = Debug|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|x64.ActiveCfg = Release|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|x86.ActiveCfg = Release|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM64.ActiveCfg = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM64.Build.0 = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM64.Deploy.0 = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x64.ActiveCfg = Release|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x64.Build.0 = Release|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x64.Deploy.0 = Release|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x86.ActiveCfg = Release|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x86.Build.0 = Release|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x86.Deploy.0 = Release|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.ReleaseStatic|ARM64.ActiveCfg = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.ReleaseStatic|x64.ActiveCfg = Release|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.ReleaseStatic|x86.ActiveCfg = Release|x86 {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|ARM64.ActiveCfg = Debug {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|x64.ActiveCfg = Debug {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|x86.ActiveCfg = Debug {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|ARM64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|x64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|x86.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|ARM64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|x64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|x86.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.ReleaseStatic|ARM64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.ReleaseStatic|x64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.ReleaseStatic|x86.ActiveCfg = Release {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|ARM64.ActiveCfg = Debug|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|x64.ActiveCfg = Debug|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|x64.Build.0 = Debug|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|x86.ActiveCfg = Debug|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|x86.Build.0 = Debug|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|ARM64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|x64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|x86.ActiveCfg = Release|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|ARM64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|x64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|x64.Build.0 = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|x86.ActiveCfg = Release|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|x86.Build.0 = Release|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.ReleaseStatic|ARM64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.ReleaseStatic|x64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.ReleaseStatic|x86.ActiveCfg = Release|x86 {1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|ARM64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|x64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|x86.ActiveCfg = Fuzzing|Win32 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|ARM64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|x64.Build.0 = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|x86.Build.0 = Fuzzing|Win32 {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|ARM64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|x64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|x86.ActiveCfg = Fuzzing|Win32 {1622DA16-914F-4F57-A259-D5169003CC8C}.ReleaseStatic|ARM64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.ReleaseStatic|x64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.ReleaseStatic|x86.ActiveCfg = Fuzzing|Win32 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|ARM64.ActiveCfg = Debug|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|x64.ActiveCfg = Debug|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|x64.Build.0 = Debug|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|x86.ActiveCfg = Debug|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|x86.Build.0 = Debug|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|ARM64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|x64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|x86.ActiveCfg = Release|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|ARM64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|x64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|x64.Build.0 = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|x86.ActiveCfg = Release|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|x86.Build.0 = Release|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.ReleaseStatic|ARM64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.ReleaseStatic|x64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.ReleaseStatic|x86.ActiveCfg = Release|x86 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM64.ActiveCfg = Debug|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM64.Build.0 = Debug|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x64.ActiveCfg = Debug|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x64.Build.0 = Debug|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x86.ActiveCfg = Debug|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x86.Build.0 = Debug|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|x64.ActiveCfg = Release|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|x86.ActiveCfg = Release|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM64.ActiveCfg = Release|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM64.Build.0 = Release|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x64.ActiveCfg = Release|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x64.Build.0 = Release|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x86.ActiveCfg = Release|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x86.Build.0 = Release|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM64.Build.0 = Debug|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x64.ActiveCfg = Debug|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x64.Build.0 = Debug|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x86.ActiveCfg = Debug|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x86.Build.0 = Debug|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|x64.ActiveCfg = Release|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|x86.ActiveCfg = Release|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM64.ActiveCfg = Release|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM64.Build.0 = Release|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x64.ActiveCfg = Release|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x64.Build.0 = Release|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x86.ActiveCfg = Release|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x86.Build.0 = Release|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|ARM64.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|ARM64.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|x64.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|x64.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|x86.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|x86.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|x64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|x86.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|ARM64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|ARM64.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|x64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|x64.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|x86.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|x86.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.ReleaseStatic|ARM64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.ReleaseStatic|x64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.ReleaseStatic|x86.ActiveCfg = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|ARM64.ActiveCfg = Debug|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|x64.ActiveCfg = Debug|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|x64.Build.0 = Debug|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|x86.ActiveCfg = Debug|x86 {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|x86.Build.0 = Debug|x86 {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|ARM64.ActiveCfg = Release|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|x64.ActiveCfg = Release|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|x86.ActiveCfg = Release|x86 {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|ARM64.ActiveCfg = Release|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|x64.ActiveCfg = Release|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|x64.Build.0 = Release|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|x86.ActiveCfg = Release|x86 {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|x86.Build.0 = Release|x86 {68808357-902B-406C-8C19-E8E26A69DE8A}.ReleaseStatic|ARM64.ActiveCfg = Release|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.ReleaseStatic|x64.ActiveCfg = Release|x64 {68808357-902B-406C-8C19-E8E26A69DE8A}.ReleaseStatic|x86.ActiveCfg = Release|x86 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|ARM64.Build.0 = Debug|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|x64.ActiveCfg = Debug|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|x64.Build.0 = Debug|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|x86.ActiveCfg = Debug|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|x86.Build.0 = Debug|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|x64.ActiveCfg = Release|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|x86.ActiveCfg = Release|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|ARM64.ActiveCfg = Release|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|ARM64.Build.0 = Release|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|x64.ActiveCfg = Release|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|x64.Build.0 = Release|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|x86.ActiveCfg = Release|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|x86.Build.0 = Release|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|ARM64.ActiveCfg = Debug|ARM64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|ARM64.Build.0 = Debug|ARM64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|x64.ActiveCfg = Debug|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|x64.Build.0 = Debug|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|x86.ActiveCfg = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|x86.Build.0 = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|x64.ActiveCfg = Release|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|x86.ActiveCfg = Release|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|ARM64.ActiveCfg = Release|ARM64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|x64.ActiveCfg = Release|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|x64.Build.0 = Release|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|x86.ActiveCfg = Release|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|x86.Build.0 = Release|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|ARM64.ActiveCfg = Debug|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|ARM64.Build.0 = Debug|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|x64.ActiveCfg = Debug|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|x64.Build.0 = Debug|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|x86.ActiveCfg = Debug|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|x86.Build.0 = Debug|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|x64.ActiveCfg = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|x86.ActiveCfg = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|ARM64.ActiveCfg = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|ARM64.Build.0 = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|x64.ActiveCfg = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|x64.Build.0 = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|x86.ActiveCfg = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|x86.Build.0 = Release|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|ARM64.ActiveCfg = Debug|arm64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|x64.ActiveCfg = Debug|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|x64.Build.0 = Debug|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|x86.ActiveCfg = Debug|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|x86.Build.0 = Debug|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|ARM64.ActiveCfg = Release|arm64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|x64.ActiveCfg = Release|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|x86.ActiveCfg = Release|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|ARM64.ActiveCfg = Release|arm64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|x64.ActiveCfg = Release|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|x64.Build.0 = Release|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|x86.ActiveCfg = Release|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|x86.Build.0 = Release|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|arm64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|arm64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.ReleaseStatic|x86.Build.0 = ReleaseStatic|x86 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|ARM64.Build.0 = Debug|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|x64.ActiveCfg = Debug|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|x64.Build.0 = Debug|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|x86.ActiveCfg = Debug|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|x86.Build.0 = Debug|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|x64.ActiveCfg = Release|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|x86.ActiveCfg = Release|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|ARM64.ActiveCfg = Release|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|ARM64.Build.0 = Release|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x64.ActiveCfg = Release|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x64.Build.0 = Release|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x86.ActiveCfg = Release|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x86.Build.0 = Release|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|ARM64.Build.0 = Debug|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|x64.ActiveCfg = Debug|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|x64.Build.0 = Debug|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|x86.ActiveCfg = Debug|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|x86.Build.0 = Debug|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|ARM64.ActiveCfg = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|x64.Build.0 = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|x86.Build.0 = Fuzzing|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|ARM64.ActiveCfg = Release|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|ARM64.Build.0 = Release|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|x64.ActiveCfg = Release|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|x64.Build.0 = Release|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|x86.ActiveCfg = Release|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|x86.Build.0 = Release|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|ARM64.Build.0 = Debug|ARM64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|x64.ActiveCfg = Debug|x64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|x64.Build.0 = Debug|x64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|x86.ActiveCfg = Debug|Win32 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|x86.Build.0 = Debug|Win32 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|x64.ActiveCfg = Release|x64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|x86.ActiveCfg = Release|Win32 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|ARM64.ActiveCfg = Release|ARM64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|ARM64.Build.0 = Release|ARM64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|x64.ActiveCfg = Release|x64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|x64.Build.0 = Release|x64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|x86.ActiveCfg = Release|Win32 {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|x86.Build.0 = Release|Win32 {CA460806-5E41-4E97-9A3D-1D74B433B663}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {CA460806-5E41-4E97-9A3D-1D74B433B663}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {CA460806-5E41-4E97-9A3D-1D74B433B663}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|ARM64.ActiveCfg = Debug|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|ARM64.Build.0 = Debug|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|x64.ActiveCfg = Debug|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|x64.Build.0 = Debug|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|x86.ActiveCfg = Debug|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|x86.Build.0 = Debug|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|x64.ActiveCfg = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|x86.ActiveCfg = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|ARM64.ActiveCfg = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|ARM64.Build.0 = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|x64.ActiveCfg = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|x64.Build.0 = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|x86.ActiveCfg = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|x86.Build.0 = Release|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {E8454BF1-2068-4513-A525-ABF55CC8742C}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|ARM64.ActiveCfg = Debug|arm64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|ARM64.Build.0 = Debug|arm64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|x64.ActiveCfg = Debug|x64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|x64.Build.0 = Debug|x64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|x86.ActiveCfg = Debug|x86 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|x86.Build.0 = Debug|x86 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|ARM64.ActiveCfg = Release|arm64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|x64.ActiveCfg = Release|x64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|x86.ActiveCfg = Release|x86 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|ARM64.ActiveCfg = Release|arm64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|ARM64.Build.0 = Release|arm64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|x64.ActiveCfg = Release|x64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|x64.Build.0 = Release|x64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|x86.ActiveCfg = Release|x86 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|x86.Build.0 = Release|x86 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.ReleaseStatic|ARM64.ActiveCfg = Release|arm64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.ReleaseStatic|x64.ActiveCfg = Release|x64 {EE43C990-7789-4A60-B077-BF0ED3D093A1}.ReleaseStatic|x86.ActiveCfg = Release|x86 {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Debug|ARM64.ActiveCfg = Debug|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Debug|ARM64.Build.0 = Debug|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Debug|x64.ActiveCfg = Debug|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Debug|x64.Build.0 = Debug|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Debug|x86.ActiveCfg = Debug|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Debug|x86.Build.0 = Debug|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Fuzzing|x64.ActiveCfg = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Fuzzing|x86.ActiveCfg = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Release|ARM64.ActiveCfg = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Release|ARM64.Build.0 = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Release|x64.ActiveCfg = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Release|x64.Build.0 = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Release|x86.ActiveCfg = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.Release|x86.Build.0 = Release|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {71FA29AA-9035-468B-A11D-0F0B0F5D5AF4}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Debug|ARM64.ActiveCfg = Debug|arm64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Debug|ARM64.Build.0 = Debug|arm64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Debug|x64.ActiveCfg = Debug|x64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Debug|x64.Build.0 = Debug|x64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Debug|x86.ActiveCfg = Debug|x86 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Debug|x86.Build.0 = Debug|x86 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Fuzzing|ARM64.ActiveCfg = Release|arm64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Fuzzing|x64.ActiveCfg = Release|x64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Fuzzing|x86.ActiveCfg = Release|x86 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Release|ARM64.ActiveCfg = Release|arm64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Release|ARM64.Build.0 = Release|arm64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Release|x64.ActiveCfg = Release|x64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Release|x64.Build.0 = Release|x64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Release|x86.ActiveCfg = Release|x86 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.Release|x86.Build.0 = Release|x86 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.ReleaseStatic|ARM64.ActiveCfg = Release|arm64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.ReleaseStatic|x64.ActiveCfg = Release|x64 {6597EB04-D105-49A7-A5A3-D27FE1DF895E}.ReleaseStatic|x86.ActiveCfg = Release|x86 {1F56BECB-D65D-4BBA-8788-6671B251392A}.Debug|ARM64.ActiveCfg = Debug|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Debug|ARM64.Build.0 = Debug|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Debug|x64.ActiveCfg = Debug|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Debug|x64.Build.0 = Debug|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Debug|x86.ActiveCfg = Debug|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Debug|x86.Build.0 = Debug|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Fuzzing|x64.ActiveCfg = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Fuzzing|x86.ActiveCfg = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Release|ARM64.ActiveCfg = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Release|ARM64.Build.0 = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Release|x64.ActiveCfg = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Release|x64.Build.0 = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Release|x86.ActiveCfg = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.Release|x86.Build.0 = Release|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {1F56BECB-D65D-4BBA-8788-6671B251392A}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Debug|ARM64.ActiveCfg = Debug|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Debug|ARM64.Build.0 = Debug|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Debug|x64.ActiveCfg = Debug|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Debug|x64.Build.0 = Debug|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Debug|x86.ActiveCfg = Debug|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Debug|x86.Build.0 = Debug|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Fuzzing|x64.ActiveCfg = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Fuzzing|x86.ActiveCfg = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Release|ARM64.ActiveCfg = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Release|ARM64.Build.0 = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Release|x64.ActiveCfg = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Release|x64.Build.0 = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Release|x86.ActiveCfg = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.Release|x86.Build.0 = Release|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {167F634B-A3AD-494E-8E67-B888103E35FF}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Debug|ARM64.ActiveCfg = Debug|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Debug|ARM64.Build.0 = Debug|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Debug|x64.ActiveCfg = Debug|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Debug|x64.Build.0 = Debug|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Debug|x86.ActiveCfg = Debug|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Debug|x86.Build.0 = Debug|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Fuzzing|x64.ActiveCfg = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Fuzzing|x86.ActiveCfg = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Release|ARM64.ActiveCfg = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Release|ARM64.Build.0 = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Release|x64.ActiveCfg = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Release|x64.Build.0 = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Release|x86.ActiveCfg = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.Release|x86.Build.0 = Release|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {C54F80ED-B736-49B0-9BD3-662F57024D01}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Debug|ARM64.Build.0 = Debug|ARM64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Debug|x64.ActiveCfg = Debug|x64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Debug|x64.Build.0 = Debug|x64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Debug|x86.ActiveCfg = Debug|Win32 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Debug|x86.Build.0 = Debug|Win32 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Fuzzing|x64.ActiveCfg = Release|x64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Fuzzing|x86.ActiveCfg = Release|Win32 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Release|ARM64.ActiveCfg = Release|ARM64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Release|ARM64.Build.0 = Release|ARM64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Release|x64.ActiveCfg = Release|x64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Release|x64.Build.0 = Release|x64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Release|x86.ActiveCfg = Release|Win32 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.Release|x86.Build.0 = Release|Win32 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {2268D5AD-7F2A-485A-8C4B-C574497514C9}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Debug|ARM64.ActiveCfg = Debug|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Debug|ARM64.Build.0 = Debug|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Debug|x64.ActiveCfg = Debug|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Debug|x64.Build.0 = Debug|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Debug|x86.ActiveCfg = Debug|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Debug|x86.Build.0 = Debug|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Fuzzing|x64.ActiveCfg = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Fuzzing|x86.ActiveCfg = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Release|ARM64.ActiveCfg = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Release|ARM64.Build.0 = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Release|x64.ActiveCfg = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Release|x64.Build.0 = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Release|x86.ActiveCfg = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.Release|x86.Build.0 = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.ReleaseStatic|ARM64.ActiveCfg = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.ReleaseStatic|x64.ActiveCfg = Release|Any CPU {52EC37D6-088C-40D3-AD0B-BDE8F8DAF9EB}.ReleaseStatic|x86.ActiveCfg = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Debug|ARM64.ActiveCfg = Debug|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Debug|ARM64.Build.0 = Debug|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Debug|x64.ActiveCfg = Debug|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Debug|x64.Build.0 = Debug|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Debug|x86.ActiveCfg = Debug|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Debug|x86.Build.0 = Debug|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Fuzzing|x64.ActiveCfg = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Fuzzing|x86.ActiveCfg = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Release|ARM64.ActiveCfg = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Release|ARM64.Build.0 = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Release|x64.ActiveCfg = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Release|x64.Build.0 = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Release|x86.ActiveCfg = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.Release|x86.Build.0 = Release|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|ARM64.ActiveCfg = Debug|ARM64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|ARM64.Build.0 = Debug|ARM64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|x64.ActiveCfg = Debug|x64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|x64.Build.0 = Debug|x64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|x86.ActiveCfg = Debug|Win32 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|x86.Build.0 = Debug|Win32 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Fuzzing|x64.ActiveCfg = Release|x64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Fuzzing|x86.ActiveCfg = Release|Win32 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|ARM64.ActiveCfg = Release|ARM64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|ARM64.Build.0 = Release|ARM64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|x64.ActiveCfg = Release|x64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|x64.Build.0 = Release|x64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|x86.ActiveCfg = Release|Win32 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|x86.Build.0 = Release|Win32 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|ARM64.Build.0 = Debug|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|x64.ActiveCfg = Debug|x64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|x64.Build.0 = Debug|x64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|x86.ActiveCfg = Debug|Win32 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|x86.Build.0 = Debug|Win32 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Fuzzing|x64.ActiveCfg = Release|x64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Fuzzing|x86.ActiveCfg = Release|Win32 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Release|ARM64.ActiveCfg = Release|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Release|ARM64.Build.0 = Release|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Release|x64.ActiveCfg = Release|x64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Release|x64.Build.0 = Release|x64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Release|x86.ActiveCfg = Release|Win32 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Release|x86.Build.0 = Release|Win32 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {9406322E-6272-487E-902A-9953889719EA}.Debug|ARM64.ActiveCfg = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Debug|ARM64.Build.0 = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Debug|x64.ActiveCfg = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Debug|x64.Build.0 = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Debug|x86.ActiveCfg = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Debug|x86.Build.0 = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Fuzzing|ARM64.ActiveCfg = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Fuzzing|x64.ActiveCfg = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Fuzzing|x86.ActiveCfg = Debug|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Release|ARM64.ActiveCfg = Release|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Release|ARM64.Build.0 = Release|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Release|x64.ActiveCfg = Release|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Release|x64.Build.0 = Release|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Release|x86.ActiveCfg = Release|Any CPU {9406322E-6272-487E-902A-9953889719EA}.Release|x86.Build.0 = Release|Any CPU {9406322E-6272-487E-902A-9953889719EA}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|Any CPU {9406322E-6272-487E-902A-9953889719EA}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|Any CPU {9406322E-6272-487E-902A-9953889719EA}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|Any CPU {9406322E-6272-487E-902A-9953889719EA}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {9406322E-6272-487E-902A-9953889719EA}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {9406322E-6272-487E-902A-9953889719EA}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Debug|ARM64.ActiveCfg = Debug|arm64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Debug|ARM64.Build.0 = Debug|arm64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Debug|x64.ActiveCfg = Debug|x64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Debug|x64.Build.0 = Debug|x64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Debug|x86.ActiveCfg = Debug|x86 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Debug|x86.Build.0 = Debug|x86 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Fuzzing|ARM64.ActiveCfg = Release|arm64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Fuzzing|x64.ActiveCfg = Release|x64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Fuzzing|x86.ActiveCfg = Release|x86 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Release|ARM64.ActiveCfg = Release|arm64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Release|ARM64.Build.0 = Release|arm64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Release|x64.ActiveCfg = Release|x64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Release|x64.Build.0 = Release|x64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Release|x86.ActiveCfg = Release|x86 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.Release|x86.Build.0 = Release|x86 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.ReleaseStatic|ARM64.ActiveCfg = Release|arm64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.ReleaseStatic|x64.ActiveCfg = Release|x64 {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A}.ReleaseStatic|x86.ActiveCfg = Release|x86 {33745E4A-39E2-676F-7E23-50FB43848D25}.Debug|ARM64.ActiveCfg = Debug|arm64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Debug|ARM64.Build.0 = Debug|arm64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Debug|x64.ActiveCfg = Debug|x64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Debug|x64.Build.0 = Debug|x64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Debug|x86.ActiveCfg = Debug|x86 {33745E4A-39E2-676F-7E23-50FB43848D25}.Debug|x86.Build.0 = Debug|x86 {33745E4A-39E2-676F-7E23-50FB43848D25}.Fuzzing|ARM64.ActiveCfg = Release|arm64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Fuzzing|x64.ActiveCfg = Release|x64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Fuzzing|x86.ActiveCfg = Release|x86 {33745E4A-39E2-676F-7E23-50FB43848D25}.Release|ARM64.ActiveCfg = Release|arm64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Release|ARM64.Build.0 = Release|arm64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Release|x64.ActiveCfg = Release|x64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Release|x64.Build.0 = Release|x64 {33745E4A-39E2-676F-7E23-50FB43848D25}.Release|x86.ActiveCfg = Release|x86 {33745E4A-39E2-676F-7E23-50FB43848D25}.Release|x86.Build.0 = Release|x86 {33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|ARM64.ActiveCfg = Release|arm64 {33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|ARM64.Build.0 = Release|arm64 {33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|x64.ActiveCfg = Release|x64 {33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|x64.Build.0 = Release|x64 {33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|x86.ActiveCfg = Release|x86 {33745E4A-39E2-676F-7E23-50FB43848D25}.ReleaseStatic|x86.Build.0 = Release|x86 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|ARM64.ActiveCfg = Debug|ARM64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|ARM64.Build.0 = Debug|ARM64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|x64.ActiveCfg = Debug|x64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|x64.Build.0 = Debug|x64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|x86.ActiveCfg = Debug|Win32 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Debug|x86.Build.0 = Debug|Win32 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|x64.ActiveCfg = Release|x64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Fuzzing|x86.ActiveCfg = Release|Win32 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|ARM64.ActiveCfg = Release|ARM64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|ARM64.Build.0 = Release|ARM64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|x64.ActiveCfg = Release|x64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|x64.Build.0 = Release|x64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|x86.ActiveCfg = Release|Win32 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.Release|x86.Build.0 = Release|Win32 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|ARM64.ActiveCfg = Release|ARM64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|x64.ActiveCfg = Release|x64 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94}.ReleaseStatic|x86.ActiveCfg = Release|Win32 {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Debug|ARM64.ActiveCfg = Debug|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Debug|ARM64.Build.0 = Debug|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Debug|x64.ActiveCfg = Debug|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Debug|x64.Build.0 = Debug|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Debug|x86.ActiveCfg = Debug|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Debug|x86.Build.0 = Debug|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Fuzzing|ARM64.ActiveCfg = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Fuzzing|x64.ActiveCfg = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Fuzzing|x86.ActiveCfg = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Release|ARM64.ActiveCfg = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Release|ARM64.Build.0 = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Release|x64.ActiveCfg = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Release|x64.Build.0 = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Release|x86.ActiveCfg = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.Release|x86.Build.0 = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.ReleaseStatic|ARM64.ActiveCfg = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.ReleaseStatic|x64.ActiveCfg = Release|Any CPU {5421394F-5619-4E4B-8923-F3FB30D5EFAD}.ReleaseStatic|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {6CB84692-5994-407D-B9BD-9216AF77FE83} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {6E36DDD7-1602-474E-B1D7-D0A7E1D5AD86} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {3C0269FA-E582-4CA7-9E33-3881A005CA0C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {3B8466CF-4FDD-4329-9C80-91321C4AAC99} = {EA8CD934-0702-4911-A2C5-A40600E616DE} {1622DA16-914F-4F57-A259-D5169003CC8C} = {6D7776A8-42FE-46DD-B0F8-712F35EA0C79} {3BAF989F-7F65-465B-ACE8-BAFE42D1017E} = {EA8CD934-0702-4911-A2C5-A40600E616DE} {7D05F64D-CE5A-42AA-A2C1-E91458F061CF} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {952B513F-8A00-4D74-9271-925AFB3C6252} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {2ACDE176-F13F-42FA-8159-C34FA3D37837} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {1A47951F-5C7A-4D6D-BB5F-D77484437940} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {68808357-902B-406C-8C19-E8E26A69DE8A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {409CD681-22A4-469D-88AE-CB5E4836E07A} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {B0BBBD92-943B-408F-B2B2-DBBAB4A22D23} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {31ED69A8-5310-45A9-953F-56C351D2C3E1} = {60618CAC-2995-4DF9-9914-45C6FC02C995} {8E43F982-40D5-4DF1-9044-C08047B5F43B} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {EE43C990-7789-4A60-B077-BF0ED3D093A1} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {1F56BECB-D65D-4BBA-8788-6671B251392A} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {167F634B-A3AD-494E-8E67-B888103E35FF} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {C54F80ED-B736-49B0-9BD3-662F57024D01} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {272B2B0E-40D4-4F0F-B187-519A6EF89B10} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {5A52D9FC-0059-4A4A-8196-427A7AA0D1C5} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4} = {60618CAC-2995-4DF9-9914-45C6FC02C995} {76B26B2C-602A-4AD0-9736-4162D3FCA92A} = {1A5D7A7D-5CB2-47D5-B40D-4E61CAEDC798} {A0B4F808-B190-41C4-97CB-C8EA1932F84F} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {A33223D2-550B-4D99-A53D-488B1F68683E} = {60618CAC-2995-4DF9-9914-45C6FC02C995} {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {F49C4C89-447E-4D15-B38B-5A8DCFB134AF} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {40D7CA7F-EB86-4345-9641-AD27180C559D} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {5421394F-5619-4E4B-8923-F3FB30D5EFAD} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {B978E358-D2BE-4FA7-A21A-6661F3744DD7} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B6FDB70C-A751-422C-ACD1-E35419495857} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution ManifestSchema\ManifestSchema.vcxitems*{1622da16-914f-4f57-a259-d5169003cc8c}*SharedItemsImports = 4 WinGetSchemas\WinGetSchemas.vcxitems*{1c6e0108-2860-4b17-9f7e-fa5c6c1f3d3d}*SharedItemsImports = 4 COMServer\COMServer.vcxitems*{1cc41a9a-ae66-459d-9210-1e572dd7be69}*SharedItemsImports = 4 binver\binver.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4 CertificateResources\CertificateResources.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4 COMServer\COMServer.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4 ManifestSchema\ManifestSchema.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4 WinGetSchemas\WinGetSchemas.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4 COMServer\COMServer.vcxitems*{409cd681-22a4-469d-88ae-cb5e4836e07a}*SharedItemsImports = 9 CertificateResources\CertificateResources.vcxitems*{5890d6ed-7c3b-40f3-b436-b54f640d9e65}*SharedItemsImports = 4 COMServer\COMServer.vcxitems*{5890d6ed-7c3b-40f3-b436-b54f640d9e65}*SharedItemsImports = 4 ManifestSchema\ManifestSchema.vcxitems*{5890d6ed-7c3b-40f3-b436-b54f640d9e65}*SharedItemsImports = 4 PureLib\PureLib.vcxitems*{5890d6ed-7c3b-40f3-b436-b54f640d9e65}*SharedItemsImports = 4 binver\binver.vcxitems*{5b6f90df-fd19-4bae-83d9-24dad128e777}*SharedItemsImports = 4 CertificateResources\CertificateResources.vcxitems*{5eb88068-5fb9-4e69-89b2-72dbc5e068f9}*SharedItemsImports = 4 binver\binver.vcxitems*{6e36ddd7-1602-474e-b1d7-d0a7e1d5ad86}*SharedItemsImports = 9 ManifestSchema\ManifestSchema.vcxitems*{7d05f64d-ce5a-42aa-a2c1-e91458f061cf}*SharedItemsImports = 9 CertificateResources\CertificateResources.vcxitems*{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}*SharedItemsImports = 4 ManifestSchema\ManifestSchema.vcxitems*{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}*SharedItemsImports = 4 WinGetSchemas\WinGetSchemas.vcxitems*{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}*SharedItemsImports = 4 WinGetSchemas\WinGetSchemas.vcxitems*{952b513f-8a00-4d74-9271-925afb3c6252}*SharedItemsImports = 9 PureLib\PureLib.vcxitems*{a33223d2-550b-4d99-a53d-488b1f68683e}*SharedItemsImports = 9 CertificateResources\CertificateResources.vcxitems*{b0bbbd92-943b-408f-b2b2-dbbab4a22d23}*SharedItemsImports = 9 binver\binver.vcxitems*{fb313532-38b0-4676-9303-ab200aa13576}*SharedItemsImports = 4 ManifestSchema\ManifestSchema.vcxitems*{fb313532-38b0-4676-9303-ab200aa13576}*SharedItemsImports = 4 EndGlobalSection EndGlobal ================================================ FILE: src/AppInstallerCLICore/AppInstallerCLICore.vcxproj ================================================ true true true 15.0 {1c6e0108-2860-4b17-9f7e-fa5c6c1f3d3d} Win32Proj AppInstallerCLICore 10.0.26100.0 10.0.17763.0 true Debug ARM64 Debug Win32 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Debug x64 Release x64 StaticLibrary true true false true false false true false Spectre Spectre Spectre Spectre Spectre Spectre true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 5321;%(DisableSpecificWarnings) %(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING Disabled _DEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) true true true true true true false false false Windows Windows WIN32;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) true true true false Windows MaxSpeed true true NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) true true true true true true false false false false false false true true false Windows Windows Windows MaxSpeed true true NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) true true true true true true false false false MultiThreaded MultiThreaded MultiThreaded false false false true true false Windows Windows Windows Create {5890d6ed-7c3b-40f3-b436-b54f640d9e65} {5eb88068-5fb9-4e69-89b2-72dbc5e068f9} {ca460806-5e41-4e97-9a3d-1d74b433b663} This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {4b0dcf8b-b4a1-47e5-9c28-e8a3440178e6} {00cc3138-2893-4fc4-8595-d3cf9d26be1c} {fdeb940e-93e2-4bb0-a59c-1e2d1c0588d1} {bd9d8dfe-7a37-4a53-b4e7-ddbd9694c0ab} Header Files Commands Commands Header Files Header Files Header Files Public Workflows Workflows Workflows Commands Commands Commands Workflows Header Files Header Files Commands Header Files Header Files Workflows Commands Header Files Header Files Header Files Commands Commands Commands Commands Header Files Workflows Header Files Workflows Workflows Commands Commands Commands Workflows Commands Header Files Workflows Commands Header Files Header Files Header Files Workflows Commands Workflows Workflows Workflows Workflows Public Workflows Workflows Workflows Header Files Commands Workflows Workflows Header Files Workflows Public Commands Commands Commands Header Files Commands Commands Workflows Header Files Commands Workflows Header Files Header Files Commands Workflows Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Public Commands Source Files Commands Commands Source Files Source Files Workflows Workflows Commands Commands Commands Workflows Workflows Source Files Commands Source Files Source Files Workflows Source Files Commands Source Files Source Files Commands Commands Commands Commands Source Files Workflows Source Files Workflows Workflows Commands Commands Commands Workflows Commands Source Files Workflows Commands Source Files Source Files Commands Workflows Workflows Workflows Workflows Workflows Workflows Workflows Workflows Source Files Workflows Commands Workflows Source Files Workflows Source Files Commands Commands Source Files Commands Commands Workflows Source Files Commands Commands Workflows Source Files Source Files Source Files Commands Workflows Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Commands\Configuration Source Files Commands ================================================ FILE: src/AppInstallerCLICore/Argument.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Argument.h" #include "Command.h" #include "Resources.h" #include #include namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace Settings; using namespace AppInstaller::Utility::literals; namespace { bool ContainsArgumentFromList(const Execution::Args& args, const std::vector& argTypes) { return std::any_of(argTypes.begin(), argTypes.end(), [&](Execution::Args::Type arg) { return args.Contains(arg); }); } } ArgumentCommon ArgumentCommon::ForType(Execution::Args::Type type) { // A test ensures that all types are listed here switch (type) { // Args to specify where to get app case Execution::Args::Type::Query: return { type, "query"_liv, 'q', ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery }; case Execution::Args::Type::MultiQuery: return { type, "query"_liv, 'q', ArgTypeCategory::PackageQuery | ArgTypeCategory::MultiplePackages }; case Execution::Args::Type::Manifest: return { type, "manifest"_liv, 'm', ArgTypeCategory::Manifest }; // Query filtering criteria and query behavior case Execution::Args::Type::Id: return { type, "id"_liv, ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery }; case Execution::Args::Type::Name: return { type, "name"_liv, ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery }; case Execution::Args::Type::Moniker: return { type, "moniker"_liv, ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery }; case Execution::Args::Type::Tag: return { type, "tag"_liv, ArgTypeCategory::PackageQuery }; case Execution::Args::Type::Command: return { type, "command"_liv, "cmd"_liv, ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery }; case Execution::Args::Type::Source: return { type, "source"_liv, 's', ArgTypeCategory::QuerySource }; case Execution::Args::Type::Count: return { type, "count"_liv, 'n', ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery }; case Execution::Args::Type::Exact: return { type, "exact"_liv, 'e', ArgTypeCategory::PackageQuery }; // Manifest selection behavior after an app is found case Execution::Args::Type::Version: return { type, "version"_liv, 'v', ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery }; case Execution::Args::Type::Channel: return { type, "channel"_liv, 'c', ArgTypeCategory::PackageQuery }; // Install behavior case Execution::Args::Type::Interactive: return { type, "interactive"_liv, 'i', ArgTypeCategory::InstallerBehavior | ArgTypeCategory::CopyFlagToSubContext }; case Execution::Args::Type::Silent: return { type, "silent"_liv, 'h', ArgTypeCategory::InstallerBehavior | ArgTypeCategory::CopyFlagToSubContext }; case Execution::Args::Type::Locale: return { type, "locale"_liv, ArgTypeCategory::InstallerSelection | ArgTypeCategory::CopyValueToSubContext }; case Execution::Args::Type::Log: return { type, "log"_liv, 'o', ArgTypeCategory::InstallerSelection | ArgTypeCategory::SingleInstallerBehavior }; case Execution::Args::Type::CustomSwitches: return { type, "custom"_liv, ArgTypeCategory::InstallerSelection | ArgTypeCategory::SingleInstallerBehavior }; case Execution::Args::Type::Override: return { type, "override"_liv, ArgTypeCategory::InstallerSelection | ArgTypeCategory::SingleInstallerBehavior }; case Execution::Args::Type::InstallLocation: return { type, "location"_liv, 'l', ArgTypeCategory::InstallerSelection | ArgTypeCategory::SingleInstallerBehavior }; case Execution::Args::Type::InstallScope: return { type, "scope"_liv, ArgTypeCategory::InstallerSelection | ArgTypeCategory::CopyValueToSubContext }; case Execution::Args::Type::InstallArchitecture: return { type, "architecture"_liv, 'a', ArgTypeCategory::InstallerSelection | ArgTypeCategory::CopyValueToSubContext }; case Execution::Args::Type::InstallerArchitecture: // Used for input architecture that does not need applicability check. E.g. Download, Show. return { type, "architecture"_liv, 'a', ArgTypeCategory::InstallerSelection | ArgTypeCategory::CopyValueToSubContext }; case Execution::Args::Type::InstallerType: return { type, "installer-type"_liv, ArgTypeCategory::InstallerSelection }; case Execution::Args::Type::HashOverride: return { type, "ignore-security-hash"_liv, ArgTypeCategory::InstallerBehavior | ArgTypeCategory::CopyFlagToSubContext }; case Execution::Args::Type::IgnoreLocalArchiveMalwareScan: return { type, "ignore-local-archive-malware-scan"_liv, ArgTypeCategory::InstallerBehavior | ArgTypeCategory::CopyFlagToSubContext }; case Execution::Args::Type::AcceptPackageAgreements: return { type, "accept-package-agreements"_liv, ArgTypeCategory::InstallerBehavior }; case Execution::Args::Type::Rename: return { type, "rename"_liv, 'r' }; case Execution::Args::Type::NoUpgrade: return { type, "no-upgrade"_liv, ArgTypeCategory::CopyFlagToSubContext }; case Execution::Args::Type::SkipDependencies: return { type, "skip-dependencies"_liv, ArgTypeCategory::InstallerBehavior | ArgTypeCategory::CopyFlagToSubContext, ArgTypeExclusiveSet::DependenciesConflict }; case Execution::Args::Type::DependenciesOnly: return { type, "dependencies-only"_liv, ArgTypeCategory::InstallerBehavior, ArgTypeExclusiveSet::DependenciesConflict }; case Execution::Args::Type::AllowReboot: return { type, "allow-reboot"_liv, ArgTypeCategory::InstallerBehavior | ArgTypeCategory::CopyFlagToSubContext }; // Uninstall behavior case Execution::Args::Type::Purge: return { type, "purge"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::PurgePreserve }; case Execution::Args::Type::Preserve: return { type, "preserve"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::PurgePreserve }; case Execution::Args::Type::ProductCode: return { type, "product-code"_liv, ArgTypeCategory::SinglePackageQuery }; case Execution::Args::Type::AllVersions: return { type, "all-versions"_liv, "all"_liv, ArgTypeCategory::CopyFlagToSubContext, ArgTypeExclusiveSet::AllAndTargetVersion }; case Execution::Args::Type::TargetVersion: return { type, "version"_liv, 'v', ArgTypeCategory::SinglePackageQuery, ArgTypeExclusiveSet::AllAndTargetVersion }; // Source Command case Execution::Args::Type::SourceName: return { type, "name"_liv, 'n' }; case Execution::Args::Type::SourceType: return { type, "type"_liv, 't' }; case Execution::Args::Type::SourceArg: return { type, "arg"_liv, 'a' }; case Execution::Args::Type::ForceSourceReset: return { type, "force"_liv }; case Execution::Args::Type::SourceExplicit: return { type, "explicit"_liv }; case Execution::Args::Type::SourceTrustLevel: return { type, "trust-level"_liv }; case Execution::Args::Type::SourceEditExplicit: return { type, "explicit"_liv, 'e' }; case Execution::Args::Type::SourcePriority: return { type, "priority"_liv, 'p' }; // Hash Command case Execution::Args::Type::HashFile: return { type, "file"_liv, 'f' }; case Execution::Args::Type::Msix: return { type, "msix"_liv, 'm' }; // Validate Command case Execution::Args::Type::ValidateManifest: return { type, "manifest"_liv }; case Execution::Args::Type::IgnoreWarnings: return { type, "ignore-warnings"_liv, "nowarn"_liv}; // Complete Command case Execution::Args::Type::Word: return { type, "word"_liv }; case Execution::Args::Type::CommandLine: return { type, "commandline"_liv }; case Execution::Args::Type::Position: return { type, "position"_liv }; // Export Command case Execution::Args::Type::IncludeVersions: return { type, "include-versions"_liv }; // Import Command case Execution::Args::Type::ImportFile: return { type, "import-file"_liv, 'i' }; case Execution::Args::Type::IgnoreUnavailable: return { type, "ignore-unavailable"_liv }; case Execution::Args::Type::IgnoreVersions: return { type, "ignore-versions"_liv }; // Setting Command case Execution::Args::Type::AdminSettingEnable: return { type, "enable"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::EnableDisable }; case Execution::Args::Type::AdminSettingDisable: return { type, "disable"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::EnableDisable }; case Execution::Args::Type::SettingName: return { type, "setting"_liv }; case Execution::Args::Type::SettingValue: return { type, "value"_liv }; // Upgrade command case Execution::Args::Type::All: return { type, "all"_liv, 'r', "recurse"_liv, ArgTypeCategory::MultiplePackages }; case Execution::Args::Type::IncludeUnknown: return { type, "include-unknown"_liv, 'u', "unknown"_liv, ArgTypeCategory::CopyFlagToSubContext }; case Execution::Args::Type::IncludePinned: return { type, "include-pinned"_liv, "pinned"_liv, ArgTypeCategory::CopyFlagToSubContext }; case Execution::Args::Type::UninstallPrevious: return { type, "uninstall-previous"_liv, ArgTypeCategory::InstallerBehavior | ArgTypeCategory::CopyFlagToSubContext }; // Show command case Execution::Args::Type::ListVersions: return { type, "versions"_liv }; // List command case Execution::Args::Type::Upgrade: return { type, "upgrade-available"_liv}; case Execution::Args::Type::ListDetails: return { type, "details"_liv }; // Pin command case Execution::Args::Type::GatedVersion: return { type, "version"_liv, 'v', ArgTypeCategory::None, ArgTypeExclusiveSet::PinType }; case Execution::Args::Type::BlockingPin: return { type, "blocking"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::PinType }; case Execution::Args::Type::PinInstalled: return { type, "installed"_liv, ArgTypeCategory::None }; // Error command case Execution::Args::Type::ErrorInput: return { type, "input"_liv, ArgTypeCategory::None }; // Resume command case Execution::Args::Type::ResumeId: return { type, "resume-id"_liv, 'g', ArgTypeCategory::None }; case Execution::Args::Type::IgnoreResumeLimit: return { type, "ignore-resume-limit"_liv, ArgTypeCategory::None }; // Font command case Execution::Args::Type::Family: return { type, "family"_liv, ArgTypeCategory::None }; case Execution::Args::Type::Details: return { type, "details"_liv, ArgTypeCategory::None }; // Configuration commands case Execution::Args::Type::ConfigurationFile: return { type, "file"_liv, 'f', ArgTypeCategory::ConfigurationSetChoice, ArgTypeExclusiveSet::ConfigurationSetChoice }; case Execution::Args::Type::ConfigurationAcceptWarning: return { type, "accept-configuration-agreements"_liv }; case Execution::Args::Type::ConfigurationSuppressPrologue: return { type, "suppress-initial-details"_liv }; case Execution::Args::Type::ExtendedFeaturesEnable: return { type, "enable"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::StubType }; case Execution::Args::Type::ExtendedFeaturesDisable: return { type, "disable"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::StubType }; case Execution::Args::Type::ConfigurationModulePath: return { type, "module-path"_liv }; case Execution::Args::Type::ConfigurationProcessorPath: return { type, "processor-path"_liv }; case Execution::Args::Type::ConfigurationExportPackageId: return { type, "package-id"_liv }; case Execution::Args::Type::ConfigurationExportModule: return { type, "module"_liv }; case Execution::Args::Type::ConfigurationExportResource: return { type, "resource"_liv }; case Execution::Args::Type::ConfigurationExportAll: return { type, "all"_liv, 'r', "recurse"_liv }; case Execution::Args::Type::ConfigurationHistoryItem: return { type, "history"_liv, 'h', ArgTypeCategory::ConfigurationSetChoice, ArgTypeExclusiveSet::ConfigurationSetChoice }; case Execution::Args::Type::ConfigurationHistoryRemove: return { type, "remove"_liv }; case Execution::Args::Type::ConfigurationStatusWatch: return { type, "live"_liv }; // DSCv3 resources case Execution::Args::Type::DscResourceFunctionGet: return { type, "get"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionSet: return { type, "set"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionWhatIf: return { type, "whatIf"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionTest: return { type, "test"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionDelete: return { type, "delete"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionExport: return { type, "export"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionValidate: return { type, "validate"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionResolve: return { type, "resolve"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionAdapter: return { type, "adapter"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionSchema: return { type, "schema"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; case Execution::Args::Type::DscResourceFunctionManifest: return { type, "manifest"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::DscResourceFunction }; // Download command case Execution::Args::Type::DownloadDirectory: return { type, "download-directory"_liv, 'd', ArgTypeCategory::None }; case Execution::Args::Type::Platform: return { type, "platform"_liv, ArgTypeCategory::None }; case Execution::Args::Type::SkipMicrosoftStorePackageLicense: return { type, "skip-microsoft-store-package-license"_liv, "skip-license"_liv, ArgTypeCategory::None }; case Execution::Args::Type::OSVersion: return { type, "os-version"_liv, ArgTypeCategory::None }; // Common arguments case Execution::Args::Type::NoVT: return { type, "no-vt"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::ProgressBarOption }; case Execution::Args::Type::RetroStyle: return { type, "retro"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::ProgressBarOption }; case Execution::Args::Type::RainbowStyle: return { type, "rainbow"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::ProgressBarOption }; case Execution::Args::Type::NoProgress: return { type, "no-progress"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::ProgressBarOption }; case Execution::Args::Type::Help: return { type, "help"_liv, APPINSTALLER_CLI_HELP_ARGUMENT_TEXT_CHAR }; case Execution::Args::Type::Info: return { type, "info"_liv }; case Execution::Args::Type::VerboseLogs: return { type, "verbose-logs"_liv, "verbose"_liv }; case Execution::Args::Type::DisableInteractivity: return { type, "disable-interactivity"_liv }; case Execution::Args::Type::Wait: return { type, "wait"_liv }; case Execution::Args::Type::OpenLogs: return { type, "open-logs"_liv, "logs"_liv }; case Execution::Args::Type::Force: return { type, "force"_liv, ArgTypeCategory::CopyFlagToSubContext }; case Execution::Args::Type::OutputFile: return { type, "output"_liv, 'o' }; case Execution::Args::Type::Correlation: return { type, "correlation"_liv }; case Execution::Args::Type::DependencySource: return { type, "dependency-source"_liv, ArgTypeCategory::ExtendedSource }; case Execution::Args::Type::CustomHeader: return { type, "header"_liv, ArgTypeCategory::ExtendedSource }; case Execution::Args::Type::AcceptSourceAgreements: return { type, "accept-source-agreements"_liv, ArgTypeCategory::ExtendedSource }; case Execution::Args::Type::Proxy: return { type, "proxy"_liv, ArgTypeCategory::CopyValueToSubContext, ArgTypeExclusiveSet::Proxy }; case Execution::Args::Type::NoProxy: return { type, "no-proxy"_liv, ArgTypeCategory::CopyFlagToSubContext, ArgTypeExclusiveSet::Proxy }; case Execution::Args::Type::ToolVersion: return { type, "version"_liv, 'v' }; // Authentication arguments case Execution::Args::Type::AuthenticationMode: return { type, "authentication-mode"_liv, ArgTypeCategory::CopyValueToSubContext }; case Execution::Args::Type::AuthenticationAccount: return { type, "authentication-account"_liv, ArgTypeCategory::CopyValueToSubContext }; // Used for demonstration purposes case Execution::Args::Type::ExperimentalArg: return { type, "arg"_liv }; default: THROW_HR(E_UNEXPECTED); } } std::vector ArgumentCommon::GetFromExecArgs(const Execution::Args& execArgs) { auto argTypes = execArgs.GetTypes(); std::vector result; std::transform(argTypes.begin(), argTypes.end(), std::back_inserter(result), ArgumentCommon::ForType); return result; } Argument Argument::ForType(Execution::Args::Type type) { switch (type) { case Args::Type::Query: return Argument{ type, Resource::String::QueryArgumentDescription, ArgumentType::Positional}; case Args::Type::MultiQuery: return Argument{ type, Resource::String::MultiQueryArgumentDescription, ArgumentType::Positional }.SetCountLimit(128); case Args::Type::Manifest: return Argument{ type, Resource::String::ManifestArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, Settings::TogglePolicy::Policy::LocalManifestFiles, Settings::BoolAdminSetting::LocalManifestFiles }; case Args::Type::Id: return Argument{ type, Resource::String::IdArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Name: return Argument{ type, Resource::String::NameArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Moniker: return Argument{ type, Resource::String::MonikerArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Tag: return Argument{ type, Resource::String::TagArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Command: return Argument{ type, Resource::String::CommandArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Source: return Argument{ type, Resource::String::SourceArgumentDescription, ArgumentType::Standard }; case Args::Type::DependencySource: return Argument{ type, Resource::String::DependencySourceArgumentDescription, ArgumentType::Standard }; case Args::Type::Count: return Argument{ type, Resource::String::CountArgumentDescription, ArgumentType::Standard }; case Args::Type::Exact: return Argument{ type, Resource::String::ExactArgumentDescription, ArgumentType::Flag }; case Args::Type::Version: return Argument{ type, Resource::String::VersionArgumentDescription, ArgumentType::Standard }; case Args::Type::Channel: return Argument{ type, Resource::String::ChannelArgumentDescription, ArgumentType::Standard, Argument::Visibility::Hidden }; case Args::Type::Interactive: return Argument{ type, Resource::String::InteractiveArgumentDescription, ArgumentType::Flag }; case Args::Type::Silent: return Argument{ type, Resource::String::SilentArgumentDescription, ArgumentType::Flag }; case Args::Type::Locale: return Argument{ type, Resource::String::LocaleArgumentDescription, ArgumentType::Standard }; case Args::Type::InstallArchitecture: return Argument{ type, Resource::String::ArchitectureArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::InstallerArchitecture: return Argument{ type, Resource::String::ArchitectureArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Log: return Argument{ type, Resource::String::LogArgumentDescription, ArgumentType::Standard }; case Args::Type::CustomSwitches: return Argument{ type, Resource::String::CustomSwitchesArgumentDescription, ArgumentType::Standard }; case Args::Type::Override: return Argument{ type, Resource::String::OverrideArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::InstallLocation: return Argument{ type, Resource::String::LocationArgumentDescription, ArgumentType::Standard }; case Args::Type::HashOverride: return Argument{ type, Resource::String::HashOverrideArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::HashOverride, Settings::BoolAdminSetting::InstallerHashOverride }; case Args::Type::AcceptPackageAgreements: return Argument{ type, Resource::String::AcceptPackageAgreementsArgumentDescription, ArgumentType::Flag }; case Args::Type::NoUpgrade: return Argument{ type, Resource::String::NoUpgradeArgumentDescription, ArgumentType::Flag }; case Args::Type::HashFile: return Argument{ type, Resource::String::FileArgumentDescription, ArgumentType::Positional, true }; case Args::Type::Msix: return Argument{ type, Resource::String::MsixArgumentDescription, ArgumentType::Flag }; case Args::Type::ListVersions: return Argument{ type, Resource::String::VersionsArgumentDescription, ArgumentType::Flag }; case Args::Type::Help: return Argument{ type, Resource::String::HelpArgumentDescription, ArgumentType::Flag }; case Args::Type::SkipDependencies: return Argument{ type, Resource::String::SkipDependenciesArgumentDescription, ArgumentType::Flag, false }; case Args::Type::DependenciesOnly: return Argument{ type, Resource::String::DependenciesOnlyArgumentDescription, ArgumentType::Flag, false }; case Args::Type::IgnoreLocalArchiveMalwareScan: return Argument{ type, Resource::String::IgnoreLocalArchiveMalwareScanArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::LocalArchiveMalwareScanOverride, Settings::BoolAdminSetting::LocalArchiveMalwareScanOverride }; case Args::Type::SourceName: return Argument{ type, Resource::String::SourceNameArgumentDescription, ArgumentType::Positional, false }; case Args::Type::SourceArg: return Argument{ type, Resource::String::SourceArgArgumentDescription, ArgumentType::Positional, true }; case Args::Type::SourceType: return Argument{ type, Resource::String::SourceTypeArgumentDescription, ArgumentType::Positional }; case Args::Type::SourceExplicit: return Argument{ type, Resource::String::SourceExplicitArgumentDescription, ArgumentType::Flag }; case Args::Type::SourceEditExplicit: return Argument{ type, Resource::String::SourceEditExplicitArgumentDescription, ArgumentType::Standard }; case Args::Type::SourcePriority: return Argument{ type, Resource::String::SourcePriorityArgumentDescription, ArgumentType::Standard, ExperimentalFeature::Feature::SourcePriority }; case Args::Type::SourceTrustLevel: return Argument{ type, Resource::String::SourceTrustLevelArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::ValidateManifest: return Argument{ type, Resource::String::ValidateManifestArgumentDescription, ArgumentType::Positional, true }; case Args::Type::IgnoreWarnings: return Argument{ type, Resource::String::IgnoreWarningsArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }; case Args::Type::NoVT: return Argument{ type, Resource::String::NoVTArgumentDescription, ArgumentType::Flag, Argument::Visibility::Hidden }; case Args::Type::RainbowStyle: return Argument{ type, Resource::String::RainbowArgumentDescription, ArgumentType::Flag, Argument::Visibility::Hidden }; case Args::Type::RetroStyle: return Argument{ type, Resource::String::RetroArgumentDescription, ArgumentType::Flag, Argument::Visibility::Hidden }; case Args::Type::NoProgress: return Argument{ type, Resource::String::NoProgressArgumentDescription, ArgumentType::Flag, Argument::Visibility::Hidden }; case Args::Type::VerboseLogs: return Argument{ type, Resource::String::VerboseLogsArgumentDescription, ArgumentType::Flag }; case Args::Type::CustomHeader: return Argument{ type, Resource::String::HeaderArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::AcceptSourceAgreements: return Argument{ type, Resource::String::AcceptSourceAgreementsArgumentDescription, ArgumentType::Flag }; case Args::Type::AuthenticationMode: return Argument{ type, Resource::String::AuthenticationModeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::AuthenticationAccount: return Argument{ type, Resource::String::AuthenticationAccountArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::ExperimentalArg: return Argument{ type, Resource::String::ExperimentalArgumentDescription, ArgumentType::Flag, ExperimentalFeature::Feature::ExperimentalArg }; case Args::Type::Rename: return Argument{ type, Resource::String::RenameArgumentDescription, ArgumentType::Standard, false }; case Args::Type::Purge: return Argument{ type, Resource::String::PurgeArgumentDescription, ArgumentType::Flag, false }; case Args::Type::Preserve: return Argument{ type, Resource::String::PreserveArgumentDescription, ArgumentType::Flag, false }; case Args::Type::Wait: return Argument{ type, Resource::String::WaitArgumentDescription, ArgumentType::Flag, false }; case Args::Type::ProductCode: return Argument{ type, Resource::String::ProductCodeArgumentDescription, ArgumentType::Standard, false }; case Args::Type::OpenLogs: return Argument{ type, Resource::String::OpenLogsArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }; case Args::Type::UninstallPrevious: return Argument{ type, Resource::String::UninstallPreviousArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }; case Args::Type::Force: return Argument{ type, Resource::String::ForceArgumentDescription, ArgumentType::Flag, false }; case Args::Type::DownloadDirectory: return Argument{ type, Resource::String::DownloadDirectoryArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false }; case Args::Type::SkipMicrosoftStorePackageLicense: return Argument{ type, Resource::String::SkipMicrosoftStorePackageLicenseArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help, false }; case Args::Type::Platform: return Argument{ type, Resource::String::PlatformArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false }; case Args::Type::InstallerType: return Argument{ type, Resource::String::InstallerTypeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false }; case Args::Type::ResumeId: return Argument{ type, Resource::String::ResumeIdArgumentDescription, ArgumentType::Standard, true }; case Args::Type::AllowReboot: return Argument{ type, Resource::String::AllowRebootArgumentDescription, ArgumentType::Flag }; case Args::Type::IgnoreResumeLimit: return Argument{ type, Resource::String::IgnoreResumeLimitArgumentDescription, ArgumentType::Flag, ExperimentalFeature::Feature::Resume }; case Args::Type::AllVersions: return Argument{ type, Resource::String::UninstallAllVersionsArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }; case Args::Type::TargetVersion: return Argument{ type, Resource::String::TargetVersionArgumentDescription, ArgumentType::Standard }; case Args::Type::Proxy: return Argument{ type, Resource::String::ProxyArgumentDescription, ArgumentType::Standard, TogglePolicy::Policy::ProxyCommandLineOptions, BoolAdminSetting::ProxyCommandLineOptions }; case Args::Type::NoProxy: return Argument{ type, Resource::String::NoProxyArgumentDescription, ArgumentType::Flag, TogglePolicy::Policy::ProxyCommandLineOptions, BoolAdminSetting::ProxyCommandLineOptions }; case Args::Type::Family: return Argument{ type, Resource::String::FontFamilyNameArgumentDescription, ArgumentType::Positional, false }; case Args::Type::Details: return Argument{ type, Resource::String::FontDetailsArgumentDescription, ArgumentType::Flag, false }; case Args::Type::Correlation: return Argument{ type, Resource::String::CorrelationArgumentDescription, ArgumentType::Standard, Argument::Visibility::Hidden }; case Args::Type::ListDetails: return Argument{ type, Resource::String::ListDetailsArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }; default: THROW_HR(E_UNEXPECTED); } } void Argument::GetCommon(std::vector& args) { args.push_back(ForType(Args::Type::Help)); args.push_back(ForType(Args::Type::Wait)); args.push_back(ForType(Args::Type::OpenLogs)); args.push_back(ForType(Args::Type::NoVT)); args.push_back(ForType(Args::Type::RainbowStyle)); args.push_back(ForType(Args::Type::RetroStyle)); args.push_back(ForType(Args::Type::NoProgress)); args.push_back(ForType(Args::Type::VerboseLogs)); args.push_back(ForType(Args::Type::IgnoreWarnings)); args.emplace_back(Args::Type::DisableInteractivity, Resource::String::DisableInteractivityArgumentDescription, ArgumentType::Flag, false); args.push_back(ForType(Args::Type::Proxy)); args.push_back(ForType(Args::Type::NoProxy)); args.push_back(ForType(Args::Type::Correlation)); } std::string Argument::GetUsageString() const { std::ostringstream strstr; if (Alias() != ArgumentCommon::NoAlias) { strstr << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << Alias() << ','; } if (AlternateName() != Argument::NoAlternateName) { strstr << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << AlternateName() << ','; } strstr << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << Name(); return strstr.str(); } void Argument::ValidateExclusiveArguments(const Execution::Args& args) { auto argProperties = ArgumentCommon::GetFromExecArgs(args); using ExclusiveSet_t = std::underlying_type_t; for (ExclusiveSet_t i = 1 + static_cast(ArgTypeExclusiveSet::None); i < static_cast(ArgTypeExclusiveSet::Max); i <<= 1) { std::vector argsFromSet; std::copy_if( argProperties.begin(), argProperties.end(), std::back_inserter(argsFromSet), [=](const ArgumentCommon& arg) { return static_cast(arg.ExclusiveSet) & i; }); if (argsFromSet.size() > 1) { // Create a string showing the exclusive args. std::string argsString; for (const auto& arg : argsFromSet) { if (!argsString.empty()) { argsString += '|'; } argsString += arg.Name; } throw CommandException(Resource::String::MultipleExclusiveArgumentsProvided(Utility::LocIndString{ argsString })); } } } void Argument::ValidateArgumentDependency(const Execution::Args& args, Execution::Args::Type type, Execution::Args::Type dependencyArgType) { if (args.Contains(type) && !args.Contains(dependencyArgType)) { throw CommandException(Resource::String::DependencyArgumentMissing( Utility::LocIndString{ ArgumentCommon::ForType(type).Name }, Utility::LocIndString{ ArgumentCommon::ForType(dependencyArgType).Name })); } } ArgTypeCategory Argument::GetCategoriesPresent(const Execution::Args& args) { auto argProperties = ArgumentCommon::GetFromExecArgs(args); ArgTypeCategory result = ArgTypeCategory::None; for (const auto& arg : argProperties) { result |= arg.TypeCategory; } return result; } ArgTypeCategory Argument::GetCategoriesAndValidateCommonArguments(const Execution::Args& args, bool requirePackageSelectionArg) { const auto categories = GetCategoriesPresent(args); // Commands like install require some argument to select a package if (requirePackageSelectionArg) { if (WI_AreAllFlagsClear(categories, ArgTypeCategory::Manifest | ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery)) { throw CommandException(Resource::String::NoPackageSelectionArgumentProvided); } } // If a manifest is specified, we cannot also have arguments for searching if (WI_IsFlagSet(categories, ArgTypeCategory::Manifest) && WI_IsAnyFlagSet(categories, ArgTypeCategory::PackageQuery | ArgTypeCategory::QuerySource)) { throw CommandException(Resource::String::BothManifestAndSearchQueryProvided); } // If we have multiple packages, we cannot have arguments that only make sense for a single package if (WI_IsFlagSet(categories, ArgTypeCategory::MultiplePackages) && WI_IsAnyFlagSet(categories, ArgTypeCategory::SinglePackageQuery | ArgTypeCategory::SingleInstallerBehavior)) { throw CommandException(Resource::String::ArgumentForSinglePackageProvidedWithMultipleQueries); } return categories; } Argument::Visibility Argument::GetVisibility() const { if (!ExperimentalFeature::IsEnabled(m_feature)) { return Argument::Visibility::Hidden; } if (!GroupPolicies().IsEnabled(m_groupPolicy)) { return Argument::Visibility::Hidden; } return m_visibility; } } ================================================ FILE: src/AppInstallerCLICore/Argument.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" #include "Resources.h" #include #include #include #include #include #include #include #define APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR '-' #define APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_STRING "-" #define APPINSTALLER_CLI_ARGUMENT_SPLIT_CHAR '=' #define APPINSTALLER_CLI_HELP_ARGUMENT_TEXT_CHAR '?' #define APPINSTALLER_CLI_HELP_ARGUMENT_TEXT_STRING "?" #define APPINSTALLER_CLI_HELP_ARGUMENT APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_STRING APPINSTALLER_CLI_HELP_ARGUMENT_TEXT_STRING namespace AppInstaller::CLI { using namespace AppInstaller::Utility::literals; // The type of argument. enum class ArgumentType { // Argument requires specifying the name before the value. Standard, // Argument value can be specified alone; position indicates argument name. Positional, // Only argument name can be specified and indicates a bool value. Flag, }; // Categories an arg type can belong to. // Used to reason about the arguments present without having to repeat the same // lists every time. enum class ArgTypeCategory { None = 0, // The --manifest argument. Manifest = 0x1, // Arguments for querying or selecting a package. // E.g.: --query PackageQuery = 0x2, // Arguments for querying or selecting a package, which do not work for multiple packages. // E.g.: --version SinglePackageQuery = 0x4, // Arguments for installer or uninstaller selection. // E.g.: --scope InstallerSelection = 0x8, // Arguments for installer or uninstaller behavior. // E.g.: --interactive InstallerBehavior = 0x10, // Arguments for installer or uninstaller behavior, which do not work for multiple packages. // E.g.: --override SingleInstallerBehavior = 0x20, // Arguments for selecting or interacting with the source used for initial querying // E.g.: --header QuerySource = 0x40, // Arguments that only make sense when talking about multiple packages MultiplePackages = 0x80, // Flag arguments that should be copied over when creating a sub-context CopyFlagToSubContext = 0x100, // Arguments with associated values that should be copied over when creating a sub-context CopyValueToSubContext = 0x200, // Arguments for selecting or interacting with dependencies or setting specific source behaviors // E.g.: --dependency-source // E.g.: --accept-source-agreements ExtendedSource = 0x400, // Arguments for selecting a configuration set (file or history). ConfigurationSetChoice = 0x800, }; DEFINE_ENUM_FLAG_OPERATORS(ArgTypeCategory); // Exclusive sets an argument can belong to. // Only one argument from each exclusive set is allowed at a time. enum class ArgTypeExclusiveSet : uint32_t { None = 0x0, ProgressBarOption = 0x1, EnableDisable = 0x2, PurgePreserve = 0x4, PinType = 0x8, StubType = 0x10, Proxy = 0x20, AllAndTargetVersion = 0x40, ConfigurationSetChoice = 0x80, DscResourceFunction = 0x100, DependenciesConflict = 0x200, // This must always be at the end Max }; DEFINE_ENUM_FLAG_OPERATORS(ArgTypeExclusiveSet); // An argument to a command; containing only data that is common to all its uses. // Argument extends this by adding command-specific values, like help strings. struct ArgumentCommon { // Defines an argument with no alias. constexpr static char NoAlias = '\0'; ArgumentCommon(Execution::Args::Type execArgType, Utility::LocIndView name, char alias, Utility::LocIndView alternateName, ArgTypeCategory typeCategory = ArgTypeCategory::None, ArgTypeExclusiveSet exclusiveSet = ArgTypeExclusiveSet::None) : Type(execArgType), Name(name), Alias(alias), AlternateName(alternateName), TypeCategory(typeCategory), ExclusiveSet(exclusiveSet) {} ArgumentCommon(Execution::Args::Type execArgType, Utility::LocIndView name, char alias, ArgTypeCategory typeCategory = ArgTypeCategory::None, ArgTypeExclusiveSet exclusiveSet = ArgTypeExclusiveSet::None) : Type(execArgType), Name(name), Alias(alias), TypeCategory(typeCategory), ExclusiveSet(exclusiveSet) {} ArgumentCommon(Execution::Args::Type execArgType, Utility::LocIndView name, Utility::LocIndView alternateName, ArgTypeCategory typeCategory = ArgTypeCategory::None, ArgTypeExclusiveSet exclusiveSet = ArgTypeExclusiveSet::None) : Type(execArgType), Name(name), Alias(NoAlias), AlternateName(alternateName), TypeCategory(typeCategory), ExclusiveSet(exclusiveSet) {} ArgumentCommon(Execution::Args::Type execArgType, Utility::LocIndView name, ArgTypeCategory typeCategory = ArgTypeCategory::None, ArgTypeExclusiveSet exclusiveSet = ArgTypeExclusiveSet::None) : Type(execArgType), Name(name), Alias(NoAlias), TypeCategory(typeCategory), ExclusiveSet(exclusiveSet) {} // Gets the argument for the given type. static ArgumentCommon ForType(Execution::Args::Type execArgType); static std::vector GetFromExecArgs(const Execution::Args& execArgs); Execution::Args::Type Type; Utility::LocIndView Name; char Alias; Utility::LocIndView AlternateName; ArgTypeCategory TypeCategory; ArgTypeExclusiveSet ExclusiveSet; }; // An argument to a command. struct Argument { // Controls the visibility of the field. enum class Visibility { // Shown in the example. Example, // Shown only in the table below the example. Help, // Not shown in help. Hidden, }; // Defines an argument with no alternate name constexpr static std::string_view NoAlternateName = ""; Argument(Execution::Args::Type execArgType, Resource::StringId desc) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, bool required) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_required(required) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_visibility(visibility) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, bool required) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_required(required) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, bool required) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_required(required) {} #ifndef AICLI_DISABLE_TEST_HOOKS // Constructors for arguments with custom names and aliases to use in tests Argument(std::string_view name, char alias, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type) : m_argCommon(execArgType, Utility::LocIndView{ name }, alias), m_desc(std::move(desc)), m_type(type) {} Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type) : m_argCommon(execArgType, Utility::LocIndView{ name }, alias, Utility::LocIndView{ alternateName }), m_desc(std::move(desc)), m_type(type) {} #endif ~Argument() = default; Argument(const Argument&) = default; Argument& operator=(const Argument&) = default; Argument(Argument&&) = default; Argument& operator=(Argument&&) = default; // Gets the argument for the given type. static Argument ForType(Execution::Args::Type type); // Gets the common arguments for all commands. static void GetCommon(std::vector& args); // Static argument validation helpers; throw CommandException when validation fails. // Requires that at most one argument from the list is present. static void ValidateExclusiveArguments(const Execution::Args& args); // Requires that if an argument depends on another one, it is not present without the dependency. static void ValidateArgumentDependency(const Execution::Args& args, Execution::Args::Type type, Execution::Args::Type dependencyArgType); static ArgTypeCategory GetCategoriesPresent(const Execution::Args& arg); // Requires that arguments meet common requirements static ArgTypeCategory GetCategoriesAndValidateCommonArguments(const Execution::Args& args, bool requirePackageSelectionArg = true); static void ValidateCommonArguments(const Execution::Args& args, bool requirePackageSelectionArg = true) { std::ignore = GetCategoriesAndValidateCommonArguments(args, requirePackageSelectionArg); } // Gets the argument usage string in the format of "-alias,--name". std::string GetUsageString() const; // Arguments are not localized at this time. Utility::LocIndView Name() const { return m_argCommon.Name; } char Alias() const { return m_argCommon.Alias; } std::string_view AlternateName() const { return m_argCommon.AlternateName; } Execution::Args::Type ExecArgType() const { return m_argCommon.Type; } const Resource::StringId& Description() const { return m_desc; } bool Required() const { return m_required; } ArgumentType Type() const { return m_type; } size_t Limit() const { return m_countLimit; } Argument::Visibility GetVisibility() const; Settings::ExperimentalFeature::Feature Feature() const { return m_feature; } Settings::TogglePolicy::Policy GroupPolicy() const { return m_groupPolicy; } Settings::BoolAdminSetting AdminSetting() const { return m_adminSetting; } Argument& SetRequired(bool required) { m_required = required; return *this; } Argument& SetCountLimit(size_t countLimit) { m_countLimit = countLimit; return *this; } private: // Constructors that set a Feature or Policy are private to force callers to go through the ForType() function. // This helps keep it all in one place to reduce chances of missing it somewhere. Argument(Execution::Args::Type execArgType, Resource::StringId desc, Settings::ExperimentalFeature::Feature feature) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_feature(feature) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, bool required, Settings::ExperimentalFeature::Feature feature) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_required(required), m_feature(feature) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::ExperimentalFeature::Feature feature) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_feature(feature) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, Settings::ExperimentalFeature::Feature feature) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_feature(feature) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, bool required, Settings::ExperimentalFeature::Feature feature) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_required(required), m_feature(feature) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, bool required, Settings::ExperimentalFeature::Feature feature) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_required(required), m_feature(feature) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::TogglePolicy::Policy groupPolicy, Settings::BoolAdminSetting adminSetting) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, Settings::TogglePolicy::Policy groupPolicy, Settings::BoolAdminSetting adminSetting) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} Argument(Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::ExperimentalFeature::Feature feature, Settings::TogglePolicy::Policy groupPolicy, Settings::BoolAdminSetting adminSetting) : m_argCommon(ArgumentCommon::ForType(execArgType)), m_desc(std::move(desc)), m_type(type), m_feature(feature), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} ArgumentCommon m_argCommon; Resource::StringId m_desc; bool m_required = false; ArgumentType m_type = ArgumentType::Standard; Argument::Visibility m_visibility = Argument::Visibility::Example; size_t m_countLimit = 1; Settings::ExperimentalFeature::Feature m_feature = Settings::ExperimentalFeature::Feature::None; Settings::TogglePolicy::Policy m_groupPolicy = Settings::TogglePolicy::Policy::None; Settings::BoolAdminSetting m_adminSetting = Settings::BoolAdminSetting::Unknown; }; } ================================================ FILE: src/AppInstallerCLICore/COMContext.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "COMContext.h" #include #include #include namespace AppInstaller::CLI::Execution { static constexpr std::string_view s_comLogFileNamePrefix = "WinGetCOM"sv; NullStream::NullStream() { m_nullOut.reset(new std::ostream(&m_nullStreamBuf)); m_nullIn.reset(new std::istream(&m_nullStreamBuf)); } void COMContext::AddProgressCallbackFunction(ProgressCallBackFunction&& f) { std::lock_guard lock{ m_callbackLock }; m_comProgressCallbacks.push_back(std::move(f)); } void COMContext::FireCallbacks(ReportType reportType, uint64_t current, uint64_t maximum, ProgressType progressType, ::AppInstaller::CLI::Workflow::ExecutionStage executionPhase) { // Lock around iterating through the list. Callbacks should not do long running tasks. std::lock_guard lock{ m_callbackLock }; for (auto& callback : m_comProgressCallbacks) { callback(reportType, current, maximum, progressType, executionPhase); } }; void COMContext::BeginProgress() { FireCallbacks(ReportType::BeginProgress, 0, 0, ProgressType::None, m_executionStage); }; void COMContext::OnProgress(uint64_t current, uint64_t maximum, ProgressType progressType) { FireCallbacks(ReportType::Progressing, current, maximum, progressType, m_executionStage); } void COMContext::SetProgressMessage(std::string_view) { // TODO: Consider sending message to COM progress } void COMContext::EndProgress(bool) { FireCallbacks(ReportType::EndProgress, 0, 0, ProgressType::None, m_executionStage); }; void COMContext::SetExecutionStage(CLI::Workflow::ExecutionStage executionStage) { m_executionStage = executionStage; FireCallbacks(ReportType::ExecutionPhaseUpdate, 0, 0, ProgressType::None, m_executionStage); GetThreadGlobals().GetTelemetryLogger().SetExecutionStage(static_cast(m_executionStage)); } void COMContext::SetContextLoggers(const std::wstring_view telemetryCorrelationJson, const std::string& caller) { m_correlationData = telemetryCorrelationJson; std::unique_ptr setThreadGlobalsToPreviousState = this->SetForCurrentThread(); SetLoggers(); GetThreadGlobals().GetTelemetryLogger().SetTelemetryCorrelationJson(telemetryCorrelationJson); GetThreadGlobals().GetTelemetryLogger().SetCaller(caller); GetThreadGlobals().GetTelemetryLogger().LogStartup(true); } std::wstring_view COMContext::GetCorrelationJson() { return m_correlationData; } void COMContext::SetLoggers(std::optional channel, std::optional level) { // Set up debug string logging during initialization Logging::OutputDebugStringLogger::Add(); Logging::Log().SetEnabledChannels(Logging::Channel::All); Logging::Log().SetLevel(Logging::Level::Verbose); Logging::Log().SetEnabledChannels(channel.has_value() ? channel.value() : Settings::User().Get()); Logging::Log().SetLevel(level.has_value() ? level.value() : Settings::User().Get()); // TODO: Log to file for COM API calls only when debugging in visual studio Logging::FileLogger::Add(s_comLogFileNamePrefix); Logging::OutputDebugStringLogger::Remove(); #ifndef AICLI_DISABLE_TEST_HOOKS if (!Settings::User().Get()) #endif { // Initiate the background cleanup of the log file location. Logging::FileLogger::BeginCleanup(); } Logging::TraceLogger::Add(); Logging::EnableWilFailureTelemetry(); } } ================================================ FILE: src/AppInstallerCLICore/COMContext.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AppInstallerProgress.h" #include "ExecutionContext.h" #include "Workflows/WorkflowBase.h" namespace AppInstaller::CLI::Execution { enum class ReportType : uint32_t { ExecutionPhaseUpdate, BeginProgress, Progressing, EndProgress, }; class NullStreamBuf : public std::streambuf {}; struct NullStream { NullStream(); ~NullStream() = default; protected: NullStreamBuf m_nullStreamBuf; std::unique_ptr m_nullOut; std::unique_ptr m_nullIn; }; typedef std::function ProgressCallBackFunction; // NullStream constructs the Stream parameters for Context constructor // Hence, NullStream should always precede Context in base class order of COMContext's inheritance struct COMContext : IProgressSink, NullStream, CLI::Execution::Context { // When no Console streams need involvement, construct NullStreams instead to pass to Context COMContext() : NullStream(), CLI::Execution::Context(*m_nullOut, *m_nullIn) { Reporter.SetChannel(Reporter::Channel::Disabled); Reporter.SetProgressSink(this); SetFlags(CLI::Execution::ContextFlag::DisableInteractivity); } COMContext(std::ostream& out, std::istream& in) : CLI::Execution::Context(out, in) { Reporter.SetProgressSink(this); SetFlags(CLI::Execution::ContextFlag::DisableInteractivity); } ~COMContext() = default; // IProgressSink void BeginProgress() override; void OnProgress(uint64_t current, uint64_t maximum, ProgressType type) override; void SetProgressMessage(std::string_view message) override; void EndProgress(bool) override; //Execution::Context void SetExecutionStage(CLI::Workflow::ExecutionStage executionPhase); CLI::Workflow::ExecutionStage GetExecutionStage() const { return m_executionStage; } void AddProgressCallbackFunction(ProgressCallBackFunction&& f); // Set Diagnostic and Telemetry loggers, Wil failure callback // This should be called only once per COM Server instance static void SetLoggers(std::optional channel = std::nullopt, std::optional level = std::nullopt); // Set COM call context for diagnostic and telemetry loggers // This should be called for every COMContext object instance void SetContextLoggers(const std::wstring_view telemetryCorrelationJson, const std::string& caller); std::wstring_view GetCorrelationJson(); private: void FireCallbacks(ReportType reportType, uint64_t current, uint64_t maximum, ProgressType progressType, ::AppInstaller::CLI::Workflow::ExecutionStage executionPhase); CLI::Workflow::ExecutionStage m_executionStage = CLI::Workflow::ExecutionStage::Initial; std::vector m_comProgressCallbacks; std::wstring m_correlationData = L""; std::mutex m_callbackLock; }; } ================================================ FILE: src/AppInstallerCLICore/ChannelStreams.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ChannelStreams.h" namespace AppInstaller::CLI::Execution { using namespace VirtualTerminal; size_t GetConsoleWidth() { CONSOLE_SCREEN_BUFFER_INFO consoleInfo{}; if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &consoleInfo)) { return static_cast(consoleInfo.dwSize.X); } else { return 120; } } BaseStream::BaseStream(std::ostream& out, bool enabled, bool VTEnabled) : m_out(out), m_enabled(enabled), m_VTEnabled(VTEnabled) {} BaseStream& BaseStream::operator<<(std::ostream& (__cdecl* f)(std::ostream&)) { if (m_enabled) { f(m_out); } return *this; } BaseStream& BaseStream::operator<<(const Sequence& sequence) { if (m_enabled && m_VTEnabled) { m_VTUpdated = true; m_out << sequence; } return *this; } BaseStream& BaseStream::operator<<(const ConstructedSequence& sequence) { if (m_enabled && m_VTEnabled) { m_VTUpdated = true; m_out << sequence; } return *this; } void BaseStream::SetVTEnabled(bool enabled) { m_VTEnabled = enabled; } void BaseStream::RestoreDefault() { if (m_VTUpdated) { Write(TextFormat::Default, true); } } void BaseStream::Disable() { m_enabled = false; } std::ostream& BaseStream::Get() { return m_out; } OutputStream::OutputStream(BaseStream& out, bool enabled, bool VTEnabled) : m_out(out), m_enabled(enabled), m_VTEnabled(VTEnabled) {} void OutputStream::AddFormat(const Sequence& sequence) { m_format.Append(sequence); } void OutputStream::ClearFormat() { m_format.Clear(); } void OutputStream::ApplyFormat() { // Only apply format if m_applyFormatAtOne == 1 coming into this function. if (m_applyFormatAtOne) { if (!--m_applyFormatAtOne) { m_out << m_format; } } } OutputStream& OutputStream::operator<<(std::ostream& (__cdecl* f)(std::ostream&)) { if (m_enabled) { m_out << f; } return *this; } OutputStream& OutputStream::operator<<(const Sequence& sequence) { if (m_enabled && m_VTEnabled) { // Apply format as normal to ensure that any previous format doesn't bleed through. ApplyFormat(); m_out << sequence; // An incoming sequence will be valid for 1 "standard" output after this one. // We set this to 2 to make that happen, because when it is 1, we will output // the format for the current OutputStream. m_applyFormatAtOne = 2; } return *this; } OutputStream& OutputStream::operator<<(const ConstructedSequence& sequence) { if (m_enabled && m_VTEnabled) { // Apply format as normal to ensure that any previous format doesn't bleed through. ApplyFormat(); m_out << sequence; // An incoming sequence will be valid for 1 "standard" output after this one. // We set this to 2 to make that happen, because when it is 1, we will output // the format for the current OutputStream. m_applyFormatAtOne = 2; } return *this; } OutputStream& OutputStream::operator<<(const std::filesystem::path& path) { if (m_enabled) { if (m_VTEnabled) { ApplyFormat(); } m_out << path.u8string(); } return *this; } } ================================================ FILE: src/AppInstallerCLICore/ChannelStreams.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Resources.h" #include "VTSupport.h" #include #include #include namespace AppInstaller::CLI::Execution { // Gets the current console width. size_t GetConsoleWidth(); // The base stream for all channels. struct BaseStream { BaseStream(std::ostream& out, bool enabled, bool VTEnabled); template BaseStream& operator<<(const T& t) { Write(t, false); return *this; } BaseStream& operator<<(std::ostream& (__cdecl* f)(std::ostream&)); BaseStream& operator<<(const VirtualTerminal::Sequence& sequence); BaseStream& operator<<(const VirtualTerminal::ConstructedSequence& sequence); void SetVTEnabled(bool enabled); void RestoreDefault(); void Disable(); std::ostream& Get(); private: template void Write(const T& t, bool bypass) { if (bypass || m_enabled) { m_out << t; } }; std::ostream& m_out; std::atomic_bool m_enabled; std::atomic_bool m_VTUpdated; bool m_VTEnabled; }; // Holds output formatting information. struct OutputStream { OutputStream(BaseStream& out, bool enabled, bool VTEnabled = true); // Adds a format to the current value. void AddFormat(const VirtualTerminal::Sequence& sequence); // Clears the current format value. void ClearFormat(); template OutputStream& operator<<(const T& t) { // You've found your way here because you tried to output a type that may not localized. // In order to ensure that all output is localized, only the types with specializations of // details::IsApprovedForOutput above can be output. // * If your string is a simple message, it should be put in // /src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw // and referenced in /src/AppInstallerCLICore/Resources.h. Then either output the // Resource::StringId, or load it manually and output the Resource::LocString. // * If your string is *definitely* localization independent, you can tag it as such with // the Utility::LocInd(View/String) types. // * If your string came from outside of the source code, it is best to store it in a // Utility::NormalizedString so that it has a normalized representation. This also // informs the output that there is no localized version to use. // TODO: This assertion is currently only applied to placeholders in localized strings. // Convert the rest of the code base and uncomment to enforce localization. //static_assert(details::IsApprovedForOutput>::value, "This type may not be localized, see comment for more information"); if (m_enabled) { if (m_VTEnabled) { ApplyFormat(); } m_out << t; } return *this; } OutputStream& operator<<(std::ostream& (__cdecl* f)(std::ostream&)); OutputStream& operator<<(const VirtualTerminal::Sequence& sequence); OutputStream& operator<<(const VirtualTerminal::ConstructedSequence& sequence); OutputStream& operator<<(const std::filesystem::path& path); inline bool IsEnabled() { return m_enabled; } private: // Applies the format for the stream. void ApplyFormat(); BaseStream& m_out; bool m_enabled; bool m_VTEnabled; size_t m_applyFormatAtOne = 1; VirtualTerminal::ConstructedSequence m_format; }; } ================================================ FILE: src/AppInstallerCLICore/CheckpointManager.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "CheckpointManager.h" #include "Command.h" #include "ExecutionContextData.h" #include using namespace AppInstaller::CLI; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; namespace AppInstaller::Checkpoints { // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; std::filesystem::path CheckpointManager::GetCheckpointDatabasePath(const std::string_view& resumeId, bool createCheckpointDirectory) { const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / resumeId; if (createCheckpointDirectory) { if (!std::filesystem::exists(checkpointsDirectory)) { AICLI_LOG(Repo, Info, << "Creating checkpoint database directory: " << checkpointsDirectory); std::filesystem::create_directories(checkpointsDirectory); } else { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE), !std::filesystem::is_directory(checkpointsDirectory)); } } auto recordPath = checkpointsDirectory / s_CheckpointsFileName; return recordPath; } CheckpointManager::CheckpointManager() { GUID resumeId; std::ignore = CoCreateGuid(&resumeId); m_resumeId = Utility::ConvertGuidToString(resumeId); const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId, true); m_checkpointDatabase = CheckpointDatabase::CreateNew(checkpointDatabasePath.u8string()); } CheckpointManager::CheckpointManager(const std::string& resumeId) { m_resumeId = resumeId; const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); m_checkpointDatabase = CheckpointDatabase::Open(checkpointDatabasePath.u8string()); } void CheckpointManager::CreateAutomaticCheckpoint(CLI::Execution::Context& context) { CheckpointDatabase::IdType startCheckpointId = m_checkpointDatabase->AddCheckpoint(s_AutomaticCheckpoint); Checkpoint automaticCheckpoint{ m_checkpointDatabase, startCheckpointId }; automaticCheckpoint.Set(AutomaticCheckpointData::ClientVersion, {}, AppInstaller::Runtime::GetClientVersion()); const auto& executingCommand = context.GetExecutingCommand(); if (executingCommand != nullptr) { automaticCheckpoint.Set(AutomaticCheckpointData::Command, {}, std::string{ executingCommand->FullName() }); } const auto& argTypes = context.Args.GetTypes(); for (auto type : argTypes) { const auto& argument = std::to_string(static_cast(type)); auto argumentType = Argument::ForType(type).Type(); if (argumentType == ArgumentType::Flag) { automaticCheckpoint.Set(AutomaticCheckpointData::Arguments, argument, {}); } else { const auto& values = *context.Args.GetArgs(type); automaticCheckpoint.SetMany(AutomaticCheckpointData::Arguments, argument, values); } } automaticCheckpoint.Set(AutomaticCheckpointData::ResumeCount, {}, std::to_string(0)); } void LoadCommandArgsFromAutomaticCheckpoint(CLI::Execution::Context& context, Checkpoint& automaticCheckpoint) { for (const auto& fieldName : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments)) { // Command arguments are represented as integer strings in the checkpoint record. Execution::Args::Type type = static_cast(std::stoi(fieldName)); auto argumentType = Argument::ForType(type).Type(); if (argumentType == ArgumentType::Flag) { context.Args.AddArg(type); } else { const auto& values = automaticCheckpoint.GetMany(AutomaticCheckpointData::Arguments, fieldName); for (const auto& value : values) { context.Args.AddArg(type, value); } } } } std::optional> CheckpointManager::GetAutomaticCheckpoint() { const auto& checkpointIds = m_checkpointDatabase->GetCheckpointIds(); if (checkpointIds.empty()) { return {}; } CheckpointDatabase::IdType automaticCheckpointId = checkpointIds.back(); return Checkpoint{ m_checkpointDatabase, automaticCheckpointId }; } Checkpoint CheckpointManager::CreateCheckpoint(std::string_view checkpointName) { CheckpointDatabase::IdType checkpointId = m_checkpointDatabase->AddCheckpoint(checkpointName); Checkpoint checkpoint{ m_checkpointDatabase, checkpointId }; return checkpoint; } std::vector> CheckpointManager::GetCheckpoints() { auto checkpointIds = m_checkpointDatabase->GetCheckpointIds(); if (checkpointIds.empty()) { return {}; } // Remove the last checkpoint (automatic) checkpointIds.pop_back(); std::vector> checkpoints; for (const auto& checkpointId : checkpointIds) { checkpoints.emplace_back(Checkpoint{ m_checkpointDatabase, checkpointId }); } return checkpoints; } void CheckpointManager::CleanUpDatabase() { if (m_checkpointDatabase) { m_checkpointDatabase.reset(); } if (!m_resumeId.empty()) { const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId); if (std::filesystem::exists(checkpointDatabasePath)) { const auto& checkpointDatabaseParentDirectory = checkpointDatabasePath.parent_path(); AICLI_LOG(CLI, Info, << "Deleting Checkpoint database directory: " << checkpointDatabaseParentDirectory); std::filesystem::remove_all(checkpointDatabaseParentDirectory); } } } } ================================================ FILE: src/AppInstallerCLICore/CheckpointManager.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContextData.h" #include "ExecutionContext.h" #include #include namespace AppInstaller::Checkpoints { // Reads the command arguments from the automatic checkpoint and populates the context. void LoadCommandArgsFromAutomaticCheckpoint(CLI::Execution::Context& context, Checkpoint& automaticCheckpoint); // Owns the lifetime of a checkpoint data base and creates the checkpoints. struct CheckpointManager { // Constructor that generates a new resume id and creates the checkpoint database. CheckpointManager(); // Constructor that loads the resume id and opens an existing checkpoint database. CheckpointManager(const std::string& resumeId); ~CheckpointManager() = default; // Gets the file path of the checkpoint database. static std::filesystem::path GetCheckpointDatabasePath(const std::string_view& resumeId, bool createCheckpointDirectory = false); // Gets the resume id. std::string GetResumeId() { return m_resumeId; }; // Gets the automatic checkpoint. std::optional> GetAutomaticCheckpoint(); // Creates an automatic checkpoint using the provided context. void CreateAutomaticCheckpoint(CLI::Execution::Context& context); // Gets all context data checkpoints. std::vector> GetCheckpoints(); // Creates a new context data checkpoint. Checkpoint CreateCheckpoint(std::string_view checkpointName); // Cleans up the checkpoint database. void CleanUpDatabase(); private: std::string m_resumeId; std::shared_ptr m_checkpointDatabase; }; } ================================================ FILE: src/AppInstallerCLICore/Command.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Command.h" #include "Resources.h" #include "Sixel.h" #include #include #include #include #include using namespace std::string_view_literals; using namespace AppInstaller::Utility::literals; using namespace AppInstaller::Settings; namespace AppInstaller::CLI { namespace { constexpr Utility::LocIndView s_Command_ArgName_SilentAndInteractive = "silent|interactive"_liv; void LaunchLogsIfRequested(Execution::Context& context) { try { if (context.Args.Contains(Execution::Args::Type::OpenLogs)) { // TODO: Consider possibly adding functionality that if the context contains 'Execution::Args::Type::Log' to open the path provided for the log // The above was omitted initially as a security precaution to ensure that user input to '--log' wouldn't be passed directly to ShellExecute ShellExecute(NULL, NULL, Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation).wstring().c_str(), NULL, NULL, SW_SHOWNORMAL); } } CATCH_LOG(); } } Command::Command( std::string_view name, std::vector aliases, std::string_view parent, Command::Visibility visibility, Settings::ExperimentalFeature::Feature feature, Settings::TogglePolicy::Policy groupPolicy, CommandOutputFlags outputFlags) : m_name(name), m_aliases(std::move(aliases)), m_visibility(visibility), m_feature(feature), m_groupPolicy(groupPolicy), m_outputFlags(outputFlags) { if (!parent.empty()) { m_fullName.reserve(parent.length() + 1 + name.length()); m_fullName = parent; m_fullName += ParentSplitChar; m_fullName += name; } else { m_fullName = name; } } void Command::OutputIntroHeader(Execution::Reporter& reporter) const { auto infoOut = reporter.Info(); VirtualTerminal::ConstructedSequence indent; if (reporter.SixelsEnabled()) { try { std::filesystem::path imagePath = Runtime::GetPathTo(Runtime::PathName::ImageAssets); if (!imagePath.empty()) { // This image matches the target pixel size. If changing the target size, choose the most appropriate image. imagePath /= "AppList.targetsize-40.png"; VirtualTerminal::Sixel::Image wingetIcon{ imagePath }; // Using a height of 2 to match the two lines of header. UINT imageHeightCells = 2; UINT imageWidthCells = 2 * imageHeightCells; wingetIcon.RenderSizeInCells(imageWidthCells, imageHeightCells); wingetIcon.RenderTo(infoOut); indent = VirtualTerminal::Cursor::Position::Forward(static_cast(imageWidthCells)); infoOut << VirtualTerminal::Cursor::Position::Up(static_cast(imageHeightCells) - 1); } } CATCH_LOG(); } auto productName = Runtime::IsReleaseBuild() ? Resource::String::WindowsPackageManager : Resource::String::WindowsPackageManagerPreview; infoOut << indent << productName(Runtime::GetClientVersion()) << std::endl << indent << Resource::String::MainCopyrightNotice << std::endl; } void Command::OutputHelp(Execution::Reporter& reporter, const CommandException* exception) const { // Header OutputIntroHeader(reporter); reporter.EmptyLine(); // Error if given if (exception) { reporter.Error() << exception->Message() << std::endl << std::endl; } // Description auto infoOut = reporter.Info(); infoOut << LongDescription() << std::endl << std::endl; // Example usage for this command // First create the command chain for output std::string commandChain = FullName(); size_t firstSplit = commandChain.find_first_of(ParentSplitChar); if (firstSplit == std::string::npos) { commandChain.clear(); } else { commandChain = commandChain.substr(firstSplit + 1); for (char& c : commandChain) { if (c == ParentSplitChar) { c = ' '; } } } // Output the command preamble and command chain infoOut << Resource::String::Usage("winget"_liv, Utility::LocIndView{ commandChain }); auto commandAliases = Aliases(); auto commands = GetVisibleCommands(); auto arguments = GetVisibleArguments(); bool hasArguments = false; bool hasOptions = false; // Output the command token, made optional if arguments are present. if (!commands.empty()) { infoOut << ' '; if (!arguments.empty()) { infoOut << '['; } infoOut << '<' << Resource::String::Command << '>'; if (!arguments.empty()) { infoOut << ']'; } } // Arguments are required by a test to have all positionals first. for (const auto& arg : arguments) { if (arg.Type() == ArgumentType::Positional) { hasArguments = true; infoOut << ' '; if (!arg.Required()) { infoOut << '['; } infoOut << '['; if (arg.Alias() == ArgumentCommon::NoAlias) { infoOut << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << arg.Name(); } else { infoOut << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << arg.Alias(); } infoOut << "] <"_liv << arg.Name() << '>'; if (arg.Limit() > 1) { infoOut << "..."_liv; } if (!arg.Required()) { infoOut << ']'; } } else { hasOptions = true; infoOut << " [<"_liv << Resource::String::Options << ">]"_liv; break; } } infoOut << std::endl << std::endl; if (!commandAliases.empty()) { infoOut << Resource::String::AvailableCommandAliases << std::endl; for (const auto& commandAlias : commandAliases) { infoOut << " "_liv << Execution::HelpCommandEmphasis << commandAlias << std::endl; } infoOut << std::endl; } if (!commands.empty()) { if (Name() == FullName()) { infoOut << Resource::String::AvailableCommands << std::endl; } else { infoOut << Resource::String::AvailableSubcommands << std::endl; } size_t maxCommandNameLength = 0; for (const auto& command : commands) { maxCommandNameLength = std::max(maxCommandNameLength, command->Name().length()); } for (const auto& command : commands) { size_t fillChars = (maxCommandNameLength - command->Name().length()) + 2; infoOut << " "_liv << Execution::HelpCommandEmphasis << command->Name() << Utility::LocIndString{ std::string(fillChars, ' ') } << command->ShortDescription() << std::endl; } infoOut << std::endl << Resource::String::HelpForDetails << " ["_liv << APPINSTALLER_CLI_HELP_ARGUMENT << ']' << std::endl; } if (!arguments.empty()) { if (!commands.empty()) { infoOut << std::endl; } std::vector argNames; size_t maxArgNameLength = 0; for (const auto& arg : arguments) { argNames.emplace_back(arg.GetUsageString()); maxArgNameLength = std::max(maxArgNameLength, argNames.back().length()); } if (hasArguments) { infoOut << Resource::String::AvailableArguments << std::endl; size_t i = 0; for (const auto& arg : arguments) { const std::string& argName = argNames[i++]; if (arg.Type() == ArgumentType::Positional) { size_t fillChars = (maxArgNameLength - argName.length()) + 2; infoOut << " "_liv << Execution::HelpArgumentEmphasis << argName << Utility::LocIndString{ std::string(fillChars, ' ') } << arg.Description() << std::endl; } } } if (hasOptions) { if (hasArguments) { infoOut << std::endl; } infoOut << Resource::String::AvailableOptions << std::endl; size_t i = 0; for (const auto& arg : arguments) { const std::string& argName = argNames[i++]; if (arg.Type() != ArgumentType::Positional) { size_t fillChars = (maxArgNameLength - argName.length()) + 2; infoOut << " "_liv << Execution::HelpArgumentEmphasis << argName << Utility::LocIndString{ std::string(fillChars, ' ') } << arg.Description() << std::endl; } } } } // Finally, the link to the documentation pages auto helpLink = HelpLink(); if (!helpLink.empty()) { infoOut << std::endl << Resource::String::HelpLinkPreamble(helpLink) << std::endl; } } std::unique_ptr Command::FindSubCommand(Invocation& inv) const { auto itr = inv.begin(); if (itr == inv.end() || (*itr)[0] == APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR) { // No more command arguments to check, so no command to find return {}; } auto commands = GetCommands(); if (commands.empty()) { // No more subcommands return {}; } for (auto& command : commands) { if ( Utility::CaseInsensitiveEquals(*itr, command->Name()) || Utility::CaseInsensitiveContains(command->Aliases(), *itr) ) { if (!ExperimentalFeature::IsEnabled(command->Feature())) { auto feature = ExperimentalFeature::GetFeature(command->Feature()); AICLI_LOG(CLI, Error, << "Trying to use command: " << *itr << " without enabling feature " << feature.JsonName()); throw CommandException(Resource::String::FeatureDisabledMessage(feature.JsonName())); } if (!Settings::GroupPolicies().IsEnabled(command->GroupPolicy())) { auto policy = TogglePolicy::GetPolicy(command->GroupPolicy()); AICLI_LOG(CLI, Error, << "Trying to use command: " << *itr << " disabled by group policy " << policy.RegValueName()); throw GroupPolicyException(command->GroupPolicy()); } AICLI_LOG(CLI, Info, << "Found subcommand: " << *itr); inv.consume(itr); return std::move(command); } } // The command has opted-in to be executed when it has subcommands and the next token is a positional parameter value if (m_selectCurrentCommandIfUnrecognizedSubcommandFound) { return {}; } // TODO: If we get to a large number of commands, do a fuzzy search much like git throw CommandException(Resource::String::UnrecognizedCommand(Utility::LocIndView{ *itr })); } // The argument parsing state machine. // It is broken out to enable completion to process arguments, ignore errors, // and determine the likely state of the word to be completed. struct ParseArgumentsStateMachine { ParseArgumentsStateMachine(Invocation& inv, Execution::Args& execArgs, std::vector arguments); ParseArgumentsStateMachine(const ParseArgumentsStateMachine&) = delete; ParseArgumentsStateMachine& operator=(const ParseArgumentsStateMachine&) = delete; ParseArgumentsStateMachine(ParseArgumentsStateMachine&&) = default; ParseArgumentsStateMachine& operator=(ParseArgumentsStateMachine&&) = default; // Processes the next argument from the invocation. // Returns true if there was an argument to process; // returns false if there were none. bool Step(); // Throws if there was an error during the prior step. void ThrowIfError() const; // The current state of the state machine. // An empty state indicates that the next argument can be anything. struct State { State() = default; State(Execution::Args::Type type, std::string_view arg) : m_type(type), m_arg(arg) {} State(CommandException ce) : m_exception(std::move(ce)) {} // If set, indicates that the next argument is a value for this type. const std::optional& Type() const { return m_type; } // The actual argument string associated with Type. const Utility::LocIndString& Arg() const { return m_arg; } // If set, indicates that the last argument produced an error. const std::optional& Exception() const { return m_exception; } private: std::optional m_type; Utility::LocIndString m_arg; std::optional m_exception; }; const State& GetState() const { return m_state; } bool OnlyPositionalRemain() const { return m_onlyPositionalArgumentsRemain; } // Gets the next positional argument, or nullptr if there is not one. const CLI::Argument* NextPositional(); const std::vector& Arguments() const { return m_arguments; } private: State StepInternal(); void ProcessAdjoinedValue(Execution::Args::Type type, std::string_view value); Invocation& m_invocation; Execution::Args& m_executionArgs; std::vector m_arguments; Invocation::iterator m_invocationItr; std::vector::iterator m_positionalSearchItr; bool m_onlyPositionalArgumentsRemain = false; State m_state; }; ParseArgumentsStateMachine::ParseArgumentsStateMachine(Invocation& inv, Execution::Args& execArgs, std::vector arguments) : m_invocation(inv), m_executionArgs(execArgs), m_arguments(std::move(arguments)), m_invocationItr(m_invocation.begin()), m_positionalSearchItr(m_arguments.begin()) { } bool ParseArgumentsStateMachine::Step() { if (m_invocationItr == m_invocation.end()) { return false; } m_state = StepInternal(); return true; } void ParseArgumentsStateMachine::ThrowIfError() const { if (m_state.Exception()) { throw m_state.Exception().value(); } // If the next argument was to be a value, but none was provided, convert it to an exception. else if (m_state.Type() && m_invocationItr == m_invocation.end()) { throw CommandException(Resource::String::MissingArgumentError(m_state.Arg())); } } const CLI::Argument* ParseArgumentsStateMachine::NextPositional() { // Find the next appropriate positional arg if the current itr isn't one or has hit its limit. while (m_positionalSearchItr != m_arguments.end() && (m_positionalSearchItr->Type() != ArgumentType::Positional || m_executionArgs.GetCount(m_positionalSearchItr->ExecArgType()) == m_positionalSearchItr->Limit())) { ++m_positionalSearchItr; } if (m_positionalSearchItr == m_arguments.end()) { return nullptr; } return &*m_positionalSearchItr; } // Parse arguments as such: // 1. If argument starts with a single -, only the single character alias is considered. // a. If the named argument alias (a) needs a VALUE, it can be provided in these ways: // -a=VALUE // -a VALUE // b. If the argument is a flag, additional characters after are treated as if they start // with a -, repeatedly until the end of the argument is reached. Fails if non-flags hit. // 2. If the argument starts with a double --, only the full name is considered. // a. If the named argument (arg) needs a VALUE, it can be provided in these ways: // --arg=VALUE // --arg VALUE // 3. If the argument does not start with any -, it is considered the next positional argument. // 4. If the argument is only a double --, all further arguments are only considered as positional. ParseArgumentsStateMachine::State ParseArgumentsStateMachine::StepInternal() { auto currArg = Utility::LocIndView{ *m_invocationItr }; ++m_invocationItr; // If the previous step indicated a value was needed, set it and forget it. if (m_state.Type()) { m_executionArgs.AddArg(m_state.Type().value(), currArg); return {}; } // This is a positional argument if (m_onlyPositionalArgumentsRemain || currArg.empty() || currArg[0] != APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR) { const CLI::Argument* nextPositional = NextPositional(); if (!nextPositional) { return CommandException(Resource::String::ExtraPositionalError(currArg)); } m_executionArgs.AddArg(nextPositional->ExecArgType(), currArg); } // The currentArg must not be empty, and starts with a - else if (currArg.length() == 1) { return CommandException(Resource::String::InvalidArgumentSpecifierError(currArg)); } // Now it must be at least 2 chars else if (currArg[1] != APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR) { // Parse the single character alias argument char currChar = currArg[1]; auto itr = std::find_if(m_arguments.begin(), m_arguments.end(), [&](const Argument& arg) { return (currChar == arg.Alias()); }); if (itr == m_arguments.end()) { return CommandException(Resource::String::InvalidAliasError(currArg)); } if (itr->Type() == ArgumentType::Flag) { m_executionArgs.AddArg(itr->ExecArgType()); for (size_t i = 2; i < currArg.length(); ++i) { currChar = currArg[i]; auto itr2 = std::find_if(m_arguments.begin(), m_arguments.end(), [&](const Argument& arg) { return (currChar == arg.Alias()); }); if (itr2 == m_arguments.end()) { return CommandException(Resource::String::AdjoinedNotFoundError(currArg)); } else if (itr2->Type() != ArgumentType::Flag) { return CommandException(Resource::String::AdjoinedNotFlagError(currArg)); } else { m_executionArgs.AddArg(itr2->ExecArgType()); } } } else if (currArg.length() > 2) { if (currArg[2] == APPINSTALLER_CLI_ARGUMENT_SPLIT_CHAR) { ProcessAdjoinedValue(itr->ExecArgType(), currArg.substr(3)); } else { return CommandException(Resource::String::SingleCharAfterDashError(currArg)); } } else { return { itr->ExecArgType(), currArg }; } } // The currentArg is at least 2 chars, both of which are -- else if (currArg.length() == 2) { m_onlyPositionalArgumentsRemain = true; } // The currentArg is more than 2 chars, both of which are -- else { // This is an arg name, find it and process its value if needed. // Skip the double arg identifier chars. size_t argStart = currArg.find_first_not_of(APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR); std::string_view argName = currArg.substr(argStart); bool argFound = false; bool hasValue = false; std::string_view argValue; size_t splitChar = argName.find_first_of(APPINSTALLER_CLI_ARGUMENT_SPLIT_CHAR); if (splitChar != std::string::npos) { hasValue = true; argValue = argName.substr(splitChar + 1); argName = argName.substr(0, splitChar); } for (const auto& arg : m_arguments) { if ( Utility::CaseInsensitiveEquals(argName, arg.Name()) || Utility::CaseInsensitiveEquals(argName, arg.AlternateName()) ) { if (arg.Type() == ArgumentType::Flag) { if (hasValue) { return CommandException(Resource::String::FlagContainAdjoinedError(currArg)); } m_executionArgs.AddArg(arg.ExecArgType()); } else if (hasValue) { ProcessAdjoinedValue(arg.ExecArgType(), argValue); } else { return { arg.ExecArgType(), currArg }; } argFound = true; break; } } if (!argFound) { return CommandException(Resource::String::InvalidNameError(currArg)); } } // If we get here, the next argument can be anything again. return {}; } void ParseArgumentsStateMachine::ProcessAdjoinedValue(Execution::Args::Type type, std::string_view value) { // If the adjoined value is wrapped in quotes, strip them off. if (value.length() >= 2 && value[0] == '"' && value[value.length() - 1] == '"') { value = value.substr(1, value.length() - 2); } m_executionArgs.AddArg(type, std::string{ value }); } void Command::ParseArguments(Invocation& inv, Execution::Args& execArgs) const { auto definedArgs = GetArguments(); Argument::GetCommon(definedArgs); ParseArgumentsStateMachine stateMachine{ inv, execArgs, std::move(definedArgs) }; while (stateMachine.Step()) { stateMachine.ThrowIfError(); } // Special handling for multi-query arguments: execArgs.MakeMultiQueryContainUniqueValues(); execArgs.MoveMultiQueryToSingleQueryIfNeeded(); } void Command::ValidateArguments(Execution::Args& execArgs) const { // If help is asked for, don't bother validating anything else if (execArgs.Contains(Execution::Args::Type::Help)) { return; } // Common arguments need to be validated with command arguments, as there may be common arguments blocked by Experimental Feature or Group Policy auto allArgs = GetArguments(); Argument::GetCommon(allArgs); for (const auto& arg : allArgs) { if (!Settings::GroupPolicies().IsEnabled(arg.GroupPolicy()) && execArgs.Contains(arg.ExecArgType())) { auto policy = TogglePolicy::GetPolicy(arg.GroupPolicy()); AICLI_LOG(CLI, Error, << "Trying to use argument: " << arg.Name() << " disabled by group policy " << policy.RegValueName()); throw GroupPolicyException(arg.GroupPolicy()); } if (arg.AdminSetting() != BoolAdminSetting::Unknown && !Settings::IsAdminSettingEnabled(arg.AdminSetting()) && execArgs.Contains(arg.ExecArgType())) { auto setting = Settings::AdminSettingToString(arg.AdminSetting()); AICLI_LOG(CLI, Error, << "Trying to use argument: " << arg.Name() << " disabled by admin setting " << setting); throw CommandException(Resource::String::FeatureDisabledByAdminSettingMessage(setting)); } if (!ExperimentalFeature::IsEnabled(arg.Feature()) && execArgs.Contains(arg.ExecArgType())) { auto feature = ExperimentalFeature::GetFeature(arg.Feature()); AICLI_LOG(CLI, Error, << "Trying to use argument: " << arg.Name() << " without enabling feature " << feature.JsonName()); throw CommandException(Resource::String::FeatureDisabledMessage(feature.JsonName())); } if (arg.Required() && !execArgs.Contains(arg.ExecArgType())) { throw CommandException(Resource::String::RequiredArgError(arg.Name())); } if (arg.Limit() < execArgs.GetCount(arg.ExecArgType())) { throw CommandException(Resource::String::TooManyArgError(arg.Name())); } } if (execArgs.Contains(Execution::Args::Type::Silent) && execArgs.Contains(Execution::Args::Type::Interactive)) { throw CommandException(Resource::String::TooManyBehaviorsError(s_Command_ArgName_SilentAndInteractive)); } if (execArgs.Contains(Execution::Args::Type::CustomHeader) && !execArgs.Contains(Execution::Args::Type::Source) && !execArgs.Contains(Execution::Args::Type::SourceName)) { throw CommandException(Resource::String::HeaderArgumentNotApplicableWithoutSource(Argument::ForType(Execution::Args::Type::CustomHeader).Name())); } if (execArgs.Contains(Execution::Args::Type::Count)) { try { int countRequested = std::stoi(std::string(execArgs.GetArg(Execution::Args::Type::Count))); if (countRequested < 1 || countRequested > 1000) { throw CommandException(Resource::String::CountOutOfBoundsError); } } catch (...) { throw CommandException(Resource::String::CountOutOfBoundsError); } } if (execArgs.Contains(Execution::Args::Type::InstallArchitecture)) { Utility::Architecture selectedArch = Utility::ConvertToArchitectureEnum(std::string(execArgs.GetArg(Execution::Args::Type::InstallArchitecture))); if ((selectedArch == Utility::Architecture::Unknown) || (Utility::IsApplicableArchitecture(selectedArch) == Utility::InapplicableArchitecture)) { std::vector applicableArchitectures; for (Utility::Architecture i : Utility::GetApplicableArchitectures()) { applicableArchitectures.emplace_back(Utility::ToString(i)); } auto validOptions = Utility::Join(", "_liv, applicableArchitectures); throw CommandException(Resource::String::InvalidArgumentValueError(Argument::ForType(Execution::Args::Type::InstallArchitecture).Name(), validOptions)); } } if (execArgs.Contains(Execution::Args::Type::InstallerArchitecture)) { Utility::Architecture selectedArch = Utility::ConvertToArchitectureEnum(std::string(execArgs.GetArg(Execution::Args::Type::InstallerArchitecture))); if (selectedArch == Utility::Architecture::Unknown) { std::vector applicableArchitectures; for (Utility::Architecture i : Utility::GetAllArchitectures()) { applicableArchitectures.emplace_back(Utility::ToString(i)); } auto validOptions = Utility::Join(", "_liv, applicableArchitectures); throw CommandException(Resource::String::InvalidArgumentValueError(Argument::ForType(Execution::Args::Type::InstallerArchitecture).Name(), validOptions)); } } if (execArgs.Contains(Execution::Args::Type::Locale)) { if (!Locale::IsWellFormedBcp47Tag(execArgs.GetArg(Execution::Args::Type::Locale))) { throw CommandException(Resource::String::InvalidArgumentValueErrorWithoutValidValues(Argument::ForType(Execution::Args::Type::Locale).Name())); } } if (execArgs.Contains(Execution::Args::Type::InstallScope)) { if (Manifest::ConvertToScopeEnum(execArgs.GetArg(Execution::Args::Type::InstallScope)) == Manifest::ScopeEnum::Unknown) { auto validOptions = Utility::Join(", "_liv, std::vector{ "user"_lis, "machine"_lis }); throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::InstallScope).Name, validOptions)); } } if (execArgs.Contains(Execution::Args::Type::InstallerType)) { Manifest::InstallerTypeEnum selectedInstallerType = Manifest::ConvertToInstallerTypeEnum(std::string(execArgs.GetArg(Execution::Args::Type::InstallerType))); if (selectedInstallerType == Manifest::InstallerTypeEnum::Unknown) { throw CommandException(Resource::String::InvalidArgumentValueErrorWithoutValidValues(Argument::ForType(Execution::Args::Type::InstallerType).Name())); } } if (execArgs.Contains(Execution::Args::Type::AuthenticationMode)) { if (Authentication::ConvertToAuthenticationMode(execArgs.GetArg(Execution::Args::Type::AuthenticationMode)) == Authentication::AuthenticationMode::Unknown) { auto validOptions = Utility::Join(", "_liv, std::vector{ "interactive"_lis, "silentPreferred"_lis, "silent"_lis }); throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::AuthenticationMode).Name, validOptions)); } } Argument::ValidateExclusiveArguments(execArgs); ValidateArgumentsInternal(execArgs); } // Completion can produce one of several things if the completion context is appropriate: // 1. Sub commands, if the context is immediately after this command. // 2. Argument names, if a value is not expected. // 3. Argument values, if one is expected. void Command::Complete(Execution::Context& context) const { CompletionData& data = context.Get(); const std::string& word = data.Word(); // The word we are to complete is directly after the command, thus it's sub-commands are potentials. if (data.BeforeWord().begin() == data.BeforeWord().end()) { for (const auto& command : GetCommands()) { if (word.empty() || Utility::CaseInsensitiveStartsWith(command->Name(), word)) { context.Reporter.Completion() << command->Name() << std::endl; } // Allow for command aliases to be auto-completed if (!(command->Aliases()).empty() && !word.empty()) { for (const auto& commandAlias : command->Aliases()) { if (Utility::CaseInsensitiveStartsWith(commandAlias, word)) { context.Reporter.Completion() << commandAlias << std::endl; } } } } } // Consume what remains, if any, of the preceding values to determine what type the word is. auto definedArgs = GetArguments(); Argument::GetCommon(definedArgs); ParseArgumentsStateMachine stateMachine{ data.BeforeWord(), context.Args, std::move(definedArgs) }; // We don't care if there are errors along the way, just do the best that can be done and try to // complete whatever would be next if the bad strings were simply ignored. To do that we just spin // through the state until we reach our word. while (stateMachine.Step()); const auto& state = stateMachine.GetState(); // This means that anything is possible, so argument names are on the table. if (!state.Type() && !stateMachine.OnlyPositionalRemain()) { // Use argument names if: // 1. word is empty // 2. word is just "-" // 3. word starts with "--" if (word.empty() || word == APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_STRING || Utility::CaseInsensitiveStartsWith(word, APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_STRING APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_STRING)) { for (const auto& arg : stateMachine.Arguments()) { if (word.length() <= 2 || Utility::CaseInsensitiveStartsWith(arg.Name(), word.substr(2))) { context.Reporter.Completion() << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << arg.Name() << std::endl; } } } // Use argument aliases if the word is already one; allow cycling through them. else if (Utility::CaseInsensitiveStartsWith(word, APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_STRING) && word.length() == 2) { for (const auto& arg : stateMachine.Arguments()) { if (arg.Alias() != ArgumentCommon::NoAlias) { context.Reporter.Completion() << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << arg.Alias() << std::endl; } } } } std::optional typeToComplete = state.Type(); // We are not waiting on an argument value, so the next could be a positional if the incoming word is not an argument name. // If there is one, offer to complete it. if (!typeToComplete && (word.empty() || word[0] != APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR)) { const auto* nextPositional = stateMachine.NextPositional(); if (nextPositional) { typeToComplete = nextPositional->ExecArgType(); } } // To enable more complete scenarios, also attempt to parse any arguments after the word to complete. // This will allow these later values to affect the result of the completion (for instance, if a specific source is listed). { ParseArgumentsStateMachine afterWordStateMachine{ data.AfterWord(), context.Args, stateMachine.Arguments() }; while (afterWordStateMachine.Step()); } // Let the derived command take over supplying context sensitive argument value. if (typeToComplete) { Complete(context, typeToComplete.value()); } } void Command::Complete(Execution::Context&, Execution::Args::Type) const { // Derived commands must supply context sensitive argument values. } void Command::Execute(Execution::Context& context) const { // Block any execution if winget is disabled by policy. // Override the function to bypass this. if (!Settings::GroupPolicies().IsEnabled(Settings::TogglePolicy::Policy::WinGet)) { AICLI_LOG(CLI, Error, << "WinGet is disabled by group policy"); throw Settings::GroupPolicyException::GroupPolicyException(Settings::TogglePolicy::Policy::WinGet); } AICLI_LOG(CLI, Info, << "Executing command: " << Name()); if (context.Args.Contains(Execution::Args::Type::Help)) { OutputHelp(context.Reporter); } else { ExecuteInternal(context); } // NOTE: Reboot logic will still run even if the context is terminated (not including unhandled exceptions). if (context.Args.Contains(Execution::Args::Type::AllowReboot) && WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::RebootRequired)) { context.Reporter.Warn() << Resource::String::InitiatingReboot << std::endl; if (context.Args.Contains(Execution::Args::Type::Wait)) { context.Reporter.PromptForEnter(); } if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::RegisterResume)) { // RegisterResume context flag assumes we already wrote to the RunOnce registry. // Since we are about to initiate a restart, this is no longer needed as a safety net. Reboot::UnregisterRestartForWER(); context.ClearFlags(Execution::ContextFlag::RegisterResume); } context.ClearFlags(Execution::ContextFlag::RebootRequired); if (!Reboot::InitiateReboot()) { context.Reporter.Error() << Resource::String::FailedToInitiateReboot << std::endl; } } else { LaunchLogsIfRequested(context); if (context.Args.Contains(Execution::Args::Type::Wait)) { context.Reporter.PromptForEnter(); } } } void Command::Resume(Execution::Context& context) const { context.Reporter.Error() << Resource::String::CommandDoesNotSupportResumeMessage << std::endl; AICLI_TERMINATE_CONTEXT(E_NOTIMPL); } void Command::SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value) { m_selectCurrentCommandIfUnrecognizedSubcommandFound = value; } void Command::ValidateArgumentsInternal(Execution::Args&) const { // Do nothing by default. // Commands may not need any extra validation. } void Command::ExecuteInternal(Execution::Context& context) const { context.Reporter.Error() << Resource::String::PendingWorkError << std::endl; THROW_HR(E_NOTIMPL); } Command::Visibility Command::GetVisibility() const { if (!ExperimentalFeature::IsEnabled(m_feature)) { return Command::Visibility::Hidden; } if (!Settings::GroupPolicies().IsEnabled(m_groupPolicy)) { return Command::Visibility::Hidden; } return m_visibility; } std::vector> Command::GetVisibleCommands() const { auto commands = GetCommands(); commands.erase( std::remove_if( commands.begin(), commands.end(), [](const std::unique_ptr& command) { return command->GetVisibility() == Command::Visibility::Hidden; }), commands.end()); return commands; } std::vector Command::GetVisibleArguments() const { auto arguments = GetArguments(); Argument::GetCommon(arguments); arguments.erase( std::remove_if( arguments.begin(), arguments.end(), [](const Argument& arg) { return arg.GetVisibility() == Argument::Visibility::Hidden; }), arguments.end()); return arguments; } void ExecuteWithoutLoggingSuccess(Execution::Context& context, Command* command) { try { if (!Settings::User().GetWarnings().empty() && !WI_IsFlagSet(command->GetOutputFlags(), CommandOutputFlags::IgnoreSettingsWarnings)) { context.Reporter.Warn() << Resource::String::SettingsWarnings << std::endl; } command->Execute(context); } catch (...) { context.SetTerminationHR(Workflow::HandleException(context, std::current_exception())); LaunchLogsIfRequested(context); } } int Execute(Execution::Context& context, std::unique_ptr& command) { ExecuteWithoutLoggingSuccess(context, command.get()); if (SUCCEEDED(context.GetTerminationHR())) { Logging::Telemetry().LogCommandSuccess(command->FullName()); } return context.GetTerminationHR(); } } ================================================ FILE: src/AppInstallerCLICore/Command.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Argument.h" #include "ExecutionContext.h" #include "Invocation.h" #include "Resources.h" #include #include #include #include #include #include #include #include #include #include #include namespace AppInstaller::CLI { struct CommandException { CommandException(Resource::LocString message) : m_message(std::move(message)) {} const Utility::LocIndString Message() const { return m_message; } private: Resource::LocString m_message; }; // Flags to control the behavior of the command output. enum class CommandOutputFlags : int { None = 0x0, IgnoreSettingsWarnings = 0x1, }; DEFINE_ENUM_FLAG_OPERATORS(CommandOutputFlags); struct Command { // Controls the visibility of the field. enum class Visibility { // Shown in help. Show, // Not shown in help. Hidden, }; Command(std::string_view name, std::string_view parent) : Command(name, {}, parent) {} Command(std::string_view name, std::vector aliases, std::string_view parent) : Command(name, aliases, parent, Settings::ExperimentalFeature::Feature::None) {} Command(std::string_view name, std::string_view parent, CommandOutputFlags outputFlags) : Command(name, {}, parent, Command::Visibility::Show, Settings::ExperimentalFeature::Feature::None, Settings::TogglePolicy::Policy::None, outputFlags) {} Command(std::string_view name, std::vector aliases, std::string_view parent, Command::Visibility visibility) : Command(name, aliases, parent, visibility, Settings::ExperimentalFeature::Feature::None) {} Command(std::string_view name, std::string_view parent, Settings::ExperimentalFeature::Feature feature) : Command(name, {}, parent, Command::Visibility::Show, feature) {} Command(std::string_view name, std::string_view parent, Settings::ExperimentalFeature::Feature feature, CommandOutputFlags outputFlags) : Command(name, {}, parent, Command::Visibility::Show, feature, Settings::TogglePolicy::Policy::None, outputFlags) {} Command(std::string_view name, std::vector aliases, std::string_view parent, Settings::ExperimentalFeature::Feature feature) : Command(name, aliases, parent, Command::Visibility::Show, feature) {} Command(std::string_view name, std::vector aliases, std::string_view parent, Settings::TogglePolicy::Policy groupPolicy) : Command(name, aliases, parent, Command::Visibility::Show, Settings::ExperimentalFeature::Feature::None, groupPolicy, CommandOutputFlags::None) {} Command(std::string_view name, std::vector aliases, std::string_view parent, Command::Visibility visibility, Settings::ExperimentalFeature::Feature feature) : Command(name, aliases, parent, visibility, feature, Settings::TogglePolicy::Policy::None, CommandOutputFlags::None) {} Command(std::string_view name, std::vector aliases, std::string_view parent, Command::Visibility visibility, Settings::TogglePolicy::Policy groupPolicy) : Command(name, aliases, parent, visibility, Settings::ExperimentalFeature::Feature::None, groupPolicy, CommandOutputFlags::None) {} Command(std::string_view name, std::vector aliases, std::string_view parent, Command::Visibility visibility, Settings::ExperimentalFeature::Feature feature, Settings::TogglePolicy::Policy groupPolicy, CommandOutputFlags outputFlags); virtual ~Command() = default; Command(const Command&) = default; Command& operator=(const Command&) = default; Command(Command&&) = default; Command& operator=(Command&&) = default; // The character used to split between commands and their parents in FullName. constexpr static char ParentSplitChar = ':'; std::string_view Name() const { return m_name; } const std::vector& Aliases() const& { return m_aliases; } const std::string& FullName() const { return m_fullName; } Command::Visibility GetVisibility() const; Settings::ExperimentalFeature::Feature Feature() const { return m_feature; } Settings::TogglePolicy::Policy GroupPolicy() const { return m_groupPolicy; } CommandOutputFlags GetOutputFlags() const { return m_outputFlags; } virtual std::vector> GetCommands() const { return {}; } virtual std::vector GetArguments() const { return {}; } std::vector> GetVisibleCommands() const; std::vector GetVisibleArguments() const; virtual Resource::LocString ShortDescription() const = 0; virtual Resource::LocString LongDescription() const = 0; virtual void OutputIntroHeader(Execution::Reporter& reporter) const; virtual void OutputHelp(Execution::Reporter& reporter, const CommandException* exception = nullptr) const; virtual Utility::LocIndView HelpLink() const { return {}; } virtual std::unique_ptr FindSubCommand(Invocation& inv) const; virtual void ParseArguments(Invocation& inv, Execution::Args& execArgs) const; virtual void ValidateArguments(Execution::Args& execArgs) const; virtual void Complete(Execution::Context& context) const; virtual void Complete(Execution::Context& context, Execution::Args::Type valueType) const; virtual void Execute(Execution::Context& context) const; virtual void Resume(Execution::Context& context) const; protected: void SelectCurrentCommandIfUnrecognizedSubcommandFound(bool value); virtual void ValidateArgumentsInternal(Execution::Args& execArgs) const; virtual void ExecuteInternal(Execution::Context& context) const; private: std::string_view m_name; std::string m_fullName; std::vector m_aliases; Command::Visibility m_visibility; Settings::ExperimentalFeature::Feature m_feature; Settings::TogglePolicy::Policy m_groupPolicy; CommandOutputFlags m_outputFlags; bool m_selectCurrentCommandIfUnrecognizedSubcommandFound = false; std::string m_commandArguments; }; template Container InitializeFromMoveOnly(std::initializer_list il) { using Value = typename Container::value_type; Container result; for (const auto& v : il) { result.emplace_back(std::move(*const_cast(&v))); } return result; } void ExecuteWithoutLoggingSuccess(Execution::Context& context, Command* command); int Execute(Execution::Context& context, std::unique_ptr& command); } ================================================ FILE: src/AppInstallerCLICore/Commands/COMCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "COMCommand.h" #include "Workflows/DownloadFlow.h" #include "Workflows/InstallFlow.h" #include "Workflows/PromptFlow.h" #include "Workflows/UninstallFlow.h" #include "Workflows/WorkflowBase.h" #include "Workflows/DependenciesFlow.h" #include "Workflows/RepairFlow.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Manifest; using namespace AppInstaller::Utility::literals; // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data void COMDownloadCommand::ExecuteInternal(Context& context) const { context << Workflow::InitializeInstallerDownloadAuthenticatorsMap << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << Workflow::ShowPromptsForSinglePackage(/* ensureAcceptance */ true) << Workflow::SetDownloadDirectory << Workflow::DownloadPackageDependencies << Workflow::DownloadInstaller; } // IMPORTANT: To use this command, the caller should have already executed the COMDownloadCommand void COMInstallCommand::ExecuteInternal(Context& context) const { context << Workflow::InstallDependencies << Workflow::ReverifyInstallerHash << Workflow::InstallPackageInstaller; } // IMPORTANT: To use this command, the caller should have already retrieved the InstalledPackageVersion and added it to the Context Data void COMUninstallCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::UninstallSinglePackage; } // IMPORTANT: To use this command, the caller should have already retrieved the InstalledPackageVersion and added it to the Context Data void COMRepairCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::InitializeInstallerDownloadAuthenticatorsMap << Workflow::SelectApplicableInstallerIfNecessary << Workflow::RepairSinglePackage; } } ================================================ FILE: src/AppInstallerCLICore/Commands/COMCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data struct COMDownloadCommand final : public Command { constexpr static std::string_view CommandName = "download"sv; COMDownloadCommand(std::string_view parent) : Command(CommandName, parent) {} CLI::Resource::LocString ShortDescription() const override { return {}; } CLI::Resource::LocString LongDescription() const override { return {}; } protected: void ExecuteInternal(Execution::Context& context) const override; }; // IMPORTANT: To use this command, the caller should have already retrieved the package manifest (GetManifest()) and added it to the Context Data struct COMInstallCommand final : public Command { constexpr static std::string_view CommandName = "install"sv; COMInstallCommand(std::string_view parent) : Command(CommandName, parent) {} CLI::Resource::LocString ShortDescription() const override { return {}; } CLI::Resource::LocString LongDescription() const override { return {}; } protected: void ExecuteInternal(Execution::Context& context) const override; }; // IMPORTANT: To use this command, the caller should have already retrieved the InstalledPackageVersion and added it to the Context Data struct COMUninstallCommand final : public Command { constexpr static std::string_view CommandName = "uninstall"sv; COMUninstallCommand(std::string_view parent) : Command(CommandName, parent) {} CLI::Resource::LocString ShortDescription() const override { return {}; } CLI::Resource::LocString LongDescription() const override { return {}; } protected: void ExecuteInternal(Execution::Context& context) const override; }; // IMPORTANT: To use this command, the caller should have already retrieved the InstalledPackageVersion and added it to the Context Data struct COMRepairCommand final : public Command { constexpr static std::string_view CommandName = "repair"sv; COMRepairCommand(std::string_view parent) : Command(CommandName, parent) {} CLI::Resource::LocString ShortDescription() const override { return {}; } CLI::Resource::LocString LongDescription() const override { return {}; } protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/CompleteCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "CompleteCommand.h" #include "RootCommand.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace std::string_view_literals; using namespace Execution; std::vector CompleteCommand::GetArguments() const { return { Argument{ Args::Type::Word, Resource::String::WordArgumentDescription, ArgumentType::Standard, Argument::Visibility::Example, true }, Argument{ Args::Type::CommandLine, Resource::String::CommandLineArgumentDescription, ArgumentType::Standard, Argument::Visibility::Example, true }, Argument{ Args::Type::Position, Resource::String::PositionArgumentDescription, ArgumentType::Standard, Argument::Visibility::Example, true }, }; } Resource::LocString CompleteCommand::ShortDescription() const { return { Resource::String::CompleteCommandShortDescription }; } Resource::LocString CompleteCommand::LongDescription() const { return { Resource::String::CompleteCommandLongDescription }; } Utility::LocIndView CompleteCommand::HelpLink() const { return "https://aka.ms/winget-command-complete"_liv; } void CompleteCommand::ExecuteInternal(Execution::Context& context) const { try { CompletionData data{ context.Args.GetArg(Args::Type::Word), context.Args.GetArg(Args::Type::CommandLine), context.Args.GetArg(Args::Type::Position) }; std::unique_ptr command = std::make_unique(); std::unique_ptr subCommand = command->FindSubCommand(data.BeforeWord()); while (subCommand) { command = std::move(subCommand); subCommand = command->FindSubCommand(data.BeforeWord()); } // Create a new Context to execute the Complete from auto subContextPtr = context.CreateSubContext(); Context& subContext = *subContextPtr; auto previousThreadGlobals = subContext.SetForCurrentThread(); subContext.Reporter.SetChannel(Execution::Reporter::Channel::Completion); subContext.Add(std::move(data)); // Disable all telemetry while doing a completion Logging::DisableTelemetryScope disable; AICLI_LOG(CLI, Info, << "Complete handing off to command " << command->FullName()); command->Complete(subContext); } catch (const CommandException& ce) { AICLI_LOG(CLI, Info, << "Error encountered during completion, ignoring: " << ce.Message()); } catch (const Settings::GroupPolicyException& e) { AICLI_LOG(CLI, Info, << "Error encountered during completion, ignoring: Blocked by Group Policy " << Settings::TogglePolicy::GetPolicy(e.Policy()).RegValueName()); } catch (...) { AICLI_LOG(CLI, Info, << "Error encountered during completion, ignoring..."); } } } ================================================ FILE: src/AppInstallerCLICore/Commands/CompleteCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { // Command to enable tab completion scenarios, including allowing them to // be context sensitive in their data output. struct CompleteCommand final : public Command { CompleteCommand(std::string_view parent) : Command("complete", {}, parent, Visibility::Hidden) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigureCommand.h" #include "ConfigureListCommand.h" #include "ConfigureShowCommand.h" #include "ConfigureTestCommand.h" #include "ConfigureValidateCommand.h" #include "ConfigureExportCommand.h" #include "Workflows/ConfigurationFlow.h" #include "Workflows/MSStoreInstallerHandler.h" #include "ConfigurationCommon.h" using namespace AppInstaller::CLI::Workflow; namespace AppInstaller::CLI { ConfigureCommand::ConfigureCommand(std::string_view parent) : Command("configure", { "configuration", "dsc"}, parent, Settings::TogglePolicy::Policy::Configuration) { SelectCurrentCommandIfUnrecognizedSubcommandFound(true); } std::vector> ConfigureCommand::GetCommands() const { return InitializeFromMoveOnly>>({ std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), }); } std::vector ConfigureCommand::GetArguments() const { return { Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional }, Argument{ Execution::Args::Type::ConfigurationModulePath, Resource::String::ConfigurationModulePath, ArgumentType::Positional }, Argument{ Execution::Args::Type::ConfigurationProcessorPath, Resource::String::ConfigurationProcessorPath, ArgumentType::Standard, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ConfigurationHistoryItem, Resource::String::ConfigurationHistoryItemArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ConfigurationAcceptWarning, Resource::String::ConfigurationAcceptWarningArgumentDescription, ArgumentType::Flag }, Argument{ Execution::Args::Type::ConfigurationSuppressPrologue, Resource::String::ConfigurationSuppressPrologueArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ExtendedFeaturesEnable, Resource::String::ExtendedFeaturesEnableMessage, ArgumentType::Flag, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ExtendedFeaturesDisable, Resource::String::ExtendedFeaturesDisableMessage, ArgumentType::Flag, Argument::Visibility::Help }, }; } Resource::LocString ConfigureCommand::ShortDescription() const { return { Resource::String::ConfigureCommandShortDescription }; } Resource::LocString ConfigureCommand::LongDescription() const { return { Resource::String::ConfigureCommandLongDescription }; } Utility::LocIndView ConfigureCommand::HelpLink() const { return "https://aka.ms/winget-command-configure"_liv; } void ConfigureCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::ExtendedFeaturesEnable)) { context << EnableExtendedFeatures; } else if (context.Args.Contains(Execution::Args::Type::ExtendedFeaturesDisable)) { context << DisableExtendedFeatures; } else { context << VerifyIsFullPackage << VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) << CreateConfigurationProcessorWithoutFactory << OpenConfigurationSet << CreateConfigurationProcessor << ShowConfigurationSet << ShowConfigurationSetConflicts << ConfirmConfigurationProcessing(true) << ApplyConfigurationSet; } } void ConfigureCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { if (execArgs.Contains(Execution::Args::Type::ExtendedFeaturesEnable) || execArgs.Contains(Execution::Args::Type::ExtendedFeaturesDisable)) { if (execArgs.GetArgsCount() > 1) { throw CommandException(Resource::String::ExtendedFeaturesEnableArgumentError); } } else { Configuration::ValidateCommonArguments(execArgs, true); } } void ConfigureCommand::Complete(Execution::Context& context, Execution::Args::Type argType) const { if (argType == Execution::Args::Type::ConfigurationHistoryItem) { context << CompleteConfigurationHistoryItem; } } } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ConfigureCommand final : public Command { ConfigureCommand(std::string_view parent); std::vector> GetCommands() const override; std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void Complete(Execution::Context& context, Execution::Args::Type argType) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureListCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigureListCommand.h" #include "Workflows/ConfigurationFlow.h" #include "Workflows/MSStoreInstallerHandler.h" #include "ConfigurationCommon.h" using namespace AppInstaller::CLI::Workflow; namespace AppInstaller::CLI { std::vector ConfigureListCommand::GetArguments() const { return { Argument{ Execution::Args::Type::ConfigurationHistoryItem, Resource::String::ConfigurationHistoryItemArgumentDescription, ArgumentType::Standard }, Argument{ Execution::Args::Type::OutputFile, Resource::String::OutputFileArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ConfigurationHistoryRemove, Resource::String::ConfigurationHistoryRemoveArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ConfigurationStatusWatch, Resource::String::ConfigurationStatusWatchArgumentDescription, ArgumentType::Flag, Argument::Visibility::Hidden }, }; } Resource::LocString ConfigureListCommand::ShortDescription() const { return { Resource::String::ConfigureListCommandShortDescription }; } Resource::LocString ConfigureListCommand::LongDescription() const { return { Resource::String::ConfigureListCommandLongDescription }; } Utility::LocIndView ConfigureListCommand::HelpLink() const { return "https://aka.ms/winget-command-configure#list"_liv; } void ConfigureListCommand::ExecuteInternal(Execution::Context& context) const { context << VerifyIsFullPackage << CreateConfigurationProcessorWithoutFactory << GetConfigurationSetHistory; if (context.Args.Contains(Execution::Args::Type::ConfigurationHistoryItem)) { context << SelectSetFromHistory; if (context.Args.Contains(Execution::Args::Type::OutputFile)) { context << SerializeConfigurationSetHistory; } if (context.Args.Contains(Execution::Args::Type::ConfigurationHistoryRemove)) { context << RemoveConfigurationSetHistory; } else { context << ShowSingleConfigurationSetHistory; } } else if (context.Args.Contains(Execution::Args::Type::ConfigurationStatusWatch)) { context << MonitorConfigurationStatus; } else { context << ShowConfigurationSetHistory; } } void ConfigureListCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { if ((execArgs.Contains(Execution::Args::Type::ConfigurationHistoryRemove) || execArgs.Contains(Execution::Args::Type::OutputFile)) && !execArgs.Contains(Execution::Args::Type::ConfigurationHistoryItem)) { throw CommandException(Resource::String::RequiredArgError(ArgumentCommon::ForType(Execution::Args::Type::ConfigurationHistoryItem).Name)); } } void ConfigureListCommand::Complete(Execution::Context& context, Execution::Args::Type argType) const { if (argType == Execution::Args::Type::ConfigurationHistoryItem) { context << CompleteConfigurationHistoryItem; } } } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureListCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ConfigureListCommand final : public Command { ConfigureListCommand(std::string_view parent) : Command("list", { "ls" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void Complete(Execution::Context& context, Execution::Args::Type argType) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureShowCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigureShowCommand.h" #include "Workflows/ConfigurationFlow.h" #include "Workflows/MSStoreInstallerHandler.h" #include "ConfigurationCommon.h" using namespace AppInstaller::CLI::Workflow; namespace AppInstaller::CLI { std::vector ConfigureShowCommand::GetArguments() const { return { // Required for now, make exclusive when history implemented Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional }, Argument{ Execution::Args::Type::ConfigurationModulePath, Resource::String::ConfigurationModulePath, ArgumentType::Positional }, Argument{ Execution::Args::Type::ConfigurationProcessorPath, Resource::String::ConfigurationProcessorPath, ArgumentType::Standard, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ConfigurationHistoryItem, Resource::String::ConfigurationHistoryItemArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, }; } Resource::LocString ConfigureShowCommand::ShortDescription() const { return { Resource::String::ConfigureShowCommandShortDescription }; } Resource::LocString ConfigureShowCommand::LongDescription() const { return { Resource::String::ConfigureShowCommandLongDescription }; } Utility::LocIndView ConfigureShowCommand::HelpLink() const { return "https://aka.ms/winget-command-configure#show"_liv; } void ConfigureShowCommand::ExecuteInternal(Execution::Context& context) const { context << VerifyIsFullPackage << VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) << CreateConfigurationProcessorWithoutFactory << OpenConfigurationSet << CreateConfigurationProcessor << ShowConfigurationSet; } void ConfigureShowCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { Configuration::ValidateCommonArguments(execArgs, true); } void ConfigureShowCommand::Complete(Execution::Context& context, Execution::Args::Type argType) const { if (argType == Execution::Args::Type::ConfigurationHistoryItem) { context << CompleteConfigurationHistoryItem; } } } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureShowCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ConfigureShowCommand final : public Command { ConfigureShowCommand(std::string_view parent) : Command("show", { "view" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void Complete(Execution::Context& context, Execution::Args::Type argType) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureTestCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigureTestCommand.h" #include "Workflows/ConfigurationFlow.h" #include "Workflows/MSStoreInstallerHandler.h" #include "ConfigurationCommon.h" using namespace AppInstaller::CLI::Workflow; namespace AppInstaller::CLI { std::vector ConfigureTestCommand::GetArguments() const { return { Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional }, Argument{ Execution::Args::Type::ConfigurationModulePath, Resource::String::ConfigurationModulePath, ArgumentType::Positional }, Argument{ Execution::Args::Type::ConfigurationProcessorPath, Resource::String::ConfigurationProcessorPath, ArgumentType::Standard, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ConfigurationHistoryItem, Resource::String::ConfigurationHistoryItemArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ConfigurationSuppressPrologue, Resource::String::ConfigurationSuppressPrologueArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ConfigurationAcceptWarning, Resource::String::ConfigurationAcceptWarningArgumentDescription, ArgumentType::Flag }, }; } Resource::LocString ConfigureTestCommand::ShortDescription() const { return { Resource::String::ConfigureTestCommandShortDescription }; } Resource::LocString ConfigureTestCommand::LongDescription() const { return { Resource::String::ConfigureTestCommandLongDescription }; } Utility::LocIndView ConfigureTestCommand::HelpLink() const { return "https://aka.ms/winget-command-configure#test"_liv; } void ConfigureTestCommand::ExecuteInternal(Execution::Context& context) const { context << VerifyIsFullPackage << VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) << CreateConfigurationProcessorWithoutFactory << OpenConfigurationSet << CreateConfigurationProcessor << ShowConfigurationSet << ShowConfigurationSetConflicts << ConfirmConfigurationProcessing(false) << TestConfigurationSet; } void ConfigureTestCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { Configuration::ValidateCommonArguments(execArgs, true); } void ConfigureTestCommand::Complete(Execution::Context& context, Execution::Args::Type argType) const { if (argType == Execution::Args::Type::ConfigurationHistoryItem) { context << CompleteConfigurationHistoryItem; } } } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureTestCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ConfigureTestCommand final : public Command { ConfigureTestCommand(std::string_view parent) : Command("test", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void Complete(Execution::Context& context, Execution::Args::Type argType) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureValidateCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigureValidateCommand.h" #include "Workflows/ConfigurationFlow.h" #include "Workflows/MSStoreInstallerHandler.h" #include "ConfigurationCommon.h" using namespace AppInstaller::CLI::Workflow; namespace AppInstaller::CLI { std::vector ConfigureValidateCommand::GetArguments() const { return { Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional, true }, Argument{ Execution::Args::Type::ConfigurationModulePath, Resource::String::ConfigurationModulePath, ArgumentType::Positional }, Argument{ Execution::Args::Type::ConfigurationProcessorPath, Resource::String::ConfigurationProcessorPath, ArgumentType::Standard, Argument::Visibility::Help }, }; } Resource::LocString ConfigureValidateCommand::ShortDescription() const { return { Resource::String::ConfigureValidateCommandShortDescription }; } Resource::LocString ConfigureValidateCommand::LongDescription() const { return { Resource::String::ConfigureValidateCommandLongDescription }; } Utility::LocIndView ConfigureValidateCommand::HelpLink() const { return "https://aka.ms/winget-command-configure#validate"_liv; } void ConfigureValidateCommand::ExecuteInternal(Execution::Context& context) const { context << VerifyIsFullPackage << VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) << CreateConfigurationProcessorWithoutFactory << OpenConfigurationSet << CreateConfigurationProcessor << ValidateConfigurationSetSemantics << ValidateConfigurationSetUnitProcessors << ValidateConfigurationSetUnitContents << ValidateAllGoodMessage; } void ConfigureValidateCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { Configuration::ValidateCommonArguments(execArgs); } } ================================================ FILE: src/AppInstallerCLICore/Commands/ConfigureValidateCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ConfigureValidateCommand final : public Command { ConfigureValidateCommand(std::string_view parent) : Command("validate", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DebugCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #if _DEBUG #include "DebugCommand.h" #include #include #include "AppInstallerDownloader.h" #include "Sixel.h" using namespace AppInstaller::CLI::Execution; namespace AppInstaller::CLI { namespace { std::string MakeInterfaceNameAttribute(std::wstring_view name) { std::string result = Utility::ConvertToUTF8(name); Utility::FindAndReplace(result, "<", "<"); Utility::FindAndReplace(result, ">", ">"); return result; } std::string MakeIIDAttribute(const winrt::guid& guid) { std::string result; wchar_t buffer[256]; if (StringFromGUID2(guid, buffer, ARRAYSIZE(buffer))) { result = AppInstaller::Utility::ConvertToUTF8(buffer); result = result.substr(1, result.length() - 2); } else { result = "error"; } return result; } template void OutputProxyStubInterfaceRegistration(Execution::Context& context) { context.Reporter.Info() << "()) << "\" InterfaceId=\"" << MakeIIDAttribute(winrt::guid_of()) << "\" />" << std::endl; } template void OutputIIDMapping(Execution::Context& context) { context.Reporter.Info() << Utility::ConvertToUTF8(winrt::name_of()) << " == " << winrt::guid_of() << std::endl; } } std::vector> DebugCommand::GetCommands() const { return InitializeFromMoveOnly>>({ std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), }); } Resource::LocString DebugCommand::ShortDescription() const { return Utility::LocIndString("Debug only dev commands"sv); } Resource::LocString DebugCommand::LongDescription() const { return Utility::LocIndString("Commands that are useful in debugging and development."sv); } void DebugCommand::ExecuteInternal(Execution::Context& context) const { OutputHelp(context.Reporter); } Resource::LocString DumpProxyStubRegistrationsCommand::ShortDescription() const { return Utility::LocIndString("Dump proxy-stub registrations"sv); } Resource::LocString DumpProxyStubRegistrationsCommand::LongDescription() const { return Utility::LocIndString("Dump proxy-stub registrations for WinRT interfaces to be place in the manifest."sv); } void DumpProxyStubRegistrationsCommand::ExecuteInternal(Execution::Context& context) const { OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration>(context); OutputProxyStubInterfaceRegistration(context); OutputProxyStubInterfaceRegistration(context); OutputProxyStubInterfaceRegistration(context); OutputProxyStubInterfaceRegistration(context); OutputProxyStubInterfaceRegistration(context); OutputProxyStubInterfaceRegistration(context); OutputProxyStubInterfaceRegistration(context); OutputProxyStubInterfaceRegistration(context); // TODO: Fix the layering inversion created by the COM deployment API (probably in order to operate winget.exe against the COM server). // Then this code can just have a CppWinRT reference to the deployment API and spit out the interface registrations just like for configuration. HMODULE module = nullptr; if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(&MakeInterfaceNameAttribute), &module)) { return; } // TODO: Have a PRIVATE export from WindowsPackageManager that returns a set of names and IIDs to include from the Deployment API surface } Resource::LocString DumpInterestingIIDsCommand::ShortDescription() const { return Utility::LocIndString("Dump some IIDs"sv); } Resource::LocString DumpInterestingIIDsCommand::LongDescription() const { return Utility::LocIndString("Dump some IIDs that might be useful."sv); } void DumpInterestingIIDsCommand::ExecuteInternal(Execution::Context& context) const { OutputIIDMapping(context); OutputIIDMapping(context); OutputIIDMapping(context); } Resource::LocString DumpErrorResourceCommand::ShortDescription() const { return Utility::LocIndString("Dump error resources"sv); } Resource::LocString DumpErrorResourceCommand::LongDescription() const { return Utility::LocIndString("Dump the error information as resources."sv); } void DumpErrorResourceCommand::ExecuteInternal(Execution::Context& context) const { auto info = context.Reporter.Info(); // // Another installation is already in progress. Try again later. // for (const auto& error : Errors::GetWinGetErrors()) { info << " Symbol() << "\" xml:space=\"preserve\">\n" " " << error->GetDescription() << "\n" " " << std::endl; } } #define WINGET_DEBUG_SIXEL_FILE Args::Type::Manifest #define WINGET_DEBUG_SIXEL_ASPECT_RATIO Args::Type::AcceptPackageAgreements #define WINGET_DEBUG_SIXEL_TRANSPARENT Args::Type::AcceptSourceAgreements #define WINGET_DEBUG_SIXEL_COLOR_COUNT Args::Type::ConfigurationAcceptWarning #define WINGET_DEBUG_SIXEL_WIDTH Args::Type::AdminSettingEnable #define WINGET_DEBUG_SIXEL_HEIGHT Args::Type::AllowReboot #define WINGET_DEBUG_SIXEL_STRETCH Args::Type::AllVersions #define WINGET_DEBUG_SIXEL_REPEAT Args::Type::Name #define WINGET_DEBUG_SIXEL_OUT_FILE Args::Type::BlockingPin std::vector ShowSixelCommand::GetArguments() const { return { Argument{ "file", 'f', WINGET_DEBUG_SIXEL_FILE, Resource::String::SourceListUpdatedNever, ArgumentType::Positional }, Argument{ "aspect-ratio", 'a', WINGET_DEBUG_SIXEL_ASPECT_RATIO, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, Argument{ "transparent", 't', WINGET_DEBUG_SIXEL_TRANSPARENT, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, Argument{ "color-count", 'c', WINGET_DEBUG_SIXEL_COLOR_COUNT, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, Argument{ "width", 'w', WINGET_DEBUG_SIXEL_WIDTH, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, Argument{ "height", 'h', WINGET_DEBUG_SIXEL_HEIGHT, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, Argument{ "stretch", 's', WINGET_DEBUG_SIXEL_STRETCH, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, Argument{ "repeat", 'r', WINGET_DEBUG_SIXEL_REPEAT, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, Argument{ "out-file", 'o', WINGET_DEBUG_SIXEL_OUT_FILE, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, }; } Resource::LocString ShowSixelCommand::ShortDescription() const { return Utility::LocIndString("Output an image with sixels"sv); } Resource::LocString ShowSixelCommand::LongDescription() const { return Utility::LocIndString("Outputs an image from a file using sixel format."sv); } void ShowSixelCommand::ExecuteInternal(Execution::Context& context) const { using namespace VirtualTerminal; std::unique_ptr sixelImagePtr; std::string imageUrl{ context.Args.GetArg(WINGET_DEBUG_SIXEL_FILE) }; if (Utility::IsUrlRemote(imageUrl)) { auto imageStream = std::make_unique(); ProgressCallback emptyCallback; Utility::DownloadToStream(imageUrl, *imageStream, Utility::DownloadType::Manifest, emptyCallback); sixelImagePtr = std::make_unique(*imageStream, Manifest::IconFileTypeEnum::Unknown); } else { sixelImagePtr = std::make_unique(Utility::ConvertToUTF16(imageUrl)); } Sixel::Image& sixelImage = *sixelImagePtr.get(); if (context.Args.Contains(WINGET_DEBUG_SIXEL_ASPECT_RATIO)) { switch (context.Args.GetArg(WINGET_DEBUG_SIXEL_ASPECT_RATIO)[0]) { case '1': sixelImage.AspectRatio(Sixel::AspectRatio::OneToOne); break; case '2': sixelImage.AspectRatio(Sixel::AspectRatio::TwoToOne); break; case '3': sixelImage.AspectRatio(Sixel::AspectRatio::ThreeToOne); break; case '5': sixelImage.AspectRatio(Sixel::AspectRatio::FiveToOne); break; } } sixelImage.Transparency(context.Args.Contains(WINGET_DEBUG_SIXEL_TRANSPARENT)); if (context.Args.Contains(WINGET_DEBUG_SIXEL_COLOR_COUNT)) { sixelImage.ColorCount(std::stoul(std::string{ context.Args.GetArg(WINGET_DEBUG_SIXEL_COLOR_COUNT) })); } if (context.Args.Contains(WINGET_DEBUG_SIXEL_WIDTH) && context.Args.Contains(WINGET_DEBUG_SIXEL_HEIGHT)) { sixelImage.RenderSizeInCells( std::stoul(std::string{ context.Args.GetArg(WINGET_DEBUG_SIXEL_WIDTH) }), std::stoul(std::string{ context.Args.GetArg(WINGET_DEBUG_SIXEL_HEIGHT) })); } sixelImage.StretchSourceToFill(context.Args.Contains(WINGET_DEBUG_SIXEL_STRETCH)); sixelImage.UseRepeatSequence(context.Args.Contains(WINGET_DEBUG_SIXEL_REPEAT)); if (context.Args.Contains(WINGET_DEBUG_SIXEL_OUT_FILE)) { std::ofstream stream{ Utility::ConvertToUTF16(context.Args.GetArg(WINGET_DEBUG_SIXEL_OUT_FILE)) }; stream << sixelImage.Render().Get(); } else { OutputStream stream = context.Reporter.GetOutputStream(Reporter::Level::Info); stream.ClearFormat(); sixelImage.RenderTo(stream); // Force a new line to show entire image stream << std::endl; } } #define WINGET_DEBUG_PROGRESS_SIXEL Args::Type::Manifest #define WINGET_DEBUG_PROGRESS_DISABLED Args::Type::GatedVersion #define WINGET_DEBUG_PROGRESS_HIDE Args::Type::AcceptPackageAgreements #define WINGET_DEBUG_PROGRESS_TIME Args::Type::AcceptSourceAgreements #define WINGET_DEBUG_PROGRESS_MESSAGE Args::Type::ConfigurationAcceptWarning #define WINGET_DEBUG_PROGRESS_PERCENT Args::Type::AllowReboot #define WINGET_DEBUG_PROGRESS_POST Args::Type::AllVersions std::vector ProgressCommand::GetArguments() const { return { Argument{ "sixel", 's', WINGET_DEBUG_PROGRESS_SIXEL, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, Argument{ "disabled", 'd', WINGET_DEBUG_PROGRESS_DISABLED, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, Argument{ "hide", 'h', WINGET_DEBUG_PROGRESS_HIDE, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, Argument{ "time", 't', WINGET_DEBUG_PROGRESS_TIME, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, Argument{ "message", 'm', WINGET_DEBUG_PROGRESS_MESSAGE, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, Argument{ "percent", 'p', WINGET_DEBUG_PROGRESS_PERCENT, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, Argument{ "post", 0, WINGET_DEBUG_PROGRESS_POST, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, }; } Resource::LocString ProgressCommand::ShortDescription() const { return Utility::LocIndString("Show progress"sv); } Resource::LocString ProgressCommand::LongDescription() const { return Utility::LocIndString("Show progress with various controls to emulate different behaviors."sv); } void ProgressCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(WINGET_DEBUG_PROGRESS_SIXEL)) { context.Reporter.SetStyle(Settings::VisualStyle::Sixel); } if (context.Args.Contains(WINGET_DEBUG_PROGRESS_DISABLED)) { context.Reporter.SetStyle(Settings::VisualStyle::Disabled); } auto progress = context.Reporter.BeginAsyncProgress(context.Args.Contains(WINGET_DEBUG_PROGRESS_HIDE)); if (context.Args.Contains(WINGET_DEBUG_PROGRESS_MESSAGE)) { progress->Callback().SetProgressMessage(context.Args.GetArg(WINGET_DEBUG_PROGRESS_MESSAGE)); } bool sendProgress = context.Args.Contains(WINGET_DEBUG_PROGRESS_PERCENT); UINT timeInSeconds = 3600; if (context.Args.Contains(WINGET_DEBUG_PROGRESS_TIME)) { timeInSeconds = std::stoul(std::string{ context.Args.GetArg(WINGET_DEBUG_PROGRESS_TIME) }); } UINT ticks = timeInSeconds * 10; for (UINT i = 0; i < ticks; ++i) { if (sendProgress) { progress->Callback().OnProgress(i, ticks, ProgressType::Bytes); } if (progress->Callback().IsCancelledBy(CancelReason::Any)) { sendProgress = false; break; } std::this_thread::sleep_for(100ms); } if (sendProgress) { progress->Callback().OnProgress(ticks, ticks, ProgressType::Bytes); } progress.reset(); if (context.Args.Contains(WINGET_DEBUG_PROGRESS_POST)) { context.Reporter.Info() << context.Args.GetArg(WINGET_DEBUG_PROGRESS_POST) << std::endl; } } } #endif ================================================ FILE: src/AppInstallerCLICore/Commands/DebugCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #if _DEBUG namespace AppInstaller::CLI { // Command that is only available with debug builds to aid development. // Don't create localized strings for use here. struct DebugCommand final : public Command { DebugCommand(std::string_view parent) : Command("debug", {}, parent, Visibility::Hidden) {} std::vector> GetCommands() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; // Outputs the proxy stub registrations for the manifest. struct DumpProxyStubRegistrationsCommand final : public Command { DumpProxyStubRegistrationsCommand(std::string_view parent) : Command("dump-proxystub-reg", {}, parent) {} Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; // Outputs some IIDs. struct DumpInterestingIIDsCommand final : public Command { DumpInterestingIIDsCommand(std::string_view parent) : Command("dump-iids", {}, parent) {} Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; // Outputs the errors as resources. struct DumpErrorResourceCommand final : public Command { DumpErrorResourceCommand(std::string_view parent) : Command("dump-error-resource", {}, parent) {} Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; // Outputs a sixel image. struct ShowSixelCommand final : public Command { ShowSixelCommand(std::string_view parent) : Command("sixel", {}, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; // Invokes progress display. struct ProgressCommand final : public Command { ProgressCommand(std::string_view parent) : Command("progress", {}, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } #endif ================================================ FILE: src/AppInstallerCLICore/Commands/DownloadCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DownloadCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/DownloadFlow.h" #include "Workflows/InstallFlow.h" #include "Workflows/PromptFlow.h" #include "Resources.h" #include #include namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Utility::literals; std::vector DownloadCommand::GetArguments() const { return { Argument::ForType(Args::Type::Query), Argument::ForType(Args::Type::DownloadDirectory), Argument::ForType(Args::Type::Manifest), Argument::ForType(Args::Type::Id), Argument::ForType(Args::Type::Name), Argument::ForType(Args::Type::Moniker), Argument::ForType(Args::Type::Version), Argument::ForType(Args::Type::Channel), Argument::ForType(Args::Type::Source), Argument{ Args::Type::InstallScope, Resource::String::InstallScopeDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument::ForType(Args::Type::InstallerArchitecture), Argument::ForType(Args::Type::InstallerType), Argument::ForType(Args::Type::Exact), Argument::ForType(Args::Type::Locale), Argument::ForType(Args::Type::HashOverride), Argument::ForType(Args::Type::SkipDependencies), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AuthenticationMode), Argument::ForType(Args::Type::AuthenticationAccount), Argument::ForType(Args::Type::AcceptPackageAgreements), Argument::ForType(Args::Type::AcceptSourceAgreements), Argument::ForType(Args::Type::SkipMicrosoftStorePackageLicense), Argument::ForType(Args::Type::Platform), Argument{ Args::Type::OSVersion, Resource::String::OSVersionDescription, ArgumentType::Standard, Argument::Visibility::Help }, }; } Resource::LocString DownloadCommand::ShortDescription() const { return { Resource::String::DownloadCommandShortDescription }; } Resource::LocString DownloadCommand::LongDescription() const { return { Resource::String::DownloadCommandLongDescription }; } void DownloadCommand::Complete(Context& context, Args::Type valueType) const { switch (valueType) { case Args::Type::Query: case Args::Type::Manifest: case Args::Type::Id: case Args::Type::Name: case Args::Type::Moniker: case Args::Type::Version: case Args::Type::Channel: case Args::Type::Source: context << Workflow::CompleteWithSingleSemanticsForValue(valueType); break; case Args::Type::InstallerArchitecture: case Args::Type::Locale: // May well move to CompleteWithSingleSemanticsForValue, // but for now output nothing. context << Workflow::CompleteWithEmptySet; break; case Args::Type::Log: case Args::Type::DownloadDirectory: // Intentionally output nothing to allow pass through to filesystem. break; } } Utility::LocIndView DownloadCommand::HelpLink() const { return "https://aka.ms/winget-command-download"_liv; } void DownloadCommand::ValidateArgumentsInternal(Args& execArgs) const { Argument::ValidateCommonArguments(execArgs); if (execArgs.Contains(Execution::Args::Type::Platform)) { Manifest::PlatformEnum selectedPlatform = Manifest::ConvertToPlatformEnumForMSStoreDownload(execArgs.GetArg(Execution::Args::Type::Platform)); if (selectedPlatform == Manifest::PlatformEnum::Unknown) { auto validOptions = Utility::Join(", "_liv, std::vector{ "Windows.Universal"_lis, "Windows.Desktop"_lis, "Windows.IoT"_lis, "Windows.Team"_lis, "Windows.Holographic"_lis }); throw CommandException(Resource::String::InvalidArgumentValueError(Argument::ForType(Execution::Args::Type::Platform).Name(), validOptions)); } } } void DownloadCommand::ExecuteInternal(Context& context) const { context.SetFlags(AppInstaller::CLI::Execution::ContextFlag::InstallerDownloadOnly); context << Workflow::InitializeInstallerDownloadAuthenticatorsMap; if (context.Args.Contains(Execution::Args::Type::Manifest)) { context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::GetManifestFromArg; } else { context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::OpenSource() << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Download) << Workflow::GetManifestFromPackage(false); } context << Workflow::SetDownloadDirectory << Workflow::SelectInstaller << Workflow::EnsureApplicableInstaller << Workflow::ReportIdentityAndInstallationDisclaimer << Workflow::ShowPromptsForSinglePackage(/* ensureAcceptance */ true) << Workflow::DownloadPackageDependencies << Workflow::DownloadInstaller; } } ================================================ FILE: src/AppInstallerCLICore/Commands/DownloadCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct DownloadCommand final : public Command { DownloadCommand(std::string_view parent) : Command("download", {} /* aliases */, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DscAdminSettingsResource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscAdminSettingsResource.h" #include "DscComposableObject.h" #include "Resources.h" #include using namespace AppInstaller::Utility::literals; using namespace AppInstaller::Repository; namespace AppInstaller::CLI { namespace AdminSettingsDetails { // The base for admin settings. struct IAdminSetting { virtual ~IAdminSetting() = default; // Gets the name of the setting. virtual Utility::LocIndView SettingName() const = 0; // Tests the value. // Returns true if in the desired state; false if not. virtual bool Test() const = 0; // Sets the value. // Returns true if the value could be set; false if not. virtual bool Set() const = 0; }; // A boolean based admin setting struct AdminSetting_Bool : public IAdminSetting { AdminSetting_Bool(Settings::BoolAdminSetting setting, bool value) : m_setting(setting), m_value(value) {} Utility::LocIndView SettingName() const override { return Settings::AdminSettingToString(m_setting); } bool Test() const override { return Settings::IsAdminSettingEnabled(m_setting) == m_value; } bool Set() const override { return m_value ? Settings::EnableAdminSetting(m_setting) : Settings::DisableAdminSetting(m_setting); } private: Settings::BoolAdminSetting m_setting; bool m_value; }; // A string based admin setting struct AdminSetting_String : public IAdminSetting { AdminSetting_String(Settings::StringAdminSetting setting, std::optional value) : m_setting(setting), m_value(std::move(value)) {} Utility::LocIndView SettingName() const override { return Settings::AdminSettingToString(m_setting); } bool Test() const override { return Settings::GetAdminSetting(m_setting) == m_value; } bool Set() const override { return m_value ? Settings::SetAdminSetting(m_setting, m_value.value()) : Settings::ResetAdminSetting(m_setting); } private: Settings::StringAdminSetting m_setting; std::optional m_value; }; } namespace { WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(SettingsProperty, Json::Value, Settings, "settings", Resource::String::DscResourcePropertyDescriptionAdminSettingsSettings); using AdminSettingsResourceObject = DscComposableObject; struct AdminSettingsFunctionData { AdminSettingsFunctionData() = default; AdminSettingsFunctionData(const std::optional& json) : Input(json) { } const AdminSettingsResourceObject Input; AdminSettingsResourceObject Output; std::vector> InputSettings; // Converts the input settings into the appropriate settings object. void ParseSettings() { if (Input.Settings()) { const Json::Value& inputSettings = Input.Settings().value(); for (const auto& property : inputSettings.getMemberNames()) { auto boolSetting = Settings::StringToBoolAdminSetting(property); if (boolSetting != Settings::BoolAdminSetting::Unknown) { bool value = inputSettings[property].asBool(); AICLI_LOG(Config, Info, << "Bool admin setting: " << property << " => " << (value ? "true" : "false")); InputSettings.emplace_back(std::make_unique(boolSetting, value)); continue; } auto stringSetting = Settings::StringToStringAdminSetting(property); if (stringSetting != Settings::StringAdminSetting::Unknown) { const auto& propertyNode = inputSettings[property]; std::optional value; if (propertyNode.isNull()) { AICLI_LOG(Config, Info, << "String admin setting: " << property << " => null"); } else { value = propertyNode.asString(); AICLI_LOG(Config, Info, << "String admin setting: " << property << " => `" << value.value() << "`"); } InputSettings.emplace_back(std::make_unique(stringSetting, std::move(value))); continue; } AICLI_LOG(Config, Warning, << "Unknown admin setting: " << property); } } } // Fills the Output object with the current state void Get() { Json::Value adminSettings{ Json::objectValue }; for (const auto& setting : Settings::GetAllBoolAdminSettings()) { auto str = std::string{ Settings::AdminSettingToString(setting) }; adminSettings[str] = Settings::IsAdminSettingEnabled(setting); } for (const auto& setting : Settings::GetAllStringAdminSettings()) { auto name = std::string{ Settings::AdminSettingToString(setting) }; auto value = Settings::GetAdminSetting(setting); if (value) { adminSettings[name] = value.value(); } } Output.Settings(std::move(adminSettings)); } // Determines if the current Output values match the Input values state. bool Test() { for (const auto& setting : InputSettings) { if (!setting->Test()) { return false; } } return true; } // Sets all of the input settings. void Set() { for (const auto& setting : InputSettings) { if (!setting->Test()) { if (!setting->Set()) { auto message = Resource::String::DisabledByGroupPolicy(setting->SettingName()); THROW_HR_MSG(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, "%hs", message.get().c_str()); } } } } Json::Value DiffJson(bool inDesiredState) { Json::Value result{ Json::ValueType::arrayValue }; if (!inDesiredState) { result.append(std::string{ SettingsProperty::Name() }); } return result; } }; } DscAdminSettingsResource::DscAdminSettingsResource(std::string_view parent) : DscCommandBase(parent, "admin-settings", DscResourceKind::Resource, DscFunctions::Get | DscFunctions::Set | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema, DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::ReturnsStateAndDiff) { } Resource::LocString DscAdminSettingsResource::ShortDescription() const { return Resource::String::DscAdminSettingsResourceShortDescription; } Resource::LocString DscAdminSettingsResource::LongDescription() const { return Resource::String::DscAdminSettingsResourceLongDescription; } std::string DscAdminSettingsResource::ResourceType() const { return "AdminSettings"; } void DscAdminSettingsResource::ResourceFunctionGet(Execution::Context& context) const { AdminSettingsFunctionData data; data.Get(); WriteJsonOutputLine(context, data.Output.ToJson()); } void DscAdminSettingsResource::ResourceFunctionSet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { AdminSettingsFunctionData data{ json }; data.ParseSettings(); bool inDesiredState = data.Test(); if (!inDesiredState) { Workflow::EnsureRunningAsAdmin(context); if (context.IsTerminated()) { return; } data.Set(); } data.Get(); WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, data.DiffJson(inDesiredState)); } } void DscAdminSettingsResource::ResourceFunctionTest(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { AdminSettingsFunctionData data{ json }; data.ParseSettings(); data.Get(); data.Output.InDesiredState(data.Test()); WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, data.DiffJson(data.Output.InDesiredState().value())); } } void DscAdminSettingsResource::ResourceFunctionExport(Execution::Context& context) const { ResourceFunctionGet(context); } void DscAdminSettingsResource::ResourceFunctionSchema(Execution::Context& context) const { WriteJsonOutputLine(context, AdminSettingsResourceObject::Schema(ResourceType())); } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscAdminSettingsResource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "DscCommandBase.h" namespace AppInstaller::CLI { // A resource for managing admin settings. struct DscAdminSettingsResource : public DscCommandBase { DscAdminSettingsResource(std::string_view parent); Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: std::string ResourceType() const override; void ResourceFunctionGet(Execution::Context& context) const override; void ResourceFunctionSet(Execution::Context& context) const override; void ResourceFunctionTest(Execution::Context& context) const override; void ResourceFunctionExport(Execution::Context& context) const override; void ResourceFunctionSchema(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DscCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscCommand.h" #include "DscPackageResource.h" #include "DscUserSettingsFileResource.h" #include "DscSourceResource.h" #include "DscAdminSettingsResource.h" #ifndef AICLI_DISABLE_TEST_HOOKS #include "DscTestFileResource.h" #include "DscTestJsonResource.h" #endif namespace AppInstaller::CLI { namespace { Argument GetOutputFileArgument() { return { Execution::Args::Type::OutputFile, Resource::String::OutputDirectoryArgumentDescription, ArgumentType::Standard }; } } DscCommand::DscCommand(std::string_view parent) : Command(StaticName(), parent) { } std::vector> DscCommand::GetCommands() const { // These should all derive from DscCommandBase return InitializeFromMoveOnly>>({ std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), #ifndef AICLI_DISABLE_TEST_HOOKS std::make_unique(FullName()), std::make_unique(FullName()), #endif }); } std::vector DscCommand::GetArguments() const { std::vector result; result.emplace_back(Execution::Args::Type::DscResourceFunctionManifest, Resource::String::DscResourceFunctionDescriptionManifest, ArgumentType::Flag); result.emplace_back(GetOutputFileArgument()); return result; } Resource::LocString DscCommand::ShortDescription() const { return { Resource::String::DscCommandShortDescription }; } Resource::LocString DscCommand::LongDescription() const { return { Resource::String::DscCommandLongDescription }; } Utility::LocIndView DscCommand::HelpLink() const { return "https://aka.ms/winget-dsc-resources"_liv; } void DscCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::DscResourceFunctionManifest)) { std::filesystem::path outputDirectory{ Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::OutputFile)) }; std::filesystem::create_directories(outputDirectory); std::string filePrefix = Utility::ToLower(DscCommandBase::ModuleName()); for (const auto& command : GetCommands()) { DscCommandBase* commandBase = static_cast(command.get()); std::filesystem::path outputPath = outputDirectory; outputPath /= std::string{ filePrefix }.append(".").append(commandBase->Name()).append(".dsc.resource.json"); commandBase->WriteManifest(context, outputPath); } } else { OutputHelp(context.Reporter); } } void DscCommand::ValidateArgumentsInternal(Execution::Args& args) const { if (args.Contains(Execution::Args::Type::DscResourceFunctionManifest) && !args.Contains(Execution::Args::Type::OutputFile)) { throw CommandException(Resource::String::RequiredArgError(GetOutputFileArgument().Name())); } } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #include namespace AppInstaller::CLI { struct DscCommand final : public Command { DscCommand(std::string_view parent); static constexpr std::string_view StaticName() { return "dscv3"sv; }; std::vector> GetCommands() const override; std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; void ValidateArgumentsInternal(Execution::Args& args) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DscCommandBase.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscCommandBase.h" #include "DscCommand.h" #include #include #define WINGET_DSC_FUNCTION_FOREACH(_macro_) \ _macro_(Get); \ _macro_(Set); \ _macro_(WhatIf); \ _macro_(Test); \ _macro_(Delete); \ _macro_(Export); \ _macro_(Validate); \ _macro_(Resolve); \ _macro_(Adapter); \ _macro_(Schema); \ namespace AppInstaller::CLI { namespace { std::string GetFunctionManifestString(DscFunctions function) { THROW_HR_IF(E_INVALIDARG, !WI_IsSingleFlagSet(function)); switch (function) { case DscFunctions::Get: return "get"; case DscFunctions::Set: return "set"; case DscFunctions::WhatIf: return "whatIf"; case DscFunctions::Test: return "test"; case DscFunctions::Delete: return "delete"; case DscFunctions::Export: return "export"; case DscFunctions::Validate: return "validate"; case DscFunctions::Resolve: return "resolve"; case DscFunctions::Adapter: return "adapter"; case DscFunctions::Schema: return "schema"; } THROW_HR(E_NOTIMPL); } std::string GetFunctionArgumentString(DscFunctions function) { return std::string{ "--" } + GetFunctionManifestString(function); } bool FunctionSpecifiesInput(DscFunctions function) { switch (function) { case DscFunctions::Get: case DscFunctions::Set: case DscFunctions::WhatIf: case DscFunctions::Test: case DscFunctions::Delete: case DscFunctions::Export: case DscFunctions::Validate: case DscFunctions::Resolve: return true; } return false; } bool FunctionIsSetLike(DscFunctions function) { switch (function) { case DscFunctions::Set: case DscFunctions::WhatIf: return true; } return false; } bool FunctionSpecifiesReturn(DscFunctions function) { switch (function) { case DscFunctions::Set: case DscFunctions::WhatIf: case DscFunctions::Test: return true; } return false; } std::optional GetReturnType(DscFunctionModifiers modifiers) { if (WI_IsFlagSet(modifiers, DscFunctionModifiers::ReturnsStateAndDiff)) { return "stateAndDiff"; } if (WI_IsFlagSet(modifiers, DscFunctionModifiers::ReturnsState)) { return "state"; } return std::nullopt; } Json::Value CreateJsonDefinitionFor(std::string_view name, DscFunctions function, DscFunctionModifiers modifiers) { THROW_HR_IF(E_INVALIDARG, !WI_IsSingleFlagSet(function)); THROW_HR_IF(E_NOTIMPL, function == DscFunctions::Adapter); Json::Value result{ Json::ValueType::objectValue }; #ifndef USE_PROD_CLSIDS result["executable"] = "wingetdev"; #else result["executable"] = "winget"; #endif Json::Value args{ Json::ValueType::arrayValue }; args.append(std::string{ DscCommand::StaticName() }); args.append(std::string{ name }); args.append(GetFunctionArgumentString(function)); result["args"] = std::move(args); if (FunctionSpecifiesInput(function)) { result["input"] = "stdin"; } if (FunctionIsSetLike(function)) { if (WI_IsFlagSet(modifiers, DscFunctionModifiers::ImplementsPretest)) { result["implementsPretest"] = true; } if (WI_IsFlagSet(modifiers, DscFunctionModifiers::HandlesExist)) { result["handlesExist"] = true; } } if (FunctionSpecifiesReturn(function)) { std::optional returnType = GetReturnType(modifiers); if (returnType) { result["return"] = returnType.value(); } } if (function == DscFunctions::Schema) { Json::Value newResult{ Json::ValueType::objectValue }; newResult["command"] = std::move(result); result = std::move(newResult); } return result; } } DscCommandBase::DscCommandBase(std::string_view parent, std::string_view resourceName, DscResourceKind kind, DscFunctions functions, DscFunctionModifiers modifiers) : Command(resourceName, parent, CommandOutputFlags::IgnoreSettingsWarnings), m_kind(kind), m_functions(functions), m_modifiers(modifiers) { // Limits on current implementation THROW_HR_IF(E_NOTIMPL, kind != DscResourceKind::Resource); THROW_HR_IF(E_NOTIMPL, WI_IsFlagSet(functions, DscFunctions::Adapter)); } std::vector DscCommandBase::GetArguments() const { std::vector result; #define WINGET_DSC_FUNCTION_ARGUMENT(_function_) \ if (WI_IsFlagSet(m_functions, DscFunctions::_function_)) \ { \ result.emplace_back(Execution::Args::Type::DscResourceFunction ## _function_, Resource::String::DscResourceFunctionDescription ## _function_, ArgumentType::Flag); \ } WINGET_DSC_FUNCTION_FOREACH(WINGET_DSC_FUNCTION_ARGUMENT); #undef WINGET_DSC_FUNCTION_ARGUMENT result.emplace_back(Execution::Args::Type::DscResourceFunctionManifest, Resource::String::DscResourceFunctionDescriptionManifest, ArgumentType::Flag); result.emplace_back(Execution::Args::Type::OutputFile, Resource::String::OutputFileArgumentDescription, ArgumentType::Standard); return result; } Utility::LocIndView DscCommandBase::HelpLink() const { return "https://aka.ms/winget-dsc-resources"_liv; } void DscCommandBase::ExecuteInternal(Execution::Context& context) const { context.Reporter.SetChannel(Execution::Reporter::Channel::Json); Logging::StdErrLogger::Add(); #define WINGET_DSC_FUNCTION_ARGUMENT(_function_) \ if (context.Args.Contains(Execution::Args::Type::DscResourceFunction ## _function_)) \ { \ return ResourceFunction ## _function_(context); \ } WINGET_DSC_FUNCTION_FOREACH(WINGET_DSC_FUNCTION_ARGUMENT); WINGET_DSC_FUNCTION_ARGUMENT(Manifest); #undef WINGET_DSC_FUNCTION_ARGUMENT } #define WINGET_DSC_FUNCTION_METHOD(_function_) \ void DscCommandBase::ResourceFunction ## _function_(Execution::Context&) const \ { \ THROW_HR(E_NOTIMPL); \ } \ WINGET_DSC_FUNCTION_FOREACH(WINGET_DSC_FUNCTION_METHOD); void DscCommandBase::WriteManifest(Execution::Context& context, const std::filesystem::path& filePath) const { Json::Value json{ Json::ValueType::objectValue }; // TODO: Move to release schema when released (there should be an aka.ms link as well, but it wasn't active yet) //json["$schema"] = "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/v3/bundled/resource/manifest.json"; json["$schema"] = "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json"; json["type"] = std::string{ ModuleName() } + '/' + ResourceType(); json["description"] = LongDescription().get(); json["version"] = Runtime::GetClientVersion().get(); Json::Value tags{ Json::ValueType::arrayValue }; tags.append("WinGet"); json["tags"] = std::move(tags); #define WINGET_DSC_FUNCTION_MANIFEST(_function_) \ if (WI_IsFlagSet(m_functions, DscFunctions::_function_)) \ { \ json[GetFunctionManifestString(DscFunctions::_function_)] = CreateJsonDefinitionFor(Name(), DscFunctions::_function_, m_modifiers); \ } WINGET_DSC_FUNCTION_FOREACH(WINGET_DSC_FUNCTION_MANIFEST); #undef WINGET_DSC_FUNCTION_MANIFEST Json::StreamWriterBuilder writerBuilder; writerBuilder.settings_["indentation"] = " "; std::string jsonString = Json::writeString(writerBuilder, json); if (!filePath.empty()) { std::ofstream stream{ filePath, std::ios::binary }; stream.write(jsonString.c_str(), jsonString.length()); } else { context.Reporter.Json() << jsonString; } } void DscCommandBase::ResourceFunctionManifest(Execution::Context& context) const { std::filesystem::path path; if (context.Args.Contains(Execution::Args::Type::OutputFile)) { path = std::filesystem::path{ Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::OutputFile)) }; } WriteManifest(context, path); } #undef WINGET_DSC_FUNCTION_METHOD std::optional DscCommandBase::GetJsonFromInput(Execution::Context& context, bool terminateContextOnError) const { // Don't attempt to read from an interactive stream as this will just block if (!context.Reporter.InputStreamIsInteractive()) { AICLI_LOG(CLI, Verbose, << "Reading Json from input stream..."); Json::Value result; Json::CharReaderBuilder builder; Json::String errors; if (Json::parseFromStream(builder, context.Reporter.RawInputStream(), &result, &errors)) { AICLI_LOG(CLI, Info, << "Json from input stream:\n" << Json::writeString(Json::StreamWriterBuilder{}, result)); return result; } AICLI_LOG(CLI, Error, << "Failed to read input JSON: " << errors); } if (terminateContextOnError) { AICLI_TERMINATE_CONTEXT_RETURN(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, std::nullopt); } else { return std::nullopt; } } void DscCommandBase::WriteJsonOutputLine(Execution::Context& context, const Json::Value& value) const { Json::StreamWriterBuilder writerBuilder; writerBuilder.settings_["indentation"] = ""; writerBuilder.settings_["commentStyle"] = "None"; writerBuilder.settings_["emitUTF8"] = true; context.Reporter.Json() << Json::writeString(writerBuilder, value) << std::endl; } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscCommandBase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #include #include #ifndef USE_PROD_CLSIDS #define WINGET_DSCV3_MODULE_NAME "Microsoft.WinGet.Dev" #define WINGET_DSCV3_MODULE_NAME_WIDE L"Microsoft.WinGet.Dev" #else #define WINGET_DSCV3_MODULE_NAME "Microsoft.WinGet" #define WINGET_DSCV3_MODULE_NAME_WIDE L"Microsoft.WinGet" #endif namespace AppInstaller::CLI { // The kind of resource that this command is implementing. enum class DscResourceKind { // Standard resource Resource, // Group resource Group, // Adapter resource Adapter, // Import resource Import, }; // The functions that a DSC resource can provide. enum class DscFunctions { None = 0x000, // Gets the current state; should always be implemented. Get = 0x001, // Sets the state; should always be implemented. Set = 0x002, // Produces the output of Set without modifying state. WhatIf = 0x004, // Determines if the current state matches the given state. Test = 0x008, // Deletes the given state. Delete = 0x010, // Gets all instances of the resource. Export = 0x020, // Required for a Group resource, ignored for all others. Validate = 0x040, // Required for an Import resource. Resolve = 0x080, // Required for an Adapter resource. Adapter = 0x100, // Gets the schema for the resource's properties. Schema = 0x200, }; DEFINE_ENUM_FLAG_OPERATORS(DscFunctions); // Behavior changes for DSC functions. enum class DscFunctionModifiers { None = 0x00, // The resource implements a check during Set (and WhatIf) to determine if already in the correct state. // If not provided, DSC will ensure that the state is tested beforehand. ImplementsPretest = 0x01, // The resource will act on the `_exist` property during Set (and WhatIf). // If not provided, the resource should implement Delete. HandlesExist = 0x02, // Functions that may return state information (set, what-if, test) return only the state. ReturnsState = 0x04, // Functions that may return state information (set, what-if, test) return the state and property difference. ReturnsStateAndDiff = 0x08, }; DEFINE_ENUM_FLAG_OPERATORS(DscFunctionModifiers); // Provides infrastructure for DSC commands to be implemented. struct DscCommandBase : public Command { DscCommandBase(std::string_view parent, std::string_view resourceName, DscResourceKind kind, DscFunctions functions, DscFunctionModifiers modifiers); std::vector GetArguments() const override; Utility::LocIndView HelpLink() const override; static constexpr std::string_view ModuleName() { return WINGET_DSCV3_MODULE_NAME; } // Writes the manifest for the command to the file path. // If the path is empty, writes the manifest to the output stream. void WriteManifest(Execution::Context& context, const std::filesystem::path& filePath) const; protected: void ExecuteInternal(Execution::Context& context) const override; // Gets the resource specific type name. virtual std::string ResourceType() const = 0; virtual void ResourceFunctionGet(Execution::Context& context) const; virtual void ResourceFunctionSet(Execution::Context& context) const; virtual void ResourceFunctionWhatIf(Execution::Context& context) const; virtual void ResourceFunctionTest(Execution::Context& context) const; virtual void ResourceFunctionDelete(Execution::Context& context) const; virtual void ResourceFunctionExport(Execution::Context& context) const; virtual void ResourceFunctionValidate(Execution::Context& context) const; virtual void ResourceFunctionResolve(Execution::Context& context) const; virtual void ResourceFunctionAdapter(Execution::Context& context) const; virtual void ResourceFunctionSchema(Execution::Context& context) const; virtual void ResourceFunctionManifest(Execution::Context& context) const; // Parses a JSON object from stdin. std::optional GetJsonFromInput(Execution::Context& context, bool terminateContextOnError = true) const; // Writes the value to the context output. void WriteJsonOutputLine(Execution::Context& context, const Json::Value& value) const; private: DscResourceKind m_kind = DscResourceKind::Resource; DscFunctions m_functions = DscFunctions::None; DscFunctionModifiers m_modifiers = DscFunctionModifiers::None; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DscComposableObject.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscComposableObject.h" namespace AppInstaller::CLI { namespace { std::string GetTypeString(Json::ValueType type) { switch (type) { case Json::nullValue: return "null"; case Json::intValue: case Json::uintValue: case Json::realValue: return "number"; case Json::stringValue: return "string"; case Json::booleanValue: return "boolean"; case Json::arrayValue: return "array"; case Json::objectValue: return "object"; default: THROW_HR(E_UNEXPECTED); } } } namespace details { const Json::Value* GetProperty(const Json::Value& object, std::string_view name) { return object.find(name.data(), name.data() + name.length()); } void AddProperty(Json::Value& object, std::string_view name, std::optional&& value) { if (value) { object[std::string{ name }] = std::move(value).value(); } } Json::Value GetBaseSchema(const std::string& title) { Json::Value result{ Json::ValueType::objectValue }; result["$schema"] = "http://json-schema.org/draft-07/schema#"; result["title"] = title; result["type"] = "object"; result["additionalProperties"] = false; return result; } void AddPropertySchema( Json::Value& object, std::string_view name, DscComposablePropertyFlag flags, Json::ValueType type, std::string_view description, const std::vector& enumValues, const std::optional& defaultValue) { Json::Value& propertiesObject = object["properties"]; if (propertiesObject.isNull()) { propertiesObject = Json::Value{ Json::ValueType::objectValue }; } std::string nameString{ name }; Json::Value property{ Json::ValueType::objectValue }; if (type != Json::ValueType::objectValue) { property["type"] = GetTypeString(type); } property["description"] = std::string{ description }; if (!enumValues.empty()) { Json::Value enumArray{ Json::ValueType::arrayValue }; for (const std::string& enumValue : enumValues) { enumArray.append(enumValue); } property["enum"] = std::move(enumArray); } if (defaultValue) { property["default"] = defaultValue.value(); } propertiesObject[nameString] = std::move(property); if (WI_IsFlagSet(flags, DscComposablePropertyFlag::Required)) { Json::Value& requiredArray = object["required"]; if (requiredArray.isNull()) { requiredArray = Json::Value{ Json::ValueType::arrayValue }; } requiredArray.append(nameString); } } } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscComposableObject.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include "Resources.h" #include #include #include #include using namespace std::string_view_literals; using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI { // Flags that define how to treat properties. enum DscComposablePropertyFlag { None = 0x0, Required = 0x1, CopyToOutput = 0x2, }; DEFINE_ENUM_FLAG_OPERATORS(DscComposablePropertyFlag); namespace details { // Gets a property or null if not present. const Json::Value* GetProperty(const Json::Value& object, std::string_view name); // Adds the given property and value to the object, if provided. void AddProperty(Json::Value& object, std::string_view name, std::optional&& value); // Gets the default schema object. Json::Value GetBaseSchema(const std::string& title); // Adds a property to the schema object. void AddPropertySchema( Json::Value& object, std::string_view name, DscComposablePropertyFlag flags, Json::ValueType type, std::string_view description, const std::vector& enumValues, const std::optional& defaultValue); } template struct GetJsonTypeValue { static_assert(false, "Implement for this type."); }; template <> struct GetJsonTypeValue { static bool Get(const Json::Value& value) { return value.asBool(); } static Json::ValueType SchemaType() { return Json::ValueType::booleanValue; } }; template <> struct GetJsonTypeValue { static std::string Get(const Json::Value& value) { return value.asString(); } static Json::ValueType SchemaType() { return Json::ValueType::stringValue; } }; template <> struct GetJsonTypeValue { static int32_t Get(const Json::Value& value) { return value.asInt(); } static Json::ValueType SchemaType() { return Json::ValueType::intValue; } }; template <> struct GetJsonTypeValue { static Json::Value Get(const Json::Value& value) { return value; } static Json::ValueType SchemaType() { return Json::ValueType::objectValue; } }; // Template useful for composing objects for DSC resources. // Properties should be of the shape: // // struct Property // { // using PropertyType = { bool, std::string }; // static std::string_view Name(); // static void FromJson(Property*, const Json::Value*); // static std::optional ToJson(const Property*); // // const PropertyType& PROPERTY_NAME() const; // void PROPERTY_NAME(const PropertyType&); // } template struct DscComposableObject : public Property... { DscComposableObject() = default; DscComposableObject(const std::optional& input, bool ignoreFieldRequirements = false) { THROW_HR_IF(E_POINTER, !input && !ignoreFieldRequirements); if (input) { FromJson(input.value(), ignoreFieldRequirements); } } // Read values for each property void FromJson(const Json::Value& input, bool ignoreFieldRequirements = false) { (FoldHelper{}, ..., Property::FromJson(this, details::GetProperty(input, Property::Name()), ignoreFieldRequirements)); } // Populate JSON object with properties. Json::Value ToJson() { Json::Value result{ Json::ValueType::objectValue }; (FoldHelper{}, ..., details::AddProperty(result, Property::Name(), Property::ToJson(this))); return result; } // Copies the appropriate values to a new object for output. DscComposableObject CopyForOutput() const { DscComposableObject result; (FoldHelper{}, ..., Property::CopyForOutput(this, &result)); return result; } // Get the JSON Schema for this object static Json::Value Schema(const std::string& title) { Json::Value result = details::GetBaseSchema(title); (FoldHelper{}, ..., details::AddPropertySchema(result, Property::Name(), Property::Flags, GetJsonTypeValue::SchemaType(), Property::Description(), Property::EnumValues(), Property::Default())); return result; } }; template struct DscComposableProperty { using PropertyType = PropertyTypeT; static constexpr DscComposablePropertyFlag Flags = PropertyFlags; static void FromJson(Derived* self, const Json::Value* value, bool ignoreFieldRequirements) { if (value && !value->isNull()) { self->m_value = GetJsonTypeValue::Get(*value); } else { if (!ignoreFieldRequirements && WI_IsFlagSet(PropertyFlags, DscComposablePropertyFlag::Required)) { THROW_HR_MSG(WINGET_CONFIG_ERROR_MISSING_FIELD, "Required property `%hs` not provided.", Derived::Name().data()); } else { self->m_value = std::nullopt; } } } static std::optional ToJson(const Derived* self) { if constexpr (WI_IsFlagSet(PropertyFlags, DscComposablePropertyFlag::Required)) { THROW_HR_IF(WINGET_CONFIG_ERROR_MISSING_FIELD, !self->m_value); return self->m_value.value(); } else { return self->m_value ? std::optional{ self->m_value.value() } : std::nullopt; } } static void CopyForOutput(const Derived* self, Derived* other) { if constexpr (WI_IsFlagSet(PropertyFlags, DscComposablePropertyFlag::CopyToOutput)) { other->m_value = self->m_value; } } protected: std::optional m_value; }; #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \ struct _property_type_ : public DscComposableProperty<_property_type_, _value_type_, _flags_> \ { \ static std::string_view Name() { return _json_name_; } \ static Resource::LocString Description() { return _description_; } \ static std::vector EnumValues() { return std::vector _enum_vals_; } \ static std::optional Default() { return _default_; } \ std::optional& _property_name_() { return m_value; } \ const std::optional& _property_name_() const { return m_value; } \ void _property_name_(std::optional value) { m_value = std::move(value); } \ #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \ WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \ }; #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(_property_type_, _value_type_, _property_name_, _json_name_, _description_) \ WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_, {}, {}) #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_) \ WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, {}, {}) #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_DEFAULT(_property_type_, _value_type_, _property_name_, _json_name_, _description_, _default_) \ WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_, {}, _default_) #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(_property_type_, _value_type_, _property_name_, _json_name_, _description_, _enum_vals_, _default_) \ WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_, _enum_vals_, _default_) #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM_FLAGS(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \ WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(StandardExistProperty, bool, Exist, "_exist", DscComposablePropertyFlag::None, Resource::String::DscResourcePropertyDescriptionExist, {}, {}) bool ShouldExist() const { return m_value.value_or(true); } }; WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(StandardInDesiredStateProperty, bool, InDesiredState, "_inDesiredState", Resource::String::DscResourcePropertyDescriptionInDesiredState); } ================================================ FILE: src/AppInstallerCLICore/Commands/DscPackageResource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscPackageResource.h" #include "DscComposableObject.h" #include "Resources.h" #include "Workflows/WorkflowBase.h" #include "Workflows/ConfigurationFlow.h" #include "Workflows/InstallFlow.h" #include "Workflows/UninstallFlow.h" #include "Workflows/UpdateFlow.h" #include using namespace AppInstaller::Utility::literals; using namespace AppInstaller::Repository; namespace AppInstaller::CLI { namespace { WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(IdProperty, std::string, Identifier, "id", DscComposablePropertyFlag::Required | DscComposablePropertyFlag::CopyToOutput, Resource::String::DscResourcePropertyDescriptionPackageId); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(SourceProperty, std::string, Source, "source", DscComposablePropertyFlag::CopyToOutput, Resource::String::DscResourcePropertyDescriptionPackageSource); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(VersionProperty, std::string, Version, "version", Resource::String::DscResourcePropertyDescriptionPackageVersion); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(MatchOptionProperty, std::string, MatchOption, "matchOption", Resource::String::DscResourcePropertyDescriptionPackageMatchOption, ({ "equals", "equalsCaseInsensitive", "startsWithCaseInsensitive", "containsCaseInsensitive" }), "equalsCaseInsensitive"); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_DEFAULT(UseLatestProperty, bool, UseLatest, "useLatest", Resource::String::DscResourcePropertyDescriptionPackageUseLatest, "false"); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(InstallModeProperty, std::string, InstallMode, "installMode", Resource::String::DscResourcePropertyDescriptionPackageInstallMode, ({ "default", "silent", "interactive" }), "silent"); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(AcceptAgreementsProperty, bool, AcceptAgreements, "acceptAgreements", Resource::String::DscResourcePropertyDescriptionAcceptAgreements); // TODO: To support Scope on this resource: // 1. Change the installed source to pull in all package info for both scopes by default // 2. Change the installed source open in workflows to always open for everything, regardless of scope // 3. Improve correlation handling if needed for cross-scope package installations // 4. Update the test EXE installer to handle being installed for both scopes using PackageResourceObject = DscComposableObject; std::optional ToMatchType(const std::optional& value) { if (!value) { return std::nullopt; } std::string lowerValue = Utility::ToLower(value.value()); if (lowerValue == "equals") { return MatchType::Exact; } else if (lowerValue == "equals""case""insensitive") { return MatchType::CaseInsensitive; } else if (lowerValue == "starts""with""case""insensitive") { return MatchType::StartsWith; } else if (lowerValue == "contains""case""insensitive") { return MatchType::Substring; } THROW_HR(E_INVALIDARG); } struct PackageFunctionData { PackageFunctionData(Execution::Context& context, const std::optional& json, bool ignoreFieldRequirements = false) : Input(json, ignoreFieldRequirements), ParentContext(context) { Reset(); } const PackageResourceObject Input; PackageResourceObject Output; Execution::Context& ParentContext; std::unique_ptr SubContext; // Reset the state that is modified by Get void Reset() { Output = Input.CopyForOutput(); SubContext = ParentContext.CreateSubContext(); SubContext->SetFlags(Execution::ContextFlag::DisableInteractivity); if (Input.AcceptAgreements().value_or(false)) { SubContext->Args.AddArg(Execution::Args::Type::AcceptSourceAgreements); SubContext->Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); } } void PrepareSubContextInputs() { if (Input.Source()) { SubContext->Args.AddArg(Execution::Args::Type::Source, Input.Source().value()); } } // Fills the Output object with the current state bool Get() { PrepareSubContextInputs(); *SubContext << Workflow::ReportExecutionStage(Workflow::ExecutionStage::Discovery) << Workflow::OpenSource() << Workflow::OpenCompositeSource(Workflow::DetermineInstalledSource(*SubContext), false, CompositeSearchBehavior::AllPackages); if (SubContext->IsTerminated()) { ParentContext.Terminate(SubContext->GetTerminationHR()); return false; } // Do a manual search of the now opened source Source& source = SubContext->Get(); MatchType matchType = ToMatchType(Input.MatchOption()).value_or(MatchType::CaseInsensitive); SearchRequest request; request.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, matchType, Input.Identifier().value())); SearchResult result = source.Search(request); SubContext->Add(result); if (result.Matches.empty()) { Output.Exist(false); } else if (result.Matches.size() > 1) { AICLI_LOG(Config, Warning, << "Found " << result.Matches.size() << " matches when searching for '" << Input.Identifier().value() << "'"); Output.Exist(false); } else { auto& package = result.Matches.front().Package; SubContext->Add(package); auto installedPackage = package->GetInstalled(); // Fill Output and SubContext Output.Exist(static_cast(installedPackage)); Output.Identifier(package->GetProperty(PackageProperty::Id)); if (installedPackage) { auto versionKeys = installedPackage->GetVersionKeys(); AICLI_LOG(CLI, Verbose, << "Package::Get found " << versionKeys.size() << " installed versions"); std::shared_ptr installedVersion; // Find the specific version provided if possible if (Input.Version()) { Utility::Version inputVersion{ Input.Version().value() }; for (const auto& key : versionKeys) { if (inputVersion == Utility::Version{ key.Version }) { installedVersion = installedPackage->GetVersion(key); break; } } } if (!installedVersion) { installedVersion = installedPackage->GetLatestVersion(); } if (installedVersion) { Output.Version(installedVersion->GetProperty(PackageVersionProperty::Version)); } auto data = Repository::GetLatestApplicableVersion(package); Output.UseLatest(!data.UpdateAvailable); } } AICLI_LOG(CLI, Verbose, << "Package::Get found:\n" << Json::writeString(Json::StreamWriterBuilder{}, Output.ToJson())); return true; } void Uninstall() { AICLI_LOG(CLI, Verbose, << "Package::Uninstall invoked"); if (Input.Version()) { SubContext->Args.AddArg(Execution::Args::Type::TargetVersion, Input.Version().value()); } else { SubContext->Args.AddArg(Execution::Args::Type::AllVersions); } *SubContext << Workflow::UninstallSinglePackage; if (SubContext->IsTerminated()) { ParentContext.Terminate(SubContext->GetTerminationHR()); return; } Output.Exist(false); Output.Version(std::nullopt); Output.UseLatest(std::nullopt); } void Install(bool allowDowngrade = false) { AICLI_LOG(CLI, Verbose, << "Package::Install invoked"); if (Input.Version()) { SubContext->Args.AddArg(Execution::Args::Type::Version, Input.Version().value()); } *SubContext << Workflow::SelectSinglePackageVersionForInstallOrUpgrade(Workflow::OperationType::Install, allowDowngrade) << Workflow::InstallSinglePackage; if (SubContext->IsTerminated()) { ParentContext.Terminate(SubContext->GetTerminationHR()); return; } Output.Exist(true); Output.Version(std::nullopt); if (SubContext->Contains(Execution::Data::PackageVersion)) { const auto& packageVersion = SubContext->Get(); if (packageVersion) { Output.Version(packageVersion->GetProperty(Repository::PackageVersionProperty::Version)); } } Output.UseLatest(std::nullopt); } void Reinstall() { AICLI_LOG(CLI, Verbose, << "Package::Reinstall invoked"); SubContext->Args.AddArg(Execution::Args::Type::UninstallPrevious); Install(true); } // Determines if the current Output values match the Input values state. bool Test() { // Need to populate Output before calling THROW_HR_IF(E_UNEXPECTED, !Output.Exist().has_value()); if (Input.ShouldExist()) { if (Output.Exist().value()) { AICLI_LOG(CLI, Verbose, << "Package::Test needed to inspect these properties: Version(" << TestVersion() << "), Latest(" << TestLatest() << ")"); return TestVersion() && TestLatest(); } else { AICLI_LOG(CLI, Verbose, << "Package::Test was false because the package was not installed"); return false; } } else { AICLI_LOG(CLI, Verbose, << "Package::Test desired the package to not exist, and it " << (Output.Exist().value() ? "did" : "did not")); return !Output.Exist().value(); } } Json::Value DiffJson() { // Need to populate Output before calling THROW_HR_IF(E_UNEXPECTED, !Output.Exist().has_value()); Json::Value result{ Json::ValueType::arrayValue }; if (Input.ShouldExist() != Output.Exist().value()) { result.append(std::string{ StandardExistProperty::Name() }); } else { if (!TestVersion()) { result.append(std::string{ VersionProperty::Name() }); } if (!TestLatest()) { result.append(std::string{ UseLatestProperty::Name() }); } } return result; } bool TestVersion() { if (Input.Version()) { if (Output.Version()) { return Utility::Version{ Input.Version().value() } == Utility::Version{ Output.Version().value() }; } else { return false; } } else { return true; } } bool TestLatest() { if (Input.UseLatest() && Input.UseLatest().value()) { if (Output.UseLatest()) { return Output.UseLatest().value(); } else { return false; } } else { return true; } } }; } DscPackageResource::DscPackageResource(std::string_view parent) : DscCommandBase(parent, "package", DscResourceKind::Resource, DscFunctions::Get | DscFunctions::Set | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema, DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::HandlesExist | DscFunctionModifiers::ReturnsStateAndDiff) { } Resource::LocString DscPackageResource::ShortDescription() const { return Resource::String::DscPackageResourceShortDescription; } Resource::LocString DscPackageResource::LongDescription() const { return Resource::String::DscPackageResourceLongDescription; } std::string DscPackageResource::ResourceType() const { return "Package"; } void DscPackageResource::ResourceFunctionGet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { PackageFunctionData data{ context, json }; if (!data.Get()) { return; } WriteJsonOutputLine(context, data.Output.ToJson()); } } void DscPackageResource::ResourceFunctionSet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { PackageFunctionData data{ context, json }; if (!data.Get()) { return; } // Capture the diff before updating the output auto diff = data.DiffJson(); if (!data.Test()) { if (data.Input.ShouldExist()) { if (data.Output.Exist().value()) { if (!data.TestLatest()) { // Install will swap to update flow AICLI_LOG(CLI, Info, << "Installing package to update to latest"); data.Install(); } else // (!data.TestVersion()) { Utility::Version inputVersion{ data.Input.Version().value() }; Utility::Version outputVersion{ data.Output.Version().value() }; if (outputVersion < inputVersion) { // Install will swap to update flow AICLI_LOG(CLI, Info, << "Installing package to update to desired version"); data.Install(); } else { AICLI_LOG(CLI, Info, << "Reinstalling package to downgrade to desired version"); data.Reinstall(); } } } else { AICLI_LOG(CLI, Info, << "Installing package as it was not found"); data.Install(); } } else { AICLI_LOG(CLI, Info, << "Uninstalling package as desired"); data.Uninstall(); } if (data.SubContext->IsTerminated()) { return; } } WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, diff); } } void DscPackageResource::ResourceFunctionTest(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { PackageFunctionData data{ context, json }; if (!data.Get()) { return; } data.Output.InDesiredState(data.Test()); WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, data.DiffJson()); } } void DscPackageResource::ResourceFunctionExport(Execution::Context& context) const { auto json = GetJsonFromInput(context, false); PackageFunctionData data{ context, json, true }; data.PrepareSubContextInputs(); if (!data.Input.UseLatest().value_or(true)) { data.SubContext->Args.AddArg(Execution::Args::Type::IncludeVersions); } data.SubContext->Args.AddArg(Execution::Args::Type::ConfigurationExportAll); *data.SubContext << Workflow::SearchSourceForPackageExport; if (data.SubContext->IsTerminated()) { context.Terminate(data.SubContext->GetTerminationHR()); return; } const auto& packageCollection = data.SubContext->Get(); for (const auto& source : packageCollection.Sources) { for (const auto& package : source.Packages) { PackageResourceObject output; output.Identifier(package.Id); output.Source(source.Details.Name); if (!package.VersionAndChannel.GetVersion().IsEmpty()) { output.Version(package.VersionAndChannel.GetVersion().ToString()); } // TODO: Exporting scope requires one or more of the following: // 1. Support for "OrUnknown" scope variants during Set (and workflows) // 2. Tracking scope intent as we do for some other installer properties // 3. Checking for the availability of the current scope in the package WriteJsonOutputLine(context, output.ToJson()); } } } void DscPackageResource::ResourceFunctionSchema(Execution::Context& context) const { WriteJsonOutputLine(context, PackageResourceObject::Schema(ResourceType())); } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscPackageResource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "DscCommandBase.h" namespace AppInstaller::CLI { // A resource for managing package state. struct DscPackageResource : public DscCommandBase { DscPackageResource(std::string_view parent); Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: std::string ResourceType() const override; void ResourceFunctionGet(Execution::Context& context) const override; void ResourceFunctionSet(Execution::Context& context) const override; void ResourceFunctionTest(Execution::Context& context) const override; void ResourceFunctionExport(Execution::Context& context) const override; void ResourceFunctionSchema(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DscSourceResource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscSourceResource.h" #include "DscComposableObject.h" #include "Resources.h" #include "Workflows/SourceFlow.h" #include #include using namespace AppInstaller::Utility::literals; using namespace AppInstaller::Repository; namespace AppInstaller::CLI { namespace { WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(NameProperty, std::string, SourceName, "name", DscComposablePropertyFlag::Required | DscComposablePropertyFlag::CopyToOutput, Resource::String::DscResourcePropertyDescriptionSourceName); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(ArgumentProperty, std::string, Argument, "argument", Resource::String::DscResourcePropertyDescriptionSourceArgument); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(TypeProperty, std::string, Type, "type", Resource::String::DscResourcePropertyDescriptionSourceType); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(TrustLevelProperty, std::string, TrustLevel, "trustLevel", Resource::String::DscResourcePropertyDescriptionSourceTrustLevel, ({ "undefined", "none", "trusted" }), "undefined"); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(ExplicitProperty, bool, Explicit, "explicit", Resource::String::DscResourcePropertyDescriptionSourceExplicit); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(AcceptAgreementsProperty, bool, AcceptAgreements, "acceptAgreements", Resource::String::DscResourcePropertyDescriptionAcceptAgreements); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(PriorityProperty, int32_t, Priority, "priority", Resource::String::DscResourcePropertyDescriptionSourcePriority); using SourceResourceObject = DscComposableObject; std::string TrustLevelStringFromFlags(SourceTrustLevel trustLevel) { return WI_IsFlagSet(trustLevel, SourceTrustLevel::Trusted) ? "trusted" : "none"; } // The values as the resource uses them. enum class ResourceTrustLevel { Undefined, Invalid, None, Trusted }; ResourceTrustLevel EffectiveTrustLevel(const std::optional& input) { if (!input) { return ResourceTrustLevel::Undefined; } std::string inputValue = Utility::ToLower(input.value()); if (inputValue == "undefined") { return ResourceTrustLevel::Undefined; } else if (inputValue == "none") { return ResourceTrustLevel::None; } else if (inputValue == "trusted") { return ResourceTrustLevel::Trusted; } else { return ResourceTrustLevel::Invalid; } } struct SourceFunctionData { SourceFunctionData(Execution::Context& context, const std::optional& json, bool ignoreFieldRequirements = false) : Input(json, ignoreFieldRequirements), ParentContext(context) { Reset(); } const SourceResourceObject Input; SourceResourceObject Output; Execution::Context& ParentContext; std::unique_ptr SubContext; // Reset the state that is modified by Get void Reset() { Output = Input.CopyForOutput(); SubContext = ParentContext.CreateSubContext(); SubContext->SetFlags(Execution::ContextFlag::DisableInteractivity); if (Input.AcceptAgreements().value_or(false)) { SubContext->Args.AddArg(Execution::Args::Type::AcceptSourceAgreements); } } // Fills the Output object with the current state void Get() { auto currentSources = Repository::Source::GetCurrentSources(); const std::string& name = Input.SourceName().value(); Output.Exist(false); for (auto const& source : currentSources) { if (Utility::ICUCaseInsensitiveEquals(source.Name, name)) { Output.Exist(true); Output.Argument(source.Arg); Output.Type(source.Type); Output.TrustLevel(TrustLevelStringFromFlags(source.TrustLevel)); Output.Explicit(source.Explicit); if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority)) { Output.Priority(source.Priority); } std::vector sources; sources.emplace_back(source); SubContext->Add(std::move(sources)); break; } } AICLI_LOG(CLI, Verbose, << "Source::Get found:\n" << Json::writeString(Json::StreamWriterBuilder{}, Output.ToJson())); } void Add() { AICLI_LOG(CLI, Verbose, << "Source::Add invoked"); if (!SubContext->Args.Contains(Execution::Args::Type::SourceName)) { SubContext->Args.AddArg(Execution::Args::Type::SourceName, Input.SourceName().value()); } THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !Input.Argument().has_value()); SubContext->Args.AddArg(Execution::Args::Type::SourceArg, Input.Argument().value()); if (Input.Type()) { SubContext->Args.AddArg(Execution::Args::Type::SourceType, Input.Type().value()); } ResourceTrustLevel effectiveTrustLevel = EffectiveTrustLevel(Input.TrustLevel()); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, effectiveTrustLevel == ResourceTrustLevel::Invalid); if (effectiveTrustLevel == ResourceTrustLevel::Trusted) { SubContext->Args.AddArg(Execution::Args::Type::SourceTrustLevel, TrustLevelStringFromFlags(SourceTrustLevel::Trusted)); } if (Input.Explicit().value_or(false)) { SubContext->Args.AddArg(Execution::Args::Type::SourceExplicit); } std::string priorityString; if (Input.Priority()) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED, !Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority)); priorityString = std::to_string(Input.Priority().value()); SubContext->Args.AddArg(Execution::Args::Type::SourcePriority, priorityString); } *SubContext << Workflow::EnsureRunningAsAdmin << Workflow::CreateSourceForSourceAdd << Workflow::AddSource; } void Remove() { AICLI_LOG(CLI, Verbose, << "Source::Remove invoked"); if (!SubContext->Args.Contains(Execution::Args::Type::SourceName)) { SubContext->Args.AddArg(Execution::Args::Type::SourceName, Input.SourceName().value()); } *SubContext << Workflow::EnsureRunningAsAdmin << Workflow::RemoveSources; } void Edit() { AICLI_LOG(CLI, Verbose, << "Source::Edit invoked"); if (!SubContext->Args.Contains(Execution::Args::Type::SourceName)) { SubContext->Args.AddArg(Execution::Args::Type::SourceName, Input.SourceName().value()); } std::string explicitString; if (Input.Explicit()) { explicitString = Utility::ConvertBoolToString(Input.Explicit().value()); SubContext->Args.AddArg(Execution::Args::Type::SourceEditExplicit, explicitString); } std::string priorityString; if (Input.Priority()) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED, !Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority)); priorityString = std::to_string(Input.Priority().value()); SubContext->Args.AddArg(Execution::Args::Type::SourcePriority, priorityString); } *SubContext << Workflow::EnsureRunningAsAdmin << Workflow::EditSources; } void Replace() { AICLI_LOG(CLI, Verbose, << "Source::Replace invoked"); // Check to see if we can use an edit rather than a complete replacement if (TestArgument() && TestType() && TestTrustLevel()) { // Implies that the failing portion of Test was in the editable Explicit or Priority properties Edit(); } else { Remove(); Add(); } } // Determines if the current Output values match the Input values state. bool Test() { // Need to populate Output before calling THROW_HR_IF(E_UNEXPECTED, !Output.Exist().has_value()); if (Input.ShouldExist()) { if (Output.Exist().value()) { AICLI_LOG(CLI, Verbose, << "Source::Test needed to inspect these properties: Argument(" << TestArgument() << "), Type(" << TestType() << "), TrustLevel(" << TestTrustLevel() << "), Explicit(" << TestExplicit() << "), Priority(" << TestPriority() << ")"); return TestArgument() && TestType() && TestTrustLevel() && TestExplicit() && TestPriority(); } else { AICLI_LOG(CLI, Verbose, << "Source::Test was false because the source is not present"); return false; } } else { AICLI_LOG(CLI, Verbose, << "Source::Test desired the source to not exist, and it " << (Output.Exist().value() ? "did" : "did not")); return !Output.Exist().value(); } } Json::Value DiffJson() { // Need to populate Output before calling THROW_HR_IF(E_UNEXPECTED, !Output.Exist().has_value()); Json::Value result{ Json::ValueType::arrayValue }; if (Input.ShouldExist() != Output.Exist().value()) { result.append(std::string{ StandardExistProperty::Name() }); } else { if (!TestArgument()) { result.append(std::string{ ArgumentProperty::Name() }); } if (!TestType()) { result.append(std::string{ TypeProperty::Name() }); } if (!TestTrustLevel()) { result.append(std::string{ TrustLevelProperty::Name() }); } if (!TestExplicit()) { result.append(std::string{ ExplicitProperty::Name() }); } if (!TestPriority()) { result.append(std::string{ PriorityProperty::Name() }); } } return result; } bool TestArgument() { if (Input.Argument()) { if (Output.Argument()) { return Input.Argument().value() == Output.Argument().value(); } else { return false; } } else { return true; } } bool TestType() { if (Input.Type()) { if (Output.Type()) { return Utility::CaseInsensitiveEquals(Input.Type().value(), Output.Type().value()); } else { return false; } } else { return true; } } bool TestTrustLevel() { auto inputTrustLevel = EffectiveTrustLevel(Input.TrustLevel()); if (inputTrustLevel != ResourceTrustLevel::Undefined) { return inputTrustLevel == EffectiveTrustLevel(Output.TrustLevel()); } else { return true; } } bool TestExplicit() { if (Input.Explicit()) { if (Output.Explicit()) { return Input.Explicit().value() == Output.Explicit().value(); } else { return false; } } else { return true; } } bool TestPriority() { if (Input.Priority()) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED, !Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority)); if (Output.Priority()) { return Input.Priority().value() == Output.Priority().value(); } else { return false; } } else { return true; } } }; } DscSourceResource::DscSourceResource(std::string_view parent) : DscCommandBase(parent, "source", DscResourceKind::Resource, DscFunctions::Get | DscFunctions::Set | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema, DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::HandlesExist | DscFunctionModifiers::ReturnsStateAndDiff) { } Resource::LocString DscSourceResource::ShortDescription() const { return Resource::String::DscSourceResourceShortDescription; } Resource::LocString DscSourceResource::LongDescription() const { return Resource::String::DscSourceResourceLongDescription; } std::string DscSourceResource::ResourceType() const { return "Source"; } void DscSourceResource::ResourceFunctionGet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { SourceFunctionData data{ context, json }; data.Get(); WriteJsonOutputLine(context, data.Output.ToJson()); } } void DscSourceResource::ResourceFunctionSet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { SourceFunctionData data{ context, json }; data.Get(); // Capture the diff before updating the output auto diff = data.DiffJson(); if (!data.Test()) { if (data.Input.ShouldExist()) { if (data.Output.Exist().value()) { AICLI_LOG(CLI, Info, << "Replacing source with new information"); data.Replace(); } else { AICLI_LOG(CLI, Info, << "Adding source as it was not found"); data.Add(); } } else { AICLI_LOG(CLI, Info, << "Removing source as desired"); data.Remove(); } if (data.SubContext->IsTerminated()) { data.ParentContext.Terminate(data.SubContext->GetTerminationHR()); return; } data.Reset(); data.Get(); } WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, diff); } } void DscSourceResource::ResourceFunctionTest(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { SourceFunctionData data{ context, json }; data.Get(); data.Output.InDesiredState(data.Test()); WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, data.DiffJson()); } } void DscSourceResource::ResourceFunctionExport(Execution::Context& context) const { auto currentSources = Repository::Source::GetCurrentSources(); for (auto const& source : currentSources) { SourceResourceObject output; output.SourceName(source.Name); output.Argument(source.Arg); output.Type(source.Type); output.TrustLevel(TrustLevelStringFromFlags(source.TrustLevel)); output.Explicit(source.Explicit); if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::SourcePriority)) { output.Priority(source.Priority); } WriteJsonOutputLine(context, output.ToJson()); } } void DscSourceResource::ResourceFunctionSchema(Execution::Context& context) const { WriteJsonOutputLine(context, SourceResourceObject::Schema(ResourceType())); } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscSourceResource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "DscCommandBase.h" namespace AppInstaller::CLI { // A resource for managing source configuration. struct DscSourceResource : public DscCommandBase { DscSourceResource(std::string_view parent); Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: std::string ResourceType() const override; void ResourceFunctionGet(Execution::Context& context) const override; void ResourceFunctionSet(Execution::Context& context) const override; void ResourceFunctionTest(Execution::Context& context) const override; void ResourceFunctionExport(Execution::Context& context) const override; void ResourceFunctionSchema(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DscTestFileResource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscTestFileResource.h" #include "DscComposableObject.h" using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI { namespace { WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(PathProperty, std::string, Path, "path", DscComposablePropertyFlag::Required | DscComposablePropertyFlag::CopyToOutput, "The absolute path to a file."_lis); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(ContentProperty, std::string, Content, "content", "The content of the file."_lis); using TestFileObject = DscComposableObject; struct TestFileFunctionData { TestFileFunctionData(const std::optional& json) : Input(json), Output(Input.CopyForOutput()) { Path = Utility::ConvertToUTF16(Input.Path().value()); THROW_HR_IF(E_INVALIDARG, !Path.is_absolute()); } TestFileObject Input; TestFileObject Output; std::filesystem::path Path; // Fills the Output object with the current state void Get() { if (std::filesystem::exists(Path) && std::filesystem::is_regular_file(Path)) { Output.Exist(true); std::ifstream stream{ Path, std::ios::binary }; Output.Content(Utility::ReadEntireStream(stream)); } else { Output.Exist(false); } } // Determines if the current Output values match the Input values state. bool Test() { // Need to populate Output before calling THROW_HR_IF(E_UNEXPECTED, !Output.Exist().has_value()); if (Input.ShouldExist()) { if (Output.Exist().value()) { return ContentMatches(); } else { return false; } } else { return !Output.Exist().value(); } } Json::Value DiffJson() { // Need to populate Output before calling THROW_HR_IF(E_UNEXPECTED, !Output.Exist().has_value()); Json::Value result{ Json::ValueType::arrayValue }; if (Input.ShouldExist() != Output.Exist().value()) { result.append(std::string{ StandardExistProperty::Name() }); } else { if (!ContentMatches()) { result.append(std::string{ ContentProperty::Name() }); } } return result; } private: bool ContentMatches() { bool hasInput = Input.Content().has_value() && !Input.Content().value().empty(); bool hasOutput = Output.Content().has_value() && !Output.Content().value().empty(); return (hasInput && hasOutput && Input.Content().value() == Output.Content().value()) || (!hasInput && !hasOutput); } }; } DscTestFileResource::DscTestFileResource(std::string_view parent) : DscCommandBase(parent, "test-file", DscResourceKind::Resource, DscFunctions::Get | DscFunctions::Set | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema, DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::HandlesExist | DscFunctionModifiers::ReturnsStateAndDiff) { } Resource::LocString DscTestFileResource::ShortDescription() const { return "[TEST] File content resource"_lis; } Resource::LocString DscTestFileResource::LongDescription() const { return "[TEST] This resource is only available for tests. It provides file content configuration."_lis; } std::string DscTestFileResource::ResourceType() const { return "TestFile"; } void DscTestFileResource::ResourceFunctionGet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { TestFileFunctionData data{ json }; data.Get(); WriteJsonOutputLine(context, data.Output.ToJson()); } } void DscTestFileResource::ResourceFunctionSet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { TestFileFunctionData data{ json }; data.Get(); if (!data.Test()) { bool exists = std::filesystem::exists(data.Path); if (exists) { // Don't delete a directory or other special files in this test resource THROW_WIN32_IF(ERROR_DIRECTORY_NOT_SUPPORTED, !std::filesystem::is_regular_file(data.Path)); } if (data.Input.ShouldExist()) { std::filesystem::create_directories(data.Path.parent_path()); std::ofstream stream{ data.Path, std::ios::binary | std::ios::trunc }; if (data.Input.Content()) { stream.write(data.Input.Content().value().c_str(), data.Input.Content().value().length()); } } else if (exists) { std::filesystem::remove(data.Path); } } // Capture the diff before updating the output auto diff = data.DiffJson(); data.Output.Exist(data.Input.ShouldExist()); if (data.Output.Exist().value()) { data.Output.Content(data.Input.Content().value_or("")); } WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, diff); } } void DscTestFileResource::ResourceFunctionTest(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { TestFileFunctionData data{ json }; data.Get(); data.Output.InDesiredState(data.Test()); WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, data.DiffJson()); } } void DscTestFileResource::ResourceFunctionExport(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { TestFileFunctionData data{ json }; if (std::filesystem::exists(data.Path)) { if (std::filesystem::is_regular_file(data.Path)) { data.Get(); WriteJsonOutputLine(context, data.Output.ToJson()); } else if (std::filesystem::is_directory(data.Path)) { for (const auto& file : std::filesystem::directory_iterator{ data.Path }) { if (std::filesystem::is_regular_file(file)) { TestFileObject output; output.Path(file.path().u8string()); std::ifstream stream{ file.path(), std::ios::binary}; output.Content(Utility::ReadEntireStream(stream)); WriteJsonOutputLine(context, output.ToJson()); } } } } } } void DscTestFileResource::ResourceFunctionSchema(Execution::Context& context) const { WriteJsonOutputLine(context, TestFileObject::Schema(ResourceType())); } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscTestFileResource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "DscCommandBase.h" namespace AppInstaller::CLI { // A test resource implementing file content configuration. struct DscTestFileResource : public DscCommandBase { DscTestFileResource(std::string_view parent); Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: std::string ResourceType() const override; void ResourceFunctionGet(Execution::Context& context) const override; void ResourceFunctionSet(Execution::Context& context) const override; void ResourceFunctionTest(Execution::Context& context) const override; void ResourceFunctionExport(Execution::Context& context) const override; void ResourceFunctionSchema(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DscTestJsonResource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscTestJsonResource.h" #include "DscComposableObject.h" #include using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI { namespace { WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(PropertyProperty, std::string, Property, "property", DscComposablePropertyFlag::Required | DscComposablePropertyFlag::CopyToOutput, "The JSON property name."_lis); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(ValueProperty, Json::Value, Value, "value", "The value for the JSON property."_lis); using TestJsonObject = DscComposableObject; struct TestJsonFunctionData { TestJsonFunctionData() { InitializeFileData(); } TestJsonFunctionData(const std::optional& json) : Input(json), Output(Input.CopyForOutput()) { InitializeFileData(); } TestJsonObject Input; TestJsonObject Output; std::filesystem::path FilePath; Json::Value RootValue; static std::filesystem::path GetFilePath() { std::filesystem::path result = Runtime::GetPathTo(Runtime::PathName::LocalState); result /= "test-json-file.json"; return result; } // Fills the Output object with the current state void Get() { const std::string& propertyName = Input.Property().value(); const Json::Value* propertyValue = RootValue.find(propertyName.data(), propertyName.data() + propertyName.length()); if (propertyValue) { Output.Exist(true); Output.Value(*propertyValue); } else { Output.Exist(false); } } private: void InitializeFileData() { FilePath = GetFilePath(); RootValue = GetJsonFromFile(); } Json::Value GetJsonFromFile() const { Json::Value result; Json::CharReaderBuilder builder; Json::String errors; std::ifstream stream{ FilePath, std::ios::binary }; if (stream) { if (!Json::parseFromStream(builder, stream, &result, &errors)) { AICLI_LOG(CLI, Warning, << "Failed to read test JSON file: " << errors); result = Json::Value{}; } } else { AICLI_LOG(CLI, Warning, << "Couldn't open test JSON file: " << FilePath); } return result; } }; } DscTestJsonResource::DscTestJsonResource(std::string_view parent) : DscCommandBase(parent, "test-json", DscResourceKind::Resource, DscFunctions::Get | DscFunctions::Set | DscFunctions::Export | DscFunctions::Schema, DscFunctionModifiers::HandlesExist | DscFunctionModifiers::ReturnsState) { } std::vector DscTestJsonResource::GetArguments() const { auto result = DscCommandBase::GetArguments(); result.emplace_back(Execution::Args::Type::DscResourceFunctionDelete, Resource::String::DscResourceFunctionDescriptionDelete, ArgumentType::Flag); return result; } Resource::LocString DscTestJsonResource::ShortDescription() const { return "[TEST] JSON content resource"_lis; } Resource::LocString DscTestJsonResource::LongDescription() const { return "[TEST] This resource is only available for tests. It provides JSON content configuration of a well known file."_lis; } void DscTestJsonResource::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::DscResourceFunctionDelete)) { std::filesystem::remove_all(TestJsonFunctionData::GetFilePath()); return; } DscCommandBase::ExecuteInternal(context); } std::string DscTestJsonResource::ResourceType() const { return "TestJSON"; } void DscTestJsonResource::ResourceFunctionGet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { TestJsonFunctionData data{ json }; data.Get(); WriteJsonOutputLine(context, data.Output.ToJson()); } } void DscTestJsonResource::ResourceFunctionSet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { TestJsonFunctionData data{ json }; data.Get(); if (data.RootValue.isNull()) { data.RootValue = Json::Value{ Json::objectValue }; } if (data.Input.ShouldExist()) { data.RootValue[data.Input.Property().value()] = data.Input.Value().value_or(Json::Value{ Json::nullValue }); data.Output.Exist(true); data.Output.Value(data.RootValue[data.Input.Property().value()]); } else if (data.Output.Exist().value()) { data.RootValue.removeMember(data.Input.Property().value()); data.Output.Exist(false); } std::ofstream stream{ data.FilePath, std::ios::binary }; Json::StreamWriterBuilder writerBuilder; writerBuilder.settings_["indentation"] = " "; stream << Json::writeString(writerBuilder, data.RootValue); WriteJsonOutputLine(context, data.Output.ToJson()); } } void DscTestJsonResource::ResourceFunctionExport(Execution::Context& context) const { TestJsonFunctionData data; if (data.RootValue.isObject()) { for (const auto& member : data.RootValue.getMemberNames()) { const Json::Value* memberValue = data.RootValue.find(member.data(), member.data() + member.length()); if (memberValue) { TestJsonObject output; output.Property(member); output.Value(*memberValue); WriteJsonOutputLine(context, output.ToJson()); } } } } void DscTestJsonResource::ResourceFunctionSchema(Execution::Context& context) const { WriteJsonOutputLine(context, TestJsonObject::Schema(ResourceType())); } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscTestJsonResource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "DscCommandBase.h" namespace AppInstaller::CLI { // A test resource implementing JSON content configuration of a well known file. // This is exists to enable an input-less export, which is required in DSC v3.0.0 struct DscTestJsonResource : public DscCommandBase { DscTestJsonResource(std::string_view parent); std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; std::string ResourceType() const override; void ResourceFunctionGet(Execution::Context& context) const override; void ResourceFunctionSet(Execution::Context& context) const override; void ResourceFunctionExport(Execution::Context& context) const override; void ResourceFunctionSchema(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/DscUserSettingsFileResource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DscUserSettingsFileResource.h" #include "DscComposableObject.h" #include "Resources.h" #include "AppInstallerStrings.h" using namespace AppInstaller::Utility::literals; using namespace AppInstaller::Settings; #define ACTION_FULL "Full" #define ACTION_PARTIAL "Partial" namespace AppInstaller::CLI { namespace { WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(SettingsProperty, Json::Value, Settings, "settings", DscComposablePropertyFlag::Required | DscComposablePropertyFlag::CopyToOutput, Resource::String::DscResourcePropertyDescriptionUserSettingsFileSettings); WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(ActionProperty, std::string, Action, "action", Resource::String::DscResourcePropertyDescriptionUserSettingsFileAction, ({ ACTION_PARTIAL, ACTION_FULL }), ACTION_PARTIAL); using UserSettingsFileResourceObject = DscComposableObject; struct UserSettingsFileFunctionData { UserSettingsFileFunctionData() : UserSettingsFileFunctionData(std::nullopt, true) { } UserSettingsFileFunctionData(const std::optional& json, bool ignoreFieldRequirements = false) : Input(json, ignoreFieldRequirements), _userSettingsPath(UserSettings::SettingsFilePath()) { } const UserSettingsFileResourceObject Input; UserSettingsFileResourceObject Output; void Get() { Output.Settings(GetUserSettings()); } bool Test() { return GetResolvedInput() == Output.Settings(); } Json::Value DiffJson() { Json::Value result{ Json::ValueType::arrayValue }; if (!Test()) { result.append(std::string{ SettingsProperty::Name() }); } return result; } const Json::Value& GetResolvedInput() { THROW_HR_IF(E_UNEXPECTED, !Input.Settings().has_value()); if (!_resolvedInputUserSettings) { if(Input.Action().has_value() && Utility::CaseInsensitiveEquals(Input.Action().value(), ACTION_FULL)) { Output.Action(ACTION_FULL); _resolvedInputUserSettings = Input.Settings(); } else { Output.Action(ACTION_PARTIAL); _resolvedInputUserSettings = MergeUserSettingsFiles(*Input.Settings()); } } return *_resolvedInputUserSettings; } bool WriteOutput() { THROW_HR_IF(E_UNEXPECTED, !Output.Settings().has_value()); std::ofstream file(_userSettingsPath, std::ios::binary); if (file) { Json::StreamWriterBuilder writer; writer["indentation"] = " "; file << Json::writeString(writer, *Output.Settings()); return true; } AICLI_LOG(Config, Error, << "Failed to open or create user settings file: " << _userSettingsPath); return false; } private: std::filesystem::path _userSettingsPath; std::optional _userSettings; std::optional _resolvedInputUserSettings; Json::Value MergeUserSettingsFiles(const Json::Value& overlay) { Json::Value mergedUserSettingsFile = GetUserSettings(); MergeUserSettingsFiles(mergedUserSettingsFile, overlay); return mergedUserSettingsFile; } // Merges the overlay settings into the target settings. void MergeUserSettingsFiles(Json::Value& target, const Json::Value& overlay) { // If either is not an object, we can't merge. if (!overlay.isObject() || !target.isObject()) { return; } // Iterate through the overlay settings and merge them into the target. for (const auto& overlayKey : overlay.getMemberNames()) { const Json::Value& overlayValue = overlay[overlayKey]; if (target.isMember(overlayKey)) { Json::Value& targetValue = target[overlayKey]; if (targetValue.isObject() && overlayValue.isObject()) { // Recursively merge the objects. MergeUserSettingsFiles(targetValue, overlayValue); } else { // Replace the value in the target. // Note: Arrays are not merged, they are replaced. target[overlayKey] = overlayValue; } } else { // Add the overlay key to the target. target[overlayKey] = overlayValue; } } } const Json::Value& GetUserSettings() { if (!_userSettings) { _userSettings = Json::objectValue; std::ifstream file(_userSettingsPath, std::ios::binary); if (file) { Json::CharReaderBuilder builder; std::string errs; Json::Value jsonRoot; if (Json::parseFromStream(builder, file, &jsonRoot, &errs)) { _userSettings = jsonRoot; } else { AICLI_LOG(Config, Warning, << "Failed to parse user settings file: " << _userSettingsPath << ", error: " << errs); } } else { AICLI_LOG(Config, Warning, << "Failed to open user settings file: " << _userSettingsPath); } } return *_userSettings; } }; } DscUserSettingsFileResource::DscUserSettingsFileResource(std::string_view parent) : DscCommandBase(parent, "user-settings-file", DscResourceKind::Resource, DscFunctions::Get | DscFunctions::Set | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema, DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::HandlesExist | DscFunctionModifiers::ReturnsStateAndDiff) { } Resource::LocString DscUserSettingsFileResource::ShortDescription() const { return Resource::String::DscUserSettingsFileShortDescription; } Resource::LocString DscUserSettingsFileResource::LongDescription() const { return Resource::String::DscUserSettingsFileLongDescription; } std::string DscUserSettingsFileResource::ResourceType() const { return "UserSettingsFile"; } void DscUserSettingsFileResource::ResourceFunctionGet(Execution::Context& context) const { ResourceFunctionExport(context); } void DscUserSettingsFileResource::ResourceFunctionSet(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { UserSettingsFileFunctionData data{ json }; data.Get(); // Capture the diff before updating the output auto diff = data.DiffJson(); if (!data.Test()) { data.Output.Settings(data.GetResolvedInput()); if (!data.WriteOutput()) { AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_OPEN_FAILED)); return; } } WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, diff); } } void DscUserSettingsFileResource::ResourceFunctionTest(Execution::Context& context) const { if (auto json = GetJsonFromInput(context)) { UserSettingsFileFunctionData data{ json }; data.Get(); data.Output.InDesiredState(data.Test()); WriteJsonOutputLine(context, data.Output.ToJson()); WriteJsonOutputLine(context, data.DiffJson()); } } void DscUserSettingsFileResource::ResourceFunctionExport(Execution::Context& context) const { UserSettingsFileFunctionData data; data.Get(); WriteJsonOutputLine(context, data.Output.ToJson()); } void DscUserSettingsFileResource::ResourceFunctionSchema(Execution::Context& context) const { WriteJsonOutputLine(context, UserSettingsFileResourceObject::Schema(ResourceType())); } } ================================================ FILE: src/AppInstallerCLICore/Commands/DscUserSettingsFileResource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "DscCommandBase.h" namespace AppInstaller::CLI { // A resource for managing user settings file. struct DscUserSettingsFileResource : public DscCommandBase { DscUserSettingsFileResource(std::string_view parent); Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: std::string ResourceType() const override; void ResourceFunctionGet(Execution::Context& context) const override; void ResourceFunctionSet(Execution::Context& context) const override; void ResourceFunctionTest(Execution::Context& context) const override; void ResourceFunctionExport(Execution::Context& context) const override; void ResourceFunctionSchema(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ErrorCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ErrorCommand.h" #include "AppInstallerStrings.h" #include "AppInstallerErrors.h" #include "VTSupport.h" namespace AppInstaller::CLI { namespace { // 0x12345678 : SYMBOL_VALUE // Descriptive text void OutputHResultInformation(Execution::Context& context, const Errors::HResultInformation& error) { auto info = context.Reporter.Info(); info << VirtualTerminal::TextFormat::Foreground::Bright << "0x" << VirtualTerminal::TextFormat::Foreground::Bright << Logging::SetHRFormat << error.Value(); if (!error.Symbol().empty()) { info << " : "_liv << VirtualTerminal::TextFormat::Foreground::BrightCyan << error.Symbol(); } info << std::endl; info << error.GetDescription() << std::endl; } } std::vector ErrorCommand::GetArguments() const { return { Argument{ Execution::Args::Type::ErrorInput, Resource::String::ErrorInputArgumentDescription, ArgumentType::Positional, true }, }; } Resource::LocString ErrorCommand::ShortDescription() const { return { Resource::String::ErrorCommandShortDescription }; } Resource::LocString ErrorCommand::LongDescription() const { return { Resource::String::ErrorCommandLongDescription }; } void ErrorCommand::ExecuteInternal(Execution::Context& context) const { std::string input{ context.Args.GetArg(Execution::Args::Type::ErrorInput) }; const char* begin = input.c_str(); char* end = nullptr; errno = 0; HRESULT errorNumber = strtol(begin, &end, 0); if (errno == ERANGE) { errno = 0; unsigned long unsignedError = strtoul(begin, &end, 0); if (errno == ERANGE) { context.Reporter.Error() << Resource::String::ErrorNumberIsTooLarge << std::endl; AICLI_TERMINATE_CONTEXT(E_INVALIDARG); } errorNumber = static_cast(unsignedError); } // If the entire string was consumed as a number, treat it as an HRESULT if (static_cast(end - begin) == input.length()) { auto error = Errors::HResultInformation::Find(errorNumber); if (error) { OutputHResultInformation(context, *error); } } // otherwise, treat it as a string and search our error list else { auto errors = Errors::HResultInformation::Find(Utility::Trim(input)); for (const auto& error : errors) { OutputHResultInformation(context, *error); } } } } ================================================ FILE: src/AppInstallerCLICore/Commands/ErrorCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { // This command makes it easier to get information on winget errors. struct ErrorCommand final : public Command { ErrorCommand(std::string_view parent) : Command("error", { "err" }, parent, Visibility::Hidden) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ExperimentalCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ExperimentalCommand.h" #include namespace AppInstaller::CLI { using namespace Utility::literals; using namespace AppInstaller::Settings; using namespace std::string_view_literals; std::vector ExperimentalCommand::GetArguments() const { return { Argument::ForType(Execution::Args::Type::ExperimentalArg) }; } Resource::LocString ExperimentalCommand::ShortDescription() const { return { Resource::String::ExperimentalCommandShortDescription }; } Resource::LocString ExperimentalCommand::LongDescription() const { return { Resource::String::ExperimentalCommandLongDescription }; } Utility::LocIndView ExperimentalCommand::HelpLink() const { return "https://aka.ms/winget-settings"_liv; } void ExperimentalCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::ExperimentalArg)) { static constexpr std::string_view s_ninjaCat = R"( ->$$$^ -T}uymkZdqUdN3mowkh_ T06m\xrwingetxYxcky` wDNf3omhXucVi!"YyjL `VmhkuvrHG}VVT)~:_:! r!`-_ `hqf-.-TH3r"` !-`r^,`-!:^)kUdE" -jGhkyY*- 'T!YyTykzGNkxLxv- ,]Vyx!` `"kKPGHKzhHy)*>' `_|fZV*- `*\*~~*LuVkXzXUm3GGx|x` `<}cc*` _ryGNm]~- `_"!^**vL}ycVkh5MG}|L: !V0QMv=r|_`` `"rumdNbfyLr>!:",_--_""",---_!*iwHNNNZxv}XqbNNNNNNNNZX3RQBQQR98QTrrr*. `"*ThZNNNNNNNNNNNNNNNNNN9NNNNNNNNNN]VNNNbNNNNdNNNQ#BBBB80N8#@QZc}}^` `">vuhMNNNNNNNNNNN6$gENNNNNNR9&NKyZNNNRgd53GbN0#BBB$N60bNRNNNZ3XT! .:*]coPbNNNNNN$Q$NNNdNNN$QQ6NNNNN6QR5Xy3bNEdMMENNNbkT5NNNNN60T `-:^)YuyX3Z0QNNdMMZdN8QBQRNNN68Q6Z55dbMqqqGqdNNNUy#Ho5NNG3y `._=*vc0NZGKa5dEBBBBQQ8QQQ8NNNgGjjjjjx=:yNNNqgBN}` ~NZ3kyhq9B03oycuuymdNNN$ZvvxcqU~` :zN}Q#8: }bHk}cmZNcxxxxxxxYVhg9N3! .V3ZEm, .MZkZ@v `rPMKyVhG*-````-L8@@@@@@#x}3yvr:` r&^ 'cddoX. xd#N53Um9r m####BM rdGY. :PV:::,:- __` ^NN#BZ5EQv -QBQ$N3' `=^.!>;,.` `-,"::",:r!,~ ~q68#@@@Dx` rQ86Nd" .=;~- `x-- `P9BBbx:` ~9NNX- _~;!' -^)*:," rN6QZ` `KNNZ_ `"x*' -^ yNgD5^` XNNNy >)`` .NNg#NQa` ^qN$N}. `:r^ ZNk#QNQQdx- !N@#RgQy,- _Zy ,h03o= ` -!G@BNQ@#85T. ` ` :*vxL`)L! ' )"sv; context.Reporter.Info() << s_ninjaCat << std::endl; } context.Reporter.Info() << Resource::String::ThankYou << std::endl; } } ================================================ FILE: src/AppInstallerCLICore/Commands/ExperimentalCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #include namespace AppInstaller::CLI { struct ExperimentalCommand final : public Command { // This command is used as an example on how experimental features can be used. // To enable this command set ExperimentalCmd = true in the settings file. ExperimentalCommand(std::string_view parent) : Command("experimental", {}, parent, Settings::ExperimentalFeature::Feature::ExperimentalCmd) {} virtual std::vector GetArguments() const override; virtual Resource::LocString ShortDescription() const override; virtual Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ExportCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ExportCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/ImportExportFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Workflow; using namespace std::string_view_literals; std::vector ExportCommand::GetArguments() const { return { Argument{ Execution::Args::Type::OutputFile, Resource::String::OutputFileArgumentDescription, ArgumentType::Positional, true }, Argument{ Execution::Args::Type::Source, Resource::String::ExportSourceArgumentDescription, ArgumentType::Standard }, Argument{ Execution::Args::Type::IncludeVersions, Resource::String::ExportIncludeVersionsArgumentDescription, ArgumentType::Flag }, Argument::ForType(Execution::Args::Type::AcceptSourceAgreements), }; } Resource::LocString ExportCommand::ShortDescription() const { return { Resource::String::ExportCommandShortDescription }; } Resource::LocString ExportCommand::LongDescription() const { return { Resource::String::ExportCommandLongDescription }; } void ExportCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const { if (valueType == Execution::Args::Type::OutputFile) { // Intentionally output nothing to allow pass through to filesystem. return; } if (valueType == Execution::Args::Type::Source) { context << Workflow::CompleteSourceName; return; } } Utility::LocIndView ExportCommand::HelpLink() const { return "https://aka.ms/winget-command-export"_liv; } void ExportCommand::ExecuteInternal(Execution::Context& context) const { context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed) << Workflow::SearchSourceForMany << Workflow::HandleSearchResultFailures << Workflow::EnsureMatchesFromSearchResult(OperationType::Export) << Workflow::SelectVersionsToExport << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::WriteImportFile; } } ================================================ FILE: src/AppInstallerCLICore/Commands/ExportCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { // Command to get the set of installed packages on the system. struct ExportCommand final : public Command { ExportCommand(std::string_view parent) : Command("export", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/FeaturesCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "FeaturesCommand.h" #include "TableOutput.h" #include namespace AppInstaller::CLI { using namespace Utility::literals; using namespace AppInstaller::Settings; Resource::LocString FeaturesCommand::ShortDescription() const { return { Resource::String::FeaturesCommandShortDescription }; } Resource::LocString FeaturesCommand::LongDescription() const { return { Resource::String::FeaturesCommandLongDescription }; } Utility::LocIndView FeaturesCommand::HelpLink() const { return "https://aka.ms/winget-experimentalfeatures"_liv; } void FeaturesCommand::ExecuteInternal(Execution::Context& context) const { #ifdef WINGET_DISABLE_EXPERIMENTAL_FEATURES context.Reporter.Info() << Resource::String::FeaturesMessageDisabledByBuild << std::endl; #else if (GroupPolicies().IsEnabled(TogglePolicy::Policy::ExperimentalFeatures) && GroupPolicies().IsEnabled(TogglePolicy::Policy::Settings)) { context.Reporter.Info() << Resource::String::FeaturesMessage << std::endl << std::endl; } else { context.Reporter.Info() << Resource::String::FeaturesMessageDisabledByPolicy << std::endl << std::endl; } auto features = ExperimentalFeature::GetAllFeatures(); if (!features.empty()) { Execution::TableOutput<4> table(context.Reporter, { Resource::String::FeaturesFeature, Resource::String::FeaturesStatus, Resource::String::FeaturesProperty, Resource::String::FeaturesLink }); for (const auto& feature : features) { table.OutputLine({ std::string{ feature.Name() }, Resource::LocString{ ExperimentalFeature::IsEnabled(feature.GetFeature()) ? Resource::String::FeaturesEnabled : Resource::String::FeaturesDisabled}, std::string { feature.JsonName() }, std::string{ feature.Link() } }); } table.Complete(); } else { // Better work hard to get some out there! context.Reporter.Info() << Resource::String::NoExperimentalFeaturesMessage << std::endl; } #endif } } ================================================ FILE: src/AppInstallerCLICore/Commands/FeaturesCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #include namespace AppInstaller::CLI { struct FeaturesCommand final : public Command { // This command outputs all the experimental features that are available, if they are enabled/disabled // and a link to the spec. FeaturesCommand(std::string_view parent) : Command("features", parent) {} virtual Resource::LocString ShortDescription() const override; virtual Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } #pragma once ================================================ FILE: src/AppInstallerCLICore/Commands/FontCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "FontCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/FontFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Utility::literals; using namespace std::string_view_literals; Utility::LocIndView s_FontCommand_HelpLink = "https://aka.ms/winget-command-font"_liv; // Base Font Command std::vector> FontCommand::GetCommands() const { return InitializeFromMoveOnly>>({ std::make_unique(FullName()), }); } Resource::LocString FontCommand::ShortDescription() const { return { Resource::String::FontCommandShortDescription }; } Resource::LocString FontCommand::LongDescription() const { return { Resource::String::FontCommandLongDescription }; } Utility::LocIndView FontCommand::HelpLink() const { return s_FontCommand_HelpLink; } void FontCommand::ExecuteInternal(Execution::Context& context) const { OutputHelp(context.Reporter); } // FontListCommand std::vector FontListCommand::GetArguments() const { return { Argument::ForType(Args::Type::Family), Argument::ForType(Args::Type::Details), }; } Resource::LocString FontListCommand::ShortDescription() const { return { Resource::String::FontListCommandShortDescription }; } Resource::LocString FontListCommand::LongDescription() const { return { Resource::String::FontListCommandLongDescription }; } void FontListCommand::Complete(Execution::Context& context, Args::Type valueType) const { UNREFERENCED_PARAMETER(valueType); context.Reporter.Error() << Resource::String::PendingWorkError << std::endl; THROW_HR(E_NOTIMPL); } Utility::LocIndView FontListCommand::HelpLink() const { return s_FontCommand_HelpLink; } void FontListCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::ReportInstalledFonts; } } ================================================ FILE: src/AppInstallerCLICore/Commands/FontCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #include namespace AppInstaller::CLI { struct FontCommand final : public Command { FontCommand(std::string_view parent) : Command("font", { "fonts" }, parent, Settings::ExperimentalFeature::Feature::Font) {} std::vector> GetCommands() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct FontListCommand final : public Command { FontListCommand(std::string_view parent) : Command("list", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/HashCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "HashCommand.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" #include namespace AppInstaller::CLI { using namespace std::string_view_literals; using namespace Utility::literals; std::vector HashCommand::GetArguments() const { return { Argument::ForType(Execution::Args::Type::HashFile), Argument::ForType(Execution::Args::Type::Msix), }; } Resource::LocString HashCommand::ShortDescription() const { return { Resource::String::HashCommandShortDescription }; } Resource::LocString HashCommand::LongDescription() const { return { Resource::String::HashCommandLongDescription }; } Utility::LocIndView HashCommand::HelpLink() const { return "https://aka.ms/winget-command-hash"_liv; } void HashCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::VerifyFile(Execution::Args::Type::HashFile) << [](Execution::Context& context) { auto inputFile = context.Args.GetArg(Execution::Args::Type::HashFile); std::ifstream inStream{ Utility::ConvertToUTF16(inputFile), std::ifstream::binary }; context.Reporter.Info() << "InstallerSha256: "_liv << Utility::LocIndString{ Utility::SHA256::ConvertToString(Utility::SHA256::ComputeHash(inStream)) } << std::endl; if (context.Args.Contains(Execution::Args::Type::Msix)) { try { Msix::MsixInfo msixInfo{ inputFile }; auto signatureHash = msixInfo.GetSignatureHash(); context.Reporter.Info() << "SignatureSha256: "_liv << Utility::LocIndString{ Utility::SHA256::ConvertToString(signatureHash) } << std::endl; } catch (const wil::ResultException& re) { context.Reporter.Warn() << Resource::String::MsixSignatureHashFailed << std::endl << Resource::String::VerifyFileSignedMsix << std::endl; AICLI_TERMINATE_CONTEXT(re.GetErrorCode()); } } }; } } ================================================ FILE: src/AppInstallerCLICore/Commands/HashCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct HashCommand final : public Command { HashCommand(std::string_view parent) : Command("hash", parent) {} virtual std::vector GetArguments() const override; virtual Resource::LocString ShortDescription() const override; virtual Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ImportCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ImportCommand.h" #include "Workflows/DownloadFlow.h" #include "Workflows/CompletionFlow.h" #include "Workflows/ImportExportFlow.h" #include "Workflows/MultiQueryFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace std::string_view_literals; std::vector ImportCommand::GetArguments() const { return { Argument{ Execution::Args::Type::ImportFile, Resource::String::ImportFileArgumentDescription, ArgumentType::Positional, true }, Argument{ Execution::Args::Type::IgnoreUnavailable, Resource::String::ImportIgnoreUnavailableArgumentDescription, ArgumentType::Flag }, Argument{ Execution::Args::Type::IgnoreVersions, Resource::String::ImportIgnorePackageVersionsArgumentDescription, ArgumentType::Flag }, Argument::ForType(Execution::Args::Type::NoUpgrade), Argument::ForType(Execution::Args::Type::AcceptPackageAgreements), Argument::ForType(Execution::Args::Type::AcceptSourceAgreements), }; } Resource::LocString ImportCommand::ShortDescription() const { return { Resource::String::ImportCommandShortDescription }; } Resource::LocString ImportCommand::LongDescription() const { return { Resource::String::ImportCommandLongDescription }; } Utility::LocIndView ImportCommand::HelpLink() const { return "https://aka.ms/winget-command-import"_liv; } void ImportCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::InitializeInstallerDownloadAuthenticatorsMap << Workflow::ReportExecutionStage(Workflow::ExecutionStage::Discovery) << Workflow::VerifyFile(Execution::Args::Type::ImportFile) << Workflow::ReadImportFile << Workflow::OpenSourcesForImport << Workflow::OpenPredefinedSource(Repository::PredefinedSource::Installed) << Workflow::GetSearchRequestsForImport << Workflow::SearchSubContextsForSingle() << Workflow::ReportExecutionStage(Workflow::ExecutionStage::Execution) << Workflow::InstallImportedPackages; } } ================================================ FILE: src/AppInstallerCLICore/Commands/ImportCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { // Command to install a set of packages from a list. struct ImportCommand final : public Command { ImportCommand(std::string_view parent) : Command("import", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/InstallCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerRuntime.h" #include "CheckpointManager.h" #include "InstallCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/DownloadFlow.h" #include "Workflows/InstallFlow.h" #include "Workflows/UpdateFlow.h" #include "Workflows/MultiQueryFlow.h" #include "Workflows/ResumeFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Manifest; using namespace AppInstaller::Utility::literals; std::vector InstallCommand::GetArguments() const { return { Argument::ForType(Args::Type::MultiQuery), Argument::ForType(Args::Type::Manifest), Argument::ForType(Args::Type::Id), Argument::ForType(Args::Type::Name), Argument::ForType(Args::Type::Moniker), Argument::ForType(Args::Type::Version), Argument::ForType(Args::Type::Channel), Argument::ForType(Args::Type::Source), Argument{ Args::Type::InstallScope, Resource::String::InstallScopeDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument::ForType(Args::Type::InstallArchitecture), Argument::ForType(Args::Type::InstallerType), Argument::ForType(Args::Type::Exact), Argument::ForType(Args::Type::Interactive), Argument::ForType(Args::Type::Silent), Argument::ForType(Args::Type::Locale), Argument::ForType(Args::Type::Log), Argument::ForType(Args::Type::CustomSwitches), Argument::ForType(Args::Type::Override), Argument::ForType(Args::Type::InstallLocation), Argument::ForType(Args::Type::HashOverride), Argument::ForType(Args::Type::AllowReboot), Argument::ForType(Args::Type::SkipDependencies), Argument::ForType(Args::Type::DependenciesOnly), Argument::ForType(Args::Type::IgnoreLocalArchiveMalwareScan), Argument::ForType(Args::Type::DependencySource), Argument::ForType(Args::Type::AcceptPackageAgreements), Argument::ForType(Args::Type::NoUpgrade), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AuthenticationMode), Argument::ForType(Args::Type::AuthenticationAccount), Argument::ForType(Args::Type::AcceptSourceAgreements), Argument::ForType(Args::Type::Rename), Argument::ForType(Args::Type::UninstallPrevious), Argument::ForType(Args::Type::Force), Argument{ Args::Type::IncludeUnknown, Resource::String::IncludeUnknownArgumentDescription, ArgumentType::Flag, Argument::Visibility::Hidden}, }; } Resource::LocString InstallCommand::ShortDescription() const { return { Resource::String::InstallCommandShortDescription }; } Resource::LocString InstallCommand::LongDescription() const { return { Resource::String::InstallCommandLongDescription }; } void InstallCommand::Complete(Context& context, Args::Type valueType) const { switch (valueType) { case Args::Type::MultiQuery: case Args::Type::Manifest: case Args::Type::Id: case Args::Type::Name: case Args::Type::Moniker: case Args::Type::Version: case Args::Type::Channel: case Args::Type::Source: context << Workflow::CompleteWithSingleSemanticsForValue(valueType); break; case Args::Type::InstallArchitecture: case Args::Type::Locale: // May well move to CompleteWithSingleSemanticsForValue, // but for now output nothing. context << Workflow::CompleteWithEmptySet; break; case Args::Type::Log: case Args::Type::InstallLocation: // Intentionally output nothing to allow pass through to filesystem. break; } } Utility::LocIndView InstallCommand::HelpLink() const { return "https://aka.ms/winget-command-install"_liv; } void InstallCommand::ValidateArgumentsInternal(Args& execArgs) const { Argument::ValidateCommonArguments(execArgs); } void InstallCommand::Resume(Context& context) const { // TODO: Load context data from checkpoint for install command. ExecuteInternal(context); } void InstallCommand::ExecuteInternal(Context& context) const { context.SetFlags(ContextFlag::ShowSearchResultsOnPartialFailure); context << InitializeInstallerDownloadAuthenticatorsMap; if (context.Args.Contains(Execution::Args::Type::Manifest)) { context << ReportExecutionStage(ExecutionStage::Discovery) << GetManifestFromArg << SelectInstaller << EnsureApplicableInstaller << Checkpoint("PreInstallCheckpoint", {}) << // TODO: Capture context data InstallSinglePackage; } else { context << ReportExecutionStage(ExecutionStage::Discovery) << OpenSource(); if (!context.Args.Contains(Execution::Args::Type::Force)) { context << OpenCompositeSource(DetermineInstalledSource(context), false, Repository::CompositeSearchBehavior::AvailablePackages); } if (context.Args.Contains(Execution::Args::Type::MultiQuery)) { ProcessMultiplePackages::Flags flags = ProcessMultiplePackages::Flags::None; if (Settings::User().Get() || context.Args.Contains(Execution::Args::Type::SkipDependencies)) { flags = ProcessMultiplePackages::Flags::IgnoreDependencies; } else if (context.Args.Contains(Execution::Args::Type::DependenciesOnly)) { flags = ProcessMultiplePackages::Flags::DependenciesOnly; } context << GetMultiSearchRequests << SearchSubContextsForSingle() << ReportExecutionStage(ExecutionStage::Execution) << ProcessMultiplePackages(Resource::String::PackageRequiresDependencies, APPINSTALLER_CLI_ERROR_MULTIPLE_INSTALL_FAILED, flags); } else { context << Checkpoint("PreInstallCheckpoint", {}) << // TODO: Capture context data InstallOrUpgradeSinglePackage(OperationType::Install); } } } } ================================================ FILE: src/AppInstallerCLICore/Commands/InstallCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct InstallCommand final : public Command { InstallCommand(std::string_view parent) : Command("install", { "add" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; void Resume(Execution::Context& context) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ListCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ListCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Workflow; using namespace std::string_view_literals; std::vector ListCommand::GetArguments() const { return { Argument::ForType(Execution::Args::Type::Query), Argument::ForType(Execution::Args::Type::Id), Argument::ForType(Execution::Args::Type::Name), Argument::ForType(Execution::Args::Type::Moniker), Argument::ForType(Execution::Args::Type::Source), Argument::ForType(Execution::Args::Type::Tag), Argument::ForType(Execution::Args::Type::Command), Argument::ForType(Execution::Args::Type::Count), Argument::ForType(Execution::Args::Type::Exact), Argument{ Execution::Args::Type::InstallScope, Resource::String::InstalledScopeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument::ForType(Execution::Args::Type::CustomHeader), Argument::ForType(Execution::Args::Type::AuthenticationMode), Argument::ForType(Execution::Args::Type::AuthenticationAccount), Argument::ForType(Execution::Args::Type::AcceptSourceAgreements), Argument{ Execution::Args::Type::Upgrade, Resource::String::UpgradeArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }, Argument{ Execution::Args::Type::IncludeUnknown, Resource::String::IncludeUnknownInListArgumentDescription, ArgumentType::Flag }, Argument{ Execution::Args::Type::IncludePinned, Resource::String::IncludePinnedInListArgumentDescription, ArgumentType::Flag}, Argument::ForType(Execution::Args::Type::ListDetails), }; } Resource::LocString ListCommand::ShortDescription() const { return { Resource::String::ListCommandShortDescription }; } Resource::LocString ListCommand::LongDescription() const { return { Resource::String::ListCommandLongDescription }; } void ListCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const { context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) { case Execution::Args::Type::Query: context << Workflow::RequireCompletionWordNonEmpty << Workflow::SearchSourceForManyCompletion << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::Source: case Execution::Args::Type::Tag: case Execution::Args::Type::Command: context << Workflow::CompleteWithSingleSemanticsForValueUsingExistingSource(valueType); break; } } Utility::LocIndView ListCommand::HelpLink() const { return "https://aka.ms/winget-command-list"_liv; } void ListCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { Argument::ValidateArgumentDependency(execArgs, Execution::Args::Type::IncludeUnknown, Execution::Args::Type::Upgrade); Argument::ValidateArgumentDependency(execArgs, Execution::Args::Type::IncludePinned, Execution::Args::Type::Upgrade); } void ListCommand::ExecuteInternal(Execution::Context& context) const { context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Workflow::DetermineInstalledSource(context)) << Workflow::SearchSourceForMany << Workflow::HandleSearchResultFailures << Workflow::EnsureMatchesFromSearchResult(OperationType::List) << Workflow::ReportListResult(context.Args.Contains(Execution::Args::Type::Upgrade)); } } ================================================ FILE: src/AppInstallerCLICore/Commands/ListCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { // Command to get the set of installed packages on the system. struct ListCommand final : public Command { ListCommand(std::string_view parent) : Command("list", { "ls" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/McpCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "McpCommand.h" #include "Workflows/MSStoreInstallerHandler.h" #include using namespace AppInstaller::CLI::Workflow; namespace AppInstaller::CLI { McpCommand::McpCommand(std::string_view parent) : Command("mcp", {}, parent, Settings::TogglePolicy::Policy::McpServer) { } std::vector McpCommand::GetArguments() const { return { Argument{ Execution::Args::Type::ExtendedFeaturesEnable, Resource::String::ExtendedFeaturesEnableMessage, ArgumentType::Flag, Argument::Visibility::Help }, Argument{ Execution::Args::Type::ExtendedFeaturesDisable, Resource::String::ExtendedFeaturesDisableMessage, ArgumentType::Flag, Argument::Visibility::Help }, }; } Resource::LocString McpCommand::ShortDescription() const { return { Resource::String::McpCommandShortDescription }; } Resource::LocString McpCommand::LongDescription() const { return { Resource::String::McpCommandLongDescription }; } Utility::LocIndView McpCommand::HelpLink() const { return "https://aka.ms/winget-command-mcp"_liv; } void McpCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::ExtendedFeaturesEnable)) { context << EnableExtendedFeatures; } else if (context.Args.Contains(Execution::Args::Type::ExtendedFeaturesDisable)) { context << DisableExtendedFeatures; } else { context << VerifyIsFullPackage << [](Execution::Context& context) { std::filesystem::path exePath = Runtime::GetPathTo(Runtime::PathName::MCPExecutable); std::string jsonCompatiblePath = exePath.u8string(); Utility::FindAndReplace(jsonCompatiblePath, "\\", "\\\\"); context.Reporter.Info() << Resource::String::McpConfigurationPreamble << R"( "winget-mcp": { "type": "stdio", "command": ")" << jsonCompatiblePath << R"(" })" << std::endl; }; } } void McpCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { if (execArgs.Contains(Execution::Args::Type::ExtendedFeaturesEnable) || execArgs.Contains(Execution::Args::Type::ExtendedFeaturesDisable)) { if (execArgs.GetArgsCount() > 1) { throw CommandException(Resource::String::ExtendedFeaturesEnableArgumentError); } } } } ================================================ FILE: src/AppInstallerCLICore/Commands/McpCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct McpCommand final : public Command { McpCommand(std::string_view parent); std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/PinCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PinCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/PinFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Utility::literals; using namespace std::string_view_literals; Utility::LocIndView s_PinCommand_HelpLink = "https://aka.ms/winget-command-pin"_liv; std::vector> PinCommand::GetCommands() const { return InitializeFromMoveOnly>>({ std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), }); } Resource::LocString PinCommand::ShortDescription() const { return { Resource::String::PinCommandShortDescription }; } Resource::LocString PinCommand::LongDescription() const { return { Resource::String::PinCommandLongDescription }; } Utility::LocIndView PinCommand::HelpLink() const { return s_PinCommand_HelpLink; } void PinCommand::ExecuteInternal(Execution::Context& context) const { OutputHelp(context.Reporter); } std::vector PinAddCommand::GetArguments() const { return { Argument::ForType(Args::Type::Query), Argument::ForType(Args::Type::Id), Argument::ForType(Args::Type::Name), Argument::ForType(Args::Type::Moniker), Argument::ForType(Args::Type::Tag), Argument::ForType(Args::Type::Command), Argument::ForType(Args::Type::Exact), Argument{ Args::Type::GatedVersion, Resource::String::GatedVersionArgumentDescription, ArgumentType::Standard }, Argument::ForType(Args::Type::Source), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AuthenticationMode), Argument::ForType(Args::Type::AuthenticationAccount), Argument::ForType(Args::Type::AcceptSourceAgreements), Argument::ForType(Args::Type::Force), Argument{ Args::Type::BlockingPin, Resource::String::PinAddBlockingArgumentDescription, ArgumentType::Flag }, Argument{ Args::Type::PinInstalled, Resource::String::PinInstalledArgumentDescription, ArgumentType::Flag }, }; } Resource::LocString PinAddCommand::ShortDescription() const { return { Resource::String::PinAddCommandShortDescription }; } Resource::LocString PinAddCommand::LongDescription() const { return { Resource::String::PinAddCommandLongDescription }; } void PinAddCommand::Complete(Execution::Context& context, Args::Type valueType) const { context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) { case Execution::Args::Type::Query: context << Workflow::RequireCompletionWordNonEmpty << Workflow::SearchSourceForManyCompletion << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::Source: case Execution::Args::Type::Tag: case Execution::Args::Type::Command: context << Workflow::CompleteWithSingleSemanticsForValueUsingExistingSource(valueType); break; } } Utility::LocIndView PinAddCommand::HelpLink() const { return s_PinCommand_HelpLink; } void PinAddCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { Argument::ValidateCommonArguments(execArgs); } void PinAddCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::Id)) { // When we are given an ID, just pin that available package without checking for installed. // This helps when there are matching issues, for example due to multiple side-by-side installs. context << Workflow::OpenSource(); } else { // If not working from just ID, try matching a single installed package context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); } context << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Pin) << Workflow::GetInstalledPackageVersion << Workflow::ReportPackageIdentity << Workflow::OpenPinningIndex() << Workflow::AddPin; } std::vector PinRemoveCommand::GetArguments() const { return { Argument::ForType(Args::Type::Query), Argument::ForType(Args::Type::Id), Argument::ForType(Args::Type::Name), Argument::ForType(Args::Type::Moniker), Argument::ForType(Args::Type::Source), Argument::ForType(Args::Type::Tag), Argument::ForType(Args::Type::Command), Argument::ForType(Args::Type::Exact), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AuthenticationMode), Argument::ForType(Args::Type::AuthenticationAccount), Argument::ForType(Args::Type::AcceptSourceAgreements), Argument{ Args::Type::PinInstalled, Resource::String::PinInstalledArgumentDescription, ArgumentType::Flag }, }; } Resource::LocString PinRemoveCommand::ShortDescription() const { return { Resource::String::PinRemoveCommandShortDescription }; } Resource::LocString PinRemoveCommand::LongDescription() const { return { Resource::String::PinRemoveCommandLongDescription }; } void PinRemoveCommand::Complete(Execution::Context& context, Args::Type valueType) const { context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) { case Execution::Args::Type::Query: context << Workflow::RequireCompletionWordNonEmpty << Workflow::SearchSourceForManyCompletion << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::Source: case Execution::Args::Type::Tag: case Execution::Args::Type::Command: context << Workflow::CompleteWithSingleSemanticsForValueUsingExistingSource(valueType); break; } } Utility::LocIndView PinRemoveCommand::HelpLink() const { return s_PinCommand_HelpLink; } void PinRemoveCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::Id)) { // When we are given an ID, just un-pin that available package without checking for installed. // This helps when there are matching issues, for example due to multiple side-by-side installs. context << Workflow::OpenSource(); } else { // If not working from just ID, try matching a single installed package context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); } context << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Pin) << Workflow::GetInstalledPackageVersion << Workflow::ReportPackageIdentity << Workflow::OpenPinningIndex() << Workflow::SearchPin << Workflow::RemovePin; } std::vector PinListCommand::GetArguments() const { return { Argument::ForType(Args::Type::Query), Argument::ForType(Args::Type::Id), Argument::ForType(Args::Type::Name), Argument::ForType(Args::Type::Moniker), Argument::ForType(Args::Type::Source), Argument::ForType(Args::Type::Tag), Argument::ForType(Args::Type::Command), Argument::ForType(Args::Type::Exact), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AuthenticationMode), Argument::ForType(Args::Type::AuthenticationAccount), Argument::ForType(Args::Type::AcceptSourceAgreements), }; } Resource::LocString PinListCommand::ShortDescription() const { return { Resource::String::PinListCommandShortDescription }; } Resource::LocString PinListCommand::LongDescription() const { return { Resource::String::PinListCommandLongDescription }; } void PinListCommand::Complete(Execution::Context& context, Args::Type valueType) const { context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) { case Execution::Args::Type::Query: context << Workflow::RequireCompletionWordNonEmpty << Workflow::SearchSourceForManyCompletion << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::Source: case Execution::Args::Type::Tag: case Execution::Args::Type::Command: context << Workflow::CompleteWithSingleSemanticsForValueUsingExistingSource(valueType); break; } } Utility::LocIndView PinListCommand::HelpLink() const { return s_PinCommand_HelpLink; } void PinListCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::OpenPinningIndex(/* readOnly */ true) << Workflow::GetAllPins << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, false, Repository::CompositeSearchBehavior::AllPackages) << Workflow::ReportPins; } std::vector PinResetCommand::GetArguments() const { return { Argument::ForType(Args::Type::Force), Argument::ForType(Args::Type::Source), }; } Resource::LocString PinResetCommand::ShortDescription() const { return { Resource::String::PinResetCommandShortDescription }; } Resource::LocString PinResetCommand::LongDescription() const { return { Resource::String::PinResetCommandLongDescription }; } Utility::LocIndView PinResetCommand::HelpLink() const { return s_PinCommand_HelpLink; } void PinResetCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::Force)) { context << Workflow::OpenPinningIndex() << Workflow::ResetAllPins; } else { AICLI_LOG(CLI, Info, << "--force argument is not present"); context.Reporter.Info() << Resource::String::PinResetUseForceArg << std::endl; context << Workflow::OpenPinningIndex(/* readOnly */ true) << Workflow::GetAllPins << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed) << Workflow::ReportPins; } } } ================================================ FILE: src/AppInstallerCLICore/Commands/PinCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #include namespace AppInstaller::CLI { struct PinCommand final : public Command { PinCommand(std::string_view parent) : Command("pin", {} /* aliases */, parent) {} std::vector> GetCommands() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct PinAddCommand final : public Command { PinAddCommand(std::string_view parent) : Command("add", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; struct PinRemoveCommand final : public Command { PinRemoveCommand(std::string_view parent) : Command("remove", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct PinListCommand final : public Command { PinListCommand(std::string_view parent) : Command("list", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct PinResetCommand final : public Command { PinResetCommand(std::string_view parent) : Command("reset", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/RepairCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RepairCommand.h" #include "Workflows/RepairFlow.h" #include "Workflows/CompletionFlow.h" #include "Workflows/DownloadFlow.h" #include "Workflows/InstallFlow.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; std::vector RepairCommand::GetArguments() const { return { Argument::ForType(Args::Type::Query), // -q Argument::ForType(Args::Type::Manifest), // -m Argument::ForType(Args::Type::Id), // -id Argument::ForType(Args::Type::Name), // -n Argument::ForType(Args::Type::Channel), Argument::ForType(Args::Type::Moniker), // -mn Argument::ForType(Args::Type::TargetVersion), // -v Argument::ForType(Args::Type::ProductCode), Argument::ForType(Args::Type::InstallArchitecture), // -arch Argument{ Execution::Args::Type::InstallScope, Resource::String::InstalledScopeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument::ForType(Args::Type::Source), // -s Argument::ForType(Args::Type::Interactive), // -i Argument::ForType(Args::Type::Silent), // -h Argument::ForType(Args::Type::Log), // -o Argument::ForType(Args::Type::IgnoreLocalArchiveMalwareScan), // -ignore-local-archive-malware-scan Argument::ForType(Args::Type::AcceptSourceAgreements), // -accept-source-agreements Argument::ForType(Args::Type::AcceptPackageAgreements), Argument::ForType(Args::Type::Locale), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AuthenticationMode), Argument::ForType(Args::Type::AuthenticationAccount), Argument::ForType(Args::Type::Force), Argument::ForType(Args::Type::HashOverride), Argument::ForType(Args::Type::Exact), }; } Resource::LocString RepairCommand::ShortDescription() const { return { Resource::String::RepairCommandShortDescription }; } Resource::LocString RepairCommand::LongDescription() const { return { Resource::String::RepairCommandLongDescription }; } void RepairCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const { if (valueType == Execution::Args::Type::Manifest || valueType == Execution::Args::Type::Log) { // Intentionally output nothing to allow pass through to filesystem. return; } context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) { case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::TargetVersion: case Execution::Args::Type::Channel: case Execution::Args::Type::Source: context << Workflow::CompleteWithSingleSemanticsForValueUsingExistingSource(valueType); break; } } Utility::LocIndView RepairCommand::HelpLink() const { // TODO: point to the right place return "https://aka.ms/winget-command-repair"_liv; } void RepairCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { Argument::ValidateCommonArguments(execArgs); } void RepairCommand::ExecuteInternal(Execution::Context& context) const { context.SetFlags(Execution::ContextFlag::InstallerExecutionUseRepair); context << Workflow::InitializeInstallerDownloadAuthenticatorsMap << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::OpenSource() << Workflow::OpenCompositeSource(DetermineInstalledSource(context)); if (context.Args.Contains(Args::Type::Manifest)) { context << Workflow::GetManifestFromArg << Workflow::ReportManifestIdentity << Workflow::SearchSourceUsingManifest << Workflow::EnsureOneMatchFromSearchResult(OperationType::Repair); } else { context << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Repair) << Workflow::ReportPackageIdentity; } context << Workflow::GetInstalledPackageVersion << Workflow::SelectApplicableInstallerIfNecessary << Workflow::RepairSinglePackage; } } ================================================ FILE: src/AppInstallerCLICore/Commands/RepairCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct RepairCommand final : public Command { RepairCommand(std::string_view parent) : Command("repair", { "fix" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ResumeCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerRuntime.h" #include "Resources.h" #include "ResumeCommand.h" #include "RootCommand.h" #include "CheckpointManager.h" #include "Workflows/ResumeFlow.h" using namespace AppInstaller::Checkpoints; namespace AppInstaller::CLI { namespace { std::unique_ptr FindCommandToResume(const std::string& commandFullName) { std::unique_ptr commandToResume = std::make_unique(); for (const auto& commandPart : Utility::Split(commandFullName, ':')) { bool commandFound = false; if (Utility::CaseInsensitiveEquals(commandPart, commandToResume->Name())) { // Since we always expect to start at the 'root' command, skip and check the next command part. continue; } for (auto& command : commandToResume->GetCommands()) { if (Utility::CaseInsensitiveEquals(commandPart, command->Name())) { commandFound = true; commandToResume = std::move(command); break; } } if (!commandFound) { THROW_HR_MSG(E_UNEXPECTED, "Command to resume not found."); } } return std::move(commandToResume); } } using namespace std::string_view_literals; using namespace Execution; std::vector ResumeCommand::GetArguments() const { return { Argument::ForType(Execution::Args::Type::ResumeId), Argument::ForType(Execution::Args::Type::IgnoreResumeLimit), }; } Resource::LocString ResumeCommand::ShortDescription() const { return { Resource::String::ResumeCommandShortDescription }; } Resource::LocString ResumeCommand::LongDescription() const { return { Resource::String::ResumeCommandLongDescription }; } Utility::LocIndView ResumeCommand::HelpLink() const { return "https://aka.ms/winget-command-resume"_liv; } void ResumeCommand::ExecuteInternal(Execution::Context& context) const { const auto& resumeId = context.Args.GetArg(Execution::Args::Type::ResumeId); if (!std::filesystem::exists(Checkpoints::CheckpointManager::GetCheckpointDatabasePath(resumeId))) { context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeId }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); } Execution::Context resumeContext = context.CreateEmptyContext(); std::optional> foundAutomaticCheckpoint = resumeContext.LoadCheckpoint(std::string{ resumeId }); if (!foundAutomaticCheckpoint.has_value()) { context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); } Checkpoint automaticCheckpoint = foundAutomaticCheckpoint.value(); const auto& checkpointClientVersion = automaticCheckpoint.Get(AutomaticCheckpointData::ClientVersion, {}); if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get()) { context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ checkpointClientVersion }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); } const auto& resumeCountString = automaticCheckpoint.Get(AutomaticCheckpointData::ResumeCount, {}); int resumeCount = std::stoi(resumeCountString); if (!context.Args.Contains(Execution::Args::Type::IgnoreResumeLimit) && resumeCount >= Settings::User().Get()) { std::string manualResumeString = "winget resume -g " + std::string{ resumeId } + " --ignore-resume-limit"; context.Reporter.Error() << Resource::String::ResumeLimitExceeded(Utility::LocIndView{ resumeCountString }) << Utility::LocIndView{ manualResumeString } << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_LIMIT_EXCEEDED); } else { automaticCheckpoint.Update(AutomaticCheckpointData::ResumeCount, {}, std::to_string(resumeCount + 1)); } const auto& checkpointCommand = automaticCheckpoint.Get(AutomaticCheckpointData::Command, {}); AICLI_LOG(CLI, Info, << "Resuming command: " << checkpointCommand); std::unique_ptr commandToResume = FindCommandToResume(checkpointCommand); LoadCommandArgsFromAutomaticCheckpoint(resumeContext, automaticCheckpoint); resumeContext.SetExecutingCommand(commandToResume.get()); // TODO: Ensure telemetry is properly handled for resume context. resumeContext.SetFlags(Execution::ContextFlag::Resume); auto previousThreadGlobals = resumeContext.SetForCurrentThread(); resumeContext.EnableSignalTerminationHandler(); commandToResume->Resume(resumeContext); context.SetTerminationHR(resumeContext.GetTerminationHR()); } } ================================================ FILE: src/AppInstallerCLICore/Commands/ResumeCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ResumeCommand final : public Command { ResumeCommand(std::string_view parent) : Command("resume", {}, parent, Visibility::Hidden, Settings::ExperimentalFeature::Feature::Resume) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/RootCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RootCommand.h" #include #include "InstallCommand.h" #include "ShowCommand.h" #include "SourceCommand.h" #include "SearchCommand.h" #include "ListCommand.h" #include "UpgradeCommand.h" #include "UninstallCommand.h" #include "HashCommand.h" #include "ValidateCommand.h" #include "SettingsCommand.h" #include "FeaturesCommand.h" #include "FontCommand.h" #include "ExperimentalCommand.h" #include "CompleteCommand.h" #include "ExportCommand.h" #include "ImportCommand.h" #include "PinCommand.h" #include "ConfigureCommand.h" #include "DebugCommand.h" #include "TestCommand.h" #include "DownloadCommand.h" #include "ErrorCommand.h" #include "ResumeCommand.h" #include "RepairCommand.h" #include "DscCommand.h" #include "McpCommand.h" #include "Resources.h" #include "TableOutput.h" namespace AppInstaller::CLI { using namespace AppInstaller::Utility::literals; using namespace Settings; namespace { void OutputGroupPolicySourceList(Execution::Context& context, const std::vector& sources, Resource::StringId header) { Execution::TableOutput<3> sourcesTable{ context.Reporter, { header, Resource::String::SourceListType, Resource::String::SourceListArg } }; for (const auto& source : sources) { sourcesTable.OutputLine({ source.Name, source.Type, source.Arg }); } sourcesTable.Complete(); } void OutputGroupPolicies(Execution::Context& context) { const auto& groupPolicies = Settings::GroupPolicies(); // Get the state of policies that are a simple enabled/disabled toggle std::map activePolicies; for (const auto& togglePolicy : Settings::TogglePolicy::GetAllPolicies()) { auto state = groupPolicies.GetState(togglePolicy.GetPolicy()); if (state != Settings::PolicyState::NotConfigured) { activePolicies[togglePolicy.GetPolicy()] = state; } } // The source update interval is the only ValuePolicy that is not gated by a TogglePolicy. // We need to output the table if there is a TogglePolicy configured or if this one is configured. // We can rework this when more policies are added. auto sourceAutoUpdateIntervalPolicy = groupPolicies.GetValue(); if (!activePolicies.empty() || sourceAutoUpdateIntervalPolicy.has_value()) { auto info = context.Reporter.Info(); info << std::endl; Execution::TableOutput<2> policiesTable{ context.Reporter, { Resource::String::PoliciesPolicy, Resource::String::StateHeader } }; // Output the toggle policies. for (const auto& activePolicy : activePolicies) { auto policy = Settings::TogglePolicy::GetPolicy(activePolicy.first); policiesTable.OutputLine({ Resource::LocString{ policy.PolicyName() }.get(), Resource::LocString{ activePolicy.second == Settings::PolicyState::Enabled ? Resource::String::StateEnabled : Resource::String::StateDisabled }.get() }); } // Output the update interval in the same table if needed. if (sourceAutoUpdateIntervalPolicy.has_value()) { policiesTable.OutputLine({ Resource::LocString{ AppInstaller::StringResource::String::PolicySourceAutoUpdateInterval }, std::to_string(sourceAutoUpdateIntervalPolicy.value()) }); } policiesTable.Complete(); // Output the additional and allowed sources as separate tables. if (groupPolicies.GetState(Settings::TogglePolicy::Policy::AdditionalSources) == Settings::PolicyState::Enabled) { info << std::endl; auto sources = groupPolicies.GetValueRef(); if (sources.has_value() && !sources->get().empty()) { OutputGroupPolicySourceList(context, sources->get(), Resource::String::SourceListAdditionalSource); } } if (groupPolicies.GetState(Settings::TogglePolicy::Policy::AllowedSources) == Settings::PolicyState::Enabled) { info << std::endl; auto sources = groupPolicies.GetValueRef(); if (sources.has_value() && !sources->get().empty()) { OutputGroupPolicySourceList(context, sources->get(), Resource::String::SourceListAllowedSource); } } info << std::endl; } } void OutputAdminSettings(Execution::Context& context) { Execution::TableOutput<2> adminSettingsTable{ context.Reporter, { Resource::String::AdminSettingHeader, Resource::String::StateHeader } }; // Output the admin settings. for (const auto& setting : Settings::GetAllBoolAdminSettings()) { adminSettingsTable.OutputLine({ std::string{ AdminSettingToString(setting)}, Resource::LocString{ IsAdminSettingEnabled(setting) ? Resource::String::StateEnabled : Resource::String::StateDisabled } }); } for (const auto& setting : Settings::GetAllStringAdminSettings()) { auto settingValue = GetAdminSetting(setting); adminSettingsTable.OutputLine({ std::string{ AdminSettingToString(setting)}, settingValue ? Utility::LocIndString{ settingValue.value() } : Resource::LocString{ Resource::String::StateDisabled } }); } adminSettingsTable.Complete(); } void OutputKeyDirectories(Execution::Context& context) { Execution::TableOutput<2> keyDirectories{ context.Reporter, { Resource::String::KeyDirectoriesHeader, {} } }; keyDirectories.OutputLine({ Resource::LocString{ Resource::String::Logs }, Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation, true).u8string() }); keyDirectories.OutputLine({ Resource::LocString{ Resource::String::UserSettings }, UserSettings::SettingsFilePath(true).u8string() }); keyDirectories.OutputLine({ Resource::LocString{ Resource::String::PortableLinksUser }, Runtime::GetPathTo(Runtime::PathName::PortableLinksUserLocation, true).u8string() }); keyDirectories.OutputLine({ Resource::LocString{ Resource::String::PortableLinksMachine }, Runtime::GetPathTo(Runtime::PathName::PortableLinksMachineLocation, true).u8string() }); keyDirectories.OutputLine({ Resource::LocString{ Resource::String::PortableRootUser }, Runtime::GetPathTo(Runtime::PathName::PortablePackageUserRoot, true).u8string() }); keyDirectories.OutputLine({ Resource::LocString{ Resource::String::PortableRoot }, Runtime::GetPathTo(Runtime::PathName::PortablePackageMachineRoot, true).u8string() }); keyDirectories.OutputLine({ Resource::LocString{ Resource::String::PortableRoot86 }, Runtime::GetPathTo(Runtime::PathName::PortablePackageMachineRootX86, true).u8string() }); keyDirectories.OutputLine({ Resource::LocString{ Resource::String::InstallerDownloads }, Runtime::GetPathTo(Runtime::PathName::UserProfileDownloads, true).u8string() }); keyDirectories.OutputLine({ Resource::LocString{ Resource::String::ConfigurationModules }, Runtime::GetPathTo(Runtime::PathName::ConfigurationModules, true).u8string() }); keyDirectories.Complete(); context.Reporter.Info() << std::endl; } void OutputLinks(Execution::Context& context) { Execution::TableOutput<2> links{ context.Reporter, { Resource::String::Links, {} } }; links.OutputLine({ Resource::LocString{ Resource::String::PrivacyStatement }, "https://aka.ms/winget-privacy" }); links.OutputLine({ Resource::LocString{ Resource::String::LicenseAgreement }, "https://aka.ms/winget-license" }); links.OutputLine({ Resource::LocString{ Resource::String::ThirdPartSoftwareNotices }, "https://aka.ms/winget-3rdPartyNotice" }); links.OutputLine({ Resource::LocString{ Resource::String::MainHomepage }, "https://aka.ms/winget" }); links.OutputLine({ Resource::LocString{ Resource::String::WindowsStoreTerms }, "https://www.microsoft.com/en-us/storedocs/terms-of-sale" }); links.Complete(); context.Reporter.Info() << std::endl; } } std::vector> RootCommand::GetCommands() const { return InitializeFromMoveOnly>>({ std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), #if _DEBUG std::make_unique(FullName()), #endif #ifndef AICLI_DISABLE_TEST_HOOKS std::make_unique(FullName()), #endif }); } std::vector RootCommand::GetArguments() const { return { Argument{ Execution::Args::Type::ToolVersion, Resource::String::ToolVersionArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }, Argument{ Execution::Args::Type::Info, Resource::String::ToolInfoArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help }, }; } Resource::LocString RootCommand::ShortDescription() const { return {}; } Resource::LocString RootCommand::LongDescription() const { return { Resource::String::ToolDescription }; } Utility::LocIndView RootCommand::HelpLink() const { return "https://aka.ms/winget-command-help"_liv; } void RootCommand::Execute(Execution::Context& context) const { AICLI_LOG(CLI, Info, << "Executing command: " << Name()); if (context.Args.Contains(Execution::Args::Type::Help)) { OutputHelp(context.Reporter); } else { ExecuteInternal(context); } if (context.Args.Contains(Execution::Args::Type::OpenLogs)) { ShellExecute(NULL, NULL, Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation).wstring().c_str(), NULL, NULL, SW_SHOWNORMAL); } if (context.Args.Contains(Execution::Args::Type::Wait)) { context.Reporter.PromptForEnter(); } } void RootCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::Info)) { OutputIntroHeader(context.Reporter); auto info = context.Reporter.Info(); info << std::endl << "Windows: "_liv << Runtime::GetOSVersion() << std::endl; info << Resource::String::SystemArchitecture(Utility::ToString(Utility::GetSystemArchitecture())) << std::endl; if (Runtime::IsRunningInPackagedContext()) { info << Resource::String::Package(Runtime::GetPackageVersion()) << std::endl; }; info << std::endl; OutputKeyDirectories(context); OutputLinks(context); OutputGroupPolicies(context); OutputAdminSettings(context); } else if (context.Args.Contains(Execution::Args::Type::ToolVersion)) { context.Reporter.Info() << 'v' << Runtime::GetClientVersion() << std::endl; } else { OutputHelp(context.Reporter); } } } ================================================ FILE: src/AppInstallerCLICore/Commands/RootCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct RootCommand final : public Command { constexpr static std::string_view CommandName = "root"sv; RootCommand() : Command(CommandName, {}) {} std::vector> GetCommands() const override; std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; void Execute(Execution::Context& context) const override; protected: virtual void ExecuteInternal(Execution::Context& context) const; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/SearchCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace std::string_view_literals; std::vector SearchCommand::GetArguments() const { return { Argument::ForType(Execution::Args::Type::Query), Argument::ForType(Execution::Args::Type::Id), Argument::ForType(Execution::Args::Type::Name), Argument::ForType(Execution::Args::Type::Moniker), Argument::ForType(Execution::Args::Type::Tag), Argument::ForType(Execution::Args::Type::Command), Argument::ForType(Execution::Args::Type::Source), Argument::ForType(Execution::Args::Type::Count), Argument::ForType(Execution::Args::Type::Exact), Argument::ForType(Execution::Args::Type::CustomHeader), Argument::ForType(Execution::Args::Type::AuthenticationMode), Argument::ForType(Execution::Args::Type::AuthenticationAccount), Argument::ForType(Execution::Args::Type::AcceptSourceAgreements), Argument::ForType(Execution::Args::Type::ListVersions), }; } Resource::LocString SearchCommand::ShortDescription() const { return { Resource::String::SearchCommandShortDescription }; } Resource::LocString SearchCommand::LongDescription() const { return { Resource::String::SearchCommandLongDescription }; } void SearchCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const { switch (valueType) { case Execution::Args::Type::Query: context << Workflow::OpenSource() << Workflow::RequireCompletionWordNonEmpty << Workflow::SearchSourceForManyCompletion << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::Tag: case Execution::Args::Type::Command: case Execution::Args::Type::Source: context << Workflow::CompleteWithSingleSemanticsForValue(valueType); break; } } Utility::LocIndView SearchCommand::HelpLink() const { return "https://aka.ms/winget-command-search"_liv; } void SearchCommand::ValidateArgumentsInternal(Args& execArgs) const { Argument::ValidateCommonArguments(execArgs); } void SearchCommand::ExecuteInternal(Context& context) const { context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); context << Workflow::OpenSource() << Workflow::SearchSourceForMany << Workflow::HandleSearchResultFailures; if (context.Args.Contains(Execution::Args::Type::ListVersions)) { context << Workflow::EnsureOneMatchFromSearchResult(OperationType::Search) << Workflow::ReportPackageIdentity << Workflow::ShowAppVersions; } else { context << Workflow::EnsureMatchesFromSearchResult(OperationType::Search) << Workflow::ReportSearchResult; } } } ================================================ FILE: src/AppInstallerCLICore/Commands/SearchCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct SearchCommand final : public Command { SearchCommand(std::string_view parent) : Command("search", { "find" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/SettingsCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SettingsCommand.h" #include "Workflows/WorkflowBase.h" #include "Workflows/SettingsFlow.h" namespace AppInstaller::CLI { using namespace Utility::literals; using namespace AppInstaller::Settings; using namespace std::string_view_literals; namespace { Utility::LocIndView s_SettingsCommand_HelpLink = "https://aka.ms/winget-settings"_liv; } std::vector> SettingsCommand::GetCommands() const { return InitializeFromMoveOnly>>({ std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), }); } std::vector SettingsCommand::GetArguments() const { return { Argument{ Execution::Args::Type::AdminSettingEnable, Resource::String::AdminSettingEnableDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument{ Execution::Args::Type::AdminSettingDisable, Resource::String::AdminSettingDisableDescription, ArgumentType::Standard, Argument::Visibility::Help }, }; } Resource::LocString SettingsCommand::ShortDescription() const { return { Resource::String::SettingsCommandShortDescription }; } Resource::LocString SettingsCommand::LongDescription() const { return { Resource::String::SettingsCommandLongDescription }; } Utility::LocIndView SettingsCommand::HelpLink() const { return s_SettingsCommand_HelpLink; } void SettingsCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { // Get admin setting string for all available options except Unknown std::vector adminSettingList; for (auto setting : GetAllSequentialEnumValues(BoolAdminSetting::Unknown)) { adminSettingList.emplace_back(AdminSettingToString(setting)); } Utility::LocIndString validOptions = Join(", "_liv, adminSettingList); if (execArgs.Contains(Execution::Args::Type::AdminSettingEnable) && BoolAdminSetting::Unknown == StringToBoolAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingEnable))) { throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::AdminSettingEnable).Name, validOptions)); } if (execArgs.Contains(Execution::Args::Type::AdminSettingDisable) && BoolAdminSetting::Unknown == StringToBoolAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingDisable))) { throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::AdminSettingDisable).Name, validOptions)); } } void SettingsCommand::ExecuteInternal(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::AdminSettingEnable)) { context << Workflow::EnsureRunningAsAdmin << Workflow::EnableAdminSetting; } else if (context.Args.Contains(Execution::Args::Type::AdminSettingDisable)) { context << Workflow::EnsureRunningAsAdmin << Workflow::DisableAdminSetting; } else { context << Workflow::OpenUserSetting; } } Resource::LocString SettingsExportCommand::ShortDescription() const { return { Resource::String::SettingsExportCommandShortDescription }; } Resource::LocString SettingsExportCommand::LongDescription() const { return { Resource::String::SettingsExportCommandLongDescription }; } Utility::LocIndView SettingsExportCommand::HelpLink() const { return s_SettingsCommand_HelpLink; } void SettingsExportCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::ExportSettings; } std::vector SettingsSetCommand::GetArguments() const { return { Argument { Execution::Args::Type::SettingName, Resource::String::SettingNameArgumentDescription, ArgumentType::Positional, true }, Argument { Execution::Args::Type::SettingValue, Resource::String::SettingValueArgumentDescription, ArgumentType::Positional, true }, }; } Resource::LocString SettingsSetCommand::ShortDescription() const { return { Resource::String::SettingsSetCommandShortDescription }; } Resource::LocString SettingsSetCommand::LongDescription() const { return { Resource::String::SettingsSetCommandLongDescription }; } Utility::LocIndView SettingsSetCommand::HelpLink() const { return s_SettingsCommand_HelpLink; } void SettingsSetCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { // Get admin setting string for all available options except Unknown std::vector adminSettingList; for (auto setting : GetAllSequentialEnumValues(StringAdminSetting::Unknown)) { adminSettingList.emplace_back(AdminSettingToString(setting)); } Utility::LocIndString validOptions = Join(", "_liv, adminSettingList); if (StringAdminSetting::Unknown == StringToStringAdminSetting(execArgs.GetArg(Execution::Args::Type::SettingName))) { throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::SettingName).Name, validOptions)); } } void SettingsSetCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::EnsureRunningAsAdmin << Workflow::SetAdminSetting; } std::vector SettingsResetCommand::GetArguments() const { return { Argument { Execution::Args::Type::SettingName, Resource::String::SettingNameArgumentDescription, ArgumentType::Positional }, Argument { Execution::Args::Type::All, Resource::String::ResetAllAdminSettingsArgumentDescription, ArgumentType::Flag }, }; } Resource::LocString SettingsResetCommand::ShortDescription() const { return { Resource::String::SettingsResetCommandShortDescription }; } Resource::LocString SettingsResetCommand::LongDescription() const { return { Resource::String::SettingsResetCommandLongDescription }; } Utility::LocIndView SettingsResetCommand::HelpLink() const { return s_SettingsCommand_HelpLink; } void SettingsResetCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { if (execArgs.Contains(Execution::Args::Type::All)) { if (execArgs.Contains(Execution::Args::Type::SettingName)) { throw CommandException(Resource::String::MultipleExclusiveArgumentsProvided("all|setting"_liv)); } return; } if (!execArgs.Contains(Execution::Args::Type::SettingName)) { throw CommandException(Resource::String::RequiredArgError(ArgumentCommon::ForType(Execution::Args::Type::SettingName).Name)); } // Get admin setting string for all available options except Unknown. // We accept both bool and string settings std::vector adminSettingList; for (auto setting : GetAllSequentialEnumValues(BoolAdminSetting::Unknown)) { adminSettingList.emplace_back(AdminSettingToString(setting)); } for (auto setting : GetAllSequentialEnumValues(StringAdminSetting::Unknown)) { adminSettingList.emplace_back(AdminSettingToString(setting)); } Utility::LocIndString validOptions = Join(", "_liv, adminSettingList); if (StringAdminSetting::Unknown == StringToStringAdminSetting(execArgs.GetArg(Execution::Args::Type::SettingName)) && BoolAdminSetting::Unknown == StringToBoolAdminSetting(execArgs.GetArg(Execution::Args::Type::SettingName))) { throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::SettingName).Name, validOptions)); } } void SettingsResetCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::EnsureRunningAsAdmin << (context.Args.Contains(Execution::Args::Type::All) ? Workflow::ResetAllAdminSettings : Workflow::ResetAdminSetting); } } ================================================ FILE: src/AppInstallerCLICore/Commands/SettingsCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct SettingsCommand final : public Command { SettingsCommand(std::string_view parent) : Command("settings", { "config" }, parent, Settings::TogglePolicy::Policy::Settings) {} std::vector> GetCommands() const override; std::vector GetArguments() const override; virtual Resource::LocString ShortDescription() const override; virtual Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; struct SettingsExportCommand final : public Command { SettingsExportCommand(std::string_view parent) : Command("export", parent, CommandOutputFlags::IgnoreSettingsWarnings) {} Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct SettingsSetCommand final : public Command { SettingsSetCommand(std::string_view parent) : Command("set", {}, parent, Settings::TogglePolicy::Policy::Settings) {} std::vector GetArguments() const override; virtual Resource::LocString ShortDescription() const override; virtual Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; struct SettingsResetCommand final : public Command { SettingsResetCommand(std::string_view parent) : Command("reset", {}, parent, Settings::TogglePolicy::Policy::Settings) {} std::vector GetArguments() const override; virtual Resource::LocString ShortDescription() const override; virtual Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ShowCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ShowCommand.h" #include "Workflows/ShowFlow.h" #include "Workflows/CompletionFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; std::vector ShowCommand::GetArguments() const { return { Argument::ForType(Execution::Args::Type::Query), // The manifest argument from Argument::ForType can be blocked by Group Policy but we don't want that here Argument{ Execution::Args::Type::Manifest, Resource::String::ManifestArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument::ForType(Execution::Args::Type::Id), Argument::ForType(Execution::Args::Type::Name), Argument::ForType(Execution::Args::Type::Moniker), Argument::ForType(Execution::Args::Type::Version), Argument::ForType(Execution::Args::Type::Channel), Argument::ForType(Execution::Args::Type::Source), Argument::ForType(Execution::Args::Type::Exact), Argument{ Args::Type::InstallScope, Resource::String::InstallScopeDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument::ForType(Execution::Args::Type::InstallerArchitecture), Argument::ForType(Execution::Args::Type::InstallerType), Argument::ForType(Execution::Args::Type::Locale), Argument::ForType(Execution::Args::Type::ListVersions), Argument::ForType(Execution::Args::Type::CustomHeader), Argument::ForType(Execution::Args::Type::AuthenticationMode), Argument::ForType(Execution::Args::Type::AuthenticationAccount), Argument::ForType(Execution::Args::Type::AcceptSourceAgreements), }; } Resource::LocString ShowCommand::ShortDescription() const { return { Resource::String::ShowCommandShortDescription }; } Resource::LocString ShowCommand::LongDescription() const { return { Resource::String::ShowCommandLongDescription }; } void ShowCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const { switch (valueType) { case Args::Type::InstallerArchitecture: case Args::Type::Locale: // May well move to CompleteWithSingleSemanticsForValue, // but for now output nothing. context << Workflow::CompleteWithEmptySet; break; default: context << Workflow::CompleteWithSingleSemanticsForValue(valueType); } } Utility::LocIndView ShowCommand::HelpLink() const { return "https://aka.ms/winget-command-show"_liv; } void ShowCommand::ValidateArgumentsInternal(Args& execArgs) const { Argument::ValidateCommonArguments(execArgs); } void ShowCommand::ExecuteInternal(Execution::Context& context) const { context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); if (context.Args.Contains(Execution::Args::Type::ListVersions)) { if (context.Args.Contains(Execution::Args::Type::Manifest)) { context << Workflow::GetManifestFromArg << Workflow::ReportManifestIdentity << Workflow::ShowManifestVersion; } else { context << Workflow::OpenSource() << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Show) << Workflow::ReportPackageIdentity << Workflow::ShowAppVersions; } } else { context << GetManifest( /* considerPins */ false) << Workflow::ReportManifestIdentity << Workflow::SelectInstaller << Workflow::ShowManifestInfo; } } } ================================================ FILE: src/AppInstallerCLICore/Commands/ShowCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ShowCommand final : public Command { ShowCommand(std::string_view parent) : Command("show", { "view" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(AppInstaller::CLI::Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/SourceCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SourceCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/SourceFlow.h" #include "Workflows/WorkflowBase.h" #include "Resources.h" namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace std::string_view_literals; namespace { void ValidateSourcePriorityArgument(const Args& execArgs) { if (execArgs.Contains(Execution::Args::Type::SourcePriority)) { std::string_view priorityArg = execArgs.GetArg(Execution::Args::Type::SourcePriority); auto convertedArg = Utility::TryConvertStringToInt32(priorityArg); if (!convertedArg.has_value()) { throw CommandException(Resource::String::InvalidArgumentValueErrorWithoutValidValues(Argument::ForType(Execution::Args::Type::SourcePriority).Name())); } } } } Utility::LocIndView s_SourceCommand_HelpLink = "https://aka.ms/winget-command-source"_liv; std::vector> SourceCommand::GetCommands() const { return InitializeFromMoveOnly>>({ std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), }); } Resource::LocString SourceCommand::ShortDescription() const { return { Resource::String::SourceCommandShortDescription }; } Resource::LocString SourceCommand::LongDescription() const { return { Resource::String::SourceCommandLongDescription }; } Utility::LocIndView SourceCommand::HelpLink() const { return s_SourceCommand_HelpLink; } void SourceCommand::ExecuteInternal(Context& context) const { OutputHelp(context.Reporter); } std::vector SourceAddCommand::GetArguments() const { return { Argument::ForType(Args::Type::SourceName).SetRequired(true), Argument::ForType(Args::Type::SourceArg), Argument::ForType(Args::Type::SourceType), Argument::ForType(Args::Type::SourceTrustLevel), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AcceptSourceAgreements), Argument::ForType(Args::Type::SourceExplicit), Argument::ForType(Args::Type::SourcePriority), }; } Resource::LocString SourceAddCommand::ShortDescription() const { return { Resource::String::SourceAddCommandShortDescription }; } Resource::LocString SourceAddCommand::LongDescription() const { return { Resource::String::SourceAddCommandLongDescription }; } Utility::LocIndView SourceAddCommand::HelpLink() const { return s_SourceCommand_HelpLink; } void SourceAddCommand::ValidateArgumentsInternal(Args& execArgs) const { if (execArgs.Contains(Execution::Args::Type::SourceTrustLevel)) { try { std::string trustLevelArg = std::string{ execArgs.GetArg(Execution::Args::Type::SourceTrustLevel) }; for (auto trustLevel : Utility::Split(trustLevelArg, '|', true)) { Repository::ConvertToSourceTrustLevelEnum(trustLevel); } } catch (...) { auto validOptions = std::vector{ Utility::LocIndString{ Repository::SourceTrustLevelEnumToString(Repository::SourceTrustLevel::None) }, Utility::LocIndString{ Repository::SourceTrustLevelEnumToString(Repository::SourceTrustLevel::Trusted) } }; throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::SourceTrustLevel).Name, Utility::Join(","_liv, validOptions))); } } ValidateSourcePriorityArgument(execArgs); } void SourceAddCommand::ExecuteInternal(Context& context) const { // Note: Group Policy for allowed sources is enforced at the RepositoryCore level // as we need to validate the source data and handle sources that were already added. context << Workflow::EnsureRunningAsAdmin << Workflow::GetSourceList << Workflow::CheckSourceListAgainstAdd << Workflow::CreateSourceForSourceAdd << Workflow::AddSource; } std::vector SourceListCommand::GetArguments() const { return { Argument::ForType(Args::Type::SourceName), }; } Resource::LocString SourceListCommand::ShortDescription() const { return { Resource::String::SourceListCommandShortDescription }; } Resource::LocString SourceListCommand::LongDescription() const { return { Resource::String::SourceListCommandLongDescription }; } void SourceListCommand::Complete(Context& context, Args::Type valueType) const { if (valueType == Args::Type::SourceName) { context << Workflow::CompleteSourceName; } } Utility::LocIndView SourceListCommand::HelpLink() const { return s_SourceCommand_HelpLink; } void SourceListCommand::ExecuteInternal(Context& context) const { context << Workflow::GetSourceListWithFilter << Workflow::ListSources; } std::vector SourceUpdateCommand::GetArguments() const { return { Argument::ForType(Args::Type::SourceName), }; } Resource::LocString SourceUpdateCommand::ShortDescription() const { return { Resource::String::SourceUpdateCommandShortDescription }; } Resource::LocString SourceUpdateCommand::LongDescription() const { return { Resource::String::SourceUpdateCommandLongDescription }; } void SourceUpdateCommand::Complete(Context& context, Args::Type valueType) const { if (valueType == Args::Type::SourceName) { context << Workflow::CompleteSourceName; } } Utility::LocIndView SourceUpdateCommand::HelpLink() const { return s_SourceCommand_HelpLink; } void SourceUpdateCommand::ExecuteInternal(Context& context) const { context << Workflow::GetSourceListWithFilter << Workflow::UpdateSources; } std::vector SourceRemoveCommand::GetArguments() const { return { Argument::ForType(Args::Type::SourceName).SetRequired(true), }; } Resource::LocString SourceRemoveCommand::ShortDescription() const { return { Resource::String::SourceRemoveCommandShortDescription }; } Resource::LocString SourceRemoveCommand::LongDescription() const { return { Resource::String::SourceRemoveCommandLongDescription }; } void SourceRemoveCommand::Complete(Context& context, Args::Type valueType) const { if (valueType == Args::Type::SourceName) { context << Workflow::CompleteSourceName; } } Utility::LocIndView SourceRemoveCommand::HelpLink() const { return s_SourceCommand_HelpLink; } void SourceRemoveCommand::ExecuteInternal(Context& context) const { // Note: Group Policy for unremovable sources is enforced at the RepositoryCore. context << Workflow::EnsureRunningAsAdmin << Workflow::GetSourceListWithFilter << Workflow::RemoveSources; } std::vector SourceResetCommand::GetArguments() const { return { Argument::ForType(Args::Type::SourceName), Argument{ Args::Type::ForceSourceReset, Resource::String::SourceResetForceArgumentDescription, ArgumentType::Flag }, }; } Resource::LocString SourceResetCommand::ShortDescription() const { return { Resource::String::SourceResetCommandShortDescription }; } Resource::LocString SourceResetCommand::LongDescription() const { return { Resource::String::SourceResetCommandLongDescription }; } void SourceResetCommand::Complete(Context& context, Args::Type valueType) const { if (valueType == Args::Type::SourceName) { context << Workflow::CompleteSourceName; } } Utility::LocIndView SourceResetCommand::HelpLink() const { return s_SourceCommand_HelpLink; } void SourceResetCommand::ExecuteInternal(Context& context) const { if (context.Args.Contains(Args::Type::SourceName)) { context << Workflow::EnsureRunningAsAdmin << Workflow::GetSourceListWithFilter << Workflow::ResetSourceList; } else { context << Workflow::EnsureRunningAsAdmin << Workflow::QueryUserForSourceReset << Workflow::ResetAllSources; } } std::vector SourceExportCommand::GetArguments() const { return { Argument::ForType(Args::Type::SourceName), }; } Resource::LocString SourceExportCommand::ShortDescription() const { return { Resource::String::SourceExportCommandShortDescription }; } Resource::LocString SourceExportCommand::LongDescription() const { return { Resource::String::SourceExportCommandLongDescription }; } void SourceExportCommand::Complete(Context& context, Args::Type valueType) const { if (valueType == Args::Type::SourceName) { context << Workflow::CompleteSourceName; } } Utility::LocIndView SourceExportCommand::HelpLink() const { return s_SourceCommand_HelpLink; } void SourceExportCommand::ExecuteInternal(Context& context) const { context << Workflow::GetSourceListWithFilter << Workflow::ExportSourceList; } // Source Edit Command std::vector SourceEditCommand::GetArguments() const { return { Argument::ForType(Args::Type::SourceName).SetRequired(true), Argument::ForType(Args::Type::SourceEditExplicit), Argument::ForType(Args::Type::SourcePriority), }; } Resource::LocString SourceEditCommand::ShortDescription() const { return { Resource::String::SourceEditCommandShortDescription }; } Resource::LocString SourceEditCommand::LongDescription() const { return { Resource::String::SourceEditCommandLongDescription }; } Utility::LocIndView SourceEditCommand::HelpLink() const { return s_SourceCommand_HelpLink; } void SourceEditCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { if (execArgs.Contains(Execution::Args::Type::SourceEditExplicit)) { std::string_view explicitArg = execArgs.GetArg(Execution::Args::Type::SourceEditExplicit); auto convertedArg = Utility::TryConvertStringToBool(explicitArg); if (!convertedArg.has_value()) { auto validOptions = Utility::Join(", "_liv, std::vector{ "true"_lis, "false"_lis, }); throw CommandException(Resource::String::InvalidArgumentValueError(Argument::ForType(Execution::Args::Type::SourceEditExplicit).Name(), validOptions)); } } ValidateSourcePriorityArgument(execArgs); } void SourceEditCommand::ExecuteInternal(Context& context) const { context << Workflow::EnsureRunningAsAdmin << Workflow::GetSourceListWithFilter << Workflow::EditSources; } } ================================================ FILE: src/AppInstallerCLICore/Commands/SourceCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct SourceCommand final : public Command { SourceCommand(std::string_view parent) : Command("source", parent) {} std::vector> GetCommands() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct SourceAddCommand final : public Command { SourceAddCommand(std::string_view parent) : Command("add", {}, parent, Settings::TogglePolicy::Policy::AllowedSources) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; struct SourceListCommand final : public Command { SourceListCommand(std::string_view parent) : Command("list", { "ls" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct SourceUpdateCommand final : public Command { SourceUpdateCommand(std::string_view parent) : Command("update", { "refresh" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct SourceRemoveCommand final : public Command { // We can remove user or default sources, so this is not gated by any single policy. SourceRemoveCommand(std::string_view parent) : Command("remove", { "rm" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct SourceResetCommand final : public Command { SourceResetCommand(std::string_view parent) : Command("reset", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct SourceExportCommand final : public Command { SourceExportCommand(std::string_view parent) : Command("export", parent, CommandOutputFlags::IgnoreSettingsWarnings) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; struct SourceEditCommand final : public Command { SourceEditCommand(std::string_view parent) : Command("edit", { "config", "set" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/TestCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #ifndef AICLI_DISABLE_TEST_HOOKS #include "TestCommand.h" #include "AppInstallerRuntime.h" #include "TableOutput.h" #include "Public/ConfigurationSetProcessorFactoryRemoting.h" #include "Public/ShutdownMonitoring.h" #include "Workflows/ConfigurationFlow.h" #include "Workflows/MSStoreInstallerHandler.h" #include #include #include using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI { namespace { void LogAndReport(Execution::Context& context, std::string_view message) { context.Reporter.Info() << message << std::endl; AICLI_LOG(CLI, Info, << message); } HRESULT WaitForShutdown(Execution::Context& context) { LogAndReport(context, "Waiting for app shutdown event"); if (!ShutdownMonitoring::ServerShutdownSynchronization::WaitForShutdown(300000)) { LogAndReport(context, "Failed getting app shutdown event"); return APPINSTALLER_CLI_ERROR_INTERNAL_ERROR; } LogAndReport(context, "Succeeded waiting for app shutdown event"); return S_OK; } HRESULT AppShutdownWindowMessage(Execution::Context& context) { auto windowHandle = ShutdownMonitoring::TerminationSignalHandler::Instance()->GetWindowHandle(); if (windowHandle == NULL) { LogAndReport(context, "Window was not created"); return APPINSTALLER_CLI_ERROR_INTERNAL_ERROR; } if (context.Args.Contains(Execution::Args::Type::Force)) { LogAndReport(context, "Sending WM_QUERYENDSESSION message"); THROW_LAST_ERROR_IF(!SendMessageTimeout( windowHandle, WM_QUERYENDSESSION, NULL, ENDSESSION_CLOSEAPP, (SMTO_ABORTIFHUNG | SMTO_ERRORONEXIT), 5000, NULL)); } HRESULT hr = WaitForShutdown(context); if (context.Args.Contains(Execution::Args::Type::Force)) { LogAndReport(context, "Sending WM_ENDSESSION message"); THROW_LAST_ERROR_IF(!SendMessageTimeout( windowHandle, WM_ENDSESSION, NULL, ENDSESSION_CLOSEAPP, (SMTO_ABORTIFHUNG | SMTO_ERRORONEXIT), 10000, NULL)); } return hr; } void AppShutdownTestSystemBlockNewWork(CancelReason reason) { AICLI_LOG(CLI, Info, << "AppShutdownTestSystemBlockNewWork :: " << reason); } void AppShutdownTestSystemBeginShutdown(CancelReason reason) { AICLI_LOG(CLI, Info, << "AppShutdownTestSystemBeginShutdown :: " << reason); } void AppShutdownTestSystemWait() { AICLI_LOG(CLI, Info, << "AppShutdownTestSystemWait"); } void EnsureDSCv3Processor(Execution::Context& context) { auto& configurationSet = context.Get().Set(); configurationSet.Environment().ProcessorIdentifier(L"dscv3"); } void InvokeGetAllUnits(Execution::Context& context) { auto& configurationContext = context.Get(); winrt::Microsoft::Management::Configuration::ConfigurationUnit unit; unit.Type(Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::ConfigurationExportResource))); auto result = configurationContext.Processor().GetAllUnits(unit); if (FAILED(result.ResultInformation().ResultCode())) { context.Reporter.Error() << "Failed to export: " << WINGET_OSTREAM_FORMAT_HRESULT(result.ResultInformation().ResultCode()) << std::endl; AICLI_TERMINATE_CONTEXT(result.ResultInformation().ResultCode()); } for (const auto& resultUnit : result.Units()) { configurationContext.Set().Units().Append(resultUnit); } } // Command to directly invoke the export flow. struct TestConfigurationExportCommand final : public Command { TestConfigurationExportCommand(std::string_view parent) : Command("config-export-units", {}, parent) {} std::vector GetArguments() const override { return { Argument{ Execution::Args::Type::OutputFile, Resource::String::OutputFileArgumentDescription, true }, Argument{ Execution::Args::Type::ConfigurationExportResource, Resource::String::ConfigureExportResource }, }; } Resource::LocString ShortDescription() const override { return "Run config export"_lis; } Resource::LocString LongDescription() const override { return "Runs the GetAllUnits configuration method to test export on a DSC v3 directly."_lis; } protected: void ExecuteInternal(Execution::Context& context) const override { context << VerifyIsFullPackage << CreateConfigurationProcessorWithoutFactory << CreateOrOpenConfigurationSet{ "0.3" } << EnsureDSCv3Processor << CreateConfigurationProcessor << InvokeGetAllUnits << WriteConfigFile; } }; void InvokeFindUnitProcessors(Execution::Context& context) { auto& configurationContext = context.Get(); winrt::Microsoft::Management::Configuration::FindUnitProcessorsOptions findOptions; if (context.Args.Contains(Execution::Args::Type::InstallLocation)) { findOptions.SearchPaths(Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::InstallLocation))); findOptions.SearchPathsExclusive(true); findOptions.UnitDetailFlags(winrt::Microsoft::Management::Configuration::ConfigurationUnitDetailFlags::Local); } auto result = configurationContext.Processor().FindUnitProcessors(findOptions); if (result.Size() > 0) { Execution::TableOutput<2> table(context.Reporter, { "Type"_lis, "Description"_lis }); for (const auto& resultUnitProcessor : result) { table.OutputLine({ Utility::ConvertToUTF8(resultUnitProcessor.UnitType()), Utility::ConvertToUTF8(resultUnitProcessor.UnitDescription()) }); } table.Complete(); } else { context.Reporter.Info() << "No unit processors found."_lis << std::endl; } } // Command to directly invoke find unit processors. struct TestConfigurationFindUnitProcessorsCommand final : public Command { TestConfigurationFindUnitProcessorsCommand(std::string_view parent) : Command("config-find-unit-processors", {}, parent) {} std::vector GetArguments() const override { return { Argument{ Execution::Args::Type::InstallLocation, Resource::String::LocationArgumentDescription }, }; } Resource::LocString ShortDescription() const override { return "Run find unit processors"_lis; } Resource::LocString LongDescription() const override { return "Runs find unit processors. Search paths could be provided."_lis; } protected: void ExecuteInternal(Execution::Context& context) const override { context << VerifyIsFullPackage << CreateConfigurationProcessorWithoutFactory << CreateOrOpenConfigurationSet{ "0.3" } << EnsureDSCv3Processor << CreateConfigurationProcessor << InvokeFindUnitProcessors; } }; struct TestCanUnloadNowCommand final : public Command { TestCanUnloadNowCommand(std::string_view parent) : Command("can-unload-now", {}, parent, Visibility::Hidden) {} Resource::LocString ShortDescription() const override { return "Test DllCanUnloadNow"_lis; } Resource::LocString LongDescription() const override { return "Verifies that the function that implements the inproc DllCanUnloadNow properly blocks unload due to static storage object."_lis; } protected: void ExecuteInternal(Execution::Context& context) const override { Repository::Source source{ Repository::PredefinedSource::Installed }; ProgressCallback progress; source.Open(progress); HMODULE self = GetModuleHandle(L"WindowsPackageManager.dll"); if (!self) { LogAndReport(context, "Couldn't get WindowsPackageManager module"); return; } auto WindowsPackageManagerInProcModuleTerminate = reinterpret_cast(GetProcAddress(self, "WindowsPackageManagerInProcModuleTerminate")); // Report the object counts, attempt to terminate, report the object counts again ReportObjectCounts(context); LogAndReport(context, WindowsPackageManagerInProcModuleTerminate() ? "DllCanUnloadNow" : "DllCannotUnloadNow"); ReportObjectCounts(context); } private: void ReportObjectCounts(Execution::Context& context) const { std::ostringstream stream; stream << "Internal objects: " << GetInternalObjectCount() << '\n'; stream << "External objects: " << GetExternalObjectCount(); LogAndReport(context, stream.str()); } uint32_t GetInternalObjectCount() const { return winrt::get_module_lock().operator unsigned int(); } unsigned long GetExternalObjectCount() const { auto module = Microsoft::WRL::GetModuleBase(); return module ? module->GetObjectCount() : 0; } }; struct TestTerminateTerminationSignalHandler final : public Command { TestTerminateTerminationSignalHandler(std::string_view parent) : Command("term-signal-handler", {}, parent, Visibility::Hidden) {} Resource::LocString ShortDescription() const override { return "Test TerminationSignalHandler thread"_lis; } Resource::LocString LongDescription() const override { return "Forces the TerminationSignalHandler static object to be destroyed so that the thread behavior can be observed."_lis; } protected: void ExecuteInternal(Execution::Context& context) const override { // Destroy the one created by standard execution // We join on the window thread, so if this never exits we have failed the test. winrt::Windows::ApplicationModel::Core::CoreApplication::Properties().TryRemove(L"WindowsPackageManager.TerminationSignalHandler"); // Create a new instance auto instance = ShutdownMonitoring::TerminationSignalHandler::Instance(); if (instance->GetWindowHandle() == nullptr) { LogAndReport(context, "Didn't get a window handle"); } else { LogAndReport(context, "Got a window handle"); } } }; } std::vector> TestCommand::GetCommands() const { return InitializeFromMoveOnly>>( { std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), }); } void TestCommand::ExecuteInternal(Execution::Context& context) const { UNREFERENCED_PARAMETER(context); Sleep(INFINITE); } Resource::LocString TestCommand::ShortDescription() const { return Utility::LocIndString("Waits infinitely"sv); } Resource::LocString TestCommand::LongDescription() const { return Utility::LocIndString("Waits infinitely. Use this if you want winget to wait forever while something is going on"sv); } std::vector TestAppShutdownCommand::GetArguments() const { return { Argument::ForType(Execution::Args::Type::Force) }; } void TestAppShutdownCommand::ExecuteInternal(Execution::Context& context) const { HRESULT hr = E_FAIL; ShutdownMonitoring::ServerShutdownSynchronization::ComponentSystem appShutdownTestSystem{}; appShutdownTestSystem.BlockNewWork = AppShutdownTestSystemBlockNewWork; appShutdownTestSystem.BeginShutdown = AppShutdownTestSystemBeginShutdown; appShutdownTestSystem.Wait = AppShutdownTestSystemWait; ShutdownMonitoring::ServerShutdownSynchronization::AddComponent(appShutdownTestSystem); // Only package context and admin won't create the window message. if (!Runtime::IsRunningInPackagedContext() || !Runtime::IsRunningAsAdmin()) { hr = AppShutdownWindowMessage(context); } else { hr = WaitForShutdown(context); } AICLI_TERMINATE_CONTEXT(hr); } Resource::LocString TestAppShutdownCommand::ShortDescription() const { return Utility::LocIndString("Test command to verify appshutdown event."sv); } Resource::LocString TestAppShutdownCommand::LongDescription() const { return Utility::LocIndString("Test command for appshutdown. Verifies the window was created and waits for the app shutdown event"sv); } } #endif ================================================ FILE: src/AppInstallerCLICore/Commands/TestCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #ifndef AICLI_DISABLE_TEST_HOOKS namespace AppInstaller::CLI { // Command: winget test // Convenient command for debugging. Waits infinitely. // Use this if you want to debug things that happen out side of workflows or modify locally to do whatever you need. struct TestCommand final : public Command { TestCommand(std::string_view parent) : Command("test", {}, parent, Visibility::Hidden) {} std::vector> GetCommands() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; // Command: winget test appshutdown // Verifies the window was created and waits for the app shutdown event. // Used in E2E. struct TestAppShutdownCommand final : public Command { TestAppShutdownCommand(std::string_view parent) : Command("appshutdown", {}, parent, Visibility::Hidden) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } #endif ================================================ FILE: src/AppInstallerCLICore/Commands/UninstallCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "UninstallCommand.h" #include "Workflows/UninstallFlow.h" #include "Workflows/CompletionFlow.h" #include "Workflows/WorkflowBase.h" #include "Workflows/MultiQueryFlow.h" #include "Resources.h" namespace AppInstaller::CLI { using AppInstaller::CLI::Execution::Args; using namespace AppInstaller::CLI::Workflow; std::vector UninstallCommand::GetArguments() const { return { Argument::ForType(Args::Type::MultiQuery), Argument::ForType(Args::Type::Manifest), Argument::ForType(Args::Type::Id), Argument::ForType(Args::Type::Name), Argument::ForType(Args::Type::Moniker), Argument::ForType(Args::Type::ProductCode), Argument::ForType(Args::Type::TargetVersion), Argument::ForType(Args::Type::AllVersions), Argument::ForType(Args::Type::Channel), Argument::ForType(Args::Type::Source), Argument::ForType(Args::Type::Exact), Argument{ Args::Type::InstallScope, Resource::String::InstalledScopeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument::ForType(Args::Type::Interactive), Argument::ForType(Args::Type::Silent), Argument::ForType(Args::Type::Force), Argument::ForType(Args::Type::Purge), Argument::ForType(Args::Type::Preserve), Argument::ForType(Args::Type::Log), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AuthenticationMode), Argument::ForType(Args::Type::AuthenticationAccount), Argument::ForType(Args::Type::AcceptSourceAgreements), }; } Resource::LocString UninstallCommand::ShortDescription() const { return { Resource::String::UninstallCommandShortDescription }; } Resource::LocString UninstallCommand::LongDescription() const { return { Resource::String::UninstallCommandLongDescription }; } void UninstallCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const { if (valueType == Execution::Args::Type::Manifest || valueType == Execution::Args::Type::Log) { // Intentionally output nothing to allow pass through to filesystem. return; } context << Workflow::OpenSource() << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) { case Execution::Args::Type::MultiQuery: context << Workflow::RequireCompletionWordNonEmpty << Workflow::SearchSourceForManyCompletion << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::TargetVersion: case Execution::Args::Type::Channel: case Execution::Args::Type::Source: case Execution::Args::Type::ProductCode: context << Workflow::CompleteWithSingleSemanticsForValueUsingExistingSource(valueType); break; } } Utility::LocIndView UninstallCommand::HelpLink() const { return "https://aka.ms/winget-command-uninstall"_liv; } void UninstallCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { Argument::ValidateCommonArguments(execArgs); } void UninstallCommand::ExecuteInternal(Execution::Context& context) const { context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); // open the sources where to search for the package context << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << Workflow::OpenSource() << Workflow::OpenCompositeSource(Workflow::DetermineInstalledSource(context)); // find the uninstaller if (context.Args.Contains(Execution::Args::Type::Manifest)) { // --manifest case where new manifest is provided context << Workflow::GetManifestFromArg << Workflow::SearchSourceUsingManifest << Workflow::EnsureOneMatchFromSearchResult(OperationType::Uninstall) << Workflow::UninstallSinglePackage; } else { // search for specific packages to uninstall if (!context.Args.Contains(Execution::Args::Type::MultiQuery)) { context << Workflow::SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Uninstall) << Workflow::UninstallSinglePackage; } else { context << Workflow::GetMultiSearchRequests << Workflow::SearchSubContextsForSingle(OperationType::Uninstall) << Workflow::UninstallMultiplePackages; } } } } ================================================ FILE: src/AppInstallerCLICore/Commands/UninstallCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct UninstallCommand final : public Command { UninstallCommand(std::string_view parent) : Command("uninstall", { "remove", "rm" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/UpgradeCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "UpgradeCommand.h" #include "Workflows/CompletionFlow.h" #include "Workflows/DownloadFlow.h" #include "Workflows/InstallFlow.h" #include "Workflows/MultiQueryFlow.h" #include "Workflows/UpdateFlow.h" #include "Workflows/WorkflowBase.h" #include "Workflows/DependenciesFlow.h" #include "Resources.h" #include namespace AppInstaller::CLI { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Manifest; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Utility::literals; namespace { // Determines whether we should list available upgrades, instead // of performing an upgrade bool ShouldListUpgrade(const Execution::Args& args, ArgTypeCategory argCategories = ArgTypeCategory::None) { if (argCategories == ArgTypeCategory::None) { argCategories = Argument::GetCategoriesPresent(args); } // Valid arguments for list are only those related to the sources and which packages to include (e.g. --include-unknown). // Instead of checking for them, we check that there aren't any other arguments present. return !args.Contains(Args::Type::All) && WI_AreAllFlagsClear(argCategories, ArgTypeCategory::Manifest | ArgTypeCategory::PackageQuery | ArgTypeCategory::InstallerBehavior); } } std::vector UpgradeCommand::GetArguments() const { return { Argument::ForType(Args::Type::MultiQuery), // -q Argument::ForType(Args::Type::Manifest), // -m Argument::ForType(Args::Type::Id), Argument::ForType(Args::Type::Name), Argument::ForType(Args::Type::Moniker), Argument::ForType(Args::Type::Version), // -v Argument::ForType(Args::Type::Channel), Argument::ForType(Args::Type::Source), // -s Argument::ForType(Args::Type::Exact), // -e Argument::ForType(Args::Type::Interactive), // -i Argument::ForType(Args::Type::Silent), // -h Argument::ForType(Args::Type::Purge), Argument::ForType(Args::Type::Log), // -o Argument::ForType(Args::Type::CustomSwitches), Argument::ForType(Args::Type::Override), Argument::ForType(Args::Type::InstallLocation), // -l Argument{ Args::Type::InstallScope, Resource::String::InstalledScopeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }, Argument::ForType(Args::Type::InstallArchitecture), // -a Argument::ForType(Args::Type::InstallerType), Argument::ForType(Args::Type::Locale), Argument::ForType(Args::Type::HashOverride), Argument::ForType(Args::Type::AllowReboot), Argument::ForType(Args::Type::SkipDependencies), Argument::ForType(Args::Type::IgnoreLocalArchiveMalwareScan), Argument::ForType(Args::Type::AcceptPackageAgreements), Argument::ForType(Args::Type::AcceptSourceAgreements), Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AuthenticationMode), Argument::ForType(Args::Type::AuthenticationAccount), Argument{ Args::Type::All, Resource::String::UpdateAllArgumentDescription, ArgumentType::Flag }, Argument{ Args::Type::IncludeUnknown, Resource::String::IncludeUnknownArgumentDescription, ArgumentType::Flag }, Argument{ Args::Type::IncludePinned, Resource::String::IncludePinnedArgumentDescription, ArgumentType::Flag}, Argument::ForType(Args::Type::UninstallPrevious), Argument::ForType(Args::Type::Force), }; } Resource::LocString UpgradeCommand::ShortDescription() const { return { Resource::String::UpgradeCommandShortDescription }; } Resource::LocString UpgradeCommand::LongDescription() const { return { Resource::String::UpgradeCommandLongDescription }; } void UpgradeCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const { if (valueType == Execution::Args::Type::Manifest || valueType == Execution::Args::Type::Log || valueType == Execution::Args::Type::Override || valueType == Execution::Args::Type::InstallLocation) { // Intentionally output nothing to allow pass through to filesystem. return; } context << OpenSource() << OpenCompositeSource(Repository::PredefinedSource::Installed); switch (valueType) { case Execution::Args::Type::MultiQuery: context << RequireCompletionWordNonEmpty << SearchSourceForManyCompletion << CompleteWithMatchedField; break; case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::Version: case Execution::Args::Type::Channel: case Execution::Args::Type::Source: context << CompleteWithSingleSemanticsForValueUsingExistingSource(valueType); break; case Args::Type::InstallArchitecture: case Args::Type::Locale: // May well move to CompleteWithSingleSemanticsForValue, // but for now output nothing. context << Workflow::CompleteWithEmptySet; break; } } Utility::LocIndView UpgradeCommand::HelpLink() const { return "https://aka.ms/winget-command-upgrade"_liv; } void UpgradeCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { const auto argCategories = Argument::GetCategoriesAndValidateCommonArguments(execArgs, /* requirePackageSelectionArg */ false); if (!ShouldListUpgrade(execArgs, argCategories) && WI_IsFlagClear(argCategories, ArgTypeCategory::PackageQuery) && WI_IsFlagSet(argCategories, ArgTypeCategory::SingleInstallerBehavior)) { throw CommandException(Resource::String::InvalidArgumentWithoutQueryError); } } void UpgradeCommand::ExecuteInternal(Execution::Context& context) const { context.SetFlags(Execution::ContextFlag::InstallerExecutionUseUpdate); // Only allow for source failures when doing a list of available upgrades. // We have to set it now to allow for source open failures to also just warn. if (ShouldListUpgrade(context.Args)) { context.SetFlags(Execution::ContextFlag::TreatSourceFailuresAsWarning); } context << InitializeInstallerDownloadAuthenticatorsMap << ReportExecutionStage(ExecutionStage::Discovery) << OpenSource() << OpenCompositeSource(DetermineInstalledSource(context)); if (ShouldListUpgrade(context.Args)) { // Upgrade with no args list packages with updates available context << SearchSourceForMany << HandleSearchResultFailures << EnsureMatchesFromSearchResult(OperationType::Upgrade) << ReportListResult(true); } else if (context.Args.Contains(Execution::Args::Type::All)) { // --all switch updates all packages found context << SearchSourceForMany << HandleSearchResultFailures << EnsureMatchesFromSearchResult(OperationType::Upgrade) << ReportListResult(true) << UpdateAllApplicable; } else if (context.Args.Contains(Execution::Args::Type::Manifest)) { // --manifest case where new manifest is provided context << GetManifestFromArg << SearchSourceUsingManifest << EnsureOneMatchFromSearchResult(OperationType::Upgrade) << GetInstalledPackageVersion << EnsureUpdateVersionApplicable << SelectInstaller << EnsureApplicableInstaller << InstallSinglePackage; } else { // The remaining case: search for specific packages to update if (!context.Args.Contains(Execution::Args::Type::MultiQuery)) { context << InstallOrUpgradeSinglePackage(OperationType::Upgrade); } else { ProcessMultiplePackages::Flags flags = ProcessMultiplePackages::Flags::None; if (Settings::User().Get() || context.Args.Contains(Execution::Args::Type::SkipDependencies)) { flags = ProcessMultiplePackages::Flags::IgnoreDependencies; } context << GetMultiSearchRequests << SearchSubContextsForSingle(OperationType::Upgrade) << ReportExecutionStage(ExecutionStage::Execution) << ProcessMultiplePackages(Resource::String::PackageRequiresDependencies, APPINSTALLER_CLI_ERROR_MULTIPLE_INSTALL_FAILED, flags); } } } } ================================================ FILE: src/AppInstallerCLICore/Commands/UpgradeCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct UpgradeCommand final : public Command { UpgradeCommand(std::string_view parent) : Command("upgrade", { "update" }, parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; Utility::LocIndView HelpLink() const override; protected: void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Commands/ValidateCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ValidateCommand.h" #include "Workflows/WorkflowBase.h" #include "Workflows/DependenciesFlow.h" #include "Resources.h" #include namespace AppInstaller::CLI { using namespace std::string_view_literals; using namespace AppInstaller::Manifest; std::vector ValidateCommand::GetArguments() const { return { Argument::ForType(Execution::Args::Type::ValidateManifest), }; } Resource::LocString ValidateCommand::ShortDescription() const { return Resource::LocString{ Resource::String::ValidateCommandShortDescription }; } Resource::LocString ValidateCommand::LongDescription() const { return Resource::LocString{ Resource::String::ValidateCommandLongDescription }; } Utility::LocIndView ValidateCommand::HelpLink() const { return "https://aka.ms/winget-command-validate"_liv; } void ValidateCommand::ExecuteInternal(Execution::Context& context) const { context << Workflow::VerifyPath(Execution::Args::Type::ValidateManifest) << [](Execution::Context& context) { auto inputFile = Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::ValidateManifest)); try { ManifestValidateOption validateOption; validateOption.FullValidation = true; validateOption.SchemaHeaderValidationAsWarning = true; validateOption.ThrowOnWarning = !(context.Args.Contains(Execution::Args::Type::IgnoreWarnings)); auto manifest = YamlParser::CreateFromPath(inputFile, validateOption); context.Add(manifest); context << Workflow::GetInstallersDependenciesFromManifest << Workflow::ReportDependencies(Resource::String::ValidateCommandReportDependencies); context.Reporter.Info() << Resource::String::ManifestValidationSuccess << std::endl; } catch (const ManifestException& e) { HRESULT hr = S_OK; if (e.IsWarningOnly()) { context.Reporter.Warn() << Resource::String::ManifestValidationWarning << std::endl; hr = APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_WARNING; } else { context.Reporter.Error() << Resource::String::ManifestValidationFail << std::endl; hr = APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_FAILURE; } context.Reporter.Info() << e.GetManifestErrorMessage() << std::endl; AICLI_TERMINATE_CONTEXT(hr); } }; } } ================================================ FILE: src/AppInstallerCLICore/Commands/ValidateCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ValidateCommand final : public Command { ValidateCommand(std::string_view parent) : Command("validate", parent) {} virtual std::vector GetArguments() const override; virtual Resource::LocString ShortDescription() const override; virtual Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/CompletionData.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "CompletionData.h" #include "Resources.h" #include #include namespace AppInstaller::CLI { using namespace std::string_view_literals; using namespace Utility::literals; // Completion takes in the following values: // Word :: The token from the command line that is being targeted for completion. // This value may have quotes surrounding it, and will need to be removed in such a case. // CommandLine :: The full command line that contains the word to be completed. // This value has the fully quoted strings, as well as escaped quotations if needed. // Position :: The position of the cursor within the command line. // // Completions here will not attempt to take exact cursor position into account; meaning if the cursor // is in the middle of the word, it is not different than at the beginning or end. This functionality // could be added later. CompletionData::CompletionData(std::string_view word, std::string_view commandLine, std::string_view position) { m_word = word; AICLI_LOG(CLI, Info, << "Completing word '" << m_word << '\''); // Determine position as an integer size_t cursor = wil::safe_cast(std::stoull(std::string{ position })); AICLI_LOG(CLI, Info, << "Cursor position starts at '" << cursor << '\''); // First, move the cursor from the UTF-8 grapheme position to the UTF-8 byte position. // This simplifies the rest of the code. cursor = Utility::UTF8Substring(commandLine, 0, cursor).length(); AICLI_LOG(CLI, Info, << "Cursor position moved to '" << cursor << '\''); std::vector argsBeforeWord; std::vector argsAfterWord; // If the word is empty, we must determine where the split is. We operate as PowerShell does; the cursor // being at the front of a token results in an empty word and an insertion rather than a replacement. // If the user put spaces at the front of the statement, this can lead to the position being out of sorts; // PowerShell sends the cursor position, but does not include leading spaces in the AST output. If the // user puts too many spaces at the front we will be unable to determine the true location. if (m_word.empty()) { // The cursor is past the end, so everything is before the word. if (cursor >= commandLine.length()) { // Move the position to the end in case it was extended past it. ParseInto(commandLine, argsBeforeWord, true); } // The cursor is not past the end; ensure that the preceding character is whitespace or move the // position back until it is. This is far from foolproof, but until we have evidence otherwise, // very few users are likely to put any spaces at the front of their statements, let alone many. else { for (; cursor > 0 && !std::isspace(static_cast(commandLine[cursor - 1])); --cursor); AICLI_LOG(CLI, Info, << "Cursor position moved to '" << cursor << '\''); // If we actually hit the front of the string, something bad probably happened. THROW_HR_IF(APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD, cursor == 0); ParseInto(commandLine.substr(0, cursor), argsBeforeWord, true); ParseInto(commandLine.substr(cursor), argsAfterWord, false); } } // If the word is not empty, the cursor is either in the middle of a token, or at the end of one. // The value will be replaced, and we will remove it from the args here. else { std::vector allArgs; ParseInto(commandLine, allArgs, true); // Find the word amongst the arguments std::vector wordIndices; for (size_t i = 0; i < allArgs.size(); ++i) { if (m_word == allArgs[i]) { wordIndices.push_back(i); } } // If we didn't find a matching string, we probably made some bad assumptions. THROW_HR_IF(APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD, wordIndices.empty()); // If we find an exact match only once, we can just split on that. size_t wordIndexForSplit = wordIndices[0]; // If we found more than one match, we have to rely on the position to // determine which argument is the word in question. if (wordIndices.size() > 1) { // Escape the word and search for it in the command line. std::string escapedWord = m_word; Utility::FindAndReplace(escapedWord, "\"", "\"\""); std::vector escapedIndices; for (size_t offset = 0; offset < commandLine.length();) { size_t pos = commandLine.find(escapedWord, offset); if (pos == std::string::npos) { break; } escapedIndices.push_back(pos); offset = pos + escapedWord.length(); } // If these are out of sync we don't have much hope. THROW_HR_IF(APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD, wordIndices.size() != escapedIndices.size()); // Find the closest one to the position. This can be fooled as above if there is // leading whitespace in the statement. But it is the best we can do. size_t indexToUse = std::numeric_limits::max(); size_t distanceToCursor = std::numeric_limits::max(); for (size_t i = 0; i < escapedIndices.size(); ++i) { size_t lowerBound = escapedIndices[i]; size_t upperBound = lowerBound + escapedWord.length(); size_t distance = 0; // The cursor is square in the middle of this location, this is the one. if (cursor > lowerBound && cursor <= upperBound) { indexToUse = i; break; } else if (cursor <= lowerBound) { distance = lowerBound - cursor; } else // cursor > upperBound { distance = cursor - upperBound; } if (distance < distanceToCursor) { indexToUse = i; distanceToCursor = distance; } } // It really would be unexpected to not find a closest one. THROW_HR_IF(APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD, indexToUse == std::numeric_limits::max()); wordIndexForSplit = wordIndices[indexToUse]; } std::vector* moveTarget = &argsBeforeWord; for (size_t i = 0; i < allArgs.size(); ++i) { if (i == wordIndexForSplit) { // Intentionally leave the matched arg behind. moveTarget = &argsAfterWord; } else { moveTarget->emplace_back(std::move(allArgs[i])); } } } // Move the arguments into an Invocation for future use. m_argsBeforeWord = std::make_unique(std::move(argsBeforeWord)); m_argsAfterWord = std::make_unique(std::move(argsAfterWord)); AICLI_LOG(CLI, Info, << "Completion invoked for arguments:" << [&]() { std::stringstream strstr; for (const auto& arg : *m_argsBeforeWord) { strstr << " '" << arg << '\''; } if (m_word.empty()) { strstr << " << [insert] >> "; } else { strstr << " << [replace] '" << m_word << "' >> "; } for (const auto& arg : *m_argsAfterWord) { strstr << " '" << arg << '\''; } return strstr.str(); }()); } void CompletionData::ParseInto(std::string_view line, std::vector& args, bool skipFirst) { std::wstring commandLineW = Utility::ConvertToUTF16(line); int argc = 0; wil::unique_hlocal_ptr argv{ CommandLineToArgvW(commandLineW.c_str(), &argc) }; THROW_LAST_ERROR_IF_NULL(argv); for (int i = (skipFirst ? 1 : 0); i < argc; ++i) { args.emplace_back(Utility::ConvertToUTF8(argv.get()[i])); } } } ================================================ FILE: src/AppInstallerCLICore/CompletionData.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionArgs.h" #include "Invocation.h" #include #include #include #include namespace AppInstaller::CLI { // Data created by CompleteCommand to be consumed by other commands in order // to provide context sensitive results. struct CompletionData { CompletionData(std::string_view word, std::string_view commandLine, std::string_view position); const std::string& Word() const { return m_word; } Invocation& BeforeWord() const { return *m_argsBeforeWord; } Invocation& AfterWord() const { return *m_argsAfterWord; } private: static void ParseInto(std::string_view line, std::vector& args, bool skipFirst); std::string m_word; std::unique_ptr m_argsBeforeWord; std::unique_ptr m_argsAfterWord; }; } ================================================ FILE: src/AppInstallerCLICore/ConfigurationCommon.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationCommon.h" #include "ExecutionArgs.h" #include #include "Command.h" #include #include namespace AppInstaller::CLI { using namespace winrt::Microsoft::Management::Configuration; namespace { constexpr std::string_view s_ModulePath_Default = "default"; constexpr std::string_view s_ModulePath_CurrentUser = "currentuser"; constexpr std::string_view s_ModulePath_AllUsers = "allusers"; struct ModulePathInfo { SetProcessorFactory::PwshConfigurationProcessorLocation location; std::optional customLocation; }; ModulePathInfo GetModulePathInfo(Execution::Args& execArgs) { if (execArgs.Contains(Execution::Args::Type::ConfigurationModulePath)) { auto modulePath = execArgs.GetArg(Execution::Args::Type::ConfigurationModulePath); if (Utility::CaseInsensitiveEquals(modulePath, s_ModulePath_Default)) { return { SetProcessorFactory::PwshConfigurationProcessorLocation::Default, {} }; } else if (Utility::CaseInsensitiveEquals(modulePath, s_ModulePath_CurrentUser)) { return { SetProcessorFactory::PwshConfigurationProcessorLocation::CurrentUser, {} }; } else if (Utility::CaseInsensitiveEquals(modulePath, s_ModulePath_AllUsers)) { return { SetProcessorFactory::PwshConfigurationProcessorLocation::AllUsers, {} }; } else { return { SetProcessorFactory::PwshConfigurationProcessorLocation::Custom, std::string(execArgs.GetArg(Execution::Args::Type::ConfigurationModulePath)) }; } } std::filesystem::path defaultModuleRoot = Settings::User().Get(); if (!defaultModuleRoot.empty()) { return { SetProcessorFactory::PwshConfigurationProcessorLocation::Custom, defaultModuleRoot.u8string() }; } return { SetProcessorFactory::PwshConfigurationProcessorLocation::WinGetModulePath, {} }; } } namespace Configuration { void ValidateCommonArguments(Execution::Args& execArgs, bool requireConfigurationSetChoice) { auto modulePath = GetModulePathInfo(execArgs); if (modulePath.location == SetProcessorFactory::PwshConfigurationProcessorLocation::AllUsers && !Runtime::IsRunningAsAdmin()) { throw CommandException(Resource::String::ConfigurationAllUsersElevated); } if (modulePath.location == SetProcessorFactory::PwshConfigurationProcessorLocation::Custom) { auto path = std::filesystem::path{ modulePath.customLocation.value() }; if (!path.is_absolute()) { throw CommandException(Resource::String::ConfigurationModulePathArgError); } } if (requireConfigurationSetChoice && !WI_IsFlagSet(Argument::GetCategoriesPresent(execArgs), ArgTypeCategory::ConfigurationSetChoice)) { throw CommandException(Resource::String::RequiredArgError("file"_liv)); } } void SetModulePath(Execution::Context& context, IConfigurationSetProcessorFactory const& factory) { auto pwshFactory = factory.as(); auto modulePath = GetModulePathInfo(context.Args); if (modulePath.location == SetProcessorFactory::PwshConfigurationProcessorLocation::Custom) { pwshFactory.CustomLocation(winrt::to_hstring(modulePath.customLocation.value())); } pwshFactory.Location(modulePath.location); } } } ================================================ FILE: src/AppInstallerCLICore/ConfigurationCommon.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" #include namespace AppInstaller::CLI { namespace Configuration { // Validates common arguments between configuration commands. void ValidateCommonArguments(Execution::Args& execArgs, bool requireConfigurationSetChoice = false); // Sets the module path to install modules in the set processor. void SetModulePath(Execution::Context& context, winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory const& factory); } } ================================================ FILE: src/AppInstallerCLICore/ConfigurationContext.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationContext.h" #include using namespace winrt::Microsoft::Management::Configuration; namespace AppInstaller::CLI::Execution { namespace details { struct ConfigurationContextData { ConfigurationProcessor Processor = nullptr; ConfigurationSet Set = nullptr; std::vector History; }; } ConfigurationContext::ConfigurationContext() : m_data(std::make_unique()) { } ConfigurationContext::~ConfigurationContext() = default; ConfigurationContext::ConfigurationContext(ConfigurationContext&&) = default; ConfigurationContext& ConfigurationContext::operator=(ConfigurationContext&&) = default; ConfigurationProcessor& ConfigurationContext::Processor() { return m_data->Processor; } const ConfigurationProcessor& ConfigurationContext::Processor() const { return m_data->Processor; } void ConfigurationContext::Processor(const ConfigurationProcessor& value) { m_data->Processor = value; } void ConfigurationContext::Processor(ConfigurationProcessor&& value) { m_data->Processor = std::move(value); } ConfigurationSet& ConfigurationContext::Set() { return m_data->Set; } const ConfigurationSet& ConfigurationContext::Set() const { return m_data->Set; } void ConfigurationContext::Set(const ConfigurationSet& value) { m_data->Set = value; } void ConfigurationContext::Set(ConfigurationSet&& value) { m_data->Set = std::move(value); } std::vector& ConfigurationContext::History() { return m_data->History; } const std::vector& ConfigurationContext::History() const { return m_data->History; } void ConfigurationContext::History(const winrt::Windows::Foundation::Collections::IVector& value) { std::vector history{ value.Size() }; value.GetMany(0, history); m_data->History = std::move(history); } } ================================================ FILE: src/AppInstallerCLICore/ConfigurationContext.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace winrt::Microsoft::Management::Configuration { struct ConfigurationProcessor; struct ConfigurationSet; } namespace AppInstaller::CLI::Execution { namespace details { struct ConfigurationContextData; } struct ConfigurationContext { using ConfigurationSet = winrt::Microsoft::Management::Configuration::ConfigurationSet; 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); ConfigurationSet& Set(); const ConfigurationSet& Set() const; void Set(const ConfigurationSet& value); void Set(ConfigurationSet&& value); std::vector& History(); const std::vector& History() const; void History(const winrt::Windows::Foundation::Collections::IVector& value); private: std::unique_ptr m_data; }; } ================================================ FILE: src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/ConfigurationSetProcessorFactoryRemoting.h" #include #include #include #include #include #include #include using namespace winrt::Windows::Foundation; using namespace winrt::Microsoft::Management::Configuration; using namespace winrt::Windows::Storage; namespace AppInstaller::CLI::ConfigurationRemoting { namespace anonymous { #ifndef AICLI_DISABLE_TEST_HOOKS constexpr std::wstring_view EnableTestModeTestGuid = L"1e62d683-2999-44e7-81f7-6f8f35e8d731"; constexpr std::wstring_view ForceHighIntegrityLevelUnitsTestGuid = L"f698d20f-3584-4f28-bc75-28037e08e651"; constexpr std::wstring_view EnableRestrictedIntegrityLevelTestGuid = L"5cae3226-185f-4289-815c-3c089d238dc6"; // Checks the configuration set metadata for a specific test guid that controls the behavior flow. bool GetConfigurationSetMetadataOverride(const ConfigurationSet& configurationSet, const std::wstring_view& testGuid) { auto metadataOverride = configurationSet.Metadata().TryLookup(testGuid); if (metadataOverride) { auto metadataOverrideProperty = metadataOverride.try_as(); if (metadataOverrideProperty && metadataOverrideProperty.Type() == PropertyType::Boolean) { return metadataOverrideProperty.GetBoolean(); } } return false; } #endif // This is implemented completely in the packaged context for now, if we want to make it more configurable, we will probably want to move it to configuration and // have this implementation leverage that one with an event handler for the packaged specifics. // TODO: Add SetProcessorFactory::IPwshConfigurationSetProcessorFactoryProperties and pass values along to sets on creation // In turn, any properties must only be set via the command line (or eventual UI requests to the user). struct DynamicFactory : winrt::implements, winrt::cloaked>, WinRT::LifetimeWatcherBase { DynamicFactory(ProcessorEngine processorEngine); IConfigurationSetProcessor CreateSetProcessor(const ConfigurationSet& configurationSet); winrt::event_token Diagnostics(const EventHandler& handler); void Diagnostics(const winrt::event_token& token) noexcept; DiagnosticLevel MinimumLevel(); void MinimumLevel(DiagnosticLevel value); HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); IConfigurationSetProcessorFactory& DefaultFactory(); void SendDiagnostics(const IDiagnosticInformation& information); Collections::IVectorView AdditionalModulePaths() const { THROW_HR(E_NOTIMPL); } void AdditionalModulePaths(const Collections::IVectorView&) { THROW_HR(E_NOTIMPL); } SetProcessorFactory::PwshConfigurationProcessorPolicy Policy() const { THROW_HR(E_NOTIMPL); } void Policy(SetProcessorFactory::PwshConfigurationProcessorPolicy) { THROW_HR(E_NOTIMPL); } SetProcessorFactory::PwshConfigurationProcessorLocation Location() const { return m_location; } void Location(SetProcessorFactory::PwshConfigurationProcessorLocation value) { auto pwshFactory = m_defaultRemoteFactory.as(); pwshFactory.Location(value); m_location = value; } winrt::hstring CustomLocation() const { return m_customLocation; } void CustomLocation(winrt::hstring value) { auto pwshFactory = m_defaultRemoteFactory.as(); pwshFactory.CustomLocation(value); m_customLocation = value; } // Implement a subset of IMap to enable property bag semantics uint32_t Size() { THROW_HR(E_NOTIMPL); } void Clear() { THROW_HR(E_NOTIMPL); } Collections::IMapView GetView() { THROW_HR(E_NOTIMPL); } bool HasKey(winrt::hstring) { THROW_HR(E_NOTIMPL); } void Remove(winrt::hstring) { THROW_HR(E_NOTIMPL); } bool Insert(winrt::hstring key, winrt::hstring value) { auto result = m_defaultRemoteFactory.as>().Insert(key, value); m_factoryMapValues[key] = value; return result; } winrt::hstring Lookup(winrt::hstring key) { return m_defaultRemoteFactory.as>().Lookup(key); } ProcessorEngine Engine() const { return m_processorEngine; } std::optional GetFactoryMapValue(winrt::hstring key) { auto itr = m_factoryMapValues.find(key); return itr != m_factoryMapValues.end() ? std::make_optional(itr->second) : std::nullopt; } private: IConfigurationSetProcessorFactory m_defaultRemoteFactory; winrt::event> m_diagnostics; IConfigurationSetProcessorFactory::Diagnostics_revoker m_factoryDiagnosticsEventRevoker; std::mutex m_diagnosticsMutex; DiagnosticLevel m_minimumLevel = DiagnosticLevel::Informational; SetProcessorFactory::PwshConfigurationProcessorLocation m_location = SetProcessorFactory::PwshConfigurationProcessorLocation::Default; winrt::hstring m_customLocation; ProcessorEngine m_processorEngine; std::map m_factoryMapValues; }; struct DynamicProcessorInfo { IConfigurationSetProcessorFactory Factory; IConfigurationSetProcessor Processor; IConfigurationSetProcessorFactory::Diagnostics_revoker DiagnosticsEventRevoker; }; struct DynamicSetProcessor : winrt::implements { using ProcessorMap = std::map; DynamicSetProcessor(winrt::com_ptr dynamicFactory, IConfigurationSetProcessor defaultRemoteSetProcessor, const ConfigurationSet& configurationSet) : m_dynamicFactory(std::move(dynamicFactory)), m_configurationSet(configurationSet) { #ifndef AICLI_DISABLE_TEST_HOOKS if (m_configurationSet) { m_enableTestMode = GetConfigurationSetMetadataOverride(m_configurationSet, EnableTestModeTestGuid); m_enableRestrictedIntegrityLevel = GetConfigurationSetMetadataOverride(m_configurationSet, EnableRestrictedIntegrityLevelTestGuid); m_forceHighIntegrityLevelUnits = GetConfigurationSetMetadataOverride(m_configurationSet, ForceHighIntegrityLevelUnitsTestGuid); } m_currentIntegrityLevel = m_enableTestMode ? Security::IntegrityLevel::Medium : Security::GetEffectiveIntegrityLevel(); #else m_currentIntegrityLevel = Security::GetEffectiveIntegrityLevel(); #endif m_setIntegrityLevel = m_currentIntegrityLevel; if (m_configurationSet) { m_setIntegrityLevel = SecurityContextToIntegrityLevel(m_configurationSet.Environment().Context()); // Check for multiple integrity level requirements bool multipleIntegrityLevels = false; bool higherIntegrityLevelsThanCurrent = false; for (const auto& environment : m_configurationSet.GetUnitEnvironments()) { auto integrityLevel = SecurityContextToIntegrityLevel(environment.Context()); if (integrityLevel != m_currentIntegrityLevel) { multipleIntegrityLevels = true; if (ToIntegral(m_currentIntegrityLevel) < ToIntegral(integrityLevel)) { higherIntegrityLevelsThanCurrent = true; break; } } } // Prevent supplied parameters from crossing integrity levels for (const auto& parameter : m_configurationSet.Parameters()) { if (parameter.ProvidedValue() != nullptr) { THROW_HR_IF(WINGET_CONFIG_ERROR_PARAMETER_INTEGRITY_BOUNDARY, higherIntegrityLevelsThanCurrent || (multipleIntegrityLevels && parameter.IsSecure())); } } } m_setProcessors.emplace(m_currentIntegrityLevel, DynamicProcessorInfo{ m_dynamicFactory->DefaultFactory(), defaultRemoteSetProcessor}); } IConfigurationUnitProcessorDetails GetUnitProcessorDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags) { // Always get processor details from the current integrity level return m_setProcessors[m_currentIntegrityLevel].Processor.GetUnitProcessorDetails(unit, detailFlags); } // Creates a configuration unit processor for the given unit. IConfigurationUnitProcessor CreateUnitProcessor(const ConfigurationUnit& unit) { // Determine and create set processors for all required integrity levels. // Doing this here avoids creating them if the only call is going to be for details (ex. `configure show`) std::call_once(m_createUnitSetProcessorsOnce, [&]() { if (m_configurationSet) { for (const auto& environment : m_configurationSet.GetUnitEnvironments()) { Security::IntegrityLevel requiredIntegrityLevel = SecurityContextToIntegrityLevel(environment.Context()); if (m_setProcessors.find(requiredIntegrityLevel) == m_setProcessors.end()) { CreateSetProcessorForIntegrityLevel(requiredIntegrityLevel); } } } }); // Create set and unit processor for current unit. #ifndef AICLI_DISABLE_TEST_HOOKS Security::IntegrityLevel requiredIntegrityLevel = m_forceHighIntegrityLevelUnits ? Security::IntegrityLevel::High : GetIntegrityLevelForUnit(unit); #else Security::IntegrityLevel requiredIntegrityLevel = GetIntegrityLevelForUnit(unit); #endif auto itr = m_setProcessors.find(requiredIntegrityLevel); if (itr == m_setProcessors.end()) { THROW_WIN32_IF_MSG(ERROR_NOT_SUPPORTED, !m_configurationSet, "Using configuration unit integrity level other than current level without a configuration set is not supported."); itr = CreateSetProcessorForIntegrityLevel(requiredIntegrityLevel); } return itr->second.Processor.CreateUnitProcessor(unit); } Collections::IVector FindUnitProcessors(const FindUnitProcessorsOptions& findOptions) { IFindUnitProcessorsSetProcessor findUnitProcessorsSetProcessor; if (m_setProcessors[m_currentIntegrityLevel].Processor.try_as(findUnitProcessorsSetProcessor)) { return findUnitProcessorsSetProcessor.FindUnitProcessors(findOptions); } else { AICLI_LOG(Config, Error, << "Set Processor does not support FindUnitProcessors operation"); THROW_HR(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR); } } private: // Converts the string representation of SecurityContext to the target integrity level for this instance Security::IntegrityLevel SecurityContextToIntegrityLevel(SecurityContext securityContext) { switch (securityContext) { case SecurityContext::Current: return m_setIntegrityLevel; case SecurityContext::Restricted: #ifndef AICLI_DISABLE_TEST_HOOKS if (m_enableRestrictedIntegrityLevel) { return Security::IntegrityLevel::Medium; } else #endif { // Not supporting elevated callers downgrading at the moment. THROW_WIN32(ERROR_NOT_SUPPORTED); // Technically this means the default level of the user token, so if UAC is disabled it would be the only integrity level (aka current). // return Security::IntegrityLevel::Medium; } case SecurityContext::Elevated: return Security::IntegrityLevel::High; default: THROW_WIN32(ERROR_NOT_SUPPORTED); } } // Gets the integrity level that the given unit should be run at Security::IntegrityLevel GetIntegrityLevelForUnit(const ConfigurationUnit& unit) { return SecurityContextToIntegrityLevel(unit.Environment().Context()); } // Serializes the set properties to be sent to the remote server std::string SerializeSetProperties() { Json::Value json{ Json::ValueType::objectValue }; json["path"] = winrt::to_string(m_configurationSet.Path()); std::string locationString; switch (m_dynamicFactory->Location()) { case SetProcessorFactory::PwshConfigurationProcessorLocation::AllUsers: locationString = "AllUsers"; break; case SetProcessorFactory::PwshConfigurationProcessorLocation::CurrentUser: locationString = "CurrentUser"; break; case SetProcessorFactory::PwshConfigurationProcessorLocation::Custom: locationString = Utility::ConvertToUTF8(m_dynamicFactory->CustomLocation()); break; case SetProcessorFactory::PwshConfigurationProcessorLocation::Default: break; } if (!locationString.empty()) { json["modulePath"] = locationString; } // Ensure that we always pass a path to the executable if (m_dynamicFactory->Engine() == ProcessorEngine::DSCv3) { winrt::hstring dscExecutablePathPropertyName = ToHString(PropertyName::DscExecutablePath); std::optional dscExecutablePath = m_dynamicFactory->GetFactoryMapValue(dscExecutablePathPropertyName); if (!dscExecutablePath) { dscExecutablePath = m_dynamicFactory->Lookup(ToHString(PropertyName::FoundDscExecutablePath)); } if (dscExecutablePath->empty()) { // This is backstop to prevent a case where dsc.exe not found. AICLI_LOG(Config, Error, << "Could not find dsc.exe, it must be provided by the user."); THROW_WIN32(ERROR_FILE_NOT_FOUND); } json["processorPath"] = Utility::ConvertToUTF8(dscExecutablePath.value()); } Json::StreamWriterBuilder writerBuilder; writerBuilder.settings_["indentation"] = "\t"; return Json::writeString(writerBuilder, json); } /// /// Creates a separate configuration set containing high integrity units and returns the serialized string value. /// /// Serialized string value. std::string SerializeHighIntegrityLevelSet() { ConfigurationSet highIntegritySet; highIntegritySet.SchemaVersion(m_configurationSet.SchemaVersion()); highIntegritySet.Metadata(m_configurationSet.Metadata()); highIntegritySet.Parameters(m_configurationSet.Parameters()); highIntegritySet.Variables(m_configurationSet.Variables()); std::vector highIntegrityUnits; auto units = m_configurationSet.Units(); for (auto unit : units) { if (unit.IsActive() && GetIntegrityLevelForUnit(unit) == Security::IntegrityLevel::High) { highIntegrityUnits.emplace_back(unit); } } highIntegritySet.Units(std::move(highIntegrityUnits)); // Serialize high integrity set and return output string. Streams::InMemoryRandomAccessStream memoryStream; highIntegritySet.Serialize(memoryStream); Streams::DataReader reader(memoryStream.GetInputStreamAt(0)); THROW_HR_IF(E_UNEXPECTED, memoryStream.Size() > std::numeric_limits::max()); uint32_t streamSize = (uint32_t)memoryStream.Size(); std::vector bytes; bytes.resize(streamSize); reader.LoadAsync(streamSize); reader.ReadBytes(bytes); reader.DetachStream(); memoryStream.Close(); return { bytes.begin(), bytes.end() }; } ProcessorMap::iterator CreateSetProcessorForIntegrityLevel(Security::IntegrityLevel integrityLevel) { IConfigurationSetProcessorFactory factory; IConfigurationSetProcessorFactory::Diagnostics_revoker factoryDiagnosticsEventRevoker; // If we got here, the only option is that the current integrity level is not High. if (integrityLevel == Security::IntegrityLevel::High) { bool useRunAs = true; #ifndef AICLI_DISABLE_TEST_HOOKS useRunAs = !m_enableTestMode; #endif factory = CreateOutOfProcessFactory(m_dynamicFactory->Engine(), useRunAs, SerializeSetProperties(), SerializeHighIntegrityLevelSet()); } else { THROW_WIN32(ERROR_NOT_SUPPORTED); } if (factory) { factory.MinimumLevel(m_dynamicFactory->MinimumLevel()); factoryDiagnosticsEventRevoker = factory.Diagnostics(winrt::auto_revoke, [weak_this{ get_weak() }](const IInspectable&, const IDiagnosticInformation& information) { if (auto strong_this{ weak_this.get() }) { strong_this->m_dynamicFactory->SendDiagnostics(information); } }); winrt::hstring propertyName = ConfigurationRemoting::ToHString(ConfigurationRemoting::PropertyName::DiagnosticTraceEnabled); if (auto propertyValue = m_dynamicFactory->GetFactoryMapValue(propertyName)) { factory.as>().Insert(propertyName, propertyValue.value()); } } return m_setProcessors.emplace(integrityLevel, DynamicProcessorInfo{ factory, factory.CreateSetProcessor(m_configurationSet), std::move(factoryDiagnosticsEventRevoker) }).first; } winrt::com_ptr m_dynamicFactory; Security::IntegrityLevel m_currentIntegrityLevel; Security::IntegrityLevel m_setIntegrityLevel; ProcessorMap m_setProcessors; ConfigurationSet m_configurationSet; std::once_flag m_createUnitSetProcessorsOnce; #ifndef AICLI_DISABLE_TEST_HOOKS bool m_enableTestMode = false; bool m_enableRestrictedIntegrityLevel = false; bool m_forceHighIntegrityLevelUnits = false; #endif }; DynamicFactory::DynamicFactory(ProcessorEngine processorEngine) { m_processorEngine = processorEngine; m_defaultRemoteFactory = CreateOutOfProcessFactory(processorEngine); if (m_defaultRemoteFactory) { m_factoryDiagnosticsEventRevoker = m_defaultRemoteFactory.Diagnostics(winrt::auto_revoke, [weak_this{ get_weak() }](const IInspectable&, const IDiagnosticInformation& information) { if (auto strong_this{ weak_this.get() }) { strong_this->SendDiagnostics(information); } }); } } IConfigurationSetProcessor DynamicFactory::CreateSetProcessor(const ConfigurationSet& configurationSet) { return winrt::make(get_strong(), m_defaultRemoteFactory.CreateSetProcessor(configurationSet), configurationSet); } winrt::event_token DynamicFactory::Diagnostics(const EventHandler& handler) { return m_diagnostics.add(handler); } void DynamicFactory::Diagnostics(const winrt::event_token& token) noexcept { m_diagnostics.remove(token); } DiagnosticLevel DynamicFactory::MinimumLevel() { return m_minimumLevel; } void DynamicFactory::MinimumLevel(DiagnosticLevel value) { m_minimumLevel = value; if (m_defaultRemoteFactory) { m_defaultRemoteFactory.MinimumLevel(value); } } HRESULT STDMETHODCALLTYPE DynamicFactory::SetLifetimeWatcher(IUnknown* watcher) { return WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher); } IConfigurationSetProcessorFactory& DynamicFactory::DefaultFactory() { return m_defaultRemoteFactory; } void DynamicFactory::SendDiagnostics(const IDiagnosticInformation& information) try { if (information.Level() >= m_minimumLevel) { std::lock_guard lock{ m_diagnosticsMutex }; m_diagnostics(*this, information); } } // While diagnostics can be important, a failure to send them should not cause additional issues. catch (...) {} } winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory CreateDynamicRuntimeFactory(ProcessorEngine processorEngine) { return winrt::make(processorEngine); } } ================================================ FILE: src/AppInstallerCLICore/ConfigurationSetProcessorFactoryRemoting.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/ConfigurationSetProcessorFactoryRemoting.h" #include #include #include #include #include #include #include #include using namespace winrt::Windows::Foundation; using namespace winrt::Microsoft::Management::Configuration; using namespace std::string_view_literals; namespace AppInstaller::CLI::ConfigurationRemoting { namespace { // The executable file name for the remote server process. constexpr std::wstring_view s_RemoteServerFileName = L"DotNet\\ConfigurationRemotingServer.exe"sv; constexpr std::wstring_view s_ProcessorEngine_PowerShell = L"pwsh"sv; constexpr std::wstring_view s_ProcessorEngine_DSCv3 = L"dscv3"sv; // The string used to divide the arguments sent to the remote server constexpr std::wstring_view s_ArgumentsDivider = L"\n~~~~~~\n"sv; // A helper with a convenient function that we use to receive the remote factory object. struct RemoteFactoryCallback : winrt::implements { RemoteFactoryCallback() { m_initEvent.create(); } ConfigurationUnit CreateConfigurationUnit() { THROW_HR(E_NOTIMPL); } ConfigurationSet CreateConfigurationSet() { THROW_HR(E_NOTIMPL); } IAsyncOperation CreateConfigurationSetProcessorFactoryAsync(winrt::hstring handler) { // TODO: Ensure calling process has same package identity std::wstringstream stringStream{ std::wstring{ static_cast(handler) } }; stringStream >> m_result; m_initEvent.SetEvent(); return nullptr; } ConfigurationProcessor CreateConfigurationProcessor(IConfigurationSetProcessorFactory factory) { // TODO: Ensure calling process has same package identity m_factory = factory; m_initEvent.SetEvent(); return nullptr; } bool IsConfigurationAvailable() { THROW_HR(E_NOTIMPL); } IAsyncActionWithProgress EnsureConfigurationAvailableAsync() { THROW_HR(E_NOTIMPL); } IConfigurationSetProcessorFactory Wait(HANDLE process) { HANDLE waitHandles[2]; waitHandles[0] = m_initEvent.get(); waitHandles[1] = process; for (;;) { // Wait up to 10 seconds for the server to complete initialization. // This time is fairly arbitrary, although it does correspond with the maximum time for a COM fast rundown. DWORD waitResult = WaitForMultipleObjects(ARRAYSIZE(waitHandles), waitHandles, FALSE, 10000); THROW_LAST_ERROR_IF(waitResult == WAIT_FAILED); // The init event was signaled. if (waitResult == WAIT_OBJECT_0) { break; } // Don't break things if the process is being debugged if (waitResult == WAIT_TIMEOUT && IsDebuggerPresent()) { continue; } // If the process exited, then try to use the exit code. DWORD processExitCode = 0; if (waitResult == (WAIT_OBJECT_0 + 1) && GetExitCodeProcess(process, &processExitCode) && FAILED(processExitCode)) { THROW_HR(static_cast(processExitCode)); } else { // The server timed out or didn't have a failed exit code. THROW_HR(E_FAIL); } } THROW_IF_FAILED(m_result); // Double-check the result THROW_HR_IF(E_POINTER, !m_factory); return m_factory; } private: IConfigurationSetProcessorFactory m_factory; HRESULT m_result = S_OK; wil::unique_event m_initEvent; }; // Represents a remote factory object that was created from a specific process. struct RemoteFactory : winrt::implements, winrt::cloaked>, WinRT::LifetimeWatcherBase { RemoteFactory(ProcessorEngine processorEngine, bool useRunAs, const std::string& properties, const std::string& restrictions) { AICLI_LOG(Config, Verbose, << "Launching process for configuration processing..."); // Create our callback and marshal it auto callback = winrt::make_self(); wil::com_ptr stream; THROW_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream)); THROW_IF_FAILED(CoMarshalInterface(stream.get(), winrt::guid_of(), reinterpret_cast<::IUnknown*>(winrt::get_abi(callback.as())), MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL)); ULARGE_INTEGER streamSize{}; THROW_IF_FAILED(stream->Seek({}, STREAM_SEEK_CUR, &streamSize)); ULONG bufferSize = static_cast(streamSize.QuadPart); std::vector buffer; buffer.resize(bufferSize); THROW_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr)); ULONG bytesRead = 0; THROW_IF_FAILED(stream->Read(&buffer[0], bufferSize, &bytesRead)); THROW_HR_IF(E_UNEXPECTED, bytesRead != bufferSize); std::wstring marshalledCallback = Utility::ConvertToUTF16(Utility::ConvertToHexString(buffer)); // Create the event that the remote process will wait on to keep the object alive. std::wstring completionEventName = Utility::CreateNewGuidNameWString(); m_completionEvent.create(wil::EventOptions::None, completionEventName.c_str()); auto completeEventIfFailureDuringConstruction = wil::scope_exit([&]() { m_completionEvent.SetEvent(); }); // This will be presented to the user so it must be formatted nicely. // Arguments are: // server.exe // // Optionally, we may also place additional data that limits what the server may do as: // ~~~~~~ // { "JSON properties" } // ~~~~~~ // YAML configuration set definition std::wostringstream argumentsStream; argumentsStream << s_RemoteServerFileName << L' ' << marshalledCallback << L' ' << completionEventName << L' ' << GetCurrentProcessId() << L' ' << ToString(processorEngine); if (!properties.empty() && !restrictions.empty()) { argumentsStream << L' ' << s_ArgumentsDivider << Utility::ConvertToUTF16(properties) << s_ArgumentsDivider << Utility::ConvertToUTF16(restrictions); } std::wstring arguments = argumentsStream.str(); std::filesystem::path serverPath = Runtime::GetPathTo(Runtime::PathName::SelfPackageRoot); serverPath /= s_RemoteServerFileName; std::wstring serverPathString = serverPath.wstring(); // Per documentation, the maximum length is 32767 *counting* the null. THROW_WIN32_IF(ERROR_BUFFER_OVERFLOW, serverPathString.length() > 32766); THROW_WIN32_IF(ERROR_BUFFER_OVERFLOW, arguments.length() > 32766); // Overflow safe since we verify that each of the individual strings is also small. // +1 for the space between the path and args. THROW_WIN32_IF(ERROR_BUFFER_OVERFLOW, serverPathString.length() + 1 + arguments.length() > 32766); SHELLEXECUTEINFOW execInfo = { 0 }; execInfo.cbSize = sizeof(execInfo); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI | SEE_MASK_NO_CONSOLE; execInfo.lpFile = serverPath.c_str(); execInfo.lpParameters = arguments.c_str(); execInfo.nShow = SW_HIDE; if (useRunAs) { execInfo.lpVerb = L"runas"; } THROW_LAST_ERROR_IF(!ShellExecuteExW(&execInfo) || !execInfo.hProcess); wil::unique_process_handle process{ execInfo.hProcess }; AICLI_LOG(Config, Verbose, << " Configuration remote PID is " << GetProcessId(process.get())); m_remoteFactory = callback->Wait(process.get()); AICLI_LOG(Config, Verbose, << "... configuration processing connection established."); completeEventIfFailureDuringConstruction.release(); } ~RemoteFactory() { m_completionEvent.SetEvent(); } IConfigurationSetProcessor CreateSetProcessor(const ConfigurationSet& configurationSet) { return m_remoteFactory.CreateSetProcessor(configurationSet); } winrt::event_token Diagnostics(const EventHandler& handler) { return m_remoteFactory.Diagnostics(handler); } void Diagnostics(const winrt::event_token& token) noexcept { m_remoteFactory.Diagnostics(token); } DiagnosticLevel MinimumLevel() { return m_remoteFactory.MinimumLevel(); } void MinimumLevel(DiagnosticLevel value) { m_remoteFactory.MinimumLevel(value); } Collections::IVectorView AdditionalModulePaths() const { return m_additionalModulePaths.GetView(); } void AdditionalModulePaths(const Collections::IVectorView& value) { // Extract all values from incoming view std::vector newModulePaths{ value.Size() }; value.GetMany(0, newModulePaths); // Create a copy for remote and set remote module paths std::vector newRemotePaths{ newModulePaths }; m_remoteAdditionalModulePaths = winrt::single_threaded_vector(std::move(newRemotePaths)); m_remoteFactory.as().AdditionalModulePaths(m_remoteAdditionalModulePaths.GetView()); // Store the updated module paths that we were given m_additionalModulePaths = winrt::single_threaded_vector(std::move(newModulePaths)); } SetProcessorFactory::PwshConfigurationProcessorPolicy Policy() const { return m_remoteFactory.as().Policy(); } void Policy(SetProcessorFactory::PwshConfigurationProcessorPolicy value) { m_remoteFactory.as().Policy(value); } SetProcessorFactory::PwshConfigurationProcessorLocation Location() const { return m_remoteFactory.as().Location(); } void Location(SetProcessorFactory::PwshConfigurationProcessorLocation value) { m_remoteFactory.as().Location(value); } winrt::hstring CustomLocation() const { return m_remoteFactory.as().CustomLocation(); } void CustomLocation(winrt::hstring value) { m_remoteFactory.as().CustomLocation(value); } // Implement a subset of IMap to enable property bag semantics uint32_t Size() { THROW_HR(E_NOTIMPL); } void Clear() { THROW_HR(E_NOTIMPL); } Collections::IMapView GetView() { THROW_HR(E_NOTIMPL); } bool HasKey(winrt::hstring) { THROW_HR(E_NOTIMPL); } void Remove(winrt::hstring) { THROW_HR(E_NOTIMPL); } bool Insert(winrt::hstring key, winrt::hstring value) { auto map = m_remoteFactory.try_as>(); return map ? map.Insert(key, value) : false; } winrt::hstring Lookup(winrt::hstring key) { auto map = m_remoteFactory.try_as>(); return map ? map.Lookup(key) : winrt::hstring{}; } HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher) { return WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher); } private: IConfigurationSetProcessorFactory m_remoteFactory; wil::unique_event m_completionEvent; Collections::IVector m_additionalModulePaths{ winrt::single_threaded_vector() }; Collections::IVector m_remoteAdditionalModulePaths{ winrt::single_threaded_vector() }; }; } IConfigurationSetProcessorFactory CreateOutOfProcessFactory(ProcessorEngine processorEngine, bool useRunAs, const std::string& properties, const std::string& restrictions) { return winrt::make(processorEngine, useRunAs, properties, restrictions); } ProcessorEngine DetermineProcessorEngine(ConfigurationSet set) { Utility::Version schemaVersion{ Utility::ConvertToUTF8(set.SchemaVersion()) }; if (schemaVersion <= Utility::Version{ "0.3" }) { ProcessorEngine result = ProcessorEngine::Unknown; std::wstring processorIdentifier = Utility::ToLower(set.Environment().ProcessorIdentifier()); if (processorIdentifier.empty() || processorIdentifier == s_ProcessorEngine_PowerShell) { // Default to PowerShell result = ProcessorEngine::PowerShell; } else if (processorIdentifier == s_ProcessorEngine_DSCv3) { result = ProcessorEngine::DSCv3; } else { AICLI_LOG(Config, Warning, << "Unknown processor: " << Utility::ConvertToUTF8(processorIdentifier)); } return result; } else { // Intentionally fail out here until a decision is made. THROW_HR(E_NOTIMPL); } } std::wstring_view ToString(ProcessorEngine value) { switch (value) { case ProcessorEngine::PowerShell: return s_ProcessorEngine_PowerShell; case ProcessorEngine::DSCv3: return s_ProcessorEngine_DSCv3; default: THROW_HR(E_UNEXPECTED); } } winrt::hstring ToHString(PropertyName name) { switch (name) { case PropertyName::DscExecutablePath: return L"DscExecutablePath"; case PropertyName::FoundDscExecutablePath: return L"FoundDscExecutablePath"; case PropertyName::DiagnosticTraceEnabled: return L"DiagnosticTraceEnabled"; case PropertyName::FindDscStateMachine: return L"FindDscStateMachine"; } THROW_HR(E_UNEXPECTED); } } HRESULT WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(HRESULT result, void* factory, LPWSTR staticsCallback, LPWSTR completionEventName, DWORD parentProcessId) try { { wil::com_ptr globalOptions; RETURN_IF_FAILED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&globalOptions))); RETURN_IF_FAILED(globalOptions->Set(COMGLB_RO_SETTINGS, COMGLB_FAST_RUNDOWN)); RETURN_IF_FAILED(globalOptions->Set(COMGLB_UNMARSHALING_POLICY, COMGLB_UNMARSHALING_POLICY_STRONG)); RETURN_IF_FAILED(globalOptions->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE_ANY)); } using namespace AppInstaller; using namespace AppInstaller::CLI::ConfigurationRemoting; RETURN_HR_IF(E_POINTER, !staticsCallback); auto callbackBytes = Utility::ParseFromHexString(Utility::ConvertToUTF8(staticsCallback)); RETURN_HR_IF(E_INVALIDARG, callbackBytes.size() > (1 << 15)); wil::com_ptr stream; RETURN_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream)); RETURN_IF_FAILED(stream->Write(&callbackBytes[0], static_cast(callbackBytes.size()), nullptr)); RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr)); wil::com_ptr<::IUnknown> output; RETURN_IF_FAILED(CoUnmarshalInterface(stream.get(), winrt::guid_of(), reinterpret_cast(&output))); IConfigurationStatics callback{ output.detach(), winrt::take_ownership_from_abi }; if (FAILED(result)) { std::ignore = callback.CreateConfigurationSetProcessorFactoryAsync(std::to_wstring(result)); } else { IConfigurationSetProcessorFactory factoryObject; winrt::copy_from_abi(factoryObject, factory); std::ignore = callback.CreateConfigurationProcessor(factoryObject); } // Wait until the caller releases the object (signalling the event) or the parent process exits wil::unique_event completionEvent; completionEvent.open(completionEventName); wil::unique_process_handle parentProcess{ OpenProcess(SYNCHRONIZE, FALSE, parentProcessId) }; HANDLE waitHandles[2]; waitHandles[0] = completionEvent.get(); waitHandles[1] = parentProcess.get(); std::ignore = WaitForMultipleObjects(ARRAYSIZE(waitHandles), waitHandles, FALSE, INFINITE); return S_OK; } CATCH_RETURN(); ================================================ FILE: src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationWingetDscModuleUnitValidation.h" #include "ExecutionContext.h" #include 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 { namespace { constexpr static std::string_view UnitType_WinGetSource = "WinGetSource"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; constexpr static std::string_view WellKnownSourceName_WinGetFont = "winget-font"sv; constexpr static std::string_view ValueSetKey_TreatAsArray = "treatAsArray"sv; constexpr static std::string_view WinGetSourceValueSetKey_Name = "name"sv; constexpr static std::string_view WinGetSourceValueSetKey_Type = "type"sv; constexpr static std::string_view WinGetSourceValueSetKey_Arg = "argument"sv; constexpr static std::string_view WinGetSourceValueSetKey_Ensure = "ensure"sv; constexpr static std::string_view WinGetSourceValueSetKey_Ensure_Present = "present"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; std::string Arg; bool Present = true; bool Empty() { return Name.empty() && Arg.empty() && Type.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; } WinGetSource ParseWinGetSourceFromSettings(const ValueSet& settings) { WinGetSource result; // Iterate through the value set as Powershell variables are case-insensitive. for (auto const& settingsPair : settings) { auto settingsKey = Utility::ConvertToUTF8(settingsPair.Key()); if (Utility::CaseInsensitiveEquals(WinGetSourceValueSetKey_Name, settingsKey)) { result.Name = GetPropertyValueAsString(settingsPair.Value()); } else if (Utility::CaseInsensitiveEquals(WinGetSourceValueSetKey_Type, settingsKey)) { result.Type = GetPropertyValueAsString(settingsPair.Value()); } else if (Utility::CaseInsensitiveEquals(WinGetSourceValueSetKey_Arg, settingsKey)) { result.Arg = GetPropertyValueAsString(settingsPair.Value()); } else if (Utility::CaseInsensitiveEquals(WinGetSourceValueSetKey_Ensure, settingsKey)) { result.Present = Utility::CaseInsensitiveEquals(WinGetSourceValueSetKey_Ensure_Present, GetPropertyValueAsString(settingsPair.Value())); } } return result; } bool IsWellKnownSourceName(std::string_view sourceName) { return Utility::CaseInsensitiveEquals(WellKnownSourceName_WinGet, sourceName) || Utility::CaseInsensitiveEquals(WellKnownSourceName_MSStore, sourceName) || Utility::CaseInsensitiveEquals(WellKnownSourceName_WinGetFont, sourceName); } bool ValidateWellKnownSource(const WinGetSource& source) { static std::vector wellKnownSourceDetails = { Repository::Source{ Repository::WellKnownSource::WinGet }.GetDetails(), Repository::Source{ Repository::WellKnownSource::MicrosoftStore }.GetDetails(), Repository::Source{ Repository::WellKnownSource::WinGetFont }.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(WinGetPackageValueSetKey_Id, settingsKey)) { result.Id = GetPropertyValueAsString(settingsPair.Value()); } else if (Utility::CaseInsensitiveEquals(WinGetPackageValueSetKey_Version, settingsKey)) { result.Version = GetPropertyValueAsString(settingsPair.Value()); } else if (Utility::CaseInsensitiveEquals(WinGetPackageValueSetKey_Source, settingsKey)) { result.Source = GetPropertyValueAsString(settingsPair.Value()); } else if (Utility::CaseInsensitiveEquals(WinGetPackageValueSetKey_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_WinGetSource, unitType)) { auto source = ParseWinGetSourceFromSettings(unit.Settings()); // Validate basic semantics. if (source.Name.empty()) { AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Name"); context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetSource }, "Name"_liv) << std::endl; foundIssues = true; } if (source.Arg.empty() && source.Present) { AICLI_LOG(Config, Error, << "WinGetSource unit missing required arg: Argument"); context.Reporter.Error() << Resource::String::WinGetResourceUnitMissingRequiredArg(Utility::LocIndView{ UnitType_WinGetSource }, "Argument"_liv) << std::endl; foundIssues = true; } // Validate well known source or process 3rd party source. if (IsWellKnownSourceName(source.Name)) { 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 if (unitIntent == ConfigurationUnitIntent::Apply) { // 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)) { 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, << "WinGetPackage unit both UseLatest and Version declared. Package: " << package.Id); context.Reporter.Warn() << 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, 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 { 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()) { // 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; } } } } // Validate package is found and version available. try { 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 { source.SetCaller("winget-cli-configuration-unit-module-validation"); 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); 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()) { std::shared_ptr availablePackage = searchResult.Matches.at(0).Package->GetAvailable().at(0); auto versionKeys = availablePackage->GetVersionKeys(); 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); 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; } } return !foundIssues; } } ================================================ FILE: src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace winrt::Microsoft::Management::Configuration { struct ConfigurationUnit; } namespace AppInstaller::CLI::Execution { struct Context; } namespace AppInstaller::CLI::Configuration { using namespace std::string_view_literals; struct WingetDscModuleUnitValidator { bool ValidateConfigurationSetUnit(AppInstaller::CLI::Execution::Context& context, const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit); std::string_view ModuleName() { return "Microsoft.WinGet.DSC"sv; }; private: std::map m_dependenciesSourceAndUnitIdMap; }; } ================================================ FILE: src/AppInstallerCLICore/ConfigureExportCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigureExportCommand.h" #include "Workflows/ConfigurationFlow.h" #include "Workflows/MSStoreInstallerHandler.h" #include "ConfigurationCommon.h" using namespace AppInstaller::CLI::Workflow; namespace AppInstaller::CLI { std::vector ConfigureExportCommand::GetArguments() const { return { Argument{ Execution::Args::Type::OutputFile, Resource::String::OutputFileArgumentDescription, true }, Argument{ Execution::Args::Type::ConfigurationExportPackageId, Resource::String::ConfigureExportPackageId }, Argument{ Execution::Args::Type::ConfigurationExportModule, Resource::String::ConfigureExportModule }, Argument{ Execution::Args::Type::ConfigurationExportResource, Resource::String::ConfigureExportResource }, Argument{ Execution::Args::Type::ConfigurationModulePath, Resource::String::ConfigurationModulePath }, Argument{ Execution::Args::Type::ConfigurationProcessorPath, Resource::String::ConfigurationProcessorPath, ArgumentType::Standard, Argument::Visibility::Help }, Argument{ Execution::Args::Type::Source, Resource::String::ExportSourceArgumentDescription, ArgumentType::Standard }, Argument{ Execution::Args::Type::IncludeVersions, Resource::String::ExportIncludeVersionsArgumentDescription, ArgumentType::Flag }, Argument{ Execution::Args::Type::ConfigurationExportAll, Resource::String::ConfigureExportAll, ArgumentType::Flag }, Argument::ForType(Execution::Args::Type::AcceptSourceAgreements), }; } Resource::LocString ConfigureExportCommand::ShortDescription() const { return { Resource::String::ConfigureExportCommandShortDescription }; } Resource::LocString ConfigureExportCommand::LongDescription() const { return { Resource::String::ConfigureExportCommandLongDescription }; } Utility::LocIndView ConfigureExportCommand::HelpLink() const { return "https://aka.ms/winget-command-configure#export"_liv; } void ConfigureExportCommand::ExecuteInternal(Execution::Context& context) const { context << VerifyIsFullPackage << CreateConfigurationProcessorWithoutFactory << CreateOrOpenConfigurationSet{ "0.3", context.Args.Contains(Execution::Args::Type::ConfigurationExportAll) } << CreateConfigurationProcessor << PopulateConfigurationSetForExport << WriteConfigFile; } void ConfigureExportCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const { Configuration::ValidateCommonArguments(execArgs); if (!execArgs.Contains(Execution::Args::Type::ConfigurationExportModule, Execution::Args::Type::ConfigurationExportResource) && !execArgs.Contains(Execution::Args::Type::ConfigurationExportPackageId) && !execArgs.Contains(Execution::Args::Type::ConfigurationExportAll)) { throw CommandException(Resource::String::ConfigureExportArgumentRequiredError); } if (execArgs.Contains(Execution::Args::Type::ConfigurationExportAll) && (execArgs.Contains(Execution::Args::Type::ConfigurationExportPackageId) || execArgs.Contains(Execution::Args::Type::ConfigurationExportModule) || execArgs.Contains(Execution::Args::Type::ConfigurationExportResource))) { throw CommandException(Resource::String::ConfigureExportArgumentConflictWithAllError); } } } ================================================ FILE: src/AppInstallerCLICore/ConfigureExportCommand.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" namespace AppInstaller::CLI { struct ConfigureExportCommand final : public Command { ConfigureExportCommand(std::string_view parent) : Command("export", parent) {} std::vector GetArguments() const override; Resource::LocString ShortDescription() const override; Resource::LocString LongDescription() const override; Utility::LocIndView HelpLink() const override; protected: void ExecuteInternal(Execution::Context& context) const override; void ValidateArgumentsInternal(Execution::Args& execArgs) const override; }; } ================================================ FILE: src/AppInstallerCLICore/ContextOrchestrator.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ExecutionContext.h" #include "ContextOrchestrator.h" #include "COMContext.h" #include "Commands/COMCommand.h" #include "Public/ShutdownMonitoring.h" #include "winget/UserSettings.h" #include namespace AppInstaller::CLI::Execution { namespace { // Operation command queue used by install, uninstall and repair commands. constexpr static std::string_view OperationCommandQueueName = "operation"sv; // Callback function used by worker threads in the queue. // context must be a pointer to a queue item. void CALLBACK OrchestratorQueueWorkCallback(PTP_CALLBACK_INSTANCE, PVOID context, PTP_WORK) { auto queueItem = reinterpret_cast(context); auto queue = queueItem->GetCurrentQueue(); if (queue) { queue->RunItem(queueItem->GetId()); } } // Get command queue name based on command name. std::string_view GetCommandQueueName(std::string_view commandName) { if (commandName == COMInstallCommand::CommandName || commandName == COMUninstallCommand::CommandName || commandName == COMRepairCommand::CommandName) { return OperationCommandQueueName; } return commandName; } } ContextOrchestrator& ContextOrchestrator::Instance() { static ContextOrchestrator s_instance; return s_instance; } ContextOrchestrator::ContextOrchestrator() : ContextOrchestrator(std::thread::hardware_concurrency()) {} ContextOrchestrator::ContextOrchestrator(unsigned int hardwareConcurrency) { ProgressCallback progress; m_installingWriteableSource = Repository::Source(Repository::PredefinedSource::Installing); m_installingWriteableSource.Open(progress); // Decide how many threads to use for each command. // We always allow only one install at a time. // For download, if we can find the number of supported concurrent threads, // use that as the maximum (up to 3); otherwise use a single thread. const UINT32 maxDownloadThreads = 3; const UINT32 operationThreads = 1; const UINT32 downloadThreads = std::min(hardwareConcurrency > 1 ? hardwareConcurrency - 1 : 1, maxDownloadThreads); AddCommandQueue(COMDownloadCommand::CommandName, downloadThreads); AddCommandQueue(OperationCommandQueueName, operationThreads); } void ContextOrchestrator::AddCommandQueue(std::string_view commandName, UINT32 allowedThreads) { std::lock_guard lockQueue{ m_queueLock }; m_commandQueues.emplace(commandName, std::make_unique(*this, commandName, allowedThreads)); } _Requires_lock_held_(m_queueLock) std::shared_ptr ContextOrchestrator::FindById(const OrchestratorQueueItemId& comparisonQueueItemId) { for (const auto& queue : m_commandQueues) { auto item = queue.second->FindById(comparisonQueueItemId); if (item) { return item; } } return {}; } void ContextOrchestrator::EnqueueAndRunItem(const std::shared_ptr& item) { std::lock_guard lockQueue{ m_queueLock }; if (item->IsOnFirstCommand()) { // Directly error on attempting to enqueue first time THROW_HR_IF(ToHRESULT(m_disabledReason), !m_enabled); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING), FindById(item->GetId())); // Log the beginning of the item item->GetContext().GetThreadGlobals().GetTelemetryLogger().LogCommand(item->GetItemCommandName()); } else if (!m_enabled) { // On subsequent command enqueues, cancel and complete the item item->GetContext().Cancel(m_disabledReason, true); item->HandleItemCompletion(*this); } std::string commandQueueName{ GetCommandQueueName(item->GetNextCommand().Name()) }; m_commandQueues.at(commandQueueName)->EnqueueAndRunItem(item); } void ContextOrchestrator::RemoveItemInState(const OrchestratorQueueItem& item, OrchestratorQueueItemState state) { std::lock_guard lockQueue{ m_queueLock }; for (const auto& queue : m_commandQueues) { if (queue.second->RemoveItemInState(item, state, true)) { return; } } } void ContextOrchestrator::CancelQueueItem(const OrchestratorQueueItem& item) { // Always cancel the item, even if it isn't running yet, to get the terminationHR set correctly. item.GetContext().Cancel(CancelReason::Abort, true); RemoveItemInState(item, OrchestratorQueueItemState::Queued); } std::shared_ptr ContextOrchestrator::GetQueueItem(const OrchestratorQueueItemId& queueItemId) { std::lock_guard lock{ m_queueLock }; return FindById(queueItemId); } void ContextOrchestrator::AddItemManifestToInstallingSource(const OrchestratorQueueItem& queueItem) { if (queueItem.IsApplicableForInstallingSource()) { const auto& manifest = queueItem.GetContext().Get(); m_installingWriteableSource.AddPackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); } } void ContextOrchestrator::RemoveItemManifestFromInstallingSource(const OrchestratorQueueItem& queueItem) { if (queueItem.IsApplicableForInstallingSource()) { const auto& manifest = queueItem.GetContext().Get(); m_installingWriteableSource.RemovePackageVersion(manifest, std::filesystem::path{ manifest.Id + '.' + manifest.Version }); } } void ContextOrchestrator::RegisterForShutdownSynchronization() { static std::once_flag registerComponentOnceFlag; std::call_once(registerComponentOnceFlag, [&]() { using namespace ShutdownMonitoring; ServerShutdownSynchronization::ComponentSystem component; component.BlockNewWork = StaticDisable; component.BeginShutdown = StaticCancelQueuedItems; component.Wait = StaticWaitForRunningItems; ServerShutdownSynchronization::AddComponent(component); }); } void ContextOrchestrator::StaticDisable(CancelReason reason) { Instance().Disable(reason); } void ContextOrchestrator::StaticCancelQueuedItems(CancelReason reason) { Instance().CancelQueuedItems(reason); } void ContextOrchestrator::StaticWaitForRunningItems() { Instance().WaitForRunningItems(); } void ContextOrchestrator::Disable(CancelReason reason) { std::lock_guard lock{ m_queueLock }; m_enabled = false; m_disabledReason = reason; } void ContextOrchestrator::CancelQueuedItems(CancelReason reason) { std::lock_guard lock{ m_queueLock }; for (const auto& queue : m_commandQueues) { queue.second->CancelAllItems(reason); } } void ContextOrchestrator::WaitForRunningItems() { std::lock_guard lock{ m_queueLock }; for (const auto& queue : m_commandQueues) { queue.second->WaitForEmptyQueue(); } } bool ContextOrchestrator::WaitForRunningItems(DWORD timeoutMilliseconds) { std::lock_guard lock{ m_queueLock }; for (const auto& queue : m_commandQueues) { if (!queue.second->WaitForEmptyQueue(timeoutMilliseconds)) { return false; } } return true; } std::string ContextOrchestrator::GetStatusString() { std::ostringstream stream; std::lock_guard lock{ m_queueLock }; if (!m_enabled) { stream << "Disabled due to " << ToIntegral(m_disabledReason) << std::endl; } for (const auto& queue : m_commandQueues) { stream << queue.second->GetStatusString(); } return stream.str(); } _Requires_lock_held_(m_itemLock) std::deque>::iterator OrchestratorQueue::FindIteratorById(const OrchestratorQueueItemId& comparisonQueueItemId) { return std::find_if(m_queueItems.begin(), m_queueItems.end(), [&comparisonQueueItemId](const std::shared_ptr& item) {return (item->GetId().IsSame(comparisonQueueItemId)); }); } _Requires_lock_held_(m_itemLock) std::shared_ptr OrchestratorQueue::FindById(const OrchestratorQueueItemId& comparisonQueueItemId) { auto itr = FindIteratorById(comparisonQueueItemId); if (itr != m_queueItems.end()) { return *itr; } return {}; } void OrchestratorQueue::EnqueueItem(const std::shared_ptr& item) { { std::lock_guard lockQueue{ m_itemLock }; m_queueItems.push_back(item); m_queueEmpty.ResetEvent(); } // Add the package to the Installing source so that it can be queried using the Source interface. // Only do this the first time the item is queued. if (item->IsOnFirstCommand()) { try { m_orchestrator.AddItemManifestToInstallingSource(*item); } catch (...) { std::lock_guard lockQueue{ m_itemLock }; auto itr = FindIteratorById(item->GetId()); if (itr != m_queueItems.end()) { m_queueItems.erase(itr); if (m_queueItems.empty()) { m_queueEmpty.SetEvent(); } } throw; } } { std::lock_guard lockQueue{ m_itemLock }; item->SetState(OrchestratorQueueItemState::Queued); } } OrchestratorQueue::OrchestratorQueue(ContextOrchestrator& orchestrator, std::string_view commandName, UINT32 allowedThreads) : m_orchestrator(orchestrator), m_commandName(commandName), m_allowedThreads(allowedThreads) { m_threadPool.reset(CreateThreadpool(nullptr)); THROW_LAST_ERROR_IF_NULL(m_threadPool); m_threadPoolCleanupGroup.reset(CreateThreadpoolCleanupGroup()); THROW_LAST_ERROR_IF_NULL(m_threadPoolCleanupGroup); InitializeThreadpoolEnvironment(&m_threadPoolCallbackEnviron); SetThreadpoolCallbackPool(&m_threadPoolCallbackEnviron, m_threadPool.get()); SetThreadpoolCallbackCleanupGroup(&m_threadPoolCallbackEnviron, m_threadPoolCleanupGroup.get(), nullptr); SetThreadpoolThreadMaximum(m_threadPool.get(), m_allowedThreads); THROW_LAST_ERROR_IF(!SetThreadpoolThreadMinimum(m_threadPool.get(), 1)); } OrchestratorQueue::~OrchestratorQueue() { CloseThreadpoolCleanupGroupMembers(m_threadPoolCleanupGroup.get(), false, nullptr); } void OrchestratorQueue::EnqueueAndRunItem(const std::shared_ptr& item) { EnqueueItem(item); item->SetCurrentQueue(this); auto work = CreateThreadpoolWork(OrchestratorQueueWorkCallback, item.get(), &m_threadPoolCallbackEnviron); SubmitThreadpoolWork(work); } void OrchestratorQueue::RunItem(const OrchestratorQueueItemId& itemId) { try { std::shared_ptr item; bool isCancelled = false; // Try to find the item in the queue. { std::lock_guard lockQueue{ m_itemLock }; item = FindById(itemId); if (!item) { // Item should be in the queue; this shouldn't happen. return; } // Only run if the item is queued and not cancelled. if (item->GetState() == OrchestratorQueueItemState::Queued) { // Mark it as running so that it cannot be cancelled by other threads. item->SetState(OrchestratorQueueItemState::Running); } else if (item->GetState() == OrchestratorQueueItemState::Cancelled) { isCancelled = true; } } if (isCancelled) { // Do this separate from above block as the Remove function needs to manage the lock. RemoveItemInState(*item, OrchestratorQueueItemState::Cancelled, true); } // Get the item's command and execute it. HRESULT exceptionHR = S_OK; try { std::unique_ptr command = item->PopNextCommand(); std::unique_ptr setThreadGlobalsToPreviousState = item->GetContext().SetForCurrentThread(); command->ValidateArguments(item->GetContext().Args); item->GetContext().EnableSignalTerminationHandler(); ::AppInstaller::CLI::ExecuteWithoutLoggingSuccess(item->GetContext(), command.get()); } WINGET_CATCH_STORE(exceptionHR, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); if (FAILED(exceptionHR)) { // Set the termination hr directly from any exception that escaped so that the context always // has the result of the operation no matter how it failed. item->GetContext().SetTerminationHR(exceptionHR); } item->GetContext().EnableSignalTerminationHandler(false); if (FAILED(item->GetContext().GetTerminationHR()) || item->IsComplete()) { if (SUCCEEDED(item->GetContext().GetTerminationHR())) { item->GetContext().GetThreadGlobals().GetTelemetryLogger().LogCommandSuccess(item->GetItemCommandName()); } RemoveItemInState(*item, OrchestratorQueueItemState::Running, true); } else { // Remove item from this queue and add it to the queue for the next command. RemoveItemInState(*item, OrchestratorQueueItemState::Running, false); m_orchestrator.EnqueueAndRunItem(item); } } catch (...) { } } void OrchestratorQueue::CancelAllItems(CancelReason reason) { std::lock_guard lockQueue{ m_itemLock }; for (auto itr = m_queueItems.begin(); itr != m_queueItems.end(); itr++) { auto& item = *itr; item->GetContext().Cancel(reason, true); // This mimics ContextOrchestrator::CancelQueueItem, which speeds up the process of cancelling queued items if (item->GetState() == OrchestratorQueueItemState::Queued) { item->SetState(OrchestratorQueueItemState::Cancelled); item->HandleItemCompletion(m_orchestrator); } } } void OrchestratorQueue::WaitForEmptyQueue() { m_queueEmpty.wait(); } bool OrchestratorQueue::WaitForEmptyQueue(DWORD timeoutMilliseconds) { return m_queueEmpty.wait(timeoutMilliseconds); } std::string OrchestratorQueue::GetStatusString() { std::ostringstream stream; stream << m_commandName << '[' << m_allowedThreads << "]\n"; std::map stateCounts; stateCounts[OrchestratorQueueItemState::NotQueued] = 0; stateCounts[OrchestratorQueueItemState::Queued] = 0; stateCounts[OrchestratorQueueItemState::Running] = 0; stateCounts[OrchestratorQueueItemState::Cancelled] = 0; { std::lock_guard lock{ m_itemLock }; for (const auto& item : m_queueItems) { stateCounts[item->GetState()] += 1; } } for (const auto& stateCount : stateCounts) { stream << " " << ToString(stateCount.first) << " : " << stateCount.second << std::endl; } return stream.str(); } bool OrchestratorQueue::RemoveItemInState(const OrchestratorQueueItem& item, OrchestratorQueueItemState state, bool isGlobalRemove) { // OrchestratorQueueItemState::Running items should only be removed by the thread that ran the item. // Queued items can be removed by any thread. // NotQueued items should not be removed since, if found in the queue, they are in the process of being queued by another thread. bool foundItem = false; { std::lock_guard lockQueue{ m_itemLock }; // Look for the item. It's ok if the item is not found since multiple listeners may try to remove the same item. auto itr = FindIteratorById(item.GetId()); if (itr != m_queueItems.end() && (*itr)->GetState() == state) { foundItem = true; // The item must only be removed from the queue by the thread that runs // it, because the callback uses it. If any other thread tries to remove // it, we simply mark it as cancelled. if (state == OrchestratorQueueItemState::Running || state == OrchestratorQueueItemState::Cancelled) { (*itr)->SetCurrentQueue(nullptr); m_queueItems.erase(itr); if (m_queueItems.empty()) { m_queueEmpty.SetEvent(); } } else if (state == OrchestratorQueueItemState::Queued) { (*itr)->SetState(OrchestratorQueueItemState::Cancelled); } } } if (foundItem && isGlobalRemove) { item.HandleItemCompletion(m_orchestrator); } return foundItem; } bool OrchestratorQueueItemId::IsSame(const OrchestratorQueueItemId& comparedId) const { return ((GetPackageId() == comparedId.GetPackageId()) && (GetSourceId() == comparedId.GetSourceId())); } std::string_view OrchestratorQueueItem::GetItemCommandName() const { // The goal is that these should match the winget.exe commands for easy correlation. switch (m_operationType) { case PackageOperationType::Search: return "root:search"sv; case PackageOperationType::Install: return "root:install"sv; case PackageOperationType::Upgrade: return "root:upgrade"sv; case PackageOperationType::Uninstall: return "root:uninstall"sv; case PackageOperationType::Download: return "root:download"sv; case PackageOperationType::Repair: return "root:repair"sv; default: return "unknown"; } } void OrchestratorQueueItem::HandleItemCompletion(ContextOrchestrator& orchestrator) const { orchestrator.RemoveItemManifestFromInstallingSource(*this); GetCompletedEvent().SetEvent(); } std::unique_ptr OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context, bool isUpgrade) { std::unique_ptr item = std::make_unique(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), isUpgrade ? PackageOperationType::Upgrade : PackageOperationType::Install); item->AddCommand(std::make_unique<::AppInstaller::CLI::COMDownloadCommand>(RootCommand::CommandName)); item->AddCommand(std::make_unique<::AppInstaller::CLI::COMInstallCommand>(RootCommand::CommandName)); return item; } std::unique_ptr OrchestratorQueueItemFactory::CreateItemForUninstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context) { std::unique_ptr item = std::make_unique(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), PackageOperationType::Uninstall); item->AddCommand(std::make_unique<::AppInstaller::CLI::COMUninstallCommand>(RootCommand::CommandName)); return item; } std::unique_ptr OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring packageId, std::wstring sourceId, std::unique_ptr context) { std::unique_ptr item = std::make_unique(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), PackageOperationType::Search); return item; } std::unique_ptr OrchestratorQueueItemFactory::CreateItemForDownload(std::wstring packageId, std::wstring sourceId, std::unique_ptr context) { std::unique_ptr item = std::make_unique(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), PackageOperationType::Download); item->AddCommand(std::make_unique<::AppInstaller::CLI::COMDownloadCommand>(RootCommand::CommandName)); return item; } std::unique_ptr OrchestratorQueueItemFactory::CreateItemForRepair(std::wstring packageId, std::wstring sourceId, std::unique_ptr context) { std::unique_ptr item = std::make_unique(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), PackageOperationType::Repair); item->AddCommand(std::make_unique<::AppInstaller::CLI::COMRepairCommand>(RootCommand::CommandName)); return item; } std::string_view ToString(OrchestratorQueueItemState state) { switch (state) { case OrchestratorQueueItemState::NotQueued: return "NotQueued"; case OrchestratorQueueItemState::Queued: return "Queued"; case OrchestratorQueueItemState::Running: return "Running"; case OrchestratorQueueItemState::Cancelled: return "Cancelled"; default: return "Unknown"; } } } ================================================ FILE: src/AppInstallerCLICore/ContextOrchestrator.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "ExecutionReporter.h" #include "ExecutionArgs.h" #include "ExecutionContextData.h" #include "CompletionData.h" #include "Command.h" #include "COMContext.h" #include #include #include namespace AppInstaller::CLI::Execution { enum class OrchestratorQueueItemState { // Created but not yet queued NotQueued, // Queued and waiting to be run Queued, // Running in the thread pool Running, // Cancelled before it was run; will be deleted when we try to run it Cancelled }; std::string_view ToString(OrchestratorQueueItemState state); struct OrchestratorQueueItemId { OrchestratorQueueItemId(std::wstring packageId, std::wstring sourceId) : m_packageId(std::move(packageId)), m_sourceId(std::move(sourceId)) {} std::wstring_view GetPackageId() const { return m_packageId; } std::wstring_view GetSourceId() const { return m_sourceId; } bool IsSame(const OrchestratorQueueItemId& comparisonQueueItemId) const; private: std::wstring m_packageId; std::wstring m_sourceId; }; struct OrchestratorQueue; enum class PackageOperationType { Search, Install, Upgrade, Uninstall, Download, Repair, }; struct ContextOrchestrator; struct OrchestratorQueueItem { OrchestratorQueueItem(OrchestratorQueueItemId id, std::unique_ptr context, PackageOperationType operationType) : m_id(std::move(id)), m_context(std::move(context)), m_operationType(operationType) {} OrchestratorQueueItemState GetState() const { return m_state; } void SetState(OrchestratorQueueItemState state) { m_state = state; } OrchestratorQueue* GetCurrentQueue() const { return m_currentQueue; } void SetCurrentQueue(OrchestratorQueue* currentQueue) { m_currentQueue = currentQueue; } COMContext& GetContext() const { return *m_context; } const wil::unique_event& GetCompletedEvent() const { return m_completedEvent; } const OrchestratorQueueItemId& GetId() const { return m_id; } void AddCommand(std::unique_ptr command) { m_commands.push_back(std::move(command)); } const Command& GetNextCommand() const { return *m_commands.front(); } std::unique_ptr PopNextCommand() { m_isOnFirstCommand = false; std::unique_ptr command = std::move(m_commands.front()); m_commands.pop_front(); return command; } bool IsOnFirstCommand() const { return m_isOnFirstCommand; } bool IsComplete() const { return m_commands.empty(); } bool IsApplicableForInstallingSource() const { return m_operationType == PackageOperationType::Install || m_operationType == PackageOperationType::Upgrade; } PackageOperationType GetPackageOperationType() const { return m_operationType; } std::string_view GetItemCommandName() const; void HandleItemCompletion(ContextOrchestrator& orchestrator) const; private: OrchestratorQueueItemState m_state = OrchestratorQueueItemState::NotQueued; std::unique_ptr m_context; wil::unique_event m_completedEvent{ wil::EventOptions::ManualReset }; OrchestratorQueueItemId m_id; std::deque> m_commands; bool m_isOnFirstCommand = true; OrchestratorQueue* m_currentQueue = nullptr; PackageOperationType m_operationType; }; struct OrchestratorQueueItemFactory { // Create queue item for install/upgrade static std::unique_ptr CreateItemForInstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context, bool isUpgrade); // Create queue item for uninstall static std::unique_ptr CreateItemForUninstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr context); // Create queue item for finding existing entry from the orchestrator queue static std::unique_ptr CreateItemForSearch(std::wstring packageId, std::wstring sourceId, std::unique_ptr context); // Create queue item for download static std::unique_ptr CreateItemForDownload(std::wstring packageId, std::wstring sourceId, std::unique_ptr context); // Create queue item for repair static std::unique_ptr CreateItemForRepair(std::wstring packageId, std::wstring sourceId, std::unique_ptr context); }; struct ContextOrchestrator { ContextOrchestrator(); ContextOrchestrator(unsigned int hardwareConcurrency); static ContextOrchestrator& Instance(); void EnqueueAndRunItem(const std::shared_ptr& queueItem); void CancelQueueItem(const OrchestratorQueueItem& item); std::shared_ptr GetQueueItem(const OrchestratorQueueItemId& queueItemId); void AddItemManifestToInstallingSource(const OrchestratorQueueItem& queueItem); void RemoveItemManifestFromInstallingSource(const OrchestratorQueueItem& queueItem); // Functions for ServerShutdownSynchronization::ComponentSystem registration static void RegisterForShutdownSynchronization(); static void StaticDisable(CancelReason reason); static void StaticCancelQueuedItems(CancelReason reason); static void StaticWaitForRunningItems(); void Disable(CancelReason reason); void CancelQueuedItems(CancelReason reason); void WaitForRunningItems(); // Waits for running items to complete; waits up to full time out in *each* queue. // Returns true to indicate all queues are empty before the timeout. bool WaitForRunningItems(DWORD timeoutMilliseconds); // Gets a string that represents the current state of the orchestrator. std::string GetStatusString(); private: std::mutex m_queueLock; bool m_enabled = true; CancelReason m_disabledReason = CancelReason::None; void AddCommandQueue(std::string_view commandName, UINT32 allowedThreads); void RemoveItemInState(const OrchestratorQueueItem& item, OrchestratorQueueItemState state); _Requires_lock_held_(m_queueLock) std::shared_ptr FindById(const OrchestratorQueueItemId& queueItemId); Repository::Source m_installingWriteableSource; std::map> m_commandQueues; }; // One of the queues used by the orchestrator. // All items in the queue execute the same command. // The queue allows multiple items to run at the same time, up to a limit. struct OrchestratorQueue { OrchestratorQueue(ContextOrchestrator& orchestrator, std::string_view commandName, UINT32 allowedThreads); ~OrchestratorQueue(); // Name of the command this queue can execute std::string_view CommandName() const { return m_commandName; } // Enqueues an item to be run when there are threads available. void EnqueueAndRunItem(const std::shared_ptr& item); // Removes an item by id, provided that it is in the given state. // Returns true if an item was removed. // The item can be removed globally from the orchestrator, or from just this queue. bool RemoveItemInState(const OrchestratorQueueItem& item, OrchestratorQueueItemState state, bool isGlobalRemove); // Finds an item by id, if it is in the queue. _Requires_lock_held_(m_itemLock) std::shared_ptr FindById(const OrchestratorQueueItemId& queueItemId); // Runs a single item from the queue. void RunItem(const OrchestratorQueueItemId& itemId); // Cancels and "removes" all items in the queue. void CancelAllItems(CancelReason reason); // Waits until the empty queue event is signaled. void WaitForEmptyQueue(); // Waits until the empty queue event is signaled. // Returns true to indicate the queue is empty before the timeout. bool WaitForEmptyQueue(DWORD timeoutMilliseconds); // Gets a string that represents the current state of the queue. std::string GetStatusString(); private: // Enqueues an item. void EnqueueItem(const std::shared_ptr& item); _Requires_lock_held_(m_itemLock) std::deque>::iterator FindIteratorById(const OrchestratorQueueItemId& comparisonQueueItemId); ContextOrchestrator& m_orchestrator; std::string_view m_commandName; // Number of threads allowed to run items in this queue. const UINT32 m_allowedThreads; // Thread pool for this queue, and associated objects. // All work items will be added to the callback environment, and the cleanup group // will manage their closing. // See https://docs.microsoft.com/windows/win32/procthread/using-the-thread-pool-functions TP_CALLBACK_ENVIRON m_threadPoolCallbackEnviron; wil::unique_any m_threadPool; wil::unique_any m_threadPoolCleanupGroup; std::mutex m_itemLock; std::deque> m_queueItems; wil::slim_event_manual_reset m_queueEmpty{ true }; }; } ================================================ FILE: src/AppInstallerCLICore/Core.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerCLICore.h" #include "Commands/RootCommand.h" #include "ExecutionContext.h" #include "Workflows/WorkflowBase.h" #include #include "Commands/InstallCommand.h" #include "COMContext.h" #include #include #include "Public/ShutdownMonitoring.h" #ifndef AICLI_DISABLE_TEST_HOOKS #include #endif using namespace winrt; using namespace winrt::Windows::Foundation; using namespace AppInstaller::CLI; using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI { namespace { // RAII class to restore the console output codepage. struct ConsoleOutputCPRestore { ConsoleOutputCPRestore(UINT cpToChangeTo) { m_previousCP = GetConsoleOutputCP(); LOG_LAST_ERROR_IF(!SetConsoleOutputCP(cpToChangeTo)); } ~ConsoleOutputCPRestore() { SetConsoleOutputCP(m_previousCP); } ConsoleOutputCPRestore(const ConsoleOutputCPRestore&) = delete; ConsoleOutputCPRestore& operator=(const ConsoleOutputCPRestore&) = delete; ConsoleOutputCPRestore(ConsoleOutputCPRestore&&) = delete; ConsoleOutputCPRestore& operator=(ConsoleOutputCPRestore&&) = delete; private: UINT m_previousCP = 0; }; void __CRTDECL abort_signal_handler(int) { #ifndef AICLI_DISABLE_TEST_HOOKS if (Settings::User().Get()) { Debugging::WriteMinidump(); } #endif std::_Exit(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); } wil::slim_event_manual_reset& GetMainWaitEvent() { static wil::slim_event_manual_reset s_mainWait; return s_mainWait; } void WaitOnMainWaitEvent() { GetMainWaitEvent().wait(5000); } void RegisterShutdownBlocker() { ShutdownMonitoring::ServerShutdownSynchronization::ComponentSystem main{}; main.Wait = WaitOnMainWaitEvent; ShutdownMonitoring::ServerShutdownSynchronization::AddComponent(main); } } int CoreMain(int argc, wchar_t const** argv) try { // This prevents the OS package management from terminating the CLI process before it has had a chance to gracefully exit. RegisterShutdownBlocker(); auto signalMainExit = wil::scope_exit([]() { GetMainWaitEvent().SetEvent(); }); std::signal(SIGABRT, abort_signal_handler); init_apartment(); #ifndef AICLI_DISABLE_TEST_HOOKS // We have to do this here so the auto minidump config initialization gets caught Logging::OutputDebugStringLogger::Add(); Logging::Log().SetEnabledChannels(Logging::Channel::All); Logging::Log().SetLevel(Logging::Level::Verbose); if (Settings::User().Get()) { Debugging::EnableSelfInitiatedMinidump(); } Logging::OutputDebugStringLogger::Remove(); #endif Logging::UseGlobalTelemetryLoggerActivityIdOnly(); Execution::Context context; auto previousThreadGlobals = context.SetForCurrentThread(); // Set up debug string logging during initialization Logging::OutputDebugStringLogger::Add(); Logging::Log().SetEnabledChannels(Logging::Channel::All); Logging::Log().SetLevel(Logging::Level::Verbose); Logging::Log().SetEnabledChannels(Settings::User().Get()); Logging::Log().SetLevel(Settings::User().Get()); Logging::FileLogger::Add(); Logging::OutputDebugStringLogger::Remove(); Logging::EnableWilFailureTelemetry(); // Set output to UTF8 ConsoleOutputCPRestore utf8CP(CP_UTF8); Logging::Telemetry().SetCaller("winget-cli"); Logging::Telemetry().LogStartup(); #ifndef AICLI_DISABLE_TEST_HOOKS if (!Settings::User().Get()) #endif { // Initiate the background cleanup of the log file location. Logging::FileLogger::BeginCleanup(); } context.EnableSignalTerminationHandler(); context << Workflow::ReportExecutionStage(Workflow::ExecutionStage::ParseArgs); // Convert incoming wide char args to UTF8 std::vector utf8Args; for (int i = 1; i < argc; ++i) { utf8Args.emplace_back(Utility::ConvertToUTF8(argv[i])); } AICLI_LOG(CLI, Info, << "WinGet invoked with arguments:" << [&]() { std::stringstream strstr; for (const auto& arg : utf8Args) { strstr << " '" << arg << '\''; } return strstr.str(); }()); Invocation invocation{ std::move(utf8Args) }; // The root command is our fallback in the event of very bad or very little input std::unique_ptr command = std::make_unique(); try { // Block CLI execution if WinGetCommandLineInterfaces is disabled by Policy if (!Settings::GroupPolicies().IsEnabled(Settings::TogglePolicy::Policy::WinGetCommandLineInterfaces)) { AICLI_LOG(CLI, Error, << "WinGet is disabled by group policy"); throw Settings::GroupPolicyException(Settings::TogglePolicy::Policy::WinGetCommandLineInterfaces); } std::unique_ptr subCommand = command->FindSubCommand(invocation); while (subCommand) { command = std::move(subCommand); subCommand = command->FindSubCommand(invocation); } Logging::Telemetry().LogCommand(command->FullName()); command->ParseArguments(invocation, context.Args); context.UpdateForArgs(); context.SetExecutingCommand(command.get()); command->ValidateArguments(context.Args); } // Exceptions specific to parsing the arguments of a command catch (const CommandException& ce) { command->OutputHelp(context.Reporter, &ce); AICLI_LOG(CLI, Error, << "Error encountered parsing command line: " << ce.Message()); return APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS; } catch (const Settings::GroupPolicyException& e) { // Report any action blocked by Group Policy. auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); AICLI_LOG(CLI, Error, << "Operation blocked by Group Policy: " << policy.RegValueName()); context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } catch (...) { return Workflow::HandleException(context, std::current_exception()); } return Execute(context, command); } // End of the line exceptions that are not ever expected. // Telemetry cannot be reliable beyond this point, so don't let these happen. catch (...) { return APPINSTALLER_CLI_ERROR_INTERNAL_ERROR; } void ServerInitialize() { #ifndef AICLI_DISABLE_TEST_HOOKS // We have to do this here so the auto minidump config initialization gets caught Logging::OutputDebugStringLogger::Add(); Logging::Log().SetEnabledChannels(Logging::Channel::All); Logging::Log().SetLevel(Logging::Level::Verbose); if (Settings::User().Get()) { Debugging::EnableSelfInitiatedMinidump(); } Logging::OutputDebugStringLogger::Remove(); #endif AppInstaller::CLI::Execution::COMContext::SetLoggers(); } void InProcInitialize() { #ifndef AICLI_DISABLE_TEST_HOOKS // We have to do this here so the auto minidump config initialization gets caught Logging::OutputDebugStringLogger::Add(); Logging::Log().SetEnabledChannels(Logging::Channel::All); Logging::Log().SetLevel(Logging::Level::Verbose); if (Settings::User().Get()) { Debugging::EnableSelfInitiatedMinidump(); } Logging::OutputDebugStringLogger::Remove(); #endif // Explicitly set default channel and level before user settings from PackageManagerSettings AppInstaller::CLI::Execution::COMContext::SetLoggers(AppInstaller::Logging::Channel::Defaults, AppInstaller::Logging::Level::Info); } } ================================================ FILE: src/AppInstallerCLICore/ExecutionArgs.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::CLI::Execution { struct Args { enum class Type : uint32_t { // Args to specify where to get app Query, // Query to be performed against index MultiQuery, // Like query, but can take multiple values Manifest, // Provide the app manifest directly // Query filtering criteria and query behavior Id, Name, Moniker, Tag, Command, Source, // Index source to be queried against Count, // Maximum query results Exact, // Exact match required // Manifest selection behavior after an app is found Version, Channel, // Install behavior Interactive, Silent, Locale, Log, CustomSwitches, // CustomSwitches args are args passed to the installer in addition to any defined in the manifest Override, // Override args are (and the only args) directly passed to installer InstallLocation, InstallScope, InstallArchitecture, InstallerArchitecture, InstallerType, HashOverride, // Ignore hash mismatches SkipDependencies, // Skip dependencies DependenciesOnly, // Install only dependencies, not the target package IgnoreLocalArchiveMalwareScan, // Ignore the local malware scan on archive files AcceptPackageAgreements, // Accept all license agreements for packages Rename, // Renames the file of the executable. Only applies to the portable installerType NoUpgrade, // Install flow should not try to convert to upgrade flow upon finding existing installed version AllowReboot, // Allows the reboot flow to proceed if applicable // Uninstall behavior Purge, // Removes all files and directories related to a package during an uninstall. Only applies to the portable installerType. Preserve, // Retains any files and directories created by the portable exe. ProductCode, // Uninstalls using the product code as the identifier. AllVersions, // Uninstall all versions of the package TargetVersion, // The specific version to target //Source Command SourceName, SourceType, SourceArg, ForceSourceReset, SourceExplicit, SourceTrustLevel, SourcePriority, SourceEditExplicit, //Hash Command HashFile, Msix, // Flag to indicate the input file is msix //Validate Command ValidateManifest, IgnoreWarnings, // Complete Command Word, CommandLine, Position, // Export Command IncludeVersions, // Import Command ImportFile, IgnoreUnavailable, IgnoreVersions, // Download Command DownloadDirectory, SkipMicrosoftStorePackageLicense, Platform, OSVersion, // Setting Command AdminSettingEnable, AdminSettingDisable, SettingName, SettingValue, // Upgrade command All, // Used in Update command to update all installed packages to latest IncludeUnknown, // Used in Upgrade command to allow upgrades of packages with unknown versions IncludePinned, // Used in Upgrade command to allow upgrades to pinned packages (only for pinning type of pins) UninstallPrevious, // Used in Upgrade command to override the default manifest behavior to UninstallPrevious // Show command ListVersions, // Used in Show command to list all available versions of an app // List Command Upgrade, // Used in List command to only show versions with upgrades ListDetails, // Pin command GatedVersion, // Differs from Version in that this supports wildcards BlockingPin, PinInstalled, // Error command ErrorInput, // Resume Command ResumeId, IgnoreResumeLimit, // Font Command Family, Details, // Stub package (extended features) ExtendedFeaturesEnable, ExtendedFeaturesDisable, // Configuration ConfigurationFile, ConfigurationAcceptWarning, ConfigurationSuppressPrologue, ConfigurationProcessorPath, ConfigurationModulePath, ConfigurationExportPackageId, ConfigurationExportModule, ConfigurationExportResource, ConfigurationExportAll, ConfigurationHistoryItem, ConfigurationHistoryRemove, ConfigurationStatusWatch, // DSCv3 resources DscResourceFunctionGet, DscResourceFunctionSet, DscResourceFunctionWhatIf, DscResourceFunctionTest, DscResourceFunctionDelete, DscResourceFunctionExport, DscResourceFunctionValidate, DscResourceFunctionResolve, DscResourceFunctionAdapter, DscResourceFunctionSchema, DscResourceFunctionManifest, // Common arguments NoVT, // Disable VirtualTerminal outputs RetroStyle, // Makes progress display as retro RainbowStyle, // Makes progress display as a rainbow NoProgress, // Disables progress bar and spinner Help, // Show command usage Info, // Show general info about WinGet VerboseLogs, // Increases winget logging level to verbose DisableInteractivity, // Disable interactive prompts Wait, // Prompts the user to press any key before exiting OpenLogs, // Opens the default logs directory after executing the command Force, // Forces the execution of the workflow with non security related issues OutputFile, Correlation, DependencySource, // Index source to be queried against for finding dependencies CustomHeader, // Optional Rest source header AcceptSourceAgreements, // Accept all source agreements AuthenticationMode, // Authentication mode (silent, silentPreferred or interactive) AuthenticationAccount, // Authentication account to be used // Network Behavior Proxy, // Set a proxy to use in this execution NoProxy, // Do not use the default proxy ToolVersion, // Used for demonstration purposes ExperimentalArg, // This should always be at the end Max }; template), bool> = true> bool Contains(T... arg) const { return (... && (m_parsedArgs.count(arg) != 0)); } const std::vector* GetArgs(Type arg) const { auto itr = m_parsedArgs.find(arg); return (itr == m_parsedArgs.end() ? nullptr : &(itr->second)); } std::string_view GetArg(Type arg) const { auto itr = m_parsedArgs.find(arg); if (itr == m_parsedArgs.end()) { return {}; } return itr->second[0]; } size_t GetCount(Type arg) const { auto args = GetArgs(arg); return (args ? args->size() : 0); } bool AddArg(Type arg) { return m_parsedArgs[arg].empty(); } void AddArg(Type arg, std::string value) { m_parsedArgs[arg].emplace_back(std::move(value)); } void AddArg(Type arg, std::string_view value) { m_parsedArgs[arg].emplace_back(value); } bool Empty() { return m_parsedArgs.empty(); } size_t GetArgsCount() const { return m_parsedArgs.size(); } std::vector GetTypes() const { std::vector types; for (auto const& i : m_parsedArgs) { types.emplace_back(i.first); } return types; } // If the user passes the same value multiple times inside a MultiQuery, operations will be repeated // Since there currently is not a way to include search options within a MultiQuery, processing duplicates // does not make sense within a single invocation void MakeMultiQueryContainUniqueValues() { auto itr = m_parsedArgs.find(Type::MultiQuery); // If there is not a value in MultiQuery, or there is only one value, it is presumed to be unique if (itr == m_parsedArgs.end() || itr->second.size() == 1) { return; } std::set querySet; std::vector& queryStrings = itr->second; queryStrings.erase(std::remove_if(queryStrings.begin(), queryStrings.end(), [&](const std::string value) { return !querySet.insert(value).second; }), queryStrings.end()); } // If we get a single value for multi-query, we remove the argument and add it back as a single query. // This way the rest of the code can assume that if there is a MultiQuery we will always have multiple values, // and if there is a single one it will be in the Query type. void MoveMultiQueryToSingleQueryIfNeeded() { auto itr = m_parsedArgs.find(Type::MultiQuery); if (itr != m_parsedArgs.end() && itr->second.size() == 1) { // A test ensures that commands don't have both Query and MultiQuery arguments, // so if we had a MultiQuery value, we can be sure there is no Query value m_parsedArgs[Type::Query].emplace_back(std::move(itr->second[0])); m_parsedArgs.erase(itr); } } private: std::map> m_parsedArgs; }; } ================================================ FILE: src/AppInstallerCLICore/ExecutionContext.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerRuntime.h" #include "Argument.h" #include "COMContext.h" #include "Command.h" #include "ExecutionContext.h" #include "Public/ShutdownMonitoring.h" #include #include #include #include using namespace AppInstaller::Checkpoints; namespace AppInstaller::CLI::Execution { using namespace Settings; namespace { bool ShouldRemoveCheckpointDatabase(HRESULT hr) { switch (hr) { case APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL: case APPINSTALLER_CLI_ERROR_RESUME_LIMIT_EXCEEDED: case APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH: return false; default: return true; } } } Context::~Context() { if (Settings::ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::Resume)) { if (m_checkpointManager && (!IsTerminated() || ShouldRemoveCheckpointDatabase(GetTerminationHR()))) { m_checkpointManager->CleanUpDatabase(); AppInstaller::Reboot::UnregisterRestartForWER(); } } if (m_disableSignalTerminationHandlerOnExit) { EnableSignalTerminationHandler(false); } } Context Context::CreateEmptyContext() { AppInstaller::ThreadLocalStorage::WingetThreadGlobals threadGlobals; return Context(Reporter, threadGlobals); } std::unique_ptr Context::CreateSubContext() { auto clone = std::make_unique(Reporter, *m_threadGlobals); clone->m_flags = m_flags; clone->m_executingCommand = m_executingCommand; // If the parent is hooked up to the CTRL signal, have the clone be as well if (m_disableSignalTerminationHandlerOnExit) { clone->EnableSignalTerminationHandler(); } CopyArgsToSubContext(clone.get()); CopyDataToSubContext(clone.get()); return clone; } void Context::CopyArgsToSubContext(Context* subContext) { auto argProperties = ArgumentCommon::GetFromExecArgs(Args); for (const auto& arg : argProperties) { if (WI_IsFlagSet(arg.TypeCategory, ArgTypeCategory::CopyFlagToSubContext)) { subContext->Args.AddArg(arg.Type); } else if (WI_IsFlagSet(arg.TypeCategory, ArgTypeCategory::CopyValueToSubContext)) { subContext->Args.AddArg(arg.Type, Args.GetArg(arg.Type)); } } } void Context::CopyDataToSubContext(Context* subContext) { #define COPY_DATA_IF_EXISTS(dataType) \ if (this->Contains(dataType)) \ { \ subContext->Add(this->Get()); \ } COPY_DATA_IF_EXISTS(Data::InstallerDownloadAuthenticators); } void Context::EnableSignalTerminationHandler(bool enabled) { ShutdownMonitoring::TerminationSignalHandler::EnableListener(enabled, this); m_disableSignalTerminationHandlerOnExit = enabled; } void Context::UpdateForArgs() { // Change logging level to Info if Verbose not requested if (Args.Contains(Args::Type::VerboseLogs)) { Logging::Log().SetLevel(Logging::Level::Verbose); } // Disable warnings if requested if (Args.Contains(Args::Type::IgnoreWarnings)) { Reporter.SetLevelMask(Reporter::Level::Warning, false); } // Set proxy if (Args.Contains(Args::Type::Proxy)) { Network().SetProxyUri(std::string{ Args.GetArg(Args::Type::Proxy) }); } else if (Args.Contains(Args::Type::NoProxy)) { Network().SetProxyUri(std::nullopt); } // Set visual style if (Args.Contains(Args::Type::NoProgress)) { Reporter.SetStyle(VisualStyle::Disabled); } else if (Args.Contains(Args::Type::NoVT)) { Reporter.SetStyle(VisualStyle::NoVT); } else if (Args.Contains(Args::Type::RetroStyle)) { Reporter.SetStyle(VisualStyle::Retro); } else if (Args.Contains(Args::Type::RainbowStyle)) { Reporter.SetStyle(VisualStyle::Rainbow); } else { Reporter.SetStyle(User().Get()); } } void Context::Terminate(HRESULT hr, std::string_view file, size_t line) { if (hr == APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED) { ++m_CtrlSignalCount; // Use a more recognizable error hr = E_ABORT; // If things aren't terminating fast enough for the user, they will probably press CTRL+C again. // In that case, we should forcibly terminate. // Unless we want to spin a separate thread for all work, we have to just exit here. if (m_CtrlSignalCount >= 2) { Reporter.CloseOutputStream(true); Logging::Telemetry().LogCommandTermination(hr, file, line); std::exit(hr); } } else if (hr == APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED) { AICLI_LOG(CLI, Info, << "Got app termination signal"); hr = E_ABORT; } Logging::Telemetry().LogCommandTermination(hr, file, line); if (!m_isTerminated) { SetTerminationHR(hr); } } void Context::SetTerminationHR(HRESULT hr) { m_terminationHR = hr; m_isTerminated = true; } void Context::Cancel(CancelReason reason, bool bypassUser) { HRESULT hr = ToHRESULT(reason); Terminate(hr); Reporter.CancelInProgressTask(bypassUser, reason); } void Context::SetExecutionStage(Workflow::ExecutionStage stage) { if (m_executionStage == stage) { return; } else if (m_executionStage > stage) { THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), "Reporting ExecutionStage to an earlier Stage: current[%d], new[%d]", ToIntegral(m_executionStage), ToIntegral(stage)); } m_executionStage = stage; GetThreadGlobals().GetTelemetryLogger().SetExecutionStage(static_cast(m_executionStage)); } AppInstaller::ThreadLocalStorage::WingetThreadGlobals& Context::GetThreadGlobals() { return *m_threadGlobals; } std::shared_ptr Context::GetSharedThreadGlobals() { return m_threadGlobals; } std::unique_ptr Context::SetForCurrentThread() { return m_threadGlobals->SetForCurrentThread(); } #ifndef AICLI_DISABLE_TEST_HOOKS bool Context::ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task) { return (m_shouldExecuteWorkflowTask ? m_shouldExecuteWorkflowTask(task) : true); } #endif void ContextEnumBasedVariantMapActionCallback(const void* map, Data data, EnumBasedVariantMapAction action) { switch (action) { case EnumBasedVariantMapAction::Add: AICLI_LOG(Workflow, Verbose, << "Setting data item: " << data); break; case EnumBasedVariantMapAction::Contains: AICLI_LOG(Workflow, Verbose, << "Checking data item: " << data); break; case EnumBasedVariantMapAction::Get: AICLI_LOG(Workflow, Verbose, << "Getting data item: " << data); break; } UNREFERENCED_PARAMETER(map); } std::string Context::GetResumeId() { return m_checkpointManager->GetResumeId(); } std::optional> Context::LoadCheckpoint(const std::string& resumeId) { m_checkpointManager = std::make_unique(resumeId); return m_checkpointManager->GetAutomaticCheckpoint(); } std::vector> Context::GetCheckpoints() { return m_checkpointManager->GetCheckpoints(); } void Context::Checkpoint(std::string_view checkpointName, std::vector contextData) { UNREFERENCED_PARAMETER(checkpointName); UNREFERENCED_PARAMETER(contextData); if (!m_checkpointManager) { m_checkpointManager = std::make_unique(); m_checkpointManager->CreateAutomaticCheckpoint(*this); // Register for restart only when we first call checkpoint to support restarting from an unexpected shutdown. AppInstaller::Reboot::RegisterRestartForWER("resume -g " + GetResumeId()); } // TODO: Capture context data for checkpoint. } } ================================================ FILE: src/AppInstallerCLICore/ExecutionContext.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winget/ThreadGlobals.h" #include "ExecutionReporter.h" #include "ExecutionArgs.h" #include "ExecutionContextData.h" #include "CompletionData.h" #include "CheckpointManager.h" #include #include #include #define WINGET_CATCH_RESULT_EXCEPTION_STORE(exceptionHR) catch (const wil::ResultException& re) { exceptionHR = re.GetErrorCode(); } #define WINGET_CATCH_HRESULT_EXCEPTION_STORE(exceptionHR) catch (const winrt::hresult_error& hre) { exceptionHR = hre.code(); } #define WINGET_CATCH_COMMAND_EXCEPTION_STORE(exceptionHR) catch (const ::AppInstaller::CLI::CommandException&) { exceptionHR = APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS; } #define WINGET_CATCH_POLICY_EXCEPTION_STORE(exceptionHR) catch (const ::AppInstaller::Settings::GroupPolicyException&) { exceptionHR = APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } #define WINGET_CATCH_STD_EXCEPTION_STORE(exceptionHR, genericHR) catch (const std::exception&) { exceptionHR = genericHR; } #define WINGET_CATCH_ALL_EXCEPTION_STORE(exceptionHR, genericHR) catch (...) { exceptionHR = genericHR; } #define WINGET_CATCH_STORE(exceptionHR, genericHR) \ WINGET_CATCH_RESULT_EXCEPTION_STORE(exceptionHR) \ WINGET_CATCH_HRESULT_EXCEPTION_STORE(exceptionHR) \ WINGET_CATCH_COMMAND_EXCEPTION_STORE(exceptionHR) \ WINGET_CATCH_POLICY_EXCEPTION_STORE(exceptionHR) \ WINGET_CATCH_STD_EXCEPTION_STORE(exceptionHR, genericHR) \ WINGET_CATCH_ALL_EXCEPTION_STORE(exceptionHR, genericHR) // Terminates the Context with some logging to indicate the location. // Also returns from the current function. #define AICLI_TERMINATE_CONTEXT_ARGS(_context_,_hr_,_ret_) \ do { \ _context_.Terminate(_hr_, __FILE__, __LINE__); \ return _ret_; \ } while(0,0) // Terminates the Context named 'context' with some logging to indicate the location. // Also returns from the current function. #define AICLI_TERMINATE_CONTEXT(_hr_) AICLI_TERMINATE_CONTEXT_ARGS(context,_hr_,) // Terminates the Context named 'context' with some logging to indicate the location. // Also returns the specified value from the current function. #define AICLI_TERMINATE_CONTEXT_RETURN(_hr_,_ret_) AICLI_TERMINATE_CONTEXT_ARGS(context,_hr_,_ret_) // Returns if the context is terminated. #define AICLI_RETURN_IF_TERMINATED(_context_) if ((_context_).IsTerminated()) { return; } namespace AppInstaller::CLI { struct Command; } namespace AppInstaller::CLI::Workflow { struct WorkflowTask; enum class ExecutionStage : uint32_t; } namespace AppInstaller::CLI::Execution { // bit masks used as Context flags enum class ContextFlag : int { None = 0x0, InstallerExecutionUseUpdate = 0x1, InstallerHashMatched = 0x2, InstallerTrusted = 0x4, // Allows a failure in a single source to generate a warning rather than an error. // TODO: Remove when the source interface is refactored. TreatSourceFailuresAsWarning = 0x8, ShowSearchResultsOnPartialFailure = 0x10, DisableInteractivity = 0x40, BypassIsStoreClientBlockedPolicyCheck = 0x80, InstallerDownloadOnly = 0x100, Resume = 0x200, RebootRequired = 0x400, RegisterResume = 0x800, InstallerExecutionUseRepair = 0x1000, }; DEFINE_ENUM_FLAG_OPERATORS(ContextFlag); // Callback to log data actions. void ContextEnumBasedVariantMapActionCallback(const void* map, Data data, EnumBasedVariantMapAction action); // The context within which all commands execute. // Contains input/output via Execution::Reporter and // arguments via Execution::Args. struct Context : EnumBasedVariantMap, ICancellable { Context() = default; Context(std::ostream& out, std::istream& in) : Reporter(out, in) {} // Constructor for creating a sub-context. Context(Execution::Reporter& reporter, ThreadLocalStorage::WingetThreadGlobals& threadGlobals) : Reporter(reporter, Execution::Reporter::clone_t{}), m_threadGlobals(std::make_shared(threadGlobals, ThreadLocalStorage::WingetThreadGlobals::create_sub_thread_globals_t{})) {} virtual ~Context(); // The path for console input/output for all functionality. Reporter Reporter; // The arguments given to execute with. Args Args; // Creates a empty context, inheriting Context CreateEmptyContext(); // Creates a child of this context. virtual std::unique_ptr CreateSubContext(); // Enables reception of CTRL signals and window messages. void EnableSignalTerminationHandler(bool enabled = true); // Applies changes based on the parsed args. void UpdateForArgs(); // Returns a value indicating whether the context is terminated. bool IsTerminated() const { return m_isTerminated; } // Resets the context to a nonterminated state. void ResetTermination() { m_terminationHR = S_OK; m_isTerminated = false; } // Gets the HRESULT reason for the termination. HRESULT GetTerminationHR() const { return m_terminationHR; } // Set the context to the terminated state. void Terminate(HRESULT hr, std::string_view file = {}, size_t line = {}); // Set the termination hr of the context. void SetTerminationHR(HRESULT hr); // Cancel the context; this terminates it as well as informing any in progress task to stop cooperatively. // Multiple attempts with CancelReason::CancelSignal may cause the process to simply exit. // The bypassUser indicates whether the user should be asked for cancellation (does not currently have any effect). void Cancel(CancelReason reason, bool bypassUser = false) override; // Gets context flags ContextFlag GetFlags() const { return m_flags; } // Set context flags void SetFlags(ContextFlag flags) { WI_SetAllFlags(m_flags, flags); } // Clear context flags void ClearFlags(ContextFlag flags) { WI_ClearAllFlags(m_flags, flags); } virtual void SetExecutionStage(Workflow::ExecutionStage stage); // Get Globals for Current Context ThreadLocalStorage::WingetThreadGlobals& GetThreadGlobals(); std::shared_ptr GetSharedThreadGlobals(); std::unique_ptr SetForCurrentThread(); // Gets the executing command AppInstaller::CLI::Command* GetExecutingCommand() { return m_executingCommand; } // Sets the executing command void SetExecutingCommand(AppInstaller::CLI::Command* command) { m_executingCommand = command; } #ifndef AICLI_DISABLE_TEST_HOOKS // Enable tests to override behavior bool ShouldExecuteWorkflowTask(const Workflow::WorkflowTask& task); #endif // Returns the resume id. std::string GetResumeId(); // Called by the resume command. Loads the checkpoint manager with the resume id and returns the automatic checkpoint. std::optional> LoadCheckpoint(const std::string& resumeId); // Returns data checkpoints in the order of latest checkpoint to earliest. std::vector> GetCheckpoints(); // Creates a checkpoint for the provided context data. void Checkpoint(std::string_view checkpointName, std::vector contextData); protected: // Copies the args that are also needed in a sub-context. E.g., silent void CopyArgsToSubContext(Context* subContext); // Copies the execution data that are also needed in a sub-context. E.g., shared installer download authenticator map void CopyDataToSubContext(Context* subContext); // Neither virtual functions nor member fields can be inside AICLI_DISABLE_TEST_HOOKS // or we could have ODR violations that lead to nasty bugs. So we will simply never // use this if AICLI_DISABLE_TEST_HOOKS is defined. std::function m_shouldExecuteWorkflowTask; private: DestructionToken m_disableSignalTerminationHandlerOnExit = false; bool m_isTerminated = false; HRESULT m_terminationHR = S_OK; size_t m_CtrlSignalCount = 0; ContextFlag m_flags = ContextFlag::None; Workflow::ExecutionStage m_executionStage = Workflow::ExecutionStage::Initial; std::shared_ptr m_threadGlobals = std::make_shared(); AppInstaller::CLI::Command* m_executingCommand = nullptr; std::unique_ptr m_checkpointManager; }; } ================================================ FILE: src/AppInstallerCLICore/ExecutionContextData.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include "CompletionData.h" #include "PackageCollection.h" #include "PortableInstaller.h" #include "Workflows/WorkflowBase.h" #include "ConfigurationContext.h" #include #include #include #include #include #include namespace AppInstaller::CLI::Execution { // Names a piece of data stored in the context by a workflow step. // Must start at 0 to enable direct access to variant in Context. // Max must be last and unused. enum class Data : size_t { Source, SearchRequest, // Only set for multiple installs SearchResult, SourceList, Package, Manifest, PackageVersion, Installer, DownloadHashInfo, InstallerPath, LogPath, InstallerArgs, OperationReturnCode, CompletionData, InstalledPackageVersion, UninstallString, PackageFamilyNames, ProductCodes, // On export: A collection of packages to be exported to a file // On import: A collection of packages read from a file PackageCollection, // When installing multiple packages at once (upgrade all, import, install with multiple args, dependencies): // A collection of sub-contexts, each of which handles the installation of a single package. PackageSubContexts, // On import: Sources for the imported packages Sources, ARPCorrelationData, CorrelatedAppsAndFeaturesEntries, Dependencies, DependencySource, AllowedArchitectures, AllowUnknownScope, PortableInstaller, PinningData, Pins, ConfigurationContext, DownloadDirectory, ModifyPath, RepairString, MsixDigests, InstallerDownloadAuthenticators, Max }; struct Context; namespace details { template struct DataMapping { // value_t type specifies the type of this data }; template <> struct DataMapping { using value_t = Repository::Source; }; template <> struct DataMapping { using value_t = Repository::SearchRequest; }; template <> struct DataMapping { using value_t = Repository::SearchResult; }; template <> struct DataMapping { using value_t = std::vector; }; template <> struct DataMapping { using value_t = std::shared_ptr; }; template <> struct DataMapping { using value_t = Manifest::Manifest; }; template <> struct DataMapping { using value_t = std::shared_ptr; }; template <> struct DataMapping { using value_t = std::optional; }; template <> struct DataMapping { using value_t = std::pair, Utility::DownloadResult>; }; template <> struct DataMapping { using value_t = std::filesystem::path; }; template <> struct DataMapping { using value_t = std::filesystem::path; }; template <> struct DataMapping { using value_t = std::string; }; template <> struct DataMapping { using value_t = DWORD; }; template <> struct DataMapping { using value_t = CLI::CompletionData; }; template <> struct DataMapping { using value_t = std::shared_ptr; }; template <> struct DataMapping { using value_t = std::string; }; template <> struct DataMapping { using value_t = std::vector; }; template <> struct DataMapping { using value_t = std::vector; }; template <> struct DataMapping { using value_t = CLI::PackageCollection; }; template <> struct DataMapping { using value_t = std::vector>; }; template <> struct DataMapping { using value_t = std::vector; }; template <> struct DataMapping { using value_t = Repository::Correlation::ARPCorrelationData; }; template <> struct DataMapping { using value_t = std::vector; }; template <> struct DataMapping { using value_t = Manifest::DependencyList; }; template <> struct DataMapping { using value_t = Repository::Source; }; template <> struct DataMapping { using value_t = std::vector; }; template <> struct DataMapping { using value_t = bool; }; template <> struct DataMapping { using value_t = CLI::Portable::PortableInstaller; }; template <> struct DataMapping { using value_t = Pinning::PinningData; }; template <> struct DataMapping { using value_t = std::vector; }; template <> struct DataMapping { using value_t = ConfigurationContext; }; template <> struct DataMapping { using value_t = std::filesystem::path; }; template<> struct DataMapping { using value_t = std::string; }; template<> struct DataMapping { using value_t = std::string; }; template<> struct DataMapping { // The pair is { URL, Digest } using value_t = std::vector>; }; template<> struct DataMapping { // The authenticator map shared with sub contexts using value_t = std::shared_ptr>; }; } } ================================================ FILE: src/AppInstallerCLICore/ExecutionProgress.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ExecutionProgress.h" #include "VTSupport.h" #include "AppInstallerRuntime.h" #include "Sixel.h" using namespace AppInstaller::Settings; using namespace AppInstaller::CLI::VirtualTerminal; using namespace std::string_view_literals; namespace AppInstaller::CLI::Execution { namespace { static constexpr size_t s_ProgressBarCellWidth = 30; struct BytesFormatData { uint64_t PowerOfTwo; std::string_view Name; }; BytesFormatData s_bytesFormatData[] = { // Multi-terabyte installers should be fairly rare for the foreseeable future... { 40, "TB"sv }, { 30, "GB"sv }, { 20, "MB"sv }, { 10, "KB"sv }, { 0, "B"sv }, }; const BytesFormatData& GetFormatForSize(uint64_t bytes) { for (const auto& format : s_bytesFormatData) { if (bytes > (1ull << format.PowerOfTwo)) { return format; } } // Just to make the compiler happy, return the last in the list if we get here. return s_bytesFormatData[ARRAYSIZE(s_bytesFormatData) - 1]; } void OutputBytes(BaseStream& out, uint64_t byteCount) { const BytesFormatData& bfd = GetFormatForSize(byteCount); uint64_t integralAmount = byteCount >> bfd.PowerOfTwo; uint64_t remainder = byteCount & ((1ull << bfd.PowerOfTwo) - 1); size_t remainderDigits = 0; if (integralAmount < 10) { remainder *= 100; remainderDigits = 2; } else if (integralAmount < 100) { remainder *= 10; remainderDigits = 1; } else if (integralAmount < 1000) { // Put an extra space to ensure a consistent 4 chars per numeric output out << ' '; } out << integralAmount; if (remainderDigits) { remainder = remainder >> bfd.PowerOfTwo; out << '.' << std::setw(remainderDigits) << std::setfill('0') << remainder; } out << ' ' << bfd.Name; } void SetColor(BaseStream& out, const TextFormat::Color& color, bool foregroundOnly) { out << TextFormat::Foreground::Extended(color); if (!foregroundOnly) { constexpr uint8_t divisor = 3; auto reduced = color; reduced.R /= divisor; reduced.G /= divisor; reduced.B /= divisor; out << TextFormat::Background::Extended(reduced); } } void SetRainbowColor(BaseStream& out, size_t i, size_t max, bool foregroundOnly) { TextFormat::Color rainbow[] = { { 0xff, 0x00, 0x00 }, { 0xff, 0x77, 0x00 }, { 0xff, 0xdd, 0x00 }, { 0x00, 0xff, 0x00 }, { 0x00, 0x00, 0xff }, { 0x8a, 0x2b, 0xe2 }, { 0xc7, 0x7d, 0xf3 }, }; double target = (static_cast(i) / (max - 1)) * (ARRAYSIZE(rainbow) - 1); size_t lower = static_cast(std::floor(target)); const auto& lowerVal = rainbow[lower]; TextFormat::Color result; if (lower == (ARRAYSIZE(rainbow) - 1)) { result = lowerVal; } else { double upperContribution = target - lower; #define AICLI_AVERAGE(v) static_cast(((lowerVal.v * (1.0 - upperContribution)) + (rainbow[lower + 1].v * upperContribution))) result = { AICLI_AVERAGE(R), AICLI_AVERAGE(G), AICLI_AVERAGE(B) }; } SetColor(out, result, foregroundOnly); } } // Shared functionality for progress visualizers. struct ProgressVisualizerBase { ProgressVisualizerBase(BaseStream& stream, bool enableVT) : m_out(stream), m_enableVT(enableVT) {} void SetMessage(std::string_view message) { std::atomic_store(&m_message, std::make_shared(message)); } std::shared_ptr Message() { return std::atomic_load(&m_message); } protected: BaseStream& m_out; bool VT_Enabled() const { return m_enableVT; } void ClearLine() { if (VT_Enabled()) { m_out << TextModification::EraseLineEntirely << '\r'; } else { m_out << '\r' << std::string(GetConsoleWidth(), ' ') << '\r'; } } private: bool m_enableVT = false; std::shared_ptr m_message; }; // Shared functionality for progress visualizers. struct CharacterProgressVisualizerBase : public ProgressVisualizerBase { CharacterProgressVisualizerBase(BaseStream& stream, bool enableVT, VisualStyle style) : ProgressVisualizerBase(stream, enableVT && style != AppInstaller::Settings::VisualStyle::NoVT), m_style(style) {} protected: Settings::VisualStyle m_style = AppInstaller::Settings::VisualStyle::Accent; // Applies the selected visual style. void ApplyStyle(size_t i, size_t max, bool foregroundOnly) { if (!VT_Enabled()) { // Either no style set or VT disabled return; } switch (m_style) { case VisualStyle::Retro: m_out << TextFormat::Default; break; case VisualStyle::Accent: SetColor(m_out, TextFormat::Color::GetAccentColor(), foregroundOnly); break; case VisualStyle::Rainbow: SetRainbowColor(m_out, i, max, foregroundOnly); break; default: LOG_HR(E_UNEXPECTED); } } }; // Displays an indefinite spinner via a character. struct CharacterIndefiniteSpinner : public CharacterProgressVisualizerBase, public IIndefiniteSpinner { CharacterIndefiniteSpinner(BaseStream& stream, bool enableVT, VisualStyle style) : CharacterProgressVisualizerBase(stream, enableVT, style) {} void ShowSpinner() override { if (!m_spinnerJob.valid() && !m_spinnerRunning && !m_canceled) { m_spinnerRunning = true; m_spinnerJob = std::async(std::launch::async, &CharacterIndefiniteSpinner::ShowSpinnerInternal, this); } } void StopSpinner() override { if (!m_canceled && m_spinnerJob.valid() && m_spinnerRunning) { m_canceled = true; m_spinnerJob.get(); } } void SetMessage(std::string_view message) override { ProgressVisualizerBase::SetMessage(message); } std::shared_ptr Message() override { return ProgressVisualizerBase::Message(); } private: std::atomic m_canceled = false; std::atomic m_spinnerRunning = false; std::future m_spinnerJob; void ShowSpinnerInternal() { char spinnerChars[] = { '-', '\\', '|', '/' }; // First wait for a small amount of time to enable a fast task to skip // showing anything, or a progress task to skip straight to progress. Sleep(100); if (!m_canceled) { if (VT_Enabled()) { // Additional VT-based progress reporting, for terminals that support it m_out << Progress::Construct(Progress::State::Indeterminate); } // Indent two spaces for the spinner, but three here so that we can overwrite it in the loop. std::string_view indent = " "; std::shared_ptr message = ProgressVisualizerBase::Message(); size_t messageLength = message ? Utility::UTF8ColumnWidth(*message) : 0; for (size_t i = 0; !m_canceled; ++i) { constexpr size_t repetitionCount = 20; ApplyStyle(i % repetitionCount, repetitionCount, true); m_out << '\r' << indent << spinnerChars[i % ARRAYSIZE(spinnerChars)]; m_out.RestoreDefault(); std::shared_ptr newMessage = ProgressVisualizerBase::Message(); std::string eraser; if (newMessage) { size_t newLength = Utility::UTF8ColumnWidth(*newMessage); if (newLength < messageLength) { eraser = std::string(messageLength - newLength, ' '); } message = newMessage; messageLength = newLength; } m_out << ' ' << (message ? *message : std::string{}) << eraser << std::flush; Sleep(250); } ClearLine(); if (VT_Enabled()) { m_out << Progress::Construct(Progress::State::None); } } m_canceled = false; m_spinnerRunning = false; } }; // Displays progress via character output. class CharacterProgressBar : public CharacterProgressVisualizerBase, public IProgressBar { public: CharacterProgressBar(BaseStream& stream, bool enableVT, VisualStyle style) : CharacterProgressVisualizerBase(stream, enableVT, style) {} void ShowProgress(uint64_t current, uint64_t maximum, ProgressType type) override { if (current < m_lastCurrent) { ClearLine(); } // TODO: Progress bar does not currently use message if (VT_Enabled()) { ShowProgressWithVT(current, maximum, type); } else { ShowProgressNoVT(current, maximum, type); } m_lastCurrent = current; m_isVisible = true; } void EndProgress(bool hideProgressWhenDone) override { if (m_isVisible) { if (hideProgressWhenDone) { ClearLine(); } else { m_out << std::endl; } if (VT_Enabled()) { // We always clear the VT-based progress bar, even if hideProgressWhenDone is false // since it would be confusing for users if progress continues to be shown after winget exits // (it is typically not automatically cleared by terminals on process exit) m_out << Progress::Construct(Progress::State::None); } m_isVisible = false; } } private: std::atomic m_isVisible = false; uint64_t m_lastCurrent = 0; void ShowProgressNoVT(uint64_t current, uint64_t maximum, ProgressType type) { m_out << "\r "; if (maximum) { const char* const blockOn = u8"\x2588"; const char* const blockOff = u8"\x2592"; constexpr size_t blockWidth = 30; double percentage = static_cast(current) / maximum; size_t blocksOn = static_cast(std::floor(percentage * blockWidth)); for (size_t i = 0; i < blocksOn; ++i) { m_out << blockOn; } for (size_t i = 0; i < blockWidth - blocksOn; ++i) { m_out << blockOff; } m_out << " "; switch (type) { case AppInstaller::ProgressType::Bytes: OutputBytes(m_out, current); m_out << " / "; OutputBytes(m_out, maximum); break; case AppInstaller::ProgressType::Percent: default: m_out << static_cast(percentage * 100) << '%'; break; } } else { switch (type) { case AppInstaller::ProgressType::Bytes: OutputBytes(m_out, current); break; case AppInstaller::ProgressType::Percent: m_out << current << '%'; break; default: m_out << current << " unknowns"; break; } } } void ShowProgressWithVT(uint64_t current, uint64_t maximum, ProgressType type) { m_out << TextFormat::Default; m_out << "\r "; if (maximum) { const char* const blocks[] = { u8" ", // block off u8"\x258F", // block 1/8 u8"\x258E", // block 2/8 u8"\x258D", // block 3/8 u8"\x258C", // block 4/8 u8"\x258B", // block 5/8 u8"\x258A", // block 6/8 u8"\x2589", // block 7/8 u8"\x2588" // block on }; const char* const blockOn = blocks[8]; const char* const blockOff = blocks[0]; constexpr size_t blockWidth = s_ProgressBarCellWidth; double percentage = static_cast(current) / maximum; size_t blocksOn = static_cast(std::floor(percentage * blockWidth)); size_t partialBlockIndex = static_cast((percentage * blockWidth - blocksOn) * 8); TextFormat::Color accent = TextFormat::Color::GetAccentColor(); for (size_t i = 0; i < blockWidth; ++i) { ApplyStyle(i, blockWidth, false); if (i < blocksOn) { m_out << blockOn; } else if (i == blocksOn) { m_out << blocks[partialBlockIndex]; } else { m_out << blockOff; } } m_out << TextFormat::Default; m_out << " "; switch (type) { case AppInstaller::ProgressType::Bytes: OutputBytes(m_out, current); m_out << " / "; OutputBytes(m_out, maximum); break; case AppInstaller::ProgressType::Percent: default: m_out << static_cast(percentage * 100) << '%'; break; } // Additional VT-based progress reporting, for terminals that support it m_out << Progress::Construct(Progress::State::Normal, static_cast(percentage * 100)); } else { switch (type) { case AppInstaller::ProgressType::Bytes: OutputBytes(m_out, current); break; case AppInstaller::ProgressType::Percent: m_out << current << '%'; break; default: m_out << current << " unknowns"; break; } } } }; // Displays an indefinite spinner via a sixel. struct SixelIndefiniteSpinner : public ProgressVisualizerBase, public IIndefiniteSpinner { SixelIndefiniteSpinner(BaseStream& stream, bool enableVT) : ProgressVisualizerBase(stream, enableVT) { Sixel::RenderControls& renderControls = m_compositor.Controls(); renderControls.RenderSizeInCells(2, 1); // Create palette from full image std::filesystem::path imageAssetsRoot = Runtime::GetPathTo(Runtime::PathName::ImageAssets); THROW_WIN32_IF(ERROR_FILE_NOT_FOUND, imageAssetsRoot.empty()); // This image matches the target pixel size. If changing the target size, choose the most appropriate image. Sixel::ImageSource wingetIcon{ imageAssetsRoot / "AppList.targetsize-20.png" }; wingetIcon.Resize(renderControls); Sixel::Palette palette = wingetIcon.CreatePalette(renderControls); m_folder = Sixel::ImageSource{ imageAssetsRoot / "progress-sixel/folders_only.png" }; m_arrow = Sixel::ImageSource{ imageAssetsRoot / "progress-sixel/arrow_only.png" }; m_folder.Resize(renderControls); m_folder.ApplyPalette(palette); Sixel::RenderControls arrowControls = renderControls; arrowControls.InterpolationMode = Sixel::InterpolationMode::Linear; m_arrow.Resize(arrowControls); m_arrow.ApplyPalette(palette); m_compositor.Palette(std::move(palette)); m_compositor.AddView(m_arrow.Copy()); m_compositor.AddView(m_folder.Copy()); } void ShowSpinner() override { if (!m_spinnerJob.valid() && !m_spinnerRunning && !m_canceled) { m_spinnerRunning = true; m_spinnerJob = std::async(std::launch::async, &SixelIndefiniteSpinner::ShowSpinnerInternal, this); } } void StopSpinner() override { if (!m_canceled && m_spinnerJob.valid() && m_spinnerRunning) { m_canceled = true; m_spinnerJob.get(); } } void SetMessage(std::string_view message) override { ProgressVisualizerBase::SetMessage(message); } std::shared_ptr Message() override { return ProgressVisualizerBase::Message(); } private: std::atomic m_canceled = false; std::atomic m_spinnerRunning = false; std::future m_spinnerJob; Sixel::ImageSource m_folder; Sixel::ImageSource m_arrow; Sixel::Compositor m_compositor; void ShowSpinnerInternal() { // First wait for a small amount of time to enable a fast task to skip // showing anything, or a progress task to skip straight to progress. Sleep(100); if (!m_canceled) { // Additional VT-based progress reporting, for terminals that support it m_out << Progress::Construct(Progress::State::Indeterminate); // Indent two spaces for the spinner, but three here so that we can overwrite it in the loop. std::string_view indent = " "; std::shared_ptr message = ProgressVisualizerBase::Message(); size_t messageLength = message ? Utility::UTF8ColumnWidth(*message) : 0; UINT imageHeight = m_compositor.Controls().PixelHeight; for (size_t i = 0; !m_canceled; ++i) { m_out << '\r' << indent; // Move arrow down one pixel each time m_compositor[0].Translate(0, i % imageHeight, true); m_compositor.RenderTo(m_out); message = ProgressVisualizerBase::Message(); size_t newLength = (message ? Utility::UTF8ColumnWidth(*message) : 0); std::string eraser; if (newLength < messageLength) { eraser = std::string(messageLength - newLength, ' '); } messageLength = newLength; m_out << VirtualTerminal::Cursor::Position::Forward(3) << (message ? *message : std::string{}) << eraser << std::flush; Sleep(100); } ClearLine(); m_out << Progress::Construct(Progress::State::None); } m_canceled = false; m_spinnerRunning = false; } }; // Displays progress with a sixel image. class SixelProgressBar : public ProgressVisualizerBase, public IProgressBar { public: SixelProgressBar(BaseStream& stream, bool enableVT) : ProgressVisualizerBase(stream, enableVT) { static constexpr UINT s_colorsForBelt = 20; Sixel::RenderControls imageRenderControls; imageRenderControls.RenderSizeInCells(2, 1); // This image matches the target pixel size. If changing the target size, choose the most appropriate image. std::filesystem::path imageAssetsRoot = Runtime::GetPathTo(Runtime::PathName::ImageAssets); THROW_WIN32_IF(ERROR_FILE_NOT_FOUND, imageAssetsRoot.empty()); m_icon = Sixel::ImageSource{ imageAssetsRoot / "AppList.targetsize-20.png" }; m_icon.Resize(imageRenderControls); imageRenderControls.ColorCount = Sixel::Palette::MaximumColorCount - s_colorsForBelt; Sixel::Palette iconPalette = m_icon.CreatePalette(imageRenderControls); // TODO: Move to real location m_belt = Sixel::ImageSource{ imageAssetsRoot / "progress-sixel/conveyor.png" }; m_belt.Resize(imageRenderControls); imageRenderControls.ColorCount = s_colorsForBelt; imageRenderControls.InterpolationMode = Sixel::InterpolationMode::Linear; Sixel::Palette beltPalette = m_belt.CreatePalette(imageRenderControls); Sixel::Palette combinedPalette{ iconPalette, beltPalette }; m_icon.ApplyPalette(combinedPalette); m_belt.ApplyPalette(combinedPalette); m_compositor.Palette(std::move(combinedPalette)); m_compositor.AddView(m_icon.Copy()); m_compositor.AddView(m_belt.Copy()); m_compositor.Controls().TransparencyEnabled = false; m_compositor.Controls().RenderSizeInCells(s_ProgressBarCellWidth, 1); } void ShowProgress(uint64_t current, uint64_t maximum, ProgressType type) override { if (current < m_lastCurrent) { ClearLine(); } m_out << TextFormat::Default; m_out << "\r "; if (maximum) { double percentage = static_cast(current) / maximum; // Translate icon so that its leading edge is the progress line INT translation = static_cast((percentage * m_compositor.Controls().PixelWidth) - m_compositor[0].Width()); m_compositor[0].Translate(translation, 0, false); m_compositor[1].Translate(translation, 0, true); m_compositor.RenderTo(m_out); m_out << VirtualTerminal::Cursor::Position::Forward(s_ProgressBarCellWidth + 2); switch (type) { case AppInstaller::ProgressType::Bytes: OutputBytes(m_out, current); m_out << " / "; OutputBytes(m_out, maximum); break; case AppInstaller::ProgressType::Percent: default: m_out << static_cast(percentage * 100) << '%'; break; } // Additional VT-based progress reporting, for terminals that support it m_out << Progress::Construct(Progress::State::Normal, static_cast(percentage * 100)); } else { switch (type) { case AppInstaller::ProgressType::Bytes: OutputBytes(m_out, current); break; case AppInstaller::ProgressType::Percent: m_out << current << '%'; break; default: m_out << current << " unknowns"; break; } } m_lastCurrent = current; m_isVisible = true; } void EndProgress(bool hideProgressWhenDone) override { if (m_isVisible) { if (hideProgressWhenDone) { ClearLine(); } else { m_out << std::endl; } if (VT_Enabled()) { // We always clear the VT-based progress bar, even if hideProgressWhenDone is false // since it would be confusing for users if progress continues to be shown after winget exits // (it is typically not automatically cleared by terminals on process exit) m_out << Progress::Construct(Progress::State::None); } m_isVisible = false; } } private: std::atomic m_isVisible = false; uint64_t m_lastCurrent = 0; Sixel::ImageSource m_icon; Sixel::ImageSource m_belt; Sixel::Compositor m_compositor; }; std::unique_ptr IIndefiniteSpinner::CreateForStyle(BaseStream& stream, bool enableVT, VisualStyle style, const std::function& sixelSupported) { std::unique_ptr result; switch (style) { case VisualStyle::NoVT: case VisualStyle::Retro: case VisualStyle::Accent: case VisualStyle::Rainbow: result = std::make_unique(stream, enableVT, style); break; case VisualStyle::Sixel: if (sixelSupported()) { try { result = std::make_unique(stream, enableVT); } CATCH_LOG(); } if (!result) { result = std::make_unique(stream, enableVT, VisualStyle::Accent); } break; case VisualStyle::Disabled: break; default: THROW_HR(E_NOTIMPL); } return result; } std::unique_ptr IProgressBar::CreateForStyle(BaseStream& stream, bool enableVT, VisualStyle style, const std::function& sixelSupported) { std::unique_ptr result; switch (style) { case VisualStyle::NoVT: case VisualStyle::Retro: case VisualStyle::Accent: case VisualStyle::Rainbow: result = std::make_unique(stream, enableVT, style); break; case VisualStyle::Sixel: if (sixelSupported()) { try { result = std::make_unique(stream, enableVT); } CATCH_LOG(); } if (!result) { result = std::make_unique(stream, enableVT, VisualStyle::Accent); } break; case VisualStyle::Disabled: break; default: THROW_HR(E_NOTIMPL); } return result; } } ================================================ FILE: src/AppInstallerCLICore/ExecutionProgress.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ChannelStreams.h" #include #include #include #include #include #include #include namespace AppInstaller::CLI::Execution { // Displays an indefinite spinner. struct IIndefiniteSpinner { virtual ~IIndefiniteSpinner() = default; // Set the message for the spinner. virtual void SetMessage(std::string_view message) = 0; // Get the current message for the spinner. virtual std::shared_ptr Message() = 0; // Show the indefinite spinner. virtual void ShowSpinner() = 0; // Stop showing the indefinite spinner. virtual void StopSpinner() = 0; // Creates an indefinite spinner for the given style. static std::unique_ptr CreateForStyle(BaseStream& stream, bool enableVT, AppInstaller::Settings::VisualStyle style, const std::function& sixelSupported); }; // Displays a progress bar. struct IProgressBar { virtual ~IProgressBar() = default; // Show progress with the given values. virtual void ShowProgress(uint64_t current, uint64_t maximum, ProgressType type) = 0; // Stop showing progress. virtual void EndProgress(bool hideProgressWhenDone) = 0; // Creates a progress bar for the given style. static std::unique_ptr CreateForStyle(BaseStream& stream, bool enableVT, AppInstaller::Settings::VisualStyle style, const std::function& sixelSupported); }; } ================================================ FILE: src/AppInstallerCLICore/ExecutionReporter.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ExecutionReporter.h" #include namespace AppInstaller::CLI::Execution { using namespace Settings; using namespace VirtualTerminal; const Sequence& HelpCommandEmphasis = TextFormat::Foreground::Bright; const Sequence& HelpArgumentEmphasis = TextFormat::Foreground::Bright; const Sequence& ManifestInfoEmphasis = TextFormat::Foreground::Bright; const Sequence& SourceInfoEmphasis = TextFormat::Foreground::Bright; const Sequence& NameEmphasis = TextFormat::Foreground::BrightCyan; const Sequence& IdEmphasis = TextFormat::Foreground::BrightCyan; const Sequence& UrlEmphasis = TextFormat::Foreground::BrightBlue; const Sequence& PromptEmphasis = TextFormat::Foreground::Bright; const Sequence& ConvertToUpgradeFlowEmphasis = TextFormat::Foreground::BrightYellow; const Sequence& ConfigurationIntentEmphasis = TextFormat::Foreground::Bright; const Sequence& ConfigurationUnitEmphasis = TextFormat::Foreground::BrightCyan; const Sequence& AuthenticationEmphasis = TextFormat::Foreground::BrightYellow; namespace { DWORD GetStdHandleType(DWORD stdHandle) { DWORD result = FILE_TYPE_UNKNOWN; HANDLE handle = GetStdHandle(stdHandle); if (handle != INVALID_HANDLE_VALUE && handle != NULL) { result = GetFileType(handle); } return result; } } Reporter::Reporter() : Reporter(std::cout, std::cin) { m_outStreamFileType = GetStdHandleType(STD_OUTPUT_HANDLE); m_inStreamFileType = GetStdHandleType(STD_INPUT_HANDLE); } Reporter::Reporter(std::ostream& outStream, std::istream& inStream) : Reporter(std::make_shared(outStream, true, ConsoleModeRestore::Instance().IsVTEnabled()), inStream) { SetProgressSink(this); } Reporter::Reporter(std::shared_ptr outStream, std::istream& inStream) : m_out(outStream), m_in(inStream) { auto sixelSupported = [&]() { return SixelsSupported(); }; m_spinner = IIndefiniteSpinner::CreateForStyle(*m_out, ConsoleModeRestore::Instance().IsVTEnabled(), VisualStyle::Accent, sixelSupported); m_progressBar = IProgressBar::CreateForStyle(*m_out, ConsoleModeRestore::Instance().IsVTEnabled(), VisualStyle::Accent, sixelSupported); SetProgressSink(this); } Reporter::~Reporter() { this->CloseOutputStream(); } Reporter::Reporter(const Reporter& other, clone_t) : Reporter(other.m_out, other.m_in) { m_outStreamFileType = other.m_outStreamFileType; m_inStreamFileType = other.m_inStreamFileType; SetChannel(other.m_channel); if (other.m_style.has_value()) { SetStyle(*other.m_style); } } std::optional Reporter::GetPrimaryDeviceAttributes() { if (ConsoleModeRestore::Instance().IsVTEnabled()) { return PrimaryDeviceAttributes{ m_out->Get(), m_in }; } else { return std::nullopt; } } OutputStream Reporter::GetOutputStream(Level level) { // If the level is not enabled, return a default stream which is disabled if (WI_AreAllFlagsClear(m_enabledLevels, level)) { return OutputStream(*m_out, false, false); } OutputStream result = GetBasicOutputStream(); switch (level) { case Level::Verbose: result.AddFormat(TextFormat::Default); break; case Level::Info: result.AddFormat(TextFormat::Default); break; case Level::Warning: result.AddFormat(TextFormat::Foreground::BrightYellow); break; case Level::Error: result.AddFormat(TextFormat::Foreground::BrightRed); break; default: THROW_HR(E_UNEXPECTED); } return result; } OutputStream Reporter::GetBasicOutputStream() { return { *m_out, m_channel == Channel::Output }; } void Reporter::SetChannel(Channel channel) { m_channel = channel; if (m_channel != Channel::Output) { // Disable progress for non-output channels m_spinner.reset(); m_progressBar.reset(); } } void Reporter::SetStyle(VisualStyle style) { m_style = style; if (m_channel == Channel::Output) { auto sixelSupported = [&]() { return SixelsSupported(); }; m_spinner = IIndefiniteSpinner::CreateForStyle(*m_out, ConsoleModeRestore::Instance().IsVTEnabled(), style, sixelSupported); m_progressBar = IProgressBar::CreateForStyle(*m_out, ConsoleModeRestore::Instance().IsVTEnabled(), style, sixelSupported); } if (style == VisualStyle::NoVT) { m_out->SetVTEnabled(false); } } std::istream& Reporter::RawInputStream() { return m_in; } bool Reporter::InputStreamIsInteractive() const { AICLI_LOG(CLI, Verbose, << "Reporter::m_inStreamFileType is " << m_inStreamFileType); return m_inStreamFileType == FILE_TYPE_CHAR; } bool Reporter::PromptForBoolResponse(Resource::LocString message, Level level, bool resultIfDisabled) { auto out = GetOutputStream(level); if (!out.IsEnabled()) { return resultIfDisabled; } const std::vector options { BoolPromptOption{ Resource::String::PromptOptionYes, 'Y', true }, BoolPromptOption{ Resource::String::PromptOptionNo, 'N', false }, }; out << message << std::endl; // Try prompting until we get a recognized option for (;;) { // Output all options for (size_t i = 0; i < options.size(); ++i) { out << PromptEmphasis << "[" + options[i].Hotkey.get() + "] " + options[i].Label.get(); if (i + 1 == options.size()) { out << PromptEmphasis << ": "; } else { out << " "; } } // Read the response std::string response; if (!std::getline(m_in, response)) { THROW_HR(APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR); } // Find the matching option ignoring whitespace Utility::Trim(response); for (const auto& option : options) { if (Utility::CaseInsensitiveEquals(response, option.Label) || Utility::CaseInsensitiveEquals(response, option.Hotkey)) { return option.Value; } } } } void Reporter::PromptForEnter(Level level) { auto out = GetOutputStream(level); if (!out.IsEnabled()) { return; } out << std::endl << Resource::String::PressEnterToContinue << std::endl; m_in.get(); } std::filesystem::path Reporter::PromptForPath(Resource::LocString message, Level level, std::filesystem::path resultIfDisabled) { auto out = GetOutputStream(level); if (!out.IsEnabled()) { return resultIfDisabled; } // Try prompting until we get a valid answer for (;;) { out << message << ' '; // Read the response std::string response; if (!std::getline(m_in, response)) { THROW_HR(APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR); } // Validate the path std::filesystem::path path{ response }; if (path.is_absolute()) { return path; } } } void Reporter::ShowIndefiniteProgress(bool running) { if (m_spinner) { if (running) { m_spinner->ShowSpinner(); } else { m_spinner->StopSpinner(); } } } void Reporter::OnProgress(uint64_t current, uint64_t maximum, ProgressType type) { ShowIndefiniteProgress(false); if (m_progressBar) { m_progressBar->ShowProgress(current, maximum, type); } } void Reporter::SetProgressMessage(std::string_view message) { if (m_spinner) { m_spinner->SetMessage(message); } } void Reporter::BeginProgress() { GetBasicOutputStream() << VirtualTerminal::Cursor::Visibility::DisableShow; ShowIndefiniteProgress(true); }; void Reporter::EndProgress(bool hideProgressWhenDone) { ShowIndefiniteProgress(false); if (m_progressBar) { m_progressBar->EndProgress(hideProgressWhenDone); } SetProgressMessage({}); GetBasicOutputStream() << VirtualTerminal::Cursor::Visibility::EnableShow; }; Reporter::AsyncProgressScope::AsyncProgressScope(Reporter& reporter, IProgressSink* sink, bool hideProgressWhenDone) : m_reporter(reporter), m_callback(sink) { reporter.SetProgressCallback(&m_callback); sink->BeginProgress(); m_hideProgressWhenDone = hideProgressWhenDone; } Reporter::AsyncProgressScope::~AsyncProgressScope() { m_reporter.get().SetProgressCallback(nullptr); m_callback.GetSink()->EndProgress(m_hideProgressWhenDone); } ProgressCallback& Reporter::AsyncProgressScope::Callback() { return m_callback; } IProgressCallback* Reporter::AsyncProgressScope::operator->() { return &m_callback; } bool Reporter::AsyncProgressScope::HideProgressWhenDone() const { return m_hideProgressWhenDone; } void Reporter::AsyncProgressScope::HideProgressWhenDone(bool value) { m_hideProgressWhenDone.store(value); } std::unique_ptr Reporter::BeginAsyncProgress(bool hideProgressWhenDone) { return std::make_unique(*this, m_progressSink.load(), hideProgressWhenDone); } void Reporter::SetProgressCallback(ProgressCallback* callback) { auto lock = m_progressCallbackLock.lock_exclusive(); // Attempting two progress operations at the same time; not supported. THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_progressCallback != nullptr && callback != nullptr); m_progressCallback = callback; } void Reporter::CancelInProgressTask(bool force, CancelReason reason) { // TODO: Maybe ask the user if they really want to cancel? UNREFERENCED_PARAMETER(force); auto lock = m_progressCallbackLock.lock_shared(); ProgressCallback* callback = m_progressCallback.load(); if (callback) { if (!callback->IsCancelledBy(CancelReason::Any)) { callback->SetProgressMessage(Resource::String::CancellingOperation()); callback->Cancel(reason); } } } void Reporter::CloseOutputStream(bool forceDisable) { if (forceDisable) { m_out->Disable(); } m_out->RestoreDefault(); } void Reporter::SetLevelMask(Level reporterLevel, bool setEnabled) { if (setEnabled) { WI_SetAllFlags(m_enabledLevels, reporterLevel); } else { WI_ClearAllFlags(m_enabledLevels, reporterLevel); } } bool Reporter::SixelsSupported() { auto attributes = GetPrimaryDeviceAttributes(); return (attributes ? attributes->Supports(PrimaryDeviceAttributes::Extension::Sixel) : false); } bool Reporter::SixelsEnabled() { return Settings::User().Get() && SixelsSupported(); } } ================================================ FILE: src/AppInstallerCLICore/ExecutionReporter.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionProgress.h" #include "ChannelStreams.h" #include "Resources.h" #include "VTSupport.h" #include #include #include #include #include #include #include #include #include #include namespace AppInstaller::CLI::Execution { #define WINGET_OSTREAM_FORMAT_HRESULT(hr) "0x" << Logging::SetHRFormat << hr // One of the options available to the users when prompting for something. struct BoolPromptOption { BoolPromptOption(Resource::StringId labelId, char hotkey, bool value) : Label(labelId), Hotkey(std::string(1, hotkey)), Value(value) {} Utility::LocIndString Hotkey; Resource::LocString Label; // Value associated with this option. bool Value; }; // Reporter should be the central place to show workflow status to user. struct Reporter : public IProgressSink { // The channel that the reporter is targeting. // Based on commands/arguments, only one of these channels can be chosen. enum class Channel { Output, Completion, Json, Disabled, }; // The level for the Output channel. enum class Level : uint32_t { None = 0x0, Verbose = 0x1, Info = 0x2, Warning = 0x4, Error = 0x8, All = Verbose | Info | Warning | Error, }; Reporter(); Reporter(std::ostream& outStream, std::istream& inStream); Reporter(const Reporter&) = delete; Reporter& operator=(const Reporter&) = delete; Reporter(Reporter&&) = default; Reporter& operator=(Reporter&&) = default; // Request that a clone be constructed from the given reporter. struct clone_t {}; Reporter(const Reporter& other, clone_t); ~Reporter(); // Gets the primary device attributes if available. std::optional GetPrimaryDeviceAttributes(); // Get a stream for verbose output. OutputStream Verbose() { return GetOutputStream(Level::Verbose); } // Get a stream for informational output. OutputStream Info() { return GetOutputStream(Level::Info); } // Get a stream for warning output. OutputStream Warn() { return GetOutputStream(Level::Warning); } // Get a stream for error output. OutputStream Error() { return GetOutputStream(Level::Error); } // Get a stream for outputting completion words. OutputStream Completion() { return OutputStream(*m_out, m_channel == Channel::Completion, false); } // Get a stream for outputting completion words. OutputStream Json() { return OutputStream(*m_out, m_channel == Channel::Json, false); } // Gets a stream for output of the given level. OutputStream GetOutputStream(Level level); void EmptyLine() { GetBasicOutputStream() << std::endl; } // Sets the channel that will be reported to. // Only do this once and as soon as the channel is determined. void SetChannel(Channel channel); // Sets the visual style (mostly for progress currently) void SetStyle(AppInstaller::Settings::VisualStyle style); // Get the raw input stream. std::istream& RawInputStream(); // Check if the input stream is interactive or not. bool InputStreamIsInteractive() const; // Prompts the user, return true if they consented. bool PromptForBoolResponse(Resource::LocString message, Level level = Level::Info, bool resultIfDisabled = false); // Prompts the user, continues when Enter is pressed void PromptForEnter(Level level = Level::Info); // Prompts the user for a path. std::filesystem::path PromptForPath(Resource::LocString message, Level level = Level::Info, std::filesystem::path resultIfDisabled = std::filesystem::path::path()); // IProgressSink void BeginProgress() override; void OnProgress(uint64_t current, uint64_t maximum, ProgressType type) override; void SetProgressMessage(std::string_view message) override; void EndProgress(bool hideProgressWhenDone) override; // Contains the objects used for async progress and the lifetime of said progress. struct AsyncProgressScope { AsyncProgressScope(Reporter& reporter, IProgressSink* sink, bool hideProgressWhenDone); ~AsyncProgressScope(); AsyncProgressScope(const AsyncProgressScope&) = delete; AsyncProgressScope& operator=(const AsyncProgressScope&) = delete; AsyncProgressScope(AsyncProgressScope&&) = delete; AsyncProgressScope& operator=(AsyncProgressScope&&) = delete; ProgressCallback& Callback(); IProgressCallback* operator->(); bool HideProgressWhenDone() const; void HideProgressWhenDone(bool value); private: std::reference_wrapper m_reporter; ProgressCallback m_callback; std::atomic_bool m_hideProgressWhenDone; }; // Runs the given callable of type: auto(IProgressCallback&) template auto ExecuteWithProgress(F&& f, bool hideProgressWhenDone = false) { auto progressScope = BeginAsyncProgress(hideProgressWhenDone); return f(progressScope->Callback()); } // Begins an asynchronous progress operation. std::unique_ptr BeginAsyncProgress(bool hideProgressWhenDone = false); // Sets the in progress callback. void SetProgressCallback(ProgressCallback* callback); // Cancels the in progress task. void CancelInProgressTask(bool force, CancelReason reason); void CloseOutputStream(bool forceDisable = false); void SetProgressSink(IProgressSink* sink) { m_progressSink = sink; } bool IsLevelEnabled(Level reporterLevel) { return WI_AreAllFlagsSet(m_enabledLevels, reporterLevel); } void SetLevelMask(Level reporterLevel, bool setEnabled = true); // Determines if sixels are supported by the current instance. bool SixelsSupported(); // Determines if sixels are enabled; they must be both supported and enabled by user settings. bool SixelsEnabled(); private: Reporter(std::shared_ptr outStream, std::istream& inStream); // Gets a stream for output for internal use. OutputStream GetBasicOutputStream(); // Used to show indefinite progress. Currently an indefinite spinner is the form of // showing indefinite progress. // running: shows indefinite progress if set to true, stops indefinite progress if set to false void ShowIndefiniteProgress(bool running); Channel m_channel = Channel::Output; std::shared_ptr m_out; std::istream& m_in; std::optional m_style; std::unique_ptr m_spinner; std::unique_ptr m_progressBar; wil::srwlock m_progressCallbackLock; std::atomic m_progressCallback; std::atomic m_progressSink; DWORD m_outStreamFileType = FILE_TYPE_UNKNOWN; DWORD m_inStreamFileType = FILE_TYPE_UNKNOWN; // Enable all levels by default Level m_enabledLevels = Level::All; }; DEFINE_ENUM_FLAG_OPERATORS(Reporter::Level); // Indirection to enable change without tracking down every place extern const VirtualTerminal::Sequence& HelpCommandEmphasis; extern const VirtualTerminal::Sequence& HelpArgumentEmphasis; extern const VirtualTerminal::Sequence& ManifestInfoEmphasis; extern const VirtualTerminal::Sequence& SourceInfoEmphasis; extern const VirtualTerminal::Sequence& NameEmphasis; extern const VirtualTerminal::Sequence& IdEmphasis; extern const VirtualTerminal::Sequence& UrlEmphasis; extern const VirtualTerminal::Sequence& PromptEmphasis; extern const VirtualTerminal::Sequence& ConvertToUpgradeFlowEmphasis; extern const VirtualTerminal::Sequence& ConfigurationIntentEmphasis; extern const VirtualTerminal::Sequence& ConfigurationUnitEmphasis; extern const VirtualTerminal::Sequence& AuthenticationEmphasis; } ================================================ FILE: src/AppInstallerCLICore/Invocation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::CLI { // Contains the raw command line arguments and functionality to iterate and consume them. struct Invocation { Invocation(std::vector&& args) : m_args(std::move(args)) {} struct iterator { iterator(size_t arg, std::vector& args) : m_arg(arg), m_args(args) {} iterator(const iterator&) = default; iterator& operator=(const iterator&) = default; iterator operator++() { return { ++m_arg, m_args }; } iterator operator++(int) { return { m_arg++, m_args }; } iterator operator--() { return { --m_arg, m_args }; } iterator operator--(int) { return { m_arg--, m_args }; } bool operator==(const iterator& other) const { return m_arg == other.m_arg; } bool operator!=(const iterator& other) const { return m_arg != other.m_arg; } const std::string& operator*() const { return m_args[m_arg]; } const std::string* operator->() const { return &(m_args[m_arg]); } size_t index() const { return m_arg; } private: size_t m_arg; std::vector& m_args; }; size_t size() const { return m_args.size(); } iterator begin() { return { m_currentFirstArg, m_args }; } iterator end() { return { m_args.size(), m_args }; } void consume(const iterator& i) { m_currentFirstArg = i.index() + 1; } private: std::vector m_args; size_t m_currentFirstArg = 0; }; } ================================================ FILE: src/AppInstallerCLICore/PackageCollection.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageCollection.h" #include #include #include #include "PackagesSchema.h" #include #include using namespace AppInstaller::Repository; namespace AppInstaller::CLI { namespace { // Strings used in the Packages JSON file. // Most will be used to access a JSON value, so they need to be std::string struct StaticStrings { const std::string PackagesJson_Schema = "$schema"; const std::string PackagesJson_SchemaUri_v1_0 = "https://aka.ms/winget-packages.schema.1.0.json"; const std::string PackagesJson_SchemaUri_v2_0 = "https://aka.ms/winget-packages.schema.2.0.json"; const std::string PackagesJson_WinGetVersion = "WinGetVersion"; const std::string PackagesJson_CreationDate = "CreationDate"; const std::string PackagesJson_Sources = "Sources"; const std::string PackagesJson_Source_Details = "SourceDetails"; const std::string PackagesJson_Source_Name = "Name"; const std::string PackagesJson_Source_Identifier = "Identifier"; const std::string PackagesJson_Source_Argument = "Argument"; const std::string PackagesJson_Source_Type = "Type"; const std::string PackagesJson_Packages = "Packages"; const std::string PackagesJson_Package_Id = "Id"; const std::string PackagesJson_Package_PackageIdentifier = "PackageIdentifier"; const std::string PackagesJson_Package_Version = "Version"; const std::string PackagesJson_Package_Channel = "Channel"; const std::string PackagesJson_Package_Scope = "Scope"; static const StaticStrings& Instance() { static StaticStrings instance; return instance; } }; // Gets or creates a property of a JSON object by its name. Json::Value& GetJsonProperty(Json::Value& node, const std::string& propertyName, Json::ValueType valueType) { if (!node.isMember(propertyName)) { node[propertyName] = Json::Value{ valueType }; } else { THROW_HR_IF(E_NOT_VALID_STATE, node[propertyName].type() != valueType); } return node[propertyName]; } // The interface for a package collection parser. struct IPackageCollectionParser { virtual ~IPackageCollectionParser() = default; virtual PackageCollection Parse(const Json::Value& root) = 0; }; // The parsing code for schema v1.0 struct PackageCollectionParser_1_0 : public IPackageCollectionParser { PackageCollection Parse(const Json::Value& root) override { PackageCollection result; // Regardless of the fact that the value is required in 1.0, allow it to be optional if (root.isMember(ss.PackagesJson_WinGetVersion)) { result.ClientVersion = root[ss.PackagesJson_WinGetVersion].asString(); } for (const auto& sourceNode : root[ss.PackagesJson_Sources]) { auto newSource = ParseSourceNode(sourceNode); auto existingSource = std::find_if(result.Sources.begin(), result.Sources.end(), [&](const PackageCollection::Source& s) { return s.Details.Identifier == newSource.Details.Identifier; }); if (existingSource == result.Sources.end()) { result.Sources.push_back(std::move(newSource)); } else { existingSource->Packages.insert(existingSource->Packages.end(), newSource.Packages.begin(), newSource.Packages.end()); } } return result; } protected: // Reads the description of a package from a Package node in the JSON. virtual PackageCollection::Package ParsePackageNode(const Json::Value& packageNode) { std::string id = packageNode[ss.PackagesJson_Package_Id].asString(); std::string version = packageNode.isMember(ss.PackagesJson_Package_Version) ? packageNode[ss.PackagesJson_Package_Version].asString() : ""; std::string channel = packageNode.isMember(ss.PackagesJson_Package_Channel) ? packageNode[ss.PackagesJson_Package_Channel].asString() : ""; std::string scope = packageNode.isMember(ss.PackagesJson_Package_Scope) ? packageNode[ss.PackagesJson_Package_Scope].asString() : ""; PackageCollection::Package package{ Utility::LocIndString{ id }, Utility::Version{ version }, Utility::Channel{ channel } }; package.Scope = Manifest::ConvertToScopeEnum(scope); return package; } // Reads the description of a Source and all the packages needed from it, from a Source node in the JSON. PackageCollection::Source ParseSourceNode(const Json::Value& sourceNode) { SourceDetails sourceDetails; auto& detailsNode = sourceNode[ss.PackagesJson_Source_Details]; sourceDetails.Identifier = Utility::LocIndString{ detailsNode[ss.PackagesJson_Source_Identifier].asString() }; sourceDetails.Name = detailsNode[ss.PackagesJson_Source_Name].asString(); sourceDetails.Arg = detailsNode[ss.PackagesJson_Source_Argument].asString(); sourceDetails.Type = detailsNode[ss.PackagesJson_Source_Type].asString(); PackageCollection::Source source{ std::move(sourceDetails) }; for (const auto& packageNode : sourceNode[ss.PackagesJson_Packages]) { source.Packages.emplace_back(ParsePackageNode(packageNode)); } return source; } const StaticStrings& ss = StaticStrings::Instance(); }; // The parsing code for schema v2.0 struct PackageCollectionParser_2_0 : public PackageCollectionParser_1_0 { protected: // Reads the description of a package from a Package node in the JSON. PackageCollection::Package ParsePackageNode(const Json::Value& packageNode) override { std::string id = packageNode[ss.PackagesJson_Package_PackageIdentifier].asString(); std::string version = packageNode.isMember(ss.PackagesJson_Package_Version) ? packageNode[ss.PackagesJson_Package_Version].asString() : ""; std::string channel = packageNode.isMember(ss.PackagesJson_Package_Channel) ? packageNode[ss.PackagesJson_Package_Channel].asString() : ""; std::string scope = packageNode.isMember(ss.PackagesJson_Package_Scope) ? packageNode[ss.PackagesJson_Package_Scope].asString() : ""; PackageCollection::Package package{ Utility::LocIndString{ id }, Utility::Version{ version }, Utility::Channel{ channel } }; package.Scope = Manifest::ConvertToScopeEnum(scope); return package; } }; // Creates a minimal root object of a Packages JSON file. Json::Value CreateRoot(const std::string& wingetVersion) { const auto& ss = StaticStrings::Instance(); Json::Value root{ Json::ValueType::objectValue }; root[ss.PackagesJson_WinGetVersion] = wingetVersion; // We only generate the latest schema root[ss.PackagesJson_Schema] = ss.PackagesJson_SchemaUri_v2_0; std::stringstream currentTimeStream; Utility::OutputTimePoint(currentTimeStream, std::chrono::system_clock::now(), true); root[ss.PackagesJson_CreationDate] = currentTimeStream.str(); return root; } // Adds a new Package node to a Source node in the Json file, and returns it. Json::Value& AddPackageToSource(Json::Value& sourceNode, const PackageCollection::Package& package) { const auto& ss = StaticStrings::Instance(); Json::Value packageNode{ Json::ValueType::objectValue }; packageNode[ss.PackagesJson_Package_PackageIdentifier] = package.Id.get(); // Only add version and channel if present. // Packages may not have a channel, or versions may not have been requested. const std::string& version = package.VersionAndChannel.GetVersion().ToString(); if (!version.empty()) { packageNode[ss.PackagesJson_Package_Version] = version; } const std::string& channel = package.VersionAndChannel.GetChannel().ToString(); if (!channel.empty()) { packageNode[ss.PackagesJson_Package_Channel] = channel; } if (package.Scope != Manifest::ScopeEnum::Unknown) { packageNode[ss.PackagesJson_Package_Scope] = std::string{ Manifest::ScopeToString(package.Scope) }; } return sourceNode[ss.PackagesJson_Packages].append(std::move(packageNode)); } // Adds a new Source node to the JSON, and returns it. Json::Value& AddSourceNode(Json::Value& root, const PackageCollection::Source& source) { const auto& ss = StaticStrings::Instance(); Json::Value sourceNode{ Json::ValueType::objectValue }; Json::Value sourceDetailsNode{ Json::ValueType::objectValue }; sourceDetailsNode[ss.PackagesJson_Source_Name] = source.Details.Name; sourceDetailsNode[ss.PackagesJson_Source_Argument] = source.Details.Arg; sourceDetailsNode[ss.PackagesJson_Source_Identifier] = source.Details.Identifier; sourceDetailsNode[ss.PackagesJson_Source_Type] = source.Details.Type; sourceNode[ss.PackagesJson_Source_Details] = std::move(sourceDetailsNode); sourceNode[ss.PackagesJson_Packages] = Json::Value{ Json::ValueType::arrayValue }; auto& sourcesNode = GetJsonProperty(root, ss.PackagesJson_Sources, Json::ValueType::arrayValue); for (const auto& package : source.Packages) { AddPackageToSource(sourceNode, package); } return sourcesNode.append(std::move(sourceNode)); } } namespace PackagesJson { Json::Value CreateJson(const PackageCollection& packages) { Json::Value root = CreateRoot(packages.ClientVersion); for (const auto& source : packages.Sources) { AddSourceNode(root, source); } return root; } ParseResult TryParseJson(const Json::Value& root) { const auto& ss = StaticStrings::Instance(); // Find the schema used for the JSON if (!(root.isObject() && root.isMember(ss.PackagesJson_Schema) && root[ss.PackagesJson_Schema].isString())) { AICLI_LOG(CLI, Error, << "Import file is missing \"" << ss.PackagesJson_Schema << "\" property"); return ParseResult{ ParseResult::Type::MissingSchema }; } const auto& schemaUri = root[ss.PackagesJson_Schema].asString(); Json::Value schemaJson; std::unique_ptr parser; if (schemaUri == ss.PackagesJson_SchemaUri_v1_0) { schemaJson = JsonSchema::LoadResourceAsSchemaDoc(MAKEINTRESOURCE(IDX_PACKAGES_SCHEMA_V1), MAKEINTRESOURCE(PACKAGESSCHEMA_RESOURCE_TYPE)); parser = std::make_unique(); } else if (schemaUri == ss.PackagesJson_SchemaUri_v2_0) { schemaJson = JsonSchema::LoadResourceAsSchemaDoc(MAKEINTRESOURCE(IDX_PACKAGES_SCHEMA_V2), MAKEINTRESOURCE(PACKAGESSCHEMA_RESOURCE_TYPE)); parser = std::make_unique(); } else { AICLI_LOG(CLI, Error, << "Unrecognized schema for import file: " << schemaUri); return ParseResult{ ParseResult::Type::UnrecognizedSchema }; } // Validate the JSON against the schema. valijson::Schema schema; JsonSchema::PopulateSchema(schemaJson, schema); valijson::ValidationResults results; if (!JsonSchema::Validate(schema, root, results)) { return ParseResult{ ParseResult::Type::SchemaValidationFailed, JsonSchema::GetErrorStringFromResults(results) }; } // Extract the data from the JSON. return ParseResult{ parser->Parse(root) }; } } } ================================================ FILE: src/AppInstallerCLICore/PackageCollection.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AppInstallerDateTime.h" #include "winget/RepositorySource.h" #include #include #include namespace AppInstaller::CLI { // Container for data to identify multiple packages to be installed from multiple sources. struct PackageCollection { // Description of a package. // Does not represent the actual package, just enough to find and install it. struct Package { Package() = default; Package(Utility::LocIndString&& id) : Id(std::move(id)) {} Package(Utility::LocIndString&& id, Utility::Version&& version, Utility::Channel&& channel) : Id(std::move(id)), VersionAndChannel(std::move(version), std::move(channel)) {} Package(Utility::LocIndString&& id, Utility::VersionAndChannel&& versionAndChannel) : Id(std::move(id)), VersionAndChannel(std::move(versionAndChannel)) {} Utility::LocIndString Id; Utility::VersionAndChannel VersionAndChannel; Manifest::ScopeEnum Scope = Manifest::ScopeEnum::Unknown; std::filesystem::path InstalledLocation; }; // A source along with a set of packages available from it. struct Source { Source() = default; Source(const Repository::SourceDetails& sourceDetails) : Details(sourceDetails) {} Source(Repository::SourceDetails&& sourceDetails) : Details(std::move(sourceDetails)) {} Repository::SourceDetails Details; std::vector Packages; }; // Version of the WinGet client that produced this collection. std::string ClientVersion; // Requests from each individual source. std::vector Sources; }; namespace PackagesJson { struct ParseResult { enum class Type { MissingSchema, UnrecognizedSchema, SchemaValidationFailed, Success, }; ParseResult(Type result) : Result(result) {} ParseResult(Type result, std::string_view errors) : Result(result), Errors(errors) {} ParseResult(PackageCollection&& packages) : Result(Type::Success), Packages(std::move(packages)) {} Type Result; PackageCollection Packages; std::string Errors; }; // Converts a collection of packages to its JSON representation for exporting. Json::Value CreateJson(const PackageCollection& packages); // Tries to parse a JSON into a collection of packages. ParseResult TryParseJson(const Json::Value& root); } } ================================================ FILE: src/AppInstallerCLICore/PortableInstaller.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ExecutionContext.h" #include "PortableInstaller.h" #include #include #include #include #include #include #include #include using namespace AppInstaller::Utility; using namespace AppInstaller::Registry; using namespace AppInstaller::Registry::Portable; using namespace AppInstaller::Registry::Environment; using namespace AppInstaller::Repository; using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Repository::Microsoft::Schema; using namespace AppInstaller::CLI::Workflow; namespace AppInstaller::CLI::Portable { std::filesystem::path GetPortableLinksLocation(Manifest::ScopeEnum scope) { if (scope == Manifest::ScopeEnum::Machine) { return Runtime::GetPathTo(Runtime::PathName::PortableLinksMachineLocation); } else { return Runtime::GetPathTo(Runtime::PathName::PortableLinksUserLocation); } } std::filesystem::path GetPortableInstallRoot(Manifest::ScopeEnum scope, Utility::Architecture arch) { if (scope == Manifest::ScopeEnum::Machine) { if (arch == Utility::Architecture::X86) { return Runtime::GetPathTo(Runtime::PathName::PortablePackageMachineRootX86); } else { return Runtime::GetPathTo(Runtime::PathName::PortablePackageMachineRoot); } } else { return Runtime::GetPathTo(Runtime::PathName::PortablePackageUserRoot); } } bool VerifyPortableFile(AppInstaller::Portable::PortableFileEntry& entry) { std::filesystem::path filePath = entry.GetFilePath(); PortableFileType fileType = entry.FileType; if (fileType == PortableFileType::File) { if (std::filesystem::exists(filePath)) { SHA256::HashBuffer fileHash = SHA256::ComputeHashFromFile(filePath); if (!SHA256::AreEqual(fileHash, SHA256::ConvertToBytes(entry.SHA256))) { AICLI_LOG(CLI, Warning, << "File hash does not match ARP Entry. Expected: " << entry.SHA256 << " Actual: " << SHA256::ConvertToString(fileHash)); return false; } } } else if (fileType == PortableFileType::Symlink) { std::filesystem::path symlinkTargetPath{ AppInstaller::Utility::ConvertToUTF16(entry.SymlinkTarget) }; if (Filesystem::SymlinkExists(filePath) && !Filesystem::VerifySymlink(filePath, symlinkTargetPath)) { AICLI_LOG(CLI, Warning, << "Symlink target does not match ARP Entry. Expected: " << symlinkTargetPath << " Actual: " << std::filesystem::read_symlink(filePath)); return false; } } return true; } void PortableInstaller::InstallFile(AppInstaller::Portable::PortableFileEntry& entry) { PortableFileType fileType = entry.FileType; std::filesystem::path filePath = entry.GetFilePath(); if (entry.FileType == PortableFileType::File) { if (std::filesystem::exists(filePath)) { AICLI_LOG(Core, Info, << "Removing existing portable file at: " << filePath); std::filesystem::remove(filePath); } AICLI_LOG(Core, Info, << "Moving portable exe to: " << filePath); if (!RecordToIndex) { CommitToARPEntry(PortableValueName::PortableTargetFullPath, filePath); CommitToARPEntry(PortableValueName::SHA256, entry.SHA256); } Filesystem::RenameFile(entry.CurrentPath, filePath); } else if (fileType == PortableFileType::Directory) { if (Filesystem::IsSameVolume(entry.CurrentPath, filePath)) { AICLI_LOG(Core, Info, << "Renaming directory to: " << filePath); Filesystem::RenameFile(entry.CurrentPath, filePath); } else { // Copy directory instead of renaming as there is a known issue with renaming across drives. AICLI_LOG(Core, Info, << "Copying directory to: " << filePath); std::filesystem::copy(entry.CurrentPath, filePath, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive); } } else if (entry.FileType == PortableFileType::Symlink) { std::filesystem::path symlinkTargetPath{ Utility::ConvertToUTF16(entry.SymlinkTarget) }; if (BinariesDependOnPath && !InstallDirectoryAddedToPath) { // Scenario indicated by 'ArchiveBinariesDependOnPath' manifest entry. // Skip symlink creation for portables dependent on binaries that require the install directory to be added to PATH. std::filesystem::path installDirectory = symlinkTargetPath.parent_path(); AddToPathVariable(installDirectory); AICLI_LOG(Core, Info, << "Install directory added to PATH: " << installDirectory); CommitToARPEntry(PortableValueName::InstallDirectoryAddedToPath, InstallDirectoryAddedToPath = true); } else if (!InstallDirectoryAddedToPath) { std::filesystem::file_status status = std::filesystem::status(filePath); if (std::filesystem::is_directory(status)) { AICLI_LOG(CLI, Info, << "Unable to create symlink. '" << filePath << "points to an existing directory."); THROW_HR(APPINSTALLER_CLI_ERROR_PORTABLE_SYMLINK_PATH_IS_DIRECTORY); } if (!RecordToIndex) { CommitToARPEntry(PortableValueName::PortableSymlinkFullPath, filePath); } if (std::filesystem::remove(filePath)) { AICLI_LOG(CLI, Info, << "Removed existing file at " << filePath); m_stream << Resource::String::OverwritingExistingFileAtMessage(Utility::LocIndView{ filePath.u8string() }) << std::endl; } if (Filesystem::CreateSymlink(symlinkTargetPath, filePath)) { AICLI_LOG(Core, Info, << "Symlink created at: " << filePath << " with target path: " << symlinkTargetPath); } else { // If symlink creation fails, resort to adding the package directory to PATH. AICLI_LOG(Core, Info, << "Failed to create symlink at: " << filePath); AddToPathVariable(symlinkTargetPath.parent_path()); CommitToARPEntry(PortableValueName::InstallDirectoryAddedToPath, InstallDirectoryAddedToPath = true); } } m_stream << Resource::String::PortableAliasAdded << ' ' << filePath.stem() << std::endl; } } void PortableInstaller::RemoveFile(AppInstaller::Portable::PortableFileEntry& entry) { const auto& filePath = entry.GetFilePath(); PortableFileType fileType = entry.FileType; if (fileType == PortableFileType::File && std::filesystem::exists(filePath)) { AICLI_LOG(CLI, Info, << "Deleting portable exe at: " << filePath); std::filesystem::remove(filePath); } else if (fileType == PortableFileType::Symlink) { if (Filesystem::SymlinkExists(filePath)) { AICLI_LOG(CLI, Info, << "Deleting portable symlink at: " << filePath); std::filesystem::remove(filePath); } else if (InstallDirectoryAddedToPath) { // If symlink doesn't exist, check if install directory was added to PATH directly and remove. RemoveFromPathVariable(std::filesystem::path(Utility::ConvertToUTF16(entry.SymlinkTarget)).parent_path()); } } else if (fileType == PortableFileType::Symlink && Filesystem::SymlinkExists(filePath)) { AICLI_LOG(CLI, Info, << "Deleting portable symlink at: " << filePath); std::filesystem::remove(filePath); } else if (fileType == PortableFileType::Directory && std::filesystem::exists(filePath)) { AICLI_LOG(CLI, Info, << "Removing directory at " << filePath); std::filesystem::remove_all(filePath); } } // TODO: Optimize by applying the difference between expected and desired state. void PortableInstaller::ApplyDesiredState() { std::filesystem::path existingIndexPath = InstallLocation / GetPortableIndexFileName(); if (std::filesystem::exists(existingIndexPath)) { bool deleteIndex = false; { PortableIndex existingIndex = PortableIndex::Open(existingIndexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); for (auto expectedEntry : m_expectedEntries) { RemoveFile(expectedEntry); existingIndex.RemovePortableFile(expectedEntry); } deleteIndex = existingIndex.IsEmpty(); } if (deleteIndex) { std::filesystem::remove(existingIndexPath); AICLI_LOG(CLI, Info, << "Portable index deleted: " << existingIndexPath); } } else { for (auto expectedEntry : m_expectedEntries) { RemoveFile(expectedEntry); } } // Check if existing install location differs from the target install location for proper cleanup. if (!TargetInstallLocation.empty() && TargetInstallLocation != InstallLocation) { RemoveInstallDirectory(); } if (RecordToIndex) { std::filesystem::path targetIndexPath = TargetInstallLocation / GetPortableIndexFileName(); PortableIndex targetIndex = std::filesystem::exists(targetIndexPath) ? PortableIndex::Open(targetIndexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite) : PortableIndex::CreateNew(targetIndexPath.u8string()); for (auto desiredEntry : m_desiredEntries) { targetIndex.AddOrUpdatePortableFile(desiredEntry); InstallFile(desiredEntry); } } else { for (auto desiredEntry : m_desiredEntries) { InstallFile(desiredEntry); } } } bool PortableInstaller::VerifyExpectedState() { for (auto entry : m_expectedEntries) { if (!VerifyPortableFile(entry)) { AICLI_LOG(CLI, Info, << "Portable file has been modified: " << entry.GetFilePath()); return false; } } return true; } void PortableInstaller::Install(Workflow::OperationType operation) { // If the operation is an install, the ARP entry should be created first so that a catastrophic failure // leaves the system in a state where an uninstall may be possible if (operation == Workflow::OperationType::Install) { RegisterARPEntry(); } CreateTargetInstallDirectory(); ApplyDesiredState(); if (!InstallDirectoryAddedToPath) { AddToPathVariable(GetPortableLinksLocation(GetScope())); } // If the operation is an upgrade, the ARP entry should be created last so that a catastrophic failure // leaves the system in a state where an upgrade can be re-attempted if (operation == Workflow::OperationType::Upgrade) { RegisterARPEntry(); } } void PortableInstaller::Uninstall() { ApplyDesiredState(); RemoveInstallDirectory(); if (!InstallDirectoryAddedToPath) { RemoveFromPathVariable(GetPortableLinksLocation(GetScope())); } m_portableARPEntry.Delete(); AICLI_LOG(CLI, Info, << "PortableARPEntry deleted."); } void PortableInstaller::CreateTargetInstallDirectory() { if (std::filesystem::create_directories(TargetInstallLocation)) { AICLI_LOG(Core, Info, << "Created target install directory: " << TargetInstallLocation); CommitToARPEntry(PortableValueName::InstallDirectoryCreated, true); } CommitToARPEntry(PortableValueName::InstallLocation, TargetInstallLocation); } void PortableInstaller::RemoveInstallDirectory() { if (std::filesystem::exists(InstallLocation) && InstallDirectoryCreated) { if (Purge) { m_stream << Resource::String::PurgeInstallDirectory << std::endl; const auto& removedFilesCount = std::filesystem::remove_all(InstallLocation); AICLI_LOG(CLI, Info, << "Purged install location directory. Deleted " << removedFilesCount << " files or directories"); } else { if (std::filesystem::is_empty(InstallLocation)) { AICLI_LOG(CLI, Info, << "Removing empty install directory: " << InstallLocation); std::filesystem::remove(InstallLocation); } else { AICLI_LOG(CLI, Info, << "Unable to remove install directory as there are remaining files in: " << InstallLocation); m_stream << Resource::String::FilesRemainInInstallDirectory(Utility::LocIndView{ InstallLocation.u8string() }) << std::endl; } } } } void PortableInstaller::AddToPathVariable(std::filesystem::path value) { // Ensure the preferred separator format value.make_preferred(); if (PathVariable(GetScope()).Append(value)) { AICLI_LOG(Core, Info, << "Appending portable target directory to PATH registry: " << value); m_stream << Resource::String::ModifiedPathRequiresShellRestart << std::endl; } else { AICLI_LOG(CLI, Info, << "Portable target directory already exists in PATH registry: " << value); } } void PortableInstaller::RemoveFromPathVariable(std::filesystem::path value) { if (std::filesystem::exists(value) && !std::filesystem::is_empty(value)) { AICLI_LOG(Core, Info, << "Install directory is not empty: " << value); } else { // Attempt to remove both the original and the preferred format to ensure removal // Necessary for handling old path values associated with winget-cli#5033 if (PathVariable(GetScope()).Remove(value) || PathVariable(GetScope()).Remove(value.make_preferred())) { InstallDirectoryAddedToPath = false; AICLI_LOG(CLI, Info, << "Removed target directory from PATH registry: " << value); } else { AICLI_LOG(CLI, Info, << "Target directory not removed from PATH registry: " << value); } } } void PortableInstaller::SetAppsAndFeaturesMetadata(const Manifest::Manifest& manifest, const std::vector& entries) { AppInstaller::Manifest::AppsAndFeaturesEntry entry; if (!entries.empty()) { entry = entries[0]; } if (entry.DisplayName.empty()) { entry.DisplayName = manifest.CurrentLocalization.Get(); } if (entry.DisplayVersion.empty()) { entry.DisplayVersion = manifest.Version; } if (entry.Publisher.empty()) { entry.Publisher = manifest.CurrentLocalization.Get(); } DisplayName = entry.DisplayName; DisplayVersion = entry.DisplayVersion; Publisher = entry.Publisher; InstallDate = Utility::GetCurrentDateForARP(); URLInfoAbout = manifest.CurrentLocalization.Get(); HelpLink = manifest.CurrentLocalization.Get(); } AppInstaller::Manifest::AppsAndFeaturesEntry PortableInstaller::GetAppsAndFeaturesEntry() { Manifest::AppsAndFeaturesEntry entry; entry.DisplayName = DisplayName; entry.Publisher = Publisher; entry.DisplayVersion = DisplayVersion; entry.InstallerType = Manifest::InstallerTypeEnum::Portable; entry.ProductCode = GetProductCode(); return entry; } void PortableInstaller::SetExpectedState() { const auto& indexPath = InstallLocation / GetPortableIndexFileName(); if (std::filesystem::exists(indexPath)) { PortableIndex portableIndex = PortableIndex::Open(indexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); m_expectedEntries = portableIndex.GetAllPortableFiles(); } else { std::filesystem::path targetFullPath = PortableTargetFullPath; std::filesystem::path symlinkFullPath = PortableSymlinkFullPath; // Order matters here so that file entries are removed before symlink entries during uninstall from registry. // This is to ensure that the directory is fully uninstalled before attempting to remove from PATH registry. if (!targetFullPath.empty()) { m_expectedEntries.emplace_back(std::move(PortableFileEntry::CreateFileEntry({}, targetFullPath, SHA256))); } if (!symlinkFullPath.empty()) { m_expectedEntries.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkFullPath, targetFullPath))); } } } PortableInstaller::PortableInstaller(Manifest::ScopeEnum scope, Utility::Architecture arch, const std::string& productCode) : m_portableARPEntry(PortableARPEntry(scope, arch, productCode)) { if (ARPEntryExists()) { DisplayName = GetStringValue(PortableValueName::DisplayName); DisplayVersion = GetStringValue(PortableValueName::DisplayVersion); HelpLink = GetStringValue(PortableValueName::HelpLink); InstallDate = GetStringValue(PortableValueName::InstallDate); Publisher = GetStringValue(PortableValueName::Publisher); SHA256 = GetStringValue(PortableValueName::SHA256); URLInfoAbout = GetStringValue(PortableValueName::URLInfoAbout); UninstallString = GetStringValue(PortableValueName::UninstallString); WinGetInstallerType = GetStringValue(PortableValueName::WinGetInstallerType); WinGetPackageIdentifier = GetStringValue(PortableValueName::WinGetPackageIdentifier); WinGetSourceIdentifier = GetStringValue(PortableValueName::WinGetSourceIdentifier); InstallLocation = GetPathValue(PortableValueName::InstallLocation); PortableSymlinkFullPath = GetPathValue(PortableValueName::PortableSymlinkFullPath); PortableTargetFullPath = GetPathValue(PortableValueName::PortableTargetFullPath); InstallDirectoryAddedToPath = GetBoolValue(PortableValueName::InstallDirectoryAddedToPath); InstallDirectoryCreated = GetBoolValue(PortableValueName::InstallDirectoryCreated); } SetExpectedState(); } void PortableInstaller::RegisterARPEntry() { CommitToARPEntry(PortableValueName::WinGetPackageIdentifier, WinGetPackageIdentifier); CommitToARPEntry(PortableValueName::WinGetSourceIdentifier, WinGetSourceIdentifier); CommitToARPEntry(PortableValueName::UninstallString, "winget uninstall --product-code " + GetProductCode()); CommitToARPEntry(PortableValueName::WinGetInstallerType, InstallerTypeToString(Manifest::InstallerTypeEnum::Portable)); CommitToARPEntry(PortableValueName::DisplayName, DisplayName); CommitToARPEntry(PortableValueName::DisplayVersion, DisplayVersion); CommitToARPEntry(PortableValueName::Publisher, Publisher); CommitToARPEntry(PortableValueName::InstallDate, InstallDate); CommitToARPEntry(PortableValueName::URLInfoAbout, URLInfoAbout); CommitToARPEntry(PortableValueName::HelpLink, HelpLink); } std::string PortableInstaller::GetStringValue(PortableValueName valueName) { if (m_portableARPEntry[valueName].has_value()) { return m_portableARPEntry[valueName]->GetValue(); } else { return {}; } } std::filesystem::path PortableInstaller::GetPathValue(PortableValueName valueName) { if (m_portableARPEntry[valueName].has_value()) { return m_portableARPEntry[valueName]->GetValue(); } { return {}; } } bool PortableInstaller::GetBoolValue(PortableValueName valueName) { if (m_portableARPEntry[valueName].has_value()) { return m_portableARPEntry[valueName]->GetValue(); } else { return false; } } } ================================================ FILE: src/AppInstallerCLICore/PortableInstaller.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winget/PortableARPEntry.h" #include "winget/PortableFileEntry.h" #include #include using namespace AppInstaller::Registry::Portable; namespace AppInstaller::CLI::Portable { std::filesystem::path GetPortableLinksLocation(Manifest::ScopeEnum scope); std::filesystem::path GetPortableInstallRoot(Manifest::ScopeEnum scope, Utility::Architecture arch); // Object representation of the metadata and functionality required for installing a Portable package. struct PortableInstaller { // These values are initialized based on the values from the entry in ARP std::string DisplayName; std::string DisplayVersion; std::string HelpLink; std::string InstallDate; std::filesystem::path InstallLocation; std::filesystem::path PortableSymlinkFullPath; std::filesystem::path PortableTargetFullPath; std::string Publisher; std::string SHA256; std::string URLInfoAbout; std::string UninstallString; std::string WinGetInstallerType; std::string WinGetPackageIdentifier; std::string WinGetSourceIdentifier; bool InstallDirectoryCreated = false; bool BinariesDependOnPath = false; // If we fail to create a symlink, add install directory to PATH variable bool InstallDirectoryAddedToPath = false; bool IsUpdate = false; bool Purge = false; bool RecordToIndex = false; // This is the incoming target install location determined from the context args. std::filesystem::path TargetInstallLocation; PortableInstaller(Manifest::ScopeEnum scope, Utility::Architecture arch, const std::string& productCode); bool VerifyExpectedState(); void SetDesiredState(const std::vector& desiredEntries) { m_desiredEntries = desiredEntries; }; void PrepareForCleanUp() { m_expectedEntries = m_desiredEntries; m_desiredEntries = {}; } void Install(AppInstaller::CLI::Workflow::OperationType operation = Workflow::OperationType::Install); void Uninstall(); template void CommitToARPEntry(PortableValueName valueName, T value) { m_portableARPEntry.SetValue(valueName, value); } std::filesystem::path GetPortableIndexFileName() { return Utility::ConvertToUTF16(GetProductCode() + ".db"); } Manifest::ScopeEnum GetScope() { return m_portableARPEntry.GetScope(); }; Utility::Architecture GetArch() { return m_portableARPEntry.GetArchitecture(); }; std::string GetProductCode() { return m_portableARPEntry.GetProductCode(); }; bool ARPEntryExists() { return m_portableARPEntry.Exists(); }; std::string GetOutputMessage() { return m_stream.str(); } void SetAppsAndFeaturesMetadata( const Manifest::Manifest& manifest, const std::vector& entries); AppInstaller::Manifest::AppsAndFeaturesEntry GetAppsAndFeaturesEntry(); private: PortableARPEntry m_portableARPEntry; std::vector m_desiredEntries; std::vector m_expectedEntries; std::stringstream m_stream; std::string GetStringValue(PortableValueName valueName); std::filesystem::path GetPathValue(PortableValueName valueName); bool GetBoolValue(PortableValueName valueName); void SetExpectedState(); void RegisterARPEntry(); void ApplyDesiredState(); void InstallFile(AppInstaller::Portable::PortableFileEntry& desiredState); void RemoveFile(AppInstaller::Portable::PortableFileEntry& desiredState); void CreateTargetInstallDirectory(); void RemoveInstallDirectory(); void AddToPathVariable(std::filesystem::path value); void RemoveFromPathVariable(std::filesystem::path value); }; } ================================================ FILE: src/AppInstallerCLICore/PropertySheet.props ================================================ ================================================ FILE: src/AppInstallerCLICore/Public/AppInstallerCLICore.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once namespace AppInstaller::CLI { // The core function to act against command line input. int CoreMain(int argc, wchar_t const** argv); // Initializes the Windows Package Manager COM server. void ServerInitialize(); // Initializations for InProc invocation. void InProcInitialize(); } ================================================ FILE: src/AppInstallerCLICore/Public/ConfigurationSetProcessorFactoryRemoting.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::CLI::ConfigurationRemoting { // The processor engine being used by the factory. enum class ProcessorEngine { // An unknown processor. Unknown, // Uses PowerShell DSC v2. PowerShell, // Uses DSC v3. DSCv3, }; std::wstring_view ToString(ProcessorEngine value); // Determines the appropriate processor engine to use for the given configuration set. ProcessorEngine DetermineProcessorEngine(winrt::Microsoft::Management::Configuration::ConfigurationSet set); // Creates a factory in another process winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory CreateOutOfProcessFactory(ProcessorEngine processorEngine, bool useRunAs = false, const std::string& properties = {}, const std::string& restrictions = {}); // Creates a factory that can route configurations to the appropriate internal factory. winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory CreateDynamicRuntimeFactory(ProcessorEngine processorEngine); // The property names used with IMap property semantics of remote factories. enum class PropertyName { // The path to the dsc.exe executable. // Read / Write DscExecutablePath, // The path to the dsc.exe executable, as discovered. // Read only. FoundDscExecutablePath, // Whether to request detailed traces from the processor. // Read / Write DiagnosticTraceEnabled, // Getting this value pumps the state machine to determine the best DSC to use. // We must respond to the value it returns to properly transition states. // Read only. FindDscStateMachine, }; // Gets the string for a property name. winrt::hstring ToHString(PropertyName name); } // Export for use by the out of process factory server to report its initialization. HRESULT WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(HRESULT result, void* factory, LPWSTR staticsCallback, LPWSTR completionEventName, DWORD parentProcessId); ================================================ FILE: src/AppInstallerCLICore/Public/ShutdownMonitoring.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::ShutdownMonitoring { // Type to contain the CTRL signal and window messages handler. struct TerminationSignalHandler { TerminationSignalHandler(); ~TerminationSignalHandler(); // Gets the singleton handler. static std::shared_ptr Instance(); // Add a termination listener. void AddListener(ICancellable* cancellable); // Remove a termination listener. void RemoveListener(ICancellable* cancellable); // Add or remove the listener based on `enabled`. static void EnableListener(bool enabled, ICancellable* cancellable); // Gets whether the signal handler is enabled. static bool Enabled(); // Sets whether the signal handler is enabled; the default is true. // When set to false, the signal handler instance will not create signal listeners when created. static void Enabled(bool enabled); #ifndef AICLI_DISABLE_TEST_HOOKS // Gets the window handle for the message window. HWND GetWindowHandle() const; #endif private: void StartAppShutdown(); static BOOL WINAPI StaticCtrlHandlerFunction(DWORD ctrlType); static LRESULT WINAPI WindowMessageProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL CtrlHandlerFunction(DWORD ctrlType); // Terminates the currently attached contexts. // Returns FALSE if no contexts attached; TRUE otherwise. BOOL InformListeners(CancelReason reason, bool force); void CreateWindowAndStartMessageLoop(); std::mutex m_listenersLock; std::vector m_listeners; wil::unique_event m_messageQueueReady; wil::unique_hwnd m_windowHandle; std::thread m_windowThread; }; // Coordinates shutdown across server components struct ServerShutdownSynchronization { using ShutdownCompleteCallback = void (*)(); // Initializes the monitoring system and sets up a callback to be invoked when shutdown is completed. static void Initialize(ShutdownCompleteCallback callback, bool createTerminationSignalHandler = true); // "Interface" for a single component to synchronize with. struct ComponentSystem { // Initiate the shutdown process. // Components are expected to set flags to prevent any further work from beginning and return as quickly as possible. void (*BlockNewWork)(CancelReason reason) = nullptr; // Components are expected to cancel active or pending work as asynchronously as possible. void (*BeginShutdown)(CancelReason reason) = nullptr; // Components wait until all active and pending work have completed their void (*Wait)() = nullptr; }; // Adds a component to the system. static void AddComponent(const ComponentSystem& component); // Waits for the shutdown to complete. static bool WaitForShutdown(std::optional timeout = std::nullopt); private: ServerShutdownSynchronization() = default; ~ServerShutdownSynchronization(); friend TerminationSignalHandler; static ServerShutdownSynchronization& Instance(); // Runs the actual shutdown process and invokes the callback. void SynchronizeShutdown(CancelReason reason); // Listens for a termination signal. void Signal(CancelReason reason); ShutdownCompleteCallback m_callback = nullptr; std::mutex m_componentsLock; std::vector m_components; std::mutex m_threadLock; std::thread m_shutdownThread; wil::slim_event_manual_reset m_shutdownComplete; }; } ================================================ FILE: src/AppInstallerCLICore/Resources.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Resources.h" using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Resource { Utility::LocIndView GetFixedString(FixedString fs) { switch (fs) { case FixedString::ProductName: return "Windows Package Manager"_liv; } THROW_HR(E_UNEXPECTED); } } ================================================ FILE: src/AppInstallerCLICore/Resources.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::CLI::Resource { using AppInstaller::StringResource::StringId; using AppInstaller::Resource::LocString; // Resource string identifiers. // This list can mostly be generated by the following PowerShell: // > [xml]$res = Get-Content // > $res.root.data.name | % { "WINGET_DEFINE_RESOURCE_STRINGID($_);" } // struct String { WINGET_DEFINE_RESOURCE_STRINGID(AcceptPackageAgreementsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(AcceptSourceAgreementsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(AdjoinedNotFlagError); WINGET_DEFINE_RESOURCE_STRINGID(AdjoinedNotFoundError); WINGET_DEFINE_RESOURCE_STRINGID(AdminSettingDisabled); WINGET_DEFINE_RESOURCE_STRINGID(AdminSettingDisableDescription); WINGET_DEFINE_RESOURCE_STRINGID(AdminSettingEnabled); WINGET_DEFINE_RESOURCE_STRINGID(AdminSettingEnableDescription); WINGET_DEFINE_RESOURCE_STRINGID(AdminSettingHeader); WINGET_DEFINE_RESOURCE_STRINGID(AllowRebootArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ArchitectureArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ArchiveFailedMalwareScan); WINGET_DEFINE_RESOURCE_STRINGID(ArchiveFailedMalwareScanOverridden); WINGET_DEFINE_RESOURCE_STRINGID(ArgumentForSinglePackageProvidedWithMultipleQueries); WINGET_DEFINE_RESOURCE_STRINGID(AuthenticationAccountArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(AuthenticationModeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(AvailableArguments); WINGET_DEFINE_RESOURCE_STRINGID(AvailableCommandAliases); WINGET_DEFINE_RESOURCE_STRINGID(AvailableCommands); WINGET_DEFINE_RESOURCE_STRINGID(AvailableHeader); WINGET_DEFINE_RESOURCE_STRINGID(AvailableOptions); WINGET_DEFINE_RESOURCE_STRINGID(AvailableSubcommands); WINGET_DEFINE_RESOURCE_STRINGID(AvailableUpgrades); WINGET_DEFINE_RESOURCE_STRINGID(BothManifestAndSearchQueryProvided); WINGET_DEFINE_RESOURCE_STRINGID(Cancelled); WINGET_DEFINE_RESOURCE_STRINGID(CancellingOperation); WINGET_DEFINE_RESOURCE_STRINGID(ChannelArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ClientVersionMismatchError); WINGET_DEFINE_RESOURCE_STRINGID(Command); WINGET_DEFINE_RESOURCE_STRINGID(CommandArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(CommandDoesNotSupportResumeMessage); WINGET_DEFINE_RESOURCE_STRINGID(CommandLineArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(CommandRequiresAdmin); WINGET_DEFINE_RESOURCE_STRINGID(CompleteCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(CompleteCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationAcceptWarningArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationAllUsersElevated); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationApply); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationApplyingUnit); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationAssert); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationDescriptionWasTruncated); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportAddingToFile); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportFailed); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportFailedToGetUnitProcessors); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportingUnit); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportInstallRequiredModule); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportInstallRequiredModuleFailed); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportSuccessful); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportUnitStart); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationExportUnitFailed); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFailedToApply); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFailedToGetDetails); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFailedToTest); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFieldInvalidType); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFieldInvalidValue); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFieldMissing); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFileArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFileEmpty); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFileInvalid); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFileInvalidYAML); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationFileVersionUnknown); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationGettingDetails); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationGettingResourceSettings); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationGettingUnitProcessors); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationHistoryEmpty); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationHistoryItemArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationHistoryItemNotFound); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationHistoryRemoveArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationInDesiredState); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationInform); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationInitializing); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationInstallDscPackage); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationInstallDscPackageFailed); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationLocal); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationModuleNameOnly); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationModulePath); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationModulePathArgError); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationModules); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationModuleWithDetails); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationNoTestRun); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationNotInDesiredState); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationProcessorPath); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationReadingConfigFile); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationSetStateCompleted); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationSetStateInProgress); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationSetStatePending); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationSetStateUnknown); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationSettings); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationStatusWatchArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationSuccessfullyApplied); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitSuccessfullyApplied); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationSuppressPrologueArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnexpectedTestResult); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitAssertHadNegativeResult); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailed); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailedConfigSet); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailedDuringGet); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailedDuringSet); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailedDuringTest); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailedInternal); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailedPrecondition); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailedSystemState); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitFailedUnitProcessing); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitHasDuplicateIdentifier); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitHasMissingDependency); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitImportModuleAdmin); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitIsPartOfDependencyCycle); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitManuallySkipped); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitModuleConflict); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitModuleImportFailed); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitModuleNotProvidedWarning); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitMultipleMatches); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitNeedsPrereleaseWarning); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitNotFound); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitNotFoundInModule); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitNotInCatalogWarning); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitNotPublicWarning); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitNotRunDueToDependency); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitNotRunDueToFailedAssert); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitReturnedInvalidResult); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitSettingConfigRoot); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitSkipped); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitStateCompleted); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitStateInProgress); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitStatePending); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitStateSkipped); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationUnitStateUnknown); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationValidationFoundNoIssues); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationWaitingOnAnother); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationWarning); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationWarningPromptApply); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationWarningPromptTest); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationWarningSetViewTruncated); WINGET_DEFINE_RESOURCE_STRINGID(ConfigurationWarningValueTruncated); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportAll); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportArgumentConflictWithAllError); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportArgumentRequiredError); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportModule); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportPackageId); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportResource); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportUnitDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureExportUnitInstallDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListApplyBegun); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListApplyEnded); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListFirstApplied); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListIdentifier); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListName); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListOrigin); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListPath); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListResult); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListResultDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListState); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureListUnit); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureShowCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureShowCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureTestCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureTestCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureValidateCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConfigureValidateCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ConvertInstallFlowToUpgrade); WINGET_DEFINE_RESOURCE_STRINGID(CorrelationArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(CountArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(CountOutOfBoundsError); WINGET_DEFINE_RESOURCE_STRINGID(CustomSwitchesArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowContainsLoop); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowDownload); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowInstall); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowNoInstallerFound); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowNoMatches); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowNoMinVersion); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowNoSuitableInstallerFound); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowPackageVersionNotFound); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowSourceNotFound); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowSourceTooManyMatches); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesManagementError); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesManagementExitMessage); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesSkippedMessage); WINGET_DEFINE_RESOURCE_STRINGID(DependencyArgumentMissing); WINGET_DEFINE_RESOURCE_STRINGID(DependencySourceArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(DisableAdminSettingFailed); WINGET_DEFINE_RESOURCE_STRINGID(DisabledByGroupPolicy); WINGET_DEFINE_RESOURCE_STRINGID(DisableInteractivityArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(Done); WINGET_DEFINE_RESOURCE_STRINGID(DownloadCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(DownloadCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(DownloadDirectoryArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(Downloading); WINGET_DEFINE_RESOURCE_STRINGID(DscCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscAdminSettingsResourceShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscAdminSettingsResourceLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscPackageResourceShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscPackageResourceLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionGet); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionSet); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionWhatIf); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionTest); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionDelete); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionExport); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionValidate); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionResolve); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionAdapter); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionSchema); WINGET_DEFINE_RESOURCE_STRINGID(DscResourceFunctionDescriptionManifest); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionExist); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionInDesiredState); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionAcceptAgreements); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionAdminSettingsSettings); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageId); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageSource); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageVersion); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageScope); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageMatchOption); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageUseLatest); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionPackageInstallMode); WINGET_DEFINE_RESOURCE_STRINGID(DscUserSettingsFileShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscUserSettingsFileLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionUserSettingsFileSettings); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionUserSettingsFileAction); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourceName); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourceArgument); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourceType); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourceTrustLevel); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourceExplicit); WINGET_DEFINE_RESOURCE_STRINGID(DscResourcePropertyDescriptionSourcePriority); WINGET_DEFINE_RESOURCE_STRINGID(DscSourceResourceShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(DscSourceResourceLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(EnableAdminSettingFailed); WINGET_DEFINE_RESOURCE_STRINGID(EnableWindowsFeaturesSuccess); WINGET_DEFINE_RESOURCE_STRINGID(EnablingWindowsFeature); WINGET_DEFINE_RESOURCE_STRINGID(ErrorCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ErrorCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ErrorInputArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ErrorNumberIsTooLarge); WINGET_DEFINE_RESOURCE_STRINGID(ExactArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExperimentalArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExperimentalCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExperimentalCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExportCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExportCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExportedPackageRequiresLicenseAgreement); WINGET_DEFINE_RESOURCE_STRINGID(ExportIncludeVersionsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExportSourceArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExtendedFeaturesDisabledMessage); WINGET_DEFINE_RESOURCE_STRINGID(ExtendedFeaturesDisableMessage); WINGET_DEFINE_RESOURCE_STRINGID(ExtendedFeaturesDisablingMessage); WINGET_DEFINE_RESOURCE_STRINGID(ExtendedFeaturesEnableArgumentError); WINGET_DEFINE_RESOURCE_STRINGID(ExtendedFeaturesEnabledMessage); WINGET_DEFINE_RESOURCE_STRINGID(ExtendedFeaturesEnableMessage); WINGET_DEFINE_RESOURCE_STRINGID(ExtendedFeaturesEnablingMessage); WINGET_DEFINE_RESOURCE_STRINGID(ExtendedFeaturesNotEnabledMessage); WINGET_DEFINE_RESOURCE_STRINGID(ExternalDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ExtractArchiveFailed); WINGET_DEFINE_RESOURCE_STRINGID(ExtractArchiveSucceeded); WINGET_DEFINE_RESOURCE_STRINGID(ExtractingArchive); WINGET_DEFINE_RESOURCE_STRINGID(ExtraPositionalError); WINGET_DEFINE_RESOURCE_STRINGID(FailedToEnableWindowsFeature); WINGET_DEFINE_RESOURCE_STRINGID(FailedToEnableWindowsFeatureOverridden); WINGET_DEFINE_RESOURCE_STRINGID(FailedToEnableWindowsFeatureOverrideRequired); WINGET_DEFINE_RESOURCE_STRINGID(FailedToInitiateReboot); WINGET_DEFINE_RESOURCE_STRINGID(FailedToRefreshPathWarning); WINGET_DEFINE_RESOURCE_STRINGID(FeatureDisabledByAdminSettingMessage); WINGET_DEFINE_RESOURCE_STRINGID(FeatureDisabledMessage); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesDisabled); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesEnabled); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesFeature); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesLink); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesMessage); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesMessageDisabledByBuild); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesMessageDisabledByPolicy); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesProperty); WINGET_DEFINE_RESOURCE_STRINGID(FeaturesStatus); WINGET_DEFINE_RESOURCE_STRINGID(FileArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(FileNotFound); WINGET_DEFINE_RESOURCE_STRINGID(FilesRemainInInstallDirectory); WINGET_DEFINE_RESOURCE_STRINGID(FlagContainAdjoinedError); WINGET_DEFINE_RESOURCE_STRINGID(FontAlreadyInstalled); WINGET_DEFINE_RESOURCE_STRINGID(FontCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(FontCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(FontFace); WINGET_DEFINE_RESOURCE_STRINGID(FontFaces); WINGET_DEFINE_RESOURCE_STRINGID(FontFamily); WINGET_DEFINE_RESOURCE_STRINGID(FontFamilyNameArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(FontFileNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(FontDetailsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(FontFilePaths); WINGET_DEFINE_RESOURCE_STRINGID(FontInstallFailed); WINGET_DEFINE_RESOURCE_STRINGID(FontListCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(FontListCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(FontPackage); WINGET_DEFINE_RESOURCE_STRINGID(FontRollbackFailed); WINGET_DEFINE_RESOURCE_STRINGID(FontStatus); WINGET_DEFINE_RESOURCE_STRINGID(FontStatusCorrupt); WINGET_DEFINE_RESOURCE_STRINGID(FontStatusOK); WINGET_DEFINE_RESOURCE_STRINGID(FontStatusUnknown); WINGET_DEFINE_RESOURCE_STRINGID(FontTitle); WINGET_DEFINE_RESOURCE_STRINGID(FontUninstallFailed); WINGET_DEFINE_RESOURCE_STRINGID(FontValidationFailed); WINGET_DEFINE_RESOURCE_STRINGID(FontVersion); WINGET_DEFINE_RESOURCE_STRINGID(FontWinGetSupported); WINGET_DEFINE_RESOURCE_STRINGID(ForceArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(GatedVersionArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(GetManifestResultVersionNotFound); WINGET_DEFINE_RESOURCE_STRINGID(HashCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(HashCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(HashOverrideArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(HeaderArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(HeaderArgumentNotApplicableForNonRestSourceWarning); WINGET_DEFINE_RESOURCE_STRINGID(HeaderArgumentNotApplicableWithoutSource); WINGET_DEFINE_RESOURCE_STRINGID(HelpArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(HelpForDetails); WINGET_DEFINE_RESOURCE_STRINGID(HelpLinkPreamble); WINGET_DEFINE_RESOURCE_STRINGID(IdArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IgnoreLocalArchiveMalwareScanArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IgnoreResumeLimitArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IgnoreWarningsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportCommandReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ImportCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportFileArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportFileHasInvalidSchema); WINGET_DEFINE_RESOURCE_STRINGID(ImportIgnorePackageVersionsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportIgnoreUnavailableArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ImportInstallFailed); WINGET_DEFINE_RESOURCE_STRINGID(ImportSourceNotInstalled); WINGET_DEFINE_RESOURCE_STRINGID(IncludePinnedArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IncludePinnedInListArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IncludeUnknownArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IncludeUnknownInListArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(IncompatibleArgumentsProvided); WINGET_DEFINE_RESOURCE_STRINGID(InitiatingReboot); WINGET_DEFINE_RESOURCE_STRINGID(InstallAbandoned); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimer1); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimer2); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimerMSStore); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageNotAvailable); WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageVersionNotAvailable); WINGET_DEFINE_RESOURCE_STRINGID(InstalledScopeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallerAbortsTerminal); WINGET_DEFINE_RESOURCE_STRINGID(InstallerBlockedByPolicy); WINGET_DEFINE_RESOURCE_STRINGID(InstallerDownloadAuthenticationFailed); WINGET_DEFINE_RESOURCE_STRINGID(InstallerDownloadAuthenticationNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(InstallerDownloadCommandProhibited); WINGET_DEFINE_RESOURCE_STRINGID(InstallerDownloaded); WINGET_DEFINE_RESOURCE_STRINGID(InstallerDownloadRequiresAuthentication); WINGET_DEFINE_RESOURCE_STRINGID(InstallerDownloads); WINGET_DEFINE_RESOURCE_STRINGID(InstallerElevationExpected); WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedSecurityCheck); WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedVirusScan); WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedWithCode); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchAdminBlock); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchError); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchOverridden); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchOverrideRequired); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashVerified); WINGET_DEFINE_RESOURCE_STRINGID(InstallerLogAvailable); WINGET_DEFINE_RESOURCE_STRINGID(InstallerProhibitsElevation); WINGET_DEFINE_RESOURCE_STRINGID(InstallerRequiresInstallLocation); WINGET_DEFINE_RESOURCE_STRINGID(InstallersAbortTerminal); WINGET_DEFINE_RESOURCE_STRINGID(InstallersRequireInstallLocation); WINGET_DEFINE_RESOURCE_STRINGID(InstallerTypeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallerZeroByteFile); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowInstallSuccess); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowRegistrationDeferred); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeAlreadyInstalled); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeBlockedByPolicy); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeCancelledByUser); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeContactSupport); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeCustomError); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeDiskFull); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeDowngrade); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeFileInUse); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeInstallInProgress); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeInsufficientMemory); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeInvalidParameter); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeMissingDependency); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeNoNetwork); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodePackageInUse); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodePackageInUseByApplication); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeRebootInitiated); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeRebootRequiredForInstall); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeRebootRequiredToFinish); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeSystemNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowStartingPackageInstall); WINGET_DEFINE_RESOURCE_STRINGID(InstallFullPackageDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallLocationNotProvided); WINGET_DEFINE_RESOURCE_STRINGID(InstallScopeDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallStubPackageDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallWaitingOnAnother); WINGET_DEFINE_RESOURCE_STRINGID(InteractiveArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(InvalidAliasError); WINGET_DEFINE_RESOURCE_STRINGID(InvalidArgumentSpecifierError); WINGET_DEFINE_RESOURCE_STRINGID(InvalidArgumentValueError); WINGET_DEFINE_RESOURCE_STRINGID(InvalidArgumentValueErrorWithoutValidValues); WINGET_DEFINE_RESOURCE_STRINGID(InvalidArgumentWithoutQueryError); WINGET_DEFINE_RESOURCE_STRINGID(InvalidJsonFile); WINGET_DEFINE_RESOURCE_STRINGID(InvalidNameError); WINGET_DEFINE_RESOURCE_STRINGID(InvalidPathToNestedInstaller); WINGET_DEFINE_RESOURCE_STRINGID(KeyDirectoriesHeader); WINGET_DEFINE_RESOURCE_STRINGID(LicenseAgreement); WINGET_DEFINE_RESOURCE_STRINGID(Links); WINGET_DEFINE_RESOURCE_STRINGID(ListCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ListCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ListDetailsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(LocaleArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(LocationArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(LogArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(Logs); WINGET_DEFINE_RESOURCE_STRINGID(MainCopyrightNotice); WINGET_DEFINE_RESOURCE_STRINGID(MainHomepage); WINGET_DEFINE_RESOURCE_STRINGID(ManifestArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ManifestValidationFail); WINGET_DEFINE_RESOURCE_STRINGID(ManifestValidationSuccess); WINGET_DEFINE_RESOURCE_STRINGID(ManifestValidationWarning); WINGET_DEFINE_RESOURCE_STRINGID(McpCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(McpCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(McpConfigurationPreamble); WINGET_DEFINE_RESOURCE_STRINGID(MissingArgumentError); WINGET_DEFINE_RESOURCE_STRINGID(ModifiedPathRequiresShellRestart); WINGET_DEFINE_RESOURCE_STRINGID(MonikerArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(MsixArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(MsixSignatureHashFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreAppBlocked); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadAuthenticationNotice); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadDependencyPackages); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetDownloadInfo); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetDownloadInfoFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetLicense); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetLicenseFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetLicenseForbidden); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetLicenseSuccess); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadMainPackages); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadMultiplePackagesNotice); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadNoApplicablePackageFound); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageDownloaded); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageDownloadFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageDownloadNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageDownloadSuccess); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageHashMismatch); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageHashVerified); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadRenameNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallOrUpdateFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallTryGetEntitlement); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreRepairFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreStoreClientBlocked); WINGET_DEFINE_RESOURCE_STRINGID(MultipleExclusiveArgumentsProvided); WINGET_DEFINE_RESOURCE_STRINGID(MultipleInstalledPackagesFound); WINGET_DEFINE_RESOURCE_STRINGID(MultiplePackagesFound); WINGET_DEFINE_RESOURCE_STRINGID(MultiplePackagesFoundFilteredBySourcePriority); WINGET_DEFINE_RESOURCE_STRINGID(MultipleUnsupportedNestedInstallersSpecified); WINGET_DEFINE_RESOURCE_STRINGID(MultiQueryArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(MultiQueryPackageAlreadyInstalled); WINGET_DEFINE_RESOURCE_STRINGID(MultiQueryPackageNotFound); WINGET_DEFINE_RESOURCE_STRINGID(MultiQuerySearchFailed); WINGET_DEFINE_RESOURCE_STRINGID(MultiQuerySearchFoundMultiple); WINGET_DEFINE_RESOURCE_STRINGID(NameArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(NestedInstallerNotFound); WINGET_DEFINE_RESOURCE_STRINGID(NestedInstallerNotSpecified); WINGET_DEFINE_RESOURCE_STRINGID(NestedInstallerNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(NoAdminRepairForUserScopePackage); WINGET_DEFINE_RESOURCE_STRINGID(NoApplicableInstallers); WINGET_DEFINE_RESOURCE_STRINGID(NoExperimentalFeaturesMessage); WINGET_DEFINE_RESOURCE_STRINGID(NoInstalledFontFound); WINGET_DEFINE_RESOURCE_STRINGID(NoInstalledPackageFound); WINGET_DEFINE_RESOURCE_STRINGID(NoPackageFound); WINGET_DEFINE_RESOURCE_STRINGID(NoPackageSelectionArgumentProvided); WINGET_DEFINE_RESOURCE_STRINGID(NoPackagesFoundInImportFile); WINGET_DEFINE_RESOURCE_STRINGID(NoProxyArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(NoProgressArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(NoRepairInfoFound); WINGET_DEFINE_RESOURCE_STRINGID(Notes); WINGET_DEFINE_RESOURCE_STRINGID(NoUninstallInfoFound); WINGET_DEFINE_RESOURCE_STRINGID(NoUpgradeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(NoVTArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(OpenLogsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(OpenSourceFailedNoMatch); WINGET_DEFINE_RESOURCE_STRINGID(OpenSourceFailedNoMatchHelp); WINGET_DEFINE_RESOURCE_STRINGID(OpenSourceFailedNoSourceDefined); WINGET_DEFINE_RESOURCE_STRINGID(Options); WINGET_DEFINE_RESOURCE_STRINGID(OSVersionDescription); WINGET_DEFINE_RESOURCE_STRINGID(OutputDirectoryArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(OutputFileArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(OverrideArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(OverwritingExistingFileAtMessage); WINGET_DEFINE_RESOURCE_STRINGID(Package); WINGET_DEFINE_RESOURCE_STRINGID(PackageAgreementsNotAgreedTo); WINGET_DEFINE_RESOURCE_STRINGID(PackageAgreementsPrompt); WINGET_DEFINE_RESOURCE_STRINGID(PackageAlreadyInstalled); WINGET_DEFINE_RESOURCE_STRINGID(PackageDependencies); WINGET_DEFINE_RESOURCE_STRINGID(PackageIsPinned); WINGET_DEFINE_RESOURCE_STRINGID(PackageRequiresDependencies); WINGET_DEFINE_RESOURCE_STRINGID(PendingWorkError); WINGET_DEFINE_RESOURCE_STRINGID(PinAddBlockingArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinAddCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinAddCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinAdded); WINGET_DEFINE_RESOURCE_STRINGID(PinAlreadyExists); WINGET_DEFINE_RESOURCE_STRINGID(PinCannotOpenIndex); WINGET_DEFINE_RESOURCE_STRINGID(PinCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinDoesNotExist); WINGET_DEFINE_RESOURCE_STRINGID(PinExistsOverwriting); WINGET_DEFINE_RESOURCE_STRINGID(PinExistsUseForceArg); WINGET_DEFINE_RESOURCE_STRINGID(PinInstalledArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinInstalledSource); WINGET_DEFINE_RESOURCE_STRINGID(PinListCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinListCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinNoPinsExist); WINGET_DEFINE_RESOURCE_STRINGID(PinRemoveCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinRemoveCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinRemovedSuccessfully); WINGET_DEFINE_RESOURCE_STRINGID(PinResetCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinResetCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(PinResetSuccessful); WINGET_DEFINE_RESOURCE_STRINGID(PinResettingAll); WINGET_DEFINE_RESOURCE_STRINGID(PinResetUseForceArg); WINGET_DEFINE_RESOURCE_STRINGID(PinType); WINGET_DEFINE_RESOURCE_STRINGID(PinVersion); WINGET_DEFINE_RESOURCE_STRINGID(PlatformArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PoliciesPolicy); WINGET_DEFINE_RESOURCE_STRINGID(PortableAliasAdded); WINGET_DEFINE_RESOURCE_STRINGID(PortableHashMismatchOverridden); WINGET_DEFINE_RESOURCE_STRINGID(PortableHashMismatchOverrideRequired); WINGET_DEFINE_RESOURCE_STRINGID(PortableInstallFailed); WINGET_DEFINE_RESOURCE_STRINGID(PortableLinksMachine); WINGET_DEFINE_RESOURCE_STRINGID(PortableLinksUser); WINGET_DEFINE_RESOURCE_STRINGID(PortablePackageAlreadyExists); WINGET_DEFINE_RESOURCE_STRINGID(PortableRegistryCollisionOverridden); WINGET_DEFINE_RESOURCE_STRINGID(PortableRoot); WINGET_DEFINE_RESOURCE_STRINGID(PortableRoot86); WINGET_DEFINE_RESOURCE_STRINGID(PortableRootUser); WINGET_DEFINE_RESOURCE_STRINGID(PositionArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PreserveArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PressEnterToContinue); WINGET_DEFINE_RESOURCE_STRINGID(PrivacyStatement); WINGET_DEFINE_RESOURCE_STRINGID(ProductCodeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PromptForInstallRoot); WINGET_DEFINE_RESOURCE_STRINGID(PromptOptionNo); WINGET_DEFINE_RESOURCE_STRINGID(PromptOptionYes); WINGET_DEFINE_RESOURCE_STRINGID(PromptToProceed); WINGET_DEFINE_RESOURCE_STRINGID(ProxyArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PurgeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PurgeInstallDirectory); WINGET_DEFINE_RESOURCE_STRINGID(QueryArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(RainbowArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(RebootRequiredToEnableWindowsFeatureOverridden); WINGET_DEFINE_RESOURCE_STRINGID(RebootRequiredToEnableWindowsFeatureOverrideRequired); WINGET_DEFINE_RESOURCE_STRINGID(RelatedLink); WINGET_DEFINE_RESOURCE_STRINGID(RenameArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(RepairAbandoned); WINGET_DEFINE_RESOURCE_STRINGID(RepairCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(RepairCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(RepairDifferentInstallTechnology); WINGET_DEFINE_RESOURCE_STRINGID(RepairFailedWithCode); WINGET_DEFINE_RESOURCE_STRINGID(RepairFlowNoMatchingVersion); WINGET_DEFINE_RESOURCE_STRINGID(RepairFlowRepairSuccess); WINGET_DEFINE_RESOURCE_STRINGID(RepairFlowReturnCodeSystemNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(RepairFlowStartingPackageRepair); WINGET_DEFINE_RESOURCE_STRINGID(RepairOperationNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(ReparsePointsNotSupportedError); WINGET_DEFINE_RESOURCE_STRINGID(ReportIdentityForAgreements); WINGET_DEFINE_RESOURCE_STRINGID(ReportIdentityFound); WINGET_DEFINE_RESOURCE_STRINGID(RequiredArgError); WINGET_DEFINE_RESOURCE_STRINGID(ReservedFilenameError); WINGET_DEFINE_RESOURCE_STRINGID(ResetAdminSettingFailed); WINGET_DEFINE_RESOURCE_STRINGID(ResetAdminSettingSucceeded); WINGET_DEFINE_RESOURCE_STRINGID(ResetAllAdminSettingsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ResetAllAdminSettingsSucceeded); WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ResumeCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ResumeIdArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ResumeIdNotFoundError); WINGET_DEFINE_RESOURCE_STRINGID(ResumeLimitExceeded); WINGET_DEFINE_RESOURCE_STRINGID(ResumeStateDataNotFoundError); WINGET_DEFINE_RESOURCE_STRINGID(RetroArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SearchFailureError); WINGET_DEFINE_RESOURCE_STRINGID(SearchFailureErrorListMatches); WINGET_DEFINE_RESOURCE_STRINGID(SearchFailureErrorNoMatches); WINGET_DEFINE_RESOURCE_STRINGID(SearchFailureWarning); WINGET_DEFINE_RESOURCE_STRINGID(SearchId); WINGET_DEFINE_RESOURCE_STRINGID(SearchMatch); WINGET_DEFINE_RESOURCE_STRINGID(SearchName); WINGET_DEFINE_RESOURCE_STRINGID(SearchSource); WINGET_DEFINE_RESOURCE_STRINGID(SearchTruncated); WINGET_DEFINE_RESOURCE_STRINGID(SearchVersion); WINGET_DEFINE_RESOURCE_STRINGID(SeeLineAndColumn); WINGET_DEFINE_RESOURCE_STRINGID(SetAdminSettingFailed); WINGET_DEFINE_RESOURCE_STRINGID(SetAdminSettingSucceeded); WINGET_DEFINE_RESOURCE_STRINGID(SettingLoadFailure); WINGET_DEFINE_RESOURCE_STRINGID(SettingNameArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsExportCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsExportCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsResetCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsResetCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsSetCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsSetCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningField); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarnings); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningValue); WINGET_DEFINE_RESOURCE_STRINGID(SettingValueArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ShowChannel); WINGET_DEFINE_RESOURCE_STRINGID(ShowCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ShowCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelAgreements); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelAuthor); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelChannel); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelCopyright); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelCopyrightUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelDescription); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelDocumentation); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelExternalDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstallationNotes); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstaller); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstallerLocale); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstallerOfflineDistributionSupported); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstallerProductId); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstallerReleaseDate); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstallerSha256); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstallerType); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelInstallerUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelLicense); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelLicenseUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelMoniker); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelPackageDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelPackageUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelPrivacyUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelPublisher); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelPublisherSupportUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelPublisherUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelPurchaseUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelReleaseNotes); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelReleaseNotesUrl); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelTags); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelVersion); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelWindowsFeaturesDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ShowLabelWindowsLibrariesDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ShowListAvailableUpgrades); WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledArchitecture); WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledLocale); WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledLocation); WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledScope); WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstalledSource); WINGET_DEFINE_RESOURCE_STRINGID(ShowListInstallerCategory); WINGET_DEFINE_RESOURCE_STRINGID(ShowListLocalIdentifier); WINGET_DEFINE_RESOURCE_STRINGID(ShowListPackageFamilyName); WINGET_DEFINE_RESOURCE_STRINGID(ShowListProductCode); WINGET_DEFINE_RESOURCE_STRINGID(ShowListUpgradeCode); WINGET_DEFINE_RESOURCE_STRINGID(ShowVersion); WINGET_DEFINE_RESOURCE_STRINGID(SilentArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SingleCharAfterDashError); WINGET_DEFINE_RESOURCE_STRINGID(SkipDependenciesArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesOnlyArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesOnlyMessage); WINGET_DEFINE_RESOURCE_STRINGID(SkipMicrosoftStorePackageLicenseArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceAddAlreadyExistsDifferentArg); WINGET_DEFINE_RESOURCE_STRINGID(SourceAddAlreadyExistsDifferentName); WINGET_DEFINE_RESOURCE_STRINGID(SourceAddAlreadyExistsMatch); WINGET_DEFINE_RESOURCE_STRINGID(SourceAddBegin); WINGET_DEFINE_RESOURCE_STRINGID(SourceAddCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceAddCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceAddFailedAuthenticationNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(SourceAddOpenSourceFailed); WINGET_DEFINE_RESOURCE_STRINGID(SourceAgreementsMarketMessage); WINGET_DEFINE_RESOURCE_STRINGID(SourceAgreementsNotAgreedTo); WINGET_DEFINE_RESOURCE_STRINGID(SourceAgreementsPrompt); WINGET_DEFINE_RESOURCE_STRINGID(SourceAgreementsTitle); WINGET_DEFINE_RESOURCE_STRINGID(SourceArgArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceEditCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceEditCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceEditExplicitArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceEditNewValue); WINGET_DEFINE_RESOURCE_STRINGID(SourceEditNoChanges); WINGET_DEFINE_RESOURCE_STRINGID(SourceEditOldValue); WINGET_DEFINE_RESOURCE_STRINGID(SourceEditOne); WINGET_DEFINE_RESOURCE_STRINGID(SourceExplicitArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceExportCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceExportCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceListAdditionalSource); WINGET_DEFINE_RESOURCE_STRINGID(SourceListAllowedSource); WINGET_DEFINE_RESOURCE_STRINGID(SourceListArg); WINGET_DEFINE_RESOURCE_STRINGID(SourceListCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceListCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceListData); WINGET_DEFINE_RESOURCE_STRINGID(SourceListExplicit); WINGET_DEFINE_RESOURCE_STRINGID(SourceListField); WINGET_DEFINE_RESOURCE_STRINGID(SourceListIdentifier); WINGET_DEFINE_RESOURCE_STRINGID(SourceListName); WINGET_DEFINE_RESOURCE_STRINGID(SourceListNoneFound); WINGET_DEFINE_RESOURCE_STRINGID(SourceListNoSources); WINGET_DEFINE_RESOURCE_STRINGID(SourceListPriority); WINGET_DEFINE_RESOURCE_STRINGID(SourceListTrustLevel); WINGET_DEFINE_RESOURCE_STRINGID(SourceListType); WINGET_DEFINE_RESOURCE_STRINGID(SourceListUpdated); WINGET_DEFINE_RESOURCE_STRINGID(SourceListUpdatedNever); WINGET_DEFINE_RESOURCE_STRINGID(SourceListValue); WINGET_DEFINE_RESOURCE_STRINGID(SourceNameArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenFailedSuggestion); WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenPredefinedFailedSuggestion); WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenWithFailedUpdate); WINGET_DEFINE_RESOURCE_STRINGID(SourcePriorityArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveAll); WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveOne); WINGET_DEFINE_RESOURCE_STRINGID(SourceRequiresAuthentication); WINGET_DEFINE_RESOURCE_STRINGID(SourceResetAll); WINGET_DEFINE_RESOURCE_STRINGID(SourceResetCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceResetCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceResetForceArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceResetListAndOverridePreamble); WINGET_DEFINE_RESOURCE_STRINGID(SourceResetOne); WINGET_DEFINE_RESOURCE_STRINGID(SourceTrustLevelArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceTypeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateAll); WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateOne); WINGET_DEFINE_RESOURCE_STRINGID(StateDisabled); WINGET_DEFINE_RESOURCE_STRINGID(StateEnabled); WINGET_DEFINE_RESOURCE_STRINGID(StateHeader); WINGET_DEFINE_RESOURCE_STRINGID(SystemArchitecture); WINGET_DEFINE_RESOURCE_STRINGID(TagArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(TargetVersionArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ThankYou); WINGET_DEFINE_RESOURCE_STRINGID(ThirdPartSoftwareNotices); WINGET_DEFINE_RESOURCE_STRINGID(ToolDescription); WINGET_DEFINE_RESOURCE_STRINGID(ToolInfoArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ToolVersionArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(TooManyArgError); WINGET_DEFINE_RESOURCE_STRINGID(TooManyBehaviorsError); WINGET_DEFINE_RESOURCE_STRINGID(UnableToPurgeInstallDirectory); WINGET_DEFINE_RESOURCE_STRINGID(Unavailable); WINGET_DEFINE_RESOURCE_STRINGID(UnexpectedErrorExecutingCommand); WINGET_DEFINE_RESOURCE_STRINGID(UninstallAbandoned); WINGET_DEFINE_RESOURCE_STRINGID(UninstallAllVersionsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(UninstallCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(UninstallCommandReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(UninstallCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(UninstallFailedDueToMultipleVersions); WINGET_DEFINE_RESOURCE_STRINGID(UninstallFailedWithCode); WINGET_DEFINE_RESOURCE_STRINGID(UninstallFlowStartingPackageUninstall); WINGET_DEFINE_RESOURCE_STRINGID(UninstallFlowUninstallSuccess); WINGET_DEFINE_RESOURCE_STRINGID(UninstallPreviousArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(UnrecognizedCommand); WINGET_DEFINE_RESOURCE_STRINGID(UnsupportedArgument); WINGET_DEFINE_RESOURCE_STRINGID(UpdateAllArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(UpdateNoPackagesFound); WINGET_DEFINE_RESOURCE_STRINGID(UpdateNoPackagesFoundReason); WINGET_DEFINE_RESOURCE_STRINGID(UpdateNotApplicable); WINGET_DEFINE_RESOURCE_STRINGID(UpdateNotApplicableReason); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeAvailableForPinned); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeBlockedByManifest); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeBlockedByPinCount); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeDifferentInstallTechnology); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeDifferentInstallTechnologyInNewerVersions); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeIsPinned); WINGET_DEFINE_RESOURCE_STRINGID(UpgradePinnedByUserCount); 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); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(ValidateManifestArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(VerboseLogsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(VerifyFileFailedIsDirectory); WINGET_DEFINE_RESOURCE_STRINGID(VerifyFileFailedNotExist); WINGET_DEFINE_RESOURCE_STRINGID(VerifyFileSignedMsix); WINGET_DEFINE_RESOURCE_STRINGID(VerifyPathFailedNotExist); WINGET_DEFINE_RESOURCE_STRINGID(VersionArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(VersionsArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(WaitArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(WindowsFeatureNotFound); WINGET_DEFINE_RESOURCE_STRINGID(WindowsFeaturesDependencies); WINGET_DEFINE_RESOURCE_STRINGID(WindowsLibrariesDependencies); 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); }; // Fixed strings are not localized, but we use a similar system to prevent duplication enum class FixedString { ProductName, }; Utility::LocIndView GetFixedString(FixedString fs); } inline std::ostream& operator<<(std::ostream& out, AppInstaller::CLI::Resource::FixedString fs) { return (out << GetFixedString(fs)); } ================================================ FILE: src/AppInstallerCLICore/Search/Search.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include // TODO: This code is expected to eventually be placed into its own DLL to support the CLI // and OOP COM server for use by OS integration points. // For now we will just maintain the ABI with helper C++ wrappers for client use. // "C" ABI using AICLIString = wchar_t*; using AICLICString = wchar_t const*; using RepositoryHandle = void*; using ApplicationHandle = void*; using ManifestHandle = void*; winrt::hresult aicliGetApplicationById(RepositoryHandle repo, AICLICString id, ApplicationHandle* app); winrt::hresult aicliGetManifestByVersion(ApplicationHandle app, AICLICString version, ManifestHandle* man); winrt::hresult aicliGetManifestContents(ManifestHandle man, AICLICString* contents); void aicliFreeRepository(RepositoryHandle repo); void aicliFreeApplication(ApplicationHandle app); void aicliFreeManifest(ManifestHandle manifest); // C++ wrapper namespace AppInstaller::CLI { namespace details { struct RepositoryDeleter { void operator()(RepositoryHandle repo) { aicliFreeRepository(repo); } }; struct ApplicationDeleter { void operator()(ApplicationHandle app) { aicliFreeApplication(app); } }; struct ManifestDeleter { void operator()(ManifestHandle manifest) { aicliFreeManifest(manifest); } }; using unique_repository = std::unique_ptr, RepositoryDeleter>; using unique_application = std::unique_ptr, ApplicationDeleter>; using unique_manifest = std::unique_ptr, ManifestDeleter>; template Result CallABIFunc(Func&& f, Args&&... args) { Result result{}; winrt::check_hresult(f(std::forward(args)..., &result)); return result; } } struct Manifest { Manifest() = default; Manifest(ManifestHandle mh) : m_man(mh) {} ~Manifest() = default; Manifest(const Manifest&) = delete; Manifest& operator=(const Manifest&) = delete; Manifest(Manifest&&) = default; Manifest& operator=(Manifest&&) = default; AICLICString GetManifestContents() const { return details::CallABIFunc(aicliGetManifestContents, m_man.get()); } private: details::unique_manifest m_man; }; struct Application { Application() = default; Application(ApplicationHandle ah) : m_app(ah) {} ~Application() = default; Application(const Application&) = delete; Application& operator=(const Application&) = delete; Application(Application&&) = default; Application& operator=(Application&&) = default; static Application GetById(AICLICString id) { return details::CallABIFunc(aicliGetApplicationById, nullptr, id); } Manifest GetManifestByVersion(AICLICString version) const { return details::CallABIFunc(aicliGetManifestByVersion, m_app.get(), version); } private: details::unique_application m_app; }; } ================================================ FILE: src/AppInstallerCLICore/ShutdownMonitoring.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/ShutdownMonitoring.h" #include #include #include #include using namespace std::chrono_literals; namespace AppInstaller::ShutdownMonitoring { static std::atomic_bool s_TerminationSignalHandlerEnabled = true; std::shared_ptr TerminationSignalHandler::Instance() { struct Singleton : public WinRT::COMStaticStorageBase { Singleton() : COMStaticStorageBase(L"WindowsPackageManager.TerminationSignalHandler") {} }; static Singleton s_instance; return s_instance.Get(); } void TerminationSignalHandler::AddListener(ICancellable* cancellable) { std::lock_guard lock{ m_listenersLock }; auto itr = std::find(m_listeners.begin(), m_listeners.end(), cancellable); THROW_HR_IF(E_NOT_VALID_STATE, itr != m_listeners.end()); m_listeners.push_back(cancellable); } void TerminationSignalHandler::RemoveListener(ICancellable* cancellable) { std::lock_guard lock{ m_listenersLock }; auto itr = std::find(m_listeners.begin(), m_listeners.end(), cancellable); if (itr == m_listeners.end()) { AICLI_LOG(CLI, Warning, << "TerminationSignalHandler::RemoveListener did not find requested object"); } else { m_listeners.erase(itr); } } void TerminationSignalHandler::EnableListener(bool enabled, ICancellable* cancellable) { if (enabled) { Instance()->AddListener(cancellable); } else { Instance()->RemoveListener(cancellable); } } bool TerminationSignalHandler::Enabled() { return s_TerminationSignalHandlerEnabled; } void TerminationSignalHandler::Enabled(bool enabled) { s_TerminationSignalHandlerEnabled = enabled; } #ifndef AICLI_DISABLE_TEST_HOOKS HWND TerminationSignalHandler::GetWindowHandle() const { return m_windowHandle.get(); } #endif TerminationSignalHandler::TerminationSignalHandler() { if (!s_TerminationSignalHandlerEnabled) { AICLI_LOG(CLI, Info, << "TerminationSignalHandler is disabled, skipping creation of signal listeners"); return; } // Create message only window. m_messageQueueReady.create(); m_windowThread = std::thread(&TerminationSignalHandler::CreateWindowAndStartMessageLoop, this); if (!m_messageQueueReady.wait(100)) { AICLI_LOG(CLI, Warning, << "Timeout creating winget window"); } // Set up ctrl-c handler. LOG_IF_WIN32_BOOL_FALSE(SetConsoleCtrlHandler(StaticCtrlHandlerFunction, TRUE)); } TerminationSignalHandler::~TerminationSignalHandler() { SetConsoleCtrlHandler(StaticCtrlHandlerFunction, FALSE); // std::thread requires that any managed thread (joinable) be joined or detached before destructing if (m_windowThread.joinable()) { if (m_windowHandle) { // Inform the thread that it should stop. PostMessageW(m_windowHandle.get(), WM_DESTROY, 0, 0); } m_windowThread.join(); } } void TerminationSignalHandler::StartAppShutdown() { AICLI_LOG(CLI, Info, << "Initiating shutdown procedure"); // Lifetime manager sends CTRL-C after the WM_QUERYENDSESSION is processed. // If we disable the CTRL-C handler, the default handler will kill us. InformListeners(CancelReason::AppShutdown, true); } BOOL WINAPI TerminationSignalHandler::StaticCtrlHandlerFunction(DWORD ctrlType) { return Instance()->CtrlHandlerFunction(ctrlType); } LRESULT WINAPI TerminationSignalHandler::WindowMessageProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_QUERYENDSESSION: AICLI_LOG(CLI, Verbose, << "Received WM_QUERYENDSESSION"); Instance()->StartAppShutdown(); return TRUE; case WM_ENDSESSION: case WM_CLOSE: AICLI_LOG(CLI, Verbose, << "Received window message type: " << uMsg); // We delay as long as needed during the WM_ENDSESSION as we will be terminated on return. ServerShutdownSynchronization::WaitForShutdown(); DestroyWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return FALSE; } BOOL TerminationSignalHandler::CtrlHandlerFunction(DWORD ctrlType) { // TODO: Move this to be logged per active context when we have thread static globals AICLI_LOG(CLI, Info, << "Got CTRL type: " << ctrlType); switch (ctrlType) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: return InformListeners(CancelReason::CtrlCSignal, false); // According to MSDN, we should never receive these due to having gdi32/user32 loaded in our process. // But handle them as a force terminate anyway. case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: return InformListeners(CancelReason::CtrlCSignal, true); default: return FALSE; } } // Terminates the currently attached contexts. // Returns FALSE if no contexts attached; TRUE otherwise. BOOL TerminationSignalHandler::InformListeners(CancelReason reason, bool force) { BOOL result = FALSE; { std::lock_guard lock{ m_listenersLock }; result = m_listeners.empty() ? FALSE : TRUE; for (auto& listener : m_listeners) { listener->Cancel(reason, force); } } // Notify shutdown synchronization as well ServerShutdownSynchronization::Instance().Signal(reason); return result; } void TerminationSignalHandler::CreateWindowAndStartMessageLoop() { PCWSTR windowClass = L"wingetWindow"; HINSTANCE hInstance = GetModuleHandle(NULL); if (hInstance == NULL) { LOG_LAST_ERROR_MSG("Failed getting module handle"); return; } WNDCLASSEX wcex = {}; wcex.cbSize = sizeof(wcex); wcex.style = CS_NOCLOSE; wcex.lpfnWndProc = TerminationSignalHandler::WindowMessageProcedure; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.lpszClassName = windowClass; if (!RegisterClassEx(&wcex)) { LOG_LAST_ERROR_MSG("Failed registering window class"); return; } // Unregister the window class on exiting the thread auto classUnregister = wil::scope_exit([&]() { UnregisterClassW(windowClass, hInstance); }); m_windowHandle = wil::unique_hwnd(CreateWindow( windowClass, L"WingetMessageOnlyWindow", WS_OVERLAPPEDWINDOW, 0, /* x */ 0, /* y */ 0, /* nWidth */ 0, /* nHeight */ NULL, /* hWndParent */ NULL, /* hMenu */ hInstance, NULL)); /* lpParam */ HWND windowHandle = m_windowHandle.get(); if (windowHandle == nullptr) { LOG_LAST_ERROR_MSG("Failed creating window"); return; } // We must destroy the window first so that the class unregister can succeed auto destroyWindow = wil::scope_exit([&]() { DestroyWindow(windowHandle); }); ShowWindow(windowHandle, SW_HIDE); // Force message queue to be created. MSG msg; PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); m_messageQueueReady.SetEvent(); // Message loop, we send WM_DESTROY to terminate it BOOL getMessageResult; while ((getMessageResult = GetMessage(&msg, windowHandle, 0, 0)) != 0) { if (getMessageResult == -1) { LOG_LAST_ERROR(); break; } else if (msg.message == WM_DESTROY) { break; } else { DispatchMessage(&msg); } } } void ServerShutdownSynchronization::Initialize(ShutdownCompleteCallback callback, bool createTerminationSignalHandler) { Instance().m_callback = callback; // Force the creation of the TerminationSignalHandler singleton so that the process can listen for termination signals even if // it never attempts to run anything that explicitly registers for cancellation callbacks. if (createTerminationSignalHandler) { TerminationSignalHandler::Instance(); } } void ServerShutdownSynchronization::AddComponent(const ComponentSystem& component) { ServerShutdownSynchronization& instance = Instance(); std::lock_guard lock{ instance.m_componentsLock }; for (const auto& item : instance.m_components) { if (item.BlockNewWork == component.BlockNewWork || item.BeginShutdown == component.BeginShutdown || item.Wait == component.Wait) { return; } } instance.m_components.push_back(component); } bool ServerShutdownSynchronization::WaitForShutdown(std::optional timeout) { ServerShutdownSynchronization& instance = Instance(); if (timeout) { return instance.m_shutdownComplete.wait(timeout.value()); } else { { std::lock_guard lock{ instance.m_threadLock }; if (!instance.m_shutdownThread.joinable()) { AICLI_LOG(Core, Warning, << "Attempt to wait for shutdown when shutdown has not been initiated."); return false; } } return instance.m_shutdownComplete.wait(); } } void ServerShutdownSynchronization::Signal(CancelReason reason) { std::lock_guard lock{ m_threadLock }; if (!m_shutdownThread.joinable()) { m_shutdownThread = std::thread(&ServerShutdownSynchronization::SynchronizeShutdown, this, reason); } } ServerShutdownSynchronization::~ServerShutdownSynchronization() { if (m_shutdownThread.joinable()) { m_shutdownThread.detach(); } } ServerShutdownSynchronization& ServerShutdownSynchronization::Instance() { static ServerShutdownSynchronization s_instance; return s_instance; } void ServerShutdownSynchronization::SynchronizeShutdown(CancelReason reason) try { auto setShutdownComplete = wil::scope_exit([this]() { this->m_shutdownComplete.SetEvent(); }); std::vector components; { std::lock_guard lock{ m_componentsLock }; components = m_components; } AICLI_LOG(CLI, Verbose, << "ServerShutdownSynchronization :: BlockNewWork"); for (const auto& component : components) { if (component.BlockNewWork) { component.BlockNewWork(reason); } } AICLI_LOG(CLI, Verbose, << "ServerShutdownSynchronization :: BeginShutdown"); for (const auto& component : components) { if (component.BeginShutdown) { component.BeginShutdown(reason); } } AICLI_LOG(CLI, Verbose, << "ServerShutdownSynchronization :: Wait"); for (const auto& component : components) { if (component.Wait) { component.Wait(); } } AICLI_LOG(CLI, Verbose, << "ServerShutdownSynchronization :: ShutdownCompleteCallback"); ShutdownCompleteCallback callback = m_callback; if (callback) { callback(); } } CATCH_LOG(); } ================================================ FILE: src/AppInstallerCLICore/Sixel.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Sixel.h" #include #include #include #include namespace AppInstaller::CLI::VirtualTerminal::Sixel { namespace anon { wil::com_ptr CreateFactory() { wil::com_ptr result; THROW_IF_FAILED(CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&result))); return result; } UINT AspectRatioMultiplier(AspectRatio aspectRatio) { switch (aspectRatio) { case AspectRatio::OneToOne: return 1; case AspectRatio::TwoToOne: return 2; case AspectRatio::ThreeToOne: return 3; case AspectRatio::FiveToOne: return 5; default: THROW_HR(E_INVALIDARG); } } // Forces the given bitmap source to evaluate wil::com_ptr CacheToBitmap(IWICImagingFactory* factory, IWICBitmapSource* sourceImage) { wil::com_ptr result; THROW_IF_FAILED(factory->CreateBitmapFromSource(sourceImage, WICBitmapCacheOnLoad, &result)); return result; } // Convert [0, 255] => [0, 100] UINT32 ByteToPercent(BYTE input) { return (static_cast(input) * 100 + 127) / 255; } // Contains the state for a rendering pass. struct RenderState { RenderState( const Palette& palette, const std::vector& views, const RenderControls& renderControls) : m_palette(palette), m_views(views), m_renderControls(renderControls) { // Create render buffers m_enabledColors.resize(m_palette.Size()); m_sixelBuffer.resize(m_palette.Size() * m_renderControls.PixelWidth); } enum class State { Initial, Pixels, Final, Terminated, }; // Advances the render state machine, returning true if `Current` will return a new sequence and false when it will not. bool Advance() { std::stringstream stream; switch (m_currentState) { case State::Initial: // Initial device control string stream << AICLI_VT_ESCAPE << 'P' << ToIntegral(m_renderControls.AspectRatio) << ";1;q"; for (size_t i = 0; i < m_palette.Size(); ++i) { // 2 is RGB color space, with values from 0 to 100 stream << '#' << i << ";2;"; WICColor currentColor = m_palette[i]; BYTE red = (currentColor >> 16) & 0xFF; BYTE green = (currentColor >> 8) & 0xFF; BYTE blue = (currentColor) & 0xFF; stream << ByteToPercent(red) << ';' << ByteToPercent(green) << ';' << ByteToPercent(blue); } m_currentState = State::Pixels; break; case State::Pixels: { // Disable all colors and set all characters to empty (0x3F) memset(m_enabledColors.data(), 0, m_enabledColors.size()); memset(m_sixelBuffer.data(), 0x3F, m_sixelBuffer.size()); // Convert indexed pixel data into per-color sixel lines UINT rowsToProcess = std::min(RenderControls::PixelsPerSixel, m_renderControls.PixelHeight - m_currentPixelRow); for (UINT rowOffset = 0; rowOffset < rowsToProcess; ++rowOffset) { // The least significant bit is the top of the sixel char sixelBit = 1 << rowOffset; UINT currentRow = m_currentPixelRow + rowOffset; for (UINT i = 0; i < m_renderControls.PixelWidth; ++i) { const BYTE* pixelPtr = nullptr; size_t colorIndex = 0; for (const ImageView& view : m_views) { pixelPtr = view.GetPixel(i, currentRow); if (pixelPtr) { colorIndex = *pixelPtr; // Stop on the first non-transparent pixel we find if (((m_palette[colorIndex] >> 24) & 0xFF) != 0) { break; } } } if (pixelPtr) { m_enabledColors[colorIndex] = 1; m_sixelBuffer[(colorIndex * m_renderControls.PixelWidth) + i] += sixelBit; } } } // Output all sixel color lines bool firstOfRow = true; for (size_t i = 0; i < m_enabledColors.size(); ++i) { if (m_enabledColors[i]) { if (m_renderControls.TransparencyEnabled) { // Don't output color if transparent WICColor currentColor = m_palette[i]; BYTE alpha = (currentColor >> 24) & 0xFF; if (alpha == 0) { continue; } } if (firstOfRow) { firstOfRow = false; } else { // The carriage return operator resets for another color pass. stream << '$'; } stream << '#' << i; const char* colorRow = &m_sixelBuffer[i * m_renderControls.PixelWidth]; if (m_renderControls.UseRepeatSequence) { char currentChar = colorRow[0]; UINT repeatCount = 1; for (UINT j = 1; j <= m_renderControls.PixelWidth; ++j) { // Force processing of a final null character to handle flushing the line const char nextChar = (j == m_renderControls.PixelWidth ? 0 : colorRow[j]); if (nextChar == currentChar) { ++repeatCount; } else { if (repeatCount > 2) { stream << '!' << repeatCount; } else if (repeatCount == 2) { stream << currentChar; } stream << currentChar; currentChar = nextChar; repeatCount = 1; } } } else { stream << std::string_view{ colorRow, m_renderControls.PixelWidth }; } } } // The new line operator sets up for the next sixel row stream << '-'; m_currentPixelRow += rowsToProcess; if (m_currentPixelRow >= m_renderControls.PixelHeight) { m_currentState = State::Final; } } break; case State::Final: stream << AICLI_VT_ESCAPE << '\\'; m_currentState = State::Terminated; break; case State::Terminated: m_currentSequence.clear(); return false; } m_currentSequence = std::move(stream).str(); return true; } Sequence Current() const { return Sequence{ m_currentSequence }; } private: const Palette& m_palette; const std::vector& m_views; const RenderControls& m_renderControls; State m_currentState = State::Initial; std::vector m_enabledColors; std::vector m_sixelBuffer; UINT m_currentPixelRow = 0; // TODO-C++20: Replace with a view from the stringstream std::string m_currentSequence; }; } Palette::Palette(IWICImagingFactory* factory, IWICBitmapSource* bitmapSource, UINT colorCount, bool transparencyEnabled) : m_factory(factory) { THROW_IF_FAILED(m_factory->CreatePalette(&m_paletteObject)); THROW_IF_FAILED(m_paletteObject->InitializeFromBitmap(bitmapSource, colorCount, transparencyEnabled)); // Extract the palette for render use UINT actualColorCount = 0; THROW_IF_FAILED(m_paletteObject->GetColorCount(&actualColorCount)); m_palette.resize(actualColorCount); THROW_IF_FAILED(m_paletteObject->GetColors(actualColorCount, m_palette.data(), &actualColorCount)); } Palette::Palette(const Palette& first, const Palette& second) { auto firstPalette = first.m_palette; auto secondPalette = second.m_palette; std::sort(firstPalette.begin(), firstPalette.end()); std::sort(secondPalette.begin(), secondPalette.end()); // Construct a union of the two palettes std::set_union(firstPalette.begin(), firstPalette.end(), secondPalette.begin(), secondPalette.end(), std::back_inserter(m_palette)); THROW_HR_IF(E_INVALIDARG, m_palette.size() > MaximumColorCount); m_factory = first.m_factory; THROW_IF_FAILED(m_factory->CreatePalette(&m_paletteObject)); THROW_IF_FAILED(m_paletteObject->InitializeCustom(m_palette.data(), static_cast(m_palette.size()))); } IWICPalette* Palette::Get() const { return m_paletteObject.get(); } size_t Palette::Size() const { return m_palette.size(); } WICColor& Palette::operator[](size_t index) { return m_palette[index]; } WICColor Palette::operator[](size_t index) const { return m_palette[index]; } ImageView::ImageView(UINT width, UINT height, UINT stride, UINT byteCount, BYTE* bytes) : m_viewWidth(width), m_viewHeight(height), m_viewStride(stride), m_viewByteCount(byteCount), m_viewBytes(bytes) {} ImageView ImageView::Lock(IWICBitmap* imageSource) { WICPixelFormatGUID pixelFormat{}; THROW_IF_FAILED(imageSource->GetPixelFormat(&pixelFormat)); THROW_HR_IF(ERROR_INVALID_STATE, GUID_WICPixelFormat8bppIndexed != pixelFormat); ImageView result; UINT sourceX = 0; UINT sourceY = 0; THROW_IF_FAILED(imageSource->GetSize(&sourceX, &sourceY)); THROW_WIN32_IF(ERROR_BUFFER_OVERFLOW, sourceX > static_cast(std::numeric_limits::max()) || sourceY > static_cast(std::numeric_limits::max())); WICRect rect{}; rect.Width = static_cast(sourceX); rect.Height = static_cast(sourceY); THROW_IF_FAILED(imageSource->Lock(&rect, WICBitmapLockRead, &result.m_lockedImage)); THROW_IF_FAILED(result.m_lockedImage->GetSize(&result.m_viewWidth, &result.m_viewHeight)); THROW_IF_FAILED(result.m_lockedImage->GetStride(&result.m_viewStride)); THROW_IF_FAILED(result.m_lockedImage->GetDataPointer(&result.m_viewByteCount, &result.m_viewBytes)); return result; } ImageView ImageView::Copy(IWICBitmapSource* imageSource) { WICPixelFormatGUID pixelFormat{}; THROW_IF_FAILED(imageSource->GetPixelFormat(&pixelFormat)); THROW_HR_IF(ERROR_INVALID_STATE, GUID_WICPixelFormat8bppIndexed != pixelFormat); ImageView result; THROW_IF_FAILED(imageSource->GetSize(&result.m_viewWidth, &result.m_viewHeight)); THROW_WIN32_IF(ERROR_BUFFER_OVERFLOW, result.m_viewWidth > static_cast(std::numeric_limits::max()) || result.m_viewHeight > static_cast(std::numeric_limits::max())); result.m_viewStride = result.m_viewWidth; result.m_viewByteCount = result.m_viewStride * result.m_viewHeight; result.m_copiedImage = std::make_unique(result.m_viewByteCount); result.m_viewBytes = result.m_copiedImage.get(); THROW_IF_FAILED(imageSource->CopyPixels(nullptr, result.m_viewStride, result.m_viewByteCount, result.m_viewBytes)); return result; } void ImageView::Translate(INT x, INT y, bool tile) { m_tile = tile; if (m_tile) { m_translateX = static_cast(m_viewWidth - (x % static_cast(m_viewWidth))); m_translateY = static_cast(m_viewHeight - (y % static_cast(m_viewHeight))); } else { m_translateX = static_cast(-x); m_translateY = static_cast(-y); } } const BYTE* ImageView::GetPixel(UINT x, UINT y) const { UINT translatedX = x + m_translateX; UINT tileCountX = translatedX / m_viewWidth; UINT viewX = translatedX % m_viewWidth; if (tileCountX && !m_tile) { return nullptr; } UINT translatedY = y + m_translateY; UINT tileCountY = translatedY / m_viewHeight; UINT viewY = translatedY % m_viewHeight; if (tileCountY && !m_tile) { return nullptr; } return m_viewBytes + (static_cast(viewY) * m_viewStride) + viewX; } UINT ImageView::Width() const { return m_viewWidth; } UINT ImageView::Height() const { return m_viewHeight; } void RenderControls::RenderSizeInCells(UINT width, UINT height) { PixelWidth = width * CellWidthInPixels; // We don't want to overdraw the row below, so our height must be the largest multiple of 6 that fits in Y cells. UINT yInPixels = height * CellHeightInPixels; PixelHeight = yInPixels - (yInPixels % PixelsPerSixel); } ImageSource::ImageSource(const std::filesystem::path& imageFilePath) { m_factory = anon::CreateFactory(); wil::com_ptr decoder; THROW_IF_FAILED(m_factory->CreateDecoderFromFilename(imageFilePath.c_str(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder)); wil::com_ptr decodedFrame; THROW_IF_FAILED(decoder->GetFrame(0, &decodedFrame)); m_sourceImage = anon::CacheToBitmap(m_factory.get(), decodedFrame.get()); } ImageSource::ImageSource(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding) : ImageSource(Utility::ReadEntireStreamAsByteArray(imageStream), imageEncoding) { } ImageSource::ImageSource(const std::vector& imageBytes, Manifest::IconFileTypeEnum imageEncoding) { m_factory = anon::CreateFactory(); wil::com_ptr stream; THROW_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream)); ULONG written = 0; THROW_IF_FAILED(stream->Write(imageBytes.data(), static_cast(imageBytes.size()), &written)); THROW_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr)); wil::com_ptr decoder; bool initializeDecoder = true; switch (imageEncoding) { case Manifest::IconFileTypeEnum::Unknown: THROW_IF_FAILED(m_factory->CreateDecoderFromStream(stream.get(), NULL, WICDecodeMetadataCacheOnDemand, &decoder)); initializeDecoder = false; break; case Manifest::IconFileTypeEnum::Jpeg: THROW_IF_FAILED(m_factory->CreateDecoder(GUID_ContainerFormatJpeg, NULL, &decoder)); break; case Manifest::IconFileTypeEnum::Png: THROW_IF_FAILED(m_factory->CreateDecoder(GUID_ContainerFormatPng, NULL, &decoder)); break; case Manifest::IconFileTypeEnum::Ico: THROW_IF_FAILED(m_factory->CreateDecoder(GUID_ContainerFormatIco, NULL, &decoder)); break; default: THROW_HR(E_UNEXPECTED); } if (initializeDecoder) { THROW_IF_FAILED(decoder->Initialize(stream.get(), WICDecodeMetadataCacheOnDemand)); } wil::com_ptr decodedFrame; THROW_IF_FAILED(decoder->GetFrame(0, &decodedFrame)); m_sourceImage = anon::CacheToBitmap(m_factory.get(), decodedFrame.get()); } void ImageSource::Resize(UINT pixelWidth, UINT pixelHeight, AspectRatio targetRenderRatio, bool stretchToFill, InterpolationMode interpolationMode) { if ((pixelWidth && pixelHeight) || targetRenderRatio != AspectRatio::OneToOne) { UINT targetX = pixelWidth; UINT targetY = pixelHeight; if (!stretchToFill) { // We need to calculate which of the sizes needs to be reduced UINT sourceImageX = 0; UINT sourceImageY = 0; THROW_IF_FAILED(m_sourceImage->GetSize(&sourceImageX, &sourceImageY)); double doubleTargetX = targetX; double doubleTargetY = targetY; double doubleSourceImageX = sourceImageX; double doubleSourceImageY = sourceImageY; double scaleFactorX = doubleTargetX / doubleSourceImageX; double targetY_scaledForX = sourceImageY * scaleFactorX; if (targetY_scaledForX > doubleTargetY) { // Scaling to make X fill would make Y to large, so we must scale to fill Y targetX = static_cast(sourceImageX * (doubleTargetY / doubleSourceImageY)); } else { // Scaling to make X fill kept Y under target targetY = static_cast(targetY_scaledForX); } } // Apply aspect ratio scaling targetY /= anon::AspectRatioMultiplier(targetRenderRatio); wil::com_ptr scaler; THROW_IF_FAILED(m_factory->CreateBitmapScaler(&scaler)); THROW_IF_FAILED(scaler->Initialize(m_sourceImage.get(), targetX, targetY, ToEnum(ToIntegral(interpolationMode)))); m_sourceImage = anon::CacheToBitmap(m_factory.get(), scaler.get()); } } void ImageSource::Resize(const RenderControls& controls) { Resize(controls.PixelWidth, controls.PixelHeight, controls.AspectRatio, controls.StretchSourceToFill, controls.InterpolationMode); } Palette ImageSource::CreatePalette(UINT colorCount, bool transparencyEnabled) const { return { m_factory.get(), m_sourceImage.get(), colorCount, transparencyEnabled }; } Palette ImageSource::CreatePalette(const RenderControls& controls) const { return CreatePalette(controls.ColorCount, controls.TransparencyEnabled); } void ImageSource::ApplyPalette(const Palette& palette) { // Convert to 8bpp indexed wil::com_ptr converter; THROW_IF_FAILED(m_factory->CreateFormatConverter(&converter)); // TODO: Determine a better value or enable it to be set constexpr double s_alphaThreshold = 0.5; THROW_IF_FAILED(converter->Initialize(m_sourceImage.get(), GUID_WICPixelFormat8bppIndexed, WICBitmapDitherTypeErrorDiffusion, palette.Get(), s_alphaThreshold, WICBitmapPaletteTypeCustom)); m_sourceImage = anon::CacheToBitmap(m_factory.get(), converter.get()); } ImageView ImageSource::Lock() const { return ImageView::Lock(m_sourceImage.get()); } ImageView ImageSource::Copy() const { return ImageView::Copy(m_sourceImage.get()); } void Compositor::Palette(Sixel::Palette palette) { m_palette = std::move(palette); } void Compositor::AddView(ImageView&& view) { m_views.emplace_back(std::move(view)); } size_t Compositor::ViewCount() const { return m_views.size(); } ImageView& Compositor::operator[](size_t index) { return m_views[index]; } const ImageView& Compositor::operator[](size_t index) const { return m_views[index]; } RenderControls& Compositor::Controls() { return m_renderControls; } const RenderControls& Compositor::Controls() const { return m_renderControls; } ConstructedSequence Compositor::Render() { anon::RenderState renderState{ m_palette, m_views, m_renderControls }; std::stringstream result; while (renderState.Advance()) { result << renderState.Current().Get(); } return ConstructedSequence{ std::move(result).str() }; } void Compositor::RenderTo(Execution::BaseStream& stream) { anon::RenderState renderState{ m_palette, m_views, m_renderControls }; while (renderState.Advance()) { stream << renderState.Current(); } } void Compositor::RenderTo(Execution::OutputStream& stream) { anon::RenderState renderState{ m_palette, m_views, m_renderControls }; while (renderState.Advance()) { stream << renderState.Current(); } } Image::Image(const std::filesystem::path& imageFilePath) : m_imageSource(imageFilePath) {} Image::Image(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding) : m_imageSource(imageStream, imageEncoding) {} Image::Image(const std::vector& imageBytes, Manifest::IconFileTypeEnum imageEncoding) : m_imageSource(imageBytes, imageEncoding) {} Image& Image::AspectRatio(Sixel::AspectRatio aspectRatio) { m_renderControls.AspectRatio = aspectRatio; return *this; } Image& Image::Transparency(bool transparencyEnabled) { m_renderControls.TransparencyEnabled = transparencyEnabled; return *this; } Image& Image::ColorCount(UINT colorCount) { THROW_HR_IF(E_INVALIDARG, colorCount > Palette::MaximumColorCount || colorCount < 2); m_renderControls.ColorCount = colorCount; return *this; } Image& Image::RenderSizeInPixels(UINT width, UINT height) { m_renderControls.PixelWidth = width; m_renderControls.PixelHeight = height; return *this; } Image& Image::RenderSizeInCells(UINT width, UINT height) { m_renderControls.RenderSizeInCells(width, height); return *this; } Image& Image::StretchSourceToFill(bool stretchSourceToFill) { m_renderControls.StretchSourceToFill = stretchSourceToFill; return *this; } Image& Image::UseRepeatSequence(bool useRepeatSequence) { m_renderControls.UseRepeatSequence = useRepeatSequence; return *this; } ConstructedSequence Image::Render() { return CreateCompositor().second.Render(); } void Image::RenderTo(Execution::OutputStream& stream) { CreateCompositor().second.RenderTo(stream); } std::pair Image::CreateCompositor() { ImageSource localSource{ m_imageSource }; localSource.Resize(m_renderControls); Palette palette{ localSource.CreatePalette(m_renderControls) }; localSource.ApplyPalette(palette); ImageView view{ localSource.Lock() }; Compositor compositor; compositor.Palette(std::move(palette)); compositor.AddView(std::move(view)); compositor.Controls() = m_renderControls; return { std::move(localSource), std::move(compositor) }; } } ================================================ FILE: src/AppInstallerCLICore/Sixel.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ChannelStreams.h" #include "VTSupport.h" #include #include #include #include namespace AppInstaller::CLI::VirtualTerminal::Sixel { // Determines the height to width ratio of the pixels that make up a sixel (a sixel is 6 pixels tall and 1 pixel wide). // Note that each cell is always a height of 20 and a width of 10, regardless of the screen resolution of the terminal. // The 2:1 ratio will then result in each sixel being 12 of the 20 pixels of a cell. enum class AspectRatio { OneToOne = 7, TwoToOne = 0, ThreeToOne = 3, FiveToOne = 2, }; // Determines the algorithm used when resizing the image. enum class InterpolationMode { NearestNeighbor = WICBitmapInterpolationModeNearestNeighbor, Linear = WICBitmapInterpolationModeLinear, Cubic = WICBitmapInterpolationModeCubic, Fant = WICBitmapInterpolationModeFant, HighQualityCubic = WICBitmapInterpolationModeHighQualityCubic, }; // Contains the palette used by a sixel image. struct Palette { // Limit to 256 both as the defacto maximum supported colors and to enable always using 8bpp indexed pixel format. static constexpr UINT MaximumColorCount = 256; // Creates an empty palette. Palette() = default; // Create a palette from the given source image, color count, transparency setting. Palette(IWICImagingFactory* factory, IWICBitmapSource* bitmapSource, UINT colorCount, bool transparencyEnabled); // Create a palette combining the two palettes. Throws an exception if there are more than MaximumColorCount unique // colors between the two. This can be avoided by intentionally dividing the available colors between the palettes // when creating them. Palette(const Palette& first, const Palette& second); // Gets the WIC palette object. IWICPalette* Get() const; // Gets the color count for the palette. size_t Size() const; // Gets the color at the given index in the palette. WICColor& operator[](size_t index); WICColor operator[](size_t index) const; private: wil::com_ptr m_factory; wil::com_ptr m_paletteObject; std::vector m_palette; }; // Allows access to the pixel data of an image source. // Can be configured to translate and/or tile the view. struct ImageView { // Creates a non-owning view using the given data. ImageView(UINT width, UINT height, UINT stride, UINT byteCount, BYTE* bytes); // Create a view by locking a bitmap. // This must be used from the same thread as the bitmap. static ImageView Lock(IWICBitmap* imageSource); // Create a view by copying the pixels from the image. static ImageView Copy(IWICBitmapSource* imageSource); // Translate the view by the given pixel counts. // The pixel at [0, 0] of the original will be at [x, y]. // If tile is true, the view will % coordinates outside of its dimensions back into its own view. // If tile is false, coordinates outside of the view will be null. void Translate(INT x, INT y, bool tile); // Gets the pixel of the view at the given coordinate. // Returns null if the coordinate is outside of the view. const BYTE* GetPixel(UINT x, UINT y) const; // Get the dimensions of the view. UINT Width() const; UINT Height() const; private: ImageView() = default; bool m_tile = false; UINT m_translateX = 0; UINT m_translateY = 0; wil::com_ptr m_lockedImage; std::unique_ptr m_copiedImage; UINT m_viewWidth = 0; UINT m_viewHeight = 0; UINT m_viewStride = 0; UINT m_viewByteCount = 0; BYTE* m_viewBytes = nullptr; }; // The set of values that defines the rendered output. struct RenderControls { // Yes, its right there in the name but the compiler can't read... static constexpr UINT PixelsPerSixel = 6; // Each cell is always a height of 20 and a width of 10, regardless of the screen resolution of the terminal. static constexpr UINT CellHeightInPixels = 20; static constexpr UINT CellWidthInPixels = 10; Sixel::AspectRatio AspectRatio = AspectRatio::OneToOne; bool TransparencyEnabled = true; bool StretchSourceToFill = false; bool UseRepeatSequence = false; UINT ColorCount = Palette::MaximumColorCount; UINT PixelWidth = 0; UINT PixelHeight = 0; Sixel::InterpolationMode InterpolationMode = InterpolationMode::HighQualityCubic; // The resulting sixel image will render to this size in terminal cells, // consuming as much as possible of the given size without going over. void RenderSizeInCells(UINT width, UINT height); }; // Contains an image that can be manipulated and rendered to sixels. struct ImageSource { // Create an image source from a file. explicit ImageSource(const std::filesystem::path& imageFilePath); // Create an empty image source. ImageSource() = default; // Create an image source from a stream. ImageSource(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding); // Create an image source from bytes. ImageSource(const std::vector& imageBytes, Manifest::IconFileTypeEnum imageEncoding); // Resize the image to the given width and height, factoring in the target aspect ratio for rendering. // If stretchToFill is true, the resulting image will be both the given width and height. // If false, the resulting image will be at most the given width or height while preserving the aspect ratio. void Resize(UINT pixelWidth, UINT pixelHeight, AspectRatio targetRenderRatio, bool stretchToFill = false, InterpolationMode interpolationMode = InterpolationMode::HighQualityCubic); // Resizes the image using the given render controls. void Resize(const RenderControls& controls); // Creates a palette from the current image. Palette CreatePalette(UINT colorCount, bool transparencyEnabled) const; // Creates a palette from the current image. Palette CreatePalette(const RenderControls& controls) const; // Converts the image to be 8bpp indexed for the given palette. void ApplyPalette(const Palette& palette); // Create a view by locking the image source. // This must be used from the same thread as the image source. ImageView Lock() const; // Create a view by copying the pixels from the image source. ImageView Copy() const; private: wil::com_ptr m_factory; wil::com_ptr m_sourceImage; }; // Allows one or more image sources to be rendered to a sixel output. struct Compositor { // Create an empty compositor. Compositor() = default; // Set the palette to be used by the compositor. void Palette(Palette palette); // Adds a new view to the compositor. Each successive view will be behind all of the others. void AddView(ImageView&& view); // Gets the number of views in the compositor. size_t ViewCount() const; // Gets the color at the given index in the palette. ImageView& operator[](size_t index); const ImageView& operator[](size_t index) const; // Get the render controls for the compositor. RenderControls& Controls(); const RenderControls& Controls() const; // Render to sixel format for storage / use multiple times. ConstructedSequence Render(); // Renders to sixel format directly to the stream. void RenderTo(Execution::BaseStream& stream); // Renders to sixel format directly to the stream. void RenderTo(Execution::OutputStream& stream); private: RenderControls m_renderControls; Sixel::Palette m_palette; std::vector m_views; }; // A helpful wrapper around the sixel image primitives that makes rendering a single image easier. struct Image { // Create an image from a file. Image(const std::filesystem::path& imageFilePath); // Create an image from a stream. Image(std::istream& imageStream, Manifest::IconFileTypeEnum imageEncoding); // Create an image from bytes. Image(const std::vector& imageBytes, Manifest::IconFileTypeEnum imageEncoding); // Set the aspect ratio of the result. Image& AspectRatio(AspectRatio aspectRatio); // Determine whether transparency is enabled. // This will affect whether transparent pixels are rendered or not. Image& Transparency(bool transparencyEnabled); // If transparency is enabled, one of the colors will be reserved for it. Image& ColorCount(UINT colorCount); // The resulting sixel image will render to this size in terminal cell pixels. Image& RenderSizeInPixels(UINT width, UINT height); // The resulting sixel image will render to this size in terminal cells, // consuming as much as possible of the given size without going over. Image& RenderSizeInCells(UINT width, UINT height); // Only affects the scaling of the image that occurs when render size is set. // When true, the source image will be stretched to fill the target size. // When false, the source image will be scaled while keeping its original aspect ratio. Image& StretchSourceToFill(bool stretchSourceToFill); // Compresses the output using repeat sequences. Image& UseRepeatSequence(bool useRepeatSequence); // Render to sixel format for storage / use multiple times. ConstructedSequence Render(); // Renders to sixel format directly to the output stream. void RenderTo(Execution::OutputStream& stream); private: // Creates a compositor for the image using the current render controls. std::pair CreateCompositor(); ImageSource m_imageSource; RenderControls m_renderControls; }; } ================================================ FILE: src/AppInstallerCLICore/TableOutput.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionReporter.h" #include "Resources.h" #include #include #include #include namespace AppInstaller::CLI::Execution { // Enables output data in a table format. // TODO: Improve for use with sparse data. template struct TableOutput { using header_t = std::array; using line_t = std::array; TableOutput(Reporter& reporter, header_t&& header, size_t sizingBuffer = 50) : m_reporter(reporter), m_sizingBuffer(sizingBuffer) { for (size_t i = 0; i < FieldCount; ++i) { m_columns[i].Name = std::move(header[i]); m_columns[i].MinLength = Utility::UTF8ColumnWidth(m_columns[i].Name.get()); m_columns[i].MaxLength = 0; } } void OutputLine(line_t&& line) { m_empty = false; if (m_buffer.size() < m_sizingBuffer) { m_buffer.emplace_back(std::move(line)); } else { EvaluateAndFlushBuffer(); OutputLineToStream(line); } } void Complete() { if (!m_empty) { EvaluateAndFlushBuffer(); } } bool IsEmpty() { return m_empty; } private: // A column in the table. struct Column { Resource::LocString Name; size_t MinLength = 0; size_t MaxLength = 0; bool SpaceAfter = true; }; Reporter& m_reporter; std::array m_columns; size_t m_sizingBuffer; std::vector m_buffer; bool m_bufferEvaluated = false; bool m_empty = true; void EvaluateAndFlushBuffer() { if (m_bufferEvaluated) { return; } // Determine the maximum length for all columns for (const auto& line : m_buffer) { for (size_t i = 0; i < FieldCount; ++i) { m_columns[i].MaxLength = std::max(m_columns[i].MaxLength, Utility::UTF8ColumnWidth(line[i])); } } // If there are actually columns with data, then also bring in the minimum size for (size_t i = 0; i < FieldCount; ++i) { if (m_columns[i].MaxLength) { m_columns[i].MaxLength = std::max(m_columns[i].MaxLength, m_columns[i].MinLength); } } // Only output the extra space if: // 1. Not the last field m_columns[FieldCount - 1].SpaceAfter = false; // 2. Not empty (taken care of by not doing anything if empty) // 3. There are non-empty fields after for (size_t i = FieldCount - 1; i > 0; --i) { if (m_columns[i].MaxLength) { break; } else { m_columns[i - 1].SpaceAfter = false; } } // Determine the total width required to not truncate any columns size_t totalRequired = 0; for (size_t i = 0; i < FieldCount; ++i) { totalRequired += m_columns[i].MaxLength + (m_columns[i].SpaceAfter ? 1 : 0); } size_t consoleWidth = GetConsoleWidth(); // If the total space would be too big, shrink them. // We don't want to use the last column, lest we auto-wrap if (totalRequired >= consoleWidth) { size_t extra = (totalRequired - consoleWidth) + 1; while (extra) { size_t targetIndex = 0; size_t targetVal = m_columns[0].MaxLength; for (size_t j = 1; j < FieldCount; ++j) { if (m_columns[j].MaxLength > targetVal) { targetIndex = j; targetVal = m_columns[j].MaxLength; } } m_columns[targetIndex].MaxLength -= 1; extra -= 1; } totalRequired = consoleWidth - 1; } // Header line line_t headerLine; for (size_t i = 0; i < FieldCount; ++i) { headerLine[i] = m_columns[i].Name.get(); } OutputLineToStream(headerLine); m_reporter.Info() << std::string(totalRequired, '-') << std::endl; for (const auto& line : m_buffer) { OutputLineToStream(line); } m_bufferEvaluated = true; } void OutputLineToStream(const line_t& line) { auto out = m_reporter.Info(); for (size_t i = 0; i < FieldCount; ++i) { const auto& col = m_columns[i]; if (col.MaxLength) { size_t valueLength = Utility::UTF8ColumnWidth(line[i]); if (valueLength > col.MaxLength) { size_t actualWidth; out << Utility::UTF8TrimRightToColumnWidth(line[i], col.MaxLength - 1, actualWidth) << "\xE2\x80\xA6"; // UTF8 encoding of ellipsis (…) character // Some characters take 2 unit space, the trimmed string length might be 1 less than the expected length. if (actualWidth != col.MaxLength - 1) { out << ' '; } if (col.SpaceAfter) { out << ' '; } } else { out << line[i]; if (col.SpaceAfter) { out << std::string(col.MaxLength - valueLength + 1, ' '); } } } } out << std::endl; } }; } ================================================ FILE: src/AppInstallerCLICore/VTSupport.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "VTSupport.h" #include #include namespace AppInstaller::CLI::VirtualTerminal { namespace { TextFormat::Color GetAccentColorFromSystem() { using namespace winrt::Windows::UI::ViewManagement; UISettings settings; auto color = settings.GetColorValue(UIColorType::Accent); return { color.R, color.G, color.B }; } bool InitializeMode(DWORD handle, DWORD& previousMode, std::initializer_list modeModifierFallbacks, DWORD disabledFlags = 0) { HANDLE hStd = GetStdHandle(handle); if (hStd == INVALID_HANDLE_VALUE) { LOG_LAST_ERROR(); } else if (hStd == NULL) { AICLI_LOG(CLI, Info, << "VT not enabled due to null handle [" << handle << "]"); } else { if (!GetConsoleMode(hStd, &previousMode)) { // If the user redirects output, the handle will be invalid for this function. // Don't log it in that case. LOG_LAST_ERROR_IF(GetLastError() != ERROR_INVALID_HANDLE); } else { for (DWORD mode : modeModifierFallbacks) { DWORD outMode = (previousMode & ~disabledFlags) | mode; if (!SetConsoleMode(hStd, outMode)) { // Even if it is a different error, log it and try to carry on. LOG_LAST_ERROR_IF(GetLastError() != STATUS_INVALID_PARAMETER); } else { return true; } } } } return false; } // Extracts a VT sequence, expected one of the form ESCAPE + prefix + result + suffix, returning the result part. std::string ExtractSequence(std::istream& inStream, std::string_view prefix, std::string_view suffix) { // Force discovery of available input std::ignore = inStream.peek(); static constexpr std::streamsize s_bufferSize = 1024; char buffer[s_bufferSize]; std::streamsize bytesRead = inStream.readsome(buffer, s_bufferSize); THROW_HR_IF(E_UNEXPECTED, bytesRead >= s_bufferSize); std::string_view resultView{ buffer, static_cast(bytesRead) }; size_t escapeIndex = resultView.find(AICLI_VT_ESCAPE[0]); if (escapeIndex == std::string_view::npos) { return {}; } resultView = resultView.substr(escapeIndex); size_t overheadLength = 1 + prefix.length() + suffix.length(); if (resultView.length() <= overheadLength || resultView.substr(1, prefix.length()) != prefix || resultView.substr(resultView.length() - suffix.length()) != suffix) { return {}; } return std::string{ resultView.substr(1 + prefix.length(), resultView.length() - overheadLength) }; } } ConsoleModeRestoreBase::ConsoleModeRestoreBase(DWORD handle) : m_handle(handle) {} ConsoleModeRestoreBase::~ConsoleModeRestoreBase() { if (m_token) { LOG_LAST_ERROR_IF(!SetConsoleMode(GetStdHandle(m_handle), m_previousMode)); m_token = false; } } ConsoleModeRestore::ConsoleModeRestore() : ConsoleModeRestoreBase(STD_OUTPUT_HANDLE) { m_token = InitializeMode(STD_OUTPUT_HANDLE, m_previousMode, { ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN, ENABLE_VIRTUAL_TERMINAL_PROCESSING }); } const ConsoleModeRestore& ConsoleModeRestore::Instance() { static ConsoleModeRestore s_instance; return s_instance; } ConsoleInputModeRestore::ConsoleInputModeRestore() : ConsoleModeRestoreBase(STD_INPUT_HANDLE) { m_token = InitializeMode(STD_INPUT_HANDLE, m_previousMode, { ENABLE_EXTENDED_FLAGS | ENABLE_VIRTUAL_TERMINAL_INPUT }, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT); } void ConstructedSequence::Append(const Sequence& sequence) { if (!sequence.Get().empty()) { m_str += sequence.Get(); Set(m_str); } } void ConstructedSequence::Clear() { m_str.clear(); Set(m_str); } // The beginning of a Control Sequence Introducer #define AICLI_VT_CSI AICLI_VT_ESCAPE "[" // The beginning of an Operating system command #define AICLI_VT_OSC AICLI_VT_ESCAPE "]" PrimaryDeviceAttributes::PrimaryDeviceAttributes(std::ostream& outStream, std::istream& inStream) { try { ConsoleInputModeRestore inputMode; if (!inputMode.IsVTEnabled()) { return; } // Send DA1 Primary Device Attributes request outStream << AICLI_VT_CSI << "0c"; outStream.flush(); // Response is of the form AICLI_VT_CSI ? ; ( ;)* c std::string sequence = ExtractSequence(inStream, "[?", "c"); std::vector values = Utility::Split(sequence, ';'); if (!values.empty()) { m_conformanceLevel = std::stoul(values[0]); } for (size_t i = 1; i < values.size(); ++i) { m_extensions |= 1ull << std::stoul(values[i]); } } CATCH_LOG(); } bool PrimaryDeviceAttributes::Supports(Extension extension) const { uint64_t extensionMask = 1ull << ToIntegral(extension); return (m_extensions & extensionMask) == extensionMask; } namespace Cursor { namespace Position { ConstructedSequence Up(int16_t cells) { THROW_HR_IF(E_INVALIDARG, cells < 0); std::ostringstream result; result << AICLI_VT_CSI << cells << 'A'; return ConstructedSequence{ std::move(result).str() }; } ConstructedSequence Down(int16_t cells) { THROW_HR_IF(E_INVALIDARG, cells < 0); std::ostringstream result; result << AICLI_VT_CSI << cells << 'B'; return ConstructedSequence{ std::move(result).str() }; } ConstructedSequence Forward(int16_t cells) { THROW_HR_IF(E_INVALIDARG, cells < 0); std::ostringstream result; result << AICLI_VT_CSI << cells << 'C'; return ConstructedSequence{ std::move(result).str() }; } ConstructedSequence Backward(int16_t cells) { THROW_HR_IF(E_INVALIDARG, cells < 0); std::ostringstream result; result << AICLI_VT_CSI << cells << 'D'; return ConstructedSequence{ std::move(result).str() }; } } namespace Visibility { const Sequence EnableBlink{ AICLI_VT_CSI "?12h" }; const Sequence DisableBlink{ AICLI_VT_CSI "?12l" }; const Sequence EnableShow{ AICLI_VT_CSI "?25h" }; const Sequence DisableShow{ AICLI_VT_CSI "?25l" }; } } namespace TextFormat { // Define a text formatting sequence with an integer id #define AICLI_VT_TEXTFORMAT(_id_) AICLI_VT_CSI #_id_ "m" const Sequence Default{ AICLI_VT_TEXTFORMAT(0) }; const Sequence Negative{ AICLI_VT_TEXTFORMAT(7) }; Color Color::GetAccentColor() { static Color accent{ GetAccentColorFromSystem() }; return accent; } namespace Foreground { const Sequence Bright{ AICLI_VT_TEXTFORMAT(1) }; const Sequence NoBright{ AICLI_VT_TEXTFORMAT(22) }; const Sequence BrightRed{ AICLI_VT_TEXTFORMAT(91) }; const Sequence BrightGreen{ AICLI_VT_TEXTFORMAT(92) }; const Sequence BrightYellow{ AICLI_VT_TEXTFORMAT(93) }; const Sequence BrightBlue{ AICLI_VT_TEXTFORMAT(94) }; const Sequence BrightMagenta{ AICLI_VT_TEXTFORMAT(95) }; const Sequence BrightCyan{ AICLI_VT_TEXTFORMAT(96) }; const Sequence BrightWhite{ AICLI_VT_TEXTFORMAT(97) }; ConstructedSequence Extended(const Color& color) { std::ostringstream result; result << AICLI_VT_CSI "38;2;" << static_cast(color.R) << ';' << static_cast(color.G) << ';' << static_cast(color.B) << 'm'; return ConstructedSequence{ std::move(result).str() }; } } namespace Background { ConstructedSequence Extended(const Color& color) { std::ostringstream result; result << AICLI_VT_CSI "48;2;" << static_cast(color.R) << ';' << static_cast(color.G) << ';' << static_cast(color.B) << 'm'; return ConstructedSequence{ std::move(result).str() }; } } ConstructedSequence Hyperlink(const std::string& text, const std::string& ref) { std::ostringstream result; result << AICLI_VT_OSC "8;;" << ref << AICLI_VT_ESCAPE << "\\" << text << AICLI_VT_OSC << "8;;" << AICLI_VT_ESCAPE << "\\"; return ConstructedSequence{ std::move(result).str() }; } } namespace TextModification { const Sequence EraseLineForward{ AICLI_VT_CSI "0K" }; const Sequence EraseLineBackward{ AICLI_VT_CSI "1K" }; const Sequence EraseLineEntirely{ AICLI_VT_CSI "2K" }; } namespace Progress { ConstructedSequence Construct(State state, std::optional percentage) { // See https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC THROW_HR_IF(E_BOUNDS, percentage.has_value() && percentage > 100u); // Workaround some quirks in the Windows Terminal implementation of the progress OSC sequence switch (state) { case State::None: case State::Indeterminate: // Windows Terminal does not recognize the OSC sequence if the progress value is left out. // As a workaround, we can specify an arbitrary value since it does not matter for None and Indeterminate states. percentage = percentage.value_or(0); break; case State::Normal: case State::Error: case State::Paused: // Windows Terminal does not support switching progress states without also setting a progress value at the same time, // so we disallow this case for now. THROW_HR_IF(E_INVALIDARG, !percentage.has_value()); break; } int stateId; switch (state) { case State::None: stateId = 0; break; case State::Indeterminate: stateId = 3; break; case State::Normal: stateId = 1; break; case State::Error: stateId = 2; break; case State::Paused: stateId = 4; break; default: THROW_HR(E_UNEXPECTED); } std::ostringstream result; result << AICLI_VT_OSC "9;4;" << stateId << ";"; if (percentage.has_value()) { result << percentage.value(); } result << AICLI_VT_ESCAPE << "\\"; return ConstructedSequence{ std::move(result).str() }; } } } ================================================ FILE: src/AppInstallerCLICore/VTSupport.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include // The escape character that begins all VT sequences #define AICLI_VT_ESCAPE "\x1b" namespace AppInstaller::CLI::VirtualTerminal { // RAII class to enable VT support and restore the console mode. struct ConsoleModeRestoreBase { ConsoleModeRestoreBase(DWORD handle); ~ConsoleModeRestoreBase(); ConsoleModeRestoreBase(const ConsoleModeRestoreBase&) = delete; ConsoleModeRestoreBase& operator=(const ConsoleModeRestoreBase&) = delete; ConsoleModeRestoreBase(ConsoleModeRestoreBase&&) = default; ConsoleModeRestoreBase& operator=(ConsoleModeRestoreBase&&) = default; // Returns true if VT support has been enabled for the console. bool IsVTEnabled() const { return m_token; } protected: DestructionToken m_token = false; DWORD m_handle = 0; DWORD m_previousMode = 0; }; // RAII class to enable VT output support and restore the console mode. struct ConsoleModeRestore : public ConsoleModeRestoreBase { ConsoleModeRestore(const ConsoleModeRestore&) = delete; ConsoleModeRestore& operator=(const ConsoleModeRestore&) = delete; ConsoleModeRestore(ConsoleModeRestore&&) = default; ConsoleModeRestore& operator=(ConsoleModeRestore&&) = default; // Gets the singleton. static const ConsoleModeRestore& Instance(); private: ConsoleModeRestore(); }; // RAII class to enable VT input support and restore the console mode. struct ConsoleInputModeRestore : public ConsoleModeRestoreBase { ConsoleInputModeRestore(); ConsoleInputModeRestore(const ConsoleInputModeRestore&) = delete; ConsoleInputModeRestore& operator=(const ConsoleInputModeRestore&) = delete; ConsoleInputModeRestore(ConsoleInputModeRestore&&) = default; ConsoleInputModeRestore& operator=(ConsoleInputModeRestore&&) = default; }; // The base for all VT sequences. struct Sequence { constexpr Sequence() = default; explicit constexpr Sequence(std::string_view c) : m_chars(c) {} std::string_view Get() const { return m_chars; } protected: void Set(const std::string& s) { m_chars = s; } private: std::string_view m_chars; }; // A VT sequence that is constructed at runtime. struct ConstructedSequence : public Sequence { ConstructedSequence() { Set(m_str); } explicit ConstructedSequence(std::string s) : m_str(std::move(s)) { Set(m_str); } ConstructedSequence(const ConstructedSequence& other) : m_str(other.m_str) { Set(m_str); } ConstructedSequence& operator=(const ConstructedSequence& other) { m_str = other.m_str; Set(m_str); return *this; } ConstructedSequence(ConstructedSequence&& other) noexcept : m_str(std::move(other.m_str)) { Set(m_str); } ConstructedSequence& operator=(ConstructedSequence&& other) noexcept { m_str = std::move(other.m_str); Set(m_str); return *this; } void Append(const Sequence& sequence); void Clear(); private: std::string m_str; }; // Below are mapped to the sequences described here: // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences // Contains the response to a DA1 (Primary Device Attributes) request. struct PrimaryDeviceAttributes { // Queries the device attributes on creation. PrimaryDeviceAttributes(std::ostream& outStream, std::istream& inStream); // The extensions that a device may support. enum class Extension { Columns132 = 1, PrinterPort = 2, Sixel = 4, SelectiveErase = 6, SoftCharacterSet = 7, UserDefinedKeys = 8, NationalReplacementCharacterSets = 9, SoftCharacterSet2 = 12, EightBitInterface = 14, TechnicalCharacterSet = 15, WindowingCapability = 18, HorizontalScrolling = 21, ColorText = 22, Greek = 23, Turkish = 24, RectangularAreaOperations = 28, TextMacros = 32, ISO_Latin2CharacterSet = 42, PC_Term = 44, SoftKeyMap = 45, ASCII_Emulation = 46, }; // Determines if the given extension is supported. bool Supports(Extension extension) const; private: uint32_t m_conformanceLevel = 0; uint64_t m_extensions = 0; }; namespace Cursor { namespace Position { ConstructedSequence Up(int16_t cells); ConstructedSequence Down(int16_t cells); ConstructedSequence Forward(int16_t cells); ConstructedSequence Backward(int16_t cells); } namespace Visibility { extern const Sequence EnableBlink; extern const Sequence DisableBlink; extern const Sequence EnableShow; extern const Sequence DisableShow; } } namespace TextFormat { // Returns all attributes to the default state prior to modification extern const Sequence Default; // Swaps foreground and background colors extern const Sequence Negative; // A color, used in constructed sequences. struct Color { uint8_t R; uint8_t G; uint8_t B; static Color GetAccentColor(); }; namespace Foreground { // Applies brightness/intensity flag to foreground color extern const Sequence Bright; // Removes brightness/intensity flag from foreground color extern const Sequence NoBright; extern const Sequence BrightRed; extern const Sequence BrightGreen; extern const Sequence BrightYellow; extern const Sequence BrightBlue; extern const Sequence BrightMagenta; extern const Sequence BrightCyan; extern const Sequence BrightWhite; ConstructedSequence Extended(const Color& color); } namespace Background { ConstructedSequence Extended(const Color& color); } ConstructedSequence Hyperlink(const std::string& text, const std::string& ref); } namespace TextModification { extern const Sequence EraseLineForward; extern const Sequence EraseLineBackward; extern const Sequence EraseLineEntirely; } namespace Progress { enum class State { None, Indeterminate, Normal, Paused, Error }; ConstructedSequence Construct(State state, std::optional percentage = std::nullopt); } } inline std::ostream& operator<<(std::ostream& o, const AppInstaller::CLI::VirtualTerminal::Sequence& s) { return (o << s.Get()); } ================================================ FILE: src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ArchiveFlow.h" #include "PortableFlow.h" #include "ShellExecuteInstallerHandler.h" #include #include #include using namespace AppInstaller::Manifest; namespace AppInstaller::CLI::Workflow { namespace { constexpr std::wstring_view s_Extracted = L"extracted"; } void ScanArchiveFromLocalManifest(Execution::Context& context) { if (context.Args.Contains(Execution::Args::Type::Manifest)) { bool scanResult = Archive::ScanZipFile(context.Get()); if (scanResult) { AICLI_LOG(CLI, Info, << "Archive malware scan passed"); } else { if (context.Args.Contains(Execution::Args::Type::IgnoreLocalArchiveMalwareScan) && Settings::IsAdminSettingEnabled(Settings::BoolAdminSetting::LocalArchiveMalwareScanOverride)) { AICLI_LOG(CLI, Warning, << "Archive scan detected malware. Proceeding due to --ignore-local-archive-malware-scan"); context.Reporter.Warn() << Resource::String::ArchiveFailedMalwareScanOverridden << std::endl; } else { AICLI_LOG(CLI, Error, << "Archive malware scan failed"); context.Reporter.Error() << Resource::String::ArchiveFailedMalwareScan << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_ARCHIVE_SCAN_FAILED); } } } } void ExtractFilesFromArchive(Execution::Context& context) { const auto& installerPath = context.Get(); std::filesystem::path destinationFolder = installerPath.parent_path() / s_Extracted; std::filesystem::create_directory(destinationFolder); AICLI_LOG(CLI, Info, << "Extracting archive to: " << destinationFolder); context.Reporter.Info() << Resource::String::ExtractingArchive << std::endl; if (Settings::User().Get() == Archive::ExtractionMethod::Tar) { context << ShellExecuteExtractArchive(installerPath, destinationFolder); } else { HRESULT result = AppInstaller::Archive::TryExtractArchive(installerPath, destinationFolder); if (SUCCEEDED(result)) { AICLI_LOG(CLI, Info, << "Successfully extracted archive"); context.Reporter.Info() << Resource::String::ExtractArchiveSucceeded << std::endl; } else { AICLI_LOG(CLI, Info, << "Failed to extract archive with code " << result); context.Reporter.Error() << Resource::String::ExtractArchiveFailed << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXTRACT_ARCHIVE_FAILED); } } } void VerifyAndSetNestedInstaller(Execution::Context& context) { const auto& installer = context.Get().value(); if (installer.NestedInstallerFiles.empty()) { // Pre-install validation should prevent this from happening AICLI_LOG(CLI, Error, << "No entries specified for NestedInstallerFiles"); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST); } std::filesystem::path targetInstallerPath = context.Get().parent_path() / s_Extracted; for (const auto& nestedInstallerFile : installer.NestedInstallerFiles) { const std::filesystem::path& nestedInstallerPath = targetInstallerPath / ConvertToUTF16(nestedInstallerFile.RelativeFilePath); if (Filesystem::PathEscapesBaseDirectory(nestedInstallerPath, targetInstallerPath)) { AICLI_LOG(CLI, Error, << "Path points to a location outside of the install directory: " << nestedInstallerPath); context.Reporter.Error() << Resource::String::InvalidPathToNestedInstaller << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_INVALID_PATH); } else if (!std::filesystem::exists(nestedInstallerPath)) { AICLI_LOG(CLI, Error, << "Unable to locate nested installer at: " << nestedInstallerPath); context.Reporter.Error() << Resource::String::NestedInstallerNotFound(Utility::LocIndView{ nestedInstallerPath.u8string() }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND); } else if (!DoesInstallerTypeSupportMultipleNestedInstallers(installer.NestedInstallerType)) { // Update the installerPath to the extracted non-portable installer. AICLI_LOG(CLI, Info, << "Setting installerPath to: " << nestedInstallerPath); targetInstallerPath = nestedInstallerPath; } } context.Add(targetInstallerPath); } void EnsureValidNestedInstallerMetadataForArchiveInstall(Execution::Context& context) { auto installer = context.Get().value(); if (IsArchiveType(installer.BaseInstallerType)) { if (!IsNestedInstallerTypeSupported(installer.NestedInstallerType)) { AICLI_LOG(CLI, Error, << "Nested installer type not supported: " << installer.NestedInstallerType); context.Reporter.Error() << Resource::String::NestedInstallerNotSupported << std::endl; AICLI_TERMINATE_CONTEXT(ERROR_NOT_SUPPORTED); } auto const& nestedInstallerFiles = installer.NestedInstallerFiles; if (nestedInstallerFiles.empty()) { AICLI_LOG(CLI, Error, << "No entries specified for NestedInstallerFiles"); context.Reporter.Error() << Resource::String::NestedInstallerNotSpecified << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST); } if (!DoesInstallerTypeSupportMultipleNestedInstallers(installer.NestedInstallerType) && (nestedInstallerFiles.size() != 1)) { AICLI_LOG(CLI, Error, << "Multiple nested installers specified for unsupported nested installerType"); context.Reporter.Error() << Resource::String::MultipleUnsupportedNestedInstallersSpecified << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST); } } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/ArchiveFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Scans the archive file if downloaded from a local manifest // Required Args: None // Inputs: InstallerPath // Outputs: None void ScanArchiveFromLocalManifest(Execution::Context& context); // Extracts the files from an archive // Required Args: None // Inputs: InstallerPath // Outputs: None void ExtractFilesFromArchive(Execution::Context& context); // Verifies that the NestedInstaller exists and sets the InstallerPath // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void VerifyAndSetNestedInstaller(Execution::Context& context); // Verifies that the metadata related to the NestedInstaller is valid // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void EnsureValidNestedInstallerMetadataForArchiveInstall(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/CompletionFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "CompletionFlow.h" namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Utility::literals; namespace { // Outputs the completion string, wrapping it in quotes if needed. void OutputCompletionString(Execution::OutputStream& stream, std::string_view value) { if (value.find_first_of(' ') != std::string_view::npos) { stream << '"' << value << '"' << std::endl; } else { stream << value << std::endl; } } } void CompleteSourceName(Execution::Context& context) { const std::string& word = context.Get().Word(); auto stream = context.Reporter.Completion(); for (const auto& source : Repository::Source::GetCurrentSources()) { if (word.empty() || Utility::ICUCaseInsensitiveStartsWith(source.Name, word)) { OutputCompletionString(stream, source.Name); } } } void RequireCompletionWordNonEmpty(Execution::Context& context) { if (context.Get().Word().empty()) { AICLI_LOG(CLI, Verbose, << "Completion word empty, cannot complete"); AICLI_TERMINATE_CONTEXT(E_NOT_SET); } } void CompleteWithMatchedField(Execution::Context& context) { auto& searchResult = context.Get(); auto stream = context.Reporter.Completion(); for (size_t i = 0; i < searchResult.Matches.size(); ++i) { if (searchResult.Matches[i].MatchCriteria.Value.empty()) { OutputCompletionString(stream, searchResult.Matches[i].Package->GetProperty(Repository::PackageProperty::Id)); } else { OutputCompletionString(stream, searchResult.Matches[i].MatchCriteria.Value); } } } void CompleteWithSearchResultVersions(Execution::Context& context) { const std::string& word = context.Get().Word(); auto stream = context.Reporter.Completion(); for (const auto& ap : context.Get()->GetAvailable()) { for (const auto& vc : ap->GetVersionKeys()) { if (word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Version, word)) { OutputCompletionString(stream, vc.Version); } } } } void CompleteWithSearchResultInstalledVersions(Execution::Context& context) { const std::string& word = context.Get().Word(); auto stream = context.Reporter.Completion(); auto installedPackage = context.Get()->GetInstalled(); if (installedPackage) { for (const auto& vc : installedPackage->GetVersionKeys()) { if (word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Version, word)) { OutputCompletionString(stream, vc.Version); } } } } void CompleteWithSearchResultChannels(Execution::Context& context) { const std::string& word = context.Get().Word(); auto stream = context.Reporter.Completion(); std::vector channels; for (const auto& ap : context.Get()->GetAvailable()) { for (const auto& vc : ap->GetVersionKeys()) { if ((word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Channel, word)) && std::find(channels.begin(), channels.end(), vc.Channel) == channels.end()) { channels.emplace_back(vc.Channel); } } } for (const auto& c : channels) { OutputCompletionString(stream, c); } } void CompleteWithSingleSemanticsForValue::operator()(Execution::Context& context) const { switch (m_type) { case Execution::Args::Type::Query: case Execution::Args::Type::MultiQuery: case Execution::Args::Type::Id: case Execution::Args::Type::Name: case Execution::Args::Type::Moniker: case Execution::Args::Type::Tag: case Execution::Args::Type::Command: case Execution::Args::Type::Version: case Execution::Args::Type::Channel: context << Workflow::OpenSource(); break; } context << CompleteWithSingleSemanticsForValueUsingExistingSource(m_type); } void CompleteWithSingleSemanticsForValueUsingExistingSource::operator()(Execution::Context& context) const { switch (m_type) { case Execution::Args::Type::Query: case Execution::Args::Type::MultiQuery: context << Workflow::RequireCompletionWordNonEmpty << Workflow::SearchSourceForSingleCompletion << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Manifest: // Intentionally output none to enable pass through to filesystem. break; case Execution::Args::Type::Id: context << Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Id) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Name: context << Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Name) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Moniker: context << Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Moniker) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Tag: context << Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Tag) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Command: context << Workflow::SearchSourceForCompletionField(Repository::PackageMatchField::Command) << Workflow::CompleteWithMatchedField; break; case Execution::Args::Type::Version: // Here we require that the standard search finds a single entry, and we list those versions. context << Workflow::SearchSourceForSingle << Workflow::EnsureOneMatchFromSearchResult(OperationType::Completion) << Workflow::CompleteWithSearchResultVersions; break; case Execution::Args::Type::TargetVersion: // Here we require that the standard search finds a single entry, and we list the installed versions. context << Workflow::SearchSourceForSingle << Workflow::EnsureOneMatchFromSearchResult(OperationType::Completion) << Workflow::CompleteWithSearchResultInstalledVersions; break; case Execution::Args::Type::Channel: // Here we require that the standard search finds a single entry, and we list those channels. context << Workflow::SearchSourceForSingle << Workflow::EnsureOneMatchFromSearchResult(OperationType::Completion) << Workflow::CompleteWithSearchResultChannels; break; case Execution::Args::Type::Source: context << Workflow::CompleteSourceName; break; } } void CompleteWithEmptySet(Execution::Context& context) { context.Reporter.Completion() << std::endl; } } ================================================ FILE: src/AppInstallerCLICore/Workflows/CompletionFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" #include "WorkflowBase.h" #include namespace AppInstaller::CLI::Workflow { // Outputs completion possibilities for the source name argument. // Required Args: None // Inputs: CompletionData // Outputs: None void CompleteSourceName(Execution::Context& context); // Terminates the context if the completion word is empty. // Required Args: None // Inputs: CompletionData // Outputs: None void RequireCompletionWordNonEmpty(Execution::Context& context); // Outputs the matched field for the results. // Required Args: None // Inputs: SearchResult // Outputs: None void CompleteWithMatchedField(Execution::Context& context); // Outputs the versions available for the single search result. // Required Args: None // Inputs: CompletionData, SearchResult // Outputs: None void CompleteWithSearchResultVersions(Execution::Context& context); // Outputs the channels available for the single search result. // Required Args: None // Inputs: CompletionData, SearchResult // Outputs: None void CompleteWithSearchResultChannels(Execution::Context& context); // Executes the appropriate completion flow for the given argument in the context of a command // that targets a single manifest (ex. show or install). // Required Args: None // Inputs: CompletionData // Outputs: None struct CompleteWithSingleSemanticsForValue : public WorkflowTask { CompleteWithSingleSemanticsForValue(Execution::Args::Type type) : WorkflowTask("CompleteWithSingleSemanticsForValue"), m_type(type) {} void operator()(Execution::Context& context) const override; private: Execution::Args::Type m_type; }; // Executes the appropriate completion flow for the given argument in the context of a command // that targets a single manifest (ex. show or install), using the already open source. // Required Args: None // Inputs: CompletionData, Source // Outputs: None struct CompleteWithSingleSemanticsForValueUsingExistingSource : public WorkflowTask { CompleteWithSingleSemanticsForValueUsingExistingSource(Execution::Args::Type type) : WorkflowTask("CompleteWithSingleSemanticsForValueUsingExistingSource"), m_type(type) {} void operator()(Execution::Context& context) const override; private: Execution::Args::Type m_type; }; // Outputs an empty line to indicate that there are no completions. // Required Args: None // Inputs: None // Outputs: None void CompleteWithEmptySet(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationFlow.h" #include "ImportExportFlow.h" #include "PromptFlow.h" #include "TableOutput.h" #include "MSStoreInstallerHandler.h" #include "Public/ConfigurationSetProcessorFactoryRemoting.h" #include "ConfigurationCommon.h" #include "ConfigurationWingetDscModuleUnitValidation.h" #include "Commands/DscCommandBase.h" #include #include #include #include #include #include #include #include #include using namespace AppInstaller::CLI::Execution; using namespace winrt::Microsoft::Management::Configuration; using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::Storage; using namespace AppInstaller::Utility::literals; using namespace AppInstaller::SelfManagement; namespace AppInstaller::CLI::Workflow { #ifndef AICLI_DISABLE_TEST_HOOKS IConfigurationSetProcessorFactory s_override_IConfigurationSetProcessorFactory; void SetTestConfigurationSetProcessorFactory(IConfigurationSetProcessorFactory factory) { s_override_IConfigurationSetProcessorFactory = std::move(factory); } #endif namespace anon { static const AppInstaller::Utility::Version s_MinimumSchemaVersionModuleNameRequiredInType = { "0.3" }; constexpr std::wstring_view s_Directive_Description = L"description"; constexpr std::wstring_view s_Directive_Module = L"module"; constexpr std::wstring_view s_Directive_AllowPrerelease = L"allowPrerelease"; constexpr std::wstring_view s_Unit_WinGetPackage = L"WinGetPackage"; constexpr std::wstring_view s_Unit_WinGetSource = L"WinGetSource"; constexpr std::wstring_view s_UnitType_WinGetPackage_DSCv3 = WINGET_DSCV3_MODULE_NAME_WIDE L"/Package"; constexpr std::wstring_view s_UnitType_WinGetSource_DSCv3 = WINGET_DSCV3_MODULE_NAME_WIDE L"/Source"; constexpr std::wstring_view s_UnitType_WinGetUserSettingsFile_DSCv3 = WINGET_DSCV3_MODULE_NAME_WIDE L"/UserSettingsFile"; constexpr std::wstring_view s_UnitType_WinGetAdminSettings_DSCv3 = WINGET_DSCV3_MODULE_NAME_WIDE L"/AdminSettings"; constexpr std::wstring_view s_Module_WinGetClient = L"Microsoft.WinGet.DSC"; constexpr std::wstring_view s_Setting_WinGetPackage_Id = L"id"; constexpr std::wstring_view s_Setting_WinGetPackage_Source = L"source"; constexpr std::wstring_view s_Setting_WinGetPackage_Version = L"version"; constexpr std::wstring_view s_Setting_WinGetSource_Name = L"name"; constexpr std::wstring_view s_Setting_WinGetSource_Arg = L"argument"; constexpr std::wstring_view s_Setting_WinGetSource_Type = L"type"; constexpr std::wstring_view s_Setting_WinGetSource_TrustLevel = L"trustLevel"; constexpr std::wstring_view s_Setting_WinGetSource_Explicit = L"explicit"; constexpr std::wstring_view s_Setting_WinGetSource_Priority = L"priority"; constexpr std::wstring_view s_Predefined_PowerShell_PackageId = L"Microsoft.PowerShell"; constexpr std::wstring_view s_Predefined_PowerShell_PackageSource = L"winget"; constexpr std::string_view s_DscPackage_StoreId_Stable = "9NVTPZWRC6KQ"; constexpr std::string_view s_DscPackage_StoreId_Preview = "9PCX3HX4HZ0Z"; struct PredefinedResourceInfo { std::wstring_view UnitType; bool ElevationRequired = false; PredefinedResourceInfo(std::wstring_view unitType) : UnitType(unitType) {} PredefinedResourceInfo(std::wstring_view unitType, bool elevationRequired) : UnitType(unitType), ElevationRequired(elevationRequired) {} }; struct PredefinedResource { // RequiredModule could be empty, meaning no required modules needed. std::wstring_view RequiredModule; std::vector ResourceInfos; }; std::vector PredefinedResourcesForExport() { return { { {}, { { s_UnitType_WinGetUserSettingsFile_DSCv3 }, { s_UnitType_WinGetAdminSettings_DSCv3, true } } }, { L"Microsoft.Windows.Settings", { { L"Microsoft.Windows.Settings/WindowsSettings", true } } }, }; } std::vector PackageSettingsExclusionList() { return { L"Microsoft.WinGet/", L"Microsoft.WinGet.Dev/", L"Microsoft.DSC.Debug/", L"Microsoft.DSC/", L"Microsoft.DSC.Transitional/", L"Microsoft.Windows/RebootPending", L"Microsoft.Windows/Registry", L"Microsoft.Windows/WMI", L"Microsoft.Windows/WindowsPowerShell", L"Microsoft/OSInfo" }; }; Logging::Level ConvertLevel(DiagnosticLevel level) { switch (level) { case DiagnosticLevel::Verbose: return Logging::Level::Verbose; case DiagnosticLevel::Informational: return Logging::Level::Info; case DiagnosticLevel::Warning: return Logging::Level::Warning; case DiagnosticLevel::Error: return Logging::Level::Error; case DiagnosticLevel::Critical: return Logging::Level::Crit; } return Logging::Level::Info; } DiagnosticLevel ConvertLevel(Logging::Level level) { switch (level) { case Logging::Level::Verbose: return DiagnosticLevel::Verbose; case Logging::Level::Info: return DiagnosticLevel::Informational; case Logging::Level::Warning: return DiagnosticLevel::Warning; case Logging::Level::Error: return DiagnosticLevel::Error; case Logging::Level::Crit: return DiagnosticLevel::Critical; } return DiagnosticLevel::Informational; } Resource::StringId ToResource(ConfigurationUnitIntent intent) { switch (intent) { case ConfigurationUnitIntent::Assert: return Resource::String::ConfigurationAssert; case ConfigurationUnitIntent::Inform: return Resource::String::ConfigurationInform; case ConfigurationUnitIntent::Apply: return Resource::String::ConfigurationApply; default: return Resource::StringId::Empty(); } } void InstallDscPackage(Execution::Context& context, std::string_view productId, std::unique_ptr& progressScope) { progressScope.reset(); context.Reporter.Info() << Resource::String::ConfigurationInstallDscPackage << std::endl; auto installDscContextPtr = context.CreateSubContext(); Execution::Context& installDscContext = *installDscContextPtr; auto previousThreadGlobals = installDscContext.SetForCurrentThread(); Manifest::ManifestInstaller dscInstaller; dscInstaller.ProductId = productId; installDscContext.Add(std::move(dscInstaller)); installDscContext.Args.AddArg(Execution::Args::Type::InstallScope, Manifest::ScopeToString(Manifest::ScopeEnum::User)); installDscContext.Args.AddArg(Execution::Args::Type::Silent); installDscContext.Args.AddArg(Execution::Args::Type::Force); installDscContext << MSStoreInstall; if (installDscContext.IsTerminated()) { AICLI_LOG(Config, Error, << "Failed to install dsc v3 package: " << productId); context.Reporter.Error() << Resource::String::ConfigurationInstallDscPackageFailed << std::endl; THROW_WIN32(ERROR_FILE_NOT_FOUND); } progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationInitializing()); } IConfigurationSetProcessorFactory CreateConfigurationSetProcessorFactory(Execution::Context& context) { #ifndef AICLI_DISABLE_TEST_HOOKS // Test could override the entire workflow task, but that may require keeping more in sync than simply setting the factory. if (s_override_IConfigurationSetProcessorFactory) { return s_override_IConfigurationSetProcessorFactory; } #endif auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationInitializing()); // The configuration set must have already been opened to create the proper factory. THROW_WIN32_IF(ERROR_INVALID_STATE, !context.Contains(Data::ConfigurationContext)); const auto& configurationContext = context.Get(); THROW_WIN32_IF(ERROR_INVALID_STATE, !configurationContext.Set()); IConfigurationSetProcessorFactory factory; ConfigurationRemoting::ProcessorEngine processorEngine = ConfigurationRemoting::DetermineProcessorEngine(configurationContext.Set()); THROW_HR_IF(WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, processorEngine == ConfigurationRemoting::ProcessorEngine::Unknown); // Since downgrading is not currently supported, only use dynamic if running limited. if (Runtime::IsRunningWithLimitedToken()) { factory = ConfigurationRemoting::CreateDynamicRuntimeFactory(processorEngine); } else { factory = ConfigurationRemoting::CreateOutOfProcessFactory(processorEngine); } if (processorEngine == ConfigurationRemoting::ProcessorEngine::PowerShell) { Configuration::SetModulePath(context, factory); } else if (processorEngine == ConfigurationRemoting::ProcessorEngine::DSCv3) { auto factoryMap = factory.as>(); if (context.Args.Contains(Args::Type::ConfigurationProcessorPath)) { factoryMap.Insert(ConfigurationRemoting::ToHString(ConfigurationRemoting::PropertyName::DscExecutablePath), Utility::ConvertToUTF16(context.Args.GetArg(Args::Type::ConfigurationProcessorPath))); } else { for (;;) { // Get the next transition for the state machine winrt::hstring nextTransition = factoryMap.Lookup(ConfigurationRemoting::ToHString(ConfigurationRemoting::PropertyName::FindDscStateMachine)); AICLI_LOG(Config, Verbose, << "FindDscStateMachine returned " << Utility::ConvertToUTF8(nextTransition)); if (nextTransition == L"Found") { break; } else if (nextTransition == L"InstallStable") { AICLI_LOG(Config, Info, << "Installing stable DSC package from store..."); InstallDscPackage(context, s_DscPackage_StoreId_Stable, progressScope); } else if (nextTransition == L"InstallPreview") { AICLI_LOG(Config, Info, << "Installing preview DSC package from store..."); InstallDscPackage(context, s_DscPackage_StoreId_Preview, progressScope); } else if (nextTransition == L"NotFound") { AICLI_LOG(Config, Error, << "Failed to find appropriate dsc v3 package, it must be provided by the user."); context.Reporter.Error() << Resource::String::ConfigurationInstallDscPackageFailed << std::endl; THROW_WIN32(ERROR_FILE_NOT_FOUND); } else { AICLI_LOG(Config, Error, << "FindDscStateMachine returned unknown value `" << Utility::ConvertToUTF8(nextTransition) << "`"); THROW_HR(E_UNEXPECTED); } } } if (Logging::Log().IsEnabled(Logging::Channel::Config, Logging::Level::Verbose)) { factoryMap.Insert(ConfigurationRemoting::ToHString(ConfigurationRemoting::PropertyName::DiagnosticTraceEnabled), L"True"); } } return factory; } void ConfigureProcessorForUse(Execution::Context& context, ConfigurationProcessor&& processor) { // Set the processor to the current level of the logging. processor.MinimumLevel(anon::ConvertLevel(Logging::Log().GetLevel())); processor.Caller(L"winget"); // Use same activity as the overall winget command processor.ActivityIdentifier(*Logging::Telemetry().GetActivityId()); // Apply winget telemetry setting to configuration processor.GenerateTelemetryEvents(!Settings::User().Get()); // Route the configuration diagnostics into the context's diagnostics logging processor.Diagnostics([&context](const winrt::Windows::Foundation::IInspectable&, const IDiagnosticInformation& diagnostics) { context.GetThreadGlobals().GetDiagnosticLogger().Write(Logging::Channel::Config, anon::ConvertLevel(diagnostics.Level()), Utility::ConvertToUTF8(diagnostics.Message())); }); if (context.Contains(Data::ConfigurationContext)) { context.Get().Processor(std::move(processor)); } else { ConfigurationContext configurationContext; configurationContext.Processor(std::move(processor)); context.Add(std::move(configurationContext)); } } winrt::hstring GetValueSetString(const ValueSet& valueSet, std::wstring_view value) { if (valueSet.HasKey(value)) { auto object = valueSet.Lookup(value); IPropertyValue property = object.try_as(); if (property && property.Type() == PropertyType::String) { return property.GetString(); } } return {}; } std::optional GetValueSetBool(const ValueSet& valueSet, std::wstring_view value) { if (valueSet.HasKey(value)) { auto object = valueSet.Lookup(value); IPropertyValue property = object.try_as(); if (property && property.Type() == PropertyType::Boolean) { return property.GetBoolean(); } } return {}; } // Contains the output functions and tracks whether any fields needed to be truncated. struct OutputHelper { OutputHelper(Execution::Context& context) : m_context(context) {} size_t ValuesTruncated = 0; // Converts a string from the configuration API surface for output. // All strings coming from the API are external data and not localizable by us. Utility::LocIndString ConvertForOutput(const std::string& input, size_t maxLines) { bool truncated = false; auto lines = Utility::SplitIntoLines(input); if (maxLines == 1 && lines.size() > 1) { // If the limit was one line, don't allow line breaks but do allow a second line of overflow lines.resize(1); maxLines = 2; truncated = true; } if (Utility::LimitOutputLines(lines, GetConsoleWidth(), maxLines)) { truncated = true; } if (truncated) { ++ValuesTruncated; } return Utility::LocIndString{ Utility::Join("\n", lines) }; } Utility::LocIndString ConvertForOutput(const winrt::hstring& input, size_t maxLines) { return ConvertForOutput(Utility::ConvertToUTF8(input), maxLines); } Utility::LocIndString ConvertIdentifier(const winrt::hstring& input) { return ConvertForOutput(input, 1); } Utility::LocIndString ConvertURI(const winrt::hstring& input) { return ConvertForOutput(input, 1); } Utility::LocIndString ConvertValue(const winrt::hstring& input) { return ConvertForOutput(input, 5); } Utility::LocIndString ConvertDetailsIdentifier(const winrt::hstring& input) { return ConvertForOutput(Utility::ConvertControlCodesToPictures(Utility::ConvertToUTF8(input)), 1); } Utility::LocIndString ConvertDetailsURI(const winrt::hstring& input) { return ConvertForOutput(Utility::ConvertControlCodesToPictures(Utility::ConvertToUTF8(input)), 1); } Utility::LocIndString ConvertDetailsValue(const winrt::hstring& input) { return ConvertForOutput(Utility::ConvertControlCodesToPictures(Utility::ConvertToUTF8(input)), 5); } void OutputValueWithTruncationWarningIfNeeded(const winrt::hstring& input) { size_t truncatedBefore = ValuesTruncated; m_context.Reporter.Info() << ConvertValue(input) << '\n'; if (ValuesTruncated > truncatedBefore) { m_context.Reporter.Warn() << Resource::String::ConfigurationWarningValueTruncated << std::endl; } } void OutputPropertyValue(const IPropertyValue property) { switch (property.Type()) { case PropertyType::String: m_context.Reporter.Info() << ' '; OutputValueWithTruncationWarningIfNeeded(property.GetString()); break; case PropertyType::Boolean: m_context.Reporter.Info() << ' ' << (property.GetBoolean() ? Utility::LocIndView("true") : Utility::LocIndView("false")) << '\n'; break; case PropertyType::Int64: m_context.Reporter.Info() << ' ' << property.GetInt64() << '\n'; break; default: m_context.Reporter.Info() << " [Debug:PropertyType="_liv << property.Type() << "]\n"_liv; break; } } void OutputValueSetAsArray(const ValueSet& valueSetArray, size_t indent) { Utility::LocIndString indentString{ std::string(indent, ' ') }; std::vector> arrayValues; for (const auto& arrayValue : valueSetArray) { if (arrayValue.Key() != L"treatAsArray") { arrayValues.emplace_back(std::make_pair(std::stoi(arrayValue.Key().c_str()), arrayValue.Value())); } } std::sort( arrayValues.begin(), arrayValues.end(), [](const std::pair& a, const std::pair& b) { return a.first < b.first; }); for (const auto& arrayValue : arrayValues) { auto arrayObject = arrayValue.second; IPropertyValue arrayProperty = arrayObject.try_as(); m_context.Reporter.Info() << indentString << "-"; if (arrayProperty) { OutputPropertyValue(arrayProperty); } else { ValueSet arraySubset = arrayObject.as(); auto size = arraySubset.Size(); if (size > 0) { // First one is special. auto first = arraySubset.First().Current(); m_context.Reporter.Info() << ' ' << ConvertIdentifier(first.Key()) << ':'; auto object = first.Value(); IPropertyValue property = object.try_as(); if (property) { OutputPropertyValue(property); } else { // If not an IPropertyValue, it must be a ValueSet ValueSet subset = object.as(); m_context.Reporter.Info() << '\n'; OutputValueSet(subset, indent + 4); } if (size > 1) { arraySubset.Remove(first.Key()); OutputValueSet(arraySubset, indent + 2); arraySubset.Insert(first.Key(), first.Value()); } } } } } void OutputValueSet(const ValueSet& valueSet, size_t indent) { Utility::LocIndString indentString{ std::string(indent, ' ') }; for (const auto& value : valueSet) { m_context.Reporter.Info() << indentString << ConvertIdentifier(value.Key()) << ':'; auto object = value.Value(); IPropertyValue property = object.try_as(); if (property) { OutputPropertyValue(property); } else { // If not an IPropertyValue, it must be a ValueSet ValueSet subset = object.as(); m_context.Reporter.Info() << '\n'; if (subset.HasKey(L"treatAsArray")) { OutputValueSetAsArray(subset, indent + 2); } else { OutputValueSet(subset, indent + 2); } } } } void OutputConfigurationUnitHeader(const ConfigurationUnit& unit, const winrt::hstring& name) { m_context.Reporter.Info() << ConfigurationUnitEmphasis << ConvertIdentifier(name); if (unit.Environment().Context() == SecurityContext::Elevated) { // Shield m_context.Reporter.Info() << "\xF0\x9F\x9B\xA1 "_liv; } winrt::hstring identifier = unit.Identifier(); if (!identifier.empty()) { m_context.Reporter.Info() << " ["_liv << ConvertIdentifier(identifier) << ']'; } m_context.Reporter.Info() << '\n'; } void OutputConfigurationUnitInformation(const ConfigurationUnit& unit) { IConfigurationUnitProcessorDetails details = unit.Details(); ValueSet metadata = unit.Metadata(); if (details) { // -- Sample output when IConfigurationUnitProcessorDetails present -- // UnitType [Identifier] // UnitDocumentationUri // Description // "Module": ModuleName "by" Author / Publisher (IsLocal / ModuleSource) // "Signed by": SigningCertificateChain (leaf subject CN) // PublishedModuleUri / ModuleDocumentationUri // ModuleDescription OutputConfigurationUnitHeader(unit, details.UnitType()); auto unitDocumentationUri = details.UnitDocumentationUri(); if (unitDocumentationUri) { m_context.Reporter.Info() << " "_liv << ConvertDetailsURI(unitDocumentationUri.DisplayUri()) << '\n'; } winrt::hstring unitDescriptionFromDetails = details.UnitDescription(); if (!unitDescriptionFromDetails.empty()) { m_context.Reporter.Info() << " "_liv << ConvertDetailsValue(unitDescriptionFromDetails) << '\n'; } auto unitDescriptionFromDirectives = GetValueSetString(metadata, s_Directive_Description); if (!unitDescriptionFromDirectives.empty()) { m_context.Reporter.Info() << " "_liv; OutputValueWithTruncationWarningIfNeeded(unitDescriptionFromDirectives); } auto author = ConvertDetailsIdentifier(details.Author()); if (author.empty()) { author = ConvertDetailsIdentifier(details.Publisher()); } auto moduleName = ConvertDetailsIdentifier(details.ModuleName()); if (!moduleName.empty()) { if (details.IsLocal()) { m_context.Reporter.Info() << " "_liv << Resource::String::ConfigurationModuleWithDetails(moduleName, author, Resource::String::ConfigurationLocal) << '\n'; } else { m_context.Reporter.Info() << " "_liv << Resource::String::ConfigurationModuleWithDetails(moduleName, author, ConvertDetailsIdentifier(details.ModuleSource())) << '\n'; } } // TODO: Currently the signature information is only for the top files. Maybe each item should be tagged? // TODO: Output signing information with additional details (like whether the certificate is trusted). Doing this with the validate command // seems like a good time, as that will also need to do the check in order to inform the user on the validation. // Just saying "Signed By: Foo" is going to lead to a false sense of trust if the signature is valid but not actually trusted. auto moduleUri = details.PublishedModuleUri(); if (!moduleUri) { moduleUri = details.ModuleDocumentationUri(); } if (moduleUri) { m_context.Reporter.Info() << " "_liv << ConvertDetailsURI(moduleUri.DisplayUri()) << '\n'; } winrt::hstring moduleDescription = details.ModuleDescription(); if (!moduleDescription.empty()) { m_context.Reporter.Info() << " "_liv << ConvertDetailsValue(moduleDescription) << '\n'; } } else { // -- Sample output when no IConfigurationUnitProcessorDetails present -- // Type [identifier] // Description (from directives) // "Module": module OutputConfigurationUnitHeader(unit, unit.Type()); auto description = GetValueSetString(metadata, s_Directive_Description); if (!description.empty()) { m_context.Reporter.Info() << " "_liv; OutputValueWithTruncationWarningIfNeeded(description); } auto module = GetValueSetString(metadata, s_Directive_Module); if (!module.empty()) { m_context.Reporter.Info() << " "_liv << Resource::String::ConfigurationModuleNameOnly(ConvertIdentifier(module)) << '\n'; } } // -- Sample output footer -- // Dependencies: dep1, dep2, ... // Settings: // <... settings splat> auto dependencies = unit.Dependencies(); if (dependencies.Size() > 0) { std::ostringstream allDependencies; for (const winrt::hstring& dependency : dependencies) { allDependencies << ' ' << ConvertIdentifier(dependency); } m_context.Reporter.Info() << " "_liv << Resource::String::ConfigurationDependencies(Utility::LocIndString{ std::move(allDependencies).str() }) << '\n'; } ValueSet settings = unit.Settings(); if (settings.Size() > 0) { m_context.Reporter.Info() << " "_liv << Resource::String::ConfigurationSettings << '\n'; OutputValueSet(settings, 4); } m_context.Reporter.Info() << std::flush; } private: Execution::Context& m_context; }; void OutputConfigurationUnitHeader(Execution::Context& context, const ConfigurationUnit& unit, const winrt::hstring& name) { OutputHelper helper{ context }; helper.OutputConfigurationUnitHeader(unit, name); } void LogFailedGetConfigurationUnitDetails(const ConfigurationUnit& unit, const IConfigurationUnitResultInformation& resultInformation) { if (FAILED(resultInformation.ResultCode())) { AICLI_LOG(Config, Error, << "Failed to get unit details for " << Utility::ConvertToUTF8(unit.Type()) << " : 0x" << Logging::SetHRFormat << resultInformation.ResultCode() << '\n' << Utility::ConvertToUTF8(resultInformation.Description()) << '\n' << Utility::ConvertToUTF8(resultInformation.Details())); } } struct UnitFailedMessageData { Utility::LocIndString Message; bool ShowDescription = true; }; // TODO: We may need a detailed result code to enable the internal error to be exposed. // Additionally, some of the processor exceptions that generate these errors should be enlightened to produce better, localized descriptions. UnitFailedMessageData GetUnitFailedData(const ConfigurationUnit& unit, const IConfigurationUnitResultInformation& resultInformation) { int32_t resultCode = resultInformation.ResultCode(); switch (resultCode) { case WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER: return { Resource::String::ConfigurationUnitHasDuplicateIdentifier(Utility::LocIndString{ Utility::ConvertToUTF8(unit.Identifier()) }), false }; case WINGET_CONFIG_ERROR_MISSING_DEPENDENCY: return { Resource::String::ConfigurationUnitHasMissingDependency(Utility::LocIndString{ Utility::ConvertToUTF8(resultInformation.Details()) }), false }; case WINGET_CONFIG_ERROR_ASSERTION_FAILED: return { Resource::String::ConfigurationUnitAssertHadNegativeResult(), false }; case WINGET_CONFIG_ERROR_UNIT_NOT_INSTALLED: return { Resource::String::ConfigurationUnitNotFoundInModule(), false }; case WINGET_CONFIG_ERROR_UNIT_NOT_FOUND_REPOSITORY: return { Resource::String::ConfigurationUnitNotFound(), false }; case WINGET_CONFIG_ERROR_UNIT_MULTIPLE_MATCHES: return { Resource::String::ConfigurationUnitMultipleMatches(), false }; case WINGET_CONFIG_ERROR_UNIT_INVOKE_GET: return { Resource::String::ConfigurationUnitFailedDuringGet(), true }; case WINGET_CONFIG_ERROR_UNIT_INVOKE_TEST: return { Resource::String::ConfigurationUnitFailedDuringTest(), true }; case WINGET_CONFIG_ERROR_UNIT_INVOKE_SET: return { Resource::String::ConfigurationUnitFailedDuringSet(), true }; case WINGET_CONFIG_ERROR_UNIT_MODULE_CONFLICT: return { Resource::String::ConfigurationUnitModuleConflict(), false }; case WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE: return { Resource::String::ConfigurationUnitModuleImportFailed(), false }; case WINGET_CONFIG_ERROR_UNIT_INVOKE_INVALID_RESULT: return { Resource::String::ConfigurationUnitReturnedInvalidResult(), false }; case WINGET_CONFIG_ERROR_UNIT_SETTING_CONFIG_ROOT: return { Resource::String::ConfigurationUnitSettingConfigRoot(), false }; case WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE_ADMIN: return { Resource::String::ConfigurationUnitImportModuleAdmin(), false }; } switch (resultInformation.ResultSource()) { case ConfigurationUnitResultSource::ConfigurationSet: return { Resource::String::ConfigurationUnitFailedConfigSet(resultCode), true }; case ConfigurationUnitResultSource::Internal: return { Resource::String::ConfigurationUnitFailedInternal(resultCode), true }; case ConfigurationUnitResultSource::Precondition: return { Resource::String::ConfigurationUnitFailedPrecondition(resultCode), true }; case ConfigurationUnitResultSource::SystemState: return { Resource::String::ConfigurationUnitFailedSystemState(resultCode), true }; case ConfigurationUnitResultSource::UnitProcessing: return { Resource::String::ConfigurationUnitFailedUnitProcessing(resultCode), true }; } // All other errors use a generic message return { Resource::String::ConfigurationUnitFailed(resultCode), true }; } Utility::LocIndString GetUnitSkippedMessage(const IConfigurationUnitResultInformation& resultInformation) { int32_t resultCode = resultInformation.ResultCode(); switch (resultInformation.ResultCode()) { case WINGET_CONFIG_ERROR_MANUALLY_SKIPPED: return Resource::String::ConfigurationUnitManuallySkipped(); case WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED: return Resource::String::ConfigurationUnitNotRunDueToDependency(); case WINGET_CONFIG_ERROR_ASSERTION_FAILED: return Resource::String::ConfigurationUnitNotRunDueToFailedAssert(); } // If new cases arise and are not handled here, at least have a generic backstop message. return Resource::String::ConfigurationUnitSkipped(resultCode); } void OutputUnitRunFailure(Context& context, const ConfigurationUnit& unit, const IConfigurationUnitResultInformation& resultInformation) { std::string description = Utility::Trim(Utility::ConvertToUTF8(resultInformation.Description())); AICLI_LOG_LARGE_STRING(Config, Error, << "Configuration unit " << Utility::ConvertToUTF8(unit.Type()) << "[" << Utility::ConvertToUTF8(unit.Identifier()) << "] failed with code 0x" << Logging::SetHRFormat << resultInformation.ResultCode() << " and error message:\n" << description, Utility::ConvertToUTF8(resultInformation.Details())); UnitFailedMessageData messageData = GetUnitFailedData(unit, resultInformation); auto error = context.Reporter.Error(); error << " "_liv << messageData.Message << std::endl; if (messageData.ShowDescription && !description.empty()) { constexpr size_t maximumDescriptionLines = 3; size_t consoleWidth = GetConsoleWidth(); std::vector lines = Utility::SplitIntoLines(description, maximumDescriptionLines + 1); bool wasLimited = Utility::LimitOutputLines(lines, consoleWidth, maximumDescriptionLines); for (const auto& line : lines) { error << line << std::endl; } if (wasLimited || !resultInformation.Details().empty()) { error << Resource::String::ConfigurationDescriptionWasTruncated << std::endl; } } } // Coordinates an active progress scope and cancellation of the operation. template struct ProgressCancellationUnification { ProgressCancellationUnification(std::unique_ptr&& progressScope, const OperationT& operation) : m_progressScope(std::move(progressScope)), m_operation(operation) { SetCancellationFunction(); } void Reset() { m_cancelScope.reset(); m_progressScope.reset(); } Reporter::AsyncProgressScope& Progress() const { return *m_progressScope; } void Progress(std::unique_ptr&& progressScope) { m_cancelScope.reset(); m_progressScope = std::move(progressScope); SetCancellationFunction(); } OperationT& Operation() const { return m_operation; } private: void SetCancellationFunction() { if (m_progressScope) { m_cancelScope = m_progressScope->Callback().SetCancellationFunction([this]() { m_operation.Cancel(); }); } } std::unique_ptr m_progressScope; OperationT m_operation; IProgressCallback::CancelFunctionRemoval m_cancelScope; }; template ProgressCancellationUnification CreateProgressCancellationUnification( std::unique_ptr&& progressScope, const Operation& operation) { return { std::move(progressScope), operation }; } // The base type for progress reporting template struct ConfigurationSetProgressOutputBase { using Operation = IAsyncOperationWithProgress; ConfigurationSetProgressOutputBase(Context& context, const Operation& operation) : m_context(context), m_unification({}, operation) { operation.Progress([&](const Operation& operation, const ProgressType& data) { Progress(operation, data); }); } virtual void Progress(const Operation& operation, const ProgressType& data) = 0; protected: void MarkCompleted(const ConfigurationUnit& unit) { winrt::guid unitInstance = unit.InstanceIdentifier(); m_unitsCompleted.insert(unitInstance); } bool UnitHasPreviouslyCompleted(const ConfigurationUnit& unit) { winrt::guid unitInstance = unit.InstanceIdentifier(); return m_unitsCompleted.count(unitInstance) != 0; } // Sends VT progress to the console void OutputUnitCompletionProgress() { // TODO: Change progress reporting to enable separation of spinner and VT progress reporting // Preferably we want to be able to have: // 1. Spinner with indefinite progress VT before set application begins // 2. 1/N VT progress reporting for configuration units while also showing a spinner for the unit itself } void BeginProgress() { m_unification.Progress(m_context.Reporter.BeginAsyncProgress(true)); } void EndProgress() { m_unification.Reset(); } Context& m_context; private: ProgressCancellationUnification m_unification; std::set m_unitsCompleted; }; // Helper to handle progress callbacks from ApplyConfigurationSetAsync struct ApplyConfigurationSetProgressOutput final : public ConfigurationSetProgressOutputBase { using Operation = ConfigurationSetProgressOutputBase::Operation; ApplyConfigurationSetProgressOutput(Context& context, const Operation& operation) : ConfigurationSetProgressOutputBase(context, operation) { } void Progress(const Operation& operation, const ConfigurationSetChangeData& data) override { auto threadContext = m_context.SetForCurrentThread(); if (m_isFirstProgress) { HandleUnreportedProgress(operation.GetResults()); } switch (data.Change()) { case ConfigurationSetChangeEventType::SetStateChanged: { switch (data.SetState()) { case ConfigurationSetState::Pending: m_context.Reporter.Info() << Resource::String::ConfigurationWaitingOnAnother << std::endl; BeginProgress(); break; case ConfigurationSetState::InProgress: EndProgress(); break; case ConfigurationSetState::Completed: EndProgress(); break; } } break; case ConfigurationSetChangeEventType::UnitStateChanged: HandleUnitProgress(data.Unit(), data.UnitState(), data.ResultInformation()); break; } } // If no progress has been reported, this function will report the given results void HandleUnreportedProgress(const ApplyConfigurationSetResult& result) { if (m_isFirstProgress) { m_isFirstProgress = false; for (const ApplyConfigurationUnitResult& unitResult : result.UnitResults()) { HandleUnitProgress(unitResult.Unit(), unitResult.State(), unitResult.ResultInformation()); } } } private: void HandleUnitProgress(const ConfigurationUnit& unit, ConfigurationUnitState state, const IConfigurationUnitResultInformation& resultInformation) { if (UnitHasPreviouslyCompleted(unit)) { return; } switch (state) { case ConfigurationUnitState::Pending: // The unreported progress handler may send pending units, just ignore them break; case ConfigurationUnitState::InProgress: OutputUnitInProgressIfNeeded(unit); BeginProgress(); break; case ConfigurationUnitState::Completed: OutputUnitInProgressIfNeeded(unit); EndProgress(); if (SUCCEEDED(resultInformation.ResultCode())) { m_context.Reporter.Info() << " "_liv << Resource::String::ConfigurationUnitSuccessfullyApplied << std::endl; } else { OutputUnitRunFailure(m_context, unit, resultInformation); } MarkCompleted(unit); OutputUnitCompletionProgress(); break; case ConfigurationUnitState::Skipped: OutputUnitInProgressIfNeeded(unit); AICLI_LOG(Config, Warning, << "Configuration unit " << Utility::ConvertToUTF8(unit.Type()) << "[" << Utility::ConvertToUTF8(unit.Identifier()) << "] was skipped with code 0x" << Logging::SetHRFormat << resultInformation.ResultCode()); m_context.Reporter.Warn() << " "_liv << GetUnitSkippedMessage(resultInformation) << std::endl; MarkCompleted(unit); OutputUnitCompletionProgress(); break; } } void OutputUnitInProgressIfNeeded(const ConfigurationUnit& unit) { winrt::guid unitInstance = unit.InstanceIdentifier(); if (m_unitsSeen.count(unitInstance) == 0) { m_unitsSeen.insert(unitInstance); OutputConfigurationUnitHeader(m_context, unit, unit.Details() ? unit.Details().UnitType() : unit.Type()); } } std::set m_unitsSeen; bool m_isFirstProgress = true; }; // Helper to handle progress callbacks from TestConfigurationSetAsync struct TestConfigurationSetProgressOutput final : public ConfigurationSetProgressOutputBase { using Operation = ConfigurationSetProgressOutputBase::Operation; TestConfigurationSetProgressOutput(Context& context, const Operation& operation) : ConfigurationSetProgressOutputBase(context, operation) { // Start the spinner for the first unit being tested since we only receive completions BeginProgress(); } void Progress(const Operation& operation, const TestConfigurationUnitResult& data) override { auto threadContext = m_context.SetForCurrentThread(); if (m_isFirstProgress) { HandleUnreportedProgress(operation.GetResults()); } HandleUnitProgress(data.Unit(), data.TestResult(), data.ResultInformation()); } // If no progress has been reported, this function will report the given results void HandleUnreportedProgress(const TestConfigurationSetResult& result) { if (m_isFirstProgress) { m_isFirstProgress = false; for (const TestConfigurationUnitResult& unitResult : result.UnitResults()) { HandleUnitProgress(unitResult.Unit(), unitResult.TestResult(), unitResult.ResultInformation()); } } } private: void HandleUnitProgress(const ConfigurationUnit& unit, ConfigurationTestResult testResult, const IConfigurationUnitResultInformation& resultInformation) { if (UnitHasPreviouslyCompleted(unit)) { return; } EndProgress(); OutputConfigurationUnitHeader(m_context, unit, unit.Details() ? unit.Details().UnitType() : unit.Type()); switch (testResult) { case ConfigurationTestResult::Failed: OutputUnitRunFailure(m_context, unit, resultInformation); break; case ConfigurationTestResult::Negative: m_context.Reporter.Warn() << " "_liv << Resource::String::ConfigurationNotInDesiredState << std::endl; break; case ConfigurationTestResult::NotRun: m_context.Reporter.Warn() << " "_liv << Resource::String::ConfigurationNoTestRun << std::endl; break; case ConfigurationTestResult::Positive: m_context.Reporter.Info() << " "_liv << Resource::String::ConfigurationInDesiredState << std::endl; break; default: // ConfigurationTestResult::Unknown m_context.Reporter.Error() << " "_liv << Resource::String::ConfigurationUnexpectedTestResult(ToIntegral(testResult)) << std::endl; break; } MarkCompleted(unit); OutputUnitCompletionProgress(); BeginProgress(); } 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 SetNameAndOrigin(ConfigurationSet& set, std::filesystem::path& absolutePath) { // TODO: Consider how to properly determine a good value for name and origin. set.Name(absolutePath.filename().wstring()); set.Origin(absolutePath.parent_path().wstring()); set.Path(absolutePath.wstring()); } void OpenConfigurationSet(Execution::Context& context, const std::string& argPath, bool allowRemote) { auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationReadingConfigFile()); std::wstring argPathWide = Utility::ConvertToUTF16(argPath); bool isRemote = Utility::IsUrlRemote(argPath); std::filesystem::path absolutePath; Streams::IInputStream inputStream = nullptr; if (isRemote) { if (!allowRemote) { AICLI_LOG(Config, Error, << "Remote files are not supported"); AICLI_TERMINATE_CONTEXT(ERROR_NOT_SUPPORTED); } 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(); } OpenConfigurationSetResult openResult = nullptr; { auto openAction = context.Get().Processor().OpenConfigurationSetAsync(inputStream); auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { openAction.Cancel(); }); openResult = openAction.get(); } progressScope.reset(); if (FAILED_LOG(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()) { case WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE: context.Reporter.Error() << Resource::String::ConfigurationFieldInvalidType(Utility::LocIndString{ Utility::ConvertToUTF8(openResult.Field()) }) << std::endl; break; case WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE: context.Reporter.Error() << Resource::String::ConfigurationFieldInvalidValue(Utility::LocIndString{ Utility::ConvertToUTF8(openResult.Field()) }, Utility::LocIndString{ Utility::ConvertToUTF8(openResult.Value()) }) << std::endl; break; case WINGET_CONFIG_ERROR_MISSING_FIELD: context.Reporter.Error() << Resource::String::ConfigurationFieldMissing(Utility::LocIndString{ Utility::ConvertToUTF8(openResult.Field()) }) << std::endl; break; case WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION: context.Reporter.Error() << Resource::String::ConfigurationFileVersionUnknown(Utility::LocIndString{ Utility::ConvertToUTF8(openResult.Value()) }) << std::endl; break; case WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE: case WINGET_CONFIG_ERROR_INVALID_YAML: default: context.Reporter.Error() << Resource::String::ConfigurationFileInvalidYAML << std::endl; break; } if (openResult.Line() != 0) { context.Reporter.Error() << Resource::String::SeeLineAndColumn(openResult.Line(), openResult.Column()) << std::endl; } AICLI_TERMINATE_CONTEXT(openResult.ResultCode()); } ConfigurationSet result = openResult.Set(); // Fill out the information about the set based on it coming from a file. if (isRemote) { result.Name(Utility::GetFileNameFromURI(argPath).wstring()); result.Origin(argPathWide); // Do not set path. This means ${WinGetConfigRoot} not supported in remote configs. } else { SetNameAndOrigin(result, absolutePath); } context.Get().Set(result); } ConfigurationUnit CreateConfigurationUnitFromModuleResource(std::string_view moduleName, std::string_view resourceName, std::string_view descriptionResourceName, const Utility::Version& schemaVersion) { std::wstring moduleNameWide = Utility::ConvertToUTF16(moduleName); std::wstring resourceNameWide = Utility::ConvertToUTF16(resourceName); ConfigurationUnit unit; unit.Type(schemaVersion >= s_MinimumSchemaVersionModuleNameRequiredInType ? moduleNameWide + L'/' + resourceNameWide : resourceNameWide); unit.Identifier(unit.Type() + L'_' + Utility::ConvertToUTF16(Utility::GetRandomString())); ValueSet directives; directives.Insert(s_Directive_Module, PropertyValue::CreateString(moduleNameWide)); Utility::LocIndString description; if (!descriptionResourceName.empty()) { description = Resource::String::ConfigureExportUnitDescription(Utility::LocIndView{ descriptionResourceName }); } else { description = Resource::String::ConfigureExportUnitDescription(Utility::LocIndView{ resourceName }); } directives.Insert(s_Directive_Description, PropertyValue::CreateString(winrt::to_hstring(description.get()))); unit.Metadata(directives); return unit; } ConfigurationUnit CreateConfigurationUnitFromUnitType(std::wstring_view unitType, std::string_view descriptionResourceName = "") { ConfigurationUnit unit; unit.Type(unitType); unit.Identifier(unit.Type() + L'_' + Utility::ConvertToUTF16(Utility::GetRandomString())); ValueSet directives; Utility::LocIndString description; if (!descriptionResourceName.empty()) { description = Resource::String::ConfigureExportUnitDescription(Utility::LocIndView{ descriptionResourceName }); } else { description = Resource::String::ConfigureExportUnitDescription(Utility::LocIndView{ Utility::ConvertToUTF8(unitType) }); } directives.Insert(s_Directive_Description, PropertyValue::CreateString(winrt::to_hstring(description.get()))); unit.Metadata(directives); return unit; } ConfigurationUnit CreatePowerShellPackageUnit() { ConfigurationUnit unit = CreateConfigurationUnitFromUnitType(s_UnitType_WinGetPackage_DSCv3, "Microsoft.PowerShell"); ValueSet settings; settings.Insert(s_Setting_WinGetPackage_Id, PropertyValue::CreateString(s_Predefined_PowerShell_PackageId)); settings.Insert(s_Setting_WinGetPackage_Source, PropertyValue::CreateString(s_Predefined_PowerShell_PackageSource)); unit.Settings(settings); return unit; } ValueSet CreateValueSetFromStringVector(const std::vector& values) { ValueSet result; size_t index = 0; for (const auto& value : values) { std::wostringstream strstr; strstr << index++; result.Insert(strstr.str(), PropertyValue::CreateString(value)); } result.Insert(L"treatAsArray", PropertyValue::CreateBoolean(true)); return result; } // TODO: This is a workaround unit to ensure v2 dsc resource modules. Move to dsc v3 resource when available. ConfigurationUnit CreateRequiredModuleUnit(std::wstring_view moduleName, const ConfigurationUnit& dependentUnit) { std::wstring moduleNameString{ moduleName }; ConfigurationUnit unit = CreateConfigurationUnitFromUnitType(L"Microsoft.DSC.Transitional/RunCommandOnSet", Utility::ConvertToUTF8(moduleName)); ValueSet settings; settings.Insert(L"executable", PropertyValue::CreateString(L"pwsh")); std::vector arguments = { L"-NoProfile", L"-NoLogo", L"-Command", L"if (-not (Get-Module -ListAvailable -Name " + moduleNameString + L")) { Install-Module -Name " + moduleNameString + L" -Confirm:$False -Force -AllowPrerelease -AllowClobber }" }; settings.Insert(L"arguments", CreateValueSetFromStringVector(arguments)); unit.Settings(settings); unit.Dependencies().Append(dependentUnit.Identifier()); return unit; } std::wstring GetWinGetSourceUnitType(const ConfigurationContext& configContext) { Utility::Version schemaVersion = { Utility::ConvertToUTF8(configContext.Set().SchemaVersion()) }; ConfigurationRemoting::ProcessorEngine processorEngine = ConfigurationRemoting::DetermineProcessorEngine(configContext.Set()); if (schemaVersion >= s_MinimumSchemaVersionModuleNameRequiredInType) { if (processorEngine == ConfigurationRemoting::ProcessorEngine::DSCv3) { return std::wstring{ s_UnitType_WinGetSource_DSCv3 }; } else { return std::wstring{ s_Module_WinGetClient } + L'/' + std::wstring{ s_Unit_WinGetSource }; } } else { return std::wstring{ s_Unit_WinGetSource }; } } ConfigurationUnit CreateWinGetSourceUnit(const PackageCollection::Source& source, std::wstring_view unitType) { std::string sourceUnitId = source.Details.Name + '_' + source.Details.Type; std::wstring sourceUnitIdWide = Utility::ConvertToUTF16(sourceUnitId); ConfigurationUnit unit; unit.Type(unitType); unit.Identifier(sourceUnitIdWide); unit.Intent(ConfigurationUnitIntent::Apply); auto description = Resource::String::ConfigureExportUnitDescription(Utility::LocIndView{ sourceUnitId }); ValueSet directives; directives.Insert(s_Directive_Description, PropertyValue::CreateString(winrt::to_hstring(description.get()))); unit.Metadata(directives); ValueSet settings; settings.Insert(s_Setting_WinGetSource_Name, PropertyValue::CreateString(Utility::ConvertToUTF16(source.Details.Name))); settings.Insert(s_Setting_WinGetSource_Arg, PropertyValue::CreateString(Utility::ConvertToUTF16(source.Details.Arg))); settings.Insert(s_Setting_WinGetSource_Type, PropertyValue::CreateString(Utility::ConvertToUTF16(source.Details.Type))); if (WI_IsFlagSet(source.Details.TrustLevel, Repository::SourceTrustLevel::Trusted)) { settings.Insert(s_Setting_WinGetSource_TrustLevel, PropertyValue::CreateString(L"trusted")); } if (source.Details.Explicit) { settings.Insert(s_Setting_WinGetSource_Explicit, PropertyValue::CreateBoolean(true)); } if (source.Details.Priority != 0) { settings.Insert(s_Setting_WinGetSource_Priority, PropertyValue::CreateInt32(source.Details.Priority)); } unit.Settings(settings); unit.Environment().Context(SecurityContext::Elevated); return unit; } std::wstring GetWinGetPackageUnitType(const ConfigurationContext& configContext) { Utility::Version schemaVersion = { Utility::ConvertToUTF8(configContext.Set().SchemaVersion()) }; ConfigurationRemoting::ProcessorEngine processorEngine = ConfigurationRemoting::DetermineProcessorEngine(configContext.Set()); if (schemaVersion >= s_MinimumSchemaVersionModuleNameRequiredInType) { if (processorEngine == ConfigurationRemoting::ProcessorEngine::DSCv3) { return std::wstring{ s_UnitType_WinGetPackage_DSCv3 }; } else { return std::wstring{ s_Module_WinGetClient } + L'/' + std::wstring{ s_Unit_WinGetPackage }; } } else { return std::wstring{ s_Unit_WinGetPackage }; } } ConfigurationUnit CreateWinGetPackageUnit(const PackageCollection::Package& package, const PackageCollection::Source& source, bool includeVersion, const ConfigurationUnit& dependentUnit, std::wstring_view unitType) { std::wstring packageIdWide = Utility::ConvertToUTF16(package.Id); std::wstring sourceNameWide = Utility::ConvertToUTF16(source.Details.Name); ConfigurationUnit unit; unit.Type(unitType); unit.Identifier(sourceNameWide + L'_' + packageIdWide); unit.Intent(ConfigurationUnitIntent::Apply); auto description = Resource::String::ConfigureExportUnitInstallDescription(package.Id); ValueSet directives; directives.Insert(s_Directive_Description, PropertyValue::CreateString(winrt::to_hstring(description.get()))); unit.Metadata(directives); ValueSet settings; settings.Insert(s_Setting_WinGetPackage_Id, PropertyValue::CreateString(packageIdWide)); settings.Insert(s_Setting_WinGetPackage_Source, PropertyValue::CreateString(sourceNameWide)); if (includeVersion) { settings.Insert(s_Setting_WinGetPackage_Version, PropertyValue::CreateString(Utility::ConvertToUTF16(package.VersionAndChannel.GetVersion().ToString()))); } unit.Settings(settings); // TODO: We may consider setting security environment based on installer elevation requirements? // Add dependency if needed. if (dependentUnit) { auto dependencies = winrt::single_threaded_vector(); dependencies.Append(dependentUnit.Identifier()); unit.Dependencies(std::move(dependencies)); } return unit; } ApplyConfigurationUnitResult ApplyUnit(Execution::Context& context, ConfigurationUnit& unit) { unit.Intent(ConfigurationUnitIntent::Apply); auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationApplyingUnit()); ApplyConfigurationUnitResult applyResult = nullptr; { auto applyAction = context.Get().Processor().ApplyUnitAsync(unit); auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { applyAction.Cancel(); }); applyResult = applyAction.get(); } progressScope.reset(); return applyResult; } GetConfigurationUnitSettingsResult GetUnitSettings(Execution::Context& context, ConfigurationUnit& unit) { // This assumes there are no required properties for Get, but for example WinGetPackage requires the Id. // It is obviously wrong and will be wrong until Export is implemented for DSC v2 and a proper way to inform // about input to winget configure export is implemented. Drink the kool-aid and transcend. unit.Intent(ConfigurationUnitIntent::Inform); auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationGettingResourceSettings()); GetConfigurationUnitSettingsResult getResult = nullptr; { auto getAction = context.Get().Processor().GetUnitSettingsAsync(unit); auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { getAction.Cancel(); }); getResult = getAction.get(); } progressScope.reset(); return getResult; } GetAllConfigurationUnitsResult GetAllUnits(Execution::Context& context, ConfigurationUnit& unit) { unit.Intent(ConfigurationUnitIntent::Inform); auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationExportingUnit()); GetAllConfigurationUnitsResult getResult = nullptr; { auto getAction = context.Get().Processor().GetAllUnitsAsync(unit); auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { getAction.Cancel(); }); getResult = getAction.get(); } progressScope.reset(); return getResult; } std::vector ExportUnit(Execution::Context& context, ConfigurationUnit& unit, bool throwOnFailure = false) { std::vector result; context.Reporter.Info() << Resource::String::ConfigurationExportUnitStart(Utility::LocIndView{ Utility::ConvertToUTF8(unit.Type()) }) << std::endl; // Try export first auto exportResult = GetAllUnits(context, unit); auto exportResultCode = exportResult.ResultInformation().ResultCode(); if (SUCCEEDED(exportResultCode)) { for (auto resultUnit : exportResult.Units()) { result.emplace_back(std::move(resultUnit)); } } else { AICLI_LOG(Config, Warning, << "Failed GetAllUnits. Will try GetUnitSettings."); LogFailedGetConfigurationUnitDetails(unit, exportResult.ResultInformation()); // Try GetUnitSettings if export failed. auto getResult = GetUnitSettings(context, unit); auto getResultCode = getResult.ResultInformation().ResultCode(); if (getResultCode == WINGET_CONFIG_ERROR_UNIT_NOT_FOUND_REPOSITORY) { // Retry if it fails with not found in the case the module is a pre-released one. AICLI_LOG(Config, Info, << "Failed GetUnitSettings because module not found. Will try allow prerelease."); auto directives = unit.Metadata(); directives.Insert(s_Directive_AllowPrerelease, PropertyValue::CreateBoolean(true)); unit.Metadata(directives); getResult = GetUnitSettings(context, unit); } if (FAILED(getResult.ResultInformation().ResultCode())) { AICLI_LOG(Config, Error, << "Failed Get Unit Settings"); LogFailedGetConfigurationUnitDetails(unit, getResult.ResultInformation()); if (throwOnFailure) { context.Reporter.Error() << Resource::String::ConfigurationExportUnitFailed << std::endl; OutputUnitRunFailure(context, unit, getResult.ResultInformation()); THROW_HR(WINGET_CONFIG_ERROR_GET_FAILED); } else { context.Reporter.Warn() << Resource::String::ConfigurationExportUnitFailed << std::endl; } } else { unit.Settings(getResult.Settings()); result.emplace_back(unit); } } return result; } void AddDependentUnit(std::vector& units, const ConfigurationUnit& dependentUnit) { for (auto& unit : units) { unit.Dependencies().Append(dependentUnit.Identifier()); } } void AddElevatedEnvironment(std::vector& units) { for (auto& unit : units) { unit.Environment().Context(SecurityContext::Elevated); } } std::vector GetAllUnitProcessors(Execution::Context& context) { ConfigurationContext& configContext = context.Get(); std::vector result; // Only supported by dsc v3 processor. if (ConfigurationRemoting::ProcessorEngine::DSCv3 == ConfigurationRemoting::DetermineProcessorEngine(configContext.Set())) { auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationGettingUnitProcessors()); { FindUnitProcessorsOptions findOptions; findOptions.UnitDetailFlags(ConfigurationUnitDetailFlags::Local); auto findAction = context.Get().Processor().FindUnitProcessorsAsync(findOptions); auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { findAction.Cancel(); }); for (auto unitProcessor : findAction.get()) { result.emplace_back(std::move(unitProcessor)); } } progressScope.reset(); } return result; } void ExportPredefinedResources(Execution::Context& context) { ConfigurationContext& configContext = context.Get(); // PowerShell package needs to be present for certain predefined modules to work. ConfigurationUnit powerShellPackageUnit = CreatePowerShellPackageUnit(); configContext.Set().Units().Append(powerShellPackageUnit); // Apply the unit to make sure it's on the system. context.Reporter.Info() << Resource::String::ConfigurationExportInstallRequiredModule(Utility::LocIndView{ "Microsoft PowerShell Package" }) << std::endl; auto applyPowerShellResult = ApplyUnit(context, powerShellPackageUnit); if (FAILED(applyPowerShellResult.ResultInformation().ResultCode())) { AICLI_LOG(Config, Warning, << "Failed to ensure module. [Microsoft PowerShell Package] Related settings may not be exported."); LogFailedGetConfigurationUnitDetails(powerShellPackageUnit, applyPowerShellResult.ResultInformation()); context.Reporter.Warn() << Resource::String::ConfigurationExportInstallRequiredModuleFailed << std::endl; } for (const auto& resources : PredefinedResourcesForExport()) { std::optional requiredModuleUnit; if (!resources.RequiredModule.empty()) { requiredModuleUnit = CreateRequiredModuleUnit(resources.RequiredModule, powerShellPackageUnit); // Apply the unit to make sure it's on the system. context.Reporter.Info() << Resource::String::ConfigurationExportInstallRequiredModule(Utility::LocIndView{ Utility::ConvertToUTF8(resources.RequiredModule) }) << std::endl; auto applyResult = ApplyUnit(context, requiredModuleUnit.value()); if (SUCCEEDED(applyResult.ResultInformation().ResultCode())) { configContext.Set().Units().Append(requiredModuleUnit.value()); } else { AICLI_LOG(Config, Warning, << "Failed to ensure module. [" << Utility::ConvertToUTF8(resources.RequiredModule) << "] Related settings will not be exported."); LogFailedGetConfigurationUnitDetails(requiredModuleUnit.value(), applyResult.ResultInformation()); context.Reporter.Warn() << Resource::String::ConfigurationExportInstallRequiredModuleFailed << std::endl; continue; } } for (const auto& resourceInfo : resources.ResourceInfos) { auto resourceUnit = CreateConfigurationUnitFromUnitType(resourceInfo.UnitType); auto exportedUnits = ExportUnit(context, resourceUnit); if (requiredModuleUnit) { AddDependentUnit(exportedUnits, requiredModuleUnit.value()); } // The dynamic processor factory does not support operating elevated units without a set. // Luckily the Get/Export for all PreDefinedResources do not require elevation. // Here we add elevation environment to exported results. if (resourceInfo.ElevationRequired) { AddElevatedEnvironment(exportedUnits); } for (auto exportedUnit : exportedUnits) { configContext.Set().Units().Append(std::move(exportedUnit)); } } } } // Contains a tree of all unit processors by their path. struct UnitProcessorTree { private: struct SourceAndPackage { PackageCollection::Source Source; PackageCollection::Package Package; }; struct Node { // Packages whose installed location is at this node std::vector Packages; // Units whose location is at this node. std::vector Units; }; Filesystem::PathTree m_pathTree; Node& FindNodeForFilePath(const winrt::hstring& filePath) { std::filesystem::path path{ std::wstring{ filePath } }; return m_pathTree.FindOrInsert(path.parent_path()); } public: UnitProcessorTree(std::vector&& unitProcessors) { for (auto&& unit : unitProcessors) { IConfigurationUnitProcessorDetails3 unitProcessor3; if (unit.try_as(unitProcessor3)) { winrt::hstring unitPath = unitProcessor3.Path(); AICLI_LOG(Config, Verbose, << "Found unit `" << Utility::ConvertToUTF8(unit.UnitType()) << "` at: " << Utility::ConvertToUTF8(unitPath)); Node& node = FindNodeForFilePath(unitPath); node.Units.emplace_back(std::move(unit)); } } } void PlacePackage(const PackageCollection::Source& source, const PackageCollection::Package& package) { Node* node = m_pathTree.Find(package.InstalledLocation); if (node) { node->Packages.emplace_back(SourceAndPackage{ source, package }); } } std::vector GetResourcesForPackage(const PackageCollection::Package& package) const { std::vector result; m_pathTree.VisitIf( package.InstalledLocation, [&](const Node& node) { for (const auto& unit : node.Units) { result.emplace_back(unit); } }, [](const Node& node) { return node.Packages.empty(); }); return result; } }; void ProcessPackagesForConfigurationExportAll(Execution::Context& context) { ConfigurationContext& configContext = context.Get(); std::wstring sourceUnitType = GetWinGetSourceUnitType(configContext); std::wstring packageUnitType = GetWinGetPackageUnitType(configContext); // This will be later used by per package settings export. std::vector unitProcessors; try { unitProcessors = GetAllUnitProcessors(context); } catch (...) { AICLI_LOG(Config, Warning, << "Finding unit processors failed. Individual package settings will not be exported."); context.Reporter.Warn() << Resource::String::ConfigurationExportFailedToGetUnitProcessors << std::endl; } auto exclusionList = PackageSettingsExclusionList(); // Filter out processors in exclusion list. for (auto itr = unitProcessors.begin(); itr != unitProcessors.end(); /* itr incremented in the logic */) { bool processorRemoved = false; for (const auto& exclusionItem : exclusionList) { if (Utility::CaseInsensitiveStartsWith(itr->UnitType(), exclusionItem)) { itr = unitProcessors.erase(itr); processorRemoved = true; break; } } if (!processorRemoved) { itr++; } } // Build a tree of the unit processors and place packages onto it to indicate nearest ownership. UnitProcessorTree unitProcessorTree{ std::move(unitProcessors) }; for (const auto& source : context.Get().Sources) { for (const auto& package : source.Packages) { unitProcessorTree.PlacePackage(source, package); } } for (const auto& source : context.Get().Sources) { // Create WinGetSource unit ConfigurationUnit sourceUnit = anon::CreateWinGetSourceUnit(source, sourceUnitType); configContext.Set().Units().Append(sourceUnit); for (const auto& package : source.Packages) { AICLI_LOG(Config, Verbose, << "Exporting package `" << package.Id << "` at: " << package.InstalledLocation); auto packageUnit = anon::CreateWinGetPackageUnit(package, source, context.Args.Contains(Args::Type::IncludeVersions), sourceUnit, packageUnitType); configContext.Set().Units().Append(packageUnit); // Try package settings export. auto unitsForPackage = unitProcessorTree.GetResourcesForPackage(package); for (const auto& unit : unitsForPackage) { winrt::hstring unitType = unit.UnitType(); AICLI_LOG(Config, Verbose, << " exporting unit `" << Utility::ConvertToUTF8(unitType)); ConfigurationUnit configUnit = anon::CreateConfigurationUnitFromUnitType( unitType, Utility::ConvertToUTF8(packageUnit.Identifier())); auto exportedUnits = anon::ExportUnit(context, configUnit); anon::AddDependentUnit(exportedUnits, packageUnit); for (const auto& exportedUnit : exportedUnits) { configContext.Set().Units().Append(exportedUnit); } } } } } void ProcessPackagesForConfigurationExportSingle(Execution::Context& context) { ConfigurationContext& configContext = context.Get(); // When exporting single WinGetPackage unit, the WinGetPackage unit can be used as a dependent unit for following configuration unit. std::optional singlePackageUnit; if (context.Args.Contains(Execution::Args::Type::ConfigurationExportPackageId)) { const auto& exportSources = context.Get().Sources; // There should be 1 package under 1 source. THROW_HR_IF(E_UNEXPECTED, exportSources.size() != 1 || exportSources[0].Packages.size() != 1); ConfigurationUnit sourceUnit = anon::CreateWinGetSourceUnit(exportSources[0], GetWinGetSourceUnitType(configContext)); configContext.Set().Units().Append(sourceUnit); singlePackageUnit = anon::CreateWinGetPackageUnit(exportSources[0].Packages[0], exportSources[0], context.Args.Contains(Args::Type::IncludeVersions), sourceUnit, GetWinGetPackageUnitType(configContext)); configContext.Set().Units().Append(singlePackageUnit.value()); } if (context.Args.Contains(Execution::Args::Type::ConfigurationExportModule, Execution::Args::Type::ConfigurationExportResource)) { auto configUnit = anon::CreateConfigurationUnitFromModuleResource( context.Args.GetArg(Args::Type::ConfigurationExportModule), context.Args.GetArg(Args::Type::ConfigurationExportResource), singlePackageUnit ? Utility::ConvertToUTF8(singlePackageUnit->Identifier()) : "", Utility::Version{ Utility::ConvertToUTF8(configContext.Set().SchemaVersion()) }); auto exportedUnits = anon::ExportUnit(context, configUnit, true); if (singlePackageUnit) { anon::AddDependentUnit(exportedUnits, singlePackageUnit.value()); } for (auto exportedUnit : exportedUnits) { configContext.Set().Units().Append(exportedUnit); } } } bool HistorySetMatchesInput(const ConfigurationSet& set, const std::string& foldedInput) { if (foldedInput.empty()) { return false; } if (Utility::FoldCase(Utility::NormalizedString{ set.Name() }) == foldedInput) { return true; } std::ostringstream identifierStream; identifierStream << set.InstanceIdentifier(); std::string identifier = identifierStream.str(); THROW_HR_IF(E_UNEXPECTED, identifier.empty()); std::size_t startPosition = 0; if (identifier[0] == '{' && foldedInput[0] != '{') { startPosition = 1; } std::string_view identifierView = identifier; identifierView = identifierView.substr(startPosition); return Utility::CaseInsensitiveStartsWith(identifierView, foldedInput); } Resource::LocString ToLocString(ConfigurationSetState state) { switch (state) { case ConfigurationSetState::Pending: return Resource::String::ConfigurationSetStatePending; case ConfigurationSetState::InProgress: return Resource::String::ConfigurationSetStateInProgress; case ConfigurationSetState::Completed: return Resource::String::ConfigurationSetStateCompleted; case ConfigurationSetState::Unknown: default: return Resource::String::ConfigurationSetStateUnknown; } } Resource::LocString ToLocString(ConfigurationUnitState state) { switch (state) { case ConfigurationUnitState::Pending: return Resource::String::ConfigurationUnitStatePending; case ConfigurationUnitState::InProgress: return Resource::String::ConfigurationUnitStateInProgress; case ConfigurationUnitState::Completed: return Resource::String::ConfigurationUnitStateCompleted; case ConfigurationUnitState::Skipped: return Resource::String::ConfigurationUnitStateSkipped; case ConfigurationUnitState::Unknown: default: return Resource::String::ConfigurationUnitStateUnknown; } } std::string_view ToString(ConfigurationChangeEventType type) { switch (type) { case ConfigurationChangeEventType::SetAdded: return "SetAdded"; case ConfigurationChangeEventType::SetStateChanged: return "SetStateChanged"; case ConfigurationChangeEventType::SetRemoved: return "SetRemoved"; case ConfigurationChangeEventType::Unknown: default: return "Unknown"; } } std::string_view ToString(ConfigurationUnitResultSource source) { switch (source) { case ConfigurationUnitResultSource::Internal: return "Internal"; case ConfigurationUnitResultSource::ConfigurationSet: return "ConfigurationSet"; case ConfigurationUnitResultSource::UnitProcessing: return "UnitProcessing"; case ConfigurationUnitResultSource::SystemState: return "SystemState"; case ConfigurationUnitResultSource::Precondition: return "Precondition"; case ConfigurationUnitResultSource::None: default: return "None"; } } } void CreateConfigurationProcessor(Context& context) { anon::ConfigureProcessorForUse(context, ConfigurationProcessor{ anon::CreateConfigurationSetProcessorFactory(context) }); } void CreateConfigurationProcessorWithoutFactory(Execution::Context& context) { anon::ConfigureProcessorForUse(context, ConfigurationProcessor{ IConfigurationSetProcessorFactory{ nullptr } }); } void OpenConfigurationSet(Context& context) { if (context.Args.Contains(Args::Type::ConfigurationFile)) { std::string argPath{ context.Args.GetArg(Args::Type::ConfigurationFile) }; anon::OpenConfigurationSet(context, argPath, true); } else { THROW_HR_IF(E_UNEXPECTED, !context.Args.Contains(Args::Type::ConfigurationHistoryItem)); context << GetConfigurationSetHistory << SelectSetFromHistory; } } void CreateOrOpenConfigurationSet::operator()(Context& context) const { std::string argPath{ context.Args.GetArg(Args::Type::OutputFile) }; if (std::filesystem::exists(argPath) && !m_createAlways) { anon::OpenConfigurationSet(context, argPath, false); } else { ConfigurationSet set; set.SchemaVersion(Utility::ConvertToUTF16(m_defaultSchemaVersion)); set.Environment().ProcessorIdentifier(ConfigurationRemoting::ToString(ConfigurationRemoting::ProcessorEngine::DSCv3)); std::wstring argPathWide = Utility::ConvertToUTF16(argPath); auto absolutePath = std::filesystem::weakly_canonical(std::filesystem::path{ argPathWide }); anon::SetNameAndOrigin(set, absolutePath); context.Get().Set(set); } } void ShowConfigurationSet(Context& context) { ConfigurationContext& configContext = context.Get(); if (configContext.Set().Units().Size() == 0) { context.Reporter.Warn() << Resource::String::ConfigurationFileEmpty << std::endl; // This isn't an error termination, but there is no reason to proceed. AICLI_TERMINATE_CONTEXT(S_FALSE); } auto gettingDetailString = Resource::String::ConfigurationGettingDetails(); auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(gettingDetailString); auto getDetailsOperation = configContext.Processor().GetSetDetailsAsync(configContext.Set(), ConfigurationUnitDetailFlags::ReadOnly); auto unification = anon::CreateProgressCancellationUnification(std::move(progressScope), getDetailsOperation); bool suppressDetailsOutput = context.Args.Contains(Args::Type::ConfigurationSuppressPrologue); anon::OutputHelper outputHelper{ context }; uint32_t unitsShown = 0; if (!suppressDetailsOutput) { getDetailsOperation.Progress([&](const IAsyncOperationWithProgress& operation, const GetConfigurationUnitDetailsResult&) { auto threadContext = context.SetForCurrentThread(); unification.Reset(); auto unitResults = operation.GetResults().UnitResults(); for (unitsShown; unitsShown < unitResults.Size(); ++unitsShown) { GetConfigurationUnitDetailsResult unitResult = unitResults.GetAt(unitsShown); anon::LogFailedGetConfigurationUnitDetails(unitResult.Unit(), unitResult.ResultInformation()); outputHelper.OutputConfigurationUnitInformation(unitResult.Unit()); } progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(gettingDetailString); unification.Progress(std::move(progressScope)); }); } HRESULT hr = S_OK; GetConfigurationSetDetailsResult result = nullptr; try { result = getDetailsOperation.get(); } catch (...) { hr = LOG_CAUGHT_EXCEPTION(); } unification.Reset(); if (context.IsTerminated()) { // The context should only be terminated on us due to cancellation context.Reporter.Error() << Resource::String::Cancelled << std::endl; return; } if (FAILED(hr)) { // Failing to get details might not be fatal, warn about it but proceed context.Reporter.Warn() << Resource::String::ConfigurationFailedToGetDetails << std::endl; } // Handle any missing progress callbacks that are in the results if (result && !suppressDetailsOutput) { auto unitResults = result.UnitResults(); if (unitResults) { for (unitsShown; unitsShown < unitResults.Size(); ++unitsShown) { GetConfigurationUnitDetailsResult unitResult = unitResults.GetAt(unitsShown); anon::LogFailedGetConfigurationUnitDetails(unitResult.Unit(), unitResult.ResultInformation()); outputHelper.OutputConfigurationUnitInformation(unitResult.Unit()); } } } // Handle any units that are NOT in the results (due to an exception part of the way through) if (!suppressDetailsOutput) { auto allUnits = configContext.Set().Units(); for (unitsShown; unitsShown < allUnits.Size(); ++unitsShown) { ConfigurationUnit unit = allUnits.GetAt(unitsShown); outputHelper.OutputConfigurationUnitInformation(unit); } } if (outputHelper.ValuesTruncated) { // Using error to make this stand out from other warnings context.Reporter.Error() << Resource::String::ConfigurationWarningSetViewTruncated << std::endl; } } void ShowConfigurationSetConflicts(Execution::Context& context) { UNREFERENCED_PARAMETER(context); } void ConfirmConfigurationProcessing::operator()(Execution::Context& context) const { context.Reporter.Warn() << Resource::String::ConfigurationWarning << std::endl; if (!context.Args.Contains(Args::Type::ConfigurationAcceptWarning)) { context << RequireInteractivity(WINGET_CONFIG_ERROR_WARNING_NOT_ACCEPTED); if (context.IsTerminated()) { return; } auto promptString = m_isApply ? Resource::String::ConfigurationWarningPromptApply : Resource::String::ConfigurationWarningPromptTest; if (!context.Reporter.PromptForBoolResponse(promptString, Reporter::Level::Warning, false)) { AICLI_TERMINATE_CONTEXT(WINGET_CONFIG_ERROR_WARNING_NOT_ACCEPTED); } } } void ApplyConfigurationSet(Execution::Context& context) { ApplyConfigurationSetResult result = nullptr; ConfigurationContext& configContext = context.Get(); { auto applyOperation = configContext.Processor().ApplySetAsync(configContext.Set(), ApplyConfigurationSetFlags::None); anon::ApplyConfigurationSetProgressOutput progress{ context, applyOperation }; result = applyOperation.get(); progress.HandleUnreportedProgress(result); } if (FAILED(result.ResultCode())) { context.Reporter.Error() << Resource::String::ConfigurationFailedToApply << std::endl; // TODO: Summarize failed configuration units, especially if we put more output for each one during execution AICLI_TERMINATE_CONTEXT(result.ResultCode()); } else { context.Reporter.Info() << Resource::String::ConfigurationSuccessfullyApplied << std::endl; } } void TestConfigurationSet(Execution::Context& context) { TestConfigurationSetResult result = nullptr; ConfigurationContext& configContext = context.Get(); { auto testOperation = configContext.Processor().TestSetAsync(configContext.Set()); anon::TestConfigurationSetProgressOutput progress{ context, testOperation }; result = testOperation.get(); progress.HandleUnreportedProgress(result); } switch (result.TestResult()) { case ConfigurationTestResult::Failed: context.Reporter.Error() << Resource::String::ConfigurationFailedToTest << std::endl; AICLI_TERMINATE_CONTEXT(WINGET_CONFIG_ERROR_TEST_FAILED); break; case ConfigurationTestResult::Negative: context.Reporter.Warn() << Resource::String::ConfigurationNotInDesiredState << std::endl; context.SetTerminationHR(S_FALSE); break; case ConfigurationTestResult::NotRun: context.Reporter.Warn() << Resource::String::ConfigurationNoTestRun << std::endl; AICLI_TERMINATE_CONTEXT(WINGET_CONFIG_ERROR_TEST_NOT_RUN); break; case ConfigurationTestResult::Positive: context.Reporter.Info() << Resource::String::ConfigurationInDesiredState << std::endl; break; default: // ConfigurationTestResult::Unknown context.Reporter.Error() << Resource::String::ConfigurationUnexpectedTestResult(ToIntegral(result.TestResult())) << std::endl; AICLI_TERMINATE_CONTEXT(E_FAIL); break; } } void ValidateConfigurationSetSemantics(Execution::Context& context) { ConfigurationContext& configContext = context.Get(); if (configContext.Set().Units().Size() == 0) { context.Reporter.Warn() << Resource::String::ConfigurationFileEmpty << std::endl; // This isn't an error termination, but there is no reason to proceed. AICLI_TERMINATE_CONTEXT(S_FALSE); } ApplyConfigurationSetResult result = configContext.Processor().ApplySet(configContext.Set(), ApplyConfigurationSetFlags::PerformConsistencyCheckOnly); if (FAILED(result.ResultCode())) { for (const auto& unitResult : result.UnitResults()) { IConfigurationUnitResultInformation resultInformation = unitResult.ResultInformation(); winrt::hresult resultCode = resultInformation.ResultCode(); if (FAILED(resultCode)) { ConfigurationUnit unit = unitResult.Unit(); anon::OutputConfigurationUnitHeader(context, unit, unit.Type()); switch (resultCode) { case WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER: context.Reporter.Error() << " "_liv << Resource::String::ConfigurationUnitHasDuplicateIdentifier(Utility::LocIndString{ Utility::ConvertToUTF8(unit.Identifier()) }) << std::endl; break; case WINGET_CONFIG_ERROR_MISSING_DEPENDENCY: context.Reporter.Error() << " "_liv << Resource::String::ConfigurationUnitHasMissingDependency(Utility::LocIndString{ Utility::ConvertToUTF8(resultInformation.Details()) }) << std::endl; break; case WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED: context.Reporter.Error() << " "_liv << Resource::String::ConfigurationUnitIsPartOfDependencyCycle << std::endl; break; default: context.Reporter.Error() << " "_liv << Resource::String::ConfigurationUnitFailed(static_cast(resultCode)) << std::endl; break; } } } AICLI_TERMINATE_CONTEXT(result.ResultCode()); } } void ValidateConfigurationSetUnitProcessors(Execution::Context& context) { ConfigurationContext& configContext = context.Get(); // TODO: We could optimize this by creating a set with unique resource units // First get the local details auto gettingDetailString = Resource::String::ConfigurationGettingDetails(); auto progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(gettingDetailString); auto getLocalDetailsOperation = configContext.Processor().GetSetDetailsAsync(configContext.Set(), ConfigurationUnitDetailFlags::Local); auto unification = anon::CreateProgressCancellationUnification(std::move(progressScope), getLocalDetailsOperation); HRESULT getLocalHR = S_OK; GetConfigurationSetDetailsResult getLocalResult = nullptr; try { getLocalResult = getLocalDetailsOperation.get(); } catch (...) { getLocalHR = LOG_CAUGHT_EXCEPTION(); } unification.Reset(); if (context.IsTerminated()) { // The context should only be terminated on us due to cancellation context.Reporter.Error() << Resource::String::Cancelled << std::endl; return; } if (FAILED(getLocalHR)) { // Failing to get details might not be fatal, warn about it but proceed context.Reporter.Warn() << Resource::String::ConfigurationFailedToGetDetails << std::endl; } // Next get the details from the catalog progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(gettingDetailString); auto getCatalogDetailsOperation = configContext.Processor().GetSetDetailsAsync(configContext.Set(), ConfigurationUnitDetailFlags::Catalog); unification = anon::CreateProgressCancellationUnification(std::move(progressScope), getCatalogDetailsOperation); HRESULT getCatalogHR = S_OK; GetConfigurationSetDetailsResult getCatalogResult = nullptr; try { getCatalogResult = getCatalogDetailsOperation.get(); } catch (...) { getCatalogHR = LOG_CAUGHT_EXCEPTION(); } unification.Reset(); if (context.IsTerminated()) { // The context should only be terminated on us due to cancellation context.Reporter.Error() << Resource::String::Cancelled << std::endl; return; } if (FAILED(getCatalogHR)) { // Failing to get the catalog details means that we can't really get give much of a meaningful response. context.Reporter.Error() << Resource::String::ConfigurationFailedToGetDetails << std::endl; AICLI_TERMINATE_CONTEXT(getCatalogHR); } auto units = configContext.Set().Units(); auto localUnitResults = getLocalResult ? getLocalResult.UnitResults() : nullptr; if (localUnitResults && units.Size() != localUnitResults.Size()) { AICLI_LOG(Config, Error, << "The details result size did not match the set size: Set[" << units.Size() << "], Local[" << localUnitResults.Size() << "]"); THROW_HR(WINGET_CONFIG_ERROR_ASSERTION_FAILED); } auto catalogUnitResults = getCatalogResult.UnitResults(); if (units.Size() != catalogUnitResults.Size()) { AICLI_LOG(Config, Error, << "The details result sizes did not match the set size: Set[" << units.Size() << "], Catalog[" << catalogUnitResults.Size() << "]"); THROW_HR(WINGET_CONFIG_ERROR_ASSERTION_FAILED); } bool foundIssue = false; // Now that we have the entire set of local and catalog details, process each unit for (uint32_t i = 0; i < units.Size(); ++i) { const ConfigurationUnit& unit = units.GetAt(i); GetConfigurationUnitDetailsResult localUnitResult = localUnitResults ? localUnitResults.GetAt(i) : nullptr; GetConfigurationUnitDetailsResult catalogUnitResult = catalogUnitResults.GetAt(i); IConfigurationUnitProcessorDetails catalogDetails = catalogUnitResult.Details(); bool needsHeader = true; auto outputHeaderIfNeeded = [&]() { if (needsHeader) { anon::OutputConfigurationUnitHeader(context, unit, unit.Type()); needsHeader = false; foundIssue = true; } }; if (anon::GetValueSetString(unit.Metadata(), anon::s_Directive_Module).empty()) { outputHeaderIfNeeded(); context.Reporter.Warn() << " "_liv << Resource::String::ConfigurationUnitModuleNotProvidedWarning << std::endl; } if (catalogDetails) { // Warn if unit is not public if (!catalogDetails.IsPublic()) { outputHeaderIfNeeded(); context.Reporter.Warn() << " "_liv << Resource::String::ConfigurationUnitNotPublicWarning << std::endl; } // Since it is available, no more checks are needed continue; } // Everything below here is due to not finding in the catalog search if (FAILED(catalogUnitResult.ResultInformation().ResultCode())) { outputHeaderIfNeeded(); anon::OutputUnitRunFailure(context, unit, catalogUnitResult.ResultInformation()); continue; } // If not already prerelease, try with prerelease and warn if found std::optional allowPrereleaseDirective = anon::GetValueSetBool(unit.Metadata(), anon::s_Directive_AllowPrerelease); if (!allowPrereleaseDirective || !allowPrereleaseDirective.value()) { // Check if the configuration unit is prerelease but the author forgot it ConfigurationUnit clone = unit.Copy(); clone.Metadata().Insert(anon::s_Directive_AllowPrerelease, PropertyValue::CreateBoolean(true)); progressScope = context.Reporter.BeginAsyncProgress(true); progressScope->Callback().SetProgressMessage(gettingDetailString); auto getUnitDetailsOperation = configContext.Processor().GetUnitDetailsAsync(clone, ConfigurationUnitDetailFlags::Catalog); auto unitUnification = anon::CreateProgressCancellationUnification(std::move(progressScope), getUnitDetailsOperation); IConfigurationUnitProcessorDetails prereleaseDetails; try { prereleaseDetails = getUnitDetailsOperation.get().Details(); } CATCH_LOG(); unification.Reset(); if (prereleaseDetails) { outputHeaderIfNeeded(); context.Reporter.Warn() << " "_liv << Resource::String::ConfigurationUnitNeedsPrereleaseWarning << std::endl; continue; } } // If module is local, warn that we couldn't find it in the catalog if (localUnitResult && localUnitResult.Details()) { outputHeaderIfNeeded(); context.Reporter.Warn() << " "_liv << Resource::String::ConfigurationUnitNotInCatalogWarning << std::endl; continue; } // Finally, error that we couldn't find it at all outputHeaderIfNeeded(); context.Reporter.Error() << " "_liv << Resource::String::ConfigurationUnitNotFound << std::endl; } if (foundIssue) { // Indicate that it was not a total success AICLI_TERMINATE_CONTEXT(S_FALSE); } } void ValidateConfigurationSetUnitContents(Execution::Context& context) { ConfigurationContext& configContext = context.Get(); auto units = configContext.Set().Units(); auto validationOrder = anon::GetConfigurationSetUnitValidationOrder(units.GetView()); Configuration::WingetDscModuleUnitValidator wingetUnitValidator; bool foundIssues = false; for (const auto index : validationOrder) { const ConfigurationUnit& unit = units.GetAt(index); auto moduleName = Utility::ConvertToUTF8(unit.Details().ModuleName()); if (Utility::CaseInsensitiveEquals(wingetUnitValidator.ModuleName(), moduleName)) { bool result = wingetUnitValidator.ValidateConfigurationSetUnit(context, unit); if (!result) { foundIssues = true; } } } 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; } void SearchSourceForPackageExport(Execution::Context& context) { if (!context.Args.Contains(Args::Type::ConfigurationExportAll) && !context.Args.Contains(Args::Type::ConfigurationExportPackageId)) { // No package export needed. return; } context << OpenSource() << OpenCompositeSource(Repository::PredefinedSource::Installed); if (context.Args.Contains(Args::Type::ConfigurationExportAll)) { context << SearchSourceForMany << HandleSearchResultFailures << EnsureMatchesFromSearchResult(OperationType::Export) << SelectVersionsToExport; } else if (context.Args.Contains(Args::Type::ConfigurationExportPackageId)) { context.Args.AddArg(Args::Type::Id, context.Args.GetArg(Args::Type::ConfigurationExportPackageId)); context << SearchSourceForSingle << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(OperationType::Export) << SelectVersionsToExport; } } void PopulateConfigurationSetForExport(Execution::Context& context) { bool isExportAll = context.Args.Contains(Execution::Args::Type::ConfigurationExportAll); if (isExportAll) { context << anon::ExportPredefinedResources << SearchSourceForPackageExport << anon::ProcessPackagesForConfigurationExportAll; } else { context << SearchSourceForPackageExport << anon::ProcessPackagesForConfigurationExportSingle; } } void WriteConfigFile(Execution::Context& context) { try { std::string argPath{ context.Args.GetArg(Args::Type::OutputFile) }; context.Reporter.Info() << Resource::String::ConfigurationExportAddingToFile(Utility::LocIndView{ argPath }) << std::endl; auto tempFilePath = Runtime::GetNewTempFilePath(); { std::ofstream tempStream{ tempFilePath }; tempStream << "# Created using winget configure export " << Runtime::GetClientVersion().get() << std::endl; } auto openAction = Streams::FileRandomAccessStream::OpenAsync( tempFilePath.wstring(), FileAccessMode::ReadWrite); auto stream = openAction.get(); stream.Seek(stream.Size()); ConfigurationContext& configContext = context.Get(); configContext.Set().Serialize(openAction.get()); auto absolutePath = std::filesystem::weakly_canonical(std::filesystem::path{ argPath }); std::filesystem::rename(tempFilePath, absolutePath); context.Reporter.Info() << Resource::String::ConfigurationExportSuccessful << std::endl; } catch (...) { context.Reporter.Error() << Resource::String::ConfigurationExportFailed << std::endl; throw; } } void GetConfigurationSetHistory(Execution::Context& context) { auto progressScope = context.Reporter.BeginAsyncProgress(true); ConfigurationContext& configContext = context.Get(); configContext.History(configContext.Processor().GetConfigurationHistory()); } void ShowConfigurationSetHistory(Execution::Context& context) { const auto& history = context.Get().History(); if (history.empty()) { context.Reporter.Info() << Resource::String::ConfigurationHistoryEmpty << std::endl; } else { TableOutput<4> historyTable{ context.Reporter, { Resource::String::ConfigureListIdentifier, Resource::String::ConfigureListName, Resource::String::ConfigureListState, Resource::String::ConfigureListOrigin } }; for (const auto& set : history) { winrt::hstring origin = set.Path(); if (origin.empty()) { origin = set.Origin(); } historyTable.OutputLine({ Utility::ConvertGuidToString(set.InstanceIdentifier()), Utility::ConvertToUTF8(set.Name()), anon::ToLocString(set.State()), Utility::ConvertToUTF8(origin)}); } historyTable.Complete(); } } void SelectSetFromHistory(Execution::Context& context) { ConfigurationContext& configContext = context.Get(); ConfigurationSet selectedSet{ nullptr }; std::string foldedInput = Utility::FoldCase(context.Args.GetArg(Execution::Args::Type::ConfigurationHistoryItem)); for (const ConfigurationSet& historySet : configContext.History()) { if (anon::HistorySetMatchesInput(historySet, foldedInput)) { if (selectedSet) { selectedSet = nullptr; break; } else { selectedSet = historySet; } } } if (!selectedSet) { context.Reporter.Warn() << Resource::String::ConfigurationHistoryItemNotFound << std::endl; context << ShowConfigurationSetHistory; AICLI_TERMINATE_CONTEXT(WINGET_CONFIG_ERROR_HISTORY_ITEM_NOT_FOUND); } configContext.Set(std::move(selectedSet)); } void RemoveConfigurationSetHistory(Execution::Context& context) { auto progressScope = context.Reporter.BeginAsyncProgress(true); context.Get().Set().Remove(); } void SerializeConfigurationSetHistory(Execution::Context& context) { auto progressScope = context.Reporter.BeginAsyncProgress(true); std::filesystem::path absolutePath = std::filesystem::weakly_canonical(std::filesystem::path{ Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::OutputFile)) }); auto openAction = Streams::FileRandomAccessStream::OpenAsync(absolutePath.wstring(), FileAccessMode::ReadWrite, StorageOpenOptions::None, Streams::FileOpenDisposition::CreateAlways); auto cancellationScope = progressScope->Callback().SetCancellationFunction([&]() { openAction.Cancel(); }); auto outputStream = openAction.get(); context.Get().Set().Serialize(outputStream); } void ShowSingleConfigurationSetHistory(Execution::Context& context) { const auto& set = context.Get().Set(); // Output a table with name/value pairs for some of the set's properties. Example: // // Field Value // ---------------------------------------------------- // Identifier {7D5CF50E-F3C6-4333-BFE6-5A806F9EBA4E} // Name Test Name // Origin Test Origin // Path Test Path // State Completed // First Applied 2024-07-16 21:15:13.000 // Apply Begun 2024-07-16 21:15:13.000 // Apply Ended 2024-07-16 21:15:13.000 Execution::TableOutput<2> table(context.Reporter, { Resource::String::SourceListField, Resource::String::SourceListValue }); table.OutputLine({ Resource::LocString{ Resource::String::ConfigureListIdentifier }, Utility::ConvertGuidToString(set.InstanceIdentifier()) }); table.OutputLine({ Resource::LocString{ Resource::String::ConfigureListName }, Utility::ConvertToUTF8(set.Name()) }); table.OutputLine({ Resource::LocString{ Resource::String::ConfigureListOrigin }, Utility::ConvertToUTF8(set.Origin()) }); table.OutputLine({ Resource::LocString{ Resource::String::ConfigureListPath }, Utility::ConvertToUTF8(set.Path()) }); table.OutputLine({ Resource::LocString{ Resource::String::ConfigureListState }, anon::ToLocString(set.State()) }); table.OutputLine({ Resource::LocString{ Resource::String::ConfigureListFirstApplied }, Utility::TimePointToString(winrt::clock::to_sys(set.FirstApply())) }); auto applyBegun = set.ApplyBegun(); if (applyBegun != winrt::clock::time_point{}) { table.OutputLine({ Resource::LocString{ Resource::String::ConfigureListApplyBegun }, Utility::TimePointToString(winrt::clock::to_sys(applyBegun)) }); } auto applyEnded = set.ApplyEnded(); if (applyEnded != winrt::clock::time_point{}) { table.OutputLine({ Resource::LocString{ Resource::String::ConfigureListApplyEnded }, Utility::TimePointToString(winrt::clock::to_sys(applyEnded)) }); } table.Complete(); context.Reporter.Info() << std::endl; // Output a table with unit state information. Groups are represented by indentation beneath their parent unit. Example: // // Unit State Result Details // ------------------------------------------------------------ // Module/Resource [Name] Completed 0x00000000 // Module2/Resource [Group] Completed 0x00000000 // |-Module3/Resource [Child1] Completed 0x00000000 // |---Module4/Resource2 Completed 0x80004005 I failed :( // |-Module3/Resource [Child2] Completed 0x00000000 Execution::TableOutput<4> unitTable(context.Reporter, { Resource::String::ConfigureListUnit, Resource::String::ConfigureListState, Resource::String::ConfigureListResult, Resource::String::ConfigureListResultDescription }); struct UnitSiblings { size_t Depth = 0; size_t Current = 0; std::vector Siblings; }; std::vector stack; { UnitSiblings initial; auto units = set.Units(); initial.Siblings.resize(units.Size()); units.GetMany(0, initial.Siblings); stack.emplace_back(std::move(initial)); } // Each item on the stack is a list of sibling units. // Each iteration, we process the Current sibling from the group on top of the stack. // If it is a group, we add its children as a new stack item to be processed next. while (!stack.empty()) { UnitSiblings& currentSiblings = stack.back(); if (currentSiblings.Current >= currentSiblings.Siblings.size()) { stack.pop_back(); continue; } ConfigurationUnit& currentUnit = currentSiblings.Siblings[currentSiblings.Current++]; std::ostringstream unitStream; if (currentSiblings.Depth) { unitStream << '|' << std::string((currentSiblings.Depth * 2) - 1, '-'); } unitStream << Utility::ConvertToUTF8(currentUnit.Type()); auto identifier = currentUnit.Identifier(); if (!identifier.empty()) { unitStream << " [" << Utility::ConvertControlCodesToPictures(Utility::ConvertToUTF8(identifier)) << ']'; } auto resultInformation = currentUnit.ResultInformation(); std::ostringstream resultStream; std::string resultDetails; if (resultInformation) { resultStream << "0x" << Logging::SetHRFormat << resultInformation.ResultCode(); auto description = resultInformation.Description(); if (description.empty()) { description = resultInformation.Details(); } resultDetails = Utility::ConvertControlCodesToPictures(Utility::ConvertToUTF8(description)); } unitTable.OutputLine({ std::move(unitStream).str(), anon::ToLocString(currentUnit.State()), std::move(resultStream).str(), std::move(resultDetails) }); if (currentUnit.IsGroup()) { UnitSiblings unitChildren; unitChildren.Depth = currentSiblings.Depth + 1; auto units = currentUnit.Units(); unitChildren.Siblings.resize(units.Size()); units.GetMany(0, unitChildren.Siblings); stack.emplace_back(std::move(unitChildren)); } } unitTable.Complete(); } void CompleteConfigurationHistoryItem(Execution::Context& context) { const std::string& word = context.Get().Word(); auto stream = context.Reporter.Completion(); for (const auto& historyItem : ConfigurationProcessor{ IConfigurationSetProcessorFactory{ nullptr } }.GetConfigurationHistory()) { std::ostringstream identifierStream; identifierStream << historyItem.InstanceIdentifier(); std::string identifier = identifierStream.str(); if (word.empty() || Utility::CaseInsensitiveContainsSubstring(identifier, word)) { stream << '"' << identifier << '"' << std::endl; } std::string name = Utility::ConvertToUTF8(historyItem.Name()); if (word.empty() || Utility::CaseInsensitiveStartsWith(name, word)) { stream << '"' << name << '"' << std::endl; } } } void MonitorConfigurationStatus(Execution::Context& context) { auto& configurationContext = context.Get(); std::mutex activeSetMutex; ConfigurationSet activeSet{ nullptr }; decltype(activeSet.ConfigurationSetChange(winrt::auto_revoke, nullptr)) activeSetRevoker; auto setChangeHandler = [&](const ConfigurationSet& set, const ConfigurationSetChangeData& changeData) { if (changeData.Change() == ConfigurationSetChangeEventType::SetStateChanged) { context.Reporter.Info() << "(SetStateChanged) " << set.InstanceIdentifier() << " :: " << anon::ToLocString(changeData.SetState()) << std::endl; } else if (changeData.Change() == ConfigurationSetChangeEventType::UnitStateChanged) { context.Reporter.Info() << "(UnitStateChanged) " << changeData.Unit().InstanceIdentifier() << " :: " << anon::ToLocString(changeData.UnitState()) << std::endl; auto resultInformation = changeData.ResultInformation(); if (resultInformation) { context.Reporter.Info() << " [" << anon::ToString(resultInformation.ResultSource()) << "] :: 0x" << Logging::SetHRFormat << resultInformation.ResultCode() << std::endl; } } }; auto setActiveSet = [&](const ConfigurationSet& set, bool force) { std::lock_guard lock{ activeSetMutex }; if (force || !activeSet) { activeSet = set; activeSetRevoker = activeSet.ConfigurationSetChange(winrt::auto_revoke, setChangeHandler); } }; auto processorRevoker = configurationContext.Processor().ConfigurationChange(winrt::auto_revoke, [&](const ConfigurationSet& set, const ConfigurationChangeData& changeData) { context.Reporter.Info() << '[' << anon::ToString(changeData.Change()) << "] " << changeData.InstanceIdentifier() << " :: " << anon::ToLocString(changeData.State()) << std::endl; if (changeData.Change() == ConfigurationChangeEventType::SetStateChanged && changeData.State() == ConfigurationSetState::InProgress) { setActiveSet(set, true); } }); for (ConfigurationSet& historySet : configurationContext.History()) { if (historySet.State() == ConfigurationSetState::InProgress) { setActiveSet(historySet, false); } } for (;;) { std::this_thread::sleep_for(250ms); if (context.IsTerminated()) { return; } } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/ConfigurationFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Creates a configuration processor with a processor factory for full functionality. // Required Args: None // Inputs: None // Outputs: ConfigurationProcessor void CreateConfigurationProcessor(Execution::Context& context); // Creates a configuration processor without a processor factory for reduced functionality. // Required Args: None // Inputs: None // Outputs: ConfigurationProcessor void CreateConfigurationProcessorWithoutFactory(Execution::Context& context); // Opens the configuration set. // Required Args: ConfigurationFile // Inputs: ConfigurationProcessor // Outputs: ConfigurationSet void OpenConfigurationSet(Execution::Context& context); // Creates or opens the configuration set. // Required Args: OutputFile // Inputs: ConfigurationProcessor // Outputs: ConfigurationSet struct CreateOrOpenConfigurationSet : public WorkflowTask { CreateOrOpenConfigurationSet(std::string defaultSchemaVersion, bool createAlways = false) : WorkflowTask("CreateOrOpenConfigurationSet"), m_defaultSchemaVersion(std::move(defaultSchemaVersion)), m_createAlways(createAlways) {} void operator()(Execution::Context& context) const override; private: std::string m_defaultSchemaVersion; bool m_createAlways = false; }; // Outputs the configuration set. // Required Args: None // Inputs: ConfigurationSet // Outputs: None void ShowConfigurationSet(Execution::Context& context); // Outputs the configuration set. // Required Args: None // Inputs: ConfigurationProcessor, ConfigurationSet // Outputs: None void ShowConfigurationSetConflicts(Execution::Context& context); // Handles confirming the configuration set processing should proceed. // Required Args: None // Inputs: None // Outputs: None struct ConfirmConfigurationProcessing : public WorkflowTask { ConfirmConfigurationProcessing(bool isApply) : WorkflowTask("ConfirmConfigurationProcessing"), m_isApply(isApply) {} void operator()(Execution::Context& context) const override; private: bool m_isApply; }; // Applies the configuration set, showing progress as it proceeds. // Required Args: None // Inputs: ConfigurationProcessor, ConfigurationSet // Outputs: None void ApplyConfigurationSet(Execution::Context& context); // Tests the configuration set state, showing progress as it proceeds. // Required Args: None // Inputs: ConfigurationProcessor, ConfigurationSet // Outputs: None void TestConfigurationSet(Execution::Context& context); // Validates the configuration set semantically. // Required Args: None // Inputs: ConfigurationProcessor, ConfigurationSet // Outputs: None void ValidateConfigurationSetSemantics(Execution::Context& context); // Validates that the unit processors referenced by the set are valid/available/etc. // Required Args: None // Inputs: ConfigurationProcessor, ConfigurationSet // Outputs: None void ValidateConfigurationSetUnitProcessors(Execution::Context& context); // Validates that specific 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 // Outputs: None void ValidateAllGoodMessage(Execution::Context& context); // Search source for package(s) to be exported in configuration file. // Required Args: None // Inputs: None // Outputs: PackageCollection void SearchSourceForPackageExport(Execution::Context& context); // Adds configuration unit(s) with the winget package and/or exports resource given to configuration set. // Required Args: None // Inputs: ConfigurationProcessor, ConfigurationSet // Outputs: None void PopulateConfigurationSetForExport(Execution::Context& context); // Write the configuration file. // Required Args: OutputFile // Inputs: ConfigurationProcessor, ConfigurationSet // Outputs: None void WriteConfigFile(Execution::Context& context); // Gets the configuration set history. // Required Args: None // Inputs: ConfigurationProcessor // Outputs: ConfigurationSetHistory void GetConfigurationSetHistory(Execution::Context& context); // Outputs the configuration set history. // Required Args: None // Inputs: ConfigurationSetHistory // Outputs: None void ShowConfigurationSetHistory(Execution::Context& context); // Selects a specific configuration set history item. // Required Args: ConfigurationHistoryItem // Inputs: ConfigurationSetHistory // Outputs: ConfigurationSet void SelectSetFromHistory(Execution::Context& context); // Removes the configuration set from history. // Required Args: None // Inputs: ConfigurationSet // Outputs: None void RemoveConfigurationSetHistory(Execution::Context& context); // Write the configuration set history item to a file. // Required Args: OutputFile // Inputs: ConfigurationSet // Outputs: None void SerializeConfigurationSetHistory(Execution::Context& context); // Outputs a single configuration set (from history). // Required Args: None // Inputs: ConfigurationSet // Outputs: None void ShowSingleConfigurationSetHistory(Execution::Context& context); // Completes the configuration history item. // Required Args: None // Inputs: None // Outputs: None void CompleteConfigurationHistoryItem(Execution::Context& context); // Monitors configuration status. // Required Args: None // Inputs: None // Outputs: None void MonitorConfigurationStatus(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/DependenciesFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DependenciesFlow.h" #include #include "InstallFlow.h" #include "winget\DependenciesGraph.h" #include "DependencyNodeProcessor.h" #include "ShellExecuteInstallerHandler.h" using namespace AppInstaller::Repository; using namespace AppInstaller::Manifest; namespace AppInstaller::CLI::Workflow { namespace { // Contains all the information needed to install a dependency package. struct DependencyPackageCandidate { DependencyPackageCandidate( std::shared_ptr&& packageVersion, std::shared_ptr&& installedPackageVersion, Manifest::Manifest&& manifest, Manifest::ManifestInstaller&& installer) : PackageVersion(std::move(packageVersion)), InstalledPackageVersion(std::move(installedPackageVersion)), Manifest(std::move(manifest)), Installer(std::move(installer)) { } std::shared_ptr PackageVersion; std::shared_ptr InstalledPackageVersion; Manifest::Manifest Manifest; Manifest::ManifestInstaller Installer; }; } void ReportDependencies::operator()(Execution::Context& context) const { auto info = context.Reporter.Info(); const auto& dependencies = context.Get(); if (dependencies.HasAny()) { info << m_messageId << std::endl; if (dependencies.HasAnyOf(DependencyType::WindowsFeature)) { info << " - " << Resource::String::WindowsFeaturesDependencies << std::endl; dependencies.ApplyToType(DependencyType::WindowsFeature, [&info](Dependency dependency) {info << " " << dependency.Id() << std::endl; }); } if (dependencies.HasAnyOf(DependencyType::WindowsLibrary)) { info << " - " << Resource::String::WindowsLibrariesDependencies << std::endl; dependencies.ApplyToType(DependencyType::WindowsLibrary, [&info](Dependency dependency) {info << " " << dependency.Id() << std::endl; }); } if (dependencies.HasAnyOf(DependencyType::Package)) { info << " - " << Resource::String::PackageDependencies << std::endl; dependencies.ApplyToType(DependencyType::Package, [&info](Dependency dependency) { info << " " << dependency.Id(); if (dependency.MinVersion) { info << " [>= " << dependency.MinVersion.value().ToString() << "]"; } info << std::endl; }); } if (dependencies.HasAnyOf(DependencyType::External)) { context.Reporter.Warn() << " - " << Resource::String::ExternalDependencies << std::endl; dependencies.ApplyToType(DependencyType::External, [&info](Dependency dependency) {info << " " << dependency.Id() << std::endl; }); } } } void GetInstallersDependenciesFromManifest(Execution::Context& context) { const auto& manifest = context.Get(); DependencyList allDependencies; for (const auto& installer : manifest.Installers) { allDependencies.Add(installer.Dependencies); } context.Add(std::move(allDependencies)); } void GetDependenciesFromInstaller(Execution::Context& context) { const auto& installer = context.Get(); if (installer) { context.Add(installer->Dependencies); } } void GetDependenciesInfoForUninstall(Execution::Context& context) { // TODO make best effort to get the correct installer information, it may be better to have a record of installations and save the correct installers context.Add(DependencyList()); // sending empty list of dependencies for now } void OpenDependencySource(Execution::Context& context) { if (context.Contains(Execution::Data::PackageVersion)) { const auto& packageVersion = context.Get(); context.Add(packageVersion->GetSource()); } else { // install from manifest requires --dependency-source to be set context << Workflow::OpenSource(true); } if (WI_IsFlagClear(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly)) { // Installed source is not needed when only downloading the installer. context << Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, true, Repository::CompositeSearchBehavior::AvailablePackages); } } void EnableWindowsFeaturesDependencies(Execution::Context& context) { const auto& rootDependencies = context.Get()->Dependencies; if (rootDependencies.Empty() || !rootDependencies.HasAnyOf(DependencyType::WindowsFeature)) { return; } context << Workflow::EnsureRunningAsAdmin; if (context.IsTerminated()) { return; } bool isCancelled = false; bool enableFeaturesFailed = false; bool rebootRequired = false; bool force = context.Args.Contains(Execution::Args::Type::Force); rootDependencies.ApplyToType(DependencyType::WindowsFeature, [&context, &isCancelled, &enableFeaturesFailed, &force, &rebootRequired](Dependency dependency) { if (enableFeaturesFailed && !force || isCancelled) { return; } auto featureName = dependency.Id(); auto featureContextPtr = context.CreateSubContext(); Execution::Context& featureContext = *featureContextPtr; auto previousThreadGlobals = featureContext.SetForCurrentThread(); featureContext << Workflow::ShellExecuteEnableWindowsFeature(featureName); if (featureContext.IsTerminated()) { isCancelled = true; return; } Utility::LocIndView locIndFeatureName{ featureName }; DWORD result = featureContext.Get(); if (result == ERROR_SUCCESS) { AICLI_LOG(Core, Info, << "Successfully enabled [" << featureName << "]"); } else if (result == 0x800f080c) // DISMAPI_E_UNKNOWN_FEATURE { AICLI_LOG(Core, Warning, << "Windows Feature [" << featureName << "] does not exist"); enableFeaturesFailed = true; featureContext.Reporter.Warn() << Resource::String::WindowsFeatureNotFound(locIndFeatureName) << std::endl; } else if (result == ERROR_SUCCESS_REBOOT_REQUIRED) { AICLI_LOG(Core, Info, << "Reboot required for [" << featureName << "]"); rebootRequired = true; } else { AICLI_LOG(Core, Error, << "Failed to enable Windows Feature [" << featureName << "] with exit code: " << result); enableFeaturesFailed = true; featureContext.Reporter.Warn() << Resource::String::FailedToEnableWindowsFeature(locIndFeatureName, result) << std::endl; } }); if (isCancelled) { context.Reporter.Warn() << Resource::String::InstallAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } if (enableFeaturesFailed) { if (force) { context.Reporter.Warn() << Resource::String::FailedToEnableWindowsFeatureOverridden << std::endl; } else { context.Reporter.Error() << Resource::String::FailedToEnableWindowsFeatureOverrideRequired << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES); } } else { if (rebootRequired) { if (force) { context.Reporter.Warn() << Resource::String::RebootRequiredToEnableWindowsFeatureOverridden << std::endl; } else { context.Reporter.Error() << Resource::String::RebootRequiredToEnableWindowsFeatureOverrideRequired << std::endl; context.SetFlags(Execution::ContextFlag::RegisterResume); context.SetFlags(Execution::ContextFlag::RebootRequired); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL); } } else { context.Reporter.Info() << Resource::String::EnableWindowsFeaturesSuccess << std::endl; } } } void CreateDependencySubContexts::operator()(Execution::Context& context) const { if (Settings::User().Get() || context.Args.Contains(Execution::Args::Type::SkipDependencies)) { return; } auto info = context.Reporter.Info(); auto error = context.Reporter.Error(); const auto& rootManifest = context.Get(); Dependency rootAsDependency = Dependency(DependencyType::Package, rootManifest.Id, rootManifest.Version); const auto& rootInstaller = context.Get(); const auto& rootDependencies = rootInstaller->Dependencies; if (rootDependencies.Empty()) { // If there's no dependencies there's nothing to do aside of logging the outcome return; } context << OpenDependencySource; if (context.IsTerminated()) { info << Resource::String::DependenciesFlowSourceNotFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); } std::map idToPackageMap; bool foundError = false; DependencyGraph dependencyGraph(rootAsDependency, rootDependencies, [&](Dependency node) { DependencyNodeProcessor nodeProcessor(context); auto result = nodeProcessor.EvaluateDependencies(node); DependencyList list = nodeProcessor.GetDependencyList(); foundError = foundError || (result == DependencyNodeProcessorResult::Error); if (result == DependencyNodeProcessorResult::Success) { DependencyPackageCandidate dependencyPackageCandidate{ std::move(nodeProcessor.GetPackageLatestVersion()), std::move(nodeProcessor.GetPackageInstalledVersion()), std::move(nodeProcessor.GetManifest()), std::move(nodeProcessor.GetPreferredInstaller()) }; idToPackageMap.emplace(node.Id(), std::move(dependencyPackageCandidate)); }; return list; }); dependencyGraph.BuildGraph(); if (foundError) { error << Resource::String::DependenciesManagementExitMessage << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY); } if (dependencyGraph.HasLoop()) { context.Reporter.Warn() << Resource::String::DependenciesFlowContainsLoop; } const auto& installationOrder = dependencyGraph.GetInstallationOrder(); std::vector> dependencyPackageContexts; for (auto const& node : installationOrder) { auto itr = idToPackageMap.find(node.Id()); // if the package was already installed (with a useful version) or is the root // then there will be no installer for it on the map. if (itr != idToPackageMap.end()) { auto dependencyContextPtr = context.CreateSubContext(); Execution::Context& dependencyContext = *dependencyContextPtr; auto previousThreadGlobals = dependencyContext.SetForCurrentThread(); Logging::Telemetry().LogSelectedInstaller( static_cast(itr->second.Installer.Arch), itr->second.Installer.Url, Manifest::InstallerTypeToString(itr->second.Installer.EffectiveInstallerType()), Manifest::ScopeToString(itr->second.Installer.Scope), itr->second.Installer.Locale); Logging::Telemetry().LogManifestFields( itr->second.Manifest.Id, itr->second.Manifest.DefaultLocalization.Get(), itr->second.Manifest.Version); // Extract the data needed for installing dependencyContext.Add(itr->second.PackageVersion); dependencyContext.Add(itr->second.Manifest); dependencyContext.Add(itr->second.InstalledPackageVersion); dependencyContext.Add(itr->second.Installer); if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly)) { dependencyContext.Add(context.Get() / L"Dependencies"); } dependencyPackageContexts.emplace_back(std::move(dependencyContextPtr)); } } context.Add(std::move(dependencyPackageContexts)); } } ================================================ FILE: src/AppInstallerCLICore/Workflows/DependenciesFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Shows information about dependencies. // Required Args: message to use at the beginning, before outputting dependencies // Inputs: Dependencies // Outputs: None struct ReportDependencies : public WorkflowTask { ReportDependencies(AppInstaller::StringResource::StringId messageId) : WorkflowTask("ReportDependencies"), m_messageId(messageId) {} void operator()(Execution::Context& context) const override; private: AppInstaller::StringResource::StringId m_messageId; }; // Gathers all installers dependencies from manifest. // Required Args: None // Inputs: Manifest // Outputs: Dependencies void GetInstallersDependenciesFromManifest(Execution::Context& context); // Gathers package dependencies information from installer. // Required Args: None // Inputs: Installer // Outputs: Dependencies void GetDependenciesFromInstaller(Execution::Context& context); // TODO: // Gathers dependencies information for the uninstall command. // Required Args: None // Inputs: None // Outputs: Dependencies void GetDependenciesInfoForUninstall(Execution::Context& context); // Builds the dependency graph and creates the sub contexts for each package dependency. // Required Args: None // Inputs: Manifest, Installer and DependencySource // Outputs: Dependencies struct CreateDependencySubContexts : public WorkflowTask { CreateDependencySubContexts( AppInstaller::StringResource::StringId dependencyReportMessage) : WorkflowTask("CreateDependencySubContexts"), m_dependencyReportMessage(dependencyReportMessage) {} void operator()(Execution::Context& context) const override; private: AppInstaller::StringResource::StringId m_dependencyReportMessage; }; // Sets up the source used to get the dependencies. // Required Args: None // Inputs: PackageVersion, Manifest // Outputs: DependencySource void OpenDependencySource(Execution::Context& context); // Enables the Windows Feature dependencies. // Required Args: None // Inputs: Manifest, Installer // Outputs: None void EnableWindowsFeaturesDependencies(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DependencyNodeProcessor.h" #include "WorkflowBase.h" #include #include using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; namespace AppInstaller::CLI::Workflow { DependencyNodeProcessor::DependencyNodeProcessor(Execution::Context& context) : m_context(context) {} DependencyNodeProcessorResult DependencyNodeProcessor::EvaluateDependencies(Dependency& dependencyNode) { SearchRequest searchRequest; const auto& source = m_context.Get(); auto error = m_context.Reporter.Error(); auto info = m_context.Reporter.Info(); searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, dependencyNode.Id())); const auto& matches = source.Search(searchRequest).Matches; if (matches.empty()) { error << Resource::String::DependenciesFlowNoMatches << std::endl; return DependencyNodeProcessorResult::Error; } if (matches.size() > 1) { auto dependencyNodeId = Utility::LocIndString{ Utility::Normalize(dependencyNode.Id()) }; error << Resource::String::DependenciesFlowSourceTooManyMatches(dependencyNodeId) << std::endl; AICLI_LOG(CLI, Error, << "Too many matches for package " << dependencyNode.Id()); return DependencyNodeProcessorResult::Error; } const auto& match = matches.at(0); const auto& package = match.Package; auto packageId = package->GetProperty(PackageProperty::Id); m_nodePackageInstalledVersion = GetInstalledVersion(package); std::shared_ptr availableVersions = GetAvailableVersionsForInstalledVersion(package); if (m_context.Args.Contains(Execution::Args::Type::Force)) { m_nodePackageLatestVersion = availableVersions->GetLatestVersion(); } else { Pinning::PinBehavior pinBehavior = m_context.Args.Contains(Execution::Args::Type::IncludePinned) ? Pinning::PinBehavior::IncludePinned : Pinning::PinBehavior::ConsiderPins; Pinning::PinningData pinningData{ Pinning::PinningData::Disposition::ReadOnly }; auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, m_nodePackageInstalledVersion); m_nodePackageLatestVersion = evaluator.GetLatestAvailableVersionForPins(availableVersions); } if (m_nodePackageInstalledVersion && dependencyNode.IsVersionOk(Utility::Version(m_nodePackageInstalledVersion->GetProperty(PackageVersionProperty::Version)))) { // return empty dependency list, // as we won't keep searching for dependencies for installed packages return DependencyNodeProcessorResult::Skipped; } if (!m_nodePackageLatestVersion) { error << Resource::String::DependenciesFlowPackageVersionNotFound(Utility::LocIndView{ Utility::Normalize(packageId) }) << std::endl; AICLI_LOG(CLI, Error, << "Latest available version not found for package " << packageId); return DependencyNodeProcessorResult::Error; } if (!dependencyNode.IsVersionOk(Utility::Version(m_nodePackageLatestVersion->GetProperty(PackageVersionProperty::Version)))) { error << Resource::String::DependenciesFlowNoMinVersion(Utility::LocIndView{ Utility::Normalize(packageId) }) << std::endl; AICLI_LOG(CLI, Error, << "No suitable min version found for package " << packageId); return DependencyNodeProcessorResult::Error; } m_nodeManifest = m_nodePackageLatestVersion->GetManifest(); m_nodeManifest.ApplyLocale(); if (m_nodeManifest.Installers.empty()) { error << Resource::String::DependenciesFlowNoInstallerFound(Utility::LocIndView{ Utility::Normalize(m_nodeManifest.Id) }) << std::endl; AICLI_LOG(CLI, Error, << "Installer not found for manifest " << m_nodeManifest.Id << " with version" << m_nodeManifest.Version); return DependencyNodeProcessorResult::Error; } IPackageVersion::Metadata installationMetadata; if (m_nodePackageInstalledVersion) { installationMetadata = m_nodePackageInstalledVersion->GetMetadata(); } ManifestComparator manifestComparator(GetManifestComparatorOptions(m_context, installationMetadata)); auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller(m_nodeManifest); if (!installer.has_value()) { auto manifestId = Utility::LocIndString{ Utility::Normalize(m_nodeManifest.Id) }; auto manifestVersion = Utility::LocIndString{ m_nodeManifest.Version }; error << Resource::String::DependenciesFlowNoSuitableInstallerFound(manifestId, manifestVersion) << std::endl; AICLI_LOG(CLI, Error, << "No suitable installer found for manifest " << m_nodeManifest.Id << " with version " << m_nodeManifest.Version); return DependencyNodeProcessorResult::Error; } m_installer = installer.value(); m_dependenciesList = m_installer.Dependencies; return DependencyNodeProcessorResult::Success; } } ================================================ FILE: src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "ExecutionContext.h" #include using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; namespace AppInstaller::CLI::Workflow { enum DependencyNodeProcessorResult { Error, Success, Skipped, }; struct DependencyNodeProcessor { DependencyNodeProcessor(Execution::Context& context); DependencyNodeProcessorResult EvaluateDependencies(Dependency& dependencyNode); DependencyList GetDependencyList() { return m_dependenciesList; } std::shared_ptr GetPackageLatestVersion() { return m_nodePackageLatestVersion; } std::shared_ptr GetPackageInstalledVersion() { return m_nodePackageInstalledVersion; } Manifest::Manifest GetManifest() { return m_nodeManifest; } Manifest::ManifestInstaller GetPreferredInstaller() { return m_installer; } private: Execution::Context& m_context; DependencyList m_dependenciesList; std::shared_ptr m_nodePackageLatestVersion; std::shared_ptr m_nodePackageInstalledVersion; Manifest::ManifestInstaller m_installer; Manifest::Manifest m_nodeManifest; }; } ================================================ FILE: src/AppInstallerCLICore/Workflows/DownloadFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DownloadFlow.h" #include "MSStoreInstallerHandler.h" #include #include #include #include #include #include #include #include #include namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility; using namespace AppInstaller::Settings; using namespace std::string_view_literals; namespace { constexpr std::string_view s_MicrosoftEntraIdAuthorizationHeader = "Authorization"sv; // By default Azure blob storage does not accept Microsoft Entra Id authentication. // https://learn.microsoft.com/en-us/rest/api/storageservices/versioning-for-the-azure-storage-services#authorize-requests-by-using-microsoft-entra-id-shared-key-or-shared-key-lite constexpr std::string_view s_AzureBlobStorageApiVersionHeader = "x-ms-version"sv; constexpr std::string_view s_AzureBlobStorageApiVersionValue = "2020-04-08"sv; // Get the base download directory path for the installer. // Also creates the directory as necessary. std::filesystem::path GetInstallerBaseDownloadPath(Execution::Context& context) { const auto& manifest = context.Get(); std::filesystem::path tempInstallerPath = Runtime::GetPathTo(Runtime::PathName::Temp); tempInstallerPath /= Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); std::filesystem::create_directories(tempInstallerPath); return tempInstallerPath; } // Get the file extension to be used for the installer file. std::wstring_view GetInstallerFileExtension(Execution::Context& context) { const auto& installer = context.Get(); switch (installer->BaseInstallerType) { case InstallerTypeEnum::Burn: case InstallerTypeEnum::Exe: case InstallerTypeEnum::Inno: case InstallerTypeEnum::Nullsoft: case InstallerTypeEnum::Portable: return L".exe"sv; case InstallerTypeEnum::Msi: case InstallerTypeEnum::Wix: return L".msi"sv; case InstallerTypeEnum::Msix: // Note: We may need to distinguish between .msix and .msixbundle in the future. return L".msix"sv; case InstallerTypeEnum::Zip: return L".zip"sv; case InstallerTypeEnum::Font: { const auto& fileName = GetFileNameFromURI(installer->Url); if (fileName.has_extension()) { return fileName.extension().c_str(); } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } // Gets a file name that should not be able to ShellExecute. std::filesystem::path GetInstallerPreHashValidationFileName(Execution::Context& context) { return { SHA256::ConvertToString(context.Get()->Sha256) }; } // Gets the file name that can be used to ShellExecute the file. std::filesystem::path GetInstallerPostHashValidationFileName(Execution::Context& context) { // Get file name from download URI std::filesystem::path filename = GetFileNameFromURI(context.Get()->Url); std::wstring_view installerExtension = GetInstallerFileExtension(context); // Assuming that we find a safe stem value in the URI, use it. // This should be extremely common, but just in case fall back to the older name style. if (filename.has_stem() && ((filename.wstring().size() + installerExtension.size()) < MAX_PATH)) { filename = filename.stem(); } else { const auto& manifest = context.Get(); filename = Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); } filename += installerExtension; // Make file name suitable for file system path filename = Utility::ConvertToUTF16(Utility::MakeSuitablePathPart(filename.u8string())); return filename; } // Gets the file name for the downloaded installer in the format of {id}_{version}_{architecture}_{scope}_{installerType}_{locale}. std::filesystem::path GetInstallerDownloadOnlyFileName(Execution::Context& context, const std::wstring_view& extension = {}) { const auto& manifest = context.Get(); const auto& installer = context.Get().value(); std::string packageName = manifest.CurrentLocalization.Get(); std::string architecture{ ToString(installer.Arch) }; std::string installerType{ InstallerTypeToString(installer.EffectiveInstallerType()) }; std::string fileName = packageName; if (!Version(manifest.Version).IsUnknown()) { fileName += '_' + manifest.Version; } if (installer.Scope != ScopeEnum::Unknown) { fileName += '_' + std::string{ ScopeToString(installer.Scope) }; } fileName += '_' + architecture + '_' + installerType; std::string locale = !installer.Locale.empty() ? installer.Locale : manifest.CurrentLocalization.Locale; if (!locale.empty()) { fileName += '_' + locale; } std::filesystem::path fileNamePath = Utility::ConvertToUTF16(fileName); if (!extension.empty()) { fileNamePath += extension; } else { fileNamePath += GetInstallerFileExtension(context); } // Make file name suitable for file system path fileNamePath = Utility::ConvertToUTF16(Utility::MakeSuitablePathPart(fileNamePath.u8string())); return fileNamePath; } // Try to remove the installer file, ignoring any errors. void RemoveInstallerFile(const std::filesystem::path& path) { try { std::filesystem::remove(path); // It is assumed that the parent of the installer path will always be a directory // If it isn't, then something went severely wrong. However, we will check that // it is a directory here just to be safe. If it is an empty directory, remove it. if (std::filesystem::is_directory(path.parent_path()) && std::filesystem::is_empty(path.parent_path())) { std::filesystem::remove(path.parent_path()); } } catch (const std::exception& e) { AICLI_LOG(CLI, Warning, << "Failed to remove installer file. Reason: " << e.what()); } catch (...) { AICLI_LOG(CLI, Warning, << "Failed to remove installer file. Reason unknown."); } } // Checks the file hash for an existing installer file. // Returns true if the file exists and its hash matches, false otherwise. // If the hash does not match, deletes the file. bool ExistingInstallerFileHasHashMatch(const SHA256::HashBuffer& expectedHash, const std::filesystem::path& filePath, SHA256::HashDetails& fileHashDetails) { if (std::filesystem::exists(filePath)) { AICLI_LOG(CLI, Info, << "Found existing installer file at '" << filePath << "'. Verifying file hash."); std::ifstream inStream{ filePath, std::ifstream::binary }; fileHashDetails = SHA256::ComputeHashDetails(inStream); if (SHA256::AreEqual(expectedHash, fileHashDetails.Hash)) { return true; } AICLI_LOG(CLI, Info, << "Hash does not match. Removing existing installer file " << filePath); RemoveInstallerFile(filePath); } return false; } std::string GetInstallerDownloadAuthenticationToken(const AppInstaller::Authentication::AuthenticationInfo& authInfo, Execution::Context& context) { // First check if authenticator is already created auto& authenticatorsMap = context.Get(); auto authenticatorItr = authenticatorsMap->find(authInfo); if (authenticatorItr == authenticatorsMap->end()) { AppInstaller::Authentication::Authenticator authenticator{ authInfo, GetAuthenticationArguments(context) }; authenticatorsMap->emplace(authInfo, std::move(authenticator)); } // Get the authenticator for auth. authenticatorItr = authenticatorsMap->find(authInfo); THROW_HR_IF(E_UNEXPECTED, authenticatorItr == authenticatorsMap->end()); auto authResult = authenticatorItr->second.AuthenticateForToken(); if (FAILED(authResult.Status)) { AICLI_LOG(Repo, Error, << "Authentication failed for installer download. Result: " << authResult.Status); THROW_HR_MSG(authResult.Status, "Failed to authenticate for installer download."); } return authResult.Token; } // Get additional headers for installer download request. Auth headers are acquired here. std::vector GetInstallerDownloadAuthenticationHeaders(const AppInstaller::Manifest::ManifestInstaller& installer, Execution::Context& context) { std::vector result; switch (installer.AuthInfo.Type) { case AppInstaller::Authentication::AuthenticationType::None: // No auth needed break; case AppInstaller::Authentication::AuthenticationType::MicrosoftEntraId: case AppInstaller::Authentication::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage: context.Reporter.Info() << Execution::AuthenticationEmphasis << Resource::String::InstallerDownloadRequiresAuthentication << std::endl; result.push_back({ std::string{ s_MicrosoftEntraIdAuthorizationHeader }, Authentication::CreateBearerToken(GetInstallerDownloadAuthenticationToken(installer.AuthInfo, context)), true }); if (installer.AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage) { result.push_back({ std::string{ s_AzureBlobStorageApiVersionHeader }, std::string{ s_AzureBlobStorageApiVersionValue }, false }); } break; case AppInstaller::Authentication::AuthenticationType::Unknown: default: THROW_HR_MSG(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED, "The package installer requires authentication that is not supported."); } // Log result before return std::string logMessage = "Installer download headers: "; for (const auto& header : result) { logMessage += header.Name + ": " + (header.IsAuth ? "" : header.Value) + "; "; } AICLI_LOG(CLI, Info, << logMessage); return result; } } void DownloadInstaller(Execution::Context& context) { // Check if file was already downloaded. // This may happen after a failed installation or if the download was done // separately before, e.g. on COM scenarios. context << ReportExecutionStage(ExecutionStage::Download) << CheckForExistingInstaller; if (context.IsTerminated()) { return; } bool installerDownloadOnly = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly); // CheckForExistingInstaller will set the InstallerPath if found if (!context.Contains(Execution::Data::InstallerPath)) { const auto& installer = context.Get().value(); switch (installer.BaseInstallerType) { case InstallerTypeEnum::Exe: case InstallerTypeEnum::Burn: case InstallerTypeEnum::Inno: case InstallerTypeEnum::Msi: case InstallerTypeEnum::Nullsoft: case InstallerTypeEnum::Portable: case InstallerTypeEnum::Wix: case InstallerTypeEnum::Font: case InstallerTypeEnum::Zip: context << DownloadInstallerFile; break; case InstallerTypeEnum::Msix: // If the signature hash is provided in the manifest and we are doing an install, // we can just verify signature hash without a full download and do a streaming install. // Even if we have the signature hash, we still do a full download if InstallerDownloadOnly // flag is set, or if we need to use a proxy (as deployment APIs won't use proxy for us). // Finally, we require the digest API for streaming install as well. if (installer.SignatureSha256.empty() || installerDownloadOnly || Network().GetProxyUri() || !Deployment::IsExpectedDigestsSupported()) { context << DownloadInstallerFile; } else { context << GetMsixSignatureHash; } break; case InstallerTypeEnum::MSStore: if (installerDownloadOnly) { context << MSStoreDownload << ExportManifest; } return; default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } context << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable << RenameDownloadedInstaller; if (installerDownloadOnly) { context << ExportManifest; } } void CheckForExistingInstaller(Execution::Context& context) { const auto& installer = context.Get().value(); if (installer.EffectiveInstallerType() == InstallerTypeEnum::MSStore) { // No installer is downloaded in this case return; } // Try looking for the file with and without extension. auto installerPath = GetInstallerBaseDownloadPath(context); auto installerFilename = GetInstallerPreHashValidationFileName(context); SHA256::HashDetails fileHashDetails; if (!ExistingInstallerFileHasHashMatch(installer.Sha256, installerPath / installerFilename, fileHashDetails)) { installerFilename = GetInstallerPostHashValidationFileName(context); if (!ExistingInstallerFileHasHashMatch(installer.Sha256, installerPath / installerFilename, fileHashDetails)) { // No match return; } } AICLI_LOG(CLI, Info, << "Existing installer file hash matches. Will use existing installer."); context.Add(installerPath / installerFilename); context.Add(std::make_pair(installer.Sha256, DownloadResult{ std::move(fileHashDetails.Hash), fileHashDetails.SizeInBytes })); } void GetInstallerDownloadPath(Execution::Context& context) { if (!context.Contains(Execution::Data::InstallerPath)) { auto tempInstallerPath = GetInstallerBaseDownloadPath(context); tempInstallerPath /= GetInstallerPreHashValidationFileName(context); AICLI_LOG(CLI, Info, << "Generated temp download path: " << tempInstallerPath); context.Add(std::move(tempInstallerPath)); } } void DownloadInstallerFile(Execution::Context& context) { context << GetInstallerDownloadPath; if (context.IsTerminated()) { return; } const auto& installer = context.Get().value(); const auto& installerPath = context.Get(); Utility::DownloadInfo downloadInfo{}; downloadInfo.DisplayName = Resource::GetFixedString(Resource::FixedString::ProductName); // Use the SHA256 hash of the installer as the identifier for the download downloadInfo.ContentId = SHA256::ConvertToString(installer.Sha256); try { downloadInfo.RequestHeaders = GetInstallerDownloadAuthenticationHeaders(installer, context); } catch (const wil::ResultException& re) { AICLI_LOG(CLI, Error, << "Authentication failed for installer download. Error code: " << re.GetErrorCode()); if (re.GetErrorCode() == APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED) { context.Reporter.Error() << Resource::String::InstallerDownloadAuthenticationNotSupported << std::endl; } else { context.Reporter.Error() << Resource::String::InstallerDownloadAuthenticationFailed << std::endl; } AICLI_TERMINATE_CONTEXT(re.GetErrorCode()); } context.Reporter.Info() << Resource::String::Downloading << ' ' << Execution::UrlEmphasis << installer.Url << std::endl; DownloadResult downloadResult; constexpr int MaxRetryCount = 2; constexpr std::chrono::seconds maximumWaitTimeAllowed = 60s; for (int retryCount = 0; retryCount < MaxRetryCount; ++retryCount) { bool success = false; try { downloadResult = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download, installer.Url, installerPath, Utility::DownloadType::Installer, std::placeholders::_1, downloadInfo)); // User cancelled. if (downloadResult.Sha256Hash.empty()) { context.Reporter.Info() << Resource::String::Cancelled << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } if (downloadResult.SizeInBytes == 0) { AICLI_LOG(CLI, Info, << "Got zero byte file; retrying download after a short wait..."); std::this_thread::sleep_for(5s); } else { success = true; } } catch (const ServiceUnavailableException& sue) { if (retryCount < MaxRetryCount - 1) { auto waitSecondsForRetry = sue.RetryAfter(); if (waitSecondsForRetry > maximumWaitTimeAllowed) { throw; } bool waitCompleted = context.Reporter.ExecuteWithProgress([&waitSecondsForRetry](IProgressCallback& progress) { return ProgressCallback::Wait(progress, waitSecondsForRetry); }); if (!waitCompleted) { break; } } else { throw; } } catch (...) { if (retryCount < MaxRetryCount - 1) { AICLI_LOG(CLI, Info, << "Failed to download, waiting a bit and retry. Url: " << installer.Url); Sleep(500); } else { throw; } } if (success) { break; } } context.Add(std::make_pair(installer.Sha256, downloadResult)); } void GetMsixSignatureHash(Execution::Context& context) { // We use this when the server won't support streaming install to swap to download. bool downloadInstead = false; try { const auto& installer = context.Get().value(); // Signature hash is only used for streaming installs, which don't use proxy Msix::MsixInfo msixInfo(installer.Url); DownloadResult hashInfo{ msixInfo.GetSignatureHash() }; // Value is ASCII for MSIXSTRM // A sentinel value to indicate that this is a streaming hash rather than a download. // The primary purpose is to prevent us from falling into the code path for zero byte files. hashInfo.SizeInBytes = 0x4D5349585354524D; context.Add(std::make_pair(installer.SignatureSha256, hashInfo)); context.Add({ std::make_pair(installer.Url, msixInfo.GetDigest()) }); } catch (...) { AICLI_LOG(CLI, Info, << "Failed to get msix signature hash, fall back to direct download."); downloadInstead = true; } if (downloadInstead) { context << DownloadInstallerFile; } } void VerifyInstallerHash(Execution::Context& context) { const auto& [expectedHash, downloadResult] = context.Get(); if (!std::equal( expectedHash.begin(), expectedHash.end(), downloadResult.Sha256Hash.begin())) { bool overrideHashMismatch = context.Args.Contains(Execution::Args::Type::HashOverride); const auto& manifest = context.Get(); Logging::Telemetry().LogInstallerHashMismatch(manifest.Id, manifest.Version, manifest.Channel, expectedHash, downloadResult.Sha256Hash, overrideHashMismatch, downloadResult.SizeInBytes, downloadResult.ContentType); if (downloadResult.SizeInBytes == 0) { context.Reporter.Error() << Resource::String::InstallerZeroByteFile << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE); } // If running as admin, do not allow the user to override the hash failure. if (Runtime::IsRunningAsAdmin()) { context.Reporter.Error() << Resource::String::InstallerHashMismatchAdminBlock << std::endl; } else if (!Settings::IsAdminSettingEnabled(Settings::BoolAdminSetting::InstallerHashOverride)) { context.Reporter.Error() << Resource::String::InstallerHashMismatchError << std::endl; } else if (overrideHashMismatch) { context.Reporter.Warn() << Resource::String::InstallerHashMismatchOverridden << std::endl; return; } else { context.Reporter.Error() << Resource::String::InstallerHashMismatchOverrideRequired << std::endl; } AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH); } else { AICLI_LOG(CLI, Info, << "Installer hash verified"); context.Reporter.Info() << Resource::String::InstallerHashVerified << std::endl; context.SetFlags(Execution::ContextFlag::InstallerHashMatched); if (context.Contains(Execution::Data::PackageVersion) && context.Get()->GetSource() && WI_IsFlagSet(context.Get()->GetSource().GetDetails().TrustLevel, SourceTrustLevel::Trusted)) { context.SetFlags(Execution::ContextFlag::InstallerTrusted); } } } void UpdateInstallerFileMotwIfApplicable(Execution::Context& context) { // An initial MotW is always set to URLZONE_INTERNET at the time the file is downloaded. // This function may change that to URLZONE_TRUSTED if appropriate if (context.Contains(Execution::Data::InstallerPath)) { if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerTrusted)) { // We know the installer already went through multiple scans and we can trust it. Utility::ApplyMotwIfApplicable(context.Get(), URLZONE_TRUSTED); } else if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerHashMatched)) { // IAttachmentExecute performs some additional scans before setting MotW, for example invoking anti-virus. // A policy can be set to always mark files from a given domain as trusted, so only do this // on installers with the right hash to prevent trusting unknown installers. const auto& installer = context.Get(); HRESULT hr = Utility::ApplyMotwUsingIAttachmentExecuteIfApplicable(context.Get(), installer.value().Url, URLZONE_INTERNET); // Not using SUCCEEDED(hr) to check since there are cases file is missing after a successful scan if (hr != S_OK) { switch (hr) { case INET_E_SECURITY_PROBLEM: context.Reporter.Error() << Resource::String::InstallerBlockedByPolicy << std::endl; break; case E_FAIL: context.Reporter.Error() << Resource::String::InstallerFailedVirusScan << std::endl; break; default: context.Reporter.Error() << Resource::String::InstallerFailedSecurityCheck << std::endl; } AICLI_LOG(Fail, Error, << "Installer failed security check. Url: " << installer.value().Url << " Result: " << WINGET_OSTREAM_FORMAT_HRESULT(hr)); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED); } } } } void ReverifyInstallerHash(Execution::Context& context) { const auto& installer = context.Get().value(); if (context.Contains(Execution::Data::InstallerPath)) { // Get the hash from the installer file const auto& installerPath = context.Get(); std::ifstream inStream{ installerPath, std::ifstream::binary }; auto existingFileHashDetails = SHA256::ComputeHashDetails(inStream); context.Add(std::make_pair(installer.Sha256, DownloadResult{ existingFileHashDetails.Hash, existingFileHashDetails.SizeInBytes })); } else if (installer.EffectiveInstallerType() == InstallerTypeEnum::MSStore) { // No installer file in this case return; } else if (installer.EffectiveInstallerType() == InstallerTypeEnum::Msix && !installer.SignatureSha256.empty()) { // We didn't download the installer file before. Just verify the signature hash again. context << GetMsixSignatureHash; } else { // No installer downloaded AICLI_LOG(CLI, Error, << "Installer file not found."); AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } context << VerifyInstallerHash; } void RenameDownloadedInstaller(Execution::Context& context) { if (!context.Contains(Execution::Data::InstallerPath)) { // No installer downloaded, no need to rename anything. return; } auto& installerPath = context.Get(); std::filesystem::path renamedDownloadedInstaller; if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly)) { THROW_HR_IF(E_UNEXPECTED, !context.Contains(Execution::Data::DownloadDirectory)); std::filesystem::path downloadDirectory = context.Get(); if (!std::filesystem::exists(downloadDirectory)) { std::filesystem::create_directories(downloadDirectory); } else { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE), !std::filesystem::is_directory(downloadDirectory)); } renamedDownloadedInstaller = downloadDirectory / GetInstallerDownloadOnlyFileName(context); Filesystem::RenameFile(installerPath, renamedDownloadedInstaller); context.Reporter.Info() << Resource::String::InstallerDownloaded(Utility::LocIndView{ renamedDownloadedInstaller.u8string() }) << std::endl; } else { renamedDownloadedInstaller = installerPath; renamedDownloadedInstaller.replace_filename(GetInstallerPostHashValidationFileName(context)); if (installerPath == renamedDownloadedInstaller) { // In case we are reusing an existing downloaded file return; } Filesystem::RenameFile(installerPath, renamedDownloadedInstaller); } installerPath.assign(renamedDownloadedInstaller); AICLI_LOG(CLI, Info, << "Successfully renamed downloaded installer. Path: " << installerPath); } void RemoveInstaller(Execution::Context& context) { // Path may not be present if installed from a URL for MSIX if (context.Contains(Execution::Data::InstallerPath)) { const auto& path = context.Get(); AICLI_LOG(CLI, Info, << "Removing installer: " << path); RemoveInstallerFile(path); } } void SetDownloadDirectory(Execution::Context& context) { if (!WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly)) { return; } if (context.Args.Contains(Execution::Args::Type::DownloadDirectory)) { context.Add(std::filesystem::path{ Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::DownloadDirectory)) }); } else { std::filesystem::path downloadsDirectory = Settings::User().Get(); if (downloadsDirectory.empty()) { downloadsDirectory = AppInstaller::Runtime::GetPathTo(AppInstaller::Runtime::PathName::UserProfileDownloads); } const auto& manifest = context.Get(); std::string packageDownloadFolderName = manifest.Id; if (!Utility::Version{ manifest.Version }.IsUnknown()) { packageDownloadFolderName += '_' + manifest.Version; } context.Add(downloadsDirectory / Utility::ConvertToUTF16(packageDownloadFolderName)); } } void ExportManifest(Execution::Context& context) { const auto& downloadDirectory = context.Get(); const auto& manifest = context.Get(); const auto& installer = context.Get(); std::filesystem::path manifestFileName = GetInstallerDownloadOnlyFileName(context, L".yaml"); auto manifestDownloadPath = downloadDirectory / manifestFileName; YamlWriter::OutputYamlFile(manifest, installer.value(), manifestDownloadPath); AICLI_LOG(CLI, Info, << "Successfully generated manifest yaml. Path: " << manifestDownloadPath); } void EnsureSupportForDownload(Execution::Context& context) { // No checks needed if not download installer only. if (WI_IsFlagClear(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly)) { return; } const auto& installer = context.Get(); if (installer->DownloadCommandProhibited) { context.Reporter.Error() << Resource::String::InstallerDownloadCommandProhibited << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED); } } void InitializeInstallerDownloadAuthenticatorsMap(Execution::Context& context) { context.Add(std::make_shared>()); } } ================================================ FILE: src/AppInstallerCLICore/Workflows/DownloadFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Composite flow that chooses what to do based on the installer type. // Required Args: None // Inputs: Manifest, Installer // Outputs: None void DownloadInstaller(Execution::Context& context); // Check if the desired installer has already been downloaded. // Required Args: None // Inputs: Manifest, Installer // Outputs: HashPair, InstallerPath (only if found) void CheckForExistingInstaller(Execution::Context& context); // Computes the download path for the installer file. Does nothing if already determined // Required Args: None // Inputs: Installer, Manifest // Outputs: InstallerPath void GetInstallerDownloadPath(Execution::Context& context); // Downloads the file referenced by the Installer. // This workflow task is also used by MSStoreDownload task. // Required Args: None // Inputs: Installer, Manifest // Outputs: HashPair, InstallerPath void DownloadInstallerFile(Execution::Context& context); // Computes the hash of the MSIX signature file. // Required Args: None // Inputs: Installer // Outputs: HashPair void GetMsixSignatureHash(Execution::Context& context); // Re-verify the installer hash. This is used in Com install commands where download and install are in separate phases. // Required Args: None // Inputs: InstallerPath, Installer // Outputs: HashPair void ReverifyInstallerHash(Execution::Context& context); // Verifies that the downloaded installer hash matches the hash in the manifest. // Required Args: None // Inputs: HashPair // Outputs: None void VerifyInstallerHash(Execution::Context& context); // Update Motw of the downloaded installer if applicable // Required Args: None // Inputs: HashPair, InstallerPath?, SourceId? // Outputs: None void UpdateInstallerFileMotwIfApplicable(Execution::Context& context); // This method appends appropriate extension to the downloaded installer. // ShellExecute uses file extension to launch the installer appropriately. // Required Args: None // Inputs: Installer, InstallerPath // Modifies: InstallerPath // Outputs: None void RenameDownloadedInstaller(Execution::Context& context); // Deletes the installer file. // Required Args: None // Inputs: InstallerPath // Outputs: None void RemoveInstaller(Execution::Context& context); // Sets the target download directory location if applicable. // Required Args: None // Inputs: Manifest // Outputs: None void SetDownloadDirectory(Execution::Context& context); // Exports the manifest yaml file for the downloaded package installer. Only applies to the 'winget download' command. // Required Args: None // Inputs: Manifest, Installer, DownloadDirectory // Outputs: None void ExportManifest(Execution::Context& context); // This method ensures requirements of download for later offline installation. // Required Args: None // Inputs: Installer // Outputs: None void EnsureSupportForDownload(Execution::Context& context); // This method initializes an empty InstallerDownloadAuthenticators map. // InstallerDownloadAuthenticators map is for reusing authenticators when downloading multiple installers. // Required Args: None // Inputs: None // Outputs: New empty InstallerDownloadAuthenticators void InitializeInstallerDownloadAuthenticatorsMap(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/FontFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "FontFlow.h" #include "TableOutput.h" #include #include using namespace AppInstaller::Utility; namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Fonts; namespace { struct InstalledFontFamiliesTableLine { InstalledFontFamiliesTableLine(Utility::LocIndString familyName, int faceCount) : FamilyName(familyName), FaceCount(faceCount) { } Utility::LocIndString FamilyName; int FaceCount; }; struct InstalledFontFacesTableLine { InstalledFontFacesTableLine(Utility::LocIndString familyName, Utility::LocIndString faceName, Utility::LocIndString faceVersion, std::filesystem::path filePath) : FamilyName(familyName), FaceName(faceName), FaceVersion(faceVersion), FilePath(filePath) { } Utility::LocIndString FamilyName; Utility::LocIndString FaceName; Utility::LocIndString FaceVersion; std::filesystem::path FilePath; }; struct InstalledFontFilesTableLine { InstalledFontFilesTableLine(Utility::LocIndString title, Utility::LocIndString packageName, Resource::LocString fontStatus, std::filesystem::path filePath) : Title(title), PackageName(packageName), FontStatus(fontStatus), FilePath(filePath) { } Utility::LocIndString Title; Utility::LocIndString PackageName; Resource::LocString FontStatus; std::filesystem::path FilePath; }; void OutputInstalledFontFamiliesTable(Execution::Context& context, const std::vector& lines) { Execution::TableOutput<2> table(context.Reporter, { Resource::String::FontFamily, Resource::String::FontFaces }); for (auto line : lines) { table.OutputLine({ line.FamilyName, std::to_string(line.FaceCount) }); } table.Complete(); } void OutputInstalledFontFacesTable(Execution::Context& context, const std::vector& lines) { Execution::TableOutput<4> table(context.Reporter, { Resource::String::FontFamily, Resource::String::FontFace, Resource::String::FontVersion, Resource::String::FontFilePaths }); bool anonymizePath = Settings::User().Get(); for (auto line : lines) { if (anonymizePath) { AppInstaller::Runtime::ReplaceProfilePathsWithEnvironmentVariable(line.FilePath); } table.OutputLine({ line.FamilyName, line.FaceName, line.FaceVersion, line.FilePath.u8string() }); } table.Complete(); } void OutputInstalledFontFilesTable(Execution::Context& context, const std::vector& lines) { Execution::TableOutput<4> table(context.Reporter, { Resource::String::FontTitle, Resource::String::FontPackage, Resource::String::FontStatus, Resource::String::FontFilePaths }); bool anonymizePath = Settings::User().Get(); for (auto line : lines) { if (anonymizePath) { AppInstaller::Runtime::ReplaceProfilePathsWithEnvironmentVariable(line.FilePath); } table.OutputLine({ line.Title, line.PackageName, line.FontStatus, line.FilePath.u8string() }); } table.Complete(); } } void ReportInstalledFonts(Execution::Context& context) { Fonts::FontCatalog fontCatalog; if (context.Args.Contains(Args::Type::Family)) { // TODO: Create custom source and search mechanism for fonts. const auto& familyNameArg = AppInstaller::Utility::ConvertToUTF16(context.Args.GetArg(Args::Type::Family)); const auto& fontFamilies = fontCatalog.GetInstalledFontFamilies(familyNameArg); if (fontFamilies.empty()) { context.Reporter.Info() << Resource::String::NoInstalledFontFound << std::endl; return; } std::vector lines; for (const auto& fontFamily : fontFamilies) { const auto& familyName = Utility::LocIndString(Utility::ConvertToUTF8(fontFamily.Name)); for (const auto& fontFace : fontFamily.Faces) { for (const auto& filePath : fontFace.FilePaths) { InstalledFontFacesTableLine line( familyName, Utility::LocIndString(Utility::ToLower(Utility::ConvertToUTF8(fontFace.Name))), Utility::LocIndString(fontFace.Version.ToString()), filePath.u8string()); lines.push_back(std::move(line)); } } } OutputInstalledFontFacesTable(context, lines); } else if (context.Args.Contains(Args::Type::Details)) { const auto& fontFiles = AppInstaller::Fonts::GetInstalledFontFiles(); std::vector lines; for (const auto& fontFile : fontFiles) { Resource::LocString status; switch (fontFile.Status) { case FontStatus::OK: status = Resource::LocString(Resource::String::FontStatusOK); break; case FontStatus::Corrupt: status = Resource::LocString(Resource::String::FontStatusCorrupt); break; default: status = Resource::LocString(Resource::String::FontStatusUnknown); break; } InstalledFontFilesTableLine line( Utility::LocIndString(Utility::ConvertToUTF8(fontFile.Title)), Utility::LocIndString(Utility::ConvertToUTF8(fontFile.PackageIdentifier)), status, fontFile.FilePath.u8string()); lines.push_back(std::move(line)); } OutputInstalledFontFilesTable(context, lines); } else { const auto& fontFamilies = fontCatalog.GetInstalledFontFamilies(); std::vector lines; for (const auto& fontFamily : fontFamilies) { InstalledFontFamiliesTableLine line( Utility::LocIndString(Utility::ConvertToUTF8(fontFamily.Name)), static_cast(fontFamily.Faces.size()) ); lines.push_back(std::move(line)); } OutputInstalledFontFamiliesTable(context, lines); } } void FontInstallImpl(Execution::Context& context) { context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; // We will default to User scope. Manifest::ScopeEnum scope = Manifest::ScopeEnum::User; if (context.Args.Contains(Execution::Args::Type::InstallScope)) { scope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); } Fonts::FontContext fontContext; fontContext.InstallerSource = InstallerSource::WinGet; fontContext.Scope = scope; auto& manifest = context.Get(); fontContext.PackageId = ConvertToUTF16(manifest.Id); fontContext.PackageVersion = ConvertToUTF16(manifest.Version); if (context.Args.Contains(Execution::Args::Type::Force)) { fontContext.Force = true; } try { const auto& installerPath = context.Get(); // InstallerPath will point to a directory if extracted from an archive. if (std::filesystem::is_directory(installerPath)) { const std::vector& nestedInstallerFiles = context.Get()->NestedInstallerFiles; for (const auto& nestedInstallerFile : nestedInstallerFiles) { fontContext.AddPackageFile(installerPath / ConvertToUTF16(nestedInstallerFile.RelativeFilePath)); } } else { fontContext.AddPackageFile(installerPath); } const auto& fontValidationResult = Fonts::ValidateFontPackage(fontContext); if (fontValidationResult.Result != FontResult::Success) { context.Reporter.Error() << Resource::String::FontValidationFailed << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_FONT_VALIDATION_FAILED); } if (fontValidationResult.HasUnsupportedFonts) { context.Reporter.Error() << Resource::String::FontFileNotSupported << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_SUPPORTED); } if (fontValidationResult.Status == FontStatus::OK && !fontContext.Force) { context.Reporter.Warn() << Resource::String::FontAlreadyInstalled << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_FONT_ALREADY_INSTALLED); } auto installResult = Fonts::InstallFontPackage(fontContext); if (installResult.Result() != FontResult::Success) { context.Reporter.Error() << Resource::String::FontInstallFailed << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED); } context.Add({ fontContext.GetAppsAndFeaturesEntry() }); context.Add(installResult.HResult); } catch (...) { context.Add(Workflow::HandleException(context, std::current_exception())); context.Reporter.Warn() << Resource::String::FontInstallFailed << std::endl; try { // The Font Install code handles rollback where appropriate. If we hit an // unexpected exception, try to uninstall anyway to arrive at a consistent // absent state. Since we install side-by-side for versions, this should // only result in an absent state of an installed font if this were a forced // install of an existing version. auto uninstallResult = Fonts::UninstallFontPackage(fontContext); if (uninstallResult.Result() != FontResult::Success) { context.Reporter.Warn() << Resource::String::FontRollbackFailed << std::endl; } } CATCH_LOG(); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED); } } void FontUninstallImpl(Execution::Context& context) { context.Reporter.Info() << Resource::String::UninstallFlowStartingPackageUninstall << std::endl; try { // We will default to installed scope. auto scope = Manifest::ConvertToScopeEnum(context.Get()->GetMetadata()[Repository::PackageVersionMetadata::InstalledScope]); if (context.Args.Contains(Execution::Args::Type::InstallScope)) { scope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); } Fonts::FontContext fontContext; fontContext.InstallerSource = InstallerSource::WinGet; if (context.Args.Contains(Execution::Args::Type::Manifest)) { const auto& manifest = context.Get(); fontContext.PackageId = ConvertToUTF16(manifest.Id); fontContext.PackageVersion = ConvertToUTF16(manifest.Version); fontContext.Scope = scope; } else { const std::string moniker = context.Get()->GetProperty(AppInstaller::Repository::PackageVersionProperty::Moniker); const std::string version = context.Get()->GetProperty(AppInstaller::Repository::PackageVersionProperty::Version); fontContext.PackageId = ConvertToUTF16(moniker); fontContext.PackageVersion = ConvertToUTF16(version); fontContext.Scope = scope; } if (fontContext.Scope == Manifest::ScopeEnum::Machine && !Runtime::IsRunningAsAdmin()) { context.Reporter.Error() << Resource::String::CommandRequiresAdmin << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN); } auto uninstallResult = Fonts::UninstallFontPackage(fontContext); if (uninstallResult.Result() != FontResult::Success) { context.Reporter.Error() << Resource::String::FontUninstallFailed << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED); } context.Add(uninstallResult.HResult); } catch (...) { context.Add(Workflow::HandleException(context, std::current_exception())); context.Reporter.Error() << Resource::String::FontUninstallFailed << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED); } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/FontFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Reports the installed fonts as a table. // Required Args: None // Inputs: None // Outputs: None void ReportInstalledFonts(Execution::Context& context); // Installs the font package. // Required Args: None // Inputs: Manifest, Scope, Rename, Location // Outputs: None void FontInstallImpl(Execution::Context& context); // Uninstalls the font package. // Required Args: None // Inputs: Manifest, Scope, Rename, Location // Outputs: None void FontUninstallImpl(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "InstallFlow.h" #include "ImportExportFlow.h" #include "UpdateFlow.h" #include "PackageCollection.h" #include "DependenciesFlow.h" #include "WorkflowBase.h" #include #include #include namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::Repository; namespace { SourceDetails GetSourceDetails(const SourceDetails& source) { return source; } SourceDetails GetSourceDetails(const PackageCollection::Source& source) { return source.Details; } SourceDetails GetSourceDetails(const Repository::Source& source) { return source.GetDetails(); } // Creates a predicate that determines whether a source matches a description in a SourceDetails. template std::function GetSourceDetailsEquivalencePredicate(const SourceDetails& details) { return [&](const T& source) { SourceDetails sourceDetails = GetSourceDetails(source); return sourceDetails.Type == details.Type && sourceDetails.Identifier == details.Identifier; }; } // Finds a source equivalent to the one specified. template typename std::vector::const_iterator FindSource(const std::vector& sources, const SourceDetails& details) { return std::find_if(sources.begin(), sources.end(), GetSourceDetailsEquivalencePredicate(details)); } // Finds a source equivalent to the one specified. template typename std::vector::iterator FindSource(std::vector& sources, const SourceDetails& details) { return std::find_if(sources.begin(), sources.end(), GetSourceDetailsEquivalencePredicate(details)); } // Gets the available version of an installed package. // If requested, checks that the installed version is available and reports a warning if it is not. std::shared_ptr GetAvailableVersionForInstalledPackage( Execution::Context& context, std::shared_ptr package, Utility::LocIndView version, Utility::LocIndView channel, bool checkVersion) { std::shared_ptr availableVersions = GetAvailableVersionsForInstalledVersion(package); if (!checkVersion) { return availableVersions->GetLatestVersion(); } auto availablePackageVersion = availableVersions->GetVersion({ "", version, channel }); if (!availablePackageVersion) { availablePackageVersion = availableVersions->GetLatestVersion(); if (availablePackageVersion) { // Warn installed version is not available. AICLI_LOG( CLI, Info, << "Installed package version is not available." << " Package Id [" << availablePackageVersion->GetProperty(PackageVersionProperty::Id) << "], Version [" << version << "], Channel [" << channel << "]" << ". Found Version [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "], Channel [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "]"); context.Reporter.Warn() << Resource::String::InstalledPackageVersionNotAvailable(availablePackageVersion->GetProperty(PackageVersionProperty::Id), version, channel) << std::endl; } } return availablePackageVersion; } } void SelectVersionsToExport(Execution::Context& context) { const auto& searchResult = context.Get(); const bool includeVersions = context.Args.Contains(Execution::Args::Type::IncludeVersions); PackageCollection exportedPackages; exportedPackages.ClientVersion = Runtime::GetClientVersion().get(); auto& exportedSources = exportedPackages.Sources; for (const auto& packageMatch : searchResult.Matches) { auto installedPackageVersion = GetInstalledVersion(packageMatch.Package); auto version = installedPackageVersion->GetProperty(PackageVersionProperty::Version); auto channel = installedPackageVersion->GetProperty(PackageVersionProperty::Channel); // Find an available version of this package to determine its source. auto availablePackageVersion = GetAvailableVersionForInstalledPackage(context, packageMatch.Package, version, channel, includeVersions); if (!availablePackageVersion) { // Report package not found and move to next package. AICLI_LOG(CLI, Warning, << "No available version of package [" << installedPackageVersion->GetProperty(PackageVersionProperty::Name) << "] was found to export"); context.Reporter.Warn() << Resource::String::InstalledPackageNotAvailable(installedPackageVersion->GetProperty(PackageVersionProperty::Name)) << std::endl; continue; } const auto& sourceDetails = availablePackageVersion->GetSource().GetDetails(); AICLI_LOG(CLI, Info, << "Installed package is available. Package Id [" << availablePackageVersion->GetProperty(PackageVersionProperty::Id) << "], Source [" << sourceDetails.Identifier << "]"); if (!availablePackageVersion->GetManifest().DefaultLocalization.Get().empty()) { // Report that the package requires accepting license terms AICLI_LOG(CLI, Warning, << "Package [" << installedPackageVersion->GetProperty(PackageVersionProperty::Name) << "] requires license agreement to install"); context.Reporter.Warn() << Resource::String::ExportedPackageRequiresLicenseAgreement(installedPackageVersion->GetProperty(PackageVersionProperty::Name)) << std::endl; } // Find the exported source for this package auto sourceItr = FindSource(exportedSources, sourceDetails); if (sourceItr == exportedSources.end()) { exportedSources.emplace_back(sourceDetails); sourceItr = std::prev(exportedSources.end()); } // Take the Id from the available package because that is the one used in the source, // but take the exported version from the installed package if needed. PackageCollection::Package exportPackage; exportPackage.Id = availablePackageVersion->GetProperty(PackageVersionProperty::Id); exportPackage.InstalledLocation = Utility::ConvertToUTF16(installedPackageVersion->GetMetadata()[PackageVersionMetadata::InstalledLocation]); if (includeVersions) { exportPackage.VersionAndChannel = { version.get(), channel.get() }; } sourceItr->Packages.emplace_back(std::move(exportPackage)); } context.Add(std::move(exportedPackages)); } void WriteImportFile(Execution::Context& context) { auto packages = PackagesJson::CreateJson(context.Get()); std::filesystem::path outputFilePath{ context.Args.GetArg(Execution::Args::Type::OutputFile) }; std::ofstream outputFileStream{ outputFilePath }; outputFileStream << packages; } void ReadImportFile(Execution::Context& context) { std::ifstream importFile(Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::ImportFile))); THROW_LAST_ERROR_IF(importFile.fail()); Json::Value jsonRoot; Json::CharReaderBuilder builder; Json::String errors; if (!Json::parseFromStream(builder, importFile, &jsonRoot, &errors)) { AICLI_LOG(CLI, Error, << "Failed to read JSON: " << errors); context.Reporter.Error() << Resource::String::InvalidJsonFile << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE); } PackagesJson::ParseResult parseResult = PackagesJson::TryParseJson(jsonRoot); if (parseResult.Result != PackagesJson::ParseResult::Type::Success) { context.Reporter.Error() << Resource::String::InvalidJsonFile << std::endl; if (parseResult.Result == PackagesJson::ParseResult::Type::MissingSchema || parseResult.Result == PackagesJson::ParseResult::Type::UnrecognizedSchema) { context.Reporter.Error() << Resource::String::ImportFileHasInvalidSchema << std::endl; } else if (parseResult.Result == PackagesJson::ParseResult::Type::SchemaValidationFailed) { context.Reporter.Error() << parseResult.Errors << std::endl; } AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE); } PackageCollection& packages = parseResult.Packages; if (packages.Sources.empty()) { AICLI_LOG(CLI, Warning, << "No packages to install"); context.Reporter.Info() << Resource::String::NoPackagesFoundInImportFile << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); } if (context.Args.Contains(Execution::Args::Type::IgnoreVersions)) { // Strip out all the version information as we don't need it. for (auto& source : packages.Sources) { for (auto& package : source.Packages) { package.VersionAndChannel = {}; } } } context.Add(std::move(packages)); } void OpenSourcesForImport(Execution::Context& context) { auto availableSources = Repository::Source::GetCurrentSources(); for (auto& requiredSource : context.Get().Sources) { // Find the installed source matching the one described in the collection. AICLI_LOG(CLI, Info, << "Looking for source [" << requiredSource.Details.Identifier << "]"); auto matchingSource = FindSource(availableSources, requiredSource.Details); if (matchingSource != availableSources.end()) { requiredSource.Details.Name = matchingSource->Name; } else { AICLI_LOG(CLI, Error, << "Missing required source: " << requiredSource.Details.Name); context.Reporter.Warn() << Resource::String::ImportSourceNotInstalled(Utility::LocIndView{ requiredSource.Details.Name }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } context << Workflow::OpenNamedSourceForSources(requiredSource.Details.Name); if (context.IsTerminated()) { return; } } } void GetSearchRequestsForImport(Execution::Context& context) { const auto& sources = context.Get(); std::vector> packageSubContexts; // Look for the packages needed from each source independently. // If a package is available from multiple sources, this ensures we will get it from the right one. for (auto& requiredSource : context.Get().Sources) { // Find the required source among the open sources. This must exist as we already found them. auto sourceItr = FindSource(sources, requiredSource.Details); if (sourceItr == sources.end()) { AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR); } // Search for all the packages in the source. // Each search is done in a sub context to search everything regardless of previous failures. Repository::Source source{ context.Get(), *sourceItr, CompositeSearchBehavior::AllPackages }; AICLI_LOG(CLI, Info, << "Identifying packages requested from source [" << requiredSource.Details.Identifier << "]"); for (const auto& packageRequest : requiredSource.Packages) { AICLI_LOG(CLI, Info, << "Searching for package [" << packageRequest.Id << "]"); // Search for the current package SearchRequest searchRequest; searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, packageRequest.Id.get())); auto searchContextPtr = context.CreateSubContext(); Execution::Context& searchContext = *searchContextPtr; auto previousThreadGlobals = searchContext.SetForCurrentThread(); searchContext.Add(source); searchContext.Add(std::move(searchRequest)); if (packageRequest.Scope != Manifest::ScopeEnum::Unknown) { // TODO: In the future, it would be better to not have to convert back and forth from a string searchContext.Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(packageRequest.Scope)); } auto versionString = packageRequest.VersionAndChannel.GetVersion().ToString(); if (!versionString.empty()) { searchContext.Args.AddArg(Execution::Args::Type::Version, versionString); } auto channelString = packageRequest.VersionAndChannel.GetChannel().ToString(); if (!channelString.empty()) { searchContext.Args.AddArg(Execution::Args::Type::Channel, channelString); } packageSubContexts.emplace_back(std::move(searchContextPtr)); } } context.Add(std::move(packageSubContexts)); } void InstallImportedPackages(Execution::Context& context) { // Inform all dependencies here. During SubContexts processing, dependencies are ignored. auto& packageSubContexts = context.Get(); Manifest::DependencyList allDependencies; for (auto& packageContext : packageSubContexts) { allDependencies.Add(packageContext->Get().value().Dependencies); } context.Add(allDependencies); context << ReportDependencies(Resource::String::ImportCommandReportDependencies) << ProcessMultiplePackages( Resource::String::ImportCommandReportDependencies, APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED, ProcessMultiplePackages::Flags::IgnoreDependencies); if (context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED) { context.Reporter.Error() << Resource::String::ImportInstallFailed << std::endl; } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/ImportExportFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Selects the package versions to list on the exported file // Required Args: None // Inputs: SearchResult // Outputs: PackageCollection void SelectVersionsToExport(Execution::Context& context); // Exports a collection of packages to a JSON import file // Required Args: OutputFile // Inputs: PackageCollection // Outputs: None void WriteImportFile(Execution::Context& context); // Reads the contents of an import file // Required Args: ImportFile // Inputs: None // Outputs: PackageCollection void ReadImportFile(Execution::Context& context); // Opens the sources specified in an import file // Required Args: None // Inputs: PackageCollection // Outputs: Sources void OpenSourcesForImport(Execution::Context& context); // Create the search requests and install sub-contexts for all the imported packages. // Needs the sources for all packages and the installed source // Required Args: None // Inputs: PackageCollection, Sources, Source // Outputs: PackageSubContexts // SubContext Inputs: None // SubContext Outputs: Source, SearchRequest void GetSearchRequestsForImport(Execution::Context& context); // Installs all the packages found in the import file. // Required Args: None // Inputs: PackageSubContexts // Outputs: None void InstallImportedPackages(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/InstallFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "InstallFlow.h" #include "DownloadFlow.h" #include "FontFlow.h" #include "UninstallFlow.h" #include "UpdateFlow.h" #include "ResumeFlow.h" #include "ShowFlow.h" #include "Resources.h" #include "ShellExecuteInstallerHandler.h" #include "MSStoreInstallerHandler.h" #include "MsiInstallFlow.h" #include "ArchiveFlow.h" #include "PortableFlow.h" #include "WorkflowBase.h" #include "DependenciesFlow.h" #include "PromptFlow.h" #include "SourceFlow.h" #include #include #include #include #include #include #include #include #include using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::Management::Deployment; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Registry::Environment; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Workflow { namespace { bool MightWriteToARP(InstallerTypeEnum type) { switch (type) { case InstallerTypeEnum::Exe: case InstallerTypeEnum::Burn: case InstallerTypeEnum::Inno: case InstallerTypeEnum::Msi: case InstallerTypeEnum::Nullsoft: case InstallerTypeEnum::Wix: return true; default: return false; } } bool ShouldUseDirectMSIInstall(InstallerTypeEnum type, bool isSilentInstall) { switch (type) { case InstallerTypeEnum::Msi: case InstallerTypeEnum::Wix: return isSilentInstall || ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::DirectMSI); default: return false; } } bool ShouldErrorForUnsupportedArgument(UnsupportedArgumentEnum arg) { switch (arg) { case UnsupportedArgumentEnum::Location: return true; default: return false; } } Execution::Args::Type GetUnsupportedArgumentType(UnsupportedArgumentEnum unsupportedArgument) { Execution::Args::Type execArg; switch (unsupportedArgument) { case UnsupportedArgumentEnum::Log: execArg = Execution::Args::Type::Log; break; case UnsupportedArgumentEnum::Location: execArg = Execution::Args::Type::InstallLocation; break; default: THROW_HR(E_UNEXPECTED); } return execArg; } struct ExpectedReturnCode { ExpectedReturnCode(ExpectedReturnCodeEnum installerReturnCode, HRESULT hr, Resource::StringId message) : InstallerReturnCode(installerReturnCode), HResult(hr), Message(message) {} static ExpectedReturnCode GetExpectedReturnCode(ExpectedReturnCodeEnum returnCode) { switch (returnCode) { case ExpectedReturnCodeEnum::PackageInUse: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE, Resource::String::InstallFlowReturnCodePackageInUse); case ExpectedReturnCodeEnum::PackageInUseByApplication: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE_BY_APPLICATION, Resource::String::InstallFlowReturnCodePackageInUseByApplication); case ExpectedReturnCodeEnum::InstallInProgress: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS, Resource::String::InstallFlowReturnCodeInstallInProgress); case ExpectedReturnCodeEnum::FileInUse: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_FILE_IN_USE, Resource::String::InstallFlowReturnCodeFileInUse); case ExpectedReturnCodeEnum::MissingDependency: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY, Resource::String::InstallFlowReturnCodeMissingDependency); case ExpectedReturnCodeEnum::DiskFull: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_DISK_FULL, Resource::String::InstallFlowReturnCodeDiskFull); case ExpectedReturnCodeEnum::InsufficientMemory: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_INSUFFICIENT_MEMORY, Resource::String::InstallFlowReturnCodeInsufficientMemory); case ExpectedReturnCodeEnum::InvalidParameter: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_INVALID_PARAMETER, Resource::String::InstallFlowReturnCodeInvalidParameter); case ExpectedReturnCodeEnum::NoNetwork: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_NO_NETWORK, Resource::String::InstallFlowReturnCodeNoNetwork); case ExpectedReturnCodeEnum::ContactSupport: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT, Resource::String::InstallFlowReturnCodeContactSupport); case ExpectedReturnCodeEnum::RebootRequiredToFinish: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH, Resource::String::InstallFlowReturnCodeRebootRequiredToFinish); case ExpectedReturnCodeEnum::RebootRequiredForInstall: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL, Resource::String::InstallFlowReturnCodeRebootRequiredForInstall); case ExpectedReturnCodeEnum::RebootInitiated: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_INITIATED, Resource::String::InstallFlowReturnCodeRebootInitiated); case ExpectedReturnCodeEnum::CancelledByUser: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_CANCELLED_BY_USER, Resource::String::InstallFlowReturnCodeCancelledByUser); case ExpectedReturnCodeEnum::AlreadyInstalled: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_ALREADY_INSTALLED, Resource::String::InstallFlowReturnCodeAlreadyInstalled); case ExpectedReturnCodeEnum::Downgrade: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_DOWNGRADE, Resource::String::InstallFlowReturnCodeDowngrade); case ExpectedReturnCodeEnum::BlockedByPolicy: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY, Resource::String::InstallFlowReturnCodeBlockedByPolicy); case ExpectedReturnCodeEnum::SystemNotSupported: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED, Resource::String::InstallFlowReturnCodeSystemNotSupported); case ExpectedReturnCodeEnum::Custom: return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_CUSTOM_ERROR, Resource::String::InstallFlowReturnCodeCustomError); default: THROW_HR(E_UNEXPECTED); } } ExpectedReturnCodeEnum InstallerReturnCode; HRESULT HResult; Resource::StringId Message; }; void CheckForOnlyDependencies(Execution::Context& context) { if (context.Args.Contains(Execution::Args::Type::DependenciesOnly)) { context.Reporter.Info() << Resource::String::DependenciesOnlyMessage << std::endl; // We want the context to terminate, but successfully. context.SetTerminationHR(S_OK); } } } namespace details { // Runs the installer via ShellExecute. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void ShellExecuteInstall(Execution::Context& context) { context << GetInstallerArgs << ShellExecuteInstallImpl << ReportInstallerResult("ShellExecute"sv, APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED); } // Runs an MSI installer directly via MSI APIs. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void DirectMSIInstall(Execution::Context& context) { context << GetInstallerArgs << DirectMSIInstallImpl << ReportInstallerResult("MsiInstallProduct"sv, APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED); } // Deploys the MSIX. // Required Args: None // Inputs: Manifest?, Installer || InstallerPath // Outputs: None void MsixInstall(Execution::Context& context) { std::string uri; Deployment::Options deploymentOptions; if (context.Contains(Execution::Data::InstallerPath)) { uri = context.Get().u8string(); } else { uri = context.Get()->Url; deploymentOptions.ExpectedDigests = context.Get(); } deploymentOptions.SkipReputationCheck = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerTrusted); bool isMachineScope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)) == Manifest::ScopeEnum::Machine; // TODO: There was a bug in deployment api if provision api was called in packaged context. // Remove this check when the OS bug is fixed and back ported. if (isMachineScope && Runtime::IsRunningInPackagedContext()) { context.Reporter.Error() << Resource::String::InstallFlowReturnCodeSystemNotSupported << std::endl; context.Add(static_cast(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED)); AICLI_LOG(CLI, Error, << "Device wide install for msix type is not supported in packaged context."); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED); } context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; bool registrationDeferred = false; try { registrationDeferred = context.Reporter.ExecuteWithProgress([&](IProgressCallback& callback) { if (isMachineScope) { return Deployment::AddPackageMachineScope(uri, deploymentOptions, callback); } else { return Deployment::AddPackageWithDeferredFallback(uri, deploymentOptions, callback); } }); } catch (const wil::ResultException& re) { context.Add(re.GetErrorCode()); context << ReportInstallerResult("MSIX"sv, re.GetErrorCode(), /* isHResult */ true); return; } if (registrationDeferred) { context.Reporter.Warn() << Resource::String::InstallFlowRegistrationDeferred << std::endl; } else { context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; } } // Runs the flow for installing a Portable package. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void PortableInstall(Execution::Context& context) { context << InitializePortableInstaller << VerifyPackageAndSourceMatch << PortableInstallImpl << ReportInstallerResult("Portable"sv, APPINSTALLER_CLI_ERROR_PORTABLE_INSTALL_FAILED, true); } // Runs the flow for installing a package from an archive. // Required Args: None // Inputs: Installer, InstallerPath, Manifest // Outputs: None void ArchiveInstall(Execution::Context& context) { context << ScanArchiveFromLocalManifest << ExtractFilesFromArchive << VerifyAndSetNestedInstaller << ExecuteInstallerForType(context.Get().value().NestedInstallerType); } // Runs the flow for installing a font package. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void FontInstall(Execution::Context& context) { context << GetInstallerArgs << FontInstallImpl << ReportInstallerResult("Font"sv, APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED, true); } } bool ExemptFromSingleInstallLocking(InstallerTypeEnum type) { switch (type) { // MSStore installs are always MSIX based; MSIX installs are safe to run in parallel. case InstallerTypeEnum::Msix: case InstallerTypeEnum::MSStore: return true; default: return false; } } void EnsureApplicableInstaller(Execution::Context& context) { const auto& installer = context.Get(); if (!installer.has_value()) { context.Reporter.Error() << Resource::String::NoApplicableInstallers << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER); } context << EnsureSupportForDownload << EnsureSupportForInstall; } void CheckForUnsupportedArgs(Execution::Context& context) { bool messageDisplayed = false; const auto& unsupportedArgs = context.Get()->UnsupportedArguments; for (auto unsupportedArg : unsupportedArgs) { const auto& unsupportedArgType = GetUnsupportedArgumentType(unsupportedArg); if (context.Args.Contains(unsupportedArgType)) { if (!messageDisplayed) { context.Reporter.Warn() << Resource::String::UnsupportedArgument << std::endl; messageDisplayed = true; } const auto& executingCommand = context.GetExecutingCommand(); if (executingCommand != nullptr) { const auto& commandArguments = executingCommand->GetArguments(); for (const auto& argument : commandArguments) { if (unsupportedArgType == argument.ExecArgType()) { const auto& usageString = argument.GetUsageString(); if (ShouldErrorForUnsupportedArgument(unsupportedArg)) { context.Reporter.Error() << usageString << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT); } else { context.Reporter.Warn() << usageString << std::endl; break; } } } } } } } void ShowInstallationDisclaimer(Execution::Context& context) { auto installerType = context.Get().value().EffectiveInstallerType(); if (installerType == InstallerTypeEnum::MSStore) { context.Reporter.Info() << Execution::PromptEmphasis << Resource::String::InstallationDisclaimerMSStore << std::endl; } else { context.Reporter.Info() << Resource::String::InstallationDisclaimer1 << std::endl << Resource::String::InstallationDisclaimer2 << std::endl; } } void DisplayInstallationNotes(Execution::Context& context) { if (!Settings::User().Get()) { const auto& manifest = context.Get(); auto installationNotes = manifest.CurrentLocalization.Get(); if (!installationNotes.empty()) { context.Reporter.Info() << Resource::String::Notes(installationNotes) << std::endl; } } } void ExecuteInstallerForType::operator()(Execution::Context& context) const { bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); UpdateBehaviorEnum updateBehavior = context.Get().value().UpdateBehavior; bool doUninstallPrevious = isUpdate && (updateBehavior == UpdateBehaviorEnum::UninstallPrevious || context.Args.Contains(Execution::Args::Type::UninstallPrevious)); Synchronization::CrossProcessInstallLock lock; if (!ExemptFromSingleInstallLocking(m_installerType)) { // Acquire install lock; if the operation is cancelled it will return false so we will also return. if (!context.Reporter.ExecuteWithProgress([&](IProgressCallback& callback) { callback.SetProgressMessage(Resource::String::InstallWaitingOnAnother()); return lock.Acquire(callback); })) { AICLI_LOG(CLI, Info, << "Abandoning attempt to acquire install lock due to cancellation"); return; } } switch (m_installerType) { case InstallerTypeEnum::Exe: case InstallerTypeEnum::Burn: case InstallerTypeEnum::Inno: case InstallerTypeEnum::Msi: case InstallerTypeEnum::Nullsoft: case InstallerTypeEnum::Wix: if (doUninstallPrevious) { context << GetUninstallInfo << ExecuteUninstaller; context.ClearFlags(Execution::ContextFlag::InstallerExecutionUseUpdate); } if (ShouldUseDirectMSIInstall(m_installerType, context.Args.Contains(Execution::Args::Type::Silent))) { context << details::DirectMSIInstall; } else { context << details::ShellExecuteInstall; } break; case InstallerTypeEnum::Msix: context << details::MsixInstall; break; case InstallerTypeEnum::MSStore: context << EnsureStorePolicySatisfied << (isUpdate ? MSStoreUpdate : MSStoreInstall); break; case InstallerTypeEnum::Portable: if (doUninstallPrevious) { context << GetUninstallInfo << ExecuteUninstaller; context.ClearFlags(Execution::ContextFlag::InstallerExecutionUseUpdate); } context << details::PortableInstall; break; case InstallerTypeEnum::Zip: context << details::ArchiveInstall; break; case InstallerTypeEnum::Font: context << details::FontInstall; break; default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } void EnsureRunningAsAdminForMachineScopeInstall(Execution::Context& context) { // Admin is required for machine scope install for installer types like portable, msix and msstore. auto installerType = context.Get().value().EffectiveInstallerType(); if (Manifest::DoesInstallerTypeRequireAdminForMachineScopeInstall(installerType)) { Manifest::ScopeEnum scope = ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); if (scope == Manifest::ScopeEnum::Machine) { context << Workflow::EnsureRunningAsAdmin; } } } void ExecuteInstaller(Execution::Context& context) { context << Workflow::ExecuteInstallerForType(context.Get().value().BaseInstallerType); } void ReportInstallerResult::operator()(Execution::Context& context) const { bool isRepair = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseRepair); DWORD installResult = context.Get(); const auto& additionalSuccessCodes = context.Get()->InstallerSuccessCodes; if (installResult != 0 && (std::find(additionalSuccessCodes.begin(), additionalSuccessCodes.end(), installResult) == additionalSuccessCodes.end())) { HRESULT terminationHR = m_hr; const auto& expectedReturnCodes = context.Get()->ExpectedReturnCodes; auto expectedReturnCodeItr = expectedReturnCodes.find(installResult); if (expectedReturnCodeItr != expectedReturnCodes.end() && expectedReturnCodeItr->second.ReturnResponseEnum != ExpectedReturnCodeEnum::Unknown) { auto returnCode = ExpectedReturnCode::GetExpectedReturnCode(expectedReturnCodeItr->second.ReturnResponseEnum); terminationHR = returnCode.HResult; switch (terminationHR) { case APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH: // REBOOT_REQUIRED_TO_FINISH is treated as a success since installation has completed but is pending a reboot. context.SetFlags(ContextFlag::RebootRequired); context.Reporter.Warn() << returnCode.Message << std::endl; terminationHR = S_OK; break; case APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL: // REBOOT_REQUIRED_FOR_INSTALL is treated as an error since installation has not yet completed. context.SetFlags(ContextFlag::RebootRequired); // TODO: Add separate workflow to handle restart registration for resume. context.SetFlags(ContextFlag::RegisterResume); break; } if (FAILED(terminationHR)) { context.Reporter.Error() << returnCode.Message << std::endl; const auto& returnResponseUrl = expectedReturnCodeItr->second.ReturnResponseUrl; if (!returnResponseUrl.empty()) { context.Reporter.Error() << Resource::String::RelatedLink << ' ' << returnResponseUrl << std::endl; } } } if (FAILED(terminationHR)) { const auto& manifest = context.Get(); if (isRepair) { Logging::Telemetry().LogRepairFailure(manifest.Id, manifest.Version, m_installerType, installResult); } else { Logging::Telemetry().LogInstallerFailure(manifest.Id, manifest.Version, manifest.Channel, m_installerType, installResult); } if (m_isHResult) { context.Reporter.Error() << Resource::String::InstallerFailedWithCode(Utility::LocIndView{ GetUserPresentableMessage(installResult) }) << std::endl; } else { context.Reporter.Error() << Resource::String::InstallerFailedWithCode(installResult) << std::endl; } // Show installer log path if exists if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get())) { auto installerLogPath = Utility::LocIndString{ context.Get().u8string() }; context.Reporter.Info() << Resource::String::InstallerLogAvailable(installerLogPath) << std::endl; } AICLI_TERMINATE_CONTEXT(terminationHR); } } else { if (isRepair) { context.Reporter.Info() << Resource::String::RepairFlowRepairSuccess << std::endl; } else { context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; } } } void ReportIdentityAndInstallationDisclaimer(Execution::Context& context) { context << Workflow::ReportManifestIdentityWithVersion() << Workflow::ShowInstallationDisclaimer; } void InstallPackageInstaller(Execution::Context& context) { context << Workflow::ReportExecutionStage(ExecutionStage::PreExecution) << Workflow::SnapshotARPEntries << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteInstaller << Workflow::ReportExecutionStage(ExecutionStage::PostExecution) << Workflow::ReportARPChanges << Workflow::RecordInstall << Workflow::ForceInstalledCacheUpdate << Workflow::RemoveInstaller << Workflow::DisplayInstallationNotes; } void InstallDependencies(Execution::Context& context) { using Flags = ProcessMultiplePackages::Flags; if (Settings::User().Get() || context.Args.Contains(Execution::Args::Type::SkipDependencies)) { context.Reporter.Warn() << Resource::String::DependenciesSkippedMessage << std::endl; return; } context << GetDependenciesFromInstaller << ReportDependencies(Resource::String::PackageRequiresDependencies) << EnableWindowsFeaturesDependencies << ProcessMultiplePackages(Resource::String::PackageRequiresDependencies, APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES, Flags::IgnoreDependencies | Flags::StopOnFailure | Flags::RefreshPathVariable); } void DownloadPackageDependencies(Execution::Context& context) { using Flags = ProcessMultiplePackages::Flags; if (Settings::User().Get() || context.Args.Contains(Execution::Args::Type::SkipDependencies)) { context.Reporter.Warn() << Resource::String::DependenciesSkippedMessage << std::endl; return; } context << GetDependenciesFromInstaller << ReportDependencies(Resource::String::PackageRequiresDependencies) << CreateDependencySubContexts(Resource::String::PackageRequiresDependencies) << ProcessMultiplePackages(Resource::String::PackageRequiresDependencies, APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES, Flags::IgnoreDependencies | Flags::StopOnFailure | Flags::DownloadOnly); } void InstallSinglePackage(Execution::Context& context) { context << Workflow::CheckForUnsupportedArgs << Workflow::ReportIdentityAndInstallationDisclaimer << Workflow::ShowPromptsForSinglePackage(/* ensureAcceptance */ true) << Workflow::CreateDependencySubContexts(Resource::String::PackageRequiresDependencies) << Workflow::InstallDependencies << CheckForOnlyDependencies << Workflow::DownloadInstaller << Workflow::InstallPackageInstaller << Workflow::RegisterStartupAfterReboot(); } void EnsureSupportForInstall(Execution::Context& context) { if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly)) { return; } const auto& installer = context.Get(); // This check is only necessary for the Repair workflow when operating on an installer with RepairBehavior set to Installer. if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseRepair)) { if (installer->RepairBehavior != RepairBehaviorEnum::Installer) { return; } // At present, the installer repair behavior scenario is restricted to Exe, Inno, Nullsoft, and Burn installer types. if (!DoesInstallerTypeRequireRepairBehaviorForRepair(installer->EffectiveInstallerType())) { return; } } // This installer cannot be run elevated, but we are running elevated. // Implementation of de-elevation is complex; simply block for now. if (installer->ElevationRequirement == ElevationRequirementEnum::ElevationProhibited && Runtime::IsRunningAsAdmin()) { AICLI_LOG(CLI, Error, << "The installer cannot be run from an administrator context."); context.Reporter.Error() << Resource::String::InstallerProhibitsElevation << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_PROHIBITS_ELEVATION); } // This installer cannot be used to upgrade the currently installed application // Because the upgrade mechanism may be package-specific, simply block. bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); UpdateBehaviorEnum updateBehavior = installer->UpdateBehavior; if (isUpdate && (updateBehavior == UpdateBehaviorEnum::Deny)) { AICLI_LOG(CLI, Error, << "Manifest specifies update behavior is denied. The attempt will be cancelled."); context.Reporter.Error() << Resource::String::UpgradeBlockedByManifest << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_UPGRADE_NOT_SUPPORTED); } context << Workflow::EnsureRunningAsAdminForMachineScopeInstall << Workflow::EnsureSupportForPortableInstall << Workflow::EnsureValidNestedInstallerMetadataForArchiveInstall; } ProcessMultiplePackages::ProcessMultiplePackages( StringResource::StringId dependenciesReportMessage, HRESULT resultOnFailure, Flags flags, std::vector&& ignorableInstallResults) : WorkflowTask("ProcessMultiplePackages"), m_dependenciesReportMessage(dependenciesReportMessage), m_resultOnFailure(resultOnFailure), m_ignorableInstallResults(std::move(ignorableInstallResults)) { // Inverted m_ensurePackageAgreements = !WI_IsFlagSet(flags, Flags::SkipPackageAgreements); m_ignorePackageDependencies = WI_IsFlagSet(flags, Flags::IgnoreDependencies); m_stopOnFailure = WI_IsFlagSet(flags, Flags::StopOnFailure); m_refreshPathVariable = WI_IsFlagSet(flags, Flags::RefreshPathVariable); m_downloadOnly = WI_IsFlagSet(flags, Flags::DownloadOnly); m_dependenciesOnly = WI_IsFlagSet(flags, Flags::DependenciesOnly); } void ProcessMultiplePackages::operator()(Execution::Context& context) const { if (!context.Contains(Execution::Data::PackageSubContexts)) { return; } bool downloadInstallerOnly = m_downloadOnly ? true : WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly); // Show all prompts needed for every package before installing anything context << Workflow::ShowPromptsForMultiplePackages(m_ensurePackageAgreements, downloadInstallerOnly); if (context.IsTerminated()) { return; } auto& packageSubContexts = context.Get(); // Report dependencies if (!m_ignorePackageDependencies) { DependencyList allDependencies; for (auto& packageContext : packageSubContexts) { allDependencies.Add(packageContext->Get().value().Dependencies); } if (!allDependencies.Empty()) { if (downloadInstallerOnly) { context.Reporter.Info() << Resource::String::DependenciesFlowDownload << std::endl; } else { context.Reporter.Info() << Resource::String::DependenciesFlowInstall << std::endl; } } context.Add(allDependencies); context << Workflow::ReportDependencies(m_dependenciesReportMessage); } bool allSucceeded = true; size_t packagesCount = packageSubContexts.size(); size_t packagesProgress = 0; if (m_dependenciesOnly) { context.Reporter.Info() << Resource::String::DependenciesOnlyMessage << std::endl; } for (auto& packageContext : packageSubContexts) { packagesProgress++; context.Reporter.Info() << '(' << packagesProgress << '/' << packagesCount << ") "_liv; // We want to do best effort to install all packages regardless of previous failures Execution::Context& currentContext = *packageContext; auto previousThreadGlobals = currentContext.SetForCurrentThread(); currentContext << Workflow::ReportIdentityAndInstallationDisclaimer; // Prevent individual exceptions from breaking out of the loop try { // Handle dependencies if requested. if (!m_ignorePackageDependencies && !downloadInstallerOnly) { currentContext << Workflow::EnableWindowsFeaturesDependencies << Workflow::CreateDependencySubContexts(m_dependenciesReportMessage) << Workflow::ProcessMultiplePackages(m_dependenciesReportMessage, APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES, Flags::IgnoreDependencies | Flags::StopOnFailure | Flags::RefreshPathVariable); } if (!m_dependenciesOnly) { currentContext << Workflow::DownloadInstaller; if (!downloadInstallerOnly) { currentContext << Workflow::InstallPackageInstaller; } } } catch (...) { currentContext.SetTerminationHR(Workflow::HandleException(currentContext, std::current_exception())); } if (m_refreshPathVariable) { if (RefreshPathVariableForCurrentProcess()) { AICLI_LOG(CLI, Info, << "Successfully refreshed process PATH environment variable."); } else { AICLI_LOG(CLI, Warning, << "Failed to refresh process PATH environment variable."); context.Reporter.Warn() << Resource::String::FailedToRefreshPathWarning << std::endl; } } currentContext.Reporter.Info() << std::endl; if (currentContext.IsTerminated()) { if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) { // This means that the subcontext being terminated is due to an overall abort context.Reporter.Info() << Resource::String::Cancelled << std::endl; return; } if (m_ignorableInstallResults.end() == std::find(m_ignorableInstallResults.begin(), m_ignorableInstallResults.end(), currentContext.GetTerminationHR())) { allSucceeded = false; if (m_stopOnFailure) { break; } } } } if (!allSucceeded) { AICLI_TERMINATE_CONTEXT(m_resultOnFailure); } } void SnapshotARPEntries(Execution::Context& context) try { // Ensure that installer type might actually write to ARP, otherwise this is a waste of time auto installer = context.Get(); if (installer && MightWriteToARP(installer->EffectiveInstallerType())) { Repository::Correlation::ARPCorrelationData data; data.CapturePreInstallSnapshot(); context.Add(std::move(data)); } } CATCH_LOG() void ReportARPChanges(Execution::Context& context) try { if (!context.Contains(Execution::Data::ARPCorrelationData)) { return; } // If the installer claims to have a PackageFamilyName, and that family name is currently registered for the user, // let that be the correlated item and skip any attempt at further ARP correlation. const auto& installer = context.Get(); if (installer && !installer->PackageFamilyName.empty() && Deployment::IsRegistered(installer->PackageFamilyName)) { return; } const auto& manifest = context.Get(); auto& arpCorrelationData = context.Get(); arpCorrelationData.CapturePostInstallSnapshot(); auto correlationResult = arpCorrelationData.CorrelateForNewlyInstalled(manifest); // Store the ARP entry found to match the package to record it in the tracking catalog later if (correlationResult.Package) { std::vector entries; auto metadata = correlationResult.Package->GetMetadata(); AppsAndFeaturesEntry baseEntry; // Display name and publisher are also available as multi properties, but // for ARP there will always be only 0 or 1 values. baseEntry.DisplayName = correlationResult.Package->GetProperty(PackageVersionProperty::Name).get(); baseEntry.Publisher = correlationResult.Package->GetProperty(PackageVersionProperty::Publisher).get(); baseEntry.DisplayVersion = correlationResult.Package->GetProperty(PackageVersionProperty::Version).get(); baseEntry.InstallerType = Manifest::ConvertToInstallerTypeEnum(metadata[PackageVersionMetadata::InstalledType]); auto productCodes = correlationResult.Package->GetMultiProperty(PackageVersionMultiProperty::ProductCode); for (auto&& productCode : productCodes) { AppsAndFeaturesEntry entry = baseEntry; entry.ProductCode = std::move(productCode).get(); entries.push_back(std::move(entry)); } auto upgradeCodes = correlationResult.Package->GetMultiProperty(PackageVersionMultiProperty::UpgradeCode); for (auto&& upgradeCode : upgradeCodes) { AppsAndFeaturesEntry entry = baseEntry; entry.UpgradeCode = std::move(upgradeCode).get(); entries.push_back(std::move(entry)); } context.Add(std::move(entries)); } // We can only get the source identifier from an active source std::string sourceIdentifier; if (context.Contains(Execution::Data::PackageVersion)) { sourceIdentifier = context.Get()->GetProperty(PackageVersionProperty::SourceIdentifier); } IPackageVersion::Metadata arpEntryMetadata; if (correlationResult.Package) { arpEntryMetadata = correlationResult.Package->GetMetadata(); } Logging::Telemetry().LogSuccessfulInstallARPChange( sourceIdentifier, manifest.Id, manifest.Version, manifest.Channel, correlationResult.ChangesToARP, correlationResult.MatchesInARP, correlationResult.CountOfIntersectionOfChangesAndMatches, correlationResult.Package ? static_cast(correlationResult.Package->GetProperty(PackageVersionProperty::Name)) : "", correlationResult.Package ? static_cast(correlationResult.Package->GetProperty(PackageVersionProperty::Version)) : "", correlationResult.Package ? static_cast(correlationResult.Package->GetProperty(PackageVersionProperty::Publisher)) : "", correlationResult.Package ? static_cast(arpEntryMetadata[PackageVersionMetadata::InstalledLocale]) : "" ); } CATCH_LOG(); void RecordInstall(Context& context) { // Local manifest installs won't have a package version, and tracking them doesn't provide much // value currently. If we ever do use our own database as a primary source of packages that we // maintain, this decision will probably have to be reconsidered. if (!context.Contains(Data::PackageVersion)) { return; } auto manifest = context.Get(); // If we have determined an ARP entry matches the installed package, // we set its product code in the manifest we record to ensure we can // find it in the future. // Note that this may overwrite existing information. if (context.Contains(Data::CorrelatedAppsAndFeaturesEntries)) { // Use a new Installer entry manifest.Installers.emplace_back(); manifest.Installers.back().AppsAndFeaturesEntries = context.Get(); } auto trackingCatalog = context.Get()->GetSource().GetTrackingCatalog(); auto version = trackingCatalog.RecordInstall( manifest, context.Get().value(), WI_IsFlagSet(context.GetFlags(), ContextFlag::InstallerExecutionUseUpdate)); // Record user intent values. Command args takes precedence. Then previous user intent values. Repository::IPackageVersion::Metadata installedMetadata; if (context.Contains(Data::InstalledPackageVersion) && context.Get()) { installedMetadata = context.Get()->GetMetadata(); } if (context.Args.Contains(Execution::Args::Type::InstallArchitecture)) { version.SetMetadata(Repository::PackageVersionMetadata::UserIntentArchitecture, context.Args.GetArg(Execution::Args::Type::InstallArchitecture)); } else { auto itr = installedMetadata.find(Repository::PackageVersionMetadata::UserIntentArchitecture); if (itr != installedMetadata.end()) { version.SetMetadata(Repository::PackageVersionMetadata::UserIntentArchitecture, itr->second); } } if (context.Args.Contains(Execution::Args::Type::Locale)) { version.SetMetadata(Repository::PackageVersionMetadata::UserIntentLocale, context.Args.GetArg(Execution::Args::Type::Locale)); } else { auto itr = installedMetadata.find(Repository::PackageVersionMetadata::UserIntentLocale); if (itr != installedMetadata.end()) { version.SetMetadata(Repository::PackageVersionMetadata::UserIntentLocale, itr->second); } } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/InstallFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" #include namespace AppInstaller::CLI::Workflow { using namespace std::string_view_literals; // Token specified in installer args will be replaced by proper value. static constexpr std::string_view ARG_TOKEN_LOGPATH = ""sv; static constexpr std::string_view ARG_TOKEN_INSTALLPATH = ""sv; // Determines if an installer type is allowed to install/uninstall in parallel. bool ExemptFromSingleInstallLocking(AppInstaller::Manifest::InstallerTypeEnum type); namespace details { // These single type install flows should remain "internal" and only ExecuteInstallerForType should be used externally // so that all installs can properly handle single install locking. // Runs the installer via ShellExecute. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void ShellExecuteInstall(Execution::Context& context); // Runs an MSI installer directly via MSI APIs. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void DirectMSIInstall(Execution::Context& context); // Deploys the MSIX. // Required Args: None // Inputs: Manifest?, Installer || InstallerPath // Outputs: None void MsixInstall(Execution::Context& context); // Runs the flow for installing a Portable package. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void PortableInstall(Execution::Context& context); // Runs the flow for installing a package from an archive. // Required Args: None // Inputs: Installer, InstallerPath, Manifest // Outputs: None void ArchiveInstall(Execution::Context& context); // Runs the flow for installing a font package. // Required Args: None // Inputs: Installer, InstallerPath, Manifest // Outputs: None void FontInstall(Execution::Context& context); } // Ensures that there is an applicable installer. // Required Args: None // Inputs: Installer // Outputs: None void EnsureApplicableInstaller(Execution::Context& context); // Shows the installation disclaimer. // Required Args: None // Inputs: None // Outputs: None void ShowInstallationDisclaimer(Execution::Context& context); // Displays the installations notes after a successful install. // Required Args: None // Inputs: InstallationNotes // Outputs: None void DisplayInstallationNotes(Execution::Context& context); // Checks if there are any included arguments that are not supported for the package. // Required Args: None // Inputs: Installer // Outputs: None void CheckForUnsupportedArgs(Execution::Context& context); // Admin is required for machine scope install for installer types like portable, msix and msstore. // Required Args: None // Inputs: Installer // Outputs: None void EnsureRunningAsAdminForMachineScopeInstall(Execution::Context& context); // Composite flow that chooses what to do based on the installer type. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None void ExecuteInstaller(Execution::Context& context); // Composite flow that chooses what to do based on the installer type. // Required Args: None // Inputs: Installer, InstallerPath // Outputs: None struct ExecuteInstallerForType : public WorkflowTask { ExecuteInstallerForType(Manifest::InstallerTypeEnum installerType) : WorkflowTask("ExecuteInstallerForType"), m_installerType(installerType) {} void operator()(Execution::Context& context) const override; private: Manifest::InstallerTypeEnum m_installerType; }; // Verifies parameters for install to ensure success. // Required Args: None // Inputs: // Outputs: None void EnsureSupportForInstall(Execution::Context& context); // Reports the return code returned by the installer. // Required Args: None // Inputs: Manifest, Installer, InstallerResult // Outputs: None struct ReportInstallerResult : public WorkflowTask { ReportInstallerResult(std::string_view installerType, HRESULT hr, bool isHResult = false) : WorkflowTask("ReportInstallerResult"), m_installerType(installerType), m_hr(hr), m_isHResult(isHResult) {} void operator()(Execution::Context& context) const override; private: // Installer type used when reporting failures. std::string_view m_installerType; // Result to return if the installer failed. HRESULT m_hr; // Whether the installer result is an HRESULT. This guides how we show it. bool m_isHResult; }; // Reports manifest identity and shows installation disclaimer // Required Args: None // Inputs: Manifest // Outputs: None void ReportIdentityAndInstallationDisclaimer(Execution::Context& context); // Installs a specific package installer. See also InstallSinglePackage & ProcessMultiplePackages // Required Args: None // Inputs: InstallerPath, Manifest, Installer, PackageVersion, InstalledPackageVersion? // Outputs: None void InstallPackageInstaller(Execution::Context& context); // Installs the dependencies for a specific package. CreateDependencySubContexts should have been called before this task. // Required Args: None // Inputs: InstallerPath, Manifest, Installer, PackageVersion, InstalledPackageVersion? // Outputs: None void InstallDependencies(Execution::Context& context); // Downloads all of the package dependencies of a specific package. Only used in the 'winget download' and COM download flows. // Required Args: none // Inputs: Manifest, Installer // Outputs: None void DownloadPackageDependencies(Execution::Context& context); // Installs a single package. This also does the reporting, user interaction, and installer download // for single-package installation. // RequiredArgs: None // Inputs: Manifest, Installer, PackageVersion, InstalledPackageVersion? // Outputs: None void InstallSinglePackage(Execution::Context& context); // Processes multiple packages by handling download and/or install. This also does the reporting and user interaction needed. // Required Args: None // Inputs: PackageSubContexts // Outputs: None struct ProcessMultiplePackages : public WorkflowTask { // Flags to signal change from default behavior of the task. enum class Flags : uint32_t { None = 0x00, SkipPackageAgreements = 0x01, IgnoreDependencies = 0x02, StopOnFailure = 0x04, RefreshPathVariable = 0x08, DownloadOnly = 0x10, DependenciesOnly = 0x20, }; ProcessMultiplePackages( StringResource::StringId dependenciesReportMessage, HRESULT resultOnFailure, Flags flags = Flags::None, std::vector&& ignorableInstallResults = {}); void operator()(Execution::Context& context) const override; private: HRESULT m_resultOnFailure; std::vector m_ignorableInstallResults; StringResource::StringId m_dependenciesReportMessage; bool m_ignorePackageDependencies; bool m_ensurePackageAgreements; bool m_stopOnFailure; bool m_refreshPathVariable; bool m_downloadOnly; bool m_dependenciesOnly; }; DEFINE_ENUM_FLAG_OPERATORS(ProcessMultiplePackages::Flags); // Stores the existing set of packages in ARP. // Required Args: None // Inputs: Installer // Outputs: ARPSnapshot void SnapshotARPEntries(Execution::Context& context); // Reports on the changes between the stored ARPSnapshot and the current values, // and stores the product code of the ARP entry found for the package. // Required Args: None // Inputs: ARPSnapshot?, Manifest, PackageVersion // Outputs: CorrelatedAppsAndFeaturesEntries? void ReportARPChanges(Execution::Context& context); // Records the installation to the tracking catalog. // Required Args: None // Inputs: PackageVersion?, Manifest, Installer, CorrelatedAppsAndFeaturesEntries? // Outputs: None void RecordInstall(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "MSStoreInstallerHandler.h" #include "WorkflowBase.h" #include #include #include #include #include #include #include namespace AppInstaller::CLI::Workflow { void DownloadInstallerFile(Execution::Context& context); } namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::MSStore; using namespace AppInstaller::SelfManagement; using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::ApplicationModel::Store::Preview::InstallControl; namespace { Utility::LocIndString GetErrorCodeString(const HRESULT errorCode) { std::ostringstream ssError; ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); return Utility::LocIndString{ ssError.str() }; } HRESULT EnsureStorePolicySatisfiedImpl(const std::wstring& productId, bool bypassPolicy) { constexpr std::wstring_view s_StoreClientName = L"Microsoft.WindowsStore"sv; constexpr std::wstring_view s_StoreClientPublisher = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"sv; // Policy check AppInstallManager installManager; if (!bypassPolicy && installManager.IsStoreBlockedByPolicyAsync(s_StoreClientName, s_StoreClientPublisher).get()) { AICLI_LOG(CLI, Error, << "Store client is blocked by policy. MSStore execution failed."); return APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY; } if (!installManager.GetIsAppAllowedToInstallAsync(productId).get()) { AICLI_LOG(CLI, Error, << "App is blocked by policy. MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId)); return APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY; } return S_OK; } void AppInstallerUpdate(bool preferStub, bool bypassPolicy, Execution::Context& context) { auto appInstId = std::wstring{ s_AppInstallerProductId }; THROW_IF_FAILED(EnsureStorePolicySatisfiedImpl(appInstId, bypassPolicy)); SetStubPreferred(preferStub); auto installOperation = MSStoreOperation(MSStoreOperationType::Update, appInstId, Manifest::ScopeEnum::User, true, true); HRESULT hr = S_OK; context.Reporter.ExecuteWithProgress( [&](IProgressCallback& progress) { hr = installOperation.StartAndWaitForOperation(progress); }); THROW_IF_FAILED(hr); } HRESULT DownloadMSStorePackageFile(const MSStore::MSStoreDownloadFile& downloadFile, const std::filesystem::path& downloadDirectory, Execution::Context& context) { try { // Create a sub context to execute the package download auto subContextPtr = context.CreateSubContext(); Execution::Context& subContext = *subContextPtr; auto previousThreadGlobals = subContext.SetForCurrentThread(); // Populate Installer and temp download path for sub context Manifest::ManifestInstaller installer; installer.Url = downloadFile.Url; installer.Sha256 = downloadFile.Sha256; subContext.Add(std::move(installer)); auto tempInstallerPath = Runtime::GetPathTo(Runtime::PathName::Temp); tempInstallerPath /= Utility::SHA256::ConvertToString(downloadFile.Sha256); AICLI_LOG(CLI, Info, << "Generated temp download path: " << tempInstallerPath); subContext.Add(tempInstallerPath); subContext << Workflow::DownloadInstallerFile; if (subContext.IsTerminated()) { RETURN_HR(subContext.GetTerminationHR()); } // Verify hash const auto& hashPair = subContext.Get(); if (std::equal(hashPair.first.begin(), hashPair.first.end(), hashPair.second.Sha256Hash.begin())) { AICLI_LOG(CLI, Info, << "Microsoft Store package hash verified"); subContext.Reporter.Info() << Resource::String::MSStoreDownloadPackageHashVerified << std::endl; // Trust direct download from Store if hash matched Utility::ApplyMotwIfApplicable(tempInstallerPath, URLZONE_TRUSTED); } else { if (!subContext.Args.Contains(Execution::Args::Type::HashOverride)) { AICLI_LOG(CLI, Error, << "Microsoft Store package hash mismatch"); subContext.Reporter.Error() << Resource::String::MSStoreDownloadPackageHashMismatch << std::endl; RETURN_HR(APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH); } else { AICLI_LOG(CLI, Warning, << "Microsoft Store package hash mismatch, but overridden."); subContext.Reporter.Warn() << Resource::String::MSStoreDownloadPackageHashMismatch << std::endl; } } auto renamedDownloadedPackage = downloadDirectory / Utility::ConvertToUTF16(downloadFile.FileName); Filesystem::RenameFile(tempInstallerPath, renamedDownloadedPackage); subContext.Reporter.Info() << Resource::String::MSStoreDownloadPackageDownloaded(Utility::LocIndView{ renamedDownloadedPackage.u8string() }) << std::endl; return S_OK; } catch (...) { AICLI_LOG(CLI, Error, << "Microsoft Store package download failed. File: " << downloadFile.FileName); context.Reporter.Error() << Resource::String::MSStoreDownloadPackageDownloadFailed(Utility::LocIndView{ downloadFile.FileName }) << std::endl; RETURN_HR(APPINSTALLER_CLI_ERROR_DOWNLOAD_FAILED); } } } void MSStoreInstall(Execution::Context& context) { auto productId = Utility::ConvertToUTF16(context.Get()->ProductId); auto scope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); bool isSilentMode = context.Args.Contains(Execution::Args::Type::Silent); bool force = context.Args.Contains(Execution::Args::Type::Force); auto installOperation = MSStoreOperation(MSStoreOperationType::Install, productId, scope, isSilentMode, force); context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; HRESULT hr = S_OK; context.Reporter.ExecuteWithProgress( [&](IProgressCallback& progress) { hr = installOperation.StartAndWaitForOperation(progress); }); if (SUCCEEDED(hr)) { context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; } else { if (hr == APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED) { context.Reporter.Error() << Resource::String::InstallFlowReturnCodeSystemNotSupported << std::endl; context.Add(static_cast(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED)); } else { auto errorCodeString = GetErrorCodeString(hr); context.Reporter.Error() << Resource::String::MSStoreInstallOrUpdateFailed(errorCodeString) << std::endl; context.Add(hr); AICLI_LOG(CLI, Error, << "MSStore install failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << errorCodeString); } AICLI_TERMINATE_CONTEXT(hr); } } void MSStoreUpdate(Execution::Context& context) { bool isSilentMode = context.Args.Contains(Execution::Args::Type::Silent); auto productId = Utility::ConvertToUTF16(context.Get()->ProductId); auto scope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); bool force = context.Args.Contains(Execution::Args::Type::Force); auto installOperation = MSStoreOperation(MSStoreOperationType::Update, productId, scope, isSilentMode, force); context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; HRESULT hr = S_OK; context.Reporter.ExecuteWithProgress( [&](IProgressCallback& progress) { hr = installOperation.StartAndWaitForOperation(progress); }); if (SUCCEEDED(hr)) { context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; } else { if (hr == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE) { context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl << Resource::String::UpdateNotApplicableReason << std::endl; } else { auto errorCodeString = GetErrorCodeString(hr); context.Reporter.Error() << Resource::String::MSStoreInstallOrUpdateFailed(errorCodeString) << std::endl; context.Add(hr); AICLI_LOG(CLI, Error, << "MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << errorCodeString); } AICLI_TERMINATE_CONTEXT(hr); } } void MSStoreRepair(Execution::Context& context) { auto productId = Utility::ConvertToUTF16(context.Get()->ProductId); auto scope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); bool isSilentMode = context.Args.Contains(Execution::Args::Type::Silent); bool force = context.Args.Contains(Execution::Args::Type::Force); auto repairOperation = MSStoreOperation(MSStoreOperationType::Repair, productId, scope, isSilentMode, force); context.Reporter.Info() << Resource::String::RepairFlowStartingPackageRepair << std::endl; HRESULT hr = S_OK; context.Reporter.ExecuteWithProgress( [&](IProgressCallback& progress) { hr = repairOperation.StartAndWaitForOperation(progress); }); if (SUCCEEDED(hr)) { context.Reporter.Info() << Resource::String::RepairFlowRepairSuccess << std::endl; } else { if (hr == APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED) { context.Reporter.Error() << Resource::String::InstallFlowReturnCodeSystemNotSupported << std::endl; context.Add(static_cast(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED)); } else { auto errorCodeString = GetErrorCodeString(hr); context.Reporter.Error() << Resource::String::MSStoreRepairFailed(errorCodeString) << std::endl; context.Add(hr); AICLI_LOG(CLI, Error, << "MSStore repair failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << errorCodeString); } AICLI_TERMINATE_CONTEXT(hr); } } void MSStoreDownload(Execution::Context& context) { if (context.Args.Contains(Execution::Args::Type::Rename)) { context.Reporter.Warn() << Resource::String::MSStoreDownloadRenameNotSupported << std::endl; } // Authentication notice context.Reporter.Warn() << Resource::String::MSStoreDownloadAuthenticationNotice << std::endl; context.Reporter.Warn() << Resource::String::MSStoreDownloadMultiplePackagesNotice << std::endl; const auto& installer = context.Get().value(); Utility::Architecture requiredArchitecture = Utility::Architecture::Unknown; Manifest::PlatformEnum requiredPlatform = Manifest::PlatformEnum::Unknown; std::string requiredLocale; if (context.Args.Contains(Execution::Args::Type::InstallerArchitecture)) { requiredArchitecture = Utility::ConvertToArchitectureEnum(context.Args.GetArg(Execution::Args::Type::InstallerArchitecture)); } if (context.Args.Contains(Execution::Args::Type::Platform)) { requiredPlatform = Manifest::ConvertToPlatformEnumForMSStoreDownload(context.Args.GetArg(Execution::Args::Type::Platform)); } if (context.Args.Contains(Execution::Args::Type::Locale)) { requiredLocale = context.Args.GetArg(Execution::Args::Type::Locale); } MSStoreDownloadContext downloadContext{ installer.ProductId, requiredArchitecture, requiredPlatform, requiredLocale, GetAuthenticationArguments(context) }; if (context.Args.Contains(Execution::Args::Type::OSVersion)) { Utility::UInt64Version targetOSVersion{ std::string{ context.Args.GetArg(Execution::Args::Type::OSVersion) } }; downloadContext.TargetOSVersion(std::move(targetOSVersion)); } MSStoreDownloadInfo downloadInfo; try { context.Reporter.Info() << Resource::String::MSStoreDownloadGetDownloadInfo << std::endl; downloadInfo = downloadContext.GetDownloadInfo(); } catch (const wil::ResultException& re) { AICLI_LOG(CLI, Error, << "Getting MSStore package download info failed. Error code: " << re.GetErrorCode()); switch (re.GetErrorCode()) { case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE: case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE: context.Reporter.Error() << Resource::String::MSStoreDownloadNoApplicablePackageFound << std::endl; break; case APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED: context.Reporter.Error() << Resource::String::MSStoreDownloadPackageDownloadNotSupported << std::endl; break; default: context.Reporter.Error() << Resource::String::MSStoreDownloadGetDownloadInfoFailed << std::endl; } AICLI_TERMINATE_CONTEXT(re.GetErrorCode()); } bool skipDependencies = context.Args.Contains(Execution::Args::Type::SkipDependencies); // Prepare directories std::filesystem::path downloadDirectory = context.Get(); std::filesystem::path dependenciesDirectory = downloadDirectory / L"Dependencies"; // Create directories if needed. auto directoryToCreate = (skipDependencies || downloadInfo.DependencyPackages.empty()) ? downloadDirectory : dependenciesDirectory; if (!std::filesystem::exists(directoryToCreate)) { std::filesystem::create_directories(directoryToCreate); } else { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE), !std::filesystem::is_directory(directoryToCreate)); } // Download dependency packages if (!skipDependencies) { AICLI_LOG(CLI, Info, << "Downloading MSStore dependency packages"); context.Reporter.Info() << Resource::String::MSStoreDownloadDependencyPackages << std::endl; for (auto const& dependencyPackage : downloadInfo.DependencyPackages) { THROW_IF_FAILED(DownloadMSStorePackageFile(dependencyPackage, dependenciesDirectory, context)); } } // Download main packages AICLI_LOG(CLI, Info, << "Downloading MSStore main packages"); context.Reporter.Info() << Resource::String::MSStoreDownloadMainPackages << std::endl; for (auto const& mainPackage : downloadInfo.MainPackages) { THROW_IF_FAILED(DownloadMSStorePackageFile(mainPackage, downloadDirectory, context)); } context.Reporter.Info() << Resource::String::MSStoreDownloadPackageDownloadSuccess << std::endl; // Get license if (!context.Args.Contains(Execution::Args::Type::SkipMicrosoftStorePackageLicense)) { AICLI_LOG(CLI, Info, << "Getting MSStore package license"); context.Reporter.Info() << Resource::String::MSStoreDownloadGetLicense << std::endl; std::vector licenseContent; try { licenseContent = downloadContext.GetLicense(downloadInfo.ContentId); } catch (const wil::ResultException& re) { if (re.GetErrorCode() == APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN) { AICLI_LOG(CLI, Warning, << "Getting MSStore package license failed. The Microsoft Entra Id account does not have privilege."); context.Reporter.Warn() << Resource::String::MSStoreDownloadGetLicenseForbidden << std::endl; } else { AICLI_LOG(CLI, Warning, << "Getting MSStore package license failed. Error code: " << re.GetErrorCode()); context.Reporter.Warn() << Resource::String::MSStoreDownloadGetLicenseFailed << std::endl; } AICLI_TERMINATE_CONTEXT(re.GetErrorCode()); } std::filesystem::path licenseFilePath = downloadDirectory / Utility::ConvertToUTF16(installer.ProductId + "_License.xml"); std::ofstream licenseFile(licenseFilePath, std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); licenseFile.write((const char *)&licenseContent[0], licenseContent.size()); licenseFile.flush(); licenseFile.close(); AICLI_LOG(CLI, Info, << "Getting MSStore package license success"); context.Reporter.Info() << Resource::String::MSStoreDownloadGetLicenseSuccess(Utility::LocIndView{ licenseFilePath.u8string() }) << std::endl; } } void EnsureStorePolicySatisfied(Execution::Context& context) { auto productId = Utility::ConvertToUTF16(context.Get()->ProductId); bool bypassStorePolicy = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::BypassIsStoreClientBlockedPolicyCheck); HRESULT hr = EnsureStorePolicySatisfiedImpl(productId, bypassStorePolicy); if (FAILED(hr)) { if (hr == APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY) { context.Reporter.Error() << Resource::String::MSStoreStoreClientBlocked << std::endl; } else if (hr == APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY) { context.Reporter.Error() << Resource::String::MSStoreAppBlocked << std::endl; } AICLI_TERMINATE_CONTEXT(hr); } } void VerifyIsFullPackage(Execution::Context& context) { if (IsStubPackage()) { context.Reporter.Error() << Resource::String::ExtendedFeaturesNotEnabledMessage << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); } } void EnableExtendedFeatures(Execution::Context& context) { #ifndef AICLI_DISABLE_TEST_HOOKS AppInstallerUpdate(false, true, context); #else if (IsStubPackage()) { context.Reporter.Info() << Resource::String::ExtendedFeaturesEnablingMessage << std::endl; bool bypassStorePolicy = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::BypassIsStoreClientBlockedPolicyCheck); AppInstallerUpdate(false, bypassStorePolicy, context); } else { context.Reporter.Info() << Resource::String::ExtendedFeaturesEnabledMessage << std::endl; } #endif } void DisableExtendedFeatures(Execution::Context& context) { #ifndef AICLI_DISABLE_TEST_HOOKS AppInstallerUpdate(true, true, context); #else if (!IsStubPackage()) { context.Reporter.Info() << Resource::String::ExtendedFeaturesDisablingMessage << std::endl; bool bypassStorePolicy = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::BypassIsStoreClientBlockedPolicyCheck); AppInstallerUpdate(true, bypassStorePolicy, context); } else { context.Reporter.Info() << Resource::String::ExtendedFeaturesDisabledMessage << std::endl; } #endif } } ================================================ FILE: src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" // MSStoreInstallerHandler handles msstore installers. namespace AppInstaller::CLI::Workflow { // Deploys the Store app. // Required Args: None // Inputs: Installer // Outputs: None void MSStoreInstall(Execution::Context& context); // Updates the Store app if applicable. // Required Args: None // Inputs: Installer // Outputs: None void MSStoreUpdate(Execution::Context& context); // Attempt to repair the installation of an Store app that is already installed // Required Args: None // Inputs: Installer // Outputs: None void MSStoreRepair(Execution::Context& context); // Downloads the Store app installer. // Required Args: None // Inputs: Installer // Outputs: None void MSStoreDownload(Execution::Context& context); // Ensure the Store app is not blocked by policy. // Required Args: None // Inputs: Installer // Outputs: None void EnsureStorePolicySatisfied(Execution::Context& context); // Verifies the full package is installed. // Required Args: None // Inputs: None // Outputs: None void VerifyIsFullPackage(Execution::Context& context); // Change stub preference to full and installs full package if needed. // Required Args: None // Inputs: None // Outputs: None void EnableExtendedFeatures(Execution::Context& context); // Change stub preference to stub and installs stub package if needed. // Required Args: None // Inputs: None // Outputs: None void DisableExtendedFeatures(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "MsiInstallFlow.h" #include #include namespace AppInstaller::CLI::Workflow { namespace { std::optional InvokeMsiInstallProduct(const std::filesystem::path& installerPath, const Msi::MsiParsedArguments& msiArgs, IProgressCallback&) { if (msiArgs.LogFile) { THROW_IF_WIN32_ERROR(MsiEnableLogW(msiArgs.LogMode, msiArgs.LogFile->c_str(), msiArgs.LogAttributes)); } else { // Disable logging THROW_IF_WIN32_ERROR(MsiEnableLogW(0, nullptr, 0)); } // Returns old UI level. We don't need to reset it so we ignore it. MsiSetInternalUI(msiArgs.UILevel, nullptr); // TODO: Use progress callback return MsiInstallProductW(installerPath.c_str(), msiArgs.Properties.c_str()); } } void DirectMSIInstallImpl(Execution::Context& context) { context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; const auto& installer = context.Get(); const std::filesystem::path& installerPath = context.Get(); Msi::MsiParsedArguments parsedArgs = Msi::ParseMSIArguments(context.Get()); // Inform of elevation requirements if (!Runtime::IsRunningAsAdmin() && installer->ElevationRequirement == Manifest::ElevationRequirementEnum::ElevatesSelf) { context.Reporter.Warn() << Resource::String::InstallerElevationExpected << std::endl; } auto installResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeMsiInstallProduct, installerPath, parsedArgs, std::placeholders::_1)); if (!installResult) { context.Reporter.Warn() << Resource::String::InstallAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } else { context.Add(installResult.value()); } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/MsiInstallFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Ensures that there is an applicable installer. // Required Args: None // Inputs: InstallerArgs, Installer, InstallerPath, Manifest // Outputs: OperationReturnCode void DirectMSIInstallImpl(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/MultiQueryFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "MultiQueryFlow.h" #include "UpdateFlow.h" using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Workflow { namespace { Utility::LocIndString GetPackageStringFromSearchRequest(const SearchRequest& searchRequest) { if (searchRequest.Query) { return Utility::LocIndString{ searchRequest.Query->Value }; } if (!searchRequest.Inclusions.empty()) { return Utility::LocIndString{ searchRequest.Inclusions[0].Value }; } if (!searchRequest.Filters.empty()) { return Utility::LocIndString{ searchRequest.Filters[0].Value }; } return ""_lis; } } void GetMultiSearchRequests(Execution::Context& context) { std::vector> packageSubContexts; auto& source = context.Get(); for (const auto& query : *context.Args.GetArgs(Execution::Args::Type::MultiQuery)) { auto searchContextPtr = context.CreateSubContext(); Execution::Context& searchContext = *searchContextPtr; auto previousThreadGlobals = searchContext.SetForCurrentThread(); searchContext.Add(source); searchContext.Args.AddArg(Execution::Args::Type::Query, query); AICLI_LOG(CLI, Info, << "Creating search query for package [" << query << "]"); searchContext << GetSearchRequestForSingle; packageSubContexts.emplace_back(std::move(searchContextPtr)); } context.Add(std::move(packageSubContexts)); } void SearchSubContextsForSingle::operator()(Execution::Context& context) const { std::vector> packageSubContexts; bool foundAll = true; for (auto& searchContextPtr : context.Get()) { auto& searchContext = *searchContextPtr; SearchRequest searchRequest = searchContext.Get(); searchContext.Add(searchContext.Get().Search(searchRequest)); switch (m_operationType) { case OperationType::Install: case OperationType::Upgrade: searchContext << Workflow::SelectSinglePackageVersionForInstallOrUpgrade(m_operationType); break; case OperationType::Uninstall: searchContext << Workflow::HandleSearchResultFailures << Workflow::EnsureOneMatchFromSearchResult(m_operationType); break; default: THROW_HR(E_UNEXPECTED); } if (searchContext.IsTerminated()) { if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) { // This means that the subcontext being terminated is due to an overall abort context.Reporter.Info() << Resource::String::Cancelled << std::endl; return; } else { // We already reported the error from the sub-context, but we repeat it here because // for multi-queries we can a bit more verbose as the queries here are easier to report. auto packageString = GetPackageStringFromSearchRequest(searchRequest); auto searchTerminationHR = searchContext.GetTerminationHR(); if (searchTerminationHR == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE || searchTerminationHR == APPINSTALLER_CLI_ERROR_PACKAGE_ALREADY_INSTALLED) { AICLI_LOG(CLI, Info, << "Package is already installed: [" << packageString << "]"); context.Reporter.Info() << Resource::String::MultiQueryPackageAlreadyInstalled(packageString) << std::endl; continue; } else { if (searchTerminationHR == APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND) { AICLI_LOG(CLI, Info, << "Package not found for query: [" << packageString << "]"); context.Reporter.Warn() << Resource::String::MultiQueryPackageNotFound(packageString) << std::endl; } else if (searchTerminationHR == APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND) { AICLI_LOG(CLI, Info, << "Multiple packages found for query: [" << packageString << "]"); context.Reporter.Warn() << Resource::String::MultiQuerySearchFoundMultiple(packageString) << std::endl; } else { AICLI_LOG(CLI, Info, << "Search failed for query: [" << packageString << "]"); context.Reporter.Info() << Resource::String::MultiQuerySearchFailed(packageString) << std::endl; } // Keep searching for the remaining packages and only fail at the end. foundAll = false; continue; } } } packageSubContexts.emplace_back(std::move(searchContextPtr)); } if (!foundAll) { AICLI_LOG(CLI, Info, << "Not all queries returned one result"); if (context.Args.Contains(Execution::Args::Type::IgnoreUnavailable)) { AICLI_LOG(CLI, Info, << "Ignoring unavailable packages due to command line argument"); } else { AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE); } } context.Add(std::move(packageSubContexts)); } } ================================================ FILE: src/AppInstallerCLICore/Workflows/MultiQueryFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" // Workflow tasks related to dealing with multiple package queries at once. namespace AppInstaller::CLI::Workflow { // Gets the search requests for multiple queries from the command line. // Required Args: None // Inputs: Source // Outputs: PackageSubContexts // SubContext Inputs: None // SubContext Outputs: Source, SearchRequest void GetMultiSearchRequests(Execution::Context& context); // Performs searches on each of the sub-contexts with the semantics of targeting a single package for each one. // Required Args: a value indicating the purpose of the search // Inputs: PackageSubContexts // Outputs: None // SubContext Inputs: Source, SearchRequest // SubContext Outputs: SearchResult struct SearchSubContextsForSingle : public WorkflowTask { SearchSubContextsForSingle(OperationType operation = OperationType::Install) : WorkflowTask("SearchSubContextsForSingle"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: OperationType m_operationType; }; } ================================================ FILE: src/AppInstallerCLICore/Workflows/PinFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Resources.h" #include "PinFlow.h" #include "TableOutput.h" #include #include #include using namespace AppInstaller::Repository; namespace AppInstaller::CLI::Workflow { namespace { // Creates a Pin appropriate for the context based on the arguments provided Pinning::Pin CreatePin(Execution::Context& context, const Pinning::PinKey& pinKey) { if (context.Args.Contains(Execution::Args::Type::GatedVersion)) { return Pinning::Pin::CreateGatingPin(pinKey, context.Args.GetArg(Execution::Args::Type::GatedVersion)); } else if (context.Args.Contains(Execution::Args::Type::BlockingPin)) { return Pinning::Pin::CreateBlockingPin(pinKey); } else { return Pinning::Pin::CreatePinningPin(pinKey); } } void GetPinKeysForInstalled(const std::shared_ptr& installedVersion, std::set& pinKeys) { auto installedType = Manifest::ConvertToInstallerTypeEnum(installedVersion->GetMetadata()[PackageVersionMetadata::InstalledType]); std::vector propertyStrings; if (Manifest::DoesInstallerTypeUsePackageFamilyName(installedType)) { propertyStrings = installedVersion->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); } else if (Manifest::DoesInstallerTypeUseProductCode(installedType)) { propertyStrings = installedVersion->GetMultiProperty(PackageVersionMultiProperty::ProductCode); } for (const auto& value : propertyStrings) { pinKeys.emplace(Pinning::PinKey::GetPinKeyForInstalled(value)); } } std::set GetPinKeysForPackage(Execution::Context& context) { auto package = context.Get(); std::set pinKeys; if (context.Args.Contains(Execution::Args::Type::PinInstalled)) { auto installedVersion = GetInstalledVersion(package); if (installedVersion) { GetPinKeysForInstalled(installedVersion, pinKeys); } } else { auto availablePackages = package->GetAvailable(); for (const auto& availablePackage : availablePackages) { pinKeys.emplace( availablePackage->GetProperty(PackageProperty::Id).get(), availablePackage->GetSource().GetIdentifier()); } } return pinKeys; } // Gets a search request that can be used to find the installed package that corresponds with a pin. SearchRequest GetSearchRequestForPin(const Pinning::PinKey& pinKey) { SearchRequest searchRequest; if (pinKey.IsForInstalled()) { searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, pinKey.PackageId)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, pinKey.PackageId)); } else { searchRequest.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, pinKey.PackageId); } return searchRequest; } } void OpenPinningIndex::operator()(Execution::Context& context) const { auto pinningData = Pinning::PinningData{ m_readOnly ? Pinning::PinningData::Disposition::ReadOnly : Pinning::PinningData::Disposition::ReadWrite }; if (!m_readOnly && !pinningData) { AICLI_LOG(CLI, Error, << "Unable to open pinning index."); context.Reporter.Error() << Resource::String::PinCannotOpenIndex << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CANNOT_OPEN_PINNING_INDEX); } context.Add(std::move(pinningData)); } void GetAllPins(Execution::Context& context) { AICLI_LOG(CLI, Info, << "Getting all existing pins"); context.Add(context.Get().GetAllPins()); } void SearchPin(Execution::Context& context) { auto pinKeys = GetPinKeysForPackage(context); auto package = context.Get(); auto pinningData = context.Get(); std::vector pins; for (const auto& pinKey : pinKeys) { auto pin = pinningData.GetPin(pinKey); if (pin) { pins.emplace_back(std::move(pin.value())); } } context.Add(std::move(pins)); } void AddPin(Execution::Context& context) { auto pinKeys = GetPinKeysForPackage(context); auto package = context.Get(); auto pinningData = context.Get(); auto installedVersion = context.Get(); std::vector pinsToAddOrUpdate; for (const auto& pinKey : pinKeys) { auto pin = CreatePin(context, pinKey); AICLI_LOG(CLI, Info, << "Evaluating Pin " << pin.ToString()); auto existingPin = pinningData.GetPin(pinKey); if (existingPin) { Utility::LocIndString packageNameToReport; if (pinKey.IsForInstalled() && installedVersion) { packageNameToReport = installedVersion->GetProperty(PackageVersionProperty::Name); } else { auto availableVersion = GetAvailablePackageFromSource(package, pinKey.SourceId)->GetLatestVersion(); if (availableVersion) { packageNameToReport = availableVersion->GetProperty(PackageVersionProperty::Name); } } // Pin already exists. // If it is the same, we do nothing. If it is different, check for the --force arg if (pin == existingPin) { AICLI_LOG(CLI, Info, << "Pin already exists"); context.Reporter.Info() << Resource::String::PinAlreadyExists(packageNameToReport) << std::endl; continue; } AICLI_LOG(CLI, Info, << "Another pin already exists for the package for source " << pinKey.SourceId); if (context.Args.Contains(Execution::Args::Type::Force)) { AICLI_LOG(CLI, Info, << "Overwriting pin due to --force argument"); context.Reporter.Warn() << Resource::String::PinExistsOverwriting(packageNameToReport) << std::endl; pinsToAddOrUpdate.push_back(std::move(pin)); } else { context.Reporter.Error() << Resource::String::PinExistsUseForceArg(packageNameToReport) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PIN_ALREADY_EXISTS); } } else { pinsToAddOrUpdate.push_back(std::move(pin)); } } if (!pinsToAddOrUpdate.empty()) { for (const auto& pin : pinsToAddOrUpdate) { pinningData.AddOrUpdatePin(pin); } context.Reporter.Info() << Resource::String::PinAdded << std::endl; } } void RemovePin(Execution::Context& context) { auto package = context.Get(); auto pins = context.Get(); auto pinningData = context.Get(); bool pinExists = false; // Note that if a source was specified in the command line, // that will be the only one we get version keys from. // So, we remove pins from all sources unless one was provided. for (const auto& pin : pins) { AICLI_LOG(CLI, Info, << "Removing Pin " << pin.GetKey().ToString()); pinningData.RemovePin(pin.GetKey()); pinExists = true; } if (!pinExists) { AICLI_LOG(CLI, Warning, << "Pin does not exist"); context.Reporter.Warn() << Resource::String::PinDoesNotExist(package->GetProperty(PackageProperty::Name)) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PIN_DOES_NOT_EXIST); } context.Reporter.Info() << Resource::String::PinRemovedSuccessfully << std::endl; } void ReportPins(Execution::Context& context) { const auto& pins = context.Get(); if (pins.empty()) { context.Reporter.Info() << Resource::String::PinNoPinsExist << std::endl; return; } Execution::TableOutput<6> table(context.Reporter, { Resource::String::SearchName, Resource::String::SearchId, Resource::String::SearchVersion, Resource::String::SearchSource, Resource::String::PinType, Resource::String::PinVersion, }); const auto& source = context.Get(); for (const auto& pin : pins) { const auto& pinKey = pin.GetKey(); auto searchRequest = GetSearchRequestForPin(pin.GetKey()); auto searchResult = source.Search(searchRequest); for (const auto& match : searchResult.Matches) { Utility::LocIndString packageName; Utility::LocIndString sourceName; Utility::LocIndString version; if (pinKey.IsForInstalled()) { sourceName = Resource::LocString{ Resource::String::PinInstalledSource }; } else { // This ensures we get the info from the right source if it exists on multiple auto availablePackage = GetAvailablePackageFromSource(match.Package, pinKey.SourceId); if (availablePackage) { auto availableVersion = availablePackage->GetLatestVersion(); if (availableVersion) { packageName = availableVersion->GetProperty(PackageVersionProperty::Name); sourceName = availableVersion->GetProperty(PackageVersionProperty::SourceName); } } } auto installedVersion = GetInstalledVersion(match.Package); if (installedVersion) { packageName = installedVersion->GetProperty(PackageVersionProperty::Name); version = installedVersion->GetProperty(PackageVersionProperty::Version); } table.OutputLine({ packageName, pinKey.PackageId, version, sourceName, std::string{ ToString(pin.GetType()) }, pin.GetGatedVersion().ToString(), }); } } table.Complete(); } void ResetAllPins(Execution::Context& context) { AICLI_LOG(CLI, Info, << "Resetting all pins"); context.Reporter.Info() << Resource::String::PinResettingAll << std::endl; std::string sourceId; if (context.Args.Contains(Execution::Args::Type::Source)) { auto sourceName = context.Args.GetArg(Execution::Args::Type::Source); auto sources = Source::GetCurrentSources(); for (const auto& source : sources) { if (Utility::CaseInsensitiveEquals(source.Name, sourceName)) { sourceId = source.Identifier; break; } } } if (context.Get().ResetAllPins(sourceId)) { context.Reporter.Info() << Resource::String::PinResetSuccessful << std::endl; } else { context.Reporter.Info() << Resource::String::PinNoPinsExist << std::endl; } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/PinFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Opens the pinning index for use in future operations. // Required Args: None // Inputs: None // Outputs: PinningIndex struct OpenPinningIndex : public WorkflowTask { OpenPinningIndex(bool readOnly = false) : WorkflowTask("OpenPinningIndex"), m_readOnly(readOnly) {} void operator()(Execution::Context& context) const override; private: bool m_readOnly; }; // Gets all the pins from the index. // Required Args: None // Inputs: PinningIndex // Outputs: Pins void GetAllPins(Execution::Context& context); // Searches for all the pins associated with a package. // There may be several if a package is available from multiple sources // or if the pin is for the installed package. // Required Args: None // Inputs: PinningIndex, Package // Outputs: Pins void SearchPin(Execution::Context& context); // Adds a pin for the current package. // A separate pin will be added for each source. // Required Args: None // Inputs: PinningIndex, Package, InstalledVersion? // Outputs: None void AddPin(Execution::Context& context); // Removes all the pins associated with a package. // Required Args: None // Inputs: PinningIndex, Package, InstalledPackageVersion // Outputs: None void RemovePin(Execution::Context& context); // Report the pins in a table. // This includes searching for the corresponding installed packages // to be able to show more info, like the package name. // Required Args: None // Inputs: Pins // Outputs: None void ReportPins(Execution::Context& context); // Resets all the existing pins. // Required Args: None // Inputs: None // Outputs: None void ResetAllPins(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/PortableFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PortableFlow.h" #include "PortableInstaller.h" #include "WorkflowBase.h" #include #include #include using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility; using namespace AppInstaller::CLI::Portable; using namespace AppInstaller::Portable; using namespace AppInstaller::Repository::Microsoft; namespace AppInstaller::CLI::Workflow { namespace { constexpr std::string_view s_DefaultSource = "*DefaultSource"sv; std::string GetPortableProductCode(Execution::Context& context) { const std::string& packageId = context.Get().Id; std::string source; if (context.Contains(Execution::Data::PackageVersion)) { source = context.Get()->GetSource().GetIdentifier(); } else { source = s_DefaultSource; } return MakeSuitablePathPart(packageId + "_" + source); } void EnsureValidArgsForPortableInstall(Execution::Context& context) { std::string_view renameArg = context.Args.GetArg(Execution::Args::Type::Rename); try { if (MakeSuitablePathPart(renameArg) != renameArg) { context.Reporter.Error() << Resource::String::ReservedFilenameError << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS); } } catch (...) { context.Reporter.Error() << Resource::String::ReservedFilenameError << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS); } } void EnsureVolumeSupportsReparsePoints(Execution::Context& context) { Manifest::ScopeEnum scope = ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); const std::filesystem::path& symlinkDirectory = GetPortableLinksLocation(scope); if (!AppInstaller::Filesystem::SupportsReparsePoints(symlinkDirectory)) { context.Reporter.Error() << Resource::String::ReparsePointsNotSupportedError << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PORTABLE_REPARSE_POINT_NOT_SUPPORTED); } } } void VerifyPackageAndSourceMatch(Execution::Context& context) { const std::string& packageIdentifier = context.Get().Id; std::string sourceIdentifier; if (context.Contains(Execution::Data::PackageVersion)) { sourceIdentifier = context.Get()->GetSource().GetIdentifier(); } else { sourceIdentifier = s_DefaultSource; } PortableInstaller& portableInstaller = context.Get(); if (portableInstaller.ARPEntryExists()) { if (packageIdentifier != portableInstaller.WinGetPackageIdentifier || sourceIdentifier != portableInstaller.WinGetSourceIdentifier) { if (!context.Args.Contains(Execution::Args::Type::Force)) { AICLI_LOG(CLI, Error, << "Registry match failed, skipping write to uninstall registry"); context.Reporter.Error() << Resource::String::PortablePackageAlreadyExists << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PORTABLE_PACKAGE_ALREADY_EXISTS); } else { AICLI_LOG(CLI, Info, << "Overriding registry match check..."); context.Reporter.Warn() << Resource::String::PortableRegistryCollisionOverridden << std::endl; } } } portableInstaller.WinGetPackageIdentifier = packageIdentifier; portableInstaller.WinGetSourceIdentifier = sourceIdentifier; } void InitializePortableInstaller(Execution::Context& context) { Manifest::ScopeEnum scope = Manifest::ScopeEnum::Unknown; bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); std::shared_ptr installedVersion; if (context.Contains(Execution::Data::InstalledPackageVersion)) { installedVersion = context.Get(); } if (isUpdate && installedVersion) { IPackageVersion::Metadata installationMetadata = installedVersion->GetMetadata(); auto installerScopeItr = installationMetadata.find(Repository::PackageVersionMetadata::InstalledScope); if (installerScopeItr != installationMetadata.end()) { scope = Manifest::ConvertToScopeEnum(installerScopeItr->second); } } else { if (context.Args.Contains(Execution::Args::Type::InstallScope)) { scope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); } else { Manifest::ScopeEnum requiredScope = Settings::User().Get(); Manifest::ScopeEnum preferredScope = Settings::User().Get(); scope = requiredScope != Manifest::ScopeEnum::Unknown ? requiredScope : preferredScope; } } const auto& installer = context.Get().value(); Utility::Architecture arch = installer.Arch; const std::string& productCode = GetPortableProductCode(context); PortableInstaller portableInstaller = PortableInstaller(scope, arch, productCode); portableInstaller.IsUpdate = isUpdate; if (IsArchiveType(installer.BaseInstallerType) && installer.ArchiveBinariesDependOnPath) { portableInstaller.BinariesDependOnPath = true; } // Set target install directory std::string_view locationArg = context.Args.GetArg(Execution::Args::Type::InstallLocation); std::filesystem::path targetInstallDirectory; if (!locationArg.empty()) { targetInstallDirectory = std::filesystem::path{ ConvertToUTF16(locationArg) }; } else { targetInstallDirectory = GetPortableInstallRoot(scope, arch); targetInstallDirectory /= ConvertToUTF16(productCode); } portableInstaller.TargetInstallLocation = targetInstallDirectory; portableInstaller.SetAppsAndFeaturesMetadata(context.Get(), installer.AppsAndFeaturesEntries); context.Add(std::move(portableInstaller)); } std::vector GetDesiredStateForPortableInstall(Execution::Context& context) { std::filesystem::path& installerPath = context.Get(); PortableInstaller& portableInstaller = context.Get(); std::vector entries; const std::filesystem::path& targetInstallDirectory = portableInstaller.TargetInstallLocation; const std::filesystem::path& symlinkDirectory = GetPortableLinksLocation(portableInstaller.GetScope()); // InstallerPath will point to a directory if it is extracted from an archive. if (std::filesystem::is_directory(installerPath)) { portableInstaller.RecordToIndex = true; for (const auto& entry : std::filesystem::directory_iterator(installerPath)) { std::filesystem::path entryPath = entry.path(); PortableFileEntry portableFile; std::filesystem::path relativePath = std::filesystem::relative(entryPath, entryPath.parent_path()); std::filesystem::path targetPath = targetInstallDirectory / relativePath; if (std::filesystem::is_directory(entryPath)) { entries.emplace_back(std::move(PortableFileEntry::CreateDirectoryEntry(entryPath, targetPath))); } else { entries.emplace_back(std::move(PortableFileEntry::CreateFileEntry(entryPath, targetPath, {}))); } } const std::vector& nestedInstallerFiles = context.Get()->NestedInstallerFiles; for (const auto& nestedInstallerFile : nestedInstallerFiles) { const std::filesystem::path& targetPath = targetInstallDirectory / ConvertToUTF16(nestedInstallerFile.RelativeFilePath); std::filesystem::path commandAlias; if (nestedInstallerFile.PortableCommandAlias.empty()) { commandAlias = targetPath.filename(); } else { commandAlias = ConvertToUTF16(nestedInstallerFile.PortableCommandAlias); } Filesystem::AppendExtension(commandAlias, ".exe"); entries.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkDirectory / commandAlias, targetPath))); } } else { std::string_view renameArg = context.Args.GetArg(Execution::Args::Type::Rename); const std::vector& commands = context.Get()->Commands; std::filesystem::path commandAlias = installerPath.filename(); if (!commands.empty()) { commandAlias = ConvertToUTF16(commands[0]); } if (!renameArg.empty()) { commandAlias = ConvertToUTF16(renameArg); } AppInstaller::Filesystem::AppendExtension(commandAlias, ".exe"); const std::filesystem::path& targetFullPath = targetInstallDirectory / commandAlias; entries.emplace_back(std::move(PortableFileEntry::CreateFileEntry(installerPath, targetFullPath, {}))); entries.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkDirectory / commandAlias, targetFullPath))); } return entries; } void PortableInstallImpl(Execution::Context& context) { OperationType installType = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate) ? OperationType::Upgrade : OperationType::Install; PortableInstaller& portableInstaller = context.Get(); try { context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; std::vector desiredState = GetDesiredStateForPortableInstall(context); portableInstaller.SetDesiredState(desiredState); if (!portableInstaller.VerifyExpectedState()) { if (context.Args.Contains(Execution::Args::Type::Force)) { context.Reporter.Warn() << Resource::String::PortableHashMismatchOverridden << std::endl; } else { context.Reporter.Warn() << Resource::String::PortableHashMismatchOverrideRequired << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PORTABLE_UNINSTALL_FAILED); } } portableInstaller.Install(installType); context.Add({ portableInstaller.GetAppsAndFeaturesEntry() }); context.Add(ERROR_SUCCESS); context.Reporter.Warn() << portableInstaller.GetOutputMessage(); } catch (...) { context.Add(Workflow::HandleException(context, std::current_exception())); if (!portableInstaller.IsUpdate) { context.Reporter.Warn() << Resource::String::PortableInstallFailed << std::endl; portableInstaller.PrepareForCleanUp(); ; portableInstaller.Uninstall(); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PORTABLE_UNINSTALL_FAILED); } } } void PortableUninstallImpl(Execution::Context& context) { PortableInstaller& portableInstaller = context.Get(); try { context.Reporter.Info() << Resource::String::UninstallFlowStartingPackageUninstall << std::endl; if (!portableInstaller.VerifyExpectedState()) { if (context.Args.Contains(Execution::Args::Type::Force)) { context.Reporter.Warn() << Resource::String::PortableHashMismatchOverridden << std::endl; } else { context.Reporter.Warn() << Resource::String::PortableHashMismatchOverrideRequired << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PORTABLE_UNINSTALL_FAILED); } } portableInstaller.Purge = context.Args.Contains(Execution::Args::Type::Purge) || (!portableInstaller.IsUpdate && Settings::User().Get() && !context.Args.Contains(Execution::Args::Type::Preserve)); portableInstaller.Uninstall(); context.Add(ERROR_SUCCESS); context.Reporter.Warn() << portableInstaller.GetOutputMessage(); } catch (...) { context.Add(Workflow::HandleException(context, std::current_exception())); } } void EnsureSupportForPortableInstall(Execution::Context& context) { auto installerType = context.Get().value().EffectiveInstallerType(); if (installerType == InstallerTypeEnum::Portable) { context << EnsureValidArgsForPortableInstall << EnsureVolumeSupportsReparsePoints; } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/PortableFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Installs the portable package. // Required Args: None // Inputs: Manifest, Scope, Rename, Location // Outputs: None void PortableInstallImpl(Execution::Context& context); // Uninstalls the portable package. // Required Args: None // Inputs: ProductCode, Scope, Architecture // Outputs: None void PortableUninstallImpl(Execution::Context& context); // Verifies that the portable install operation is supported. // Required Args: None // Inputs: Scope, Rename // Outputs: None void EnsureSupportForPortableInstall(Execution::Context& context); // Initializes the portable installer. // Required Args: None // Inputs: Scope, Architecture, Manifest, Installer // Outputs: None void InitializePortableInstaller(Execution::Context& context); // Verifies that the package identifier and the source identifier match the ARP entry. // Required Args: None // Inputs: Manifest, PackageVersion, PortableInstaller // Outputs: None void VerifyPackageAndSourceMatch(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/PromptFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PromptFlow.h" #include "ShowFlow.h" #include using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Workflow { namespace { bool IsInteractivityAllowed(Execution::Context& context) { // Interactivity can be disabled for several reasons: // * We are running in a non-interactive context (e.g., COM call) // * It is disabled in the settings // * It was disabled from the command line if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DisableInteractivity)) { AICLI_LOG(CLI, Verbose, << "Skipping prompt. Interactivity is disabled due to non-interactive context."); return false; } if (context.Args.Contains(Execution::Args::Type::DisableInteractivity)) { AICLI_LOG(CLI, Verbose, << "Skipping prompt. Interactivity is disabled by command line argument."); return false; } if (Settings::User().Get()) { AICLI_LOG(CLI, Verbose, << "Skipping prompt. Interactivity is disabled in settings."); return false; } return true; } bool HandleSourceAgreementsForOneSource(Execution::Context& context, const Repository::Source& source) { auto details = source.GetDetails(); AICLI_LOG(CLI, Verbose, << "Checking Source agreements for source: " << details.Name); if (source.CheckSourceAgreements()) { AICLI_LOG(CLI, Verbose, << "Source agreements satisfied. Source: " << details.Name); return true; } // Show source agreements context.Reporter.Info() << Execution::SourceInfoEmphasis << Resource::String::SourceAgreementsTitle(Utility::LocIndView{ details.Name }) << std::endl; const auto& agreements = source.GetInformation().SourceAgreements; for (const auto& agreement : agreements) { if (!agreement.Label.empty()) { context.Reporter.Info() << Execution::SourceInfoEmphasis << Utility::LocIndString{ agreement.Label } << ": "_liv; } if (!agreement.Text.empty()) { context.Reporter.Info() << Utility::LocIndString{ agreement.Text } << std::endl; } if (!agreement.Url.empty()) { context.Reporter.Info() << Utility::LocIndString{ agreement.Url } << std::endl; } } // Show message for each individual implicit agreement field auto fields = source.GetAgreementFieldsFromSourceInformation(); if (WI_IsFlagSet(fields, Repository::ImplicitAgreementFieldEnum::Market)) { context.Reporter.Info() << Resource::String::SourceAgreementsMarketMessage << std::endl; } context.Reporter.Info() << std::endl; bool accepted = context.Args.Contains(Execution::Args::Type::AcceptSourceAgreements); if (!accepted && IsInteractivityAllowed(context)) { accepted = context.Reporter.PromptForBoolResponse(Resource::String::SourceAgreementsPrompt); } if (accepted) { AICLI_LOG(CLI, Verbose, << "Source agreements accepted. Source: " << details.Name); source.SaveAcceptedSourceAgreements(); } else { AICLI_LOG(CLI, Verbose, << "Source agreements not accepted. Source: " << details.Name); } return accepted; } // An interface for defining prompts to the user regarding a package. // Note that each prompt may behave differently when running non-interactively // (e.g. failing if it is needed vs. continuing silently), and they may // do some work while checking if the prompt is needed even if no prompt is shown, // so they need to always run. struct PackagePrompt { virtual ~PackagePrompt() = default; // Determines whether a package needs this prompt. // Inputs: Manifest, Installer // Outputs: None virtual bool PackageNeedsPrompt(Execution::Context& context) = 0; // Prompts for the information needed for a single package. // Inputs: Manifest, Installer // Outputs: None virtual void PromptForSinglePackage(Execution::Context& context) = 0; // Prompts for the information needed for multiple packages. // Inputs: Manifest, Installer (for each sub context) // Outputs: None virtual void PromptForMultiplePackages(Execution::Context& context, std::vector& packagesToPrompt) = 0; }; // Prompt for accepting package agreements. struct PackageAgreementsPrompt : public PackagePrompt { PackageAgreementsPrompt(bool ensureAgreementsAcceptance) : m_ensureAgreementsAcceptance(ensureAgreementsAcceptance) {} bool PackageNeedsPrompt(Execution::Context& context) override { const auto& agreements = context.Get().CurrentLocalization.Get(); return !agreements.empty(); } void PromptForSinglePackage(Execution::Context& context) override { ShowPackageAgreements(context); EnsurePackageAgreementsAcceptance(context, /* showPrompt */ true); } void PromptForMultiplePackages(Execution::Context& context, std::vector& packagesToPrompt) override { for (auto packageContext : packagesToPrompt) { // Show agreements for each package Execution::Context& showContext = *packageContext; auto previousThreadGlobals = showContext.SetForCurrentThread(); ShowPackageAgreements(showContext); if (showContext.IsTerminated()) { AICLI_TERMINATE_CONTEXT(showContext.GetTerminationHR()); } } EnsurePackageAgreementsAcceptance(context, /* showPrompt */ true); } private: void ShowPackageAgreements(Execution::Context& context) { const auto& manifest = context.Get(); auto agreements = manifest.CurrentLocalization.Get(); if (agreements.empty()) { // Nothing to do return; } context << Workflow::ReportManifestIdentityWithVersion(Resource::String::ReportIdentityForAgreements) << Workflow::ShowAgreementsInfo; context.Reporter.EmptyLine(); } void EnsurePackageAgreementsAcceptance(Execution::Context& context, bool showPrompt) const { if (!m_ensureAgreementsAcceptance) { return; } if (context.Args.Contains(Execution::Args::Type::AcceptPackageAgreements)) { AICLI_LOG(CLI, Info, << "Package agreements accepted by CLI flag"); return; } if (showPrompt) { AICLI_LOG(CLI, Verbose, << "Prompting to accept package agreements"); if (IsInteractivityAllowed(context)) { bool accepted = context.Reporter.PromptForBoolResponse(Resource::String::PackageAgreementsPrompt); if (accepted) { AICLI_LOG(CLI, Info, << "Package agreements accepted in prompt"); return; } else { AICLI_LOG(CLI, Info, << "Package agreements not accepted in prompt"); } } } AICLI_LOG(CLI, Error, << "Package agreements were not agreed to."); context.Reporter.Error() << Resource::String::PackageAgreementsNotAgreedTo << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED); } bool m_ensureAgreementsAcceptance; }; // Prompt for getting the install root when a package requires it and it is not // specified by the settings. struct InstallRootPrompt : public PackagePrompt { InstallRootPrompt() : m_installLocation(User().Get()) {} bool PackageNeedsPrompt(Execution::Context& context) override { if (context.Get()->InstallLocationRequired && !context.Args.Contains(Execution::Args::Type::InstallLocation)) { AICLI_LOG(CLI, Info, << "Package [" << context.Get().Id << "] requires an install location."); // An install location is required but one wasn't provided. // Check if there is a default one from settings. if (m_installLocation.empty()) { // We need to prompt return true; } else { // Use the default SetInstallLocation(context); } } return false; } void PromptForSinglePackage(Execution::Context& context) override { context.Reporter.Info() << Resource::String::InstallerRequiresInstallLocation << std::endl; PromptForInstallRoot(context); // When prompting for a single package, we use the provided location directly. // This is different from when we prompt for multiple packages or use the root in the settings. context.Args.AddArg(Execution::Args::Type::InstallLocation, m_installLocation.u8string()); } void PromptForMultiplePackages(Execution::Context& context, std::vector& packagesToPrompt) override { // Report packages that will be affected. context.Reporter.Info() << Resource::String::InstallersRequireInstallLocation << std::endl; for (auto packageContext : packagesToPrompt) { *packageContext << ReportManifestIdentityWithVersion(" - "_liv, Execution::Reporter::Level::Warning); if (packageContext->IsTerminated()) { AICLI_TERMINATE_CONTEXT(packageContext->GetTerminationHR()); } } PromptForInstallRoot(context); // Set the install location for each package. for (auto packageContext : packagesToPrompt) { SetInstallLocation(*packageContext); } } private: void PromptForInstallRoot(Execution::Context& context) { if (!IsInteractivityAllowed(context)) { AICLI_LOG(CLI, Error, << "Install location is required but was not provided."); context.Reporter.Error() << Resource::String::InstallLocationNotProvided << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_LOCATION_REQUIRED); } AICLI_LOG(CLI, Info, << "Prompting for install root."); m_installLocation = context.Reporter.PromptForPath(Resource::String::PromptForInstallRoot); if (m_installLocation.empty()) { AICLI_LOG(CLI, Error, << "Install location is required but the provided path was empty."); context.Reporter.Error() << Resource::String::InstallLocationNotProvided << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_LOCATION_REQUIRED); } AICLI_LOG(CLI, Info, << "Proceeding with installation using install root: " << m_installLocation); } // Sets the install location for an execution context. // The install location is obtained by appending the package ID to the install root. // This function assumes that m_installLocation is set, either from settings or from the prompt, // and that the context does not already have an install location. void SetInstallLocation(Execution::Context& context) { auto packageId = context.Get().Id; auto installLocation = m_installLocation; installLocation += "\\" + packageId; AICLI_LOG(CLI, Info, << "Setting install location for package [" << packageId << "] to: " << installLocation); context.Args.AddArg(Execution::Args::Type::InstallLocation, installLocation.u8string()); } std::filesystem::path m_installLocation; }; // Prompt asking whether to continue when an installer will abort the terminal. struct InstallerAbortsTerminalPrompt : public PackagePrompt { bool PackageNeedsPrompt(Execution::Context& context) override { return context.Get()->InstallerAbortsTerminal; } void PromptForSinglePackage(Execution::Context& context) override { AICLI_LOG(CLI, Info, << "This installer may abort the terminal"); context.Reporter.Warn() << Resource::String::InstallerAbortsTerminal << std::endl; PromptToProceed(context); } void PromptForMultiplePackages(Execution::Context& context, std::vector& packagesToPrompt) override { AICLI_LOG(CLI, Info, << "One or more installers may abort the terminal"); context.Reporter.Warn() << Resource::String::InstallersAbortTerminal << std::endl; for (auto packageContext : packagesToPrompt) { *packageContext << ReportManifestIdentityWithVersion(" - "_liv, Execution::Reporter::Level::Warning); if (packageContext->IsTerminated()) { AICLI_TERMINATE_CONTEXT(packageContext->GetTerminationHR()); } } PromptToProceed(context); } private: void PromptToProceed(Execution::Context& context) { AICLI_LOG(CLI, Info, << "Prompting before proceeding with installer that aborts terminal."); if (!IsInteractivityAllowed(context)) { return; } bool accepted = context.Reporter.PromptForBoolResponse(Resource::String::PromptToProceed, Reporter::Level::Warning, true); if (accepted) { AICLI_LOG(CLI, Info, << "Proceeding with installation"); } else { AICLI_LOG(CLI, Error, << "Aborting installation"); context.Reporter.Error() << Resource::String::Cancelled << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } } }; // Gets all the prompts that may be displayed, in order of appearance std::vector> GetPackagePrompts(bool ensureAgreementsAcceptance = true, bool installerDownloadOnly = false) { std::vector> result; if (installerDownloadOnly) { result.push_back(std::make_unique(ensureAgreementsAcceptance)); } else { result.push_back(std::make_unique(ensureAgreementsAcceptance)); result.push_back(std::make_unique()); result.push_back(std::make_unique()); } return result; } } void HandleSourceAgreements::operator()(Execution::Context& context) const { bool allAccepted = true; if (m_source.IsComposite()) { for (auto const& source : m_source.GetAvailableSources()) { if (!HandleSourceAgreementsForOneSource(context, source)) { allAccepted = false; } } } else { allAccepted = HandleSourceAgreementsForOneSource(context, m_source); } if (!allAccepted) { context.Reporter.Error() << Resource::String::SourceAgreementsNotAgreedTo << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED); } } void ShowPromptsForSinglePackage::operator()(Execution::Context& context) const { bool installerDownloadOnly = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerDownloadOnly); for (auto& prompt : GetPackagePrompts(true, installerDownloadOnly)) { // Show the prompt if needed if (prompt->PackageNeedsPrompt(context)) { prompt->PromptForSinglePackage(context); } if (context.IsTerminated()) { return; } } } void ShowPromptsForMultiplePackages::operator()(Execution::Context& context) const { for (auto& prompt : GetPackagePrompts(m_ensureAgreementsAcceptance, m_installerDownloadOnly)) { // Find which packages need this prompt std::vector packagesToPrompt; for (auto& packageContext : context.Get()) { if (prompt->PackageNeedsPrompt(*packageContext)) { packagesToPrompt.push_back(packageContext.get()); } } // Prompt only if needed if (!packagesToPrompt.empty()) { prompt->PromptForMultiplePackages(context, packagesToPrompt); if (context.IsTerminated()) { return; } } } } void RequireInteractivity::operator()(Execution::Context& context) const { if (!IsInteractivityAllowed(context)) { AICLI_TERMINATE_CONTEXT(m_nonInteractiveError); } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/PromptFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Handles all opened source(s) agreements if needed. // Required Args: The source to be checked for agreements // Inputs: None // Outputs: None struct HandleSourceAgreements : public WorkflowTask { HandleSourceAgreements(Repository::Source source) : WorkflowTask("HandleSourceAgreements"), m_source(std::move(source)) {} void operator()(Execution::Context& context) const override; private: Repository::Source m_source; }; // Shows all the prompts required for a single package, e.g. for package agreements // Required Args: None // Inputs: Manifest, Installer // Outputs: None struct ShowPromptsForSinglePackage : public WorkflowTask { ShowPromptsForSinglePackage(bool ensureAgreementsAcceptance) : WorkflowTask("ShowPromptsForSinglePackage"), m_ensureAgreementsAcceptance(ensureAgreementsAcceptance) {} void operator()(Execution::Context& context) const override; private: bool m_ensureAgreementsAcceptance; }; // Shows all the prompts required for multiple package, e.g. for package agreements // Required Args: None // Inputs: PackageSubContexts // Outputs: None struct ShowPromptsForMultiplePackages : public WorkflowTask { ShowPromptsForMultiplePackages(bool ensureAgreementsAcceptance, bool installerDownloadOnly) : WorkflowTask("ShowPromptsForMultiplePackages"), m_ensureAgreementsAcceptance(ensureAgreementsAcceptance), m_installerDownloadOnly(installerDownloadOnly) {} void operator()(Execution::Context& context) const override; private: bool m_ensureAgreementsAcceptance; bool m_installerDownloadOnly; }; // If the context is not interactive, terminate it with the given HRESULT. // Required Args: None // Inputs: None // Outputs: None struct RequireInteractivity : public WorkflowTask { RequireInteractivity(HRESULT nonInteractiveError) : WorkflowTask("RequireInteractivity"), m_nonInteractiveError(nonInteractiveError) {} void operator()(Execution::Context& context) const override; private: HRESULT m_nonInteractiveError; }; } ================================================ FILE: src/AppInstallerCLICore/Workflows/RepairFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RepairFlow.h" #include "Workflows/ShellExecuteInstallerHandler.h" #include "Workflows/WorkflowBase.h" #include "Workflows/DownloadFlow.h" #include "Workflows/ArchiveFlow.h" #include "Workflows/InstallFlow.h" #include "Workflows/PromptFlow.h" #include "winget/ManifestCommon.h" #include "AppInstallerDeployment.h" #include "AppInstallerMsixInfo.h" #include "AppInstallerSynchronization.h" #include "MSStoreInstallerHandler.h" #include #include using namespace AppInstaller::Manifest; using namespace AppInstaller::Msix; using namespace AppInstaller::Repository; using namespace AppInstaller::CLI::Execution; namespace AppInstaller::CLI::Workflow { // Internal implementation details namespace { void SetUninstallStringInContext(Execution::Context& context) { const auto& installedPackageVersion = context.Get(); IPackageVersion::Metadata packageMetadata = installedPackageVersion->GetMetadata(); // Default to silent unless it is not present or interactivity is requested auto uninstallCommandItr = packageMetadata.find(PackageVersionMetadata::SilentUninstallCommand); if ((!context.Args.Contains(Execution::Args::Type::Silent) && uninstallCommandItr == packageMetadata.end()) || context.Args.Contains(Execution::Args::Type::Interactive)) { auto interactiveItr = packageMetadata.find(PackageVersionMetadata::StandardUninstallCommand); if (interactiveItr != packageMetadata.end()) { uninstallCommandItr = interactiveItr; } } if (uninstallCommandItr == packageMetadata.end()) { context.Reporter.Error() << Resource::String::NoRepairInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND); } context.Add(uninstallCommandItr->second); } void SetModifyPathInContext(Execution::Context& context) { const auto& installedPackageVersion = context.Get(); IPackageVersion::Metadata packageMetadata = installedPackageVersion->GetMetadata(); // Default to silent unless it is not present or interactivity is requested auto modifyPathItr = packageMetadata.find(PackageVersionMetadata::StandardModifyCommand); if (modifyPathItr == packageMetadata.end()) { context.Reporter.Error() << Resource::String::NoRepairInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND); } context.Add(modifyPathItr->second); } void SetProductCodesInContext(Execution::Context& context) { const auto& installedPackageVersion = context.Get(); auto productCodes = installedPackageVersion->GetMultiProperty(PackageVersionMultiProperty::ProductCode); if (productCodes.empty()) { context.Reporter.Error() << Resource::String::NoRepairInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND); } context.Add(productCodes); } void SetPackageFamilyNamesInContext(Execution::Context& context) { const auto& installedPackageVersion = context.Get(); auto packageFamilyNames = installedPackageVersion->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); if (packageFamilyNames.empty()) { context.Reporter.Error() << Resource::String::NoRepairInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND); } context.Add(packageFamilyNames); } InstallerTypeEnum GetInstalledType(Execution::Context& context) { const auto& installedPackage = context.Get(); std::string installedType = installedPackage->GetMetadata()[PackageVersionMetadata::InstalledType]; return ConvertToInstallerTypeEnum(installedType); } void ApplicabilityCheckForInstalledPackage(Execution::Context& context) { // Installed Package repair applicability check const auto& installedPackageVersion = context.Get(); const std::string installerType = context.Get()->GetMetadata()[PackageVersionMetadata::InstalledType]; InstallerTypeEnum installerTypeEnum = ConvertToInstallerTypeEnum(installerType); if (installerTypeEnum == InstallerTypeEnum::Portable || installerTypeEnum == InstallerTypeEnum::Unknown) { context.Reporter.Error() << Resource::String::RepairOperationNotSupported << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_REPAIR_NOT_SUPPORTED); } IPackageVersion::Metadata packageMetadata = installedPackageVersion->GetMetadata(); auto noModifyItr = packageMetadata.find(PackageVersionMetadata::NoModify); std::string noModifyARPFlag = noModifyItr != packageMetadata.end() ? noModifyItr->second : std::string(); auto noRepairItr = packageMetadata.find(PackageVersionMetadata::NoRepair); std::string noRepairARPFlag = noRepairItr != packageMetadata.end() ? noRepairItr->second : std::string(); if (Utility::IsDwordFlagSet(noModifyARPFlag) || Utility::IsDwordFlagSet(noRepairARPFlag)) { context.Reporter.Error() << Resource::String::RepairOperationNotSupported << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_REPAIR_NOT_SUPPORTED); } } void ApplicabilityCheckForAvailablePackage(Execution::Context& context) { // Skip the Available Package applicability check for MSI and MSIX repair as they aren't needed. if (!context.Contains(Execution::Data::Installer)) { return; } // Selected Installer repair applicability check auto installerType = context.Get()->EffectiveInstallerType(); auto repairBehavior = context.Get()->RepairBehavior; if (installerType == InstallerTypeEnum::Portable || installerType == InstallerTypeEnum::Unknown) { context.Reporter.Error() << Resource::String::RepairOperationNotSupported << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_REPAIR_NOT_SUPPORTED); } // Repair behavior is required for Burn, Inno, Nullsoft, Exe installers if (DoesInstallerTypeRequireRepairBehaviorForRepair(installerType) && repairBehavior == RepairBehaviorEnum::Unknown) { context.Reporter.Error() << Resource::String::NoRepairInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND); } } void HandleModifyRepairBehavior(Execution::Context& context, std::string& repairCommand) { SetModifyPathInContext(context); repairCommand += context.Get(); } void HandleInstallerRepairBehavior(Execution::Context& context, InstallerTypeEnum installerType) { context << ShowInstallationDisclaimer << ShowPromptsForSinglePackage(/* ensureAcceptance */ true) << DownloadInstaller; if (installerType == InstallerTypeEnum::Zip) { context << ScanArchiveFromLocalManifest << ExtractFilesFromArchive << VerifyAndSetNestedInstaller; } } void HandleUninstallerRepairBehavior(Execution::Context& context, std::string& repairCommand) { SetUninstallStringInContext(context); repairCommand += context.Get(); } void GenerateRepairString(Execution::Context& context) { const auto& installer = context.Get(); auto installerType = installer->BaseInstallerType; auto repairBehavior = installer->RepairBehavior; std::string repairCommand; switch (repairBehavior) { case RepairBehaviorEnum::Modify: HandleModifyRepairBehavior(context, repairCommand); break; case RepairBehaviorEnum::Installer: HandleInstallerRepairBehavior(context, installerType); break; case RepairBehaviorEnum::Uninstaller: HandleUninstallerRepairBehavior(context, repairCommand); break; case RepairBehaviorEnum::Unknown: default: context.Reporter.Error() << Resource::String::NoRepairInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND); } context << GetInstallerArgs; // If the repair behavior is set to 'Installer', we can proceed with the repair command as is. // For repair behaviors other than 'Installer', subsequent steps will be necessary to prepare the repair command. if (repairBehavior == RepairBehaviorEnum::Installer) { return; } if (repairCommand.empty()) { context.Reporter.Error() << Resource::String::NoRepairInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND); } repairCommand += " "; repairCommand += context.Get(); context.Add(repairCommand); } bool IsInstallerMappingRequired(Execution::Context& context) { InstallerTypeEnum installerTypeEnum = GetInstalledType(context); switch (installerTypeEnum) { case InstallerTypeEnum::Msi: return false; case InstallerTypeEnum::Msix: // For MSIX packages that are from the Microsoft Store, selecting an installer is required. if (context.Contains(Execution::Data::Package)) { auto availablePackages = context.Get()->GetAvailable(); if (availablePackages.size() == 1 && availablePackages[0]->GetSource() == WellKnownSource::MicrosoftStore) { return true; } } // For MSIX packages that are not from the Microsoft Store, selecting an installer is not required. return false; default: return true; } } void HandleModifyOrUninstallerRepair(Execution::Context& context, RepairBehaviorEnum repairBehavior) { context << ShellExecuteRepairImpl << ReportRepairResult(RepairBehaviorToString(repairBehavior), APPINSTALLER_CLI_ERROR_EXEC_REPAIR_FAILED); } void HandleInstallerRepair(Execution::Context& context, RepairBehaviorEnum repairBehavior) { context << ShellExecuteInstallImpl << ReportInstallerResult(RepairBehaviorToString(repairBehavior), APPINSTALLER_CLI_ERROR_EXEC_REPAIR_FAILED); } } void RunRepairForRepairBehaviorBasedInstaller(Execution::Context& context) { const auto& installer = context.Get(); auto repairBehavior = installer->RepairBehavior; switch (repairBehavior) { case RepairBehaviorEnum::Modify: case RepairBehaviorEnum::Uninstaller: HandleModifyOrUninstallerRepair(context, repairBehavior); break; case RepairBehaviorEnum::Installer: HandleInstallerRepair(context, repairBehavior); break; default: context.Reporter.Error() << Resource::String::NoRepairInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND); } } void RepairMsiBasedInstaller(Execution::Context& context) { context << ShellExecuteMsiExecRepair << ReportRepairResult("MsiExec", APPINSTALLER_CLI_ERROR_EXEC_REPAIR_FAILED); } void RepairApplicabilityCheck(Execution::Context& context) { context << ApplicabilityCheckForInstalledPackage << ApplicabilityCheckForAvailablePackage; } void ExecuteRepair(Execution::Context& context) { InstallerTypeEnum installerTypeEnum = context.Contains(Execution::Data::Installer) ? context.Get()->EffectiveInstallerType() : GetInstalledType(context); Synchronization::CrossProcessInstallLock lock; if (!ExemptFromSingleInstallLocking(installerTypeEnum)) { // Acquire the lock , if the operation is cancelled it will return false so we will also return. if (!context.Reporter.ExecuteWithProgress([&](IProgressCallback& callback) { callback.SetProgressMessage(Resource::String::InstallWaitingOnAnother()); return lock.Acquire(callback); })) { AICLI_LOG(CLI, Info, << "Abandoning attempt to acquire repair lock due to cancellation"); return; } } switch (installerTypeEnum) { case InstallerTypeEnum::Exe: case InstallerTypeEnum::Burn: case InstallerTypeEnum::Inno: case InstallerTypeEnum::Nullsoft: { context << RunRepairForRepairBehaviorBasedInstaller; } break; case InstallerTypeEnum::Msi: case InstallerTypeEnum::Wix: { context << RepairMsiBasedInstaller; } break; case InstallerTypeEnum::Msix: { context << RepairMsixPackage; } break; case InstallerTypeEnum::MSStore: { context << EnsureStorePolicySatisfied << MSStoreRepair; } break; case InstallerTypeEnum::Portable: default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } void GetRepairInfo(Execution::Context& context) { InstallerTypeEnum installerTypeEnum = context.Contains(Execution::Data::Installer) ? context.Get()->BaseInstallerType : GetInstalledType(context); switch (installerTypeEnum) { // Exe based installers, for installed package all gets mapped to exe extension. case InstallerTypeEnum::Burn: case InstallerTypeEnum::Exe: case InstallerTypeEnum::Inno: case InstallerTypeEnum::Nullsoft: { context << GenerateRepairString; } break; // MSI based installers, for installed package all gets mapped to msi extension. case InstallerTypeEnum::Msi: case InstallerTypeEnum::Wix: { context << SetProductCodesInContext; } break; // MSIX based installers, msix. case InstallerTypeEnum::Msix: { context << SetPackageFamilyNamesInContext; } break; case InstallerTypeEnum::MSStore: break; case InstallerTypeEnum::Portable: default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } void RepairMsixPackage(Execution::Context& context) { bool isMachineScope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)) == Manifest::ScopeEnum::Machine; const auto& packageFamilyNames = context.Get(); context.Reporter.Info() << Resource::String::RepairFlowStartingPackageRepair << std::endl; for (const auto& packageFamilyName : packageFamilyNames) { auto packageFullName = Msix::GetPackageFullNameFromFamilyName(packageFamilyName); if (!packageFullName.has_value()) { AICLI_LOG(CLI, Warning, << "No package found with family name: " << packageFamilyName); continue; } AICLI_LOG(CLI, Info, << "Repairing package: " << packageFullName.value()); try { if (!isMachineScope) { // Best effort repair by registering the package. context.Reporter.ExecuteWithProgress(std::bind(Deployment::RegisterPackage, packageFamilyName, std::placeholders::_1)); } else { context.Reporter.Error() << Resource::String::RepairFlowReturnCodeSystemNotSupported << std::endl; context.Add(static_cast(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED)); AICLI_LOG(CLI, Error, << "Device wide repair for msix type is not supported."); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED); } } catch (const wil::ResultException& re) { context.Add(re.GetErrorCode()); context << ReportRepairResult("MSIX", re.GetErrorCode(), true); return; } } context.Reporter.Info() << Resource::String::RepairFlowRepairSuccess << std::endl; } void RepairSinglePackage(Execution::Context& context) { context << RepairApplicabilityCheck << GetRepairInfo << ReportExecutionStage(ExecutionStage::Execution) << ExecuteRepair << ReportExecutionStage(ExecutionStage::PostExecution); } void SelectApplicablePackageVersion(Execution::Context& context) { // If the repair flow is initiated with manifest, then we don't need to select the applicable package version. if (context.Args.Contains(Args::Type::Manifest)) { return; } const auto& installedPackage = context.Get(); Utility::Version installedVersion = Utility::Version(installedPackage->GetProperty(PackageVersionProperty::Version)); if (installedVersion.IsUnknown()) { context.Reporter.Error() << Resource::String::NoApplicableInstallers << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER); } std::string_view requestedVersion = context.Args.Contains(Execution::Args::Type::TargetVersion) ? context.Args.GetArg(Execution::Args::Type::TargetVersion) : installedVersion.ToString(); // If it's Store source with only one version unknown, use the unknown version for available version mapping. const auto& package = context.Get(); auto packageVersions = GetAvailableVersionsForInstalledVersion(package, installedPackage); auto versionKeys = packageVersions->GetVersionKeys(); if (versionKeys.size() == 1) { auto packageVersion = packageVersions->GetVersion(versionKeys.at(0)); if (packageVersion->GetSource().IsWellKnownSource(WellKnownSource::MicrosoftStore) && Utility::Version{ packageVersion->GetProperty(PackageVersionProperty::Version) }.IsUnknown()) { requestedVersion = ""; } } context << GetManifestWithVersionFromPackage( requestedVersion, context.Args.GetArg(Execution::Args::Type::Channel), false); } void SelectApplicableInstallerIfNecessary(Execution::Context& context) { // For MSI installers, the platform provides built-in support for repair via msiexec, hence no need to select an installer. // Similarly, for MSIX packages that are not from the Microsoft Store, selecting an installer is not required. if (IsInstallerMappingRequired(context)) { context << SelectApplicablePackageVersion << SelectInstaller << EnsureApplicableInstaller; } } void ReportRepairResult::operator()(Execution::Context& context) const { DWORD repairResult = context.Get(); if (repairResult != 0) { auto& repairPackage = context.Contains(Execution::Data::PackageVersion) ? context.Get() : context.Get(); Logging::Telemetry().LogRepairFailure( repairPackage->GetProperty(PackageVersionProperty::Id), repairPackage->GetProperty(PackageVersionProperty::Version), m_repairType, repairResult); if (m_isHResult) { context.Reporter.Error() << Resource::String::RepairFailedWithCode(Utility::LocIndView{ GetUserPresentableMessage(repairResult) }) << std::endl; } else { context.Reporter.Error() << Resource::String::RepairFailedWithCode(repairResult) << std::endl; } // Show log path if available if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get())) { auto installerLogPath = Utility::LocIndString{ context.Get().u8string() }; context.Reporter.Info() << Resource::String::InstallerLogAvailable(installerLogPath) << std::endl; } AICLI_TERMINATE_CONTEXT(m_hr); } else { context.Reporter.Info() << Resource::String::RepairFlowRepairSuccess << std::endl; } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/RepairFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Execute the repair operation for RepairBehavior based installers. // RequiredArgs:None // Inputs: RepairBehavior, RepairString // Outputs:None void RunRepairForRepairBehaviorBasedInstaller(Execution::Context& context); // Execute the repair operation for MSI based installers. // RequiredArgs:None // Inputs: ProductCodes // Outputs:None void RepairMsiBasedInstaller(Execution::Context& context); // Applicability check for repair operation. // RequiredArgs:None // Inputs:InstalledPackageVersion, NoModify ?, NoRepair ? // Outputs:None void RepairApplicabilityCheck(Execution::Context& context); // Execute the repair operation. // RequiredArgs:None // Inputs: InstallerType, RepairBehavior ?, RepairString? , ProductCodes?, PackageFamilyNames? // Outputs:None void ExecuteRepair(Execution::Context& context); // Obtains the necessary information for repair operation. // RequiredArgs:None // Inputs:InstallerType // Outputs:RepairString?, ProductCodes?, PackageFamilyNames? void GetRepairInfo(Execution::Context& context); // Perform the repair operation for the MSIX NonStore package. // RequiredArgs:None // Inputs:PackageFamilyNames , InstallScope? // Outputs:None void RepairMsixPackage(Execution::Context& context); // Select the applicable package version by matching the installed package version with the available package version. // RequiredArgs:None // Inputs: Package,InstalledPackageVersion, AvailablePackageVersions // Outputs:Manifest, PackageVersion, Installer void SelectApplicablePackageVersion(Execution::Context& context); /// /// Select the applicable installer for the installed package if necessary. // RequiredArgs:None // Inputs: Package,InstalledPackageVersion, AvailablePackageVersions // Outputs:Manifest, PackageVersion, Installer void SelectApplicableInstallerIfNecessary(Execution::Context& context); // Perform the repair operation for the single package. // RequiredArgs:None // Inputs: SearchResult, InstalledPackage, ApplicableInstaller // Outputs:None void RepairSinglePackage(Execution::Context& context); // Reports the result of the repair. // Required Args: None // Inputs: None // Outputs: None struct ReportRepairResult : public WorkflowTask { ReportRepairResult(std::string_view repairType, HRESULT hr, bool isHResult = false) : WorkflowTask("ReportRepairResult"), m_repairType(repairType), m_hr(hr), m_isHResult(isHResult) {} void operator()(Execution::Context& context) const override; private: // Repair type used for reporting failure. std::string_view m_repairType; // Result to return if the repair fails. HRESULT m_hr; // Whether the result is an HRESULT. bool m_isHResult; }; } ================================================ FILE: src/AppInstallerCLICore/Workflows/ResumeFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ResumeFlow.h" #include "winget/Reboot.h" #include namespace AppInstaller::CLI::Workflow { void Checkpoint::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) { return; } context.Checkpoint(m_checkpointName, m_contextData); } void RegisterStartupAfterReboot::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume)) { return; } if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::RegisterResume)) { auto executablePath = AppInstaller::Runtime::GetPathTo(AppInstaller::Runtime::PathName::CLIExecutable); // RunOnce registry value must start with the full path of the executable. const auto& resumeId = context.GetResumeId(); std::string commandLine = executablePath.u8string() + " resume -g " + resumeId; Reboot::WriteToRunOnceRegistry(resumeId, commandLine); } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/ResumeFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Applies a checkpoint to the context workflow. // Required Args: None // Inputs: Context data, command arguments, client version // Outputs: None struct Checkpoint : public WorkflowTask { Checkpoint(std::string_view checkpointName, std::vector contextData) : WorkflowTask("Checkpoint"), m_checkpointName(checkpointName), m_contextData(std::move(contextData)) {} void operator()(Execution::Context& context) const override; private: std::string_view m_checkpointName; std::vector m_contextData; }; // Registers the resume command to execute upon reboot if applicable. This task always executes even if context terminates. // Required Args: None // Inputs: None // Outputs: None struct RegisterStartupAfterReboot : public WorkflowTask { RegisterStartupAfterReboot() : WorkflowTask("RegisterStartupAfterReboot", /* executeAlways*/ true) {} void operator()(Execution::Context & context) const override; }; } ================================================ FILE: src/AppInstallerCLICore/Workflows/SettingsFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Resources.h" #include "SettingsFlow.h" #include #include #include namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; namespace { struct ExportSettingsJson { ExportSettingsJson() { root["$schema"] = "https://aka.ms/winget-settings-export.schema.json"; root["adminSettings"] = Json::ValueType::objectValue; root["userSettingsFile"] = UserSettings::SettingsFilePath().u8string(); } void AddAdminSetting(BoolAdminSetting setting) { auto str = std::string{ Settings::AdminSettingToString(setting) }; root["adminSettings"][str] = Settings::IsAdminSettingEnabled(setting); } void AddAdminSetting(StringAdminSetting setting) { auto name = std::string{ Settings::AdminSettingToString(setting) }; auto value = Settings::GetAdminSetting(setting); if (value) { root["adminSettings"][name] = value.value(); } } std::string ToJsonString() const { Json::StreamWriterBuilder writerBuilder; writerBuilder.settings_["indentation"] = ""; return Json::writeString(writerBuilder, root); } private: Json::Value root{ Json::ValueType::objectValue }; }; } void EnableAdminSetting(Execution::Context& context) { auto adminSettingString = context.Args.GetArg(Execution::Args::Type::AdminSettingEnable); BoolAdminSetting adminSetting = Settings::StringToBoolAdminSetting(adminSettingString); if (Settings::EnableAdminSetting(adminSetting)) { context.Reporter.Info() << Resource::String::AdminSettingEnabled(AdminSettingToString(adminSetting)) << std::endl; } else { context.Reporter.Error() << Resource::String::EnableAdminSettingFailed(AdminSettingToString(adminSetting)) << std::endl; } } void DisableAdminSetting(Execution::Context& context) { auto adminSettingString = context.Args.GetArg(Execution::Args::Type::AdminSettingDisable); BoolAdminSetting adminSetting = Settings::StringToBoolAdminSetting(adminSettingString); if (Settings::DisableAdminSetting(adminSetting)) { context.Reporter.Info() << Resource::String::AdminSettingDisabled(AdminSettingToString(adminSetting)) << std::endl; } else { context.Reporter.Error() << Resource::String::DisableAdminSettingFailed(AdminSettingToString(adminSetting)) << std::endl; } } void SetAdminSetting(Execution::Context& context) { auto adminSettingName = context.Args.GetArg(Execution::Args::Type::SettingName); auto adminSettingValue = context.Args.GetArg(Execution::Args::Type::SettingValue); StringAdminSetting adminSetting = Settings::StringToStringAdminSetting(adminSettingName); if (Settings::SetAdminSetting(adminSetting, adminSettingValue)) { context.Reporter.Info() << Resource::String::SetAdminSettingSucceeded(LocIndString{ adminSettingName }, LocIndString{ adminSettingValue }) << std::endl; } else { context.Reporter.Error() << Resource::String::SetAdminSettingFailed(LocIndString{ adminSettingName }) << std::endl; } } void ResetAdminSetting(Execution::Context& context) { auto adminSettingName = context.Args.GetArg(Execution::Args::Type::SettingName); // Try as both bool and string setting as we don't know the type auto boolAdminSetting = Settings::StringToBoolAdminSetting(adminSettingName); auto stringAdminSetting = Settings::StringToStringAdminSetting(adminSettingName); if ((boolAdminSetting != Settings::BoolAdminSetting::Unknown && Settings::DisableAdminSetting(boolAdminSetting)) || (stringAdminSetting != Settings::StringAdminSetting::Unknown && Settings::ResetAdminSetting(stringAdminSetting))) { context.Reporter.Info() << Resource::String::ResetAdminSettingSucceeded(LocIndString{ adminSettingName }) << std::endl; } else { context.Reporter.Error() << Resource::String::ResetAdminSettingFailed(LocIndString{ adminSettingName }) << std::endl; } } void ResetAllAdminSettings(Execution::Context& context) { Settings::ResetAllAdminSettings(); context.Reporter.Info() << Resource::String::ResetAllAdminSettingsSucceeded << std::endl; } void OpenUserSetting(Execution::Context& context) { // Show warnings only when the setting command is executed. if (!User().GetWarnings().empty()) { context.Reporter.Warn() << Resource::String::SettingLoadFailure << std::endl; for (const auto& warning : User().GetWarnings()) { auto warn = context.Reporter.Warn(); warn << warning.Message; if (!warning.Path.empty()) { if (warning.IsFieldWarning) { warn << ' ' << Resource::String::SettingsWarningField(warning.Path); } else { warn << ' ' << warning.Path; } } if (!warning.Data.empty()) { if (warning.IsFieldWarning) { warn << ' ' << Resource::String::SettingsWarningValue(warning.Data); } else { warn << std::endl << warning.Data; } } warn << std::endl; } } User().PrepareToShellExecuteFile(); auto filePathUTF16 = UserSettings::SettingsFilePath().wstring(); // Some versions of windows will fail if no file extension association exists, other will pop up the dialog // to make the user pick their default. // Kudos to the terminal team for this workaround. HINSTANCE res = ShellExecuteW(nullptr, nullptr, filePathUTF16.c_str(), nullptr, nullptr, SW_SHOW); if (static_cast(reinterpret_cast(res)) <= 32) { // User doesn't have file type association. Default to notepad AICLI_LOG(CLI, Info, << "Json file type association not found, using notepad.exe"); ShellExecuteW(nullptr, nullptr, L"notepad", filePathUTF16.c_str(), nullptr, SW_SHOW); } } void ExportSettings(Execution::Context& context) { ExportSettingsJson exportSettingsJson; for (const auto& setting : GetAllBoolAdminSettings()) { exportSettingsJson.AddAdminSetting(setting); } for (const auto& setting : GetAllStringAdminSettings()) { exportSettingsJson.AddAdminSetting(setting); } context.Reporter.Info() << exportSettingsJson.ToJsonString() << std::endl; } } ================================================ FILE: src/AppInstallerCLICore/Workflows/SettingsFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Enables an admin setting. // Required Args: AdminSettingEnable // Inputs: None // Outputs: None void EnableAdminSetting(Execution::Context& context); // Disables an admin setting. // Required Args: AdminSettingDisable // Inputs: None // Outputs: None void DisableAdminSetting(Execution::Context& context); // Sets the value of an admin setting. // Required Args: SettingName, SettingValue // Inputs: None // Outputs: None void SetAdminSetting(Execution::Context& context); // Resets an admin setting to the default. // Required Args: SettingName // Inputs: None // Outputs: None void ResetAdminSetting(Execution::Context& context); // Resets all admin settings to the default. // Required Args: None // Inputs: None // Outputs: None void ResetAllAdminSettings(Execution::Context& context); // Opens the user settings. // Required Args: None // Inputs: None // Outputs: None void OpenUserSetting(Execution::Context& context); // Lists the state of settings. // Required Args: None // Inputs: None // Outputs: None void ExportSettings(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ShellExecuteInstallerHandler.h" #include #include #include using namespace AppInstaller::CLI; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; namespace AppInstaller::CLI::Workflow { namespace { // ShellExecutes the given path. std::optional InvokeShellExecuteEx(const std::filesystem::path& filePath, const std::string& args, bool useRunAs, int show, IProgressCallback& progress) { AICLI_LOG(CLI, Info, << "Starting: '" << filePath.u8string() << "' with arguments '" << args << '\''); SHELLEXECUTEINFOW execInfo = { 0 }; execInfo.cbSize = sizeof(execInfo); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS; execInfo.lpFile = filePath.c_str(); std::wstring argsUtf16 = Utility::ConvertToUTF16(args); execInfo.lpParameters = argsUtf16.c_str(); execInfo.nShow = show; // This installer must be run elevated, but we are not currently. // Have ShellExecute elevate the installer since it won't do so itself. if (useRunAs) { execInfo.lpVerb = L"runas"; } THROW_LAST_ERROR_IF(!ShellExecuteExW(&execInfo) || !execInfo.hProcess); wil::unique_process_handle process{ execInfo.hProcess }; // Wait for installation to finish while (!progress.IsCancelledBy(CancelReason::User)) { DWORD waitResult = WaitForSingleObject(process.get(), 250); if (waitResult == WAIT_OBJECT_0) { break; } if (waitResult != WAIT_TIMEOUT) { THROW_LAST_ERROR_MSG("Unexpected WaitForSingleObjectResult: %lu", waitResult); } } if (progress.IsCancelledBy(CancelReason::Any)) { return {}; } else { DWORD exitCode = 0; GetExitCodeProcess(process.get(), &exitCode); return exitCode; } } std::optional InvokeShellExecute(const std::filesystem::path& filePath, const std::string& args, IProgressCallback& progress) { // Some installers force UI. Setting to SW_HIDE will hide installer UI and installation will never complete. // Verified setting to SW_SHOW does not hurt silent mode since no UI will be shown. return InvokeShellExecuteEx(filePath, args, false, SW_SHOW, progress); } // Gets the escaped installer args. std::string GetInstallerArgsTemplate(Execution::Context& context) { bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); bool isRepair = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseRepair); const auto& installer = context.Get(); const auto& installerSwitches = installer->Switches; std::string installerArgs = {}; // Construct install experience arg. // SilentWithProgress is default, so look for it first. auto experienceArgsItr = installerSwitches.find(InstallerSwitchType::SilentWithProgress); if (context.Args.Contains(Execution::Args::Type::Interactive)) { // If interactive requested, always use Interactive (or nothing). If the installer supports // interactive it is usually the default, and thus it is cumbersome to put a blank entry in // the manifest. experienceArgsItr = installerSwitches.find(InstallerSwitchType::Interactive); } // If no SilentWithProgress exists, or Silent requested, try to find Silent. else if (experienceArgsItr == installerSwitches.end() || context.Args.Contains(Execution::Args::Type::Silent)) { auto silentItr = installerSwitches.find(InstallerSwitchType::Silent); // If Silent requested, but doesn't exist, then continue using SilentWithProgress. if (silentItr != installerSwitches.end()) { experienceArgsItr = silentItr; } } if (experienceArgsItr != installerSwitches.end()) { installerArgs += experienceArgsItr->second; } // Construct log path arg. if (installerSwitches.find(InstallerSwitchType::Log) != installerSwitches.end()) { installerArgs += ' ' + installerSwitches.at(InstallerSwitchType::Log); } // Construct repair arg. Custom switches and other args are not applicable for repair scenario so we can return here. if (isRepair) { if (installerSwitches.find(InstallerSwitchType::Repair) != installerSwitches.end()) { installerArgs += ' ' + installerSwitches.at(InstallerSwitchType::Repair); } return installerArgs; } // Construct custom arg. if (installerSwitches.find(InstallerSwitchType::Custom) != installerSwitches.end()) { installerArgs += ' ' + installerSwitches.at(InstallerSwitchType::Custom); } // Construct custom arg passed in by cli arg if (context.Args.Contains(Execution::Args::Type::CustomSwitches)) { std::string_view customSwitches = context.Args.GetArg(Execution::Args::Type::CustomSwitches); // Since these arguments are appended to the installer at runtime, it doesn't make sense to append them if empty or whitespace if (!Utility::IsEmptyOrWhitespace(customSwitches)) { installerArgs += ' ' + std::string{ customSwitches }; } } // Construct update arg if applicable if (isUpdate && installerSwitches.find(InstallerSwitchType::Update) != installerSwitches.end()) { installerArgs += ' ' + installerSwitches.at(InstallerSwitchType::Update); } // Construct install location arg if necessary. if (context.Args.Contains(Execution::Args::Type::InstallLocation) && installerSwitches.find(InstallerSwitchType::InstallLocation) != installerSwitches.end()) { installerArgs += ' ' + installerSwitches.at(InstallerSwitchType::InstallLocation); } return installerArgs; } // Applies values to the template. void PopulateInstallerArgsTemplate(Execution::Context& context, std::string& installerArgs) { // Populate with value from command line or temp path. std::string logPath; if (context.Args.Contains(Execution::Args::Type::Log)) { logPath = context.Args.GetArg(Execution::Args::Type::Log); } else { const auto& manifest = context.Get(); auto path = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation); path /= Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); path += '-'; path += Utility::GetCurrentTimeForFilename(true); path += Logging::FileLogger::DefaultExt(); logPath = path.u8string(); } if (Utility::FindAndReplace(installerArgs, std::string(ARG_TOKEN_LOGPATH), logPath)) { context.Add(Utility::ConvertToUTF16(logPath)); } // Populate with value from command line. if (context.Args.Contains(Execution::Args::Type::InstallLocation)) { Utility::FindAndReplace(installerArgs, std::string(ARG_TOKEN_INSTALLPATH), context.Args.GetArg(Execution::Args::Type::InstallLocation)); } // Todo: language token support will be implemented later } // Gets the arguments for uninstalling an MSI with MsiExec std::string GetMsiExecUninstallArgs(Execution::Context& context, const Utility::LocIndString& productCode) { std::string args = "/x" + productCode.get(); // https://learn.microsoft.com/en-us/windows/win32/msi/standard-installer-command-line-options if (context.Args.Contains(Execution::Args::Type::Silent)) { args += " /quiet /norestart"; } else if (!context.Args.Contains(Execution::Args::Type::Interactive)) { args += " /passive /norestart"; } return args; } // Gets the arguments for repairing an MSI with MsiExec std::string GetMsiExecRepairArgs(Execution::Context& context, const Utility::LocIndString& productCode) { // https://learn.microsoft.com/en-us/windows/win32/msi/command-line-options // Available Options for '/f [p|o|e|d|c|a|u|m|s|v] ' // Default parameter for '/f' is 'omus' // o - Reinstall all files regardless of version // m - Rewrite all required registry entries (This is the default option) // u - Rewrite all required user-specific registry entries (This is the default option) // s - Overwrite all existing shortcuts (This is the default option) std::string args = "/f " + productCode.get(); // https://learn.microsoft.com/en-us/windows/win32/msi/standard-installer-command-line-options if (context.Args.Contains(Execution::Args::Type::Silent)) { args += " /quiet /norestart"; } else if (!context.Args.Contains(Execution::Args::Type::Interactive)) { args += " /passive /norestart"; } return args; } } void ShellExecuteInstallImpl(Execution::Context& context) { bool isRepair = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseRepair); if (isRepair) { context.Reporter.Info() << Resource::String::RepairFlowStartingPackageRepair << std::endl; } else { context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; } const auto& installer = context.Get(); const std::string& installerArgs = context.Get(); // Inform of elevation requirements bool isElevated = Runtime::IsRunningAsAdmin(); // The installer will run elevated, either by direct request or through the installer itself doing so. if ((installer->ElevationRequirement == ElevationRequirementEnum::ElevationRequired || installer->ElevationRequirement == ElevationRequirementEnum::ElevatesSelf) && !isElevated) { context.Reporter.Warn() << Resource::String::InstallerElevationExpected << std::endl; } // Some installers force UI. Setting to SW_HIDE will hide installer UI and installation will never complete. // Verified setting to SW_SHOW does not hurt silent mode since no UI will be shown. auto installResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecuteEx, context.Get(), installerArgs, installer->ElevationRequirement == ElevationRequirementEnum::ElevationRequired && !isElevated, SW_SHOW, std::placeholders::_1)); if (!installResult) { if (isRepair) { context.Reporter.Warn() << Resource::String::RepairAbandoned << std::endl; } else { context.Reporter.Warn() << Resource::String::InstallAbandoned << std::endl; } AICLI_TERMINATE_CONTEXT(E_ABORT); } else { context.Add(installResult.value()); } } void GetInstallerArgs(Execution::Context& context) { // If override switch is specified, use the override value as installer args. if (context.Args.Contains(Execution::Args::Type::Override)) { context.Add(std::string{ context.Args.GetArg(Execution::Args::Type::Override) }); return; } std::string installerArgs = GetInstallerArgsTemplate(context); PopulateInstallerArgsTemplate(context, installerArgs); AICLI_LOG(CLI, Info, << "Installer args: " << installerArgs); context.Add(std::move(installerArgs)); } void ShellExecuteUninstallImpl(Execution::Context& context) { context.Reporter.Info() << Resource::String::UninstallFlowStartingPackageUninstall << std::endl; std::wstring commandUtf16 = Utility::ConvertToUTF16(context.Get()); // Parse the command string as application and command line for CreateProcess wil::unique_cotaskmem_string app = nullptr; wil::unique_cotaskmem_string args = nullptr; THROW_IF_FAILED(SHEvaluateSystemCommandTemplate(commandUtf16.c_str(), &app, NULL, &args)); auto uninstallResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecute, std::filesystem::path(app.get()), Utility::ConvertToUTF8(args.get()), std::placeholders::_1)); if (!uninstallResult) { context.Reporter.Warn() << Resource::String::UninstallAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } else { context.Add(uninstallResult.value()); } } void ShellExecuteRepairImpl(Execution::Context& context) { context.Reporter.Info() << Resource::String::RepairFlowStartingPackageRepair << std::endl; std::wstring commandUtf16 = Utility::ConvertToUTF16(context.Get()); // When running as admin, block attempt to repair user scope installed package. // [NOTE:] This check is to address the security concern related to above scenario. if (Runtime::IsRunningAsAdmin()) { auto installedPackageVersion = context.Get(); const std::string installedScopeString = installedPackageVersion->GetMetadata()[PackageVersionMetadata::InstalledScope]; auto scopeEnum = ConvertToScopeEnum(installedScopeString); if (scopeEnum == ScopeEnum::User) { context.Reporter.Error() << Resource::String::NoAdminRepairForUserScopePackage << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED); } } // Parse the command string as application and command line for CreateProcess wil::unique_cotaskmem_string app = nullptr; wil::unique_cotaskmem_string args = nullptr; THROW_IF_FAILED(SHEvaluateSystemCommandTemplate(commandUtf16.c_str(), &app, NULL, &args)); auto repairResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecute, std::filesystem::path(app.get()), Utility::ConvertToUTF8(args.get()), std::placeholders::_1)); if (!repairResult) { context.Reporter.Error() << Resource::String::RepairAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } else { context.Add(repairResult.value()); } } void ShellExecuteMsiExecUninstall(Execution::Context& context) { const auto& productCodes = context.Get(); context.Reporter.Info() << Resource::String::UninstallFlowStartingPackageUninstall << std::endl; const std::filesystem::path msiexecPath{ ExpandEnvironmentVariables(L"%windir%\\system32\\msiexec.exe") }; for (const auto& productCode : productCodes) { AICLI_LOG(CLI, Info, << "Removing: " << productCode); auto uninstallResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecute, msiexecPath, GetMsiExecUninstallArgs(context, productCode), std::placeholders::_1)); if (!uninstallResult) { context.Reporter.Error() << Resource::String::UninstallAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } else { context.Add(uninstallResult.value()); } } } void ShellExecuteMsiExecRepair(Execution::Context& context) { const auto& productCodes = context.Get(); context.Reporter.Info() << Resource::String::RepairFlowStartingPackageRepair << std::endl; const std::filesystem::path msiexecPath{ ExpandEnvironmentVariables(L"%windir%\\system32\\msiexec.exe") }; for (const auto& productCode : productCodes) { AICLI_LOG(CLI, Info, << "Repairing: " << productCode); auto repairResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecute, msiexecPath, GetMsiExecRepairArgs(context, productCode), std::placeholders::_1)); if (!repairResult) { context.Reporter.Error() << Resource::String::RepairAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } else { context.Add(repairResult.value()); } } } #ifndef AICLI_DISABLE_TEST_HOOKS std::optional s_EnableWindowsFeatureResult_Override{}; void TestHook_SetEnableWindowsFeatureResult_Override(std::optional&& result) { s_EnableWindowsFeatureResult_Override = std::move(result); } std::optional s_DoesWindowsFeatureExistResult_Override{}; void TestHook_SetDoesWindowsFeatureExistResult_Override(std::optional&& result) { s_DoesWindowsFeatureExistResult_Override = std::move(result); } #endif std::filesystem::path GetDismExecutablePath() { return AppInstaller::Filesystem::GetExpandedPath("%windir%\\system32\\dism.exe"); } std::optional DoesWindowsFeatureExist(Execution::Context& context, std::string_view featureName) { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_DoesWindowsFeatureExistResult_Override) { return s_DoesWindowsFeatureExistResult_Override; } #endif std::string args = "/Online /Get-FeatureInfo /FeatureName:" + std::string{ featureName }; auto dismExecPath = GetDismExecutablePath(); auto getFeatureInfoResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecuteEx, dismExecPath, args, false, SW_HIDE, std::placeholders::_1)); return getFeatureInfoResult; } std::optional EnableWindowsFeature(Execution::Context& context, std::string_view featureName) { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_EnableWindowsFeatureResult_Override) { return s_EnableWindowsFeatureResult_Override; } #endif std::string args = "/Online /Enable-Feature /NoRestart /FeatureName:" + std::string{ featureName }; auto dismExecPath = GetDismExecutablePath(); AICLI_LOG(Core, Info, << "Enabling Windows Feature [" << featureName << "]"); auto enableFeatureResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecuteEx, dismExecPath, args, false, SW_HIDE, std::placeholders::_1)); return enableFeatureResult; } void ShellExecuteEnableWindowsFeature::operator()(Execution::Context& context) const { Utility::LocIndView locIndFeatureName{ m_featureName }; std::optional doesFeatureExistResult = DoesWindowsFeatureExist(context, m_featureName); if (!doesFeatureExistResult) { AICLI_TERMINATE_CONTEXT(E_ABORT); } else if (doesFeatureExistResult.value() != ERROR_SUCCESS) { context.Add(doesFeatureExistResult.value()); return; } context.Reporter.Info() << Resource::String::EnablingWindowsFeature(locIndFeatureName) << std::endl; std::optional enableFeatureResult = EnableWindowsFeature(context, m_featureName); if (!enableFeatureResult) { AICLI_TERMINATE_CONTEXT(E_ABORT); } else { context.Add(enableFeatureResult.value()); } } #ifndef AICLI_DISABLE_TEST_HOOKS std::optional s_ExtractArchiveWithTarResult_Override{}; void TestHook_SetExtractArchiveWithTarResult_Override(std::optional&& result) { s_ExtractArchiveWithTarResult_Override = std::move(result); } #endif void ShellExecuteExtractArchive::operator()(Execution::Context& context) const { auto tarExecPath = AppInstaller::Filesystem::GetExpandedPath("%windir%\\system32\\tar.exe"); std::string args = "-xf \"" + m_archivePath.u8string() + "\" -C \"" + m_destPath.u8string() + "\""; std::optional extractArchiveResult; #ifndef AICLI_DISABLE_TEST_HOOKS if (s_ExtractArchiveWithTarResult_Override) { extractArchiveResult = *s_ExtractArchiveWithTarResult_Override; } else #endif { extractArchiveResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecuteEx, tarExecPath, args, false, SW_HIDE, std::placeholders::_1)); } if (!extractArchiveResult) { AICLI_TERMINATE_CONTEXT(E_ABORT); } if (extractArchiveResult.value() == ERROR_SUCCESS) { AICLI_LOG(CLI, Info, << "Successfully extracted archive"); context.Reporter.Info() << Resource::String::ExtractArchiveSucceeded << std::endl; } else { AICLI_LOG(CLI, Info, << "Failed to extract archive with exit code " << extractArchiveResult.value()); context.Reporter.Error() << Resource::String::ExtractArchiveFailed << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXTRACT_ARCHIVE_FAILED); } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "ExecutionContext.h" #include #include // ShellExecuteInstallerHandler handles installers run through ShellExecute. // Exe, Wix, Nullsoft, Msi and Inno should be handled by this installer handler. namespace AppInstaller::CLI::Workflow { // Install is done through invoking ShellExecute on downloaded installer. // Required Args: None // Inputs: Manifest?, InstallerPath, InstallerArgs // Outputs: OperationReturnCode void ShellExecuteInstallImpl(Execution::Context& context); // Uninstall is done through invoking ShellExecute on uninstall string. // Required Args: None // Inputs: UninstallString // Outputs: OperationReturnCode void ShellExecuteUninstallImpl(Execution::Context& context); // Removes the MSI // Required Args: None // Inputs: ProductCodes // Output: None void ShellExecuteMsiExecUninstall(Execution::Context& context); // Gets the installer args from the context. // Required Args: None // Inputs: Manifest?, Installer, InstallerPath // Outputs: InstallerArgs void GetInstallerArgs(Execution::Context& context); // Repair is done through invoking ShellExecute on downloaded installer. // Required Args: None // Inputs: Manifest?, InstallerPath, InstallerArgs // Outputs: OperationReturnCode void ShellExecuteRepairImpl(Execution::Context& context); // Repair the MSI // Required Args: None // Inputs: ProductCodes // Output: None void ShellExecuteMsiExecRepair(Execution::Context& context); // Enables the Windows Feature dependency by invoking ShellExecute on the DISM executable. // Required Args: None // Inputs: Windows Feature dependency // Outputs: None struct ShellExecuteEnableWindowsFeature : public WorkflowTask { ShellExecuteEnableWindowsFeature(std::string_view featureName) : WorkflowTask("ShellExecuteEnableWindowsFeature"), m_featureName(featureName) {} void operator()(Execution::Context& context) const override; private: std::string_view m_featureName; }; // Extracts the installer archive using the tar executable. // Required Args: None // Inputs: InstallerPath // Outputs: None struct ShellExecuteExtractArchive : public WorkflowTask { ShellExecuteExtractArchive(const std::filesystem::path& archivePath, const std::filesystem::path& destPath) : WorkflowTask("ShellExecuteExtractArchive"), m_archivePath(archivePath), m_destPath(destPath) {} void operator()(Execution::Context& context) const override; private: std::filesystem::path m_archivePath; std::filesystem::path m_destPath; }; } ================================================ FILE: src/AppInstallerCLICore/Workflows/ShowFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ShowFlow.h" #include #include "TableOutput.h" using namespace AppInstaller::Repository; using namespace AppInstaller::CLI; using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Workflow { namespace { void ShowSingleLineField(Execution::OutputStream& outputStream, StringResource::StringId label, const Manifest::Manifest::string_t& value, bool indent = false) { Workflow::ShowSingleLineField(outputStream, label, LocIndView{ value }, indent ? 1 : 0); } void ShowMultiLineField(Execution::OutputStream& outputStream, StringResource::StringId label, const Manifest::Manifest::string_t& value) { Workflow::ShowMultiLineField(outputStream, label, LocIndView{ value }); } void ShowAgreements(Execution::OutputStream& outputStream, const std::vector& agreements) { if (agreements.empty()) { return; } outputStream << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelAgreements << std::endl; for (const auto& agreement : agreements) { if (!agreement.Label.empty()) { outputStream << " "_liv << Execution::ManifestInfoEmphasis << agreement.Label << ": "_liv; } if (!agreement.AgreementText.empty()) { outputStream << agreement.AgreementText << std::endl; } if (!agreement.AgreementUrl.empty()) { outputStream << agreement.AgreementUrl << std::endl; } } } } namespace details { LocIndView GetIndentFor(size_t indentLevel) { static constexpr std::array s_indents{ ""_liv, " "_liv, " "_liv, " "_liv }; return s_indents.at(indentLevel); } } void ShowAgreementsInfo(Execution::Context& context) { const auto& manifest = context.Get(); auto info = context.Reporter.Info(); ShowSingleLineField(info, Resource::String::ShowLabelVersion, manifest.Version); ShowSingleLineField(info, Resource::String::ShowLabelPublisher, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPublisherUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPublisherSupportUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelAuthor, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPackageUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelLicense, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelLicenseUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPrivacyUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelCopyright, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelCopyrightUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPurchaseUrl, manifest.CurrentLocalization.Get()); ShowAgreements(info, manifest.CurrentLocalization.Get()); } void ShowManifestInfo(Execution::Context& context) { context << ShowPackageInfo << ShowInstallerInfo; } void ShowPackageInfo(Execution::Context& context) { const auto& manifest = context.Get(); auto info = context.Reporter.Info(); // Get description from manifest so we can see if it is empty later auto description = manifest.CurrentLocalization.Get(); // TODO: Come up with a prettier format ShowSingleLineField(info, Resource::String::ShowLabelVersion, manifest.Version); ShowSingleLineField(info, Resource::String::ShowLabelPublisher, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPublisherUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPublisherSupportUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelAuthor, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelMoniker, manifest.Moniker); ShowMultiLineField(info, Resource::String::ShowLabelDescription, description.empty() ? manifest.CurrentLocalization.Get() : description); ShowSingleLineField(info, Resource::String::ShowLabelPackageUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelLicense, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelLicenseUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPrivacyUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelCopyright, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelCopyrightUrl, manifest.CurrentLocalization.Get()); ShowMultiLineField(info, Resource::String::ShowLabelReleaseNotes, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelReleaseNotesUrl, manifest.CurrentLocalization.Get()); ShowSingleLineField(info, Resource::String::ShowLabelPurchaseUrl, manifest.CurrentLocalization.Get()); ShowMultiLineField(info, Resource::String::ShowLabelInstallationNotes, manifest.CurrentLocalization.Get()); const auto& documentations = manifest.CurrentLocalization.Get(); if (!documentations.empty()) { context.Reporter.Info() << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelDocumentation << std::endl; for (const auto& documentation : documentations) { if (!documentation.DocumentUrl.empty()) { info << " "_liv; if (!documentation.DocumentLabel.empty()) { info << Execution::ManifestInfoEmphasis << documentation.DocumentLabel << ": "_liv; } info << documentation.DocumentUrl << std::endl; } } } ShowMultiValueField(info, Resource::String::ShowLabelTags, manifest.CurrentLocalization.Get()); ShowAgreements(info, manifest.CurrentLocalization.Get()); } void ShowInstallerInfo(Execution::Context& context) { const auto& installer = context.Get(); auto info = context.Reporter.Info(); info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstaller << std::endl; if (installer) { Manifest::InstallerTypeEnum effectiveInstallerType = installer->EffectiveInstallerType(); Manifest::InstallerTypeEnum baseInstallerType = installer->BaseInstallerType; std::string shownInstallerType; shownInstallerType = Manifest::InstallerTypeToString(effectiveInstallerType); if (effectiveInstallerType != baseInstallerType) { shownInstallerType += " ("_liv; shownInstallerType += Manifest::InstallerTypeToString(baseInstallerType); shownInstallerType += ')'; } ShowSingleLineField(info, Resource::String::ShowLabelInstallerType, shownInstallerType, true); ShowSingleLineField(info, Resource::String::ShowLabelInstallerLocale, installer->Locale, true); ShowSingleLineField(info, Resource::String::ShowLabelInstallerUrl, installer->Url, true); ShowSingleLineField(info, Resource::String::ShowLabelInstallerSha256, (installer->Sha256.empty()) ? "" : Utility::SHA256::ConvertToString(installer->Sha256), true); ShowSingleLineField(info, Resource::String::ShowLabelInstallerProductId, installer->ProductId, true); ShowSingleLineField(info, Resource::String::ShowLabelInstallerReleaseDate, installer->ReleaseDate, true); ShowSingleLineField(info, Resource::String::ShowLabelInstallerOfflineDistributionSupported, Utility::ConvertBoolToString(!installer->DownloadCommandProhibited), true); const auto& dependencies = installer->Dependencies; if (dependencies.HasAny()) { info << Execution::ManifestInfoEmphasis << " "_liv << Resource::String::ShowLabelDependencies << ' ' << std::endl; if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsFeature)) { info << " - "_liv << Resource::String::ShowLabelWindowsFeaturesDependencies << ' ' << std::endl; dependencies.ApplyToType(Manifest::DependencyType::WindowsFeature, [&info](Manifest::Dependency dependency) {info << " "_liv << dependency.Id() << std::endl; }); } if (dependencies.HasAnyOf(Manifest::DependencyType::WindowsLibrary)) { info << " - "_liv << Resource::String::ShowLabelWindowsLibrariesDependencies << ' ' << std::endl; dependencies.ApplyToType(Manifest::DependencyType::WindowsLibrary, [&info](Manifest::Dependency dependency) {info << " "_liv << dependency.Id() << std::endl; }); } if (dependencies.HasAnyOf(Manifest::DependencyType::Package)) { info << " - "_liv << Resource::String::ShowLabelPackageDependencies << ' ' << std::endl; dependencies.ApplyToType(Manifest::DependencyType::Package, [&info](Manifest::Dependency dependency) { info << " "_liv << dependency.Id(); if (dependency.MinVersion) { info << " [>= " << dependency.MinVersion.value().ToString() << "]"; } info << std::endl; }); } if (dependencies.HasAnyOf(Manifest::DependencyType::External)) { info << " - "_liv << Resource::String::ShowLabelExternalDependencies << ' ' << std::endl; dependencies.ApplyToType(Manifest::DependencyType::External, [&info](Manifest::Dependency dependency) {info << " "_liv << dependency.Id() << std::endl; }); } } } else { context.Reporter.Warn() << " "_liv << Resource::String::NoApplicableInstallers << std::endl; } } void ShowManifestVersion(Execution::Context& context) { const auto& manifest = context.Get(); Execution::TableOutput<2> table(context.Reporter, { Resource::String::ShowVersion, Resource::String::ShowChannel }); table.OutputLine({ manifest.Version, manifest.Channel }); table.Complete(); } void GetManifest::operator()(Execution::Context& context) const { if (context.Args.Contains(Execution::Args::Type::Manifest)) { context << GetManifestFromArg; } else { context << OpenSource() << SearchSourceForSingle << HandleSearchResultFailures << EnsureOneMatchFromSearchResult(OperationType::Show) << GetManifestFromPackage(m_considerPins); } } void ShowSingleLineField(Execution::OutputStream& outputStream, StringResource::StringId label, LocIndView value, size_t indentLevel) { if (value.empty()) { return; } outputStream << details::GetIndentFor(indentLevel) << Execution::ManifestInfoEmphasis << label << ' ' << value << '\n'; } void ShowMultiLineField(Execution::OutputStream& outputStream, StringResource::StringId label, LocIndView value, size_t indentLevel) { if (value.empty()) { return; } // Treat the lines as separate values for the field ShowMultiValueField(outputStream, label, Split(value, '\n'), indentLevel); } } ================================================ FILE: src/AppInstallerCLICore/Workflows/ShowFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" #include #include #include namespace AppInstaller::CLI::Workflow { // Shows information on an application; this is only the information for package agreements // Required Args: None // Inputs: Manifest // Outputs: None void ShowAgreementsInfo(Execution::Context& context); // Shows information on an application. // Required Args: None // Inputs: Manifest, Installer // Outputs: None void ShowManifestInfo(Execution::Context& context); // Shows information on a package; this is only the information common to all installers. // Required Args: None // Inputs: Manifest // Outputs: None void ShowPackageInfo(Execution::Context& context); // Shows information on an installer // Required Args: None // Inputs: Installer // Outputs: None void ShowInstallerInfo(Execution::Context& context); // Shows the version for the specific manifest. // Required Args: None // Inputs: Manifest // Outputs: None void ShowManifestVersion(Execution::Context& context); // Composite flow that produces a manifest; either from one given on the command line or by searching. // Required Args: None // Inputs: None // Outputs: Manifest struct GetManifest : public WorkflowTask { GetManifest(bool considerPins) : WorkflowTask("GetManifest"), m_considerPins(considerPins) {} void operator()(Execution::Context& context) const override; private: bool m_considerPins; }; // Reusable helpers for `show` style line output namespace details { Utility::LocIndView GetIndentFor(size_t i); } void ShowSingleLineField(Execution::OutputStream& outputStream, StringResource::StringId label, Utility::LocIndView value, size_t indentLevel = 0); void ShowMultiLineField(Execution::OutputStream& outputStream, StringResource::StringId label, Utility::LocIndView value, size_t indentLevel = 0); template void ShowMultiValueField(Execution::OutputStream& outputStream, StringResource::StringId label, const Container& values, size_t indentLevel = 0) { if (values.empty()) { return; } bool isMultiItem = values.size() > 1; outputStream << details::GetIndentFor(indentLevel) << Execution::ManifestInfoEmphasis << label; outputStream << (isMultiItem ? '\n' : ' '); for (const auto& value : values) { outputStream << details::GetIndentFor(isMultiItem ? indentLevel + 1 : 0) << value << '\n'; } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/SourceFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Resources.h" #include "SourceFlow.h" #include "PromptFlow.h" #include "TableOutput.h" #include "WorkflowBase.h" namespace AppInstaller::CLI::Workflow { using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility::literals; void GetSourceList(Execution::Context& context) { context.Add(Repository::Source::GetCurrentSources()); } void GetSourceListWithFilter(Execution::Context& context) { auto currentSources = Repository::Source::GetCurrentSources(); if (context.Args.Contains(Args::Type::SourceName)) { auto name = Utility::LocIndString{ context.Args.GetArg(Args::Type::SourceName) }; for (auto const& source : currentSources) { if (Utility::ICUCaseInsensitiveEquals(source.Name, name)) { std::vector sources; sources.emplace_back(source); context.Add(std::move(sources)); return; } } context.Reporter.Error() << Resource::String::SourceListNoneFound(name) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } else { context.Add(std::move(currentSources)); } } void CheckSourceListAgainstAdd(Execution::Context& context) { auto sourceList = context.Get(); std::string_view name = context.Args.GetArg(Args::Type::SourceName); std::string_view arg = context.Args.GetArg(Args::Type::SourceArg); std::string_view type = context.Args.GetArg(Args::Type::SourceType); // In the absence of a specified type, the default is Microsoft.PreIndexed.Package for comparison. // The default type assignment to the source takes place during the add operation (Source::Add in Repository.cpp). // This is necessary for the comparison to function correctly; otherwise, it would allow the addition of multiple // sources with different names but the same argument for all default type cases. // For example, the following commands would be allowed, but they acts as different alias to same source: // winget source add "mysource1" "https:\\mysource" --trust - level trusted // winget source add "mysource2" "https:\\mysource" --trust - level trusted if (type.empty()) { type = Repository::Source::GetDefaultSourceType(); } for (const auto& details : sourceList) { if (Utility::ICUCaseInsensitiveEquals(details.Name, name)) { if (details.Arg == arg) { // Name and arg match, indicate this to the user and bail. context.Reporter.Info() << Resource::String::SourceAddAlreadyExistsMatch << std::endl << " "_liv << details.Name << " -> "_liv << details.Arg << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS); } else { context.Reporter.Error() << Resource::String::SourceAddAlreadyExistsDifferentArg << std::endl << " "_liv << details.Name << " -> "_liv << details.Arg << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS); } } if (!details.Arg.empty() && details.Arg == arg && details.Type == type) { context.Reporter.Error() << Resource::String::SourceAddAlreadyExistsDifferentName << std::endl << " "_liv << details.Name << " -> "_liv << details.Arg << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_ARG_ALREADY_EXISTS); } } } void AddSource(Execution::Context& context) { auto& sourceToAdd = context.Get(); auto details = sourceToAdd.GetDetails(); context.Reporter.Info() << Resource::String::SourceAddBegin << std::endl << " "_liv << details.Name << " -> "_liv << details.Arg << std::endl; auto addFunction = [&](IProgressCallback& progress)->bool { return sourceToAdd.Add(progress); }; if (!context.Reporter.ExecuteWithProgress(addFunction)) { context.Reporter.Info() << Resource::String::Cancelled << std::endl; } else { context.Reporter.Info() << Resource::String::Done << std::endl; } } void CreateSourceForSourceAdd(Execution::Context& context) { try { std::string_view name = context.Args.GetArg(Args::Type::SourceName); std::string_view arg = context.Args.GetArg(Args::Type::SourceArg); std::string_view type = context.Args.GetArg(Args::Type::SourceType); Repository::SourceTrustLevel trustLevel = Repository::SourceTrustLevel::None; if (context.Args.Contains(Execution::Args::Type::SourceTrustLevel)) { std::vector trustLevelArgs = Utility::Split(std::string{ context.Args.GetArg(Execution::Args::Type::SourceTrustLevel) }, '|', true); trustLevel = Repository::ConvertToSourceTrustLevelFlag(trustLevelArgs); } Repository::SourceEdit additionalProperties; if (context.Args.Contains(Args::Type::SourceExplicit)) { additionalProperties.Explicit = true; } if (context.Args.Contains(Args::Type::SourcePriority)) { additionalProperties.Priority = Utility::TryConvertStringToInt32(context.Args.GetArg(Args::Type::SourcePriority)); } Repository::Source sourceToAdd{ name, arg, type, trustLevel, additionalProperties}; if (context.Args.Contains(Execution::Args::Type::CustomHeader)) { std::string customHeader{ context.Args.GetArg(Execution::Args::Type::CustomHeader) }; if (!sourceToAdd.SetCustomHeader(customHeader)) { context.Reporter.Warn() << Resource::String::HeaderArgumentNotApplicableForNonRestSourceWarning << std::endl; } } if (sourceToAdd.GetInformation().Authentication.Type == Authentication::AuthenticationType::Unknown) { context.Reporter.Error() << Resource::String::SourceAddFailedAuthenticationNotSupported << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED); } context << Workflow::HandleSourceAgreements(sourceToAdd); if (context.IsTerminated()) { return; } context.Add(std::move(sourceToAdd)); } catch (...) { context.Reporter.Error() << Resource::String::SourceAddOpenSourceFailed << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED); } } void ListSources(Execution::Context& context) { const std::vector& sources = context.Get(); if (context.Args.Contains(Args::Type::SourceName)) { // If a source name was specified, list full details of the one and only source. const Repository::SourceDetails& source = sources[0]; Execution::TableOutput<2> table(context.Reporter, { Resource::String::SourceListField, Resource::String::SourceListValue }); table.OutputLine({ Resource::LocString(Resource::String::SourceListName), source.Name }); table.OutputLine({ Resource::LocString(Resource::String::SourceListType), source.Type }); table.OutputLine({ Resource::LocString(Resource::String::SourceListArg), source.Arg }); table.OutputLine({ Resource::LocString(Resource::String::SourceListData), source.Data }); table.OutputLine({ Resource::LocString(Resource::String::SourceListIdentifier), source.Identifier }); table.OutputLine({ Resource::LocString(Resource::String::SourceListTrustLevel), Repository::GetSourceTrustLevelForDisplay(source.TrustLevel)}); table.OutputLine({ Resource::LocString(Resource::String::SourceListExplicit), std::string{ Utility::ConvertBoolToString(source.Explicit) } }); if (ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::SourcePriority)) { table.OutputLine({ Resource::LocString(Resource::String::SourceListPriority), std::to_string(source.Priority) }); } if (source.LastUpdateTime == Utility::ConvertUnixEpochToSystemClock(0)) { table.OutputLine({ Resource::LocString(Resource::String::SourceListUpdated), Resource::LocString(Resource::String::SourceListUpdatedNever) }); } else { std::ostringstream strstr; strstr << source.LastUpdateTime; table.OutputLine({ Resource::LocString(Resource::String::SourceListUpdated), strstr.str() }); } table.Complete(); } else { if (sources.empty()) { context.Reporter.Info() << Resource::String::SourceListNoSources << std::endl; } else { Execution::TableOutput<3> table(context.Reporter, { Resource::String::SourceListName, Resource::String::SourceListArg, Resource::String::SourceListExplicit }); for (const auto& source : sources) { table.OutputLine({ source.Name, source.Arg, std::string{ Utility::ConvertBoolToString(source.Explicit) }}); } table.Complete(); } } } void UpdateSources(Execution::Context& context) { if (!context.Args.Contains(Args::Type::SourceName)) { context.Reporter.Info() << Resource::String::SourceUpdateAll << std::endl; } const std::vector& sources = context.Get(); for (const auto& sd : sources) { Repository::Source source{ sd.Name }; context.Reporter.Info() << Resource::String::SourceUpdateOne(Utility::LocIndView{ sd.Name }) << std::endl; auto updateFunction = [&](IProgressCallback& progress)->std::vector { return source.Update(progress); }; auto sourceDetails = context.Reporter.ExecuteWithProgress(updateFunction); if (!sourceDetails.empty()) { if (std::chrono::system_clock::now() < sourceDetails[0].DoNotUpdateBefore) { context.Reporter.Warn() << Resource::String::Unavailable << std::endl; } else { context.Reporter.Info() << Resource::String::Cancelled << std::endl; } } else { context.Reporter.Info() << Resource::String::Done << std::endl; } } } void RemoveSources(Execution::Context& context) { // TODO: We currently only allow removing a single source. If that changes, // we need to check all sources with the Group Policy before removing any of them. if (!context.Args.Contains(Args::Type::SourceName)) { context.Reporter.Info() << Resource::String::SourceRemoveAll << std::endl; } const std::vector& sources = context.Get(); for (const auto& sd : sources) { Repository::Source source{ sd.Name }; context.Reporter.Info() << Resource::String::SourceRemoveOne(Utility::LocIndView{ sd.Name }) << std::endl; auto removeFunction = [&](IProgressCallback& progress)->bool { return source.Remove(progress); }; if (context.Reporter.ExecuteWithProgress(removeFunction)) { context.Reporter.Info() << Resource::String::Done << std::endl; } else { context.Reporter.Info() << Resource::String::Cancelled << std::endl; } } } void EditSources(Execution::Context& context) { // We are assuming there is only one match, as SourceName is a required parameter. const std::vector& sources = context.Get(); for (const auto& sd : sources) { // Get the current source with this name. Repository::Source targetSource{ sd.Name }; auto oldExplicitValue = sd.Explicit; auto oldPriorityValue = sd.Priority; Repository::SourceEdit edits; if (context.Args.Contains(Execution::Args::Type::SourceEditExplicit)) { edits.Explicit = Utility::TryConvertStringToBool(context.Args.GetArg(Execution::Args::Type::SourceEditExplicit)); } if (context.Args.Contains(Execution::Args::Type::SourcePriority)) { edits.Priority = Utility::TryConvertStringToInt32(context.Args.GetArg(Execution::Args::Type::SourcePriority)); } if (!targetSource.RequiresChanges(edits)) { context.Reporter.Info() << Resource::String::SourceEditNoChanges(Utility::LocIndView{ sd.Name }) << std::endl; continue; } context.Reporter.Info() << Resource::String::SourceEditOne(Utility::LocIndView{ sd.Name }) << std::endl; targetSource.Edit(edits); // Output changed source information table. The name of the source being edited is listed prior to the edits. Execution::TableOutput<3> table(context.Reporter, { Resource::String::SourceListField, Resource::String::SourceEditOldValue, Resource::String::SourceEditNewValue }); if (edits.Explicit) { table.OutputLine({ Resource::LocString(Resource::String::SourceListExplicit), std::string{ Utility::ConvertBoolToString(oldExplicitValue) }, std::string{ Utility::ConvertBoolToString(edits.Explicit.value()) } }); } if (edits.Priority) { table.OutputLine({ Resource::LocString(Resource::String::SourceListPriority), std::to_string(oldPriorityValue), std::to_string(edits.Priority.value()) }); } table.Complete(); } } void QueryUserForSourceReset(Execution::Context& context) { if (!context.Args.Contains(Execution::Args::Type::ForceSourceReset)) { context << GetSourceListWithFilter; const std::vector& sources = context.Get(); if (!sources.empty()) { context.Reporter.Info() << Resource::String::SourceResetListAndOverridePreamble << std::endl; context << ListSources; AICLI_TERMINATE_CONTEXT(E_ABORT); } } } void ResetSourceList(Execution::Context& context) { const std::vector& sources = context.Get(); for (const auto& source : sources) { context.Reporter.Info() << Resource::String::SourceResetOne(Utility::LocIndView{ source.Name }); Repository::Source::DropSource(source.Name); context.Reporter.Info() << Resource::String::Done << std::endl; } } void ResetAllSources(Execution::Context& context) { context.Reporter.Info() << Resource::String::SourceResetAll; Repository::Source::DropSource({}); context.Reporter.Info() << Resource::String::Done << std::endl; } void ExportSourceList(Execution::Context& context) { const std::vector& sources = context.Get(); if (sources.empty()) { context.Reporter.Info() << Resource::String::SourceListNoSources << std::endl; } else { for (const auto& source : sources) { SourceFromPolicy s; s.Name = source.Name; s.Type = source.Type; s.Arg = source.Arg; s.Data = source.Data; s.Identifier = source.Identifier; std::vector sourceTrustLevels = Repository::SourceTrustLevelFlagToList(source.TrustLevel); s.TrustLevel = std::vector(sourceTrustLevels.begin(), sourceTrustLevels.end()); s.Explicit = source.Explicit; if (ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::SourcePriority)) { s.Priority = source.Priority; } context.Reporter.Info() << s.ToJsonString() << std::endl; } } } void ForceInstalledCacheUpdate(Execution::Context&) { // Creating this object is currently sufficient to mark the cache as needing an update for the next time it is opened. Repository::Source ignore{ Repository::PredefinedSource::InstalledForceCacheUpdate }; } } ================================================ FILE: src/AppInstallerCLICore/Workflows/SourceFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Command.h" #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Gets the current source list. // Required Args: None // Inputs: None // Outputs: SourceList void GetSourceList(Execution::Context& context); // Gets the source list, filtering it if SourceName is present. // Required Args: None // Inputs: None // Outputs: SourceList void GetSourceListWithFilter(Execution::Context& context); // Checks the source list against the inputs to ensure a successful add after this. // Required Args: SourceName, SourceArg // Inputs: SourceList // Outputs: None void CheckSourceListAgainstAdd(Execution::Context& context); // Adds the source. // Required Args: SourceName, SourceArg // Inputs: None // Outputs: None void AddSource(Execution::Context& context); // Opens a source before source add command. // Required Args: SourceName, SourceArg // Inputs: None // Outputs: Source void CreateSourceForSourceAdd(Execution::Context& context); // Lists the sources in SourceList. // Required Args: None // Inputs: SourceList // Outputs: None void ListSources(Execution::Context& context); // Updates the sources in SourceList. // Required Args: None // Inputs: SourceList // Outputs: None void UpdateSources(Execution::Context& context); // Removes the sources in SourceList. // Required Args: None // Inputs: SourceList // Outputs: None void RemoveSources(Execution::Context& context); // Asks the user if they are ok with dropping the sources in SourceList. // Required Args: None // Inputs: SourceList // Outputs: None void QueryUserForSourceReset(Execution::Context& context); // Drops the sources in SourceList. // Required Args: None // Inputs: SourceList // Outputs: None void ResetSourceList(Execution::Context& context); // Drops all sources. // Required Args: None // Inputs: None // Outputs: None void ResetAllSources(Execution::Context& context); // Lists the sources in SourceList in a format appropriate for using in Group Policy // Required Args: None // Inputs: SourceList // Outputs: None void ExportSourceList(Execution::Context& context); // Forces an update to the cache of installed packages. // Required Args: None // Inputs: None // Outputs: None void ForceInstalledCacheUpdate(Execution::Context& context); // Edits a source in SourceList. // Required Args: SourceName // Inputs: SourceList // Outputs: None void EditSources(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/UninstallFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "UninstallFlow.h" #include "InstallFlow.h" #include "FontFlow.h" #include "WorkflowBase.h" #include "DependenciesFlow.h" #include "ShellExecuteInstallerHandler.h" #include "AppInstallerMsixInfo.h" #include "PortableFlow.h" #include #include #include #include using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Manifest; using namespace AppInstaller::Msix; using namespace AppInstaller::Repository; using namespace AppInstaller::Registry; using namespace AppInstaller::CLI::Portable; using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Workflow { namespace { // Helper for RecordUninstall struct UninstallCorrelatedSources { struct Item { Utility::LocIndString Identifier; Source FromSource; std::string SourceIdentifier; }; void AddIfRemoteAndNotPresent(Source&& source, const Utility::LocIndString& identifier) { const auto details = source.GetDetails(); if (!source.ContainsAvailablePackages()) { return; } for (const auto& item : Items) { if (item.SourceIdentifier == details.Identifier) { return; } } Items.emplace_back(Item{ identifier, std::move(source), details.Identifier }); } void AddIfRemoteAndNotPresent(const std::shared_ptr& packageVersion) { AddIfRemoteAndNotPresent(packageVersion->GetSource(), packageVersion->GetProperty(PackageVersionProperty::Id)); } void AddIfRemoteAndNotPresent(const std::shared_ptr& package) { AddIfRemoteAndNotPresent(package->GetSource(), package->GetProperty(PackageProperty::Id)); } std::vector Items; }; } void UninstallSinglePackage(Execution::Context& context) { std::shared_ptr package = context.Get(); std::shared_ptr installed = package->GetInstalled(); std::vector installedVersionKeys; if (installed) { installedVersionKeys = installed->GetVersionKeys(); } // Handle multiple installed versions when we have been told to uninstall all of them. if (installedVersionKeys.size() > 1 && context.Args.Contains(Execution::Args::Type::AllVersions)) { bool allSucceeded = true; size_t versionsCount = installedVersionKeys.size(); size_t versionsProgress = 0; for (const auto& key : installedVersionKeys) { context.Reporter.Info() << '(' << ++versionsProgress << '/' << versionsCount << ") "_liv; // We want to do best effort to uninstall all versions regardless of previous failures auto subContextPtr = context.CreateSubContext(); Execution::Context& uninstallContext = *subContextPtr; auto previousThreadGlobals = uninstallContext.SetForCurrentThread(); uninstallContext.Add(package); uninstallContext.Add(installed->GetVersion(key)); // Prevent individual exceptions from breaking out of the loop try { uninstallContext << Workflow::UninstallSinglePackageVersion; } catch (...) { uninstallContext.SetTerminationHR(Workflow::HandleException(uninstallContext, std::current_exception())); } uninstallContext.Reporter.Info() << std::endl; if (uninstallContext.IsTerminated()) { if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) { // This means that the subcontext being terminated is due to an overall abort context.Reporter.Info() << Resource::String::Cancelled << std::endl; return; } allSucceeded = false; } } if (!allSucceeded) { AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_MULTIPLE_UNINSTALL_FAILED); } } else if (installedVersionKeys.size() > 1 && !context.Args.Contains(Execution::Args::Type::TargetVersion)) { context.Reporter.Error() << Resource::String::UninstallFailedDueToMultipleVersions << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND); } else { context << Workflow::GetInstalledPackageVersion << Workflow::UninstallSinglePackageVersion; } } void UninstallSinglePackageVersion(Execution::Context& context) { context << Workflow::ReportInstalledPackageVersionIdentity << Workflow::EnsureSupportForUninstall << Workflow::GetUninstallInfo << Workflow::GetDependenciesInfoForUninstall << Workflow::ReportDependencies(Resource::String::UninstallCommandReportDependencies) << Workflow::ReportExecutionStage(ExecutionStage::Execution) << Workflow::ExecuteUninstaller << Workflow::ReportExecutionStage(ExecutionStage::PostExecution) << Workflow::RecordUninstall; } void UninstallMultiplePackages(Execution::Context& context) { bool allSucceeded = true; size_t packagesCount = context.Get().size(); size_t packagesProgress = 0; for (auto& packageContext : context.Get()) { packagesProgress++; context.Reporter.Info() << '(' << packagesProgress << '/' << packagesCount << ") "_liv; // We want to do best effort to uninstall all packages regardless of previous failures Execution::Context& uninstallContext = *packageContext; auto previousThreadGlobals = uninstallContext.SetForCurrentThread(); // Prevent individual exceptions from breaking out of the loop try { uninstallContext << Workflow::ReportPackageIdentity << Workflow::UninstallSinglePackage; } catch (...) { uninstallContext.SetTerminationHR(Workflow::HandleException(uninstallContext, std::current_exception())); } uninstallContext.Reporter.Info() << std::endl; if (uninstallContext.IsTerminated()) { if (context.IsTerminated() && context.GetTerminationHR() == E_ABORT) { // This means that the subcontext being terminated is due to an overall abort context.Reporter.Info() << Resource::String::Cancelled << std::endl; return; } allSucceeded = false; } } if (!allSucceeded) { AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_MULTIPLE_UNINSTALL_FAILED); } } void GetUninstallInfo(Execution::Context& context) { auto installedPackageVersion = context.Get(); if (!installedPackageVersion) { AICLI_LOG(CLI, Verbose, << "No installed package version; cannot get uninstall information."); return; } const std::string installedTypeString = installedPackageVersion->GetMetadata()[PackageVersionMetadata::InstalledType]; switch (ConvertToInstallerTypeEnum(installedTypeString)) { case InstallerTypeEnum::Exe: case InstallerTypeEnum::Burn: case InstallerTypeEnum::Inno: case InstallerTypeEnum::Nullsoft: { IPackageVersion::Metadata packageMetadata = installedPackageVersion->GetMetadata(); // Default to silent unless it is not present or interactivity is requested auto uninstallCommandItr = packageMetadata.find(PackageVersionMetadata::SilentUninstallCommand); if (uninstallCommandItr == packageMetadata.end() || context.Args.Contains(Execution::Args::Type::Interactive)) { auto interactiveItr = packageMetadata.find(PackageVersionMetadata::StandardUninstallCommand); if (interactiveItr != packageMetadata.end()) { uninstallCommandItr = interactiveItr; } } if (uninstallCommandItr == packageMetadata.end()) { context.Reporter.Error() << Resource::String::NoUninstallInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND); } context.Add(uninstallCommandItr->second); break; } case InstallerTypeEnum::Msi: case InstallerTypeEnum::Wix: { // Uninstall strings for MSI don't include UI level (/q) needed to avoid interactivity, // so we handle them differently. auto productCodes = installedPackageVersion->GetMultiProperty(PackageVersionMultiProperty::ProductCode); if (productCodes.empty()) { context.Reporter.Error() << Resource::String::NoUninstallInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND); } context.Add(std::move(productCodes)); break; } case InstallerTypeEnum::Msix: case InstallerTypeEnum::MSStore: { auto packageFamilyNames = installedPackageVersion->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); if (packageFamilyNames.empty()) { context.Reporter.Error() << Resource::String::NoUninstallInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND); } context.Add(packageFamilyNames); break; } case InstallerTypeEnum::Portable: { auto productCodes = installedPackageVersion->GetMultiProperty(PackageVersionMultiProperty::ProductCode); if (productCodes.empty()) { context.Reporter.Error() << Resource::String::NoUninstallInfoFound << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND); } const std::string installedScope = context.Get()->GetMetadata()[Repository::PackageVersionMetadata::InstalledScope]; const std::string installedArch = context.Get()->GetMetadata()[Repository::PackageVersionMetadata::InstalledArchitecture]; PortableInstaller portableInstaller = PortableInstaller( Manifest::ConvertToScopeEnum(installedScope), Utility::ConvertToArchitectureEnum(installedArch), productCodes[0]); context.Add(std::move(portableInstaller)); break; } case InstallerTypeEnum::Font: break; default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } void ExecuteUninstaller(Execution::Context& context) { auto installedPackageVersion = context.Get(); if (!installedPackageVersion) { AICLI_LOG(CLI, Verbose, << "No installed package version; cannot uninstall."); return; } const std::string installedTypeString = installedPackageVersion->GetMetadata()[PackageVersionMetadata::InstalledType]; InstallerTypeEnum installerType = ConvertToInstallerTypeEnum(installedTypeString); Synchronization::CrossProcessInstallLock lock; if (!ExemptFromSingleInstallLocking(installerType)) { // Acquire install lock; if the operation is cancelled it will return false so we will also return. if (!context.Reporter.ExecuteWithProgress([&](IProgressCallback& callback) { callback.SetProgressMessage(Resource::String::InstallWaitingOnAnother()); return lock.Acquire(callback); })) { AICLI_LOG(CLI, Info, << "Abandoning attempt to acquire install lock due to cancellation"); return; } } switch (installerType) { case InstallerTypeEnum::Exe: case InstallerTypeEnum::Burn: case InstallerTypeEnum::Inno: case InstallerTypeEnum::Nullsoft: context << Workflow::ShellExecuteUninstallImpl << ReportUninstallerResult("UninstallString", APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED); break; case InstallerTypeEnum::Msi: case InstallerTypeEnum::Wix: context << Workflow::ShellExecuteMsiExecUninstall << ReportUninstallerResult("MsiExec", APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED); break; case InstallerTypeEnum::Msix: case InstallerTypeEnum::MSStore: context << Workflow::MsixUninstall; break; case InstallerTypeEnum::Portable: context << Workflow::PortableUninstallImpl << ReportUninstallerResult("PortableUninstall"sv, APPINSTALLER_CLI_ERROR_PORTABLE_UNINSTALL_FAILED, true); break; case InstallerTypeEnum::Font: context << Workflow::FontUninstallImpl << ReportUninstallerResult("Font"sv, APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED, true); break; default: THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } void MsixUninstall(Execution::Context& context) { bool isMachineScope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)) == Manifest::ScopeEnum::Machine; // TODO: There was a bug in deployment api if deprovision api was called in packaged context. // Remove this check when the OS bug is fixed and back ported. if (isMachineScope && Runtime::IsRunningInPackagedContext()) { context.Reporter.Error() << Resource::String::InstallFlowReturnCodeSystemNotSupported << std::endl; context.Add(static_cast(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED)); AICLI_LOG(CLI, Error, << "Device wide uninstall for msix type is not supported in packaged context."); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED); } const auto& packageFamilyNames = context.Get(); context.Reporter.Info() << Resource::String::UninstallFlowStartingPackageUninstall << std::endl; for (const auto& packageFamilyName : packageFamilyNames) { auto packageFullName = Msix::GetPackageFullNameFromFamilyName(packageFamilyName); if (!packageFullName.has_value()) { AICLI_LOG(CLI, Warning, << "No package found with family name: " << packageFamilyName); continue; } AICLI_LOG(CLI, Info, << "Removing MSIX package: " << packageFullName.value()); try { if (isMachineScope) { context.Reporter.ExecuteWithProgress(std::bind(Deployment::RemovePackageMachineScope, packageFamilyName, packageFullName.value(), std::placeholders::_1)); } else { context.Reporter.ExecuteWithProgress(std::bind(Deployment::RemovePackage, packageFullName.value(), winrt::Windows::Management::Deployment::RemovalOptions::None, std::placeholders::_1)); } } catch (const wil::ResultException& re) { context.Add(re.GetErrorCode()); context << ReportUninstallerResult("MSIXUninstall"sv, re.GetErrorCode(), /* isHResult */ true); return; } } context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; } void RecordUninstall(Context& context) { // In order to report an uninstall to every correlated tracking catalog, we first need to find them all. auto package = context.Get(); UninstallCorrelatedSources correlatedSources; // Start with the installed version correlatedSources.AddIfRemoteAndNotPresent(GetInstalledVersion(package)); // Then look through all available versions for (const auto& availablePackage : package->GetAvailable()) { correlatedSources.AddIfRemoteAndNotPresent(availablePackage); } // Finally record the uninstall for each found value for (const auto& item : correlatedSources.Items) { auto trackingCatalog = item.FromSource.GetTrackingCatalog(); trackingCatalog.RecordUninstall(item.Identifier); } } void ReportUninstallerResult::operator()(Execution::Context& context) const { DWORD uninstallResult = context.Get(); if (uninstallResult != 0) { const auto installedPackageVersion = context.Get(); Logging::Telemetry().LogUninstallerFailure( installedPackageVersion->GetProperty(PackageVersionProperty::Id), installedPackageVersion->GetProperty(PackageVersionProperty::Version), m_uninstallerType, uninstallResult); if (m_isHResult) { context.Reporter.Error() << Resource::String::UninstallFailedWithCode(Utility::LocIndView{ GetUserPresentableMessage(uninstallResult) }) << std::endl; } else { context.Reporter.Error() << Resource::String::UninstallFailedWithCode(uninstallResult) << std::endl; } // Show installer log path if exists if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get())) { auto installerLogPath = Utility::LocIndString{ context.Get().u8string() }; context.Reporter.Info() << Resource::String::InstallerLogAvailable(installerLogPath) << std::endl; } AICLI_TERMINATE_CONTEXT(m_hr); } else { context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; } } void EnsureSupportForUninstall(Execution::Context& context) { auto installedPackageVersion = context.Get(); const std::string installedTypeString = installedPackageVersion->GetMetadata()[PackageVersionMetadata::InstalledType]; auto installedType = ConvertToInstallerTypeEnum(installedTypeString); if (installedType == InstallerTypeEnum::Portable) { const std::string installedScope = installedPackageVersion->GetMetadata()[Repository::PackageVersionMetadata::InstalledScope]; if (Manifest::ConvertToScopeEnum(installedScope) == Manifest::ScopeEnum::Machine) { context << EnsureRunningAsAdmin; } } else if (installedType == InstallerTypeEnum::Msix) { if (Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)) == Manifest::ScopeEnum::Machine) { context << EnsureRunningAsAdmin; } } } } ================================================ FILE: src/AppInstallerCLICore/Workflows/UninstallFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" namespace AppInstaller::CLI::Workflow { // Uninstalls a single package. This also does the reporting, user interaction, and recording // for single-package uninstallation. // RequiredArgs: None // Inputs: Package // Outputs: None void UninstallSinglePackage(Execution::Context& context); // Uninstalls a single package version. This also does the reporting, user interaction, and recording // for single-package uninstallation. // RequiredArgs: None // Inputs: InstalledPackageVersion // Outputs: None void UninstallSinglePackageVersion(Execution::Context& context); // Uninstalls multiple packages. // RequiredArgs: None // Inputs: PackageSubContexts // Outputs: None // SubContext Inputs: Package // SubContext Outputs: None void UninstallMultiplePackages(Execution::Context& context); // Gets the command string or package family names used to uninstall the package. // Required Args: None // Inputs: InstalledPackageVersion // Output: UninstallString?, PackageFamilyNames? void GetUninstallInfo(Execution::Context& context); // Uninstalls the package according to its type. // Required Args: None // Inputs: InstalledPackageVersion, UninstallString?, PackageFamilyNames? // Output: None void ExecuteUninstaller(Execution::Context& context); // Removes the MSIX. // Required Args: None // Inputs: PackageFamilyNames // Outputs: None void MsixUninstall(Execution::Context& context); // Records the uninstall to the tracking catalog. // Required Args: None // Inputs: Package // Outputs: None void RecordUninstall(Execution::Context& context); // Reports the return code returned by the Uninstaller. // Required Args: None // Inputs: InstalledPackageVersion // Outputs: None struct ReportUninstallerResult : public WorkflowTask { ReportUninstallerResult(std::string_view uninstallerType, HRESULT hr, bool isHResult = false) : WorkflowTask("ReportUninstallerResult"), m_uninstallerType(uninstallerType), m_hr(hr), m_isHResult(isHResult) {} void operator()(Execution::Context& context) const override; private: // Uninstaller type used when reporting failures. std::string_view m_uninstallerType; // Result to return if the Uninstaller failed. HRESULT m_hr; // Whether the Uninstaller result is an HRESULT. This guides how we show it. bool m_isHResult; }; // Verifies that the uninstall operation is supported. // Required Args: None // Inputs: InstalledPackageVersion // Outputs: None void EnsureSupportForUninstall(Execution::Context& context); } ================================================ FILE: src/AppInstallerCLICore/Workflows/UpdateFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowBase.h" #include "DependenciesFlow.h" #include "InstallFlow.h" #include "UpdateFlow.h" #include #include #include using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Pinning; namespace AppInstaller::CLI::Workflow { namespace { bool IsUpdateVersionAvailable(const Utility::Version& installedVersion, const Utility::Version& updateVersion) { return installedVersion < updateVersion; } void AddToPackageSubContextsIfNotPresent(std::vector>& packageSubContexts, std::unique_ptr packageContext) { for (auto const& existing : packageSubContexts) { if (existing->Get().Id == packageContext->Get().Id && existing->Get().Version == packageContext->Get().Version && existing->Get()->GetProperty(PackageVersionProperty::SourceIdentifier) == packageContext->Get()->GetProperty(PackageVersionProperty::SourceIdentifier)) { return; } } packageSubContexts.emplace_back(std::move(packageContext)); } } void SelectLatestApplicableVersion::operator()(Execution::Context& context) const { auto package = context.Get(); auto installedPackage = context.Get(); const bool reportVersionNotFound = m_isSinglePackage; bool isUpgrade = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate);; Utility::Version installedVersion; if (isUpgrade) { installedVersion = Utility::Version(installedPackage->GetProperty(PackageVersionProperty::Version)); } Manifest::ManifestComparator manifestComparator(GetManifestComparatorOptions(context, isUpgrade ? installedPackage->GetMetadata() : IPackageVersion::Metadata{})); bool versionFound = false; bool installedTypeInapplicable = false; bool packagePinned = false; if (isUpgrade && installedVersion.IsUnknown() && !context.Args.Contains(Execution::Args::Type::IncludeUnknown)) { // the package has an unknown version and the user did not request to upgrade it anyway if (reportVersionNotFound) { context.Reporter.Info() << Resource::String::UpgradeUnknownVersionExplanation << std::endl; } AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } // If we are updating a single package or we got the --include-pinned flag, // we include packages with Pinning pins const bool includePinned = m_isSinglePackage || context.Args.Contains(Execution::Args::Type::IncludePinned); PinningData pinningData{ PinningData::Disposition::ReadOnly }; auto evaluator = pinningData.CreatePinStateEvaluator(includePinned ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins, GetInstalledVersion(package)); // The version keys should have already been sorted by version auto availableVersions = GetAvailableVersionsForInstalledVersion(package); const auto& versionKeys = availableVersions->GetVersionKeys(); // Assume that no update versions are applicable bool upgradeVersionAvailable = false; for (const auto& key : versionKeys) { // Check Applicable Version if (!isUpgrade || IsUpdateVersionAvailable(installedVersion, Utility::Version(key.Version))) { // The only way to enter this portion of the statement with isUpgrade is if the version is available if (isUpgrade) { AICLI_LOG(CLI, Verbose, << "Updating from [" << installedVersion.ToString() << "] to [" << key.Version << "]"); upgradeVersionAvailable = true; } auto packageVersion = availableVersions->GetVersion(key); // Check if the package is pinned PinType pinType = evaluator.EvaluatePinType(packageVersion); if (pinType != Pinning::PinType::Unknown) { AICLI_LOG(CLI, Info, << "Package [" << package->GetProperty(PackageProperty::Id) << " with Version[" << key.Version << "] from Source[" << key.SourceId << "] has a Pin with type[" << ToString(pinType) << "]"); if (context.Args.Contains(Execution::Args::Type::Force)) { AICLI_LOG(CLI, Info, << "Ignoring pin due to --force argument"); } else { packagePinned = true; continue; } } auto manifest = packageVersion->GetManifest(); // Check applicable Installer auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller(manifest); if (!installer.has_value()) { // If there is at least one installer whose only reason is InstalledType. auto onlyInstalledType = std::find(inapplicabilities.begin(), inapplicabilities.end(), Manifest::InapplicabilityFlags::InstalledType); if (onlyInstalledType != inapplicabilities.end()) { installedTypeInapplicable = true; } continue; } Logging::Telemetry().LogSelectedInstaller( static_cast(installer->Arch), installer->Url, Manifest::InstallerTypeToString(installer->EffectiveInstallerType()), Manifest::ScopeToString(installer->Scope), installer->Locale); Logging::Telemetry().LogManifestFields( manifest.Id, manifest.DefaultLocalization.Get(), manifest.Version); // Since we already did installer selection, just populate the context Data manifest.ApplyLocale(installer->Locale); context.Add(std::move(manifest)); context.Add(std::move(packageVersion)); context.Add(std::move(installer)); versionFound = true; break; } else { // Any following versions are not applicable break; } } if (!versionFound) { if (reportVersionNotFound) { if (installedTypeInapplicable) { context.Reporter.Info() << Resource::String::UpgradeDifferentInstallTechnologyInNewerVersions << std::endl; } else if (packagePinned) { context.Reporter.Info() << Resource::String::UpgradeIsPinned << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PACKAGE_IS_PINNED); } else if (isUpgrade) { if (!upgradeVersionAvailable) { // This is the case when no newer versions are available in a configured source context.Reporter.Info() << Resource::String::UpdateNoPackagesFound << std::endl << Resource::String::UpdateNoPackagesFoundReason << std::endl; } else { // This is the case when newer versions are available in a configured source, but none are applicable due to OS Version, user requirement, etc. context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl << Resource::String::UpdateNotApplicableReason << std::endl; } } else { context.Reporter.Error() << Resource::String::NoApplicableInstallers << std::endl; } } AICLI_TERMINATE_CONTEXT(isUpgrade ? APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE : APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER); } } void EnsureUpdateVersionApplicable(Execution::Context& context) { auto installedPackage = context.Get(); Utility::Version installedVersion = Utility::Version(installedPackage->GetProperty(PackageVersionProperty::Version)); Utility::Version updateVersion(context.Get().Version); if (!IsUpdateVersionAvailable(installedVersion, updateVersion)) { context.Reporter.Info() << Resource::String::UpdateNoPackagesFound << std::endl << Resource::String::UpdateNoPackagesFoundReason << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } } void UpdateAllApplicable(Execution::Context& context) { const auto& matches = context.Get().Matches; std::vector> packageSubContexts; bool updateAllFoundUpdate = false; int packagesWithUnknownVersionSkipped = 0; int packagesThatRequireExplicitSkipped = 0; for (const auto& match : matches) { // We want to do best effort to update all applicable updates regardless on previous update failure auto updateContextPtr = context.CreateSubContext(); Execution::Context& updateContext = *updateContextPtr; auto previousThreadGlobals = updateContext.SetForCurrentThread(); auto installedVersion = GetInstalledVersion(match.Package); updateContext.Add(match.Package); // Filter out packages with unknown installed versions if (Utility::Version(installedVersion->GetProperty(PackageVersionProperty::Version)).IsUnknown() && !context.Args.Contains(Execution::Args::Type::IncludeUnknown)) { // we don't know what the package's version is and the user didn't ask to upgrade it anyway. AICLI_LOG(CLI, Info, << "Skipping " << match.Package->GetProperty(PackageProperty::Id) << " as it has unknown installed version"); ++packagesWithUnknownVersionSkipped; continue; } updateContext << Workflow::GetInstalledPackageVersion << Workflow::ReportExecutionStage(ExecutionStage::Discovery) << SelectLatestApplicableVersion(false); if (updateContext.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE) { continue; } // Filter out packages that require explicit upgrade. // User-defined pins are handled when selecting the version to use. auto installedMetadata = updateContext.Get()->GetMetadata(); auto pinnedState = ConvertToPinTypeEnum(installedMetadata[PackageVersionMetadata::PinnedState]); if (pinnedState == PinType::PinnedByManifest) { // Note that for packages pinned by the manifest // this does not consider whether the update to be installed has // RequireExplicitUpgrade. While this has the downside of not working with // packages installed from another source, it ensures consistency with the // list of available updates (there we don't have the selected installer) // and at most we will update each package like this once. AICLI_LOG(CLI, Info, << "Skipping " << match.Package->GetProperty(PackageProperty::Id) << " as it requires explicit upgrade"); ++packagesThatRequireExplicitSkipped; continue; } updateAllFoundUpdate = true; AddToPackageSubContextsIfNotPresent(packageSubContexts, std::move(updateContextPtr)); } if (updateAllFoundUpdate) { context.Add(std::move(packageSubContexts)); context.Reporter.Info() << std::endl; ProcessMultiplePackages::Flags flags = ProcessMultiplePackages::Flags::None; if (Settings::User().Get() || context.Args.Contains(Execution::Args::Type::SkipDependencies)) { flags = ProcessMultiplePackages::Flags::IgnoreDependencies; } context << ProcessMultiplePackages( Resource::String::PackageRequiresDependencies, APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE, flags, { APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE }); } if (packagesWithUnknownVersionSkipped > 0) { AICLI_LOG(CLI, Info, << packagesWithUnknownVersionSkipped << " package(s) skipped due to unknown installed version"); context.Reporter.Info() << Resource::String::UpgradeUnknownVersionCount(packagesWithUnknownVersionSkipped) << std::endl; } if (packagesThatRequireExplicitSkipped > 0) { AICLI_LOG(CLI, Info, << packagesThatRequireExplicitSkipped << " package(s) skipped due to requiring explicit upgrade"); context.Reporter.Info() << Resource::String::UpgradeRequireExplicitCount(packagesThatRequireExplicitSkipped) << std::endl; } } void SelectSinglePackageVersionForInstallOrUpgrade::operator()(Execution::Context& context) const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_operationType != OperationType::Install && m_operationType != OperationType::Upgrade); context << HandleSearchResultFailures << EnsureOneMatchFromSearchResult(m_operationType) << GetInstalledPackageVersion; if ( m_operationType != OperationType::Upgrade && context.Contains(Execution::Data::InstalledPackageVersion) && context.Get() != nullptr ) { if (context.Args.Contains(Execution::Args::Type::NoUpgrade)) { AICLI_LOG(CLI, Warning, << "Found installed package, exiting installation."); context.Reporter.Warn() << Resource::String::PackageAlreadyInstalled << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PACKAGE_ALREADY_INSTALLED); } else { AICLI_LOG(CLI, Info, << "Found installed package, converting to upgrade flow"); context.Reporter.Info() << Execution::ConvertToUpgradeFlowEmphasis << Resource::String::ConvertInstallFlowToUpgrade << std::endl; context.SetFlags(Execution::ContextFlag::InstallerExecutionUseUpdate); m_operationType = OperationType::Upgrade; } } if (context.Args.Contains(Execution::Args::Type::Version)) { // If version specified, use the version and verify applicability context << GetManifestFromPackage(/* considerPins */ true); if (m_operationType == OperationType::Upgrade && !m_allowDowngrade) { context << EnsureUpdateVersionApplicable; } context << SelectInstaller; } else { // Iterate through available versions to find latest applicable version. // This step also populates Manifest and Installer in context data. context << SelectLatestApplicableVersion(true); } context << EnsureApplicableInstaller; } void InstallOrUpgradeSinglePackage::operator()(Execution::Context& context) const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_operationType != OperationType::Install && m_operationType != OperationType::Upgrade); context << SearchSourceForSingle << SelectSinglePackageVersionForInstallOrUpgrade(m_operationType) << InstallSinglePackage; } } ================================================ FILE: src/AppInstallerCLICore/Workflows/UpdateFlow.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionContext.h" #include "WorkflowBase.h" namespace AppInstaller::CLI::Workflow { // Iterates through all available versions from a package and find latest applicable version // Required Args: bool indicating whether to report update not found // Inputs: InstalledPackageVersion?, Package // Outputs: Manifest?, Installer? struct SelectLatestApplicableVersion : public WorkflowTask { SelectLatestApplicableVersion(bool isSinglePackage) : WorkflowTask("SelectLatestApplicableUpdate"), m_isSinglePackage(isSinglePackage) {} void operator()(Execution::Context& context) const override; private: bool m_isSinglePackage; }; // Ensures the update package has higher version than installed // Required Args: None // Inputs: Manifest, InstalledPackageVersion // Outputs: None void EnsureUpdateVersionApplicable(Execution::Context& context); // Update all packages from SearchResult to latest if applicable // Required Args: None // Inputs: SearchResult // Outputs: None void UpdateAllApplicable(Execution::Context& context); // Select single package version for install or upgrade // Required Args: bool indicating whether the flow is for upgrade // Inputs: Source, SearchResult // Outputs: None struct SelectSinglePackageVersionForInstallOrUpgrade : public WorkflowTask { SelectSinglePackageVersionForInstallOrUpgrade(OperationType operation, bool allowDowngrade = false) : WorkflowTask("SelectSinglePackageVersionForInstallOrUpgrade"), m_operationType(operation), m_allowDowngrade(allowDowngrade) {} void operator()(Execution::Context& context) const override; private: mutable OperationType m_operationType; bool m_allowDowngrade; }; // Install or upgrade a single package // Required Args: bool indicating whether the flow is for upgrade // Inputs: Source // Outputs: None struct InstallOrUpgradeSinglePackage : public WorkflowTask { InstallOrUpgradeSinglePackage(OperationType operation) : WorkflowTask("InstallOrUpgradeSinglePackage"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: mutable OperationType m_operationType; }; } ================================================ FILE: src/AppInstallerCLICore/Workflows/WorkflowBase.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowBase.h" #include "ExecutionContext.h" #include #include "PromptFlow.h" #include "ShowFlow.h" #include "Sixel.h" #include "TableOutput.h" #include #include #include #include #include #include #include #include #include EXTERN_C IMAGE_DOS_HEADER __ImageBase; using namespace std::string_literals; 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 { namespace { std::string GetMatchCriteriaDescriptor(const ResultMatch& match) { if (match.MatchCriteria.Field != PackageMatchField::Id && match.MatchCriteria.Field != PackageMatchField::Name) { std::string result{ ToString(match.MatchCriteria.Field) }; result += ": "; result += match.MatchCriteria.Value; return result; } else { return {}; } } void ReportIdentity( Execution::Context& context, Utility::LocIndView prefix, std::optional label, std::string_view name, std::string_view id, std::string_view version = {}, Execution::Reporter::Level level = Execution::Reporter::Level::Info) { auto out = context.Reporter.GetOutputStream(level); out << prefix; if (label) { out << *label << ' '; } out << Execution::NameEmphasis << name << " ["_liv << Execution::IdEmphasis << id << ']'; if (!version.empty()) { out << ' ' << Resource::String::ShowVersion << ' ' << version; } out << std::endl; } bool IsSecondIconResolutionBetter(Manifest::IconResolutionEnum current, Manifest::IconResolutionEnum alternative) { static constexpr std::array s_iconResolutionOrder { 9, // Unknown 8, // Custom 15, // Square16 14, // Square20 13, // Square24 12, // Square30 11, // Square32 10, // Square36 6, // Square40 5, // Square48 4, // Square60 3, // Square64 2, // Square72 0, // Square80 1, // Square96 7, // Square256 }; return s_iconResolutionOrder.at(ToIntegral(alternative)) < s_iconResolutionOrder.at(ToIntegral(current)); } // Determines icon fit given two options. // Targets an 80x80 icon as the best resolution for this use case. // TODO: Consider theme based on current background color. bool IsSecondIconBetter(const Manifest::Icon& current, const Manifest::Icon& alternative) { return IsSecondIconResolutionBetter(current.Resolution, alternative.Resolution); } bool IsSecondIconBetter(const ExtractedIconInfo& current, const ExtractedIconInfo& alternative) { return IsSecondIconResolutionBetter(current.IconResolution, alternative.IconResolution); } void ShowIcon(Execution::OutputStream& outputStream, VirtualTerminal::Sixel::Image& icon) { // Using a height of 4 arbitrarily; allow width up to the entire console. UINT imageHeightCells = 4; UINT imageWidthCells = static_cast(Execution::GetConsoleWidth()); icon.RenderSizeInCells(imageWidthCells, imageHeightCells); icon.RenderTo(outputStream); // Force the final sixel line to not be overwritten outputStream << std::endl; } void ShowManifestIcon(Execution::Context& context, const Manifest::Manifest& manifest) try { if (!context.Reporter.SixelsEnabled()) { return; } auto icons = manifest.CurrentLocalization.Get(); const Manifest::Icon* bestFitIcon = nullptr; for (const auto& icon : icons) { if (!bestFitIcon || IsSecondIconBetter(*bestFitIcon, icon)) { bestFitIcon = &icon; } } if (!bestFitIcon) { return; } // Use a cache to hold the icons auto splitUri = Utility::SplitFileNameFromURI(bestFitIcon->Url); Caching::FileCache fileCache{ Caching::FileCache::Type::Icon, Utility::SHA256::ConvertToString(bestFitIcon->Sha256), { splitUri.first } }; auto iconStream = fileCache.GetFile(splitUri.second, bestFitIcon->Sha256); VirtualTerminal::Sixel::Image sixelIcon{ *iconStream, bestFitIcon->FileType }; auto infoOut = context.Reporter.Info(); ShowIcon(infoOut, sixelIcon); } CATCH_LOG(); void ShowExtractedIcon(Execution::OutputStream& outputStream, const std::vector& icons) try { const ExtractedIconInfo* bestFitIcon = nullptr; for (const auto& icon : icons) { if (!bestFitIcon || IsSecondIconBetter(*bestFitIcon, icon)) { bestFitIcon = &icon; } } if (!bestFitIcon) { return; } VirtualTerminal::Sixel::Image sixelIcon{ bestFitIcon->IconContent, bestFitIcon->IconFileType }; ShowIcon(outputStream, sixelIcon); } CATCH_LOG(); Repository::Source OpenNamedSource(Execution::Context& context, Utility::LocIndView sourceName) { Repository::Source source; try { source = Source{ sourceName }; if (!source) { std::vector sources = Source::GetCurrentSources(); if (!sourceName.empty() && !sources.empty()) { // A bad name was given, try to help. context.Reporter.Error() << Resource::String::OpenSourceFailedNoMatch(sourceName) << std::endl; context.Reporter.Info() << Resource::String::OpenSourceFailedNoMatchHelp << std::endl; for (const auto& details : sources) { context.Reporter.Info() << " "_liv << details.Name << std::endl; } AICLI_TERMINATE_CONTEXT_RETURN(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST, {}); } else { // Even if a name was given, there are no sources context.Reporter.Error() << Resource::String::OpenSourceFailedNoSourceDefined << std::endl; AICLI_TERMINATE_CONTEXT_RETURN(APPINSTALLER_CLI_ERROR_NO_SOURCES_DEFINED, {}); } } if (context.Args.Contains(Execution::Args::Type::CustomHeader)) { std::string customHeader{ context.Args.GetArg(Execution::Args::Type::CustomHeader) }; if (!source.SetCustomHeader(customHeader)) { context.Reporter.Warn() << Resource::String::HeaderArgumentNotApplicableForNonRestSourceWarning << std::endl; } } auto openFunction = [&](IProgressCallback& progress)->std::vector { source.SetCaller("winget-cli"); source.SetAuthenticationArguments(GetAuthenticationArguments(context)); source.SetThreadGlobals(context.GetSharedThreadGlobals()); return source.Open(progress); }; auto updateFailures = context.Reporter.ExecuteWithProgress(openFunction, true); // We'll only report the source update failure as warning and continue for (const auto& s : updateFailures) { context.Reporter.Warn() << Resource::String::SourceOpenWithFailedUpdate(Utility::LocIndView{ s.Name }) << std::endl; } // Report sources that may need authentication if (source.IsComposite()) { for (const auto& s : source.GetAvailableSources()) { if (s.GetInformation().Authentication.Type != Authentication::AuthenticationType::None) { context.Reporter.Info() << Execution::AuthenticationEmphasis << Resource::String::SourceRequiresAuthentication(Utility::LocIndView{ s.GetDetails().Name }) << std::endl; } } } else if (source.GetInformation().Authentication.Type != Authentication::AuthenticationType::None) { context.Reporter.Info() << Execution::AuthenticationEmphasis << Resource::String::SourceRequiresAuthentication(Utility::LocIndView{ source.GetDetails().Name }) << std::endl; } } catch (const wil::ResultException& re) { context.Reporter.Error() << Resource::String::SourceOpenFailedSuggestion << std::endl; if (re.GetErrorCode() == APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES) { // Since we know there must have been multiple errors here, just fail the context rather // than trying to get one of the exceptions back out. AICLI_TERMINATE_CONTEXT_RETURN(APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES, {}); } else { throw; } } catch (...) { context.Reporter.Error() << Resource::String::SourceOpenFailedSuggestion << std::endl; throw; } return source; } void SearchSourceApplyFilters(Execution::Context& context, SearchRequest& searchRequest, MatchType matchType) { const auto& args = context.Args; if (args.Contains(Execution::Args::Type::Id)) { searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Id, matchType, args.GetArg(Execution::Args::Type::Id))); } if (args.Contains(Execution::Args::Type::Name)) { searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Name, matchType, args.GetArg(Execution::Args::Type::Name))); } if (args.Contains(Execution::Args::Type::Moniker)) { searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Moniker, matchType, args.GetArg(Execution::Args::Type::Moniker))); } if (args.Contains(Execution::Args::Type::ProductCode)) { searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, matchType, args.GetArg(Execution::Args::Type::ProductCode))); } if (args.Contains(Execution::Args::Type::Tag)) { searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Tag, matchType, args.GetArg(Execution::Args::Type::Tag))); } if (args.Contains(Execution::Args::Type::Command)) { searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Command, matchType, args.GetArg(Execution::Args::Type::Command))); } if (args.Contains(Execution::Args::Type::Count)) { searchRequest.MaximumResults = std::stoi(std::string(args.GetArg(Execution::Args::Type::Count))); } } // Data shown on a line of a table displaying installed packages struct InstalledPackagesTableLine { InstalledPackagesTableLine( std::shared_ptr package, std::shared_ptr installedVersion, const Utility::LocIndString& availableVersion, const Utility::LocIndString& source) : Package(std::move(package)), InstalledPackageVersion(std::move(installedVersion)), AvailableVersion(availableVersion), Source(source) { Name = InstalledPackageVersion->GetProperty(PackageVersionProperty::Name); Id = Package->GetProperty(PackageProperty::Id); InstalledVersion = InstalledPackageVersion->GetProperty(PackageVersionProperty::Version); } std::shared_ptr Package; std::shared_ptr InstalledPackageVersion; Utility::LocIndString Name; Utility::LocIndString Id; Utility::LocIndString InstalledVersion; Utility::LocIndString AvailableVersion; Utility::LocIndString Source; }; void OutputInstalledPackagesTable(Execution::Context& context, std::vector& lines) { Execution::TableOutput<5> table(context.Reporter, { Resource::String::SearchName, Resource::String::SearchId, Resource::String::SearchVersion, Resource::String::AvailableHeader, Resource::String::SearchSource }); for (auto& line : lines) { table.OutputLine({ line.Name, line.Id, line.InstalledVersion, line.AvailableVersion, line.Source }); } table.Complete(); } void ShowMetadataField( Execution::OutputStream& outputStream, StringResource::StringId label, const IPackageVersion::Metadata& metadata, PackageVersionMetadata field) { auto itr = metadata.find(field); if (itr != metadata.end()) { ShowSingleLineField(outputStream, label, Utility::LocIndView{ itr->second }); } } // Outputs every package "line" with many details, with a format similar to the `show` command. void OutputInstalledPackagesDetails(Execution::Context& context, std::vector& lines) { auto info = context.Reporter.Info(); size_t packageIndex = 0; size_t totalLines = lines.size(); bool shouldGetIcon = context.Reporter.SixelsEnabled(); for (const auto& line : lines) { // Identity header including package count indicator if multiple lines provided if (totalLines > 1) { info << '(' << ++packageIndex << '/' << totalLines << ") "_liv; } ReportIdentity(context, {}, std::nullopt, line.Name, line.Id); auto metadata = line.InstalledPackageVersion->GetMetadata(); auto productCodes = line.InstalledPackageVersion->GetMultiProperty(PackageVersionMultiProperty::ProductCode); if (shouldGetIcon && !productCodes.empty()) { Manifest::ScopeEnum scope = Manifest::ScopeEnum::Unknown; auto itr = metadata.find(PackageVersionMetadata::InstalledScope); if (itr != metadata.end()) { scope = Manifest::ConvertToScopeEnum(itr->second); } auto icons = ExtractIconFromArpEntry(productCodes[0], scope); ShowExtractedIcon(info, icons); } ShowSingleLineField(info, Resource::String::ShowLabelVersion, line.InstalledVersion); ShowSingleLineField(info, Resource::String::ShowLabelChannel, line.InstalledPackageVersion->GetProperty(PackageVersionProperty::Channel)); ShowSingleLineField(info, Resource::String::ShowLabelPublisher, line.InstalledPackageVersion->GetProperty(PackageVersionProperty::Publisher)); auto localIdentifier = line.InstalledPackageVersion->GetProperty(PackageVersionProperty::Id); if (line.Id != localIdentifier) { ShowSingleLineField(info, Resource::String::ShowListLocalIdentifier, localIdentifier); } ShowMultiValueField(info, Resource::String::ShowListPackageFamilyName, line.InstalledPackageVersion->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName)); ShowMultiValueField(info, Resource::String::ShowListProductCode, productCodes); ShowMultiValueField(info, Resource::String::ShowListUpgradeCode, line.InstalledPackageVersion->GetMultiProperty(PackageVersionMultiProperty::UpgradeCode)); ShowMetadataField(info, Resource::String::ShowListInstallerCategory, metadata, PackageVersionMetadata::InstalledType); ShowMetadataField(info, Resource::String::ShowListInstalledScope, metadata, PackageVersionMetadata::InstalledScope); ShowMetadataField(info, Resource::String::ShowListInstalledArchitecture, metadata, PackageVersionMetadata::InstalledArchitecture); ShowMetadataField(info, Resource::String::ShowListInstalledLocale, metadata, PackageVersionMetadata::InstalledLocale); ShowMetadataField(info, Resource::String::ShowListInstalledLocation, metadata, PackageVersionMetadata::InstalledLocation); auto source = line.InstalledPackageVersion->GetSource(); if (source.ContainsAvailablePackages()) { ShowSingleLineField(info, Resource::String::ShowListInstalledSource, Utility::LocIndView{ source.GetDetails().Name }); } Utility::Version currentVersion{ line.InstalledVersion }; bool hasUpgradeVersion = false; for (const auto& available : line.Package->GetAvailable()) { auto latestAvailable = available->GetLatestVersion(); auto availableVersion = latestAvailable->GetProperty(PackageVersionProperty::Version); if (Utility::Version{ availableVersion } > currentVersion) { if (!hasUpgradeVersion) { hasUpgradeVersion = true; info << details::GetIndentFor(0) << Execution::ManifestInfoEmphasis << Resource::String::ShowListAvailableUpgrades << '\n'; } info << details::GetIndentFor(1) << Utility::LocIndView{ available->GetSource().GetDetails().Name } << " ["_liv << availableVersion << "]\n"_liv; } } // FUTURE: We could also pull data from the tracking database to show some things that we store there specifically. } } void OutputInstalledPackages(Execution::Context& context, std::vector& lines) { if (context.Args.Contains(Execution::Args::Type::ListDetails)) { OutputInstalledPackagesDetails(context, lines); } else { OutputInstalledPackagesTable(context, lines); } } } bool WorkflowTask::operator==(const WorkflowTask& other) const { if (m_isFunc && other.m_isFunc) { return m_func == other.m_func; } else if (!m_isFunc && !other.m_isFunc) { return m_name == other.m_name; } else { return false; } } void WorkflowTask::operator()(Execution::Context& context) const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_isFunc); m_func(context); } void WorkflowTask::Log() const { if (m_isFunc) { // Using `00000001`80000000` as base address default when loading dll into windbg as dump file. AICLI_LOG(Workflow, Verbose, << "Running task: 0x" << m_func << " [ln 00000001`80000000+" << std::hex << (reinterpret_cast(m_func) - reinterpret_cast(&__ImageBase)) << "]"); } else { AICLI_LOG(Workflow, Verbose, << "Running task: " << m_name); } } Repository::PredefinedSource DetermineInstalledSource(const Execution::Context& context) { Repository::PredefinedSource installedSource = Repository::PredefinedSource::Installed; Manifest::ScopeEnum scope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); if (scope == Manifest::ScopeEnum::Machine) { installedSource = Repository::PredefinedSource::InstalledMachine; } else if (scope == Manifest::ScopeEnum::User) { installedSource = Repository::PredefinedSource::InstalledUser; } return installedSource; } Authentication::AuthenticationArguments GetAuthenticationArguments(const Execution::Context& context) { AppInstaller::Authentication::AuthenticationArguments authArgs; if (context.Args.Contains(Execution::Args::Type::AuthenticationMode)) { authArgs.Mode = Authentication::ConvertToAuthenticationMode(context.Args.GetArg(Execution::Args::Type::AuthenticationMode)); } else { // If user did not specify authentication mode, determine based on if disable interactivity flag exists. authArgs.Mode = context.Args.Contains(Execution::Args::Type::DisableInteractivity) ? Authentication::AuthenticationMode::Silent : Authentication::AuthenticationMode::SilentPreferred; } if (context.Args.Contains(Execution::Args::Type::AuthenticationAccount)) { authArgs.AuthenticationAccount = context.Args.GetArg(Execution::Args::Type::AuthenticationAccount); } AICLI_LOG(CLI, Info, << "Created authentication arguments. Mode: " << Authentication::AuthenticationModeToString(authArgs.Mode) << ", Account: " << authArgs.AuthenticationAccount); return authArgs; } HRESULT HandleException(Execution::Context* context, std::exception_ptr exception) { try { std::rethrow_exception(exception); } // Exceptions that may occur in the process of executing an arbitrary command catch (const wil::ResultException& re) { // Even though they are logged at their source, log again here for completeness. Logging::Telemetry().LogException(Logging::FailureTypeEnum::ResultException, re.what()); if (context) { context->Reporter.Error() << Resource::String::UnexpectedErrorExecutingCommand << ' ' << std::endl << GetUserPresentableMessage(re) << std::endl; } return re.GetErrorCode(); } catch (const winrt::hresult_error& hre) { std::string message = GetUserPresentableMessage(hre); Logging::Telemetry().LogException(Logging::FailureTypeEnum::WinrtHResultError, message); if (context) { context->Reporter.Error() << Resource::String::UnexpectedErrorExecutingCommand << ' ' << std::endl << message << std::endl; } return hre.code(); } catch (const Settings::GroupPolicyException& e) { if (context) { auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); auto policyNameId = policy.PolicyName(); context->Reporter.Error() << Resource::String::DisabledByGroupPolicy(policyNameId) << std::endl; } return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } catch (const std::exception& e) { Logging::Telemetry().LogException(Logging::FailureTypeEnum::StdException, e.what()); if (context) { context->Reporter.Error() << Resource::String::UnexpectedErrorExecutingCommand << ' ' << std::endl << GetUserPresentableMessage(e) << std::endl; } return APPINSTALLER_CLI_ERROR_COMMAND_FAILED; } catch (...) { LOG_CAUGHT_EXCEPTION(); Logging::Telemetry().LogException(Logging::FailureTypeEnum::Unknown, {}); if (context) { context->Reporter.Error() << Resource::String::UnexpectedErrorExecutingCommand << " ???"_liv << std::endl; } return APPINSTALLER_CLI_ERROR_COMMAND_FAILED; } return E_UNEXPECTED; } HRESULT HandleException(Execution::Context& context, std::exception_ptr exception) { return HandleException(&context, exception); } AppInstaller::Manifest::ManifestComparator::Options GetManifestComparatorOptions(const Execution::Context& context, const IPackageVersion::Metadata& metadata) { AppInstaller::Manifest::ManifestComparator::Options options; bool getAllowedArchitecturesFromMetadata = false; if (context.Contains(Execution::Data::AllowedArchitectures)) { // Com caller can directly set allowed architectures options.AllowedArchitectures = context.Get(); } else if (context.Args.Contains(Execution::Args::Type::InstallArchitecture)) { // Arguments provided in command line options.AllowedArchitectures.emplace_back(Utility::ConvertToArchitectureEnum(context.Args.GetArg(Execution::Args::Type::InstallArchitecture))); } else if (context.Args.Contains(Execution::Args::Type::InstallerArchitecture)) { // Arguments provided in command line. Also skips applicability check. options.AllowedArchitectures.emplace_back(Utility::ConvertToArchitectureEnum(context.Args.GetArg(Execution::Args::Type::InstallerArchitecture))); options.SkipApplicabilityCheck = true; } else { getAllowedArchitecturesFromMetadata = true; } if (context.Args.Contains(Execution::Args::Type::InstallerType)) { options.RequestedInstallerType = Manifest::ConvertToInstallerTypeEnum(std::string(context.Args.GetArg(Execution::Args::Type::InstallerType))); } if (context.Args.Contains(Execution::Args::Type::InstallScope)) { options.RequestedInstallerScope = Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope)); } if (context.Contains(Execution::Data::AllowUnknownScope)) { options.AllowUnknownScope = context.Get(); } if (context.Args.Contains(Execution::Args::Type::Locale)) { options.RequestedInstallerLocale = context.Args.GetArg(Execution::Args::Type::Locale); } Repository::GetManifestComparatorOptionsFromMetadata(options, metadata, getAllowedArchitecturesFromMetadata); return options; } void OpenSource::operator()(Execution::Context& context) const { std::string_view sourceName; if (m_forDependencies) { if (context.Args.Contains(Execution::Args::Type::DependencySource)) { sourceName = context.Args.GetArg(Execution::Args::Type::DependencySource); } } else { if (context.Args.Contains(Execution::Args::Type::Source)) { sourceName = context.Args.GetArg(Execution::Args::Type::Source); } } auto source = OpenNamedSource(context, Utility::LocIndView{ sourceName }); if (context.IsTerminated()) { return; } context << HandleSourceAgreements(source); if (context.IsTerminated()) { return; } if (m_forDependencies) { context.Add(std::move(source)); } else { context.Add(std::move(source)); } } void OpenNamedSourceForSources::operator()(Execution::Context& context) const { auto source = OpenNamedSource(context, m_sourceName); if (context.IsTerminated()) { return; } context << HandleSourceAgreements(source); if (context.IsTerminated()) { return; } if (!context.Contains(Execution::Data::Sources)) { context.Add({ std::move(source) }); } else { context.Get().emplace_back(std::move(source)); } } void OpenPredefinedSource::operator()(Execution::Context& context) const { Repository::Source source; try { source = Source{ m_predefinedSource }; // A well known predefined source should return a value. THROW_HR_IF(E_UNEXPECTED, !source); auto openFunction = [&](IProgressCallback& progress)->std::vector { return source.Open(progress); }; context.Reporter.ExecuteWithProgress(openFunction, true); } catch (...) { context.Reporter.Error() << Resource::String::SourceOpenPredefinedFailedSuggestion << std::endl; throw; } if (m_forDependencies) { context.Add(std::move(source)); } else { context.Add(std::move(source)); } } void OpenCompositeSource::operator()(Execution::Context& context) const { // Get the already open source for use as the available. Repository::Source availableSource; if (m_forDependencies) { availableSource = context.Get(); } else { availableSource = context.Get(); } // Open the predefined source. context << OpenPredefinedSource(m_predefinedSource, m_forDependencies); // Create the composite source from the two. Repository::Source source; if (m_forDependencies) { source = context.Get(); } else { source = context.Get(); } Repository::Source compositeSource{ source, availableSource, m_searchBehavior }; // Overwrite the source with the composite. if (m_forDependencies) { context.Add(std::move(compositeSource)); } else { context.Add(std::move(compositeSource)); } } void SearchSourceForMany(Execution::Context& context) { const auto& args = context.Args; MatchType matchType = MatchType::Substring; if (args.Contains(Execution::Args::Type::Exact)) { matchType = MatchType::Exact; } SearchRequest searchRequest; if (args.Contains(Execution::Args::Type::Query)) { searchRequest.Query.emplace(RequestMatch(matchType, args.GetArg(Execution::Args::Type::Query))); } SearchSourceApplyFilters(context, searchRequest, matchType); Logging::Telemetry().LogSearchRequest( "many", args.GetArg(Execution::Args::Type::Query), args.GetArg(Execution::Args::Type::Id), args.GetArg(Execution::Args::Type::Name), args.GetArg(Execution::Args::Type::Moniker), args.GetArg(Execution::Args::Type::Tag), args.GetArg(Execution::Args::Type::Command), searchRequest.MaximumResults, searchRequest.ToString()); context.Add(context.Get().Search(searchRequest)); } void GetSearchRequestForSingle(Execution::Context& context) { const auto& args = context.Args; MatchType matchType = MatchType::CaseInsensitive; if (args.Contains(Execution::Args::Type::Exact)) { matchType = MatchType::Exact; } SearchRequest searchRequest; // Note: MultiQuery when we need search for single is handled with one sub-context per query. if (args.Contains(Execution::Args::Type::Query)) { std::string_view query = args.GetArg(Execution::Args::Type::Query); // Regardless of match type, always use an exact match for the system reference strings. searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, matchType, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Name, matchType, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Moniker, matchType, query)); } SearchSourceApplyFilters(context, searchRequest, matchType); context.Add(std::move(searchRequest)); } void SearchSourceForSingle(Execution::Context& context) { const auto& args = context.Args; context << GetSearchRequestForSingle; if (!context.IsTerminated()) { const auto& searchRequest = context.Get(); Logging::Telemetry().LogSearchRequest( "single", args.GetArg(Execution::Args::Type::Query), args.GetArg(Execution::Args::Type::Id), args.GetArg(Execution::Args::Type::Name), args.GetArg(Execution::Args::Type::Moniker), args.GetArg(Execution::Args::Type::Tag), args.GetArg(Execution::Args::Type::Command), searchRequest.MaximumResults, searchRequest.ToString()); context.Add(context.Get().Search(searchRequest)); } } void SearchSourceForManyCompletion(Execution::Context& context) { MatchType matchType = MatchType::StartsWith; SearchRequest searchRequest; std::string_view query = context.Get().Word(); searchRequest.Query.emplace(RequestMatch(matchType, query)); SearchSourceApplyFilters(context, searchRequest, matchType); context.Add(context.Get().Search(searchRequest)); } void SearchSourceForSingleCompletion(Execution::Context& context) { MatchType matchType = MatchType::StartsWith; SearchRequest searchRequest; std::string_view query = context.Get().Word(); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, matchType, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Name, matchType, query)); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Moniker, matchType, query)); SearchSourceApplyFilters(context, searchRequest, matchType); context.Add(context.Get().Search(searchRequest)); } void SearchSourceForCompletionField::operator()(Execution::Context& context) const { const std::string& word = context.Get().Word(); SearchRequest searchRequest; searchRequest.Inclusions.emplace_back(PackageMatchFilter(m_field, MatchType::StartsWith, word)); // If filters are provided, be generous with the search no matter the intended result. SearchSourceApplyFilters(context, searchRequest, MatchType::Substring); context.Add(context.Get().Search(searchRequest)); } void ReportSearchResult(Execution::Context& context) { auto& searchResult = context.Get(); bool sourceIsComposite = context.Get().IsComposite(); Execution::TableOutput<5> table(context.Reporter, { Resource::String::SearchName, Resource::String::SearchId, Resource::String::SearchVersion, Resource::String::SearchMatch, Resource::String::SearchSource }); for (size_t i = 0; i < searchResult.Matches.size(); ++i) { auto latestVersion = GetAllAvailableVersions(searchResult.Matches[i].Package)->GetLatestVersion(); table.OutputLine({ latestVersion->GetProperty(PackageVersionProperty::Name), latestVersion->GetProperty(PackageVersionProperty::Id), latestVersion->GetProperty(PackageVersionProperty::Version), GetMatchCriteriaDescriptor(searchResult.Matches[i]), sourceIsComposite ? static_cast(latestVersion->GetProperty(PackageVersionProperty::SourceName)) : ""s }); } table.Complete(); if (searchResult.Truncated) { context.Reporter.Info() << '<' << Resource::String::SearchTruncated << '>' << std::endl; } } void HandleSearchResultFailures(Execution::Context& context) { const auto& searchResult = context.Get(); if (!searchResult.Failures.empty()) { if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::TreatSourceFailuresAsWarning)) { auto warn = context.Reporter.Warn(); for (const auto& failure : searchResult.Failures) { warn << Resource::String::SearchFailureWarning(Utility::LocIndView{ failure.SourceName }) << std::endl; } } else { HRESULT overallHR = S_OK; auto error = context.Reporter.Error(); for (const auto& failure : searchResult.Failures) { error << Resource::String::SearchFailureError(Utility::LocIndView{ failure.SourceName }) << std::endl; HRESULT failureHR = HandleException(context, failure.Exception); // Just take first failure for now if (overallHR == S_OK) { overallHR = failureHR; } } if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::ShowSearchResultsOnPartialFailure)) { if (searchResult.Matches.empty()) { context.Reporter.Info() << std::endl << Resource::String::SearchFailureErrorNoMatches << std::endl; } else { context.Reporter.Info() << std::endl << Resource::String::SearchFailureErrorListMatches << std::endl; context << ReportMultiplePackageFoundResultWithSource; } } context.SetTerminationHR(overallHR); } } } void ReportMultiplePackageFoundResult(Execution::Context& context) { auto& searchResult = context.Get(); Execution::TableOutput<2> table(context.Reporter, { Resource::String::SearchName, Resource::String::SearchId }); for (size_t i = 0; i < searchResult.Matches.size(); ++i) { auto package = searchResult.Matches[i].Package; table.OutputLine({ package->GetProperty(PackageProperty::Name), package->GetProperty(PackageProperty::Id) }); } table.Complete(); if (searchResult.Truncated) { context.Reporter.Info() << '<' << Resource::String::SearchTruncated << '>' << std::endl; } } void ReportMultiplePackageFoundResultWithSource(Execution::Context& context) { auto& searchResult = context.Get(); Execution::TableOutput<3> table(context.Reporter, { Resource::String::SearchName, Resource::String::SearchId, Resource::String::SearchSource }); for (size_t i = 0; i < searchResult.Matches.size(); ++i) { auto package = searchResult.Matches[i].Package; std::string sourceName; auto available = package->GetAvailable(); if (!available.empty()) { auto source = available[0]->GetSource(); if (source) { sourceName = source.GetDetails().Name; } } table.OutputLine({ package->GetProperty(PackageProperty::Name), package->GetProperty(PackageProperty::Id), std::move(sourceName) }); } table.Complete(); if (searchResult.Truncated) { context.Reporter.Info() << '<' << Resource::String::SearchTruncated << '>' << std::endl; } } void ReportListResult::operator()(Execution::Context& context) const { auto& searchResult = context.Get(); std::vector lines; std::vector linesForExplicitUpgrade; std::vector linesForPins; int availableUpgradesCount = 0; // We will show a line with a summary for skipped and pinned packages at the end. // The strings suggest using a --include-unknown/pinned argument, so we should // ensure that the count is 0 when using the arguments. int packagesWithUnknownVersionSkipped = 0; int packagesWithUserPinsSkipped = 0; auto &source = context.Get(); bool shouldShowSource = source.IsComposite() && source.GetAvailableSources().size() > 1; PinBehavior pinBehavior; if (m_onlyShowUpgrades && !context.Args.Contains(Execution::Args::Type::Force)) { // For listing upgrades, show the version we would upgrade to with the given pins. pinBehavior = context.Args.Contains(Execution::Args::Type::IncludePinned) ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins; } else { // For listing installed apps or if we are ignoring pins due to --force, show the latest available. pinBehavior = PinBehavior::IgnorePins; } PinningData pinningData{ PinningData::Disposition::ReadOnly }; for (const auto& match : searchResult.Matches) { auto installedPackage = match.Package->GetInstalled(); if (!installedPackage) { continue; } // We only want to evaluate update availability for the latest version. bool isFirstInstalledVersion = true; for (const auto& installedVersionKey : installedPackage->GetVersionKeys()) { bool isFirstInstalledVersionLocal = isFirstInstalledVersion; isFirstInstalledVersion = false; auto installedVersion = installedPackage->GetVersion(installedVersionKey); auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, installedVersion); auto availableVersions = GetAvailableVersionsForInstalledVersion(match.Package, installedVersion); auto latestVersion = evaluator.GetLatestAvailableVersionForPins(availableVersions); bool updateAvailable = isFirstInstalledVersionLocal && evaluator.IsUpdate(latestVersion); bool updateIsPinned = false; if (m_onlyShowUpgrades && !context.Args.Contains(Execution::Args::Type::IncludeUnknown) && Utility::Version(installedVersion->GetProperty(PackageVersionProperty::Version)).IsUnknown() && updateAvailable) { // We are only showing upgrades, and the user did not request to include packages with unknown versions. ++packagesWithUnknownVersionSkipped; continue; } if (m_onlyShowUpgrades && !updateAvailable && isFirstInstalledVersionLocal) { // Reuse the evaluator to check if there is an update outside of the pinning auto unpinnedLatestVersion = availableVersions->GetLatestVersion(); bool updateAvailableWithoutPins = evaluator.IsUpdate(unpinnedLatestVersion); if (updateAvailableWithoutPins) { // When given the --include-pinned argument, report blocking and gating pins in a separate table. // Otherwise, simply show a count of them if (context.Args.Contains(Execution::Args::Type::IncludePinned)) { updateIsPinned = true; // Override these so we generate the table line below. latestVersion = std::move(unpinnedLatestVersion); updateAvailable = true; } else { ++packagesWithUserPinsSkipped; continue; } } } // The only time we don't want to output a line is when filtering and no update is available. if (updateAvailable || !m_onlyShowUpgrades) { Utility::LocIndString availableVersion, sourceName; if (latestVersion) { // Always show the source for correlated packages sourceName = latestVersion->GetProperty(PackageVersionProperty::SourceName); if (updateAvailable) { availableVersion = latestVersion->GetProperty(PackageVersionProperty::Version); availableUpgradesCount++; } } // Output using the local PackageName instead of the name in the manifest, to prevent confusion for packages that add multiple // Add/Remove Programs entries. // TODO: De-duplicate this list, and only show (by default) one entry per matched package. InstalledPackagesTableLine line( match.Package, installedVersion, availableVersion, shouldShowSource ? sourceName : Utility::LocIndString() ); auto pinnedState = ConvertToPinTypeEnum(installedVersion->GetMetadata()[PackageVersionMetadata::PinnedState]); if (updateIsPinned) { linesForPins.push_back(std::move(line)); } else if (m_onlyShowUpgrades && pinnedState == PinType::PinnedByManifest) { linesForExplicitUpgrade.push_back(std::move(line)); } else { lines.push_back(std::move(line)); } } } } OutputInstalledPackages(context, lines); if (lines.empty()) { context.Reporter.Info() << Resource::String::NoInstalledPackageFound << std::endl; } else { if (searchResult.Truncated) { context.Reporter.Info() << '<' << Resource::String::SearchTruncated << '>' << std::endl; } if (m_onlyShowUpgrades) { context.Reporter.Info() << Resource::String::AvailableUpgrades(availableUpgradesCount) << std::endl; } } if (!linesForExplicitUpgrade.empty()) { context.Reporter.Info() << std::endl << Resource::String::UpgradeAvailableForPinned << std::endl; OutputInstalledPackages(context, linesForExplicitUpgrade); } if (!linesForPins.empty()) { context.Reporter.Info() << std::endl << Resource::String::UpgradeBlockedByPinCount(linesForPins.size()) << std::endl; OutputInstalledPackages(context, linesForPins); } if (m_onlyShowUpgrades) { if (packagesWithUnknownVersionSkipped > 0) { AICLI_LOG(CLI, Info, << packagesWithUnknownVersionSkipped << " package(s) skipped due to unknown installed version"); context.Reporter.Info() << Resource::String::UpgradeUnknownVersionCount(packagesWithUnknownVersionSkipped) << std::endl; } if (packagesWithUserPinsSkipped > 0) { AICLI_LOG(CLI, Info, << packagesWithUserPinsSkipped << " package(s) skipped due to user pins"); context.Reporter.Info() << Resource::String::UpgradePinnedByUserCount(packagesWithUserPinsSkipped) << std::endl; } } } void EnsureMatchesFromSearchResult::operator()(Execution::Context& context) const { auto& searchResult = context.Get(); Logging::Telemetry().LogSearchResultCount(searchResult.Matches.size()); if (searchResult.Matches.size() == 0) { Logging::Telemetry().LogNoAppMatch(); switch (m_operationType) { // These search purposes require a package to be found in the Installed Packages case OperationType::Export: case OperationType::List: case OperationType::Uninstall: case OperationType::Pin: case OperationType::Upgrade: case OperationType::Repair: context.Reporter.Info() << Resource::String::NoInstalledPackageFound << std::endl; break; case OperationType::Completion: case OperationType::Install: case OperationType::Search: case OperationType::Show: case OperationType::Download: default: context.Reporter.Info() << Resource::String::NoPackageFound << std::endl; break; } AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); } } void EnsureOneMatchFromSearchResult::operator()(Execution::Context& context) const { context << EnsureMatchesFromSearchResult(m_operationType); if (!context.IsTerminated()) { auto& searchResult = context.Get(); bool operationTargetsInstalled = m_operationType == OperationType::Upgrade || m_operationType == OperationType::Uninstall || m_operationType == OperationType::Repair || m_operationType == OperationType::Export; // Try limiting results to highest priority sources if (searchResult.Matches.size() > 1 && !operationTargetsInstalled && ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::SourcePriority)) { // Find the set of matches that have the highest priority std::vector highestPriorityMatches; std::optional highestPriority; for (const auto& match : searchResult.Matches) { std::optional priority = GetSourcePriority(match.Package); // Optional provides overloads that make empty less than valued and empties equal. if (highestPriority < priority) { // Current priority is higher; reset. highestPriority = priority; highestPriorityMatches.clear(); } else if (highestPriority == priority) { // Priority is equal, add to the list. } else { // Current priority is lower, ignore the match. continue; } highestPriorityMatches.emplace_back(match); } if (highestPriorityMatches.size() < searchResult.Matches.size()) { AICLI_LOG(CLI, Info, << "Replacing search results with only those from the highest priority [" << (highestPriority ? std::to_string(highestPriority.value()) : "none"s) << "]."); searchResult.Matches = std::move(highestPriorityMatches); context.Reporter.Warn() << Resource::String::MultiplePackagesFoundFilteredBySourcePriority << std::endl; } } if (searchResult.Matches.size() > 1) { Logging::Telemetry().LogMultiAppMatch(); if (operationTargetsInstalled) { context.Reporter.Warn() << Resource::String::MultipleInstalledPackagesFound << std::endl; context << ReportMultiplePackageFoundResult; } else { context.Reporter.Warn() << Resource::String::MultiplePackagesFound << std::endl; context << ReportMultiplePackageFoundResultWithSource; } AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND); } std::shared_ptr package = searchResult.Matches.at(0).Package; Logging::Telemetry().LogAppFound(package->GetProperty(PackageProperty::Name), package->GetProperty(PackageProperty::Id)); context.Add(std::move(package)); } } void GetManifestWithVersionFromPackage::operator()(Execution::Context& context) const { PackageVersionKey key("", m_version, m_channel); std::shared_ptr package = context.Get(); std::shared_ptr requestedVersion; auto availableVersions = GetAvailableVersionsForInstalledVersion(package); if (m_considerPins) { bool isPinned = false; PinBehavior pinBehavior; if (context.Args.Contains(Execution::Args::Type::Force)) { // --force ignores any pins pinBehavior = PinBehavior::IgnorePins; } else { pinBehavior = context.Args.Contains(Execution::Args::Type::IncludePinned) ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins; } PinningData pinningData{ PinningData::Disposition::ReadOnly }; auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, GetInstalledVersion(package)); // TODO: The logic here will probably have to get more difficult once we support channels if (Utility::IsEmptyOrWhitespace(m_version) && Utility::IsEmptyOrWhitespace(m_channel)) { requestedVersion = evaluator.GetLatestAvailableVersionForPins(availableVersions); if (!requestedVersion) { // Check whether we didn't find the latest version because it was pinned or because there wasn't one auto latestVersion = availableVersions->GetLatestVersion(); if (latestVersion) { isPinned = true; } } } else { requestedVersion = availableVersions->GetVersion(key); isPinned = evaluator.EvaluatePinType(requestedVersion) != PinType::Unknown; } if (isPinned) { if (context.Args.Contains(Execution::Args::Type::Force)) { AICLI_LOG(CLI, Info, << "Ignoring pin on package due to --force argument"); } else { AICLI_LOG(CLI, Error, << "The requested package version is unavailable because of a pin"); context.Reporter.Error() << Resource::String::PackageIsPinned << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_PACKAGE_IS_PINNED); } } } else { // The simple case: Just look up the requested version requestedVersion = availableVersions->GetVersion(key); } std::optional manifest; if (requestedVersion) { manifest = requestedVersion->GetManifest(); } if (!manifest) { std::ostringstream ssVersionInfo; if (!m_version.empty()) { ssVersionInfo << m_version; } if (!m_channel.empty()) { ssVersionInfo << '[' << m_channel << ']'; } context.Reporter.Error() << Resource::String::GetManifestResultVersionNotFound(Utility::LocIndView{ ssVersionInfo.str()}) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND); } Logging::Telemetry().LogManifestFields(manifest->Id, manifest->DefaultLocalization.Get(), manifest->Version); std::string targetLocale; if (context.Args.Contains(Execution::Args::Type::Locale)) { targetLocale = context.Args.GetArg(Execution::Args::Type::Locale); } manifest->ApplyLocale(targetLocale); context.Add(std::move(manifest.value())); context.Add(std::move(requestedVersion)); } void GetManifestFromPackage::operator()(Execution::Context& context) const { context << GetManifestWithVersionFromPackage( context.Args.GetArg(Execution::Args::Type::Version), context.Args.GetArg(Execution::Args::Type::Channel), m_considerPins); } void VerifyFile::operator()(Execution::Context& context) const { std::filesystem::path path = Utility::ConvertToUTF16(context.Args.GetArg(m_arg)); if (!std::filesystem::exists(path)) { context.Reporter.Error() << Resource::String::VerifyFileFailedNotExist(Utility::LocIndView{ path.u8string() }) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } if (std::filesystem::is_directory(path)) { context.Reporter.Error() << Resource::String::VerifyFileFailedIsDirectory(Utility::LocIndView{ path.u8string() }) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_DIRECTORY_NOT_SUPPORTED)); } } void VerifyPath::operator()(Execution::Context& context) const { std::filesystem::path path = Utility::ConvertToUTF16(context.Args.GetArg(m_arg)); if (!std::filesystem::exists(path)) { context.Reporter.Error() << Resource::String::VerifyPathFailedNotExist(Utility::LocIndView{ path.u8string() }) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)); } } void VerifyFileOrUri::operator()(Execution::Context& context) const { // Argument requirement is handled elsewhere. if (!context.Args.Contains(m_arg)) { return; } auto path = context.Args.GetArg(m_arg); // try uri first Uri pathAsUri = nullptr; try { pathAsUri = Uri{ Utility::ConvertToUTF16(path) }; } catch (...) {} 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 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. context << VerifyFile(m_arg); } else if (std::find(m_supportedSchemes.begin(), m_supportedSchemes.end(), pathAsUri.SchemeName()) != m_supportedSchemes.end()) { // Scheme supported. return; } else { context.Reporter.Error() << Resource::String::UriSchemeNotSupported(Utility::LocIndView{ path }) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } else { context << VerifyFile(m_arg); } } void GetManifestFromArg(Execution::Context& context) { Logging::Telemetry().LogIsManifestLocal(true); context << VerifyPath(Execution::Args::Type::Manifest) << [](Execution::Context& context) { Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::Manifest))); Logging::Telemetry().LogManifestFields(manifest.Id, manifest.DefaultLocalization.Get(), manifest.Version); std::string targetLocale; if (context.Args.Contains(Execution::Args::Type::Locale)) { targetLocale = context.Args.GetArg(Execution::Args::Type::Locale); } manifest.ApplyLocale(targetLocale); context.Add(std::move(manifest)); }; } void ReportPackageIdentity(Execution::Context& context) { auto package = context.Get(); ReportIdentity(context, {}, Resource::String::ReportIdentityFound, package->GetProperty(PackageProperty::Name), package->GetProperty(PackageProperty::Id)); } void ReportInstalledPackageVersionIdentity(Execution::Context& context) { auto package = context.Get(); auto version = context.Get(); ReportIdentity(context, {}, Resource::String::ReportIdentityFound, version->GetProperty(PackageVersionProperty::Name), package ? package->GetProperty(PackageProperty::Id) : version->GetProperty(PackageVersionProperty::Id)); } void ReportManifestIdentity(Execution::Context& context) { const auto& manifest = context.Get(); ReportIdentity(context, {}, Resource::String::ReportIdentityFound, manifest.CurrentLocalization.Get(), manifest.Id); ShowManifestIcon(context, manifest); } void ReportManifestIdentityWithVersion::operator()(Execution::Context& context) const { const auto& manifest = context.Get(); ReportIdentity(context, m_prefix, m_label, manifest.CurrentLocalization.Get(), manifest.Id, manifest.Version, m_level); ShowManifestIcon(context, manifest); } void SelectInstaller(Execution::Context& context) { bool isUpdate = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseUpdate); bool isRepair = WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerExecutionUseRepair); IPackageVersion::Metadata installationMetadata; if (isUpdate || isRepair) { installationMetadata = context.Get()->GetMetadata(); } Manifest::ManifestComparator manifestComparator(GetManifestComparatorOptions(context, installationMetadata)); auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller(context.Get()); if (!installer.has_value()) { auto onlyInstalledType = std::find(inapplicabilities.begin(), inapplicabilities.end(), Manifest::InapplicabilityFlags::InstalledType); if (onlyInstalledType != inapplicabilities.end()) { if (isRepair) { context.Reporter.Info() << Resource::String::RepairDifferentInstallTechnology << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_REPAIR_NOT_APPLICABLE); } else { context.Reporter.Info() << Resource::String::UpgradeDifferentInstallTechnology << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } } } if (installer.has_value()) { Logging::Telemetry().LogSelectedInstaller( static_cast(installer->Arch), installer->Url, Manifest::InstallerTypeToString(installer->EffectiveInstallerType()), Manifest::ScopeToString(installer->Scope), installer->Locale); } context.Add(installer); } void EnsureRunningAsAdmin(Execution::Context& context) { if (!Runtime::IsRunningAsAdmin()) { context.Reporter.Error() << Resource::String::CommandRequiresAdmin; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN); } } void EnsureFeatureEnabled::operator()(Execution::Context& context) const { if (!Settings::ExperimentalFeature::IsEnabled(m_feature)) { context.Reporter.Error() << Resource::String::FeatureDisabledMessage(Utility::LocIndView{ Settings::ExperimentalFeature::GetFeature(m_feature).JsonName() }) << std::endl; AICLI_LOG(CLI, Error, << Settings::ExperimentalFeature::GetFeature(m_feature).Name() << " feature is disabled. Execution cancelled."); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED); } } void SearchSourceUsingManifest(Execution::Context& context) { const auto& manifest = context.Get(); auto source = context.Get(); // First try search using ProductId or PackageFamilyName for (const auto& installer : manifest.Installers) { SearchRequest searchRequest; if (!installer.PackageFamilyName.empty()) { searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, installer.PackageFamilyName)); } else if (!installer.ProductCode.empty()) { searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, installer.ProductCode)); } else if (installer.EffectiveInstallerType() == Manifest::InstallerTypeEnum::Portable) { const auto& productCode = Utility::MakeSuitablePathPart(manifest.Id + '_' + source.GetIdentifier()); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::CaseInsensitive, Utility::Normalize(productCode))); } else if (installer.EffectiveInstallerType() == Manifest::InstallerTypeEnum::Font) { // Font Packages match by Package Id first. searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id)); } if (!searchRequest.Inclusions.empty()) { auto searchResult = source.Search(searchRequest); if (!searchResult.Matches.empty()) { context.Add(std::move(searchResult)); return; } } } // If we cannot find a package using PackageFamilyName or ProductId, try manifest Id and Name pair SearchRequest searchRequest; searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id)); // In case there are same Ids from different sources, filter the result using package name for (const auto& localization : manifest.Localizations) { const auto& localizedPackageName = localization.Get(); if (!localizedPackageName.empty()) { searchRequest.Filters.emplace_back(PackageMatchField::Name, MatchType::CaseInsensitive, localizedPackageName); } } searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Name, MatchType::CaseInsensitive, manifest.DefaultLocalization.Get())); context.Add(source.Search(searchRequest)); } void GetInstalledPackageVersion(Execution::Context& context) { std::shared_ptr installed = context.Get()->GetInstalled(); if (installed) { // TODO: This may need to be expanded dramatically to enable targeting across a variety of dimensions (architecture, etc.) // Alternatively, if we make it easier to see the fully unique package identifiers, we may avoid that need. if (context.Args.Contains(Execution::Args::Type::TargetVersion)) { Repository::PackageVersionKey versionKey{ "", context.Args.GetArg(Execution::Args::Type::TargetVersion) , "" }; std::shared_ptr installedVersion = installed->GetVersion(versionKey); if (!installedVersion) { context.Reporter.Error() << Resource::String::GetManifestResultVersionNotFound(Utility::LocIndView{ versionKey.Version }) << std::endl; // This error maintains consistency with passing an available version to commands AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND); } context.Add(std::move(installedVersion)); } else { context.Add(installed->GetLatestVersion()); } } else { context.Add(nullptr); } } void ReportExecutionStage::operator()(Execution::Context& context) const { context.SetExecutionStage(m_stage); } void ShowAppVersions(Execution::Context& context) { auto versions = GetAllAvailableVersions(context.Get())->GetVersionKeys(); Execution::TableOutput<2> table(context.Reporter, { Resource::String::ShowVersion, Resource::String::ShowChannel }); for (const auto& version : versions) { table.OutputLine({ version.Version, version.Channel }); } table.Complete(); } } AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, AppInstaller::CLI::Workflow::WorkflowTask::Func f) { return (context << AppInstaller::CLI::Workflow::WorkflowTask(f)); } AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task) { if (!context.IsTerminated() || task.ExecuteAlways()) { #ifndef AICLI_DISABLE_TEST_HOOKS if (context.ShouldExecuteWorkflowTask(task)) #endif { task.Log(); task(context); } } return context; } ================================================ FILE: src/AppInstallerCLICore/Workflows/WorkflowBase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ExecutionArgs.h" #include "ExecutionReporter.h" #include #include #include #include #include #include #include namespace AppInstaller::CLI::Execution { struct Context; } namespace AppInstaller::CLI::Workflow { // Values are ordered in a typical workflow stages enum class ExecutionStage : uint32_t { Initial = 0, ParseArgs = 1000, Discovery = 2000, Download = 3000, PreExecution = 3500, Execution = 4000, PostExecution = 5000, }; enum class OperationType { Completion, Export, Install, List, Pin, Search, Show, Uninstall, Upgrade, Download, Repair, }; // A task in the workflow. struct WorkflowTask { using Func = void (*)(Execution::Context&); WorkflowTask(Func f) : m_isFunc(true), m_func(f) {} WorkflowTask(std::string_view name, bool executeAlways = false) : m_name(name), m_executeAlways(executeAlways) {} virtual ~WorkflowTask() = default; WorkflowTask(const WorkflowTask&) = default; WorkflowTask& operator=(const WorkflowTask&) = default; WorkflowTask(WorkflowTask&&) = default; WorkflowTask& operator=(WorkflowTask&&) = default; bool operator==(const WorkflowTask& other) const; virtual void operator()(Execution::Context& context) const; const std::string& GetName() const { return m_name; } bool IsFunction() const { return m_isFunc; } Func Function() const { return m_func; } bool ExecuteAlways() const { return m_executeAlways; } void Log() const; private: bool m_isFunc = false; Func m_func = nullptr; std::string m_name; bool m_executeAlways = false; }; // Helper to determine installed source to use based on context input. Repository::PredefinedSource DetermineInstalledSource(const Execution::Context& context); // Helper to create authentication arguments from context input. Authentication::AuthenticationArguments GetAuthenticationArguments(const Execution::Context& context); // Helper to report exceptions and return the HRESULT. // If context is null, no output will be attempted. HRESULT HandleException(Execution::Context* context, std::exception_ptr exception); // Helper to report exceptions and return the HRESULT. HRESULT HandleException(Execution::Context& context, std::exception_ptr exception); // Fills the options from the given context and metadata. AppInstaller::Manifest::ManifestComparator::Options GetManifestComparatorOptions(const Execution::Context& context, const Repository::IPackageVersion::Metadata& metadata); // Creates the source object. // Required Args: None // Inputs: None // Outputs: Source struct OpenSource : public WorkflowTask { OpenSource(bool forDependencies = false) : WorkflowTask("OpenSource"), m_forDependencies(forDependencies) {} void operator()(Execution::Context& context) const override; private: bool m_forDependencies; }; // Creates a source object for a source specified by name, and adds it to the list of open sources. // Required Args: None // Inputs: Sources? // Outputs: Sources struct OpenNamedSourceForSources : public WorkflowTask { OpenNamedSourceForSources(std::string_view sourceName) : WorkflowTask("OpenNamedSourceForSources"), m_sourceName(sourceName) {} void operator()(Execution::Context& context) const override; private: Utility::LocIndView m_sourceName; }; // Creates a source object for a predefined source. // Required Args: None // Inputs: None // Outputs: Source struct OpenPredefinedSource : public WorkflowTask { OpenPredefinedSource(Repository::PredefinedSource source, bool forDependencies = false) : WorkflowTask("OpenPredefinedSource"), m_predefinedSource(source), m_forDependencies(forDependencies) {} void operator()(Execution::Context& context) const override; private: Repository::PredefinedSource m_predefinedSource; bool m_forDependencies; }; // Creates a composite source from the given predefined source and the existing source. // Required Args: None // Inputs: Source // Outputs: Source struct OpenCompositeSource : public WorkflowTask { OpenCompositeSource( Repository::PredefinedSource source, bool forDependencies = false, Repository::CompositeSearchBehavior searchBehavior = Repository::CompositeSearchBehavior::Installed) : WorkflowTask("OpenCompositeSource"), m_predefinedSource(source), m_forDependencies(forDependencies), m_searchBehavior(searchBehavior) {} void operator()(Execution::Context& context) const override; private: Repository::PredefinedSource m_predefinedSource; bool m_forDependencies; Repository::CompositeSearchBehavior m_searchBehavior; }; // Performs a search on the source. // Required Args: None // Inputs: Source // Outputs: SearchResult void SearchSourceForMany(Execution::Context& context); // Creates a search request object with the semantics of targeting a single package. // Required Args: None // Inputs: Query, search filters (Id, Name, etc.) // Outputs: SearchRequest void GetSearchRequestForSingle(Execution::Context& context); // Performs a search on the source with the semantics of targeting a single package. // Required Args: None // Inputs: Source, SearchRequest // Outputs: SearchResult void SearchSourceForSingle(Execution::Context& context); // Performs a search on the source with the semantics of targeting many packages, // but for completion purposes. // Required Args: None // Inputs: Source, CompletionData // Outputs: SearchResult void SearchSourceForManyCompletion(Execution::Context& context); // Performs a search on the source with the semantics of targeting a single package, // but for completion purposes. // Required Args: None // Inputs: Source, CompletionData // Outputs: SearchResult void SearchSourceForSingleCompletion(Execution::Context& context); // Searches the source for the specific field as a completion. // Required Args: None // Inputs: CompletionData, Source // Outputs: None struct SearchSourceForCompletionField : public WorkflowTask { SearchSourceForCompletionField(Repository::PackageMatchField field) : WorkflowTask("SearchSourceForCompletionField"), m_field(field) {} void operator()(Execution::Context& context) const override; private: Repository::PackageMatchField m_field; }; // Outputs the search results. // Required Args: None // Inputs: SearchResult // Outputs: None void ReportSearchResult(Execution::Context& context); // Outputs the search results as the list command would show. // Required Args: None // Inputs: SearchResult // Outputs: None struct ReportListResult : public WorkflowTask { ReportListResult(bool onlyShowUpgrades = false) : WorkflowTask("ReportListResult"), m_onlyShowUpgrades(onlyShowUpgrades) {} void operator()(Execution::Context& context) const override; private: bool m_onlyShowUpgrades; }; // Handles failures in the SearchResult either by warning or failing. // Required Args: None // Inputs: SearchResult // Outputs: None void HandleSearchResultFailures(Execution::Context& context); // Outputs the search results when multiple packages found but only one expected. // Required Args: None // Inputs: SearchResult // Outputs: None void ReportMultiplePackageFoundResult(Execution::Context& context); // Outputs the search results when multiple packages found but only one expected. // Required Args: None // Inputs: SearchResult // Outputs: None void ReportMultiplePackageFoundResultWithSource(Execution::Context& context); // Ensures that there is at least one result in the search. // Required Args: bool indicating if the search result is from installed source // Inputs: SearchResult // Outputs: None struct EnsureMatchesFromSearchResult : public WorkflowTask { EnsureMatchesFromSearchResult(OperationType operation) : WorkflowTask("EnsureMatchesFromSearchResult"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: OperationType m_operationType; }; // Ensures that there is only one result in the search. // Required Args: bool indicating if the search result is from installed source // Inputs: SearchResult // Outputs: Package struct EnsureOneMatchFromSearchResult : public WorkflowTask { EnsureOneMatchFromSearchResult(OperationType operation) : WorkflowTask("EnsureOneMatchFromSearchResult"), m_operationType(operation) {} void operator()(Execution::Context& context) const override; private: OperationType m_operationType; }; // Gets the manifest from package. // Required Args: Version and channel; can be empty. A flag indicating whether to consider package pins // Inputs: Package // Outputs: Manifest, PackageVersion struct GetManifestWithVersionFromPackage : public WorkflowTask { GetManifestWithVersionFromPackage(std::string_view version, std::string_view channel, bool considerPins) : WorkflowTask("GetManifestWithVersionFromPackage"), m_version(version), m_channel(channel), m_considerPins(considerPins) {} GetManifestWithVersionFromPackage(const Utility::VersionAndChannel& versionAndChannel, bool considerPins) : GetManifestWithVersionFromPackage(versionAndChannel.GetVersion().ToString(), versionAndChannel.GetChannel().ToString(), considerPins) {} void operator()(Execution::Context& context) const override; private: std::string_view m_version; std::string_view m_channel; bool m_considerPins; }; // Gets the manifest from package. // Required Args: A value indicating whether to consider pins // Inputs: Package. Optionally Version and Channel // Outputs: Manifest, PackageVersion struct GetManifestFromPackage : public WorkflowTask { GetManifestFromPackage(bool considerPins) : WorkflowTask("GetManifestFromPackage"), m_considerPins(considerPins) {} void operator()(Execution::Context& context) const override; private: bool m_considerPins; }; // Ensures the file exists and is not a directory. // Required Args: the one given // Inputs: None // Outputs: None struct VerifyFile : public WorkflowTask { VerifyFile(Execution::Args::Type arg) : WorkflowTask("VerifyFile"), m_arg(arg) {} void operator()(Execution::Context& context) const override; private: Execution::Args::Type m_arg; }; // Ensures the path exists. // Required Args: the one given // Inputs: None // Outputs: None struct VerifyPath : public WorkflowTask { VerifyPath(Execution::Args::Type arg) : WorkflowTask("VerifyPath"), m_arg(arg) {} void operator()(Execution::Context& context) const override; private: Execution::Args::Type m_arg; }; // 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, 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. // Required Args: Manifest // Inputs: None // Outputs: Manifest void GetManifestFromArg(Execution::Context& context); // Reports the search result's package identity. // Required Args: None // Inputs: Package // Outputs: None void ReportPackageIdentity(Execution::Context& context); // Reports the installed package version identity. // Required Args: None // Inputs: InstalledPackageVersion // Outputs: None void ReportInstalledPackageVersionIdentity(Execution::Context& context); // Reports the manifest's identity. // Required Args: None // Inputs: Manifest // Outputs: None void ReportManifestIdentity(Execution::Context& context); // Reports the manifest's identity with version. // Required Args: None // Inputs: Manifest // Outputs: None struct ReportManifestIdentityWithVersion : public WorkflowTask { ReportManifestIdentityWithVersion(Utility::LocIndView prefix, Execution::Reporter::Level level = Execution::Reporter::Level::Info) : WorkflowTask("ReportManifestIdentityWithVersion"), m_prefix(prefix), m_level(level) {} ReportManifestIdentityWithVersion(Resource::StringId label = Resource::String::ReportIdentityFound, Execution::Reporter::Level level = Execution::Reporter::Level::Info) : WorkflowTask("ReportManifestIdentityWithVersion"), m_label(label), m_level(level) {} void operator()(Execution::Context& context) const override; private: Utility::LocIndView m_prefix; std::optional m_label; Execution::Reporter::Level m_level; }; // Selects the installer from the manifest, if one is applicable. // Required Args: None // Inputs: Manifest // Outputs: Installer void SelectInstaller(Execution::Context& context); // Ensures that the process is running as admin. // Required Args: None // Inputs: None // Outputs: None void EnsureRunningAsAdmin(Execution::Context& context); // Ensures that the feature is enabled. // Required Args: the desired feature // Inputs: None // Outputs: None struct EnsureFeatureEnabled : public WorkflowTask { EnsureFeatureEnabled(Settings::ExperimentalFeature::Feature feature) : WorkflowTask("EnsureFeatureEnabled"), m_feature(feature) {} void operator()(Execution::Context& context) const override; private: Settings::ExperimentalFeature::Feature m_feature; }; // Performs a search on the source with the semantics of targeting packages matching input manifest // Required Args: None // Inputs: Source, Manifest // Outputs: SearchResult void SearchSourceUsingManifest(Execution::Context& context); // Gets the installed package version // Required Args: None // Inputs: Package // Outputs: InstalledPackageVersion void GetInstalledPackageVersion(Execution::Context& context); // Shows all versions for an application. // Required Args: None // Inputs: SearchResult [only operates on first match] // Outputs: None void ShowAppVersions(Execution::Context& context); // Reports execution stage in a workflow // Required Args: ExecutionStage // Inputs: ExecutionStage? // Outputs: ExecutionStage struct ReportExecutionStage : public WorkflowTask { ReportExecutionStage(ExecutionStage stage) : WorkflowTask("ReportExecutionStage"), m_stage(stage) {} void operator()(Execution::Context& context) const override; private: ExecutionStage m_stage; }; } // Passes the context to the function if it has not been terminated; returns the context. AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, AppInstaller::CLI::Workflow::WorkflowTask::Func f); // Passes the context to the task if it has not been terminated; returns the context. AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task); ================================================ FILE: src/AppInstallerCLICore/packages.config ================================================  ================================================ FILE: src/AppInstallerCLICore/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/AppInstallerCLICore/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include #include #include #include #include #pragma warning( push ) #pragma warning ( disable : 4458 4100 6031 4702 26439 ) #include #include #include #include #pragma warning( pop ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma warning( push ) #pragma warning ( disable : 6001 6285 6340 6388 26451 ) #include #include #include #include #include #include #pragma warning( pop ) #include #include #include ================================================ FILE: src/AppInstallerCLIE2ETests/AppInstallerCLIE2ETests.csproj ================================================ net8.0-windows $(SolutionDir)$(Platform)\$(Configuration)\AppInstallerCLIE2ETests\ false x64;x86 Library $(OutDir)\AppInstallerCLIE2ETests.xml false 1591 true all runtime; build; native; contentfiles; analyzers; buildtransitive 10.0.26100.0 PreserveNewest PreserveNewest False False False PreserveNewest ================================================ FILE: src/AppInstallerCLIE2ETests/AppShutdownTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System; using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; using System.Xml; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// `test appshutdown` command tests. /// public class AppShutdownTests { /// /// Runs winget test appshutdown and register the application to force a WM_QUERYENDSESSION message. /// [Test] [Ignore("This test relied on a signal to terminate that was determined to be problematic. We may need OS fixes to test it when elevated.")] public void RegisterApplicationTest() { if (!TestSetup.Parameters.PackagedContext) { Assert.Ignore("Not packaged context."); } if (!TestCommon.ExecutingAsAdministrator && TestCommon.IsCIEnvironment) { Assert.Ignore("This test won't work on Window Server as non-admin"); } if (!Environment.Is64BitProcess) { // My guess is that HAM terminates us faster after the CTRL-C on x86... Assert.Ignore("This test is flaky when run as x86."); } if (string.IsNullOrEmpty(TestSetup.Parameters.AICLIPackagePath)) { throw new NullReferenceException("AICLIPackagePath"); } var appxManifest = Path.Combine(TestSetup.Parameters.AICLIPackagePath, "AppxManifest.xml"); if (!File.Exists(appxManifest)) { throw new FileNotFoundException(appxManifest); } // In order to registering the application we need a higher version number and pass the force app shutdown flag. // Doing it the long way. var xmlDoc = new XmlDocument(); XmlNamespaceManager namespaces = new XmlNamespaceManager(xmlDoc.NameTable); namespaces.AddNamespace("n", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); xmlDoc.Load(appxManifest); var identityNode = xmlDoc.SelectSingleNode("/n:Package/n:Identity", namespaces); if (identityNode == null) { throw new NullReferenceException("Identity node"); } var versionAttribute = identityNode.Attributes["Version"]; if (versionAttribute == null) { throw new NullReferenceException("Version attribute"); } var ogVersion = new Version(versionAttribute.Value); var newVersion = new Version(ogVersion.Major, ogVersion.Minor, ogVersion.Build, ogVersion.Revision + 1); versionAttribute.Value = newVersion.ToString(); xmlDoc.Save(appxManifest); // This just waits for the app termination event. var testCmdTask = new Task(() => { return TestCommon.RunAICLICommand("test", "appshutdown --verbose", timeOut: 300000, throwOnTimeout: false); }); // Register the app with the updated version. var registerTask = new Task(() => { return TestCommon.InstallMsixRegister(TestSetup.Parameters.AICLIPackagePath, true, false); }); // Give it a little time. testCmdTask.Start(); Thread.Sleep(30000); registerTask.Start(); Task.WaitAll(new Task[] { testCmdTask, registerTask }, 360000); // The ctrl-c command terminates the batch file before the exit code file gets created. // Look for the output. Assert.True(testCmdTask.Result.StdOut.Contains("Succeeded waiting for app shutdown event")); } /// /// Runs winget test can-unload-now expecting that it cannot be unloaded. /// [Test] public void CanUnloadNowTest() { var result = TestCommon.RunAICLICommand("test", "can-unload-now --verbose"); var lines = result.StdOut.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); Assert.AreEqual(5, lines.Length); Assert.True(lines[0].Contains("Internal objects:")); Assert.False(lines[0].Contains("Internal objects: 0")); Assert.True(lines[1].Contains("External objects: 0")); Assert.True(lines[2].Contains("DllCanUnloadNow")); Assert.True(lines[3].Contains("Internal objects: 0")); Assert.True(lines[4].Contains("External objects: 0")); } /// /// Runs winget test term-signal-handler to check for proper thread termination. /// [Test] public void TermSignalHandler() { var result = TestCommon.RunAICLICommand("test", "term-signal-handler --verbose"); Assert.True(result.StdOut.Contains("Got a window handle")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/BaseCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Base command. /// public class BaseCommand { /// /// Set up. /// [OneTimeSetUp] public void BaseSetup() { this.ResetTestSource(); } /// /// Tear down. /// [OneTimeTearDown] public void BaseTeardown() { TestCommon.TearDownTestSource(); } /// /// Reset test source. /// /// Use group policy from test source. public void ResetTestSource(bool useGroupPolicyForTestSource = false) { // TODO: If/when cert pinning is implemented on the packaged index source, useGroupPolicyForTestSource should be set to default true // to enable testing it by default. Until then, leaving this here... TestCommon.SetupTestSource(useGroupPolicyForTestSource); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ConfigureCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System; using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// `Configure` command tests. /// public class ConfigureCommand { private const string CommandAndAgreementsAndVerbose = "configure --accept-configuration-agreements --verbose"; /// /// Ensures that the test resources manifests are present. /// public static void EnsureTestResourcePresence() { DSCv3ResourceTestBase.EnsureTestResourcePresence(); } /// /// Setup done once before all the tests here. /// [OneTimeSetUp] public void OneTimeSetup() { this.DeleteResourceArtifacts(); EnsureTestResourcePresence(); TestCommon.SetupTestSource(false); } /// /// Teardown done once after all the tests here. /// [OneTimeTearDown] public void OneTimeTeardown() { this.DeleteResourceArtifacts(); TestCommon.TearDownTestSource(); } /// /// Simple test to confirm that a resource without a module specified can be discovered in the PSGallery. /// Intentionally has no settings to force a failure, but only after acquiring the module. /// [Test] [Ignore("PS Gallery tests are unreliable.")] public void ConfigureFromGallery() { TestCommon.EnsureModuleState(Constants.GalleryTestModuleName, present: false); var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\PSGallery_NoModule_NoSettings.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_APPLY_FAILED, result.ExitCode); Assert.True(result.StdOut.Contains("The configuration unit failed while attempting to test the current system state.")); } /// /// Simple test to confirm that a resource with a module specified can be discovered in a local repository that doesn't support resource discovery. /// [Test] public void ConfigureFromTestRepo() { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.txt"); FileAssert.Exists(targetFilePath); Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); Assert.True(Directory.Exists( Path.Combine( TestCommon.GetExpectedModulePath(TestCommon.TestModuleLocation.Default), Constants.SimpleTestModuleName))); } /// /// Simple test to confirm that the module was installed to the location specified in the DefaultModuleRoot settings. /// [Test] public void ConfigureFromTestRepo_DefaultModuleRootSetting() { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); string moduleTestDir = TestCommon.GetRandomTestDir(); WinGetSettingsHelper.ConfigureConfigureBehavior(Constants.DefaultModuleRoot, moduleTestDir); string args = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo_Location.yml"); var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, args); WinGetSettingsHelper.ConfigureConfigureBehavior(Constants.DefaultModuleRoot, string.Empty); bool moduleExists = Directory.Exists(Path.Combine(moduleTestDir, Constants.SimpleTestModuleName)); if (moduleExists) { // Clean test directory to avoid impacting other tests. Directory.Delete(moduleTestDir, true); } Assert.AreEqual(0, result.ExitCode); Assert.True(moduleExists); } /// /// Simple test to confirm that the module was installed in the right location. /// /// Location to pass. [TestCase(TestCommon.TestModuleLocation.CurrentUser)] [TestCase(TestCommon.TestModuleLocation.AllUsers)] [TestCase(TestCommon.TestModuleLocation.WinGetModulePath)] [TestCase(TestCommon.TestModuleLocation.Custom)] [TestCase(TestCommon.TestModuleLocation.Default)] public void ConfigureFromTestRepo_Location(TestCommon.TestModuleLocation location) { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); string args = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo_Location.yml"); if (location == TestCommon.TestModuleLocation.CurrentUser) { args += " --module-path currentuser"; } else if (location == TestCommon.TestModuleLocation.AllUsers) { args += " --module-path allusers"; } else if (location == TestCommon.TestModuleLocation.Default) { args += " --module-path default"; } else if (location == TestCommon.TestModuleLocation.Custom) { args += " --module-path " + TestCommon.GetExpectedModulePath(location); } var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, args); Assert.AreEqual(0, result.ExitCode); Assert.True(Directory.Exists(Path.Combine( TestCommon.GetExpectedModulePath(location), Constants.SimpleTestModuleName))); } /// /// One resource fails, but the other is not dependent and should be executed. /// [Test] public void IndependentResourceWithSingleFailure() { var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\IndependentResources_OneFailure.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_APPLY_FAILED, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\IndependentResources_OneFailure.txt"); FileAssert.Exists(targetFilePath); Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); } /// /// One resource fails, and the dependent resource should not be executed. /// [Test] public void DependentResourceWithFailure() { var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\DependentResources_Failure.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_APPLY_FAILED, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\DependentResources_Failure.txt"); FileAssert.DoesNotExist(targetFilePath); } /// /// The configuration server unexpectedly exits. Winget should continue to operate properly. /// [Test] public void ConfigServerUnexpectedExit() { var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\ConfigServerUnexpectedExit.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_APPLY_FAILED, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\ConfigServerUnexpectedExit.txt"); FileAssert.DoesNotExist(targetFilePath); } /// /// Resource name case-insensitive test. /// [Test] public void ResourceCaseInsensitive() { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\ResourceCaseInsensitive.yml")); Assert.AreEqual(0, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\ResourceCaseInsensitive.txt"); FileAssert.Exists(targetFilePath); Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); } /// /// Simple test to configure from an https configuration file. /// [Test] public void ConfigureFromHttpsConfigurationFile() { string args = $"{Constants.TestSourceUrl}/TestData/Configuration/Configure_TestRepo_Location.yml"; var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, args); Assert.AreEqual(0, result.ExitCode); } /// /// Runs a configuration, then changes the state and runs it again from history. /// [Test] public void ConfigureFromHistory() { var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.txt"); FileAssert.Exists(targetFilePath); Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); File.WriteAllText(targetFilePath, "Changed contents!"); string guid = TestCommon.GetConfigurationInstanceIdentifierFor("Configure_TestRepo.yml"); result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, $"-h {guid}"); Assert.AreEqual(0, result.ExitCode); FileAssert.Exists(targetFilePath); Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); } /// /// Specifies the module path to an "elevated" server. /// [Test] public void SpecifyModulePathToHighIntegrityServer() { string configFile = TestCommon.GetTestDataFile("Configuration\\GetPSModulePath.yml"); string testDirectory = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, $"{configFile} --module-path \"{testDirectory}\""); Assert.AreEqual(0, result.ExitCode); string testFile = Path.Join(TestCommon.GetTestDataFile("Configuration"), "PSModulePath.txt"); Assert.True(File.Exists(testFile)); string testFileContents = File.ReadAllText(testFile); Assert.True(testFileContents.StartsWith(testDirectory)); } /// /// Runs a DSCv3 configuration, then changes the state and runs it again from history. /// [Test] public void ConfigureThroughHistory_DSCv3() { var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\ShowDetails_DSCv3.yml")); Assert.AreEqual(0, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\ShowDetails_DSCv3.txt"); FileAssert.Exists(targetFilePath); Assert.AreEqual("DSCv3 Contents!", File.ReadAllText(targetFilePath)); File.WriteAllText(targetFilePath, "Changed contents!"); string guid = TestCommon.GetConfigurationInstanceIdentifierFor("ShowDetails_DSCv3.yml"); result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, $"-h {guid}"); Assert.AreEqual(0, result.ExitCode); FileAssert.Exists(targetFilePath); Assert.AreEqual("DSCv3 Contents!", File.ReadAllText(targetFilePath)); } /// /// Ensures that the test file resource schema function works. /// [Test] public void TestFileResourceSchema() { var result = TestCommon.RunAICLICommand("dscv3 test-file", "--schema"); Assert.AreEqual(0, result.ExitCode); var lines = result.StdOut.Split("\r\n", StringSplitOptions.RemoveEmptyEntries); Assert.AreEqual(1, lines.Length); } /// /// Export all with specific package id. /// [Test] public void DSCv3_Export() { // Reset state var result = TestCommon.RunAICLICommand("dscv3 test-json", "--delete"); Assert.AreEqual(0, result.ExitCode); // Configure properties string propertyName1 = "prop1"; string propertyName2 = "prop2"; string propertyValue1 = "val1"; string propertyValue2 = "val2"; string propertySetFormatString = "{{ \"property\": \"{0}\", \"value\": \"{1}\" }}"; result = TestCommon.RunAICLICommand("dscv3 test-json", "--set", string.Format(propertySetFormatString, propertyName1, propertyValue1)); Assert.AreEqual(0, result.ExitCode); result = TestCommon.RunAICLICommand("dscv3 test-json", "--set", string.Format(propertySetFormatString, propertyName2, propertyValue2)); Assert.AreEqual(0, result.ExitCode); // Export var exportDir = TestCommon.GetRandomTestDir(); var exportFile = Path.Combine(exportDir, "exported.yml"); result = TestCommon.RunAICLICommand("test config-export-units", $"-o {exportFile} --resource Microsoft.WinGet.Dev/TestJSON --verbose"); Assert.AreEqual(0, result.ExitCode); Assert.True(File.Exists(exportFile)); string exportText = File.ReadAllText(exportFile); Assert.True(exportText.Contains("Microsoft.WinGet.Dev/TestJSON")); Assert.True(exportText.Contains(propertyName1)); Assert.True(exportText.Contains(propertyName2)); Assert.True(exportText.Contains(propertyValue1)); Assert.True(exportText.Contains(propertyValue2)); } /// /// Simple test to confirm that a resource with a module specified can be discovered in a local repository that doesn't support resource discovery. /// [Test] public void ConfigureFromTestRepo_DSCv3() { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: true, repository: Constants.TestRepoName); this.DeleteResourceArtifacts(); var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo_DSCv3.yml"), timeOut: 300000); Assert.AreEqual(0, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.txt"); FileAssert.Exists(targetFilePath); Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); } /// /// Find unit processors tests. /// [Test] public void ConfigureFindUnitProcessors() { // Find all unit processors. var result = TestCommon.RunAICLICommand("test config-find-unit-processors", string.Empty, timeOut: 300000); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains("Microsoft/OSInfo")); // Setup TestExeInstaller with dsc resources. var installDir = TestCommon.GetRandomTestDir(); result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --override \"/InstallDir {installDir} /GenerateDscResourceFiles\""); Assert.AreEqual(0, result.ExitCode); // Find unit processors filtering to install location. result = TestCommon.RunAICLICommand("test config-find-unit-processors", $"-l {installDir}"); Assert.AreEqual(0, result.ExitCode); Assert.False(result.StdOut.Contains("Microsoft/OSInfo")); Assert.True(result.StdOut.Contains("AppInstallerTest/TestResource")); // Find unit processors filtering to unknown location. var unknownDir = TestCommon.GetRandomTestDir(); result = TestCommon.RunAICLICommand("test config-find-unit-processors", $"-l {unknownDir}"); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains("No unit processors found.")); // Clean up result = TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(0, result.ExitCode); } /// /// RunCommandOnSet test. /// [Test] public void RunCommandOnSetResourceTest() { var testDir = TestCommon.GetRandomTestDir(); var testConfigFile = Path.Combine(testDir, "RunCommandOnSet.yml"); File.Copy(TestCommon.GetTestDataFile("Configuration\\RunCommandOnSet.yml"), testConfigFile); var content = File.ReadAllText(testConfigFile); content = content.Replace("", testDir); File.WriteAllText(testConfigFile, content); var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, testConfigFile, timeOut: 300000); Assert.AreEqual(0, result.ExitCode); // Verify test file created. string targetFilePath = Path.Combine(testDir, "TestFile.txt"); FileAssert.Exists(targetFilePath); string testContent = File.ReadAllText(targetFilePath); Assert.True(testContent.Contains("TestContent")); } private void DeleteResourceArtifacts() { // Delete all .txt files in the test directory; they are placed there by the tests foreach (string file in Directory.GetFiles(TestCommon.GetTestDataFile("Configuration"), "*.txt")) { File.Delete(file); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ConfigureExportCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; using NUnit.Framework.Internal; /// /// `Configure export` command tests. /// public class ConfigureExportCommand { private const string Command = "configure export"; private const string ShowCommand = "configure show"; private string previousPathValue = string.Empty; /// /// Set up. /// [OneTimeSetUp] public void BaseSetup() { TestCommon.SetupTestSource(false); var installDir = TestCommon.GetRandomTestDir(); TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestPackageExport -v 1.0.0.0 --silent -l {installDir}"); this.previousPathValue = System.Environment.GetEnvironmentVariable("PATH"); // The installer puts DSCv3 resources in both locations System.Environment.SetEnvironmentVariable("PATH", this.previousPathValue + ";" + installDir + ";" + installDir + "\\SubDirectory"); DSCv3ResourceTestBase.EnsureTestResourcePresence(); } /// /// Tear down. /// [OneTimeTearDown] public void BaseTeardown() { TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestPackageExport"); TestCommon.TearDownTestSource(); if (!string.IsNullOrEmpty(this.previousPathValue)) { System.Environment.SetEnvironmentVariable("PATH", this.previousPathValue); } } /// /// Export a specific package. /// [Test] public void ExportTestPackage() { var exportDir = TestCommon.GetRandomTestDir(); var exportFile = Path.Combine(exportDir, "exported.yml"); var result = TestCommon.RunAICLICommand(Command, $"--package-id AppInstallerTest.TestPackageExport -o {exportFile}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(File.Exists(exportFile)); // Check exported file is readable and validate content var showResult = TestCommon.RunAICLICommand(ShowCommand, $"-f {exportFile}"); Assert.AreEqual(Constants.ErrorCode.S_OK, showResult.ExitCode); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/Source")); Assert.True(showResult.StdOut.Contains($"[{Constants.TestSourceName}_{Constants.TestSourceType}]")); Assert.True(showResult.StdOut.Contains($"type: {Constants.TestSourceType}")); Assert.True(showResult.StdOut.Contains($"argument: {Constants.TestSourceUrl}")); Assert.True(showResult.StdOut.Contains($"name: {Constants.TestSourceName}")); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/Package")); Assert.True(showResult.StdOut.Contains($"[{Constants.TestSourceName}_AppInstallerTest.TestPackageExport]")); Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_{Constants.TestSourceType}")); Assert.True(showResult.StdOut.Contains("id: AppInstallerTest.TestPackageExport")); Assert.True(showResult.StdOut.Contains($"source: {Constants.TestSourceName}")); } /// /// Export a specific package with related configuration. /// [Test] public void ExportTestPackageWithPackageSettings() { var exportDir = TestCommon.GetRandomTestDir(); var exportFile = Path.Combine(exportDir, "exported.yml"); var result = TestCommon.RunAICLICommand(Command, $"--package-id AppInstallerTest.TestPackageExport --module AppInstallerTest --resource TestResource -o {exportFile}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(File.Exists(exportFile)); // Check exported file is readable and validate content var showResult = TestCommon.RunAICLICommand(ShowCommand, $"-f {exportFile}"); Assert.AreEqual(Constants.ErrorCode.S_OK, showResult.ExitCode); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/Source")); Assert.True(showResult.StdOut.Contains($"[{Constants.TestSourceName}_{Constants.TestSourceType}]")); Assert.True(showResult.StdOut.Contains($"type: {Constants.TestSourceType}")); Assert.True(showResult.StdOut.Contains($"argument: {Constants.TestSourceUrl}")); Assert.True(showResult.StdOut.Contains($"name: {Constants.TestSourceName}")); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/Package")); Assert.True(showResult.StdOut.Contains($"[{Constants.TestSourceName}_AppInstallerTest.TestPackageExport]")); Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_{Constants.TestSourceType}")); Assert.True(showResult.StdOut.Contains("id: AppInstallerTest.TestPackageExport")); Assert.True(showResult.StdOut.Contains($"source: {Constants.TestSourceName}")); Assert.True(showResult.StdOut.Contains("AppInstallerTest/TestResource")); Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_AppInstallerTest.TestPackageExport")); Assert.True(showResult.StdOut.Contains("data: TestData")); } /// /// Export a specific package with version. /// [Test] public void ExportTestPackageWithVersion() { var exportDir = TestCommon.GetRandomTestDir(); var exportFile = Path.Combine(exportDir, "exported.yml"); var result = TestCommon.RunAICLICommand(Command, $"--package-id AppInstallerTest.TestPackageExport --include-versions -o {exportFile}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(File.Exists(exportFile)); // Check exported file is readable and validate content var showResult = TestCommon.RunAICLICommand(ShowCommand, $"-f {exportFile}"); Assert.AreEqual(Constants.ErrorCode.S_OK, showResult.ExitCode); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/Source")); Assert.True(showResult.StdOut.Contains($"[{Constants.TestSourceName}_{Constants.TestSourceType}]")); Assert.True(showResult.StdOut.Contains($"type: {Constants.TestSourceType}")); Assert.True(showResult.StdOut.Contains($"argument: {Constants.TestSourceUrl}")); Assert.True(showResult.StdOut.Contains($"name: {Constants.TestSourceName}")); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/Package")); Assert.True(showResult.StdOut.Contains($"[{Constants.TestSourceName}_AppInstallerTest.TestPackageExport]")); Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_{Constants.TestSourceType}")); Assert.True(showResult.StdOut.Contains("id: AppInstallerTest.TestPackageExport")); Assert.True(showResult.StdOut.Contains($"source: {Constants.TestSourceName}")); Assert.True(showResult.StdOut.Contains("version: 1.0.0.0")); } /// /// Export all. /// [Test] public void ExportAll() { var exportDir = TestCommon.GetRandomTestDir(); var exportFile = Path.Combine(exportDir, "exported.yml"); var result = TestCommon.RunAICLICommand(Command, $"--all --verbose -o {exportFile}", timeOut: 1200000); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(File.Exists(exportFile)); // Check exported file is readable and validate content var showResult = TestCommon.RunAICLICommand(ShowCommand, $"-f {exportFile}", timeOut: 1200000); Assert.AreEqual(Constants.ErrorCode.S_OK, showResult.ExitCode); Assert.True(showResult.StdOut.Contains("Microsoft.PowerShell")); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/UserSettingsFile")); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/AdminSettings")); Assert.True(showResult.StdOut.Contains("Microsoft.Windows.Settings/WindowsSettings")); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/Source")); Assert.True(showResult.StdOut.Contains($"[{Constants.TestSourceName}_{Constants.TestSourceType}]")); Assert.True(showResult.StdOut.Contains($"type: {Constants.TestSourceType}")); Assert.True(showResult.StdOut.Contains($"argument: {Constants.TestSourceUrl}")); Assert.True(showResult.StdOut.Contains($"name: {Constants.TestSourceName}")); Assert.True(showResult.StdOut.Contains("Microsoft.WinGet.Dev/Package")); Assert.True(showResult.StdOut.Contains($"[{Constants.TestSourceName}_AppInstallerTest.TestPackageExport]")); Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_{Constants.TestSourceType}")); Assert.True(showResult.StdOut.Contains("id: AppInstallerTest.TestPackageExport")); Assert.True(showResult.StdOut.Contains($"source: {Constants.TestSourceName}")); Assert.True(showResult.StdOut.Contains("AppInstallerTest/TestResource")); Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_AppInstallerTest.TestPackageExport")); Assert.True(showResult.StdOut.Contains("data: TestData")); Assert.True(showResult.StdOut.Contains("AppInstallerTest/TestResource.SubDirectory")); Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_AppInstallerTest.TestPackageExport")); Assert.True(showResult.StdOut.Contains("data: TestData")); } /// /// Export a specific package that's not installed. /// [Test] public void ExportFailedWithNotFoundPackage() { var exportDir = TestCommon.GetRandomTestDir(); var exportFile = Path.Combine(exportDir, "exported.yml"); var result = TestCommon.RunAICLICommand(Command, $"--package-id NotFound.NotFound -o {exportFile}"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); } /// /// Export all with specific package id. /// [Test] public void ExportFailedWithAllAndSpecificPackage() { var exportDir = TestCommon.GetRandomTestDir(); var exportFile = Path.Combine(exportDir, "exported.yml"); var result = TestCommon.RunAICLICommand(Command, $"--all --package-id AppInstallerTest.TestPackageExport -o {exportFile}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INVALID_CL_ARGUMENTS, result.ExitCode); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ConfigureListCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// `Configure list` command tests. /// public class ConfigureListCommand { private const string ConfigureWithAgreementsAndVerbose = "configure --accept-configuration-agreements --verbose"; private const string ConfigureTestRepoFile = "Configure_TestRepo.yml"; /// /// Teardown done once after all the tests here. /// [OneTimeTearDown] public void OneTimeTeardown() { this.DeleteTxtFiles(); } /// /// Applies a configuration, then verifies that it is in the overall list. /// [Test] public void ListAllConfigurations() { var result = TestCommon.RunAICLICommand(ConfigureWithAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); result = TestCommon.RunAICLICommand("configure list", "--verbose"); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains(ConfigureTestRepoFile)); } /// /// Applies a configuration (to ensure at least one exists), gets the overall list, then the details about the first configuration. /// [Test] public void ListSpecificConfiguration() { var result = TestCommon.RunAICLICommand(ConfigureWithAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); string guid = TestCommon.GetConfigurationInstanceIdentifierFor(ConfigureTestRepoFile); result = TestCommon.RunAICLICommand("configure list", $"-h {guid}"); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains(guid)); Assert.True(result.StdOut.Contains(ConfigureTestRepoFile)); } /// /// Applies a configuration (to ensure at least one exists), gets the overall list, then the removes the first configuration. /// [Test] public void RemoveConfiguration() { var result = TestCommon.RunAICLICommand(ConfigureWithAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); string guid = TestCommon.GetConfigurationInstanceIdentifierFor(ConfigureTestRepoFile); result = TestCommon.RunAICLICommand("configure list", $"-h {guid} --remove"); Assert.AreEqual(0, result.ExitCode); result = TestCommon.RunAICLICommand("configure list", "--verbose"); Assert.AreEqual(0, result.ExitCode); Assert.False(result.StdOut.Contains(guid)); } /// /// Applies a configuration (to ensure at least one exists), gets the overall list, then the outputs the first configuration. /// [Test] public void OutputConfiguration() { var result = TestCommon.RunAICLICommand(ConfigureWithAgreementsAndVerbose, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); string guid = TestCommon.GetConfigurationInstanceIdentifierFor(ConfigureTestRepoFile); string tempFile = TestCommon.GetRandomTestFile(".yml"); result = TestCommon.RunAICLICommand("configure list", $"-h {guid} --output {tempFile}"); Assert.AreEqual(0, result.ExitCode); result = TestCommon.RunAICLICommand("configure validate", $"--verbose {tempFile}"); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); } private void DeleteTxtFiles() { // Delete all .txt files in the test directory; they are placed there by the tests foreach (string file in Directory.GetFiles(TestCommon.GetTestDataFile("Configuration"), "*.txt")) { File.Delete(file); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ConfigureShowCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Win32; using NUnit.Framework; /// /// `Configure show` command tests. /// public class ConfigureShowCommand { /// /// Setup done once before all the tests here. /// [OneTimeSetUp] public void OneTimeSetup() { this.DeleteResourceArtifacts(); ConfigureCommand.EnsureTestResourcePresence(); } /// /// One time teardown. /// [OneTimeTearDown] public void OneTimeTearDown() { this.DeleteResourceArtifacts(); } /// /// Simple test to confirm that a resource without a module specified can be discovered in the PSGallery. /// [Test] [Ignore("PS Gallery tests are unreliable.")] public void ShowDetailsFromGallery() { TestCommon.EnsureModuleState(Constants.GalleryTestModuleName, present: false); var result = TestCommon.RunAICLICommand("configure show", $"{TestCommon.GetTestDataFile("Configuration\\PSGallery_NoModule_NoSettings.yml")} --verbose", timeOut: 120000); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.PSGalleryName)); } /// /// Simple test to confirm that a resource with a module specified can be discovered in a local repository that doesn't support resource discovery. /// [Test] public void ShowDetailsFromTestRepo() { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); var result = TestCommon.RunAICLICommand("configure show", $"{TestCommon.GetTestDataFile("Configuration\\ShowDetails_TestRepo.yml")} --verbose"); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.TestRepoName)); } /// /// Simple test to confirm that a resource that is already locally available shows that way. /// /// The location the module should be before running. [TestCase(TestCommon.TestModuleLocation.CurrentUser)] [TestCase(TestCommon.TestModuleLocation.AllUsers)] [TestCase(TestCommon.TestModuleLocation.WinGetModulePath)] [TestCase(TestCommon.TestModuleLocation.Custom)] public void ShowDetailsFromLocal(TestCommon.TestModuleLocation location) { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: true, repository: Constants.TestRepoName, location: location); string args = $"{TestCommon.GetTestDataFile("Configuration\\ShowDetails_TestRepo.yml")} --verbose"; if (location == TestCommon.TestModuleLocation.Custom) { args += " --module-path " + TestCommon.GetExpectedModulePath(location); } var result = TestCommon.RunAICLICommand("configure show", args); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.LocalModuleDescriptor)); } /// /// A schema 0.3 config file is allowed with the experimental feature. /// [Test] public void ShowDetails_Schema0_3_Succeeds() { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); var result = TestCommon.RunAICLICommand("configure show", $"{TestCommon.GetTestDataFile("Configuration\\ShowDetails_TestRepo_0_3.yml")} --verbose"); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.TestRepoName)); } /// /// A schema 0.3 config file with parameters is blocked. /// [Test] public void ShowDetails_Schema0_3_Parameters() { var result = TestCommon.RunAICLICommand("configure show", TestCommon.GetTestDataFile("Configuration\\WithParameters_0_3.yml")); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains("Failed to get detailed information about the configuration.")); } /// /// 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)); } /// /// This test ensures that there is not significant overflow from large strings in the configuration file. /// [Test] public void ShowTruncatedDetailsAndFileContent() { var result = TestCommon.RunAICLICommand("configure show", $"{TestCommon.GetTestDataFile("Configuration\\LargeContentStrings.yml")} --verbose"); Assert.AreEqual(0, result.ExitCode); Assert.True(result.StdOut.Contains("")); Assert.True(result.StdOut.Contains("Some of the data present in the configuration file was truncated for this output; inspect the file contents for the complete content.")); Assert.False(result.StdOut.Contains("Line5")); } /// /// Runs a configuration, then shows it from history. /// [Test] public void ShowFromHistory() { var result = TestCommon.RunAICLICommand("configure --accept-configuration-agreements --verbose", TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); string guid = TestCommon.GetConfigurationInstanceIdentifierFor("Configure_TestRepo.yml"); result = TestCommon.RunAICLICommand("configure show", $"-h {guid}"); Assert.AreEqual(0, result.ExitCode); } /// /// Runs a configuration, then shows it from history. /// [Test] public void ShowWithBadProcessorIdentifier() { var result = TestCommon.RunAICLICommand("configure show", $"{TestCommon.GetTestDataFile("Configuration\\Unknown_Processor.yml")} --verbose"); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_INVALID_FIELD_VALUE, result.ExitCode); } /// /// Simple test to confirm that a resource is discoverable with DSC v3. /// [Test] public void ShowDetails_DSCv3() { var result = TestCommon.RunAICLICommand("configure show", $"{TestCommon.GetTestDataFile("Configuration\\ShowDetails_DSCv3.yml")} --verbose"); Assert.AreEqual(0, result.ExitCode); var outputLines = result.StdOut.Split('\n'); int startLine = -1; for (int i = 0; i < outputLines.Length; ++i) { if (outputLines[i].Trim() == "Microsoft.WinGet.Dev/TestFile [Test File]") { startLine = i; } } Assert.AreNotEqual(-1, startLine); Assert.LessOrEqual(3, outputLines.Length - startLine); // outputLines[1] should contain the discovered resource string if working properly. Assert.AreEqual("Description 1.", outputLines[startLine + 2].Trim()); } /// /// Runs a DSCv3 configuration, then shows it from history. /// [Test] public void ShowFromHistory_DSCv3() { var result = TestCommon.RunAICLICommand("configure --accept-configuration-agreements --verbose", TestCommon.GetTestDataFile("Configuration\\ShowDetails_DSCv3.yml")); Assert.AreEqual(0, result.ExitCode); string guid = TestCommon.GetConfigurationInstanceIdentifierFor("ShowDetails_DSCv3.yml"); result = TestCommon.RunAICLICommand("configure show", $"-h {guid} --"); Assert.AreEqual(0, result.ExitCode); var outputLines = result.StdOut.Split('\n'); int startLine = -1; for (int i = 0; i < outputLines.Length; ++i) { if (outputLines[i].Trim() == "Microsoft.WinGet.Dev/TestFile [Test File]") { startLine = i; } } Assert.AreNotEqual(-1, startLine); Assert.LessOrEqual(3, outputLines.Length - startLine); // outputLines[1] should contain the discovered resource string if working properly. Assert.AreEqual("Description 1.", outputLines[startLine + 2].Trim()); } private void DeleteResourceArtifacts() { // Delete all .txt files in the test directory; they are placed there by the tests foreach (string file in Directory.GetFiles(TestCommon.GetTestDataFile("Configuration"), "*.txt")) { File.Delete(file); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ConfigureTestCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Win32; using NUnit.Framework; /// /// `Configure test` command tests. /// public class ConfigureTestCommand { private const string CommandAndAgreements = "configure test --accept-configuration-agreements"; /// /// Setup done once before all the tests here. /// [OneTimeSetUp] public void OneTimeSetup() { this.DeleteResourceArtifacts(); ConfigureCommand.EnsureTestResourcePresence(); } /// /// Teardown done once after all the tests here. /// [OneTimeTearDown] public void OneTimeTeardown() { this.DeleteResourceArtifacts(); } /// /// Checks for a resource not in the desired state. /// [Test] public void ConfigureTest_NotInDesiredState() { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); this.DeleteResourceArtifacts(); var result = TestCommon.RunAICLICommand(CommandAndAgreements, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("System is not in the described configuration state.")); Assert.True(result.StdOut.Contains("Module: xE2ETestResource")); // Details from the resource should appear if the initial details are shown } /// /// Checks for a resource in a desired state. /// [Test] public void ConfigureTest_InDesiredState() { TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false); this.DeleteResourceArtifacts(); // Set up the expected state File.WriteAllText(TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.txt"), "Contents!"); var result = TestCommon.RunAICLICommand(CommandAndAgreements, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("System is in the described configuration state.")); Assert.True(result.StdOut.Contains("Module: xE2ETestResource")); // Details from the resource should appear if the initial details are shown } /// /// One resource fails. /// [Test] public void ConfigureTest_TestFailure() { var result = TestCommon.RunAICLICommand(CommandAndAgreements, TestCommon.GetTestDataFile("Configuration\\IndependentResources_OneFailure.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_TEST_FAILED, result.ExitCode); Assert.True(result.StdOut.Contains("Some of the configuration units failed while testing their state.")); 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}/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.")); } /// /// Runs a configuration, then tests it from history. /// [Test] public void TestFromHistory() { var result = TestCommon.RunAICLICommand("configure --accept-configuration-agreements --verbose", TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); // The configuration creates a file next to itself with the given contents string targetFilePath = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.txt"); FileAssert.Exists(targetFilePath); Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); string guid = TestCommon.GetConfigurationInstanceIdentifierFor("Configure_TestRepo.yml"); result = TestCommon.RunAICLICommand(CommandAndAgreements, $"-h {guid}"); Assert.AreEqual(0, result.ExitCode); File.WriteAllText(targetFilePath, "Changed contents!"); result = TestCommon.RunAICLICommand(CommandAndAgreements, $"-h {guid}"); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); } /// /// Simple test to confirm that a resource is testable with DSC v3. /// [Test] public void ConfigureTest_DSCv3() { var result = TestCommon.RunAICLICommand(CommandAndAgreements, $"{TestCommon.GetTestDataFile("Configuration\\ShowDetails_DSCv3.yml")} --verbose"); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("System is not in the described configuration state.")); } /// /// Tests that --suppress-initial-details will suppress the initial details output. /// [Test] public void ConfigureTest_SuppressInitialDetails() { var result = TestCommon.RunAICLICommand("configure --accept-configuration-agreements --suppress-initial-details", TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(0, result.ExitCode); Assert.False(result.StdOut.Contains("Module: xE2ETestResource")); // Details from the resource should not appear if the initial details are suppressed } private void DeleteResourceArtifacts() { // Delete all .txt files in the test directory; they are placed there by the tests foreach (string file in Directory.GetFiles(TestCommon.GetTestDataFile("Configuration"), "*.txt")) { File.Delete(file); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ConfigureValidateCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// `Configure validate` command tests. /// 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. /// [Test] public void EmptyFile() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\Empty.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_INVALID_YAML, result.ExitCode); } /// /// The configuration file is not configuration YAML. /// [Test] public void NotConfigurationYAML() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\NotConfig.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_MISSING_FIELD, result.ExitCode); Assert.True(result.StdOut.Contains("$schema")); Assert.True(result.StdOut.Contains("missing")); } /// /// The configuration file does not specify the schema version. /// [Test] public void NoVersion() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\NoVersion.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_MISSING_FIELD, result.ExitCode); Assert.True(result.StdOut.Contains("configurationVersion")); Assert.True(result.StdOut.Contains("missing")); } /// /// The configuration file schema version is not known. /// [Test] public void UnknownVersion() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\UnknownVersion.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, result.ExitCode); Assert.True(result.StdOut.Contains("Configuration file version")); Assert.True(result.StdOut.Contains("is not known.")); } /// /// The resources node is not the correct type in YAML. /// [Test] public void ResourcesIsWrongType() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\ResourcesNotASequence.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_INVALID_FIELD_TYPE, result.ExitCode); Assert.True(result.StdOut.Contains("resources")); Assert.True(result.StdOut.Contains("wrong type")); } /// /// The unit node is not the correct type in YAML. /// [Test] public void UnitIsWrongType() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\UnitNotAMap.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_INVALID_FIELD_TYPE, result.ExitCode); Assert.True(result.StdOut.Contains("resources[0]")); Assert.True(result.StdOut.Contains("wrong type")); } /// /// The resource name is missing. /// [Test] public void NoResourceName() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\NoResourceName.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_INVALID_FIELD_VALUE, result.ExitCode); Assert.True(result.StdOut.Contains("resource")); Assert.True(result.StdOut.Contains("invalid value")); Assert.True(result.StdOut.Contains("Module/")); } /// /// The resource name module does not match the directives module. /// [Test] public void ModuleMismatch() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\ModuleMismatch.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_INVALID_FIELD_VALUE, result.ExitCode); Assert.True(result.StdOut.Contains("module")); Assert.True(result.StdOut.Contains("invalid value")); Assert.True(result.StdOut.Contains("DifferentModule")); } /// /// The configuration contains multiple resources with the same identifier. /// [Test] public void DuplicateIdentifiers() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\DuplicateIdentifiers.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_DUPLICATE_IDENTIFIER, result.ExitCode); Assert.True(result.StdOut.Contains("The configuration contains the identifier `same` multiple times.")); Assert.False(result.StdOut.Contains("NotMentioned")); } /// /// The configuration does not contain the dependency. /// [Test] public void MissingDependency() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\MissingDependency.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_MISSING_DEPENDENCY, result.ExitCode); Assert.True(result.StdOut.Contains("The dependency `missing` was not found within the configuration.")); Assert.False(result.StdOut.Contains("xE2ETestResource")); } /// /// The configuration contains a dependency cycle. /// [Test] public void DependencyCycle() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\DependencyCycle.yml")); Assert.AreEqual(Constants.ErrorCode.CONFIG_ERROR_SET_DEPENDENCY_CYCLE, result.ExitCode); Assert.True(result.StdOut.Contains("This configuration unit is part of a dependency cycle.")); Assert.False(result.StdOut.Contains("NotMentioned")); } /// /// The configuration unit is not available in a public catalog. /// [Test] public void ResourceIsNotPublic() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo.yml")); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("not available publicly")); } /// /// The configuration unit is not found. /// [Test] public void ResourceIsNotFound() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\ResourceNotFound.yml")); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("The configuration unit could not be found.")); } /// /// The module was not provided. /// [Test] public void ModuleNotProvided() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\PSGallery_NoModule_NoSettings.yml"), timeOut: 120000); Assert.AreEqual(Constants.ErrorCode.S_FALSE, result.ExitCode); Assert.True(result.StdOut.Contains("The module was not provided.")); } /// /// No issues detected (yet). /// [Test] public void NoIssuesDetected() { var result = TestCommon.RunAICLICommand(Command, TestCommon.GetTestDataFile("Configuration\\PSGallery_NoSettings.yml"), timeOut: 120000); 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}/TestData/Configuration/PSGallery_NoSettings.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 NoIssuesDetected_WinGetDscResource() { 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.")); } /// /// No issues detected from WinGet resource units. /// [Test] public void ValidateWinGetDscResource_DependencySourceMissing() { 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")); } /// /// No issues detected from WinGet resource units. /// [Test] public void ValidateWinGetDscResource_PackageNotFound() { 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")); } /// /// No issues detected from WinGet resource units. /// [Test] public void ValidateWinGetDscResource_PackageVersionNotFound() { 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")); } /// /// No issues detected from WinGet resource units. /// [Test] public void ValidateWinGetDscResource_SourceOpenFailed() { 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")); } /// /// No issues detected from WinGet resource units. /// [Test] public void ValidateWinGetDscResource_VersionSpecifiedWithOnlyOneVersionAvailable() { 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")); } /// /// No issues detected from WinGet resource units. /// [Test] 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 declares both UseLatest and Version. Package Id: AppInstallerTest.TestExeInstaller")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Constants.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { /// /// Constants. /// public class Constants { #pragma warning disable SA1600 // ElementsMustBeDocumented #pragma warning disable SA1310 // Field names should not contain underscore // Runtime test parameters public const string PackagedContextParameter = "PackagedContext"; public const string AICLIPathParameter = "AICLIPath"; public const string AICLIPackagePathParameter = "AICLIPackagePath"; public const string VerboseLoggingParameter = "VerboseLogging"; public const string LooseFileRegistrationParameter = "LooseFileRegistration"; public const string InvokeCommandInDesktopPackageParameter = "InvokeCommandInDesktopPackage"; public const string StaticFileRootPathParameter = "StaticFileRootPath"; public const string LocalServerCertPathParameter = "LocalServerCertPath"; public const string ExeInstallerPathParameter = "ExeTestInstallerPath"; public const string MsiInstallerPathParameter = "MsiTestInstallerPath"; public const string MsiInstallerV2PathParameter = "MsiTestInstallerV2Path"; public const string MsixInstallerPathParameter = "MsixTestInstallerPath"; public const string FontPathParameter = "FontTestPath"; public const string PackageCertificatePathParameter = "PackageCertificatePath"; public const string PowerShellModulePathParameter = "PowerShellModulePath"; public const string SkipTestSourceParameter = "SkipTestSource"; public const string ForcedExperimentalFeaturesParameter = "ForcedExperimentalFeatures"; public const string InprocTestbedPathParameter = "InprocTestbedPath"; public const string InprocTestbedUseTestPackageParameter = "InprocTestbedUseTestPackage"; // Test Sources public const string DefaultWingetSourceName = @"winget"; public const string DefaultWingetSourceUrl = @"https://winget.azureedge.net/cache"; public const string DefaultMSStoreSourceName = @"msstore"; public const string DefaultMSStoreSourceUrl = @"https://storeedgefd.dsx.mp.microsoft.com/v9.0"; public const string DefaultMSStoreSourceType = "Microsoft.Rest"; public const string DefaultMSStoreSourceIdentifier = "StoreEdgeFD"; public const string TestSourceName = @"TestSource"; public const string TestAlternateSourceName = @"TestSource2"; public const string TestSourceUrl = @"https://localhost:5001/TestKit"; public const string TestSourceType = "Microsoft.PreIndexed.Package"; public const string TestSourceIdentifier = @"WingetE2E.Tests_8wekyb3d8bbwe"; public const string AICLIPackageFamilyName = "WinGetDevCLI_8wekyb3d8bbwe"; public const string AICLIPackageName = "WinGetDevCLI"; public const string AICLIPackagePublisherHash = "8wekyb3d8bbwe"; public const string AICLIAppId = "WinGetDev"; public const string TestPackage = "TëstPackage.msix"; public const string ExeInstaller = "AppInstallerTestExeInstaller"; public const string MsiInstaller = "AppInstallerTestMsiInstaller"; public const string MsixInstaller = "AppInstallerTestMsixInstaller"; public const string ZipInstaller = "AppInstallerTestZipInstaller"; public const string ExeInstallerFileName = "AppInstallerTestExeInstaller.exe"; public const string MsiInstallerFileName = "AppInstallerTestMsiInstaller.msi"; public const string MsiInstallerV2FileName = "AppInstallerTestMsiInstallerV2.msi"; public const string MsixInstallerFileName = "AppInstallerTestMsixInstaller.msix"; public const string ZipInstallerFileName = "AppInstallerTestZipInstaller.zip"; public const string FontFileName = "AppInstallerTestFont.ttf"; public const string ModifyRepairInstaller = "AppInstallerTest.TestModifyRepair"; public const string IndexPackage = "source.msix"; public const string MakeAppx = "makeappx.exe"; public const string SignTool = "signtool.exe"; public const string IndexCreationTool = "IndexCreationTool"; public const string WinGetUtil = "WinGetUtil"; public const string E2ETestLogsPathPackaged = @"Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\DiagOutputDir"; public const string E2ETestLogsPathUnpackaged = @"WinGet\defaultState"; public const string CheckpointDirectoryPackaged = @"Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\Checkpoints"; public const string CheckpointDirectoryUnpackaged = @"Microsoft\WinGet\State\defaultState\Checkpoints"; // Installer filename public const string TestCommandExe = "testCommand.exe"; public const string AppInstallerTestExeInstallerExe = "AppInstallerTestExeInstaller.exe"; public const string AppInstallerTestMsiInstallerMsi = "AppInstallerTestMsiInstaller.msi"; public const string AppInstallerTestZipInstallerZip = "AppInstallerTestZipInstaller.zip"; // Test installers' package IDs public const string ExeInstallerPackageId = "AppInstallerTest.TestExeInstaller"; public const string MsiInstallerPackageId = "AppInstallerTest.TestMsiInstaller"; public const string MsixInstallerPackageId = "AppInstallerTest.TestMsixInstaller"; public const string PortableExePackageId = "AppInstallerTest.TestPortableExe"; public const string PortableExeWithCommandPackageId = "AppInstallerTest.TestPortableExeWithCommand"; public const string ExeInstalledDefaultProductCode = "{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}"; public const string MsiInstallerProductCode = "{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}"; public const string MsixInstallerName = "6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e"; public const string MsixInstallerPackageFamilyName = "6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e_8wekyb3d8bbwe"; public const string TestExeInstalledFileName = "TestExeInstalled.txt"; public const string TestExeUninstallerFileName = "UninstallTestExe.bat"; public const string TestExeUninstalledFileName = "TestExeUninstalled.txt"; public const string TestExeRepairCompletedFileName = "TestExeRepairCompleted.txt"; // PowerShell Cmdlets public const string FindCmdlet = "Find-WinGetPackage"; public const string GetCmdlet = "Get-WinGetPackage"; public const string GetSourceCmdlet = "Get-WinGetSource"; public const string InstallCmdlet = "Install-WinGetPackage"; public const string UninstallCmdlet = "Uninstall-WinGetPackage"; public const string UpdateCmdlet = "Update-WinGetPackage"; public const string WindowsPackageManagerServer = "WindowsPackageManagerServer"; // Locations public const string LocalAppData = "LocalAppData"; public const string Dependencies = "Dependencies"; // Package dir public const string PortableExePackageDirName = $"{PortableExePackageId}_{TestSourceIdentifier}"; public const string PortableExeWithCommandPackageDirName = $"{PortableExeWithCommandPackageId}_{TestSourceIdentifier}"; // Registry keys public const string WinGetPackageIdentifier = "WinGetPackageIdentifier"; public const string WinGetSourceIdentifier = "WinGetSourceIdentifier"; public const string UninstallSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; public const string PathSubKey_User = @"Environment"; public const string PathSubKey_Machine = @"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; public const string FontsSubKey = @"Software\Microsoft\Windows NT\CurrentVersion\Fonts"; // User settings public const string ArchiveExtractionMethod = "archiveExtractionMethod"; public const string PortablePackageUserRoot = "portablePackageUserRoot"; public const string PortablePackageMachineRoot = "portablePackageMachineRoot"; public const string InstallBehaviorScope = "scope"; public const string InstallerTypes = "installerTypes"; public const string DefaultModuleRoot = "defaultModuleRoot"; // Configuration public const string PSGalleryName = "PSGallery"; public const string TestRepoName = "AppInstallerCLIE2ETestsRepo"; public const string GalleryTestModuleName = "XmlContentDsc"; public const string SimpleTestModuleName = "xE2ETestResource"; public const string LocalModuleDescriptor = "[Local]"; public const string TestRegistryPath = "Software\\Microsoft\\WinGet\\Tests"; // Group Policy Error Message public const string BlockByWinGetPolicyErrorMessage = "This operation is disabled by Group Policy : Enable Windows Package Manager"; /// /// Error codes. /// public class ErrorCode { public const int S_OK = 0; public const int S_FALSE = 1; public const int ERROR_FILE_NOT_FOUND = unchecked((int)0x80070002); public const int ERROR_PATH_NOT_FOUND = unchecked((int)0x80070003); public const int E_INVALIDARG = unchecked((int)0x80070057); public const int ERROR_NO_RANGES_PROCESSED = unchecked((int)0x80070138); public const int OPC_E_ZIP_MISSING_END_OF_CENTRAL_DIRECTORY = unchecked((int)0x8051100F); public const int ERROR_OLD_WIN_VERSION = unchecked((int)0x8007047E); public const int HTTP_E_STATUS_NOT_FOUND = unchecked((int)0x80190194); public const int E_ABORT = unchecked((int)0x80004004); // AICLI custom HRESULTs public const int ERROR_INTERNAL_ERROR = unchecked((int)0x8A150001); public const int ERROR_INVALID_CL_ARGUMENTS = unchecked((int)0x8A150002); public const int ERROR_COMMAND_FAILED = unchecked((int)0x8A150003); public const int ERROR_MANIFEST_FAILED = unchecked((int)0x8A150004); public const int ERROR_CTRL_SIGNAL_RECEIVED = unchecked((int)0x8A150005); public const int ERROR_SHELLEXEC_INSTALL_FAILED = unchecked((int)0x8A150006); public const int ERROR_UNSUPPORTED_MANIFESTVERSION = unchecked((int)0x8A150007); public const int ERROR_DOWNLOAD_FAILED = unchecked((int)0x8A150008); public const int ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX = unchecked((int)0x8A150009); public const int ERROR_INDEX_INTEGRITY_COMPROMISED = unchecked((int)0x8A15000A); public const int ERROR_SOURCES_INVALID = unchecked((int)0x8A15000B); public const int ERROR_SOURCE_NAME_ALREADY_EXISTS = unchecked((int)0x8A15000C); public const int ERROR_INVALID_SOURCE_TYPE = unchecked((int)0x8A15000D); public const int ERROR_PACKAGE_IS_BUNDLE = unchecked((int)0x8A15000E); public const int ERROR_SOURCE_DATA_MISSING = unchecked((int)0x8A15000F); public const int ERROR_NO_APPLICABLE_INSTALLER = unchecked((int)0x8A150010); public const int ERROR_INSTALLER_HASH_MISMATCH = unchecked((int)0x8A150011); public const int ERROR_SOURCE_NAME_DOES_NOT_EXIST = unchecked((int)0x8A150012); public const int ERROR_SOURCE_ARG_ALREADY_EXISTS = unchecked((int)0x8A150013); public const int ERROR_NO_APPLICATIONS_FOUND = unchecked((int)0x8A150014); public const int ERROR_NO_SOURCES_DEFINED = unchecked((int)0x8A150015); public const int ERROR_MULTIPLE_APPLICATIONS_FOUND = unchecked((int)0x8A150016); public const int ERROR_NO_MANIFEST_FOUND = unchecked((int)0x8A150017); public const int ERROR_EXTENSION_PUBLIC_FAILED = unchecked((int)0x8A150018); public const int ERROR_COMMAND_REQUIRES_ADMIN = unchecked((int)0x8A150019); public const int ERROR_SOURCE_NOT_SECURE = unchecked((int)0x8A15001A); public const int ERROR_MSSTORE_BLOCKED_BY_POLICY = unchecked((int)0x8A15001B); public const int ERROR_MSSTORE_APP_BLOCKED_BY_POLICY = unchecked((int)0x8A15001C); public const int ERROR_EXPERIMENTAL_FEATURE_DISABLED = unchecked((int)0x8A15001D); public const int ERROR_MSSTORE_INSTALL_FAILED = unchecked((int)0x8A15001E); public const int ERROR_COMPLETE_INPUT_BAD = unchecked((int)0x8A15001F); public const int ERROR_YAML_INIT_FAILED = unchecked((int)0x8A150020); public const int ERROR_INVALID_MAPPING_KEY = unchecked((int)0x8A150021); public const int ERROR_DUPLICATE_MAPPING_KEY = unchecked((int)0x8A150022); public const int ERROR_YAML_INVALID_OPERATION = unchecked((int)0x8A150023); public const int ERROR_YAML_DOC_BUILD_FAILED = unchecked((int)0x8A150024); public const int ERROR_YAML_INVALID_EMITTER_STATE = unchecked((int)0x8A150025); public const int ERROR_YAML_INVALID_DATA = unchecked((int)0x8A150026); public const int ERROR_LIBYAML_ERROR = unchecked((int)0x8A150027); public const int ERROR_MANIFEST_VALIDATION_WARNING = unchecked((int)0x8A150028); public const int ERROR_MANIFEST_VALIDATION_FAILURE = unchecked((int)0x8A150029); public const int ERROR_INVALID_MANIFEST = unchecked((int)0x8A15002A); public const int ERROR_UPDATE_NOT_APPLICABLE = unchecked((int)0x8A15002B); public const int ERROR_UPDATE_ALL_HAS_FAILURE = unchecked((int)0x8A15002C); public const int ERROR_INSTALLER_SECURITY_CHECK_FAILED = unchecked((int)0x8A15002D); public const int ERROR_DOWNLOAD_SIZE_MISMATCH = unchecked((int)0x8A15002E); public const int ERROR_NO_UNINSTALL_INFO_FOUND = unchecked((int)0x8A15002F); public const int ERROR_EXEC_UNINSTALL_COMMAND_FAILED = unchecked((int)0x8A150030); public const int ERROR_ICU_BREAK_ITERATOR_ERROR = unchecked((int)0x8A150031); public const int ERROR_ICU_CASEMAP_ERROR = unchecked((int)0x8A150032); public const int ERROR_ICU_REGEX_ERROR = unchecked((int)0x8A150033); public const int ERROR_IMPORT_INSTALL_FAILED = unchecked((int)0x8A150034); public const int ERROR_NOT_ALL_PACKAGES_FOUND = unchecked((int)0x8A150035); public const int ERROR_JSON_INVALID_FILE = unchecked((int)0x8A150036); public const int ERROR_SOURCE_NOT_REMOTE = unchecked((int)0x8A150037); public const int ERROR_UNSUPPORTED_RESTSOURCE = unchecked((int)0x8A150038); public const int ERROR_RESTSOURCE_INVALID_DATA = unchecked((int)0x8A150039); public const int ERROR_BLOCKED_BY_POLICY = unchecked((int)0x8A15003A); public const int ERROR_RESTAPI_INTERNAL_ERROR = unchecked((int)0x8A15003B); public const int ERROR_RESTSOURCE_INVALID_URL = unchecked((int)0x8A15003C); public const int ERROR_RESTAPI_UNSUPPORTED_MIME_TYPE = unchecked((int)0x8A15003D); public const int ERROR_RESTSOURCE_INVALID_VERSION = unchecked((int)0x8A15003E); public const int ERROR_SOURCE_DATA_INTEGRITY_FAILURE = unchecked((int)0x8A15003F); public const int ERROR_STREAM_READ_FAILURE = unchecked((int)0x8A150040); public const int ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED = unchecked((int)0x8A150041); public const int ERROR_PROMPT_INPUT_ERROR = unchecked((int)0x8A150042); public const int ERROR_UNSUPPORTED_SOURCE_REQUEST = unchecked((int)0x8A150043); public const int ERROR_RESTAPI_ENDPOINT_NOT_FOUND = unchecked((int)0x8A150044); public const int ERROR_SOURCE_OPEN_FAILED = unchecked((int)0x8A150045); public const int ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED = unchecked((int)0x8A150046); public const int ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH = unchecked((int)0x8A150047); public const int ERROR_MISSING_RESOURCE_FILE = unchecked((int)0x8A150048); public const int ERROR_MSI_INSTALL_FAILED = unchecked((int)0x8A150049); public const int ERROR_INVALID_MSIEXEC_ARGUMENT = unchecked((int)0x8A15004A); public const int ERROR_FAILED_TO_OPEN_ALL_SOURCES = unchecked((int)0x8A15004B); public const int ERROR_DEPENDENCIES_VALIDATION_FAILED = unchecked((int)0x8A15004C); public const int ERROR_MISSING_PACKAGE = unchecked((int)0x8A15004D); public const int ERROR_INVALID_TABLE_COLUMN = unchecked((int)0x8A15004E); public const int ERROR_UPGRADE_VERSION_NOT_NEWER = unchecked((int)0x8A15004F); public const int ERROR_UPGRADE_VERSION_UNKNOWN = unchecked((int)0x8A150050); public const int ERROR_ICU_CONVERSION_ERROR = unchecked((int)0x8A150051); public const int ERROR_PORTABLE_INSTALL_FAILED = unchecked((int)0x8A150052); public const int ERROR_PORTABLE_REPARSE_POINT_NOT_SUPPORTED = unchecked((int)0x8A150053); public const int ERROR_PORTABLE_PACKAGE_ALREADY_EXISTS = unchecked((int)0x8A150054); public const int ERROR_PORTABLE_SYMLINK_PATH_IS_DIRECTORY = unchecked((int)0x8A150055); public const int ERROR_INSTALLER_PROHIBITS_ELEVATION = unchecked((int)0x8A150056); public const int ERROR_PORTABLE_UNINSTALL_FAILED = unchecked((int)0x8A150057); public const int ERROR_ARP_VERSION_VALIDATION_FAILED = unchecked((int)0x8A150058); public const int ERROR_UNSUPPORTED_ARGUMENT = unchecked((int)0x8A150059); public const int ERROR_BIND_WITH_EMBEDDED_NULL = unchecked((int)0x8A15005A); public const int ERROR_NESTEDINSTALLER_NOT_FOUND = unchecked((int)0x8A15005B); public const int ERROR_EXTRACT_ARCHIVE_FAILED = unchecked((int)0x8A15005C); public const int ERROR_NESTEDINSTALLER_INVALID_PATH = unchecked((int)0x8A15005D); public const int ERROR_PINNED_CERTIFICATE_MISMATCH = unchecked((int)0x8A15005E); public const int ERROR_INSTALL_LOCATION_REQUIRED = unchecked((int)0x8A15005F); public const int ERROR_ARCHIVE_SCAN_FAILED = unchecked((int)0x8A150060); public const int ERROR_PACKAGE_ALREADY_INSTALLED = unchecked((int)0x8A150061); public const int ERROR_PIN_ALREADY_EXISTS = unchecked((int)0x8A150062); public const int ERROR_PIN_DOES_NOT_EXIST = unchecked((int)0x8A150063); public const int ERROR_CANNOT_OPEN_PINNING_INDEX = unchecked((int)0x8A150064); public const int ERROR_MULTIPLE_INSTALL_FAILED = unchecked((int)0x8A150065); public const int ERROR_MULTIPLE_UNINSTALL_FAILED = unchecked((int)0x8A150066); public const int ERROR_NOT_ALL_QUERIES_FOUND_SINGLE = unchecked((int)0x8A150067); public const int ERROR_PACKAGE_IS_PINNED = unchecked((int)0x8A150068); public const int ERROR_PACKAGE_IS_STUB = unchecked((int)0x8A150069); public const int ERROR_APPTERMINATION_RECEIVED = unchecked((int)0x8A15006A); public const int ERROR_DOWNLOAD_DEPENDENCIES = unchecked((int)0x8A15006B); public const int ERROR_DOWNLOAD_COMMAND_PROHIBITED = unchecked((int)0x8A15006C); public const int ERROR_SERVICE_UNAVAILABLE = unchecked((int)0x8A15006D); public const int ERROR_RESUME_ID_NOT_FOUND = unchecked((int)0x8A15006E); public const int ERROR_CLIENT_VERSION_MISMATCH = unchecked((int)0x8A15006F); public const int ERROR_INVALID_RESUME_STATE = unchecked((int)0x8A150070); public const int ERROR_CANNOT_OPEN_CHECKPOINT_INDEX = unchecked((int)0x8A150071); public const int ERROR_NO_REPAIR_INFO_FOUND = unchecked((int)0x8A150079); public const int ERROR_REPAIR_NOT_SUPPORTED = unchecked((int)0x8A15007C); public const int ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED = unchecked((int)0x8A15007D); public const int ERROR_INSTALLER_ZERO_BYTE_FILE = unchecked((int)0x8A150086); public const int ERROR_FONT_INSTALL_FAILED = unchecked((int)0x8A150087); public const int ERROR_FONT_FILE_NOT_SUPPORTED = unchecked((int)0x8A150088); public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101); public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102); public const int ERROR_INSTALL_FILE_IN_USE = unchecked((int)0x8A150103); public const int ERROR_INSTALL_MISSING_DEPENDENCY = unchecked((int)0x8A150104); public const int ERROR_INSTALL_DISK_FULL = unchecked((int)0x8A150105); public const int ERROR_INSTALL_INSUFFICIENT_MEMORY = unchecked((int)0x8A150106); public const int ERROR_INSTALL_NO_NETWORK = unchecked((int)0x8A150107); public const int ERROR_INSTALL_CONTACT_SUPPORT = unchecked((int)0x8A150108); public const int ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH = unchecked((int)0x8A150109); public const int ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL = unchecked((int)0x8A15010A); public const int ERROR_INSTALL_REBOOT_INITIATED = unchecked((int)0x8A15010B); public const int ERROR_INSTALL_CANCELLED_BY_USER = unchecked((int)0x8A15010C); public const int ERROR_INSTALL_ALREADY_INSTALLED = unchecked((int)0x8A15010D); public const int ERROR_INSTALL_DOWNGRADE = unchecked((int)0x8A15010E); public const int ERROR_INSTALL_BLOCKED_BY_POLICY = unchecked((int)0x8A15010F); public const int ERROR_INSTALL_DEPENDENCIES = unchecked((int)0x8A150110); public const int ERROR_INSTALL_PACKAGE_IN_USE_BY_APPLICATION = unchecked((int)0x8A150111); public const int ERROR_INSTALL_INVALID_PARAMETER = unchecked((int)0x8A150112); public const int ERROR_INSTALL_SYSTEM_NOT_SUPPORTED = unchecked((int)0x8A150113); public const int APPINSTALLER_CLI_ERROR_INSTALL_UPGRADE_NOT_SUPPORTED = unchecked((int)0x8A150114); public const int INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND = unchecked((int)0x8A150201); public const int INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE = unchecked((int)0x0A150202); public const int INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND = unchecked((int)0x8A150203); public const int INSTALLED_STATUS_FILE_HASH_MISMATCH = unchecked((int)0x8A150204); public const int INSTALLED_STATUS_FILE_NOT_FOUND = unchecked((int)0x8A150205); public const int INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK = unchecked((int)0x0A150206); public const int INSTALLED_STATUS_FILE_ACCESS_ERROR = unchecked((int)0x8A150207); public const int CONFIG_ERROR_INVALID_CONFIGURATION_FILE = unchecked((int)0x8A15C001); public const int CONFIG_ERROR_INVALID_YAML = unchecked((int)0x8A15C002); public const int CONFIG_ERROR_INVALID_FIELD_TYPE = unchecked((int)0x8A15C003); public const int CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION = unchecked((int)0x8A15C004); public const int CONFIG_ERROR_SET_APPLY_FAILED = unchecked((int)0x8A15C005); public const int CONFIG_ERROR_DUPLICATE_IDENTIFIER = unchecked((int)0x8A15C006); public const int CONFIG_ERROR_MISSING_DEPENDENCY = unchecked((int)0x8A15C007); public const int CONFIG_ERROR_DEPENDENCY_UNSATISFIED = unchecked((int)0x8A15C008); public const int CONFIG_ERROR_ASSERTION_FAILED = unchecked((int)0x8A15C009); public const int CONFIG_ERROR_MANUALLY_SKIPPED = unchecked((int)0x8A15C00A); public const int CONFIG_ERROR_WARNING_NOT_ACCEPTED = unchecked((int)0x8A15C00B); public const int CONFIG_ERROR_SET_DEPENDENCY_CYCLE = unchecked((int)0x8A15C00C); public const int CONFIG_ERROR_INVALID_FIELD_VALUE = unchecked((int)0x8A15C00D); public const int CONFIG_ERROR_MISSING_FIELD = unchecked((int)0x8A15C00E); public const int CONFIG_ERROR_TEST_FAILED = unchecked((int)0x8A15C00F); public const int CONFIG_ERROR_TEST_NOT_RUN = unchecked((int)0x8A15C010); public const int WINGET_CONFIG_ERROR_GET_FAILED = unchecked((int)0x8A15C011); public const int CONFIG_ERROR_UNIT_NOT_INSTALLED = unchecked((int)0x8A15C101); public const int CONFIG_ERROR_UNIT_NOT_FOUND_REPOSITORY = unchecked((int)0x8A15C102); public const int CONFIG_ERROR_UNIT_MULTIPLE_MATCHES = unchecked((int)0x8A15C103); public const int CONFIG_ERROR_UNIT_INVOKE_GET = unchecked((int)0x8A15C104); public const int CONFIG_ERROR_UNIT_INVOKE_TEST = unchecked((int)0x8A15C105); public const int CONFIG_ERROR_UNIT_INVOKE_SET = unchecked((int)0x8A15C106); public const int CONFIG_ERROR_UNIT_MODULE_CONFLICT = unchecked((int)0x8A15C107); public const int CONFIG_ERROR_UNIT_IMPORT_MODULE = unchecked((int)0x8A15C108); public const int CONFIG_ERROR_UNIT_INVOKE_INVALID_RESULT = unchecked((int)0x8A15C109); public const int CONFIG_ERROR_UNIT_SETTING_CONFIG_ROOT = unchecked((int)0x8A15C110); public const int CONFIG_ERROR_UNIT_IMPORT_MODULE_ADMIN = unchecked((int)0x8A15C111); } #pragma warning restore SA1310 // Field names should not contain underscore #pragma warning restore SA1600 // ElementsMustBeDocumented } } ================================================ FILE: src/AppInstallerCLIE2ETests/DSCv3AdminSettingsResourceCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// `Configure` command tests. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public class DSCv3AdminSettingsResourceCommand : DSCv3ResourceTestBase { private const string AdminSettingsResource = "admin-settings"; private const string SettingsPropertyName = "settings"; // Bool settings private const string BypassCertificatePinningForMicrosoftStore = "BypassCertificatePinningForMicrosoftStore"; private const string InstallerHashOverride = "InstallerHashOverride"; private const string LocalArchiveMalwareScanOverride = "LocalArchiveMalwareScanOverride"; private const string LocalManifestFiles = "LocalManifestFiles"; private const string ProxyCommandLineOptions = "ProxyCommandLineOptions"; // String settings private const string DefaultProxy = "DefaultProxy"; // Not a setting private const string NotAnAdminSettingName = "NotAnAdminSetting"; /// /// Setup done once before all the tests here. /// [OneTimeSetUp] public void OneTimeSetup() { EnsureTestResourcePresence(); } /// /// Teardown done once after all the tests here. /// [OneTimeTearDown] public void OneTimeTeardown() { ResetAllSettings(); GroupPolicyHelper.DeleteExistingPolicies(); } /// /// Set up. /// [SetUp] public void Setup() { ResetAllSettings(); GroupPolicyHelper.DeleteExistingPolicies(); } /// /// Calls `get` on the `admin-settings` resource with the value not present. /// /// The resource function to invoke. [TestCase(GetFunction)] [TestCase(ExportFunction)] public void AdminSettings_Get(string function) { var result = RunDSCv3Command(AdminSettingsResource, function, null); AssertSuccessfulResourceRun(ref result); AdminSettingsResourceData output = GetSingleOutputLineAs(result.StdOut); Assert.IsNotNull(output); Assert.IsNotNull(output.Settings); Assert.IsTrue(output.Settings.ContainsKey(LocalManifestFiles)); Assert.IsFalse(output.Settings.ContainsKey(DefaultProxy)); Assert.IsFalse(output.Settings.ContainsKey(NotAnAdminSettingName)); } /// /// Calls `test` on the `admin-settings` resource with a bool setting. /// /// The setting to test. [TestCase(BypassCertificatePinningForMicrosoftStore)] [TestCase(InstallerHashOverride)] [TestCase(LocalArchiveMalwareScanOverride)] [TestCase(LocalManifestFiles)] [TestCase(ProxyCommandLineOptions)] public void AdminSettings_Test_BoolSetting(string settingName) { AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() }; resourceData.Settings[settingName] = true; var result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfBoolSetting(ref result, settingName, false, true); resourceData.Settings[settingName] = false; result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfBoolSetting(ref result, settingName, false, false); Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings", $"--enable {settingName}").ExitCode); resourceData.Settings[settingName] = false; result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfBoolSetting(ref result, settingName, true, false); resourceData.Settings[settingName] = true; result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfBoolSetting(ref result, settingName, true, true); } /// /// Calls `test` on the `admin-settings` resource with a string setting. /// /// The setting to test. [TestCase(DefaultProxy)] public void AdminSettings_Test_StringSetting(string settingName) { const string testValue = "A string to test"; const string differentTestValue = "A different value"; AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() }; resourceData.Settings[settingName] = null; var result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfStringSetting(ref result, settingName, null, null); resourceData.Settings[settingName] = testValue; result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfStringSetting(ref result, settingName, null, testValue); Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings set", $"{settingName} \"{testValue}\"").ExitCode); resourceData.Settings[settingName] = null; result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfStringSetting(ref result, settingName, testValue, null); resourceData.Settings[settingName] = testValue; result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfStringSetting(ref result, settingName, testValue, testValue); resourceData.Settings[settingName] = differentTestValue; result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertTestOfStringSetting(ref result, settingName, testValue, differentTestValue); } /// /// Calls `test` on the `admin-settings` resource with a complex input. /// [Test] public void AdminSettings_Test_Complex() { Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings", $"--enable {LocalArchiveMalwareScanOverride}").ExitCode); Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings", $"--enable {LocalManifestFiles}").ExitCode); AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() }; resourceData.Settings[LocalArchiveMalwareScanOverride] = true; resourceData.Settings[BypassCertificatePinningForMicrosoftStore] = false; resourceData.Settings[DefaultProxy] = null; var result = RunDSCv3Command(AdminSettingsResource, TestFunction, resourceData); AssertSuccessfulResourceRun(ref result); (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.IsTrue(output.InDesiredState); Assert.IsNotNull(output.Settings); AssertDiffState(diff, []); } /// /// Calls `set` on the `admin-settings` resource with a bool setting. /// /// The setting to test. [TestCase(BypassCertificatePinningForMicrosoftStore)] [TestCase(InstallerHashOverride)] [TestCase(LocalArchiveMalwareScanOverride)] [TestCase(LocalManifestFiles)] [TestCase(ProxyCommandLineOptions)] public void AdminSettings_Set_BoolSetting(string settingName) { AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() }; resourceData.Settings[settingName] = true; var result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfBoolSetting(ref result, settingName, false, true); result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfBoolSetting(ref result, settingName, true, true); resourceData.Settings[settingName] = false; result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfBoolSetting(ref result, settingName, true, false); result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfBoolSetting(ref result, settingName, false, false); } /// /// Calls `set` on the `admin-settings` resource with a string setting. /// /// The setting to test. [TestCase(DefaultProxy)] public void AdminSettings_Set_StringSetting(string settingName) { const string testValue = "A string to test"; const string differentTestValue = "A different value"; AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() }; resourceData.Settings[settingName] = null; var result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfStringSetting(ref result, settingName, null, null); resourceData.Settings[settingName] = testValue; result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfStringSetting(ref result, settingName, null, testValue); resourceData.Settings[settingName] = testValue; result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfStringSetting(ref result, settingName, testValue, testValue); resourceData.Settings[settingName] = differentTestValue; result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfStringSetting(ref result, settingName, testValue, differentTestValue); resourceData.Settings[settingName] = null; result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSetOfStringSetting(ref result, settingName, differentTestValue, null); } /// /// Calls `set` on the `admin-settings` resource with a complex input. /// [Test] public void AdminSettings_Set_Complex() { const string testValue = "A string to test"; AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() }; resourceData.Settings[LocalArchiveMalwareScanOverride] = true; resourceData.Settings[BypassCertificatePinningForMicrosoftStore] = false; resourceData.Settings[DefaultProxy] = testValue; var result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); AssertSuccessfulResourceRun(ref result); (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.IsNotNull(output.Settings); Assert.AreEqual(JsonValueKind.True, output.Settings[LocalArchiveMalwareScanOverride].AsValue().GetValueKind()); Assert.AreEqual(JsonValueKind.False, output.Settings[BypassCertificatePinningForMicrosoftStore].AsValue().GetValueKind()); Assert.AreEqual(testValue, output.Settings[DefaultProxy].AsValue().GetValue()); AssertDiffState(diff, [ SettingsPropertyName ]); } /// /// Calls `set` on the `admin-settings` resource attempting to change a setting with group policy enabled. /// [Test] public void AdminSettings_Set_GroupPolicyBlocked() { GroupPolicyHelper.EnableHashOverride.Disable(); AdminSettingsResourceData resourceData = new AdminSettingsResourceData() { Settings = new JsonObject() }; resourceData.Settings[InstallerHashOverride] = true; var result = RunDSCv3Command(AdminSettingsResource, SetFunction, resourceData); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); } private static void ResetAllSettings() { Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("settings reset", "--all").ExitCode); } private static void AssertTestOfBoolSetting(ref TestCommon.RunCommandResult result, string settingName, bool expectedState, bool testState) { AssertSuccessfulResourceRun(ref result); bool inDesiredState = expectedState == testState; (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.AreEqual(inDesiredState, output.InDesiredState); Assert.IsNotNull(output.Settings); Assert.IsTrue(output.Settings.ContainsKey(settingName)); Assert.AreEqual(expectedState ? JsonValueKind.True : JsonValueKind.False, output.Settings[settingName].AsValue().GetValueKind()); AssertDiffState(diff, inDesiredState ? [] : [ SettingsPropertyName ]); } private static void AssertSetOfBoolSetting(ref TestCommon.RunCommandResult result, string settingName, bool previousState, bool desiredState) { AssertSuccessfulResourceRun(ref result); bool inDesiredState = previousState == desiredState; (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.IsNotNull(output.Settings); Assert.IsTrue(output.Settings.ContainsKey(settingName)); Assert.AreEqual(desiredState ? JsonValueKind.True : JsonValueKind.False, output.Settings[settingName].AsValue().GetValueKind()); AssertDiffState(diff, inDesiredState ? [] : [ SettingsPropertyName ]); } private static void AssertTestOfStringSetting(ref TestCommon.RunCommandResult result, string settingName, string expectedState, string testState) { AssertSuccessfulResourceRun(ref result); bool inDesiredState = expectedState == testState; (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.AreEqual(inDesiredState, output.InDesiredState); Assert.IsNotNull(output.Settings); if (expectedState != null) { Assert.IsTrue(output.Settings.ContainsKey(settingName)); Assert.AreEqual(expectedState, output.Settings[settingName].AsValue().GetValue()); } else { Assert.IsFalse(output.Settings.ContainsKey(settingName)); } AssertDiffState(diff, inDesiredState ? [] : [SettingsPropertyName]); } private static void AssertSetOfStringSetting(ref TestCommon.RunCommandResult result, string settingName, string previousState, string desiredState) { AssertSuccessfulResourceRun(ref result); bool inDesiredState = previousState == desiredState; (AdminSettingsResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.IsNotNull(output.Settings); if (desiredState != null) { Assert.IsTrue(output.Settings.ContainsKey(settingName)); Assert.AreEqual(desiredState, output.Settings[settingName].AsValue().GetValue()); } else { Assert.IsFalse(output.Settings.ContainsKey(settingName)); } AssertDiffState(diff, inDesiredState ? [] : [SettingsPropertyName]); } private class AdminSettingsResourceData { [JsonPropertyName(InDesiredStatePropertyName)] public bool? InDesiredState { get; set; } public JsonObject Settings { get; set; } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/DSCv3PackageResourceCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.Collections.Generic; using System.Text.Json.Serialization; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// `Configure` command tests. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public class DSCv3PackageResourceCommand : DSCv3ResourceTestBase { private const string DefaultPackageIdentifier = Constants.ExeInstallerPackageId; private const string DefaultPackageLowVersion = "1.0.0.0"; private const string DefaultPackageMidVersion = "1.1.0.0"; private const string DefaultPackageHighVersion = "2.0.0.0"; private const string PackageResource = "package"; private const string VersionPropertyName = "version"; private const string UseLatestPropertyName = "useLatest"; /// /// Setup done once before all the tests here. /// [OneTimeSetUp] public void OneTimeSetup() { TestCommon.SetupTestSource(); WinGetSettingsHelper.ConfigureLoggingLevel("verbose"); EnsureTestResourcePresence(); } /// /// Teardown done once after all the tests here. /// [OneTimeTearDown] public void OneTimeTeardown() { RemoveTestPackage(); WinGetSettingsHelper.ConfigureLoggingLevel(null); TestCommon.TearDownTestSource(); } /// /// Set up. /// [SetUp] public void Setup() { // Try clean up TestExeInstaller for failure cases where cleanup is not successful RemoveTestPackage(); } /// /// Calls `get` on the `package` resource with the value not present. /// [Test] public void Package_Get_NotPresent() { PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier }; var result = RunDSCv3Command(PackageResource, GetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); PackageResourceData output = GetSingleOutputLineAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(packageResourceData.Identifier, output.Identifier); } /// /// Calls `get` on the `package` resource with the package not existing. /// [Test] public void Package_Get_UnknownIdentifier() { PackageResourceData packageResourceData = new PackageResourceData() { Identifier = "Not.An.Existing.Identifier.123456789.ABCDEFG" }; var result = RunDSCv3Command(PackageResource, GetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); PackageResourceData output = GetSingleOutputLineAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(packageResourceData.Identifier, output.Identifier); } /// /// Calls `get` on the `package` resource with the value present. /// [Test] public void Package_Get_Present() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageLowVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier }; var result = RunDSCv3Command(PackageResource, GetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); PackageResourceData output = GetSingleOutputLineAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageLowVersion); } /// /// Calls `get` on the `package` resource with the value present and supplying most inputs. /// [Test] public void Package_Get_MuchInput() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageLowVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, Source = Constants.TestSourceName, MatchOption = "equals", }; var result = RunDSCv3Command(PackageResource, GetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); PackageResourceData output = GetSingleOutputLineAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageLowVersion); } /// /// Calls `test` on the `package` resource with the value not present. /// [Test] public void Package_Test_NotPresent() { PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier }; var result = RunDSCv3Command(PackageResource, TestFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(packageResourceData.Identifier, output.Identifier); Assert.False(output.InDesiredState); AssertDiffState(diff, [ ExistPropertyName ]); } /// /// Calls `test` on the `package` resource with the value present. /// [Test] public void Package_Test_SimplePresent() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageLowVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier }; var result = RunDSCv3Command(PackageResource, TestFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageLowVersion); Assert.True(output.InDesiredState); AssertDiffState(diff, []); } /// /// Calls `test` on the `package` resource with a version that matches. /// [Test] public void Package_Test_VersionMatch() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageLowVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, Version = DefaultPackageLowVersion, }; var result = RunDSCv3Command(PackageResource, TestFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageLowVersion); Assert.True(output.InDesiredState); AssertDiffState(diff, []); } /// /// Calls `test` on the `package` resource with a version that does not match. /// [Test] public void Package_Test_VersionMismatch() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageLowVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, Version = DefaultPackageMidVersion, }; var result = RunDSCv3Command(PackageResource, TestFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageLowVersion); Assert.False(output.InDesiredState); AssertDiffState(diff, [ VersionPropertyName ]); } /// /// Calls `test` on the `package` resource with a version that is the latest. /// [Test] public void Package_Test_Latest() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, UseLatest = true, }; var result = RunDSCv3Command(PackageResource, TestFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageHighVersion); Assert.True(output.InDesiredState); AssertDiffState(diff, []); } /// /// Calls `test` on the `package` resource with a version that is not the latest. /// [Test] public void Package_Test_NotLatest() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageMidVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, UseLatest = true, }; var result = RunDSCv3Command(PackageResource, TestFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageMidVersion); Assert.False(output.InDesiredState); AssertDiffState(diff, [ UseLatestPropertyName ]); } /// /// Calls `set` on the `package` resource when the package is not present, and again afterward. /// [Test] public void Package_Set_SimpleRepeated() { PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, }; var result = RunDSCv3Command(PackageResource, SetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageHighVersion, ignoreLatest: true); AssertDiffState(diff, [ ExistPropertyName ]); // Set again should be a no-op result = RunDSCv3Command(PackageResource, SetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (output, diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageHighVersion); AssertDiffState(diff, []); } /// /// Calls `set` on the `package` resource to ensure that it is not present. /// [Test] public void Package_Set_Remove() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, Exist = false, }; var result = RunDSCv3Command(PackageResource, SetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(packageResourceData.Identifier, output.Identifier); AssertDiffState(diff, [ ExistPropertyName ]); // Call `get` to ensure the result PackageResourceData packageResourceDataForGet = new PackageResourceData() { Identifier = DefaultPackageIdentifier, }; result = RunDSCv3Command(PackageResource, GetFunction, packageResourceDataForGet); AssertSuccessfulResourceRun(ref result); output = GetSingleOutputLineAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(packageResourceDataForGet.Identifier, output.Identifier); } /// /// Calls `set` on the `package` resource to request the latest version when a lower version is installed. /// [Test] public void Package_Set_Latest() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageMidVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, UseLatest = true, }; var result = RunDSCv3Command(PackageResource, SetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageHighVersion, ignoreLatest: true); AssertDiffState(diff, [ UseLatestPropertyName ]); // Call `get` to ensure the result PackageResourceData packageResourceDataForGet = new PackageResourceData() { Identifier = DefaultPackageIdentifier, }; result = RunDSCv3Command(PackageResource, GetFunction, packageResourceDataForGet); AssertSuccessfulResourceRun(ref result); output = GetSingleOutputLineAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageHighVersion); } /// /// Calls `set` on the `package` resource to request a specific version when a lower version is installed. /// [Test] public void Package_Set_SpecificVersionUpgrade() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageLowVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, Version = DefaultPackageMidVersion, }; var result = RunDSCv3Command(PackageResource, SetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageMidVersion, ignoreLatest: true); AssertDiffState(diff, [ VersionPropertyName ]); // Call `get` to ensure the result PackageResourceData packageResourceDataForGet = new PackageResourceData() { Identifier = DefaultPackageIdentifier, }; result = RunDSCv3Command(PackageResource, GetFunction, packageResourceDataForGet); AssertSuccessfulResourceRun(ref result); output = GetSingleOutputLineAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageMidVersion); } /// /// Calls `set` on the `package` resource to request a specific version when a higher version is installed. /// [Test] public void Package_Set_SpecificVersionDowngrade() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, Version = DefaultPackageMidVersion, }; var result = RunDSCv3Command(PackageResource, SetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); (PackageResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageMidVersion, ignoreLatest: true); AssertDiffState(diff, [ VersionPropertyName ]); // Call `get` to ensure the result PackageResourceData packageResourceDataForGet = new PackageResourceData() { Identifier = DefaultPackageIdentifier, }; result = RunDSCv3Command(PackageResource, GetFunction, packageResourceDataForGet); AssertSuccessfulResourceRun(ref result); output = GetSingleOutputLineAs(result.StdOut); AssertExistingPackageResourceData(output, DefaultPackageMidVersion); } /// /// Calls `export` on the `package` resource without providing any input. /// [Test] public void Package_Export_NoInput() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier}"); Assert.AreEqual(0, setupInstall.ExitCode); var result = RunDSCv3Command(PackageResource, ExportFunction, " "); AssertSuccessfulResourceRun(ref result); List output = GetOutputLinesAs(result.StdOut); bool foundDefaultPackage = false; foreach (PackageResourceData item in output) { if (item.Identifier == DefaultPackageIdentifier) { foundDefaultPackage = true; Assert.IsNull(item.Version); break; } } Assert.IsTrue(foundDefaultPackage); } /// /// Calls `export` on the `package` resource providing input to request that versions be included. /// [Test] public void Package_Export_RequestVersions() { var setupInstall = TestCommon.RunAICLICommand("install", $"--id {DefaultPackageIdentifier} --version {DefaultPackageLowVersion}"); Assert.AreEqual(0, setupInstall.ExitCode); PackageResourceData packageResourceData = new PackageResourceData() { UseLatest = false, }; var result = RunDSCv3Command(PackageResource, ExportFunction, packageResourceData, 300000); AssertSuccessfulResourceRun(ref result); List output = GetOutputLinesAs(result.StdOut); bool foundDefaultPackage = false; foreach (PackageResourceData item in output) { if (item.Identifier == DefaultPackageIdentifier) { foundDefaultPackage = true; Assert.AreEqual(DefaultPackageLowVersion, item.Version); } else { Assert.IsNotNull(item.Version); Assert.IsNotEmpty(item.Version); } } Assert.IsTrue(foundDefaultPackage); } private static void RemoveTestPackage() { PackageResourceData packageResourceData = new PackageResourceData() { Identifier = DefaultPackageIdentifier, Exist = false, }; var result = RunDSCv3Command(PackageResource, SetFunction, packageResourceData); AssertSuccessfulResourceRun(ref result); } private static void AssertExistingPackageResourceData(PackageResourceData output, string version, bool ignoreLatest = false) { Assert.IsNotNull(output); Assert.True(output.Exist); Assert.AreEqual(DefaultPackageIdentifier, output.Identifier); Assert.AreEqual(version, output.Version); if (!ignoreLatest) { if (version == DefaultPackageHighVersion) { Assert.True(output.UseLatest); } else { Assert.False(output.UseLatest); } } } private class PackageResourceData { [JsonPropertyName(ExistPropertyName)] public bool? Exist { get; set; } [JsonPropertyName(InDesiredStatePropertyName)] public bool? InDesiredState { get; set; } [JsonPropertyName("id")] public string Identifier { get; set; } public string Source { get; set; } public string Version { get; set; } public string MatchOption { get; set; } public bool? UseLatest { get; set; } public string InstallMode { get; set; } public bool? AcceptAgreements { get; set; } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/DSCv3ResourceTestBase.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Provides common functionality for DSC v3 resource tests. /// public class DSCv3ResourceTestBase { /// /// The string for the `get` function. /// public const string GetFunction = "get"; /// /// The string for the `test` function. /// public const string TestFunction = "test"; /// /// The string for the `set` function. /// public const string SetFunction = "set"; /// /// The string for the `export` function. /// public const string ExportFunction = "export"; /// /// The string for the `_exist` property name. /// public const string ExistPropertyName = "_exist"; /// /// The string for the `_inDesiredState` property name. /// public const string InDesiredStatePropertyName = "_inDesiredState"; /// /// Write the resource manifests out to the WindowsApps alias directory. /// public static void EnsureTestResourcePresence() { string outputDirectory = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\WindowsApps"); Assert.IsNotEmpty(outputDirectory); var result = TestCommon.RunAICLICommand($"dscv3", $"--manifest -o {outputDirectory}"); Assert.AreEqual(0, result.ExitCode); } /// /// Runs a DSC v3 resource command. /// /// The resource to target. /// The resource function to run. /// Input for the function; supports null, direct string, or JSON serialization of complex objects. /// The maximum time to wait in milliseconds. /// Whether to throw on a timeout or simply return the incomplete result. /// A RunCommandResult containing the process exit code and output and error streams. protected static TestCommon.RunCommandResult RunDSCv3Command(string resource, string function, object input, int timeOut = 60000, bool throwOnTimeout = true) { return TestCommon.RunAICLICommand($"dscv3 {resource}", $"--{function}", ConvertToJSON(input), timeOut, throwOnTimeout); } /// /// Asserts that a RunCommandResult contains a success for a DSC v3 resource command run. /// /// The result of a DSC v3 resource command run. protected static void AssertSuccessfulResourceRun(ref TestCommon.RunCommandResult result) { Assert.AreEqual(0, result.ExitCode); Assert.IsNotEmpty(result.StdOut); } /// /// Gets the output as lines. /// /// The output stream from a DSC v3 resource command. /// The lines of the output. protected static string[] GetOutputLines(string output) { return output.TrimEnd().Split(Environment.NewLine); } /// /// Asserts that the output is a single line and deserializes that line as JSON. /// /// The type to deserialize from JSON. /// The output stream from a DSC v3 resource command. /// The object as deserialized. protected static T GetSingleOutputLineAs(string output) { string[] lines = GetOutputLines(output); Assert.AreEqual(1, lines.Length); return JsonSerializer.Deserialize(lines[0], GetDefaultJsonOptions()); } /// /// Asserts that the output is two lines and deserializes them as a JSON object and JSON string array. /// /// The type to deserialize from JSON. /// The output stream from a DSC v3 resource command. /// The object as deserialized and the contents of the string array. protected static (T, List) GetSingleOutputLineAndDiffAs(string output) { string[] lines = GetOutputLines(output); Assert.AreEqual(2, lines.Length); var options = GetDefaultJsonOptions(); return (JsonSerializer.Deserialize(lines[0], options), JsonSerializer.Deserialize>(lines[1], options)); } /// /// Deserializes all lines as JSON objects. /// /// The type to deserialize from JSON. /// The output stream from a DSC v3 resource command. /// A List of objects as deserialized. protected static List GetOutputLinesAs(string output) { List result = new List(); string[] lines = GetOutputLines(output); var options = GetDefaultJsonOptions(); foreach (string line in lines) { result.Add(JsonSerializer.Deserialize(line, options)); } return result; } /// /// Requires that the diff from a resource command contain the same set of strings as expected. /// /// The diff from a resource command. /// The expected strings. protected static void AssertDiffState(List diff, IList expected) { Assert.IsNotNull(diff); Assert.AreEqual(expected.Count, diff.Count); foreach (string item in expected) { Assert.Contains(item, diff); } } private static JsonSerializerOptions GetDefaultJsonOptions() { return new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, Converters = { new JsonStringEnumConverter(), }, }; } private static string ConvertToJSON(object value) => value switch { string s => s, null => null, _ => JsonSerializer.Serialize(value, GetDefaultJsonOptions()), }; } } ================================================ FILE: src/AppInstallerCLIE2ETests/DSCv3SourceResourceCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.Collections.Generic; using System.Text.Json.Serialization; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// `Configure` command tests. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public class DSCv3SourceResourceCommand : DSCv3ResourceTestBase { private const string DefaultSourceName = "SourceResourceTestSource"; private const string DefaultSourceType = "Microsoft.Test.Configurable"; private const string DefaultTrustLevel = "none"; private const string TrustedTrustLevel = "trusted"; private const bool DefaultExplicitState = false; private const int DefaultPriority = 0; private const string SourceResource = "source"; private const string ArgumentPropertyName = "argument"; private const string TypePropertyName = "type"; private const string TrustLevelPropertyName = "trustLevel"; private const string ExplicitPropertyName = "explicit"; private const string PriorityPropertyName = "priority"; private static string DefaultSourceArgForCmdLine { get { return CreateSourceArgument(true); } } private static string NonDefaultSourceArgForCmdLine { get { return CreateSourceArgument(true, 1, 1); } } private static string DefaultSourceArgDirect { get { return CreateSourceArgument(false); } } private static string NonDefaultSourceArgDirect { get { return CreateSourceArgument(false, 1, 1); } } /// /// Setup done once before all the tests here. /// [OneTimeSetUp] public void OneTimeSetup() { TestCommon.SetupTestSource(); EnsureTestResourcePresence(); } /// /// Teardown done once after all the tests here. /// [OneTimeTearDown] public void OneTimeTeardown() { RemoveTestSource(); TestCommon.TearDownTestSource(); } /// /// Set up. /// [SetUp] public void Setup() { RemoveTestSource(); WinGetSettingsHelper.ConfigureFeature("sourcePriority", true); } /// /// Calls `get` on the `source` resource with the value not present. /// [Test] public void Source_Get_NotPresent() { SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName }; var result = RunDSCv3Command(SourceResource, GetFunction, resourceData); AssertSuccessfulResourceRun(ref result); SourceResourceData output = GetSingleOutputLineAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(resourceData.Name, output.Name); } /// /// Calls `get` on the `source` resource with the value present. /// [Test] public void Source_Get_Present() { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {DefaultSourceArgForCmdLine} --type {DefaultSourceType} --explicit"); Assert.AreEqual(0, setup.ExitCode); SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName }; var result = RunDSCv3Command(SourceResource, GetFunction, resourceData); AssertSuccessfulResourceRun(ref result); SourceResourceData output = GetSingleOutputLineAs(result.StdOut); AssertExistingSourceResourceData(output, DefaultSourceArgDirect, DefaultTrustLevel, true); } /// /// Calls `test` on the `source` resource with the value not present. /// [Test] public void Source_Test_NotPresent() { SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName }; var result = RunDSCv3Command(SourceResource, TestFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(resourceData.Name, output.Name); Assert.False(output.InDesiredState); AssertDiffState(diff, [ ExistPropertyName ]); } /// /// Calls `test` on the `source` resource with the value present. /// [Test] public void Source_Test_SimplePresent() { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {DefaultSourceArgForCmdLine} --type {DefaultSourceType}"); Assert.AreEqual(0, setup.ExitCode); SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName }; var result = RunDSCv3Command(SourceResource, TestFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingSourceResourceData(output, DefaultSourceArgDirect, DefaultTrustLevel, DefaultExplicitState); Assert.True(output.InDesiredState); AssertDiffState(diff, []); } /// /// Calls `test` on the `source` resource with an argument that matches. /// /// The argument to use when adding the existing source. /// The trust level to use when adding the existing source. /// The explicit state to use when adding the existing source. /// The priority to use when adding the existing source. /// The property to target for the test. [TestCase(false, DefaultTrustLevel, true, 42, ArgumentPropertyName)] [TestCase(true, DefaultTrustLevel, false, 14, TypePropertyName)] [TestCase(false, TrustedTrustLevel, false, 42, TrustLevelPropertyName)] [TestCase(true, DefaultTrustLevel, true, 39, ExplicitPropertyName)] [TestCase(true, DefaultTrustLevel, true, 1, PriorityPropertyName)] public void Source_Test_PropertyMatch(bool useDefaultArgument, string trustLevel, bool isExplicit, int priority, string targetProperty) { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {(useDefaultArgument ? DefaultSourceArgForCmdLine : NonDefaultSourceArgForCmdLine)} --type {DefaultSourceType} --trust-level {trustLevel} {(isExplicit ? "--explicit" : string.Empty)} --priority {priority}"); Assert.AreEqual(0, setup.ExitCode); SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName }; switch (targetProperty) { case ArgumentPropertyName: resourceData.Argument = useDefaultArgument ? DefaultSourceArgDirect : NonDefaultSourceArgDirect; break; case TypePropertyName: resourceData.Type = DefaultSourceType; break; case TrustLevelPropertyName: resourceData.TrustLevel = trustLevel; break; case ExplicitPropertyName: resourceData.Explicit = isExplicit; break; case PriorityPropertyName: resourceData.Priority = priority; break; default: Assert.Fail($"{targetProperty} is not a handled case."); break; } var result = RunDSCv3Command(SourceResource, TestFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingSourceResourceData(output, useDefaultArgument ? DefaultSourceArgDirect : NonDefaultSourceArgDirect, trustLevel, isExplicit); Assert.True(output.InDesiredState); AssertDiffState(diff, []); } /// /// Calls `test` on the `source` resource with a argument that does not match. /// /// The argument to use when adding the existing source. /// The trust level to use when adding the existing source. /// The explicit state to use when adding the existing source. /// The priority to use when adding the existing source. /// The property to target for the test. /// The value to test against. [TestCase(false, DefaultTrustLevel, true, 2, ArgumentPropertyName, true)] [TestCase(false, DefaultTrustLevel, false, 13, TrustLevelPropertyName, TrustedTrustLevel)] [TestCase(true, DefaultTrustLevel, true, 42, ExplicitPropertyName, false)] [TestCase(true, DefaultTrustLevel, true, 8, PriorityPropertyName, 76)] public void Source_Test_PropertyMismatch(bool useDefaultArgument, string trustLevel, bool isExplicit, int priority, string targetProperty, object testValue) { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {(useDefaultArgument ? DefaultSourceArgForCmdLine : NonDefaultSourceArgForCmdLine)} --type {DefaultSourceType} --trust-level {trustLevel} {(isExplicit ? "--explicit" : string.Empty)} --priority {priority}"); Assert.AreEqual(0, setup.ExitCode); SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName }; switch (targetProperty) { case ArgumentPropertyName: resourceData.Argument = (bool)testValue ? DefaultSourceArgDirect : NonDefaultSourceArgDirect; break; case TrustLevelPropertyName: resourceData.TrustLevel = (string)testValue; break; case ExplicitPropertyName: resourceData.Explicit = (bool)testValue; break; case PriorityPropertyName: resourceData.Priority = (int)testValue; break; default: Assert.Fail($"{targetProperty} is not a handled case."); break; } var result = RunDSCv3Command(SourceResource, TestFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingSourceResourceData(output, useDefaultArgument ? DefaultSourceArgDirect : NonDefaultSourceArgDirect, trustLevel, isExplicit); Assert.False(output.InDesiredState); AssertDiffState(diff, [ targetProperty ]); } /// /// Calls `test` on the `source` resource with all properties matching. /// [Test] public void Source_Test_AllMatch() { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {NonDefaultSourceArgForCmdLine} --type {DefaultSourceType} --trust-level {TrustedTrustLevel} --explicit --priority 42"); Assert.AreEqual(0, setup.ExitCode); SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName, Argument = NonDefaultSourceArgDirect, Type = DefaultSourceType, TrustLevel = TrustedTrustLevel, Explicit = true, Priority = 42, }; var result = RunDSCv3Command(SourceResource, TestFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingSourceResourceData(output, resourceData); Assert.True(output.InDesiredState); AssertDiffState(diff, []); } /// /// Calls `set` on the `source` resource when the source is not present, and again afterward. /// [Test] public void Source_Set_SimpleRepeated() { SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName, Argument = NonDefaultSourceArgDirect, Type = DefaultSourceType, }; var result = RunDSCv3Command(SourceResource, SetFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingSourceResourceData(output, resourceData); AssertDiffState(diff, [ ExistPropertyName ]); // Set again should be a no-op result = RunDSCv3Command(SourceResource, SetFunction, resourceData); AssertSuccessfulResourceRun(ref result); (output, diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingSourceResourceData(output, resourceData); AssertDiffState(diff, []); } /// /// Calls `set` on the `source` resource to ensure that it is not present. /// [Test] public void Source_Set_Remove() { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {DefaultSourceArgForCmdLine} --type {DefaultSourceType} --explicit"); Assert.AreEqual(0, setup.ExitCode); SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName, Exist = false, }; var result = RunDSCv3Command(SourceResource, SetFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(resourceData.Name, output.Name); AssertDiffState(diff, [ ExistPropertyName ]); // Call `get` to ensure the result SourceResourceData resourceDataForGet = new SourceResourceData() { Name = DefaultSourceName, }; result = RunDSCv3Command(SourceResource, GetFunction, resourceDataForGet); AssertSuccessfulResourceRun(ref result); output = GetSingleOutputLineAs(result.StdOut); Assert.IsNotNull(output); Assert.False(output.Exist); Assert.AreEqual(resourceDataForGet.Name, output.Name); } /// /// Calls `set` on the `source` resource with an existing item, replacing it. /// [Test] public void Source_Set_Replace() { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {DefaultSourceArgForCmdLine} --type {DefaultSourceType}"); Assert.AreEqual(0, setup.ExitCode); SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName, Argument = DefaultSourceArgDirect, Type = DefaultSourceType, TrustLevel = TrustedTrustLevel, Explicit = true, }; var result = RunDSCv3Command(SourceResource, SetFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingSourceResourceData(output, resourceData); AssertDiffState(diff, [ TrustLevelPropertyName, ExplicitPropertyName ]); // Call `get` to ensure the result SourceResourceData resourceDataForGet = new SourceResourceData() { Name = DefaultSourceName, }; result = RunDSCv3Command(SourceResource, GetFunction, resourceDataForGet); AssertSuccessfulResourceRun(ref result); output = GetSingleOutputLineAs(result.StdOut); AssertExistingSourceResourceData(output, resourceData); } /// /// Calls `set` on the `source` resource with an existing item, editing it due to only changing editable properties. /// [Test] public void Source_Set_Replace_Edit() { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {DefaultSourceArgForCmdLine} --type {DefaultSourceType}"); Assert.AreEqual(0, setup.ExitCode); SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName, Explicit = true, Priority = 42, }; var result = RunDSCv3Command(SourceResource, SetFunction, resourceData); AssertSuccessfulResourceRun(ref result); (SourceResourceData output, List diff) = GetSingleOutputLineAndDiffAs(result.StdOut); AssertExistingSourceResourceData(output, resourceData); AssertDiffState(diff, [ExplicitPropertyName, PriorityPropertyName]); // Call `get` to ensure the result SourceResourceData resourceDataForGet = new SourceResourceData() { Name = DefaultSourceName, }; result = RunDSCv3Command(SourceResource, GetFunction, resourceDataForGet); AssertSuccessfulResourceRun(ref result); output = GetSingleOutputLineAs(result.StdOut); AssertExistingSourceResourceData(output, resourceData); } /// /// Calls `export` on the `source` resource without providing any input. /// [Test] public void Source_Export_NoInput() { var setup = TestCommon.RunAICLICommand("source add", $"--name {DefaultSourceName} --arg {DefaultSourceArgForCmdLine} --type {DefaultSourceType}"); Assert.AreEqual(0, setup.ExitCode); var result = RunDSCv3Command(SourceResource, ExportFunction, " "); AssertSuccessfulResourceRun(ref result); List output = GetOutputLinesAs(result.StdOut); bool foundDefaultSource = false; foreach (SourceResourceData item in output) { if (item.Name == DefaultSourceName) { foundDefaultSource = true; Assert.AreEqual(DefaultSourceName, item.Name); Assert.AreEqual(DefaultSourceArgDirect, item.Argument); Assert.AreEqual(DefaultSourceType, item.Type); Assert.AreEqual(DefaultTrustLevel, item.TrustLevel); Assert.AreEqual(DefaultExplicitState, item.Explicit); Assert.AreEqual(DefaultPriority, item.Priority); break; } } Assert.IsTrue(foundDefaultSource); } private static void RemoveTestSource() { SourceResourceData resourceData = new SourceResourceData() { Name = DefaultSourceName, Exist = false, }; var result = RunDSCv3Command(SourceResource, SetFunction, resourceData); AssertSuccessfulResourceRun(ref result); } private static void AssertExistingSourceResourceData(SourceResourceData output, SourceResourceData input) { AssertExistingSourceResourceData(output, input.Argument, input.TrustLevel, input.Explicit, input.Priority); } private static void AssertExistingSourceResourceData(SourceResourceData output, string argument, string trustLevel = null, bool? isExplicit = null, int? priority = null) { Assert.IsNotNull(output); Assert.True(output.Exist); Assert.AreEqual(DefaultSourceName, output.Name); if (argument != null) { Assert.AreEqual(argument, output.Argument); } Assert.AreEqual(DefaultSourceType, output.Type); if (trustLevel != null) { Assert.AreEqual(trustLevel, output.TrustLevel); } if (isExplicit != null) { Assert.AreEqual(isExplicit, output.Explicit); } if (priority != null) { Assert.AreEqual(priority, output.Priority); } } private static string CreateSourceArgument(bool forCommandLine = false, int openHR = 0, int searchHR = 0) { const string CommandLineFormat = @"""{{""""OpenHR"""": {0}, """"SearchHR"""": {1} }}"""; const string DirectFormat = @"{{""OpenHR"": {0}, ""SearchHR"": {1} }}"; return string.Format(forCommandLine ? CommandLineFormat : DirectFormat, openHR, searchHR); } private class SourceResourceData { [JsonPropertyName(ExistPropertyName)] public bool? Exist { get; set; } [JsonPropertyName(InDesiredStatePropertyName)] public bool? InDesiredState { get; set; } public string Name { get; set; } public string Argument { get; set; } public string Type { get; set; } public string TrustLevel { get; set; } public bool? Explicit { get; set; } public bool? AcceptAgreements { get; set; } public int? Priority { get; set; } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/DSCv3UserSettingsFileResourceCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; using System.Collections.Generic; using System.IO; using System.Text.Json.Nodes; using System.Text.Json.Serialization; /// /// `Configure` command tests. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public class DSCv3UserSettingsFileResourceCommand : DSCv3ResourceTestBase { private const string UserSettingsFileResource = "user-settings-file"; private const string SettingsPropertyName = "settings"; private const string ActionPropertyValueFull = "Full"; private const string ActionPropertyValuePartial = "Partial"; private const string SettingsMock = "mock"; private const string SettingsMockObject = "mockObject"; private const string SettingsMockNested = "mockNested"; /// /// Setup done once before all the tests here. /// [OneTimeSetUp] public void OneTimeSetup() { TestCommon.SetupTestSource(); EnsureTestResourcePresence(); } /// /// Teardown done once after all the tests here. /// [OneTimeTearDown] public void OneTimeTeardown() { WinGetSettingsHelper.InitializeWingetSettings(); } /// /// Set up. /// [SetUp] public void Setup() { // Reset the settings to default before each test. WinGetSettingsHelper.InitializeWingetSettings(); WinGetSettingsHelper.ConfigureFeature("dsc3", true); } /// /// Calls `get` on the `user-settings-file` resource. /// [Test] public void UserSettingsFile_Get() { var expected = GetCurrentUserSettings(); var getOutput = Get(new ()); Assert.IsNotNull(getOutput); Assert.IsNull(getOutput.Action); AssertSettingsAreEqual(expected, getOutput.Settings); } /// /// Calls `set` on the `user-settings-file` resource with no diff. /// /// The action value. [Test] [TestCase(ActionPropertyValueFull)] [TestCase(ActionPropertyValuePartial)] public void UserSettingsFile_Set_NoDiff(string action) { var setSettings = GetSettingsArg(action); (var setOutput, var setDiff) = Set(new () { Action = action, Settings = setSettings }); var expected = GetCurrentUserSettings(); Assert.IsNotNull(setOutput); Assert.AreEqual(action, setOutput.Action); AssertSettingsAreEqual(expected, setOutput.Settings); AssertDiffState(setDiff, []); } /// /// Calls `set` on the `user-settings-file` resource to add fields. /// /// The action value. [Test] [TestCase(ActionPropertyValueFull)] [TestCase(ActionPropertyValuePartial)] public void UserSettingsFile_Set_AddFields(string action) { // Call `set` to add mock properties to the settings var setSettings = GetSettingsArg(action); AddOrModifyMockProperties(setSettings, "mock"); (var setOutput, var setDiff) = Set(new () { Action = action, Settings = setSettings }); var expected = GetCurrentUserSettings(); // Assert that the settings are added Assert.IsNotNull(setOutput); Assert.AreEqual(action, setOutput.Action); AssertMockProperties(setOutput.Settings, "mock"); AssertSettingsAreEqual(expected, setOutput.Settings); AssertDiffState(setDiff, [ SettingsPropertyName ]); } /// /// Calls `set` on the `user-settings-file` resource to ensure action is partial by default. /// [Test] public void UserSettingsFile_Set_ActionIsPartialByDefault() { // Call `set` to add mock properties to the settings var setSettings = GetSettingsArg(ActionPropertyValuePartial); AddOrModifyMockProperties(setSettings, "mock"); var expected = GetCurrentUserSettings(); AddOrModifyMockProperties(expected, "mock"); (var setOutput, var setDiff) = Set(new () { Settings = setSettings }); // Assert that the settings are added Assert.IsNotNull(setOutput); Assert.AreEqual(setOutput.Action, ActionPropertyValuePartial); AssertMockProperties(setOutput.Settings, "mock"); AssertSettingsAreEqual(expected, setOutput.Settings); AssertDiffState(setDiff, [ SettingsPropertyName ]); } /// /// Calls `set` on the `user-settings-file` resource to update fields. /// /// The action value. [Test] [TestCase(ActionPropertyValueFull)] [TestCase(ActionPropertyValuePartial)] public void UserSettingsFile_Set_UpdateFields(string action) { // Add mock properties to the settings var set1Settings = new JsonObject(); AddOrModifyMockProperties(set1Settings, "mock_old"); Set(new () { Action = ActionPropertyValuePartial, Settings = set1Settings }); // Call `set` to update the settings var set2Settings = GetSettingsArg(action); AddOrModifyMockProperties(set2Settings, "mock_new"); (var setOutput, var setDiff) = Set(new () { Action = action, Settings = set2Settings }); var expected = GetCurrentUserSettings(); // Assert that the settings are updated Assert.IsNotNull(setOutput); Assert.AreEqual(action, setOutput.Action); AssertMockProperties(setOutput.Settings, "mock_new"); AssertSettingsAreEqual(expected, setOutput.Settings); AssertDiffState(setDiff, [ SettingsPropertyName ]); } /// /// Calls `test` on the `user-settings-file` resource to check if the settings are in desired state. /// /// The action value. [Test] [TestCase(ActionPropertyValueFull)] [TestCase(ActionPropertyValuePartial)] public void UserSettingsFile_Test_InDesiredState(string action) { // Add mock properties to the settings var setSettings = new JsonObject(); AddOrModifyMockProperties(setSettings, "mock"); Set(new () { Action = ActionPropertyValuePartial, Settings = setSettings }); // Call `test` to check the settings var testSettings = GetSettingsArg(action); AddOrModifyMockProperties(testSettings, "mock"); (var testOutput, var testDiff) = Test(new () { Action = action, Settings = testSettings }); var expected = GetCurrentUserSettings(); // Assert that the settings are in desired state Assert.IsNotNull(testOutput); Assert.AreEqual(action, testOutput.Action); AssertMockProperties(testOutput.Settings, "mock"); AssertSettingsAreEqual(expected, testOutput.Settings); Assert.IsTrue(testOutput.InDesiredState); AssertDiffState(testDiff, []); } /// /// Calls `test` on the `user-settings-file` resource to check if the settings are not in desired state. /// /// The action value. [Test] [TestCase(ActionPropertyValueFull)] [TestCase(ActionPropertyValuePartial)] public void UserSettingsFile_Test_NotInDesiredState(string action) { // Add mock properties to the settings var setSettings = new JsonObject(); AddOrModifyMockProperties(setSettings, "mock_set"); Set(new () { Action = ActionPropertyValuePartial, Settings = setSettings }); // Call `test` to check the settings var testSettings = GetSettingsArg(action); AddOrModifyMockProperties(testSettings, "mock_test"); (var testOutput, var testDiff) = Test(new () { Action = action, Settings = testSettings }); var expected = GetCurrentUserSettings(); // Assert that the settings are not in desired state Assert.IsNotNull(testOutput); Assert.AreEqual(action, testOutput.Action); AssertMockProperties(testOutput.Settings, "mock_set"); AssertSettingsAreEqual(expected, testOutput.Settings); Assert.IsFalse(testOutput.InDesiredState); AssertDiffState(testDiff, [ SettingsPropertyName ]); } /// /// Calls `export` on the `user-settings-file` resource to export the settings. /// [Test] public void UserSettingsFile_Export() { var expected = GetCurrentUserSettings(); var exportOutput = Export(new ()); Assert.IsNotNull(exportOutput); Assert.IsNull(exportOutput.Action); AssertSettingsAreEqual(expected, exportOutput.Settings); } /// /// Calls `get` on the `user-settings-file` resource. /// /// The input resource data. /// The output resource data. private static UserSettingsFileResourceData Get(UserSettingsFileResourceData resourceData) { var result = RunDSCv3Command(UserSettingsFileResource, GetFunction, resourceData); AssertSuccessfulResourceRun(ref result); return GetSingleOutputLineAs(result.StdOut); } /// /// Calls `set` on the `user-settings-file` resource. /// /// The input resource data. /// The output resource data and the diff. private static (UserSettingsFileResourceData, List) Set(UserSettingsFileResourceData resourceData) { var result = RunDSCv3Command(UserSettingsFileResource, SetFunction, resourceData); AssertSuccessfulResourceRun(ref result); return GetSingleOutputLineAndDiffAs(result.StdOut); } /// /// Calls `test` on the `user-settings-file` resource. /// /// The input resource data. /// The output resource data and the diff. private static (UserSettingsFileResourceData, List) Test(UserSettingsFileResourceData resourceData) { var result = RunDSCv3Command(UserSettingsFileResource, TestFunction, resourceData); AssertSuccessfulResourceRun(ref result); return GetSingleOutputLineAndDiffAs(result.StdOut); } /// /// Calls `export` on the `user-settings-file` resource. /// /// The input resource data. /// The output resource data. private static UserSettingsFileResourceData Export(UserSettingsFileResourceData resourceData) { var result = RunDSCv3Command(UserSettingsFileResource, ExportFunction, resourceData); AssertSuccessfulResourceRun(ref result); return GetSingleOutputLineAs(result.StdOut); } /// /// Gets the current user settings from the settings file. /// /// The current user settings as a JsonObject. private static JsonObject GetCurrentUserSettings() { var settingsContent = File.ReadAllText(WinGetSettingsHelper.GetUserSettingsPath()); return JsonNode.Parse(settingsContent).AsObject(); } /// /// Adds or modifies mock properties in the settings. /// /// Target settings. /// The mock value. private static void AddOrModifyMockProperties(JsonObject settings, string value) { settings[SettingsMock] = value; settings[SettingsMockObject] ??= new JsonObject(); settings[SettingsMockObject][SettingsMockNested] = value; } /// /// Asserts that the settings contain the expected mock properties. /// /// Target settings. /// The expected mock value. private static void AssertMockProperties(JsonObject settings, string value) { Assert.IsNotNull(settings); Assert.IsTrue(settings.ContainsKey(SettingsMock)); Assert.AreEqual(settings[SettingsMock].ToString(), value); Assert.IsTrue(settings.ContainsKey(SettingsMockObject)); Assert.IsTrue(settings[SettingsMockObject].AsObject().ContainsKey(SettingsMockNested)); Assert.AreEqual(settings[SettingsMockObject][SettingsMockNested].ToString(), value); } /// /// Asserts that the diff state is as expected. /// /// The expected settings. /// The actual settings. private static void AssertSettingsAreEqual(JsonObject expected, JsonObject actual) { Assert.IsTrue(JsonNode.DeepEquals(expected, actual)); } /// /// Gets the settings argument based on the action. /// /// The action value. /// The settings argument as a JsonObject. private static JsonObject GetSettingsArg(string action) => action == ActionPropertyValueFull ? GetCurrentUserSettings() : new (); private class UserSettingsFileResourceData { [JsonPropertyName(InDesiredStatePropertyName)] public bool? InDesiredState { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Action { get; set; } public JsonObject Settings { get; set; } } } ================================================ FILE: src/AppInstallerCLIE2ETests/DownloadCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using NUnit.Framework; using Windows.System; /// /// Test download command. /// public class DownloadCommand : BaseCommand { /// /// Downloads the test installer and its package dependencies. /// [Test] public void DownloadDependencies() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.PackageDependency --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); var dependenciesDir = Path.Combine(downloadDir, Constants.Dependencies); TestCommon.AssertInstallerDownload(dependenciesDir, "TestPortableExe", "3.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Portable, "en-US"); TestCommon.AssertInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } /// /// Downloads the test installer and skips dependencies. /// [Test] public void DownloadDependencies_Skip() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.PackageDependency --skip-dependencies --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Dependencies skipped.")); Assert.IsFalse(Directory.Exists(Path.Combine(downloadDir, Constants.Dependencies))); TestCommon.AssertInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } /// /// Downloads the test installer to the default downloads directory. /// [Test] public void DownloadToDefaultDirectory() { var packageVersion = "2.0.0.0"; var result = TestCommon.RunAICLICommand("download", $"{Constants.ExeInstallerPackageId} --version {packageVersion}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); string downloadDir = Path.Combine(TestCommon.GetDefaultDownloadDirectory(), $"{Constants.ExeInstallerPackageId}_{packageVersion}"); TestCommon.AssertInstallerDownload(downloadDir, "TestExeInstaller", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.User, PackageInstallerType.Exe); } /// /// Downloads the test installer to a specified directory. /// [Test] public void DownloadToDirectory() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"{Constants.ExeInstallerPackageId} --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); TestCommon.AssertInstallerDownload(downloadDir, "TestExeInstaller", "2.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.User, PackageInstallerType.Exe); } /// /// Downloads the test installer with Arm64. /// [Test] public void DownloadWithArm64() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --scope user --download-directory {downloadDir} --architecture Arm64"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); #pragma warning disable CA1416 // Validate platform compatibility. Arm64 is not reachable. TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.Arm64, TestCommon.Scope.User, PackageInstallerType.Nullsoft, "en-US"); #pragma warning restore CA1416 } /// /// Downloads the test installer using the user scope argument. /// [Test] public void DownloadWithUserScope() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --scope user --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.User, PackageInstallerType.Nullsoft, "en-US"); } /// /// Downloads the test installer using the machine scope argument. /// [Test] public void DownloadWithMachineScope() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --scope machine --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// /// Downloads the test installer using the 'zip' installer type argument. Verifies that base installer types such as 'zip' are still supported. /// [Test] public void DownloadWithZipInstallerTypeArg() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --installer-type zip --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true); } /// /// Downloads the test installer using the installer type argument. /// [Test] public void DownloadWithInstallerTypeArg() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --installer-type msi --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// /// Downloads the test installer using the architecture argument. /// [Test] public void DownloadWithArchitectureArg() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --architecture x86 --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// /// Downloads the test installer using the locale argument. /// [Test] public void DownloadWithLocaleArg() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestMultipleInstallers --locale zh-CN --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true); } /// /// Downloads the test installer with a hash mismatch. /// [Test] public void DownloadWithHashMismatch() { var downloadDir = TestCommon.GetRandomTestDir(); var errorResult = TestCommon.RunAICLICommand("download", $"AppInstallerTest.TestExeSha256Mismatch --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALLER_HASH_MISMATCH, errorResult.ExitCode); } /// /// Downloads the zero byte test installer with a hash mismatch. /// [Test] public void DownloadZeroByteFileWithHashMismatch() { var downloadDir = TestCommon.GetRandomTestDir(); var errorResult = TestCommon.RunAICLICommand("download", $"ZeroByteFile.IncorrectHash --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALLER_ZERO_BYTE_FILE, errorResult.ExitCode); } /// /// Downloads the zero byte test installer. /// [Test] public void DownloadZeroByteFile() { var downloadDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("download", $"ZeroByteFile.CorrectHash --download-directory {downloadDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); TestCommon.AssertInstallerDownload(downloadDir, "ZeroByteFile-CorrectHash", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ErrorCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test error command. /// public class ErrorCommand { /// /// Reset settings file to avoid affecting output from error command. /// [OneTimeSetUp] public void OneTimeSetup() { WinGetSettingsHelper.InitializeWingetSettings(); } /// /// Tests 0. /// [Test] public void Success() { var result = TestCommon.RunAICLICommand("error", "0"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("0x00000000")); } /// /// Tests 0x8a15c001. /// [Test] public void HexError() { var result = TestCommon.RunAICLICommand("error", "0x8a15c001"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("0x8a15c001")); Assert.True(result.StdOut.Contains("WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE")); } /// /// Tests a number larger than an HRESULT. /// [Test] public void HexErrorTooBig() { var result = TestCommon.RunAICLICommand("error", "0x8a15c0014"); Assert.AreEqual(Constants.ErrorCode.E_INVALIDARG, result.ExitCode); Assert.True(result.StdOut.Contains("The given number is too large to be an HRESULT.")); } /// /// Tests 2316681217. /// [Test] public void DecimalError() { var result = TestCommon.RunAICLICommand("error", "2316681217"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("0x8a15c001")); Assert.True(result.StdOut.Contains("WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE")); } /// /// Tests -1978335191. /// [Test] public void NegativeDecimalError() { var result = TestCommon.RunAICLICommand("error", "-- -1978335191"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("0x8a150029")); Assert.True(result.StdOut.Contains("APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_FAILURE")); } /// /// Tests 0x8a15c000. /// [Test] public void HexErrorNotFound() { var result = TestCommon.RunAICLICommand("error", "0x8a15c000"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("0x8a15c000")); Assert.True(result.StdOut.Contains("Unknown error code")); } /// /// Tests 0xA150202. /// [Test] public void NonError() { var result = TestCommon.RunAICLICommand("error", "0xA150202"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("0x0a150202")); Assert.True(result.StdOut.Contains("WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE")); } /// /// Tests WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE. /// [Test] public void Symbol() { var result = TestCommon.RunAICLICommand("error", "WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("0x8a15c001")); Assert.True(result.StdOut.Contains("WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE")); Assert.AreEqual(2, result.StdOut.Split('\n', System.StringSplitOptions.RemoveEmptyEntries).Length); } /// /// Tests config. /// [Test] public void String() { var result = TestCommon.RunAICLICommand("error", "config"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("0x8a15c001")); Assert.True(result.StdOut.Contains("WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE")); Assert.True(result.StdOut.Contains("0x8a15c110")); Assert.True(result.StdOut.Contains("WINGET_CONFIG_ERROR_UNIT_SETTING_CONFIG_ROOT")); // This contains config in it's message. Assert.True(result.StdOut.Contains("0x8a150038")); Assert.True(result.StdOut.Contains("APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/FeaturesCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Features command tests. /// public class FeaturesCommand : BaseCommand { /// /// Set up. /// [SetUp] public void Setup() { WinGetSettingsHelper.InitializeAllFeatures(false); } /// /// Tear down. /// [TearDown] public void TearDown() { WinGetSettingsHelper.InitializeAllFeatures(false); } /// /// Tests winget features. /// [Test] public void DisplayFeatures() { var result = TestCommon.RunAICLICommand("features", string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Direct MSI Installation")); } /// /// Tests enabled winget features. /// [Test] public void EnableExperimentalFeatures() { WinGetSettingsHelper.ConfigureFeature("experimentalArg", true); WinGetSettingsHelper.ConfigureFeature("experimentalCmd", true); WinGetSettingsHelper.ConfigureFeature("directMSI", true); WinGetSettingsHelper.ConfigureFeature("resume", true); WinGetSettingsHelper.ConfigureFeature("fonts", true); var result = TestCommon.RunAICLICommand("features", string.Empty); Assert.True(result.StdOut.Contains("Enabled")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/FontCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test font command. /// public class FontCommand : BaseCommand { /// /// One time set up. /// [OneTimeSetUp] public void OneTimeSetup() { WinGetSettingsHelper.ConfigureFeature("fonts", true); } /// /// Test install a font with user scope. /// [Test] public void InstallFont_UserScope() { var fontPackageName = "AppInstallerTest.TestFont"; var fontPackageVersion = "1.0.0.0"; var installResult = TestCommon.RunAICLICommand("install", fontPackageName); Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("Successfully installed")); TestCommon.VerifyFontPackage(fontPackageName, fontPackageVersion, TestCommon.Scope.User); var uninstallResult = TestCommon.RunAICLICommand("uninstall", fontPackageName); Assert.AreEqual(Constants.ErrorCode.S_OK, uninstallResult.ExitCode); TestCommon.VerifyFontPackage(fontPackageName, fontPackageVersion, TestCommon.Scope.User, false); } /// /// Test install a font with machine scope. /// [Test] public void InstallFont_MachineScope() { var fontPackageName = "AppInstallerTest.TestFont"; var fontPackageVersion = "1.0.0.0"; var result = TestCommon.RunAICLICommand("install", $"{fontPackageName} --scope Machine"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyFontPackage(fontPackageName, fontPackageVersion, TestCommon.Scope.Machine); var uninstallResult = TestCommon.RunAICLICommand("uninstall", fontPackageName); Assert.AreEqual(Constants.ErrorCode.S_OK, uninstallResult.ExitCode); TestCommon.VerifyFontPackage(fontPackageName, fontPackageVersion, TestCommon.Scope.Machine, false); } /// /// Test install an invalid font file. /// [Test] public void InstallInvalidFont() { var result = TestCommon.RunAICLICommand("install", "AppInstallerTest.TestInvalidFont"); Assert.AreEqual(Constants.ErrorCode.ERROR_FONT_FILE_NOT_SUPPORTED, result.ExitCode); Assert.True(result.StdOut.Contains("One or more fonts in the font package is not supported and cannot be installed.")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/GroupPolicy.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Tests for enforcement of Group Policy. /// Behavior is better tested in the unit tests; these tests mostly ensure match between the code and the definition. /// public class GroupPolicy : BaseCommand { /// /// Set up. /// [SetUp] public void Setup() { WinGetSettingsHelper.InitializeAllFeatures(false); GroupPolicyHelper.DeleteExistingPolicies(); } /// /// Tear down. /// [TearDown] public void TearDown() { WinGetSettingsHelper.InitializeAllFeatures(false); GroupPolicyHelper.DeleteExistingPolicies(); } /// /// Test winget search is disabled by policy. /// [Test] public void PolicyEnableWinget() { GroupPolicyHelper.EnableWinget.Disable(); var result = TestCommon.RunAICLICommand("search", "foo"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); // Scenario if Policy WinGet is disabled but Policy EnableWindowsPackageManagerCommandLineInterfaces is Enabled. GroupPolicyHelper.EnableWinGetCommandLineInterfaces.Enable(); result = TestCommon.RunAICLICommand("search", "foo"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); // Scenario if Policy WinGet is disabled but Policy EnableWindowsPackageManagerCommandLineInterfaces is Not-Configured. GroupPolicyHelper.EnableWinGetCommandLineInterfaces.SetNotConfigured(); result = TestCommon.RunAICLICommand("search", "foo"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); // Scenario if Policy WinGet is enabled but Policy EnableWindowsPackageManagerCommandLineInterfaces is disabled. GroupPolicyHelper.EnableWinget.Enable(); GroupPolicyHelper.EnableWinGetCommandLineInterfaces.Disable(); result = TestCommon.RunAICLICommand("search", "foo"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); // Scenario if Policy WinGet is Not-Configured but Policy EnableWindowsPackageManagerCommandLineInterfaces is disabled. GroupPolicyHelper.EnableWinget.SetNotConfigured(); GroupPolicyHelper.EnableWinGetCommandLineInterfaces.Disable(); result = TestCommon.RunAICLICommand("search", "foo"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); } /// /// Test winget settings is disable by policy. /// [Test] public void EnableSettings() { GroupPolicyHelper.EnableSettings.Disable(); var result = TestCommon.RunAICLICommand("settings", string.Empty); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); } /// /// Test experimental features policy. /// [Test] public void EnableExperimentalFeatures() { WinGetSettingsHelper.ConfigureFeature("experimentalCmd", true); var result = TestCommon.RunAICLICommand("experimental", string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); // An experimental feature disabled by Group Policy behaves the same as one that is not enabled. // The expected result is a command line error as the argument validation rejects this. GroupPolicyHelper.EnableExperimentalFeatures.Disable(); result = TestCommon.RunAICLICommand("experimental", string.Empty); Assert.AreEqual(Constants.ErrorCode.ERROR_INVALID_CL_ARGUMENTS, result.ExitCode); } /// /// Test install via manifest is disabled by policy. /// [Test] public void EnableLocalManifests() { GroupPolicyHelper.EnableLocalManifests.Disable(); var result = TestCommon.RunAICLICommand("install", $"-m {TestCommon.GetTestDataFile(@"Manifests\TestExeInstaller.yaml")}"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); } /// /// Test install without checking the hash is disabled by policy. /// [Test] public void EnableHashOverride() { GroupPolicyHelper.EnableHashOverride.Disable(); var result = TestCommon.RunAICLICommand("install", "AnyPackage --ignore-security-hash"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); } /// /// Test install ignoring the malware scan is disabled by policy. /// [Test] public void EnableIgnoreLocalArchiveMalwareScanOverride() { GroupPolicyHelper.EnableLocalArchiveMalwareScanOverride.Disable(); var result = TestCommon.RunAICLICommand("install", "AnyPackage --ignore-local-archive-malware-scan"); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); } /// /// Test winget source is enabled by policy. /// [Test] public void EnableDefaultSource() { // Default sources are disabled during setup so they are missing. var result = TestCommon.RunAICLICommand("source list", "winget"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); GroupPolicyHelper.EnableDefaultSource.Enable(); result = TestCommon.RunAICLICommand("source list", "winget"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); } /// /// Test store source is enabled by policy. /// [Test] public void EnableMicrosoftStoreSource() { // Default sources are disabled during setup so they are missing. var result = TestCommon.RunAICLICommand("source list", "msstore"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); GroupPolicyHelper.EnableMicrosoftStoreSource.Enable(); result = TestCommon.RunAICLICommand("source list", "msstore"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); } /// /// Test font source is enabled by policy. /// [Test] public void EnableFontSource() { GroupPolicyHelper.EnableFontSource.Disable(); var result = TestCommon.RunAICLICommand("source list", "winget-font"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); GroupPolicyHelper.EnableFontSource.Enable(); result = TestCommon.RunAICLICommand("source list", "winget-font"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); } /// /// Test additional sources are enabled by policy. /// [Test] public void EnableAdditionalSources() { // Remove the test source, then add it with policy. TestCommon.RunAICLICommand("source remove", "TestSource"); var result = TestCommon.RunAICLICommand("source list", "TestSource"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); GroupPolicyHelper.EnableAdditionalSources.SetEnabledList(new string[] { "{\"Arg\":\"https://localhost:5001/TestKit\",\"Data\":\"WingetE2E.Tests_8wekyb3d8bbwe\",\"Identifier\":\"WingetE2E.Tests_8wekyb3d8bbwe\",\"Name\":\"TestSource\",\"Type\":\"Microsoft.PreIndexed.Package\"}", }); result = TestCommon.RunAICLICommand("source list", "TestSource"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); } /// /// Test additional sources with trust levels and explicit are enabled by policy. /// [Test] public void EnableAdditionalSources_TrustLevel_Explicit() { // Remove the test source, then add it with policy. TestCommon.RunAICLICommand("source remove", "TestSource"); var result = TestCommon.RunAICLICommand("source list", "TestSource"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); GroupPolicyHelper.EnableAdditionalSources.SetEnabledList(new string[] { "{\"Arg\":\"https://localhost:5001/TestKit\",\"Data\":\"WingetE2E.Tests_8wekyb3d8bbwe\",\"Identifier\":\"WingetE2E.Tests_8wekyb3d8bbwe\",\"Name\":\"TestSource\",\"Type\":\"Microsoft.PreIndexed.Package\",\"TrustLevel\":[\"Trusted\"],\"Explicit\":true}", }); result = TestCommon.RunAICLICommand("source list", "TestSource"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Trust Level")); Assert.True(result.StdOut.Contains("Trusted")); var searchResult = TestCommon.RunAICLICommand("search", "TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_SOURCES_DEFINED, searchResult.ExitCode); Assert.True(searchResult.StdOut.Contains("No sources defined; add one with 'source add' or reset to defaults with 'source reset'")); } /// /// Test enable allowed sources. /// [Test] public void EnableAllowedSources() { // Try listing the test source. We should only see it if it is allowed. // With allowed sources disabled: GroupPolicyHelper.EnableAllowedSources.Disable(); var result = TestCommon.RunAICLICommand("source list", "TestSource"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); // With allowed sources enabled, but not listing the test source: GroupPolicyHelper.EnableAdditionalSources.SetEnabledList(new string[] { "{\"Arg\":\"An argument\",\"Data\":\"Some data\",\"Identifier\":\"Test id\",\"Name\":\"NotTestSource\",\"Type\":\"Microsoft.PreIndexed.Package\"}", }); result = TestCommon.RunAICLICommand("source list", "TestSource"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); // With the test source allowed: GroupPolicyHelper.EnableAdditionalSources.SetEnabledList(new string[] { "{\"Arg\":\"https://localhost:5001/TestKit\",\"Data\":\"WingetE2E.Tests_8wekyb3d8bbwe\",\"Identifier\":\"WingetE2E.Tests_8wekyb3d8bbwe\",\"Name\":\"TestSource\",\"Type\":\"Microsoft.PreIndexed.Package\"}", }); result = TestCommon.RunAICLICommand("source list", "TestSource"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); } /// /// Tests source auto update policy. /// [Test] public void SourceAutoUpdateInterval() { // Test this policy by inspecting the result of --info GroupPolicyHelper.SourceAutoUpdateInterval.SetEnabledValue(123); var result = TestCommon.RunAICLICommand(string.Empty, "--info"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("Source Auto Update Interval In Minutes 123")); } /// /// Test configuration is disabled by policy. /// [Test] public void EnableConfiguration() { GroupPolicyHelper.EnableConfiguration.Disable(); var result = TestCommon.RunAICLICommand("configure", TestCommon.GetTestDataFile("Configuration\\ShowDetails_TestRepo.yml")); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); result = TestCommon.RunAICLICommand("configure show", TestCommon.GetTestDataFile("Configuration\\ShowDetails_TestRepo.yml")); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, result.ExitCode); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/GroupPolicyHelper.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Win32; using Newtonsoft.Json; using NUnit.Framework; /// /// Helper for setting Group Policy settings. /// This helper reads the keys and values to use directly from the ADMX file to ensure that the names /// used by the source code are correct. /// /// /// This helper modifies the policies for winget configured in the machine. /// public class GroupPolicyHelper { private const string PoliciesDefinitionFileName = "DesktopAppInstaller.admx"; private static Lazy policyDefinitions = new Lazy(() => { string filePath = TestCommon.GetTestDataFile(PoliciesDefinitionFileName); string fileText = File.ReadAllText(filePath); return XElement.Parse(fileText); }); /// /// Name of the policy. Used to identify it in the file. /// private string name; /// /// ID of the value element of this policy (if it has one). /// This assumes that each policy has a single value element. /// private string elementId; private GroupPolicyHelper(string name) { this.name = name; } private GroupPolicyHelper(string name, string elementId) { this.name = name; this.elementId = elementId; } // Policies available. /// /// Gets the Enable winget policy. /// public static GroupPolicyHelper EnableWinget { get; private set; } = new GroupPolicyHelper("EnableAppInstaller"); /// /// Gets the Enable Windows Package Manager CommandLine Interfaces policy. /// public static GroupPolicyHelper EnableWinGetCommandLineInterfaces { get; private set; } = new GroupPolicyHelper("EnableWindowsPackageManagerCommandLineInterfaces"); /// /// Gets the Enable settings policy. /// public static GroupPolicyHelper EnableSettings { get; private set; } = new GroupPolicyHelper("EnableSettings"); /// /// Gets the Enable experimental features policy. /// public static GroupPolicyHelper EnableExperimentalFeatures { get; private set; } = new GroupPolicyHelper("EnableExperimentalFeatures"); /// /// Gets the Enable local manifest policy. /// public static GroupPolicyHelper EnableLocalManifests { get; private set; } = new GroupPolicyHelper("EnableLocalManifestFiles"); /// /// Gets the Enable hash override policy. /// public static GroupPolicyHelper EnableHashOverride { get; private set; } = new GroupPolicyHelper("EnableHashOverride"); /// /// Gets the Enable ignore malware scan policy. /// public static GroupPolicyHelper EnableLocalArchiveMalwareScanOverride { get; private set; } = new GroupPolicyHelper("EnableLocalArchiveMalwareScanOverride"); /// /// Gets the Enable default source policy. /// public static GroupPolicyHelper EnableDefaultSource { get; private set; } = new GroupPolicyHelper("EnableDefaultSource"); /// /// Gets the Enable store source policy. /// public static GroupPolicyHelper EnableMicrosoftStoreSource { get; private set; } = new GroupPolicyHelper("EnableMicrosoftStoreSource"); /// /// Gets the Enable font source policy. /// public static GroupPolicyHelper EnableFontSource { get; private set; } = new GroupPolicyHelper("EnableFontSource"); /// /// Gets the Enable additional sources policy. /// public static GroupPolicyHelper EnableAdditionalSources { get; private set; } = new GroupPolicyHelper("EnableAdditionalSources", "AdditionalSources"); /// /// Gets the Enable allowed sources policy. /// public static GroupPolicyHelper EnableAllowedSources { get; private set; } = new GroupPolicyHelper("EnableAllowedSources", "AllowedSources"); /// /// Gets the Enable Windows Package Manager Configuration Interfaces policy. /// public static GroupPolicyHelper EnableConfiguration { get; private set; } = new GroupPolicyHelper("EnableWindowsPackageManagerConfiguration"); /// /// Gets the Enable Windows Package Manager proxy command line options policy. /// public static GroupPolicyHelper EnableProxyCommandLineOptions { get; private set; } = new GroupPolicyHelper("EnableWindowsPackageManagerProxyCommandLineOptions"); /// /// Gets the Enable auto update interval policy. /// public static GroupPolicyHelper SourceAutoUpdateInterval { get; private set; } = new GroupPolicyHelper("SourceAutoUpdateInterval", "SourceAutoUpdateInterval"); private static GroupPolicyHelper[] AllPolicies { get; set; } = new GroupPolicyHelper[] { EnableWinget, EnableSettings, EnableExperimentalFeatures, EnableLocalManifests, EnableHashOverride, EnableLocalArchiveMalwareScanOverride, EnableDefaultSource, EnableMicrosoftStoreSource, EnableFontSource, EnableAdditionalSources, EnableAllowedSources, SourceAutoUpdateInterval, EnableWinGetCommandLineInterfaces, EnableConfiguration, EnableProxyCommandLineOptions, }; /// /// Gets the content of the ADMX file as an XML. /// private static XElement PolicyDefinitions => policyDefinitions.Value; /// /// Gets the XML element that defines this policy. /// // The XML structure is like this: // // ... // // // // private XElement PolicyElement => PolicyDefinitions .Element(XmlNames.Policies) .Elements(XmlNames.Policy) .First(policy => policy.Attribute(XmlNames.Attributes.Name).Value == this.name); /// /// Gets the path to the registry key that backs this policy. /// private string KeyPath => this.PolicyElement.Attribute(XmlNames.Attributes.Key).Value; /// /// Gets the name of the registry value that backs this policy. /// private string ValueName => this.PolicyElement.Attribute(XmlNames.Attributes.ValueName)?.Value; /// /// Gets the XElement that defines the single value element of this policy. /// This only works if the policy has a single element for its value. /// // Looks for something like this: // // // // // // We use only list and decimal elements. private XElement ValueElement => this.PolicyElement .Element(XmlNames.Elements) .Elements() .First(element => element.Attribute(XmlNames.Attributes.Id).Value == this.elementId); /// /// Deletes all of the existing policies from the registry. /// public static void DeleteExistingPolicies() { foreach (var policy in AllPolicies) { policy.SetNotConfigured(); } } /// /// Sets the policy to the Enabled state. /// This will fail if the policy's EnabledValue does not exist or is not exactly as expected. /// public void Enable() { // The expected format is like this: // // // // We expect the value to always be 1, but still parse it to catch errors in the ADMX. int enabledValue = GetDecimalValue(this.PolicyElement.Element(XmlNames.EnabledValue)); using (RegistryKey key = this.GetKey()) { key.SetValue(this.ValueName, enabledValue); } } /// /// Sets the policy to the Disabled state. /// This will fail if the policy's DisabledValue does not exist or is not exactly as expected. /// public void Disable() { // The expected format is like this: // // // // We expect the value to always be 0, but still parse it to catch errors in the ADMX. int disabledValue = GetDecimalValue(this.PolicyElement.Element(XmlNames.DisabledValue)); using (RegistryKey key = this.GetKey()) { key.SetValue(this.ValueName, disabledValue); } } /// /// Sets the policy to the Not Configured state. /// This deletes the value associated with the policy, including its list if it has one. /// public void SetNotConfigured() { // Delete the enabled/disabled value if (this.ValueName != null) { using (RegistryKey key = this.GetKey()) { key.DeleteValue(this.ValueName, throwOnMissingValue: false); } } // Delete the value element if (this.elementId != null) { if (this.ValueElement.Name == XmlNames.List) { // Lists are stored in separate keys. Registry.LocalMachine.DeleteSubKeyTree(this.ValueElement.Attribute(XmlNames.Attributes.Key).Value, throwOnMissingSubKey: false); } else if (this.ValueElement.Name == XmlNames.Decimal) { // Decimals are stored in single values using (RegistryKey key = this.GetKey()) { key.DeleteValue(this.ValueElement.Attribute(XmlNames.Attributes.ValueName).Value, throwOnMissingValue: false); } } } } /// /// Sets the value of the policy when enabled. /// This uses only the "elements" of the policy, not the "enabledValue". /// The type used in the registry is chosen automatically. /// /// Value of the policy. public void SetEnabledValue(object value) { using (RegistryKey key = this.GetKey()) { key.SetValue( this.ValueElement.Attribute(XmlNames.Attributes.ValueName).Value, value); } } /// /// Sets the list value of the policy when enabled. /// This sets from the "elements" and also sets the enabled value as lists are also gated by a toggle. /// This will fail if the value of the policy is not a list. /// /// Values to set in the list. public void SetEnabledList(IEnumerable values) { this.Enable(); // Delete the existing list string listKeyPath = this.ValueElement.Attribute(XmlNames.Attributes.Key).Value; Registry.LocalMachine.DeleteSubKeyTree(listKeyPath, throwOnMissingSubKey: false); // Create and fill the key. // This assumes that the values don't need to have special names or prefixes. var listKey = Registry.LocalMachine.CreateSubKey(listKeyPath); int index = 0; foreach (string value in values) { TestContext.Out.WriteLine($"Setting {this.name} list value: {value}"); listKey.SetValue(index++.ToString(), value); } listKey.Close(); } /// /// Sets the list value of the policy when enabled. /// This sets from the "elements" and also sets the enabled value as lists are also gated by a toggle. /// This will fail if the value of the policy is not a list. /// /// Values to set in the list. public void SetEnabledList(IEnumerable values) { this.SetEnabledList(values.Select(source => JsonConvert.SerializeObject(source))); } /// /// Gets the value from a "decimal" child element. /// /// Element containing the decimal. /// Value in the element. private static int GetDecimalValue(XElement element) { // Reads a child that looks like this: // return int.Parse(element.Element(XmlNames.Decimal).Attribute(XmlNames.Attributes.Value).Value); } /// /// Gets the registry key backing this policy. /// /// /// This assumes that all the policies are machine-wide. /// If this changes, we will need to parse the class="machine|user" attribute. /// private RegistryKey GetKey() { return Registry.LocalMachine.CreateSubKey(this.KeyPath); } /// /// A group policy source object as used by AdditionalSources and AllowedSources. /// public class GroupPolicySource { /// /// Gets or sets the source name. /// public string Name { get; set; } /// /// Gets or sets the source arg. /// public string Arg { get; set; } /// /// Gets or sets the source type. /// public string Type { get; set; } /// /// Gets or sets the source data. /// public string Data { get; set; } /// /// Gets or sets the source identifier. /// public string Identifier { get; set; } /// /// Gets or sets certificate pinning. /// public GroupPolicyCertificatePinning CertificatePinning { get; set; } /// /// Gets or sets the source trust levels. /// public string[] TrustLevel { get; set; } /// /// Gets or sets a value indicating whether the source is explicit. /// public bool Explicit { get; set; } } /// /// Group policy certificate pinning. /// public class GroupPolicyCertificatePinning { /// /// Gets or sets the cert pinning chains. /// public GroupPolicyCertificatePinningChain[] Chains { get; set; } } /// /// Group policy certificate pinning chain. /// public class GroupPolicyCertificatePinningChain { /// /// Gets or sets the cert pinning details. /// public GroupPolicyCertificatePinningDetails[] Chain { get; set; } } /// /// Group policy certificate pinning details. /// public class GroupPolicyCertificatePinningDetails { /// /// Gets or sets the validation. /// public string[] Validation { get; set; } /// /// Gets or sets the embedded cert. /// public string EmbeddedCertificate { get; set; } } /// /// Names of the XML elements and attributes that make up the definition file. /// private static class XmlNames { // Root element public static readonly XName PolicyDefinitions = XName.Get("policyDefinitions", Namespace); public static readonly XName Policies = XName.Get("policies", Namespace); public static readonly XName Policy = XName.Get("policy", Namespace); public static readonly XName EnabledValue = XName.Get("enabledValue", Namespace); public static readonly XName DisabledValue = XName.Get("disabledValue", Namespace); public static readonly XName Elements = XName.Get("elements", Namespace); public static readonly XName Decimal = XName.Get("decimal", Namespace); public static readonly XName List = XName.Get("list", Namespace); private const string Namespace = "http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions"; public static class Attributes { public const string Name = "name"; public const string Value = "value"; public const string Id = "id"; public const string Key = "key"; public const string ValueName = "valueName"; } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/HashCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; using NUnit.Framework.Internal; /// /// Test hash command. /// public class HashCommand : BaseCommand { /// /// Test hash file. /// [Test] public void HashFile() { var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile("AppInstallerTestMsiInstaller.msi")); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("21d90ee9b3569590c624836ef50bf39791c7184869c227eedc00585e1f39b4de")); } /// /// Test hash msix. /// [Test] public void HashMSIX() { var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile(Constants.TestPackage) + " -m"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("08917b781939a7796746b5e2349e1f1d83b6c15599b60cd3f62816f15e565fc4")); Assert.True(result.StdOut.Contains("223b318c4b1154a1fb72b1bc23422810faa5ce899a8e774ba2a02834b2058f00")); } /// /// Test hash invalid msix. /// [Test] public void HashInvalidMSIX() { var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile("AppInstallerTestMsiInstaller.msi") + " -m"); Assert.AreEqual(Constants.ErrorCode.OPC_E_ZIP_MISSING_END_OF_CENTRAL_DIRECTORY, result.ExitCode); Assert.True(result.StdOut.Contains("21d90ee9b3569590c624836ef50bf39791c7184869c227eedc00585e1f39b4de")); Assert.True(result.StdOut.Contains("Please verify that the input file is a valid, signed MSIX.")); } /// /// Test hash file not found. /// [Test] public void HashFileNotFound() { var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile("DoesNot.Exist")); Assert.AreEqual(Constants.ErrorCode.ERROR_FILE_NOT_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("File does not exist")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Helpers { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Management.Automation; using System.Reflection; using System.Security.Principal; using System.Text; using System.Threading; using AppInstallerCLIE2ETests; using AppInstallerCLIE2ETests.PowerShell; using Microsoft.Management.Deployment; using Microsoft.Win32; using NUnit.Framework; /// /// Test common. /// public static class TestCommon { /// /// Scope. /// public enum Scope { /// /// None. /// Unknown, /// /// User. /// User, /// /// Machine. /// Machine, } /// /// The type of location. /// public enum TestModuleLocation { /// /// Current user. /// CurrentUser, /// /// All users. /// AllUsers, /// /// Winget module path. /// WinGetModulePath, /// /// Custom. /// Custom, /// /// Default winget configure. /// Default, } /// /// Gets a value indicating whether the current assembly is executing in an administrative context. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "Windows only API")] public static bool ExecutingAsAdministrator { get { WindowsIdentity identity = WindowsIdentity.GetCurrent(); WindowsPrincipal principal = new (identity); return principal.IsInRole(WindowsBuiltInRole.Administrator); } } /// /// Gets a value indicating whether the test is running in the CI build. /// public static bool IsCIEnvironment { get { return Environment.GetEnvironmentVariable("BUILD_BUILDNUMBER") != null; } } /// /// Run winget command. /// /// Command to run. /// Parameters. /// Optional std in. /// Optional timeout. /// Throw on timeout. /// The result of the command. public static RunCommandResult RunAICLICommand(string command, string parameters, string stdIn = null, int timeOut = 60000, bool throwOnTimeout = true) { string correlationParameter = " --correlation " + Guid.NewGuid().ToString(); // Don't include correlation when the call has an option ending `--` value. foreach (string part in parameters.Split(' ', StringSplitOptions.TrimEntries)) { if (part == "--") { correlationParameter = string.Empty; } } return RunAICLICommandViaDirectProcess(command, parameters + correlationParameter, stdIn, timeOut, throwOnTimeout); } /// /// Run command. /// /// File name. /// Args. /// Time out. /// If true, throw instead of returning false on a failure. /// True if exit code is 0. public static bool RunCommand(string fileName, string args = "", int timeOut = 60000, bool throwOnFailure = false) { RunCommandResult result = RunCommandWithResult(fileName, args, timeOut); if (result.ExitCode != 0) { TestContext.Out.WriteLine($"Command failed with: {result.ExitCode}"); if (throwOnFailure) { throw new RunCommandException(fileName, args, result); } return false; } else { return true; } } /// /// Run command with result. /// /// File name. /// Args. /// Optional timeout. /// Command result. public static RunCommandResult RunCommandWithResult(string fileName, string args, int timeOut = 60000) { TestContext.Out.WriteLine($"Running command: {fileName} {args}"); Process p = new Process(); p.StartInfo = new ProcessStartInfo(fileName, args); p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.Start(); RunCommandResult result = new (); if (p.WaitForExit(timeOut)) { result.ExitCode = p.ExitCode; result.StdOut = p.StandardOutput.ReadToEnd(); result.StdErr = p.StandardError.ReadToEnd(); if (TestSetup.Parameters.VerboseLogging) { TestContext.Out.WriteLine($"Command run finished. {fileName} {args} {timeOut}. Output: {result.StdOut} Error: {result.StdErr}"); } } else { throw new TimeoutException($"Command run timed out. {fileName} {args} {timeOut}"); } return result; } /// /// Get test file path. /// /// Test file name. /// Path of test file. public static string GetTestFile(string fileName) { return Path.Combine(TestContext.CurrentContext.TestDirectory, fileName); } /// /// Get test data file path. /// /// File name. /// Test file data path. public static string GetTestDataFile(string fileName) { return GetTestFile(Path.Combine("TestData", fileName)); } /// /// Get test work directory. Creates if not exists. /// /// The work directory. public static string GetTestWorkDir() { string workDir = Path.Combine(TestContext.CurrentContext.TestDirectory, "WorkDirectory"); Directory.CreateDirectory(workDir); return workDir; } /// /// Create random test directory. /// /// Path of new test directory. public static string GetRandomTestDir() { string randDir = Path.Combine(GetTestWorkDir(), Path.GetRandomFileName()); Directory.CreateDirectory(randDir); return randDir; } /// /// Creates new random file name. File is not created. /// /// Extension of random file. /// Path of random file. public static string GetRandomTestFile(string extension) { return Path.Combine(GetTestWorkDir(), Path.GetRandomFileName() + extension); } /// /// Install msix package via PowerShell. /// /// Msix file. /// True if installed. public static bool InstallMsix(string file) { return RunCommand("powershell", $"Add-AppxPackage \"{file}\"", throwOnFailure: true); } /// /// Install and register msix package via appx manifest. /// /// Path to package. /// Force shutdown. /// Throw on failure. /// True if installed correctly. public static bool InstallMsixRegister(string packagePath, bool forceShutdown = false, bool throwOnFailure = true) { string manifestFile = Path.Combine(packagePath, "AppxManifest.xml"); var command = $"Add-AppxPackage -Register \"{manifestFile}\""; if (forceShutdown) { command += " -ForceTargetApplicationShutdown"; } return RunCommand("powershell", command, throwOnFailure: throwOnFailure); } /// /// Remove msix package. /// /// Package to remove. /// Whether the package is provisioned. /// True if removed correctly. public static bool RemoveMsix(string name, bool isProvisioned = false) { if (isProvisioned) { return RunCommand("powershell", $"Get-AppxProvisionedPackage -Online | Where-Object {{$_.PackageName -like \"*{name}*\"}} | Remove-AppxProvisionedPackage -Online -AllUsers") && RunCommand("powershell", $"Get-AppxPackage \"{name}\" | Remove-AppxPackage -AllUsers"); } else { return RunCommand("powershell", $"Get-AppxPackage \"{name}\" | Remove-AppxPackage"); } } /// /// Gets the portable symlink directory. /// /// Scope. /// The path of the symlinks. public static string GetPortableSymlinkDirectory(Scope scope) { if (scope == Scope.User) { return Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), "Microsoft", "WinGet", "Links"); } else { return Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "WinGet", "Links"); } } /// /// Gets the portable package directory. /// /// The portable package directory. public static string GetPortablePackagesDirectory() { return Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), "Microsoft", "WinGet", "Packages"); } /// /// Gets the default download directory for the download command. /// /// The default download directory. public static string GetDefaultDownloadDirectory() { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads"); } /// /// Gets the checkpoints directory based on whether the command is invoked in desktop package or not. /// /// The default checkpoints directory. public static string GetCheckpointsDirectory() { if (TestSetup.Parameters.PackagedContext) { return Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), Constants.CheckpointDirectoryPackaged); } else { return Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), Constants.CheckpointDirectoryUnpackaged); } } /// /// Gets the fonts directory based on scope. /// /// Scope. /// The path of the fonts directory. public static string GetFontsDirectory(Scope scope) { if (scope == Scope.Machine) { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts"); } else { return Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), "Microsoft", "Windows", "Fonts"); } } /// /// Verify font package. /// /// Name of the package. /// Name of the package version. /// Scope. /// If package should exist. public static void VerifyFontPackage( string packageName, string packageVersion, Scope scope = Scope.User, bool shouldExist = true) { RegistryKey baseKey = (scope == Scope.Machine) ? Registry.LocalMachine : Registry.CurrentUser; var fileList = new List(); using (RegistryKey fontsRegistryKey = baseKey.OpenSubKey(Constants.FontsSubKey, true)) { using var winGetRootKey = fontsRegistryKey.OpenSubKey("Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"); if (shouldExist) { Assert.IsNotNull(winGetRootKey); } else { return; } using var packageNameSubkey = winGetRootKey.OpenSubKey(packageName); if (shouldExist) { Assert.IsNotNull(packageNameSubkey); } if (packageNameSubkey is not null) { using var versionSubkey = packageNameSubkey.OpenSubKey(packageVersion); if (shouldExist) { Assert.IsNotNull(versionSubkey); } else { Assert.IsNull(versionSubkey); } if (versionSubkey is not null) { var valueNames = versionSubkey.GetValueNames(); foreach (var valueName in valueNames) { fileList.Add(versionSubkey.GetValue(valueName).ToString()); } Assert.AreEqual(valueNames.Length, fileList.Count); } } } // Verify each package file we expect to exist actually exists. foreach (var file in fileList) { Assert.IsTrue(File.Exists(file)); } } /// /// Verify portable package. /// /// Install dir. /// Command alias. /// File name. /// Product code. /// Should exists. /// Scope. /// Install directory added to path instead of the symlink directory. public static void VerifyPortablePackage( string installDir, string commandAlias, string filename, string productCode, bool shouldExist, Scope scope = Scope.User, bool installDirectoryAddedToPath = false) { // When portables are installed, if the exe path is inside a directory it will not be aliased // if the exe path is at the root level, it will be aliased. Therefore, if either exist, the exe exists string exePath = Path.Combine(installDir, filename); string exeAliasedPath = Path.Combine(installDir, commandAlias); bool exeExists = File.Exists(exePath) || File.Exists(exeAliasedPath); string symlinkDirectory = GetPortableSymlinkDirectory(scope); string symlinkPath = Path.Combine(symlinkDirectory, commandAlias); bool symlinkExists = File.Exists(symlinkPath); bool portableEntryExists; RegistryKey baseKey = scope == Scope.User ? Registry.CurrentUser : Registry.LocalMachine; string uninstallSubKey = Constants.UninstallSubKey; using (RegistryKey uninstallRegistryKey = baseKey.OpenSubKey(uninstallSubKey, true)) { RegistryKey portableEntry = uninstallRegistryKey.OpenSubKey(productCode, true); portableEntryExists = portableEntry != null; } bool isAddedToPath; string pathSubKey = scope == Scope.User ? Constants.PathSubKey_User : Constants.PathSubKey_Machine; using (RegistryKey environmentRegistryKey = baseKey.OpenSubKey(pathSubKey, true)) { string pathName = "Path"; var currentPathValue = (string)environmentRegistryKey.GetValue(pathName); var portablePathValue = (installDirectoryAddedToPath ? installDir : symlinkDirectory) + ';'; isAddedToPath = currentPathValue.Contains(portablePathValue); } // Always clean up as best effort. RunAICLICommand("uninstall", $"--product-code {productCode} --force"); Assert.AreEqual(shouldExist, exeExists, $"Expected portable exe path: {exePath}"); Assert.AreEqual(shouldExist && !installDirectoryAddedToPath, symlinkExists, $"Expected portable symlink path: {symlinkPath}"); Assert.AreEqual(shouldExist, portableEntryExists, $"Expected {productCode} subkey in path: {uninstallSubKey}"); Assert.AreEqual(shouldExist, isAddedToPath, $"Expected path variable: {(installDirectoryAddedToPath ? installDir : symlinkDirectory)}"); } /// /// Copies log files to the path %TEMP%\E2ETestLogs. /// public static void PublishE2ETestLogs() { string tempPath = Path.GetTempPath(); string localAppDataPath = Environment.GetEnvironmentVariable("LocalAppData"); string testLogsPackagedSourcePath = Path.Combine(localAppDataPath, Constants.E2ETestLogsPathPackaged); string testLogsUnpackagedSourcePath = Path.Combine(tempPath, Constants.E2ETestLogsPathUnpackaged); string testLogsDestPath = Path.Combine(tempPath, "E2ETestLogs"); string testLogsPackagedDestPath = Path.Combine(testLogsDestPath, "Packaged"); string testLogsUnpackagedDestPath = Path.Combine(testLogsDestPath, "Unpackaged"); if (Directory.Exists(testLogsPackagedSourcePath)) { CopyDirectory(testLogsPackagedSourcePath, testLogsPackagedDestPath); } if (Directory.Exists(testLogsUnpackagedSourcePath)) { CopyDirectory(testLogsUnpackagedSourcePath, testLogsUnpackagedDestPath); } } /// /// Gets the server certificate as a hex string. /// /// Hex string. public static string GetTestServerCertificateHexString() { if (string.IsNullOrEmpty(TestSetup.Parameters.LocalServerCertPath)) { throw new Exception($"{Constants.LocalServerCertPathParameter} not set."); } if (!File.Exists(TestSetup.Parameters.LocalServerCertPath)) { throw new FileNotFoundException(TestSetup.Parameters.LocalServerCertPath); } return Convert.ToHexString(File.ReadAllBytes(TestSetup.Parameters.LocalServerCertPath)); } /// /// Verify exe installer correctly. /// /// Install directory. /// Optional expected content. /// True if success. public static bool VerifyTestExeInstalled(string installDir, string expectedContent = null) { bool verifyInstallSuccess = true; if (!File.Exists(Path.Combine(installDir, Constants.TestExeInstalledFileName))) { TestContext.Out.WriteLine($"TestExeInstalled.exe not found at {installDir}"); verifyInstallSuccess = false; } if (verifyInstallSuccess && !string.IsNullOrEmpty(expectedContent)) { string content = File.ReadAllText(Path.Combine(installDir, Constants.TestExeInstalledFileName)); TestContext.Out.WriteLine($"TestExeInstalled.exe content: {content}"); verifyInstallSuccess = content.Contains(expectedContent); } return verifyInstallSuccess; } /// /// Verifies if the repair of the test executable was successful. /// /// The directory where the test executable is installed. /// The expected content in the test executable file. This is optional. /// Returns true if the repair was successful, false otherwise. public static bool VerifyTestExeRepairSuccessful(string installDir, string expectedContent = null) { bool verifyRepairSuccess = true; if (!File.Exists(Path.Combine(installDir, Constants.TestExeRepairCompletedFileName))) { TestContext.Out.WriteLine($"{Constants.TestExeRepairCompletedFileName} not found at {installDir}"); verifyRepairSuccess = false; } if (verifyRepairSuccess && !string.IsNullOrEmpty(expectedContent)) { string content = File.ReadAllText(Path.Combine(installDir, Constants.TestExeRepairCompletedFileName)); TestContext.Out.WriteLine($"TestExeRepairCompleted.txt content: {content}"); verifyRepairSuccess = content.Contains(expectedContent); } return verifyRepairSuccess; } /// /// Assert installer and manifest downloaded correctly and cleanup. /// /// Download directory. /// Package name. /// Package version. /// Installer architecture. /// Installer scope. /// Installer type. /// Installer locale. /// Boolean value indicating whether the installer is an archive. /// Boolean value indicating whether to remove the installer file and directory. public static void AssertInstallerDownload( string downloadDir, string name, string version, Windows.System.ProcessorArchitecture arch, Scope scope, PackageInstallerType installerType, string locale = null, bool isArchive = false, bool cleanup = true) { string expectedFileName = $"{name}_{version}"; if (scope != Scope.Unknown) { expectedFileName += $"_{scope}"; } expectedFileName += $"_{arch}_{installerType}"; if (!string.IsNullOrEmpty(locale)) { expectedFileName += $"_{locale}"; } string installerExtension; if (isArchive) { installerExtension = ".zip"; } else { installerExtension = installerType switch { PackageInstallerType.Msi => ".msi", PackageInstallerType.Msix => ".msix", _ => ".exe" }; } string installerDownloadPath = Path.Combine(downloadDir, expectedFileName + installerExtension); string manifestDownloadPath = Path.Combine(downloadDir, expectedFileName + ".yaml"); Assert.IsTrue(Directory.Exists(downloadDir), $"Download directory does not exist: {downloadDir}"); Assert.IsTrue(File.Exists(installerDownloadPath), $"Installer file does not exist: {installerDownloadPath}"); Assert.IsTrue(File.Exists(manifestDownloadPath), $"Manifest file does not exist: {manifestDownloadPath}"); if (cleanup) { Directory.Delete(downloadDir, true); } } /// /// Best effort test exe cleanup. /// /// Install directory. public static void BestEffortTestExeCleanup(string installDir) { var uninstallerPath = Path.Combine(installDir, Constants.TestExeUninstallerFileName); if (File.Exists(uninstallerPath)) { RunCommand(Path.Combine(installDir, Constants.TestExeUninstallerFileName)); } } /// /// Best effort test exe cleanup and install directory cleanup. /// /// Install directory. public static void CleanupTestExeAndDirectory(string installDir) { // Always try clean up and ignore clean up failure BestEffortTestExeCleanup(installDir); // Delete the install directory to reclaim disk space if (Directory.Exists(installDir)) { Directory.Delete(installDir, true); } } /// /// Verify exe installer correctly and then uninstall it. /// /// Install directory. /// Optional expected content. /// True if success. public static bool VerifyTestExeInstalledAndCleanup(string installDir, string expectedContent = null) { bool verifyInstallSuccess = VerifyTestExeInstalled(installDir, expectedContent); // Always try clean up and ignore clean up failure BestEffortTestExeCleanup(installDir); return verifyInstallSuccess; } /// /// Verify exe repair completed and cleanup. /// /// Install directory. /// Optional expected context. /// True if success. public static bool VerifyTestExeRepairCompletedAndCleanup(string installDir, string expectedContent = null) { bool verifyRepairSuccess = VerifyTestExeRepairSuccessful(installDir, expectedContent); CleanupTestExeAndDirectory(installDir); return verifyRepairSuccess; } /// /// Verify msi installed correctly. /// /// Installed directory. /// True if success. public static bool VerifyTestMsiInstalledAndCleanup(string installDir) { string pathToCheck = Path.Combine(installDir, Constants.AppInstallerTestExeInstallerExe); if (!File.Exists(pathToCheck)) { TestContext.Out.WriteLine($"File not found: {pathToCheck}"); return false; } return RunCommand("msiexec.exe", $"/qn /x {Constants.MsiInstallerProductCode}"); } /// /// Verify msix installed correctly. /// /// Whether the package is provisioned. /// True if success. public static bool VerifyTestMsixInstalledAndCleanup(bool isProvisioned = false) { var result = RunCommandWithResult("powershell", $"Get-AppxPackage {Constants.MsixInstallerName}"); if (!result.StdOut.Contains(Constants.MsixInstallerName)) { return false; } if (isProvisioned) { result = RunCommandWithResult("powershell", $"Get-AppxProvisionedPackage -Online | Where-Object {{$_.PackageName -like \"*{Constants.MsixInstallerName}*\"}}"); if (!result.StdOut.Contains(Constants.MsixInstallerName)) { return false; } } return RemoveMsix(Constants.MsixInstallerName, isProvisioned); } /// /// Verify test exe uninstalled. /// /// Installed directory. /// True if success. public static bool VerifyTestExeUninstalled(string installDir) { return File.Exists(Path.Combine(installDir, Constants.TestExeUninstalledFileName)); } /// /// Verify msi uninstalled. /// /// Install directory. /// True if success. public static bool VerifyTestMsiUninstalled(string installDir) { return !File.Exists(Path.Combine(installDir, Constants.AppInstallerTestExeInstallerExe)); } /// /// Verify msix uninstalled. /// /// Whether the package is provisioned. /// True if success. public static bool VerifyTestMsixUninstalled(bool isProvisioned = false) { bool isUninstalled = false; var result = RunCommandWithResult("powershell", $"Get-AppxPackage {Constants.MsixInstallerName}"); isUninstalled = string.IsNullOrWhiteSpace(result.StdOut); if (isProvisioned) { result = RunCommandWithResult("powershell", $"Get-AppxProvisionedPackage -Online | Where-Object {{$_.PackageName -like \"*{Constants.MsixInstallerName}*\"}}"); isUninstalled = isUninstalled && string.IsNullOrWhiteSpace(result.StdOut); } return isUninstalled; } /// /// Modify uninstalled registry key. /// /// Product code. /// Name. /// Value. public static void ModifyPortableARPEntryValue(string productCode, string name, string value) { using (RegistryKey uninstallRegistryKey = Registry.CurrentUser.OpenSubKey(Constants.UninstallSubKey, true)) { RegistryKey entry = uninstallRegistryKey.OpenSubKey(productCode, true); entry.SetValue(name, value); } } /// /// Set up test source. /// /// Use group policy. public static void SetupTestSource(bool useGroupPolicyForTestSource = false) { // Remove the test source so that its package is also removed. RunAICLICommand("source remove", Constants.TestSourceName); RunAICLICommand("source reset", "--force"); RunAICLICommand("source remove", Constants.DefaultWingetSourceName); RunAICLICommand("source remove", Constants.DefaultMSStoreSourceName); // TODO: If/when cert pinning is implemented on the packaged index source, useGroupPolicyForTestSource should be set to default true // to enable testing it by default. Until then, leaving this here... if (useGroupPolicyForTestSource) { GroupPolicyHelper.EnableAdditionalSources.SetEnabledList(new GroupPolicyHelper.GroupPolicySource[] { new GroupPolicyHelper.GroupPolicySource { Name = Constants.TestSourceName, Arg = Constants.TestSourceUrl, Type = Constants.TestSourceType, Data = Constants.TestSourceIdentifier, Identifier = Constants.TestSourceIdentifier, CertificatePinning = new GroupPolicyHelper.GroupPolicyCertificatePinning { Chains = new GroupPolicyHelper.GroupPolicyCertificatePinningChain[] { new GroupPolicyHelper.GroupPolicyCertificatePinningChain { Chain = new GroupPolicyHelper.GroupPolicyCertificatePinningDetails[] { new GroupPolicyHelper.GroupPolicyCertificatePinningDetails { Validation = new string[] { "publickey" }, EmbeddedCertificate = GetTestServerCertificateHexString(), }, }, }, }, }, TrustLevel = new string[] { "None" }, Explicit = false, }, }); } else { GroupPolicyHelper.EnableAdditionalSources.SetNotConfigured(); RunAICLICommand("source add", $"{Constants.TestSourceName} {Constants.TestSourceUrl} --trust-level trusted"); } Thread.Sleep(2000); } /// /// Tear down test source. /// public static void TearDownTestSource() { RunAICLICommand("source remove", Constants.TestSourceName); RunAICLICommand("source reset", "--force"); } /// /// Ensures that a module is in the desired state. /// /// The module. /// Whether the module is present or not. /// The repository to get the module from if needed. /// The location to install the module. public static void EnsureModuleState(string moduleName, bool present, string repository = null, TestCommon.TestModuleLocation location = TestModuleLocation.CurrentUser) { string wingetModulePath = TestCommon.GetExpectedModulePath(TestModuleLocation.WinGetModulePath); string customPath = TestCommon.GetExpectedModulePath(TestModuleLocation.Custom); ICollection e2eModule; bool isPresent = false; { using var pwsh = new PowerShellHost(); pwsh.AddModulePath($"{wingetModulePath};{customPath}"); e2eModule = pwsh.PowerShell.AddCommand("Get-Module").AddParameter("Name", moduleName).AddParameter("ListAvailable").Invoke(); isPresent = e2eModule.Any(); } TestContext.Out.WriteLine($"EnsureModuleState: {moduleName}[present:{present}] => isPresent:{isPresent}"); if (isPresent) { // If the module was saved in a different location we can't Uninstall-Module. foreach (var module in e2eModule) { var moduleBase = module.Path; while (Path.GetFileName(moduleBase) != moduleName) { moduleBase = Path.GetDirectoryName(moduleBase); } if (!present) { TestContext.Out.WriteLine($"EnsureModuleState: Removing {moduleName} to match present=false"); Directory.Delete(moduleBase, true); } else { // Must be present in the right location. var expectedLocation = TestCommon.GetExpectedModulePath(location); if (!moduleBase.StartsWith(expectedLocation)) { TestContext.Out.WriteLine($"EnsureModuleState: Removing {moduleName} as it is not in the correct location"); Directory.Delete(moduleBase, true); isPresent = false; } } } } if (!isPresent && present) { if (location == TestModuleLocation.CurrentUser || location == TestModuleLocation.AllUsers) { using var pwsh = new PowerShellHost(); pwsh.AddModulePath($"{wingetModulePath};{customPath}"); pwsh.PowerShell.AddCommand("Install-Module").AddParameter("Name", moduleName).AddParameter("Force"); if (!string.IsNullOrEmpty(repository)) { pwsh.PowerShell.AddParameter("Repository", repository); } if (location == TestModuleLocation.CurrentUser) { pwsh.PowerShell.AddParameter("Scope", "CurrentUser"); } else if (location == TestModuleLocation.AllUsers) { pwsh.PowerShell.AddParameter("Scope", "AllUsers"); } TestContext.Out.WriteLine($"EnsureModuleState: Installing module {moduleName} to {location}"); _ = pwsh.PowerShell.Invoke(); } else { string path = customPath; if (location == TestModuleLocation.WinGetModulePath || location == TestModuleLocation.Default) { path = wingetModulePath; } using var pwsh = new PowerShellHost(); pwsh.AddModulePath($"{wingetModulePath};{customPath}"); pwsh.PowerShell.AddCommand("Save-Module").AddParameter("Name", moduleName).AddParameter("Path", path).AddParameter("Force"); if (!string.IsNullOrEmpty(repository)) { pwsh.PowerShell.AddParameter("Repository", repository); } TestContext.Out.WriteLine($"EnsureModuleState: Saving module {moduleName} to {path}"); _ = pwsh.PowerShell.Invoke(); } } } /// /// Creates an ARP entry from the given values. /// /// Product code of the entry. /// The properties to set in the entry. /// Scope of the entry. public static void CreateARPEntry( string productCode, object properties, Scope scope = Scope.User) { RegistryKey baseKey = scope == Scope.User ? Registry.CurrentUser : Registry.LocalMachine; using (RegistryKey uninstallRegistryKey = baseKey.OpenSubKey(Constants.UninstallSubKey, true)) { RegistryKey entry = uninstallRegistryKey.CreateSubKey(productCode, true); foreach (PropertyInfo property in properties.GetType().GetProperties()) { entry.SetValue(property.Name, property.GetValue(properties)); } } } /// /// Removes an ARP entry. /// /// Product code of the entry. /// Scope of the entry. public static void RemoveARPEntry( string productCode, Scope scope = Scope.User) { RegistryKey baseKey = scope == Scope.User ? Registry.CurrentUser : Registry.LocalMachine; using (RegistryKey uninstallRegistryKey = baseKey.OpenSubKey(Constants.UninstallSubKey, true)) { uninstallRegistryKey.DeleteSubKey(productCode); } } /// /// Copies the contents of a given directory from a source path to a destination path. /// /// Source directory name. /// Destination directory name. public static void CopyDirectory(string sourceDirName, string destDirName) { DirectoryInfo dir = new DirectoryInfo(sourceDirName); DirectoryInfo[] dirs = dir.GetDirectories(); if (!Directory.Exists(destDirName)) { Directory.CreateDirectory(destDirName); } FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { string temppath = Path.Combine(destDirName, file.Name); file.CopyTo(temppath, false); } foreach (DirectoryInfo subdir in dirs) { string temppath = Path.Combine(destDirName, subdir.Name); CopyDirectory(subdir.FullName, temppath); } } /// /// Gets the expected module path. /// /// Location. /// The expected path of the module. public static string GetExpectedModulePath(TestModuleLocation location) { switch (location) { case TestModuleLocation.CurrentUser: return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"PowerShell\Modules"); case TestModuleLocation.AllUsers: return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"PowerShell\Modules"); case TestModuleLocation.WinGetModulePath: case TestModuleLocation.Default: return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\WinGet\Configuration\Modules"); case TestModuleLocation.Custom: return Path.Combine(Path.GetTempPath(), "E2ECustomModules"); default: throw new ArgumentException(location.ToString()); } } /// /// Gets the instance identifier of the first configuration history item with name in its output line. /// /// The string to search for. /// The instance identifier of a configuration that matched the search, or an empty string if none did. public static string GetConfigurationInstanceIdentifierFor(string name) { var result = TestCommon.RunAICLICommand("configure list", string.Empty); Assert.AreEqual(0, result.ExitCode); string[] lines = result.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) { if (line.Contains(name)) { // Find the first GUID in the output int left = line.IndexOf('{'); int right = line.IndexOfAny(new char[] { '}', '…' }); Assert.AreNotEqual(-1, left); Assert.AreNotEqual(-1, right); Assert.LessOrEqual(right - left, 38); return line.Substring(left, right - left); } } return string.Empty; } /// /// Copy the installer file to the ARP InstallSource directory. /// /// Test installer to be copied. /// Installer Product. /// is WoW6432Node to use. /// Returns the installer source directory if the file operation is successful, otherwise returns an empty string. public static string CopyInstallerFileToARPInstallSourceDirectory(string installerFilePath, string productCode, bool useWoW6432Node = false) { if (string.IsNullOrEmpty(installerFilePath)) { new ArgumentNullException(nameof(installerFilePath)); } if (!File.Exists(installerFilePath)) { new FileNotFoundException(installerFilePath); } string outputDirectory = string.Empty; // Define the registry paths for both x64 and x86 string registryPath = useWoW6432Node ? $@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{productCode}" : $@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{productCode}"; // Open the registry key where the uninstall information is stored using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryPath)) { if (key != null) { // Read the InstallSource value string arpInstallSourceDirectory = key.GetValue("InstallSource") as string; if (!string.IsNullOrEmpty(arpInstallSourceDirectory)) { // Copy the MSI installer to the InstallSource directory string installerFileName = Path.GetFileName(installerFilePath); string installerDestinationPath = Path.Combine(arpInstallSourceDirectory, installerFileName); if (!Directory.Exists(arpInstallSourceDirectory)) { Directory.CreateDirectory(arpInstallSourceDirectory); } File.Copy(installerFilePath, installerDestinationPath, true); outputDirectory = arpInstallSourceDirectory; } } } return outputDirectory; } /// /// Run winget command via direct process. /// /// The executable to run. /// Command to run. /// Parameters. /// Optional std in. /// Optional timeout. /// Throw on timeout. /// The result of the command. public static RunCommandResult RunProcess(string executablePath, string command, string parameters, string stdIn, int timeOut, bool throwOnTimeout) { string inputMsg = "Exe path: " + executablePath + " Command: " + command + " Parameters: " + parameters + (string.IsNullOrEmpty(stdIn) ? string.Empty : " StdIn: " + stdIn) + " Timeout: " + timeOut; TestContext.Out.WriteLine($"Starting command run. {inputMsg}"); RunCommandResult result = new (); Process p = new Process(); p.StartInfo = new ProcessStartInfo(executablePath, command + ' ' + parameters); p.StartInfo.UseShellExecute = false; p.StartInfo.StandardOutputEncoding = Encoding.UTF8; p.StartInfo.RedirectStandardOutput = true; StringBuilder outputData = new (); p.OutputDataReceived += (sender, args) => { if (args.Data != null) { outputData.AppendLine(args.Data); } }; p.StartInfo.StandardErrorEncoding = Encoding.UTF8; p.StartInfo.RedirectStandardError = true; StringBuilder errorData = new (); p.ErrorDataReceived += (sender, args) => { if (args.Data != null) { errorData.AppendLine(args.Data); } }; if (!string.IsNullOrEmpty(stdIn)) { p.StartInfo.RedirectStandardInput = true; } p.Start(); p.BeginOutputReadLine(); p.BeginErrorReadLine(); if (!string.IsNullOrEmpty(stdIn)) { p.StandardInput.Write(stdIn); p.StandardInput.Close(); } if (p.WaitForExit(timeOut)) { // According to documentation, this extra call will ensure that the redirected streams // have finished reading all of the data. p.WaitForExit(); result.ExitCode = p.ExitCode; result.StdOut = outputData.ToString(); result.StdErr = errorData.ToString(); TestContext.Out.WriteLine("Command run completed with exit code: " + result.ExitCode); if (!string.IsNullOrEmpty(result.StdErr)) { TestContext.Error.WriteLine("Command run error. Error: " + result.StdErr); } if (TestSetup.Parameters.VerboseLogging) { TestContext.Out.WriteLine("Command run output. Output:\n" + result.StdOut ?? ""); } } else if (throwOnTimeout) { throw new TimeoutException($"Direct command run timed out: {command} {parameters}"); } return result; } /// /// Run winget command via direct process. /// /// Command to run. /// Parameters. /// Optional std in. /// Optional timeout. /// Throw on timeout. /// The result of the command. private static RunCommandResult RunAICLICommandViaDirectProcess(string command, string parameters, string stdIn, int timeOut, bool throwOnTimeout) { return RunProcess(TestSetup.Parameters.AICLIPath, command, parameters, stdIn, timeOut, throwOnTimeout); } /// /// Run command result. /// public struct RunCommandResult { /// /// Exit code. /// public int ExitCode; /// /// StdOut. /// public string StdOut; /// /// StdErr. /// public string StdErr; } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Helpers/TestIndex.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Helpers { using System; using System.IO; using System.Text.Json; using Microsoft.WinGetSourceCreator; using WinGetSourceCreator.Model; /// /// Test index setup. /// public static class TestIndex { static TestIndex() { // Expected path for the installers. TestIndex.ExeInstaller = Path.Combine(TestSetup.Parameters.StaticFileRootPath, Constants.ExeInstaller, Constants.ExeInstallerFileName); TestIndex.MsiInstaller = Path.Combine(TestSetup.Parameters.StaticFileRootPath, Constants.MsiInstaller, Constants.MsiInstallerFileName); TestIndex.MsiInstallerV2 = Path.Combine(TestSetup.Parameters.StaticFileRootPath, Constants.MsiInstaller, Constants.MsiInstallerV2FileName); TestIndex.MsixInstaller = Path.Combine(TestSetup.Parameters.StaticFileRootPath, Constants.MsixInstaller, Constants.MsixInstallerFileName); TestIndex.ZipInstaller = Path.Combine(TestSetup.Parameters.StaticFileRootPath, Constants.ZipInstaller, Constants.ZipInstallerFileName); TestIndex.Font = Path.Combine(TestSetup.Parameters.StaticFileRootPath, Constants.FontFileName, Constants.FontFileName); } /// /// Gets the signed exe installer path used by the manifests in the E2E test. /// public static string ExeInstaller { get; private set; } /// /// Gets the signed msi installer path used by the manifests in the E2E test. /// public static string MsiInstaller { get; private set; } /// /// Gets the signed msi installerV2 path used by the manifests in the E2E test. /// public static string MsiInstallerV2 { get; private set; } /// /// Gets the signed msix installer path used by the manifests in the E2E test. /// public static string MsixInstaller { get; private set; } /// /// Gets the zip installer path used by the manifests in the E2E test. /// public static string ZipInstaller { get; private set; } /// /// Gets the font file path used by the manifests in the E2E test. /// public static string Font { get; private set; } /// /// Generate test source. /// public static void GenerateE2ESource() { var testParams = TestSetup.Parameters; if (string.IsNullOrEmpty(testParams.ExeInstallerPath)) { throw new ArgumentNullException($"{Constants.ExeInstallerPathParameter} is required"); } if (!File.Exists(testParams.ExeInstallerPath)) { throw new FileNotFoundException(testParams.ExeInstallerPath); } if (string.IsNullOrEmpty(testParams.MsiInstallerPath)) { throw new ArgumentNullException($"{Constants.MsiInstallerPathParameter} is required"); } if (!File.Exists(testParams.MsiInstallerPath)) { throw new FileNotFoundException(testParams.MsiInstallerPath); } if (string.IsNullOrEmpty(testParams.MsiInstallerV2Path)) { throw new ArgumentNullException($"{Constants.MsiInstallerV2PathParameter} is required"); } if (!File.Exists(testParams.MsiInstallerV2Path)) { throw new FileNotFoundException(testParams.MsiInstallerV2Path); } if (string.IsNullOrEmpty(testParams.MsixInstallerPath)) { throw new ArgumentNullException($"{Constants.MsixInstallerPathParameter} is required"); } if (!File.Exists(testParams.MsixInstallerPath)) { throw new FileNotFoundException(testParams.MsixInstallerPath); } if (string.IsNullOrEmpty(testParams.FontPath)) { throw new ArgumentNullException($"{Constants.FontPathParameter} is required"); } if (!File.Exists(testParams.FontPath)) { throw new FileNotFoundException(testParams.FontPath); } if (string.IsNullOrEmpty(testParams.PackageCertificatePath)) { throw new ArgumentNullException($"{Constants.PackageCertificatePathParameter} is required"); } if (!File.Exists(testParams.PackageCertificatePath)) { throw new FileNotFoundException(testParams.PackageCertificatePath); } LocalSource e2eSource = new () { AppxManifest = TestCommon.GetTestDataFile(Path.Combine("Package", "AppxManifest.xml")), WorkingDirectory = testParams.StaticFileRootPath, LocalManifests = new () { TestCommon.GetTestDataFile("Manifests"), }, LocalInstallers = new () { new LocalInstaller { Type = InstallerType.Exe, Name = Path.Combine(Constants.ExeInstaller, Constants.ExeInstallerFileName), Input = testParams.ExeInstallerPath, HashToken = "", }, new LocalInstaller { Type = InstallerType.Msi, Name = Path.Combine(Constants.MsiInstaller, Constants.MsiInstallerFileName), Input = testParams.MsiInstallerPath, HashToken = "", }, new LocalInstaller { Type = InstallerType.Msi, Name = Path.Combine(Constants.MsiInstaller, Constants.MsiInstallerV2FileName), Input = testParams.MsiInstallerPath, HashToken = "", }, new LocalInstaller { Type = InstallerType.Msix, Name = Path.Combine(Constants.MsixInstaller, Constants.MsixInstallerFileName), Input = testParams.MsixInstallerPath, HashToken = "", SignatureToken = "", }, new LocalInstaller { Type = InstallerType.Font, Name = Path.Combine(Constants.FontFileName, Constants.FontFileName), Input = testParams.FontPath, HashToken = "", }, }, DynamicInstallers = new () { new DynamicInstaller { Type = InstallerType.Zip, Name = Path.Combine(Constants.ZipInstaller, Constants.ZipInstallerFileName), Input = new () { ExeInstaller, MsiInstaller, MsixInstaller, }, HashToken = "", }, }, Signature = new () { CertFile = testParams.PackageCertificatePath, }, }; WinGetLocalSource.CreateLocalSource(e2eSource); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Helpers/TestSetup.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Helpers { using System; using System.IO; using NUnit.Framework; /// /// Singleton class with test parameters. /// internal class TestSetup { private static readonly Lazy Lazy = new (() => new TestSetup()); private string settingFilePath = null; private TestSetup() { if (TestContext.Parameters.Count == 0) { this.IsDefault = true; } // Read TestParameters and set runtime variables this.PackagedContext = this.InitializeBoolParam(Constants.PackagedContextParameter, true); this.VerboseLogging = this.InitializeBoolParam(Constants.VerboseLoggingParameter, true); this.LooseFileRegistration = this.InitializeBoolParam(Constants.LooseFileRegistrationParameter); this.SkipTestSource = this.InitializeBoolParam(Constants.SkipTestSourceParameter, this.IsDefault); this.InprocTestbedUseTestPackage = this.InitializeBoolParam(Constants.InprocTestbedUseTestPackageParameter); // For packaged context, default to AppExecutionAlias this.AICLIPath = this.InitializeStringParam(Constants.AICLIPathParameter, this.PackagedContext ? "WinGetDev.exe" : TestCommon.GetTestFile("winget.exe")); this.AICLIPackagePath = this.InitializeStringParam(Constants.AICLIPackagePathParameter, TestCommon.GetTestFile("AppInstallerCLIPackage.appxbundle")); this.StaticFileRootPath = this.InitializeDirectoryParam(Constants.StaticFileRootPathParameter, Path.GetTempPath()); this.LocalServerCertPath = this.InitializeFileParam(Constants.LocalServerCertPathParameter); this.PackageCertificatePath = this.InitializeFileParam(Constants.PackageCertificatePathParameter); this.ExeInstallerPath = this.InitializeFileParam(Constants.ExeInstallerPathParameter); this.MsiInstallerPath = this.InitializeFileParam(Constants.MsiInstallerPathParameter); this.MsixInstallerPath = this.InitializeFileParam(Constants.MsixInstallerPathParameter); this.MsiInstallerV2Path = this.InitializeFileParam(Constants.MsiInstallerV2PathParameter); this.FontPath = this.InitializeFileParam(Constants.FontPathParameter); this.InprocTestbedPath = this.InitializeFileParam(Constants.InprocTestbedPathParameter); this.ForcedExperimentalFeatures = this.InitializeStringArrayParam(Constants.ForcedExperimentalFeaturesParameter); } /// /// Gets the instance object. /// public static TestSetup Parameters { get { return Lazy.Value; } } /// /// Gets the cli path. /// public string AICLIPath { get; } /// /// Gets the package path. /// public string AICLIPackagePath { get; } /// /// Gets a value indicating whether the test runs in package context. /// public bool PackagedContext { get; } /// /// Gets a value indicating whether the test uses verbose logging. /// public bool VerboseLogging { get; } /// /// Gets a value indicating whether to use loose file registration. /// public bool LooseFileRegistration { get; } /// /// Gets the static file root path. /// public string StaticFileRootPath { get; } /// /// Gets the local server cert path. /// public string LocalServerCertPath { get; } /// /// Gets the exe installer path. /// public string ExeInstallerPath { get; } /// /// Gets the msi installer path. /// public string MsiInstallerPath { get; } /// /// Gets the msi installer V2 path. /// public string MsiInstallerV2Path { get; } /// /// Gets the msix installer path. /// public string MsixInstallerPath { get; } /// /// Gets the zip installer path. /// public string ZipInstallerPath { get; } /// /// Gets the font path. /// public string FontPath { get; } /// /// Gets the package cert path. /// public string PackageCertificatePath { get; } /// /// Gets the inproc testbed executable path. /// public string InprocTestbedPath { get; } /// /// Gets a value indicating whether to use the test package or not. /// public bool InprocTestbedUseTestPackage { get; } /// /// Gets a value indicating whether to skip creating test source. /// public bool SkipTestSource { get; } /// /// Gets the settings json path. /// public string SettingsJsonFilePath { get { if (this.settingFilePath == null) { this.settingFilePath = WinGetSettingsHelper.GetUserSettingsPath(); } return this.settingFilePath; } } /// /// Gets the experimental features that should be forcibly enabled. /// public string[] ForcedExperimentalFeatures { get; } /// /// Gets a value indicating whether is the default parameters. /// public bool IsDefault { get; } private bool InitializeBoolParam(string paramName, bool defaultValue = false) { if (this.IsDefault || !TestContext.Parameters.Exists(paramName)) { return defaultValue; } return TestContext.Parameters.Get(paramName).Equals("true", StringComparison.OrdinalIgnoreCase); } private string InitializeStringParam(string paramName, string defaultValue = null) { if (this.IsDefault || !TestContext.Parameters.Exists(paramName)) { return defaultValue; } return TestContext.Parameters.Get(paramName); } private string[] InitializeStringArrayParam(string paramName, string[] defaultValue = null) { if (this.IsDefault || !TestContext.Parameters.Exists(paramName)) { return defaultValue; } return TestContext.Parameters.Get(paramName).Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); } private string InitializeFileParam(string paramName, string defaultValue = null) { if (!TestContext.Parameters.Exists(paramName)) { return defaultValue; } var value = TestContext.Parameters.Get(paramName); if (!File.Exists(value)) { throw new FileNotFoundException($"{paramName}: {value}"); } return value; } private string InitializeDirectoryParam(string paramName, string defaultValue = null) { if (!TestContext.Parameters.Exists(paramName)) { return defaultValue; } var value = TestContext.Parameters.Get(paramName); if (!Directory.Exists(value)) { throw new DirectoryNotFoundException($"{paramName}: {value}"); } return value; } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Helpers/WinGetSettingsHelper.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Helpers { using System.Collections; using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; /// /// Helper class to set winget settings. /// internal static class WinGetSettingsHelper { /// /// Gets or sets the experimental features that should be forcibly enabled. /// public static string[] ForcedExperimentalFeatures { get; set; } /// /// Gets the user settings path by calling winget settings export. /// /// Expanded path for user settings. public static string GetUserSettingsPath() { var result = TestCommon.RunAICLICommand("settings", "export"); var output = result.StdOut; var serialized = JObject.Parse(output); return (string)serialized.GetValue("userSettingsFile"); } /// /// Initialize settings. /// public static void InitializeWingetSettings() { Hashtable experimentalFeatures = new Hashtable(); var forcedExperimentalFeatures = ForcedExperimentalFeatures; if (forcedExperimentalFeatures != null) { foreach (var feature in forcedExperimentalFeatures) { experimentalFeatures[feature] = true; } } var settingsJson = new Hashtable() { { "$schema", "https://aka.ms/winget-settings.schema.json" }, { "experimentalFeatures", experimentalFeatures }, { "debugging", new Hashtable() { { "enableSelfInitiatedMinidump", true }, { "keepAllLogFiles", true }, } }, { "installBehavior", new Hashtable() { } }, { "configureBehavior", new Hashtable() { } }, }; // Run winget one time to initialize settings directory // when running in unpackaged context TestCommon.RunAICLICommand(string.Empty, "-v"); SetWingetSettings(JsonConvert.SerializeObject(settingsJson, Formatting.Indented)); } /// /// Configure experimental features. /// /// Feature name. /// Status. public static void ConfigureFeature(string featureName, bool status) { JObject settingsJson = GetJsonSettingsObject("experimentalFeatures"); ConfigureFeature(settingsJson, featureName, status); SetWingetSettings(settingsJson); } /// /// Configure the install behavior. /// /// Setting name. /// Setting value. public static void ConfigureInstallBehavior(string settingName, string value) { JObject settingsJson = GetJsonSettingsObject("installBehavior"); var installBehavior = settingsJson["installBehavior"]; installBehavior[settingName] = value; SetWingetSettings(settingsJson); } /// /// Configure the configuration behavior. /// /// Setting name. /// Setting value. public static void ConfigureConfigureBehavior(string settingName, string value) { JObject settingsJson = GetJsonSettingsObject("configureBehavior"); var configureBehavior = settingsJson["configureBehavior"]; configureBehavior[settingName] = value; SetWingetSettings(settingsJson); } /// /// Configure the install behavior preferences. /// /// Setting name. /// Setting value. public static void ConfigureInstallBehaviorPreferences(string settingName, string value) { JObject settingsJson = GetJsonSettingsObject("installBehavior"); var installBehavior = settingsJson["installBehavior"]; if (installBehavior["preferences"] == null) { installBehavior["preferences"] = new JObject(); } var preferences = installBehavior["preferences"]; preferences[settingName] = value; SetWingetSettings(settingsJson); } /// /// Configure the install behavior preferences. /// /// Setting name. /// Setting value array. public static void ConfigureInstallBehaviorPreferences(string settingName, string[] value) { JObject settingsJson = GetJsonSettingsObject("installBehavior"); var installBehavior = settingsJson["installBehavior"]; if (installBehavior["preferences"] == null) { installBehavior["preferences"] = new JObject(); } var preferences = installBehavior["preferences"]; preferences[settingName] = new JArray(value); SetWingetSettings(settingsJson); } /// /// Configure the install behavior requirements. /// /// Setting name. /// Setting value. public static void ConfigureInstallBehaviorRequirements(string settingName, string value) { JObject settingsJson = GetJsonSettingsObject("installBehavior"); var installBehavior = settingsJson["installBehavior"]; if (installBehavior["requirements"] == null) { installBehavior["requirements"] = new JObject(); } var requirements = installBehavior["requirements"]; requirements[settingName] = value; SetWingetSettings(settingsJson); } /// /// Configure the install behavior requirements. /// /// Setting name. /// Setting value array. public static void ConfigureInstallBehaviorRequirements(string settingName, string[] value) { JObject settingsJson = GetJsonSettingsObject("installBehavior"); var installBehavior = settingsJson["installBehavior"]; if (installBehavior["requirements"] == null) { installBehavior["requirements"] = new JObject(); } var requirements = installBehavior["requirements"]; requirements[settingName] = new JArray(value); SetWingetSettings(settingsJson); } /// /// Initialize all features. /// /// Initialized feature value. public static void InitializeAllFeatures(bool status) { JObject settingsJson = GetJsonSettingsObject("experimentalFeatures"); ConfigureFeature(settingsJson, "experimentalArg", status); ConfigureFeature(settingsJson, "experimentalCmd", status); ConfigureFeature(settingsJson, "directMSI", status); ConfigureFeature(settingsJson, "windowsFeature", status); ConfigureFeature(settingsJson, "resume", status); ConfigureFeature(settingsJson, "reboot", status); ConfigureFeature(settingsJson, "fonts", status); ConfigureFeature(settingsJson, "sourcePriority", status); SetWingetSettings(settingsJson); } /// /// Configure the logging level for the settings. /// /// The logging level to set; null removes the value. public static void ConfigureLoggingLevel(string level) { JObject settingsJson = GetJsonSettingsObject("logging"); if (level == null) { settingsJson["logging"]["level"]?.Parent?.Remove(); } else { settingsJson["logging"]["level"] = new JValue(level); } SetWingetSettings(settingsJson); } /// /// Configure experimental features. /// /// The settings JSON object to modify. /// Feature name. /// Status. private static void ConfigureFeature(JObject settingsJson, string featureName, bool status) { var experimentalFeatures = settingsJson["experimentalFeatures"]; experimentalFeatures[featureName] = status; } private static JObject GetJsonSettingsObject(string objectName) { JObject settingsJson = JObject.Parse(File.ReadAllText(TestSetup.Parameters.SettingsJsonFilePath)); if (!settingsJson.ContainsKey(objectName)) { settingsJson[objectName] = new JObject(); } return settingsJson; } /// /// Converts a JObject to a string and writes to the settings file. /// /// Settings to set. private static void SetWingetSettings(JObject settingsJson) { var forcedExperimentalFeatures = ForcedExperimentalFeatures; if (forcedExperimentalFeatures != null) { foreach (var feature in forcedExperimentalFeatures) { ConfigureFeature(settingsJson, feature, true); } } SetWingetSettings(settingsJson.ToString()); } /// /// Writes string to settings file. /// /// Settings as string. private static void SetWingetSettings(string settings) { File.WriteAllText(TestSetup.Parameters.SettingsJsonFilePath, settings); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ImportCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Import command tests. /// public class ImportCommand : BaseCommand { /// /// Set up. /// [SetUp] public void Setup() { this.CleanupTestExe(); } /// /// Test import v1. /// [Test] public void ImportSuccessful_1_0() { var result = TestCommon.RunAICLICommand("import", this.GetTestImportFile("ImportFile-Good.1.0.json")); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(this.VerifyTestExeInstalled()); this.UninstallTestExe(); } /// /// Test import v2. /// [Test] public void ImportSuccessful_2_0() { var result = TestCommon.RunAICLICommand("import", this.GetTestImportFile("ImportFile-Good.2.0.json")); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(this.VerifyTestExeInstalled()); this.UninstallTestExe(); } /// /// Test import invalid file. /// [Test] public void ImportInvalidFile() { // Verify failure when trying to import with an invalid file var result = TestCommon.RunAICLICommand("import", this.GetTestImportFile("ImportFile-Bad-Invalid.json")); Assert.AreEqual(Constants.ErrorCode.ERROR_JSON_INVALID_FILE, result.ExitCode); Assert.True(result.StdOut.Contains("JSON file is not valid")); } /// /// Test import from an unknown source. /// [Test] public void ImportUnknownSource() { // Verify failure when trying to import from an unknown source var result = TestCommon.RunAICLICommand("import", this.GetTestImportFile("ImportFile-Bad-UnknownSource.json")); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); Assert.True(result.StdOut.Contains("Source required for import is not installed")); } /// /// Test import for an unknown package. /// [Test] public void ImportUnavailablePackage() { // Verify failure when trying to import an unavailable package var result = TestCommon.RunAICLICommand("import", this.GetTestImportFile("ImportFile-Bad-UnknownPackage.json")); Assert.AreEqual(Constants.ErrorCode.ERROR_NOT_ALL_QUERIES_FOUND_SINGLE, result.ExitCode); Assert.True(result.StdOut.Contains("Package not found: MissingPackage")); } /// /// Test import when the package version is not present. /// [Test] public void ImportUnavailableVersion() { // Verify failure when trying to import an unavailable package var result = TestCommon.RunAICLICommand("import", this.GetTestImportFile("ImportFile-Bad-UnknownPackageVersion.json")); Assert.AreEqual(Constants.ErrorCode.ERROR_NOT_ALL_QUERIES_FOUND_SINGLE, result.ExitCode); Assert.True(result.StdOut.Contains("Search failed for: AppInstallerTest.TestExeInstaller")); } /// /// Test import when the package is already installed. /// [Test] public void ImportAlreadyInstalled() { // Verify success with message when trying to import a package that is already installed var installDir = TestCommon.GetRandomTestDir(); TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -l {installDir}"); var result = TestCommon.RunAICLICommand("import", $"{this.GetTestImportFile("ImportFile-Good.1.0.json")}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Package is already installed")); Assert.False(this.VerifyTestExeInstalled()); this.UninstallTestExe(); } /// /// Test Import with an exported file. /// [Test] public void ImportExportedFile() { // Verify success when importing an exported list of packages. // First install the test package to ensure it is exported. TestCommon.RunAICLICommand("install", Constants.ExeInstallerPackageId); var jsonFile = TestCommon.GetRandomTestFile(".json"); TestCommon.RunAICLICommand("export", $"{jsonFile} -s TestSource"); // Uninstall the package to ensure we can install it again this.UninstallTestExe(); // Import the file var result = TestCommon.RunAICLICommand("import", jsonFile); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(this.VerifyTestExeInstalled()); this.UninstallTestExe(); } private string GetTestImportFile(string importFileName) { return TestCommon.GetTestDataFile(Path.Combine("ImportFiles", importFileName)); } private bool VerifyTestExeInstalled(string installDir = null) { if (string.IsNullOrEmpty(installDir)) { // Default location used by installer installDir = Path.GetTempPath(); } return File.Exists(Path.Combine(installDir, Constants.TestExeInstalledFileName)); } private void UninstallTestExe() { TestCommon.RunAICLICommand("uninstall", Constants.ExeInstallerPackageId); } private void CleanupTestExe() { this.UninstallTestExe(); File.Delete(Path.Combine(Path.GetTempPath(), Constants.TestExeInstalledFileName)); File.Delete(Path.Combine(Path.GetTempPath(), Constants.TestExeUninstallerFileName)); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/InprocTestbedTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using System.Reflection; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Tests that run the inproc testbed targeting COM lifetime. /// public class InprocTestbedTests { /// /// The activation type to use when creating objects. /// public enum ActivationType { /// /// Use the WinRT type name for activation via C++/WinRT object construction. /// ClassName, /// /// Use the CLSID for activation via C++/WinRT `create_instance`. /// CoCreateInstance, } /// /// Control when the module will allow signal that it can be unloaded if all objects are released. /// This does not affect the loader by taking additional references to the module. /// public enum UnloadBehavior { /// /// Allows the unload check function to proceed with object count checks and unload when possible. /// Allow, /// /// Prevents the unload check until just before COM is uninitialized. /// AtUninitialize, /// /// Prevents the unload check at all times. /// Never, } /// /// Gets or sets the path to the inproc testbed executable. /// private string InprocTestbedPath { get; set; } /// /// Gets or sets the string that contains the package identity to use for the tests. /// private string TargetPackageInformation { get; set; } /// /// Setup done once before all the tests here. /// [OneTimeSetUp] public void OneTimeSetup() { this.InprocTestbedPath = TestSetup.Parameters.InprocTestbedPath; if (string.IsNullOrWhiteSpace(this.InprocTestbedPath)) { string assemblyLocation = Assembly.GetExecutingAssembly().Location; this.InprocTestbedPath = Path.Join(Path.GetDirectoryName(assemblyLocation), "..\\ComInprocTestbed\\ComInprocTestbed.exe"); } if (TestSetup.Parameters.InprocTestbedUseTestPackage) { this.TargetPackageInformation = $"-pkg {Constants.ExeInstallerPackageId} -src {Constants.TestSourceName} -url {Constants.TestSourceUrl}"; } } /// /// Executes the testbed as simply as possible to ensure integrations. /// [Test] public void DefaultTest() { this.RunInprocTestbed(new TestbedParameters() { WorkTestSleepInterval = 1000 }); } /// /// Tests using the CLSID with CoCreateInstance. /// /// Control whether COM should be uninitialized at the end of the process. /// Set the unload behavior for the test. /// Sets the number of milliseconds to sleep between each work/test iteration. [Test] [TestCase(false, UnloadBehavior.AtUninitialize)] [TestCase(false, UnloadBehavior.Never)] [TestCase(true, UnloadBehavior.Allow, 1000)] [TestCase(true, UnloadBehavior.Never)] public void CLSID_Tests(bool leakCOM, UnloadBehavior unloadBehavior, int? workTestSleep = null) { this.RunInprocTestbed(new TestbedParameters() { ActivationType = ActivationType.CoCreateInstance, LeakCOM = leakCOM, UnloadBehavior = unloadBehavior, Iterations = 10, WorkTestSleepInterval = workTestSleep, }); } /// /// Tests using C++/WinRT object activation through the type name. /// /// Control whether the C++/WinRT factory cache will be cleared between iterations. /// Control whether COM should be uninitialized at the end of the process. /// Set the unload behavior for the test. /// Sets the number of milliseconds to sleep between each work/test iteration. [Test] [TestCase(false, false, UnloadBehavior.AtUninitialize)] [TestCase(false, false, UnloadBehavior.Never)] [TestCase(false, true, UnloadBehavior.Allow)] [TestCase(false, true, UnloadBehavior.Never)] [TestCase(true, false, UnloadBehavior.AtUninitialize)] [TestCase(true, false, UnloadBehavior.Never)] [TestCase(true, true, UnloadBehavior.Allow, 1000)] [TestCase(true, true, UnloadBehavior.Never)] public void TypeName_Tests(bool freeCachedFactories, bool leakCOM, UnloadBehavior unloadBehavior, int? workTestSleep = null) { this.RunInprocTestbed(new TestbedParameters() { ActivationType = ActivationType.ClassName, ClearFactories = freeCachedFactories, LeakCOM = leakCOM, UnloadBehavior = unloadBehavior, Iterations = 10, WorkTestSleepInterval = workTestSleep, }); } /// /// Tests that disable the termination signal handling. /// /// Control whether the module should listen to termination signals. /// Set the unload behavior for the test. /// Sets the number of milliseconds to sleep between each work/test iteration. [Test] [TestCase(true, UnloadBehavior.Allow, 1000)] [TestCase(true, UnloadBehavior.Never)] [TestCase(false, UnloadBehavior.Allow, 1000)] [TestCase(false, UnloadBehavior.Never)] public void TerminationSignal_Tests(bool disableTerminationSignals, UnloadBehavior unloadBehavior, int? workTestSleep = null) { this.RunInprocTestbed(new TestbedParameters() { ActivationType = ActivationType.CoCreateInstance, DisableTerminationSignals = disableTerminationSignals, UnloadBehavior = unloadBehavior, Iterations = 10, WorkTestSleepInterval = workTestSleep, }); } private void RunInprocTestbed(TestbedParameters parameters, int timeout = 300000) { string builtParameters = string.Empty; if (parameters.ActivationType != null) { builtParameters += $"-activation {parameters.ActivationType} "; } if (!parameters.ClearFactories) { builtParameters += "-keep-factories "; } if (parameters.LeakCOM) { builtParameters += "-leak-com "; } if (parameters.UnloadBehavior != null) { builtParameters += $"-unload {parameters.UnloadBehavior} "; } if (parameters.Test != null) { builtParameters += $"-test {parameters.Test} "; } if (parameters.Iterations != null) { builtParameters += $"-itr {parameters.Iterations} "; } if (parameters.WorkTestSleepInterval != null) { builtParameters += $"-work-test-sleep {parameters.WorkTestSleepInterval} "; } if (parameters.DisableTerminationSignals) { builtParameters += $"-no-term "; } var result = TestCommon.RunProcess(this.InprocTestbedPath, this.TargetPackageInformation, builtParameters, null, timeout, true); Assert.AreEqual(0, result.ExitCode); } /// /// The parameters to provide for running tests. /// private class TestbedParameters { internal ActivationType? ActivationType { get; init; } = null; internal bool ClearFactories { get; init; } = true; internal bool LeakCOM { get; init; } = false; internal UnloadBehavior? UnloadBehavior { get; init; } = null; internal string Test { get; init; } = "unload_check"; internal int? Iterations { get; init; } = null; internal int? WorkTestSleepInterval { get; init; } = null; internal bool DisableTerminationSignals { get; init; } = false; } } } ================================================ FILE: src/AppInstallerCLIE2ETests/InstallCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System; using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test install command. /// public class InstallCommand : BaseCommand { /// /// One time set up. /// [OneTimeSetUp] public void OneTimeSetup() { WinGetSettingsHelper.ConfigureFeature("sourcePriority", true); } /// /// Set up. /// [SetUp] public void Setup() { // Try clean up TestExeInstaller for failure cases where cleanup is not successful TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestExeInstaller"); } /// /// Test package doesn't exist. /// [Test] public void InstallAppDoesNotExist() { var result = TestCommon.RunAICLICommand("install", "DoesNotExist"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("No package found matching input criteria.")); } /// /// Test multiple matches found. /// [Test] public void InstallWithMultipleAppsMatchingQuery() { var result = TestCommon.RunAICLICommand("install", "TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_MULTIPLE_APPLICATIONS_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("Multiple packages found matching input criteria. Please refine the input.")); } /// /// Test install exe. /// [Test] public void InstallExe() { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); } /// /// Test inapplicable os version. /// [Test] public void InstallExeWithInsufficientMinOsVersion() { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"InapplicableOsVersion --silent -l {installDir}"); // MinOSVersion is moved to installer level, the check is performed during installer selection Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICABLE_INSTALLER, result.ExitCode); Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); } /// /// Test install exe hash mismatch. /// [Test] public void InstallExeWithHashMismatch() { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"TestExeSha256Mismatch --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALLER_HASH_MISMATCH, result.ExitCode); Assert.True(result.StdOut.Contains("Installer hash does not match")); Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); } /// /// Test install inno. /// [Test] public void InstallWithInno() { // Install test inno, manifest does not provide silent switch, we should be populating the default var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"TestInnoInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/VERYSILENT")); } /// /// Test install burn. /// [Test] public void InstallBurn() { // Install test burn, manifest does not provide silent switch, we should be populating the default var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"TestBurnInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/quiet")); } /// /// Test install nullsoft. /// [Test] public void InstallNullSoft() { // Install test Nullsoft, manifest does not provide silent switch, we should be populating the default var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"TestNullsoftInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/S")); } /// /// Test install msi. /// [Test] public void InstallMSI() { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"TestMsiInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(installDir)); } /// /// Test install msix. /// [Test] public void InstallMSIX() { var result = TestCommon.RunAICLICommand("install", $"TestMsixInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test install msix machine scope. /// [Test] public void InstallMSIXMachineScope() { // TODO: Provision and Deprovision api not supported in build server. Assert.Ignore(); var result = TestCommon.RunAICLICommand("install", $"TestMsixInstaller --scope machine"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup(true)); } /// /// Test install msix with signature hash. /// [Test] public void InstallMSIXWithSignature() { var result = TestCommon.RunAICLICommand("install", $"TestMsixWithSignatureHash"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test install msix with signature hash machine scope. /// [Test] public void InstallMSIXWithSignatureMachineScope() { // TODO: Provision and Deprovision api not supported in build server. Assert.Ignore(); var result = TestCommon.RunAICLICommand("install", $"TestMsixWithSignatureHash --scope machine"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup(true)); } /// /// Test msix hash mismatch. /// [Test] public void InstallMSIXWithSignatureHashMismatch() { var result = TestCommon.RunAICLICommand("install", $"TestMsixSignatureHashMismatch"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALLER_HASH_MISMATCH, result.ExitCode); Assert.True(result.StdOut.Contains("Installer hash does not match")); Assert.False(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test install with alternate source failure. /// [Test] public void InstallExeWithAlternateSourceFailure() { TestCommon.RunAICLICommand("source add", "failSearch \"{ \"\"SearchHR\"\": \"\"0x80070002\"\" }\" Microsoft.Test.Configurable --header \"{}\""); try { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); Assert.AreEqual(unchecked((int)0x80070002), result.ExitCode); Assert.True(result.StdOut.Contains("Failed when searching source: failSearch")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExeInstaller")); Assert.False(result.StdOut.Contains("Successfully installed")); Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); } finally { this.ResetTestSource(); } } /// /// Test install portable package. /// [Test] public void InstallPortableExe() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); // If no location specified, default behavior is to create a package directory with the name "{packageId}_{sourceId}" TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test install portable package with command. /// [Test] public void InstallPortableExeWithCommand() { var installDir = TestCommon.GetRandomTestDir(); string packageId, commandAlias, fileName, productCode; packageId = "AppInstallerTest.TestPortableExeWithCommand"; productCode = packageId + "_" + Constants.TestSourceIdentifier; fileName = "AppInstallerTestExeInstaller.exe"; commandAlias = "testCommand.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(installDir, commandAlias, fileName, productCode, true); } /// /// Test install portable package with rename. /// [Test] public void InstallPortableExeWithRename() { var installDir = TestCommon.GetRandomTestDir(); string packageId, productCode, renameArgValue; packageId = "AppInstallerTest.TestPortableExeWithCommand"; productCode = packageId + "_" + Constants.TestSourceIdentifier; renameArgValue = "testRename.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {installDir} --rename {renameArgValue}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(installDir, renameArgValue, renameArgValue, productCode, true); } /// /// Test install portable package invalid rename. /// [Test] public void InstallPortableInvalidRename() { var installDir = TestCommon.GetRandomTestDir(); string packageId, renameArgValue; packageId = "AppInstallerTest.TestPortableExeWithCommand"; renameArgValue = "test?"; var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {installDir} --rename {renameArgValue}"); Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("The specified filename is not a valid filename")); } /// /// Test install portable package with reserve names. /// [Test] public void InstallPortableReservedNames() { var installDir = TestCommon.GetRandomTestDir(); string packageId, renameArgValue; packageId = "AppInstallerTest.TestPortableExeWithCommand"; renameArgValue = "CON"; var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {installDir} --rename {renameArgValue}"); Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("The specified filename is not a valid filename")); } /// /// Test install portable package to an existing directory. /// [Test] public void InstallPortableToExistingDirectory() { var installDir = TestCommon.GetRandomTestDir(); var existingDir = Path.Combine(installDir, "testDirectory"); Directory.CreateDirectory(existingDir); string packageId, commandAlias, fileName, productCode; packageId = "AppInstallerTest.TestPortableExe"; productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {existingDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(existingDir, commandAlias, fileName, productCode, true); } /// /// Test install portable package. Symlink is a directory. /// [Test] public void InstallPortableFailsWithCleanup() { string packageId, commandAlias; packageId = "AppInstallerTest.TestPortableExe"; commandAlias = "AppInstallerTestExeInstaller.exe"; // Create a directory with the same name as the symlink in order to cause install to fail. string symlinkDirectory = TestCommon.GetPortableSymlinkDirectory(TestCommon.Scope.User); string conflictDirectory = Path.Combine(symlinkDirectory, commandAlias); Directory.CreateDirectory(conflictDirectory); var result = TestCommon.RunAICLICommand("install", $"{packageId}"); // Remove directory prior to assertions as this will impact other tests if assertions fail. Directory.Delete(conflictDirectory, true); Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Unable to create symlink")); } /// /// Test reinstalling portable package. /// [Test] public void ReinstallPortable() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); string symlinkDirectory = TestCommon.GetPortableSymlinkDirectory(TestCommon.Scope.User); string symlinkPath = Path.Combine(symlinkDirectory, commandAlias); // Clean first install should not display file overwrite message. Assert.True(result.StdOut.Contains("Successfully installed")); Assert.False(result.StdOut.Contains($"Overwriting existing file: {symlinkPath}")); // Perform second install and verify that file overwrite message is displayed. var result2 = TestCommon.RunAICLICommand("install", $"{packageId} --force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result2.ExitCode); Assert.True(result2.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test installing portable package user scope. /// [Test] public void InstallPortable_UserScope() { string installDir = TestCommon.GetRandomTestDir(); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageUserRoot, installDir); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId} --scope user"); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageUserRoot, string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test install portable package machine scope. /// [Test] public void InstallPortable_MachineScope() { string installDir = TestCommon.GetRandomTestDir(); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, installDir); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId} --scope machine"); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.Machine); } /// /// Test install portable package with settings set to user install scope. /// [Test] public void InstallPortable_InstallScopePreference_User() { string installDir = TestCommon.GetRandomTestDir(); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageUserRoot, installDir); WinGetSettingsHelper.ConfigureInstallBehaviorPreferences(Constants.InstallBehaviorScope, "user"); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId}"); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageUserRoot, string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test install portable package with settings set to machine install scope. /// [Test] public void InstallPortable_InstallScopePreference_Machine() { string installDir = TestCommon.GetRandomTestDir(); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, installDir); WinGetSettingsHelper.ConfigureInstallBehaviorPreferences(Constants.InstallBehaviorScope, "machine"); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId}"); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.Machine); } /// /// Test install zip exe. /// [Test] public void InstallZip_Exe() { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithExe --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); } /// /// Test install zip portable. /// [Test] public void InstallZip_Portable() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestZipInstallerWithPortable"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = "TestPortable.exe"; fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.User); } /// /// Test install zip portable with binaries that depend on PATH variable. /// [Test] public void InstallZip_ArchivePortableWithBinariesDependentOnPath() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.ArchivePortableWithBinariesDependentOnPath"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = "TestPortable.exe"; fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.User, true); } /// /// Test install zip with invalid relative file path. /// [Test] public void InstallZipWithInvalidRelativeFilePath() { var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInvalidRelativePath"); Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Invalid relative file path to the nested installer; path points to a location outside of the install directory")); } /// /// Test install zip msi. /// [Test] public void InstallZipWithMsi() { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithMsi --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(installDir)); } /// /// Test install zip msix. /// [Test] public void InstallZipWithMsix() { var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithMsix"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test install zip exe by extracting with tar. /// [Test] public void InstallZip_ExtractWithTar() { WinGetSettingsHelper.ConfigureInstallBehavior(Constants.ArchiveExtractionMethod, "tar"); var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithExe --silent -l {installDir}"); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.ArchiveExtractionMethod, string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); } /// /// Test install an installed package and convert to upgrade. /// [Test] public void InstallExeFoundExistingConvertToUpgrade() { var baseDir = TestCommon.GetRandomTestDir(); var baseResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -v 1.0.0.0 --silent -l {baseDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, baseResult.ExitCode); Assert.True(baseResult.StdOut.Contains("Successfully installed")); // Install will convert to upgrade var upgradeDir = TestCommon.GetRandomTestDir(); var upgradeResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {upgradeDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, upgradeResult.ExitCode); Assert.True(upgradeResult.StdOut.Contains("Trying to upgrade the installed package...")); Assert.True(upgradeResult.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(baseDir)); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(upgradeDir, "/Version 2.0.0.0")); } /// /// Test install an installed package without an available upgrade. /// [Test] public void InstallExeFoundExistingConvertToUpgradeNoAvailableUpgrade() { var baseDir = TestCommon.GetRandomTestDir(); var baseResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -v 2.0.0.0 --silent -l {baseDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, baseResult.ExitCode); Assert.True(baseResult.StdOut.Contains("Successfully installed")); // Install will convert to upgrade var upgradeDir = TestCommon.GetRandomTestDir(); var upgradeResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {upgradeDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_UPDATE_NOT_APPLICABLE, upgradeResult.ExitCode); Assert.True(upgradeResult.StdOut.Contains("Trying to upgrade the installed package...")); Assert.True(upgradeResult.StdOut.Contains("No available upgrade")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(baseDir)); } /// /// Test force installing a package. /// [Test] public void InstallExeWithLatestInstalledWithForce() { var baseDir = TestCommon.GetRandomTestDir(); var baseResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -v 2.0.0.0 --silent -l {baseDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, baseResult.ExitCode); Assert.True(baseResult.StdOut.Contains("Successfully installed")); // Install will not convert to upgrade var installDir = TestCommon.GetRandomTestDir(); var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -v 1.0.0.0 --silent -l {installDir} --force"); Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(baseDir)); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); } /// /// Test install a package with an invalid Windows Feature dependency. /// [Test] public void InstallWithWindowsFeatureDependency_FeatureNotFound() { var testDir = TestCommon.GetRandomTestDir(); var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.WindowsFeature -l {testDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_DEPENDENCIES, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("The feature [invalidFeature] was not found.")); Assert.True(installResult.StdOut.Contains("Failed to enable Windows Feature dependencies. To proceed with installation, use '--force'.")); } /// /// Test install a package with a Windows Feature dependency using the force argument. /// [Test] public void InstallWithWindowsFeatureDependency_Force() { var testDir = TestCommon.GetRandomTestDir(); var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.WindowsFeature --silent --force -l {testDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("Failed to enable Windows Feature dependencies; proceeding due to --force")); Assert.True(installResult.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(testDir)); } /// /// Test install a package with a package dependency that requires the PATH environment variable to be refreshed between dependency installs. /// [Test] public void InstallWithPackageDependency_RefreshPathVariable() { var testDir = TestCommon.GetRandomTestDir(); string installDir = TestCommon.GetPortablePackagesDirectory(); var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.PackageDependencyRequiresPathRefresh -l {testDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("Successfully installed")); // Portable package is used as a dependency. Ensure that it is installed and cleaned up successfully. string portablePackageId, commandAlias, fileName, packageDirName, productCode; portablePackageId = "AppInstallerTest.TestPortableExeWithCommand"; packageDirName = productCode = portablePackageId + "_" + Constants.TestSourceIdentifier; fileName = "AppInstallerTestExeInstaller.exe"; commandAlias = "testCommand.exe"; TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(testDir)); } /// /// Test install a package with a package dependency and specify dependencies only. /// [Test] public void InstallWithPackageDependency_DependenciesOnly() { var testDir = TestCommon.GetRandomTestDir(); string installDir = TestCommon.GetPortablePackagesDirectory(); var installResult = TestCommon.RunAICLICommand("install", $"-q AppInstallerTest.PackageDependencyRequiresPathRefresh -l {testDir} --dependencies-only"); Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("Installing dependencies only. The package itself will not be installed.")); Assert.True(installResult.StdOut.Contains("Successfully installed")); // Portable package is used as a dependency. Ensure that it is installed and cleaned up successfully. string portablePackageId, commandAlias, fileName, packageDirName, productCode; portablePackageId = "AppInstallerTest.TestPortableExeWithCommand"; packageDirName = productCode = portablePackageId + "_" + Constants.TestSourceIdentifier; fileName = "AppInstallerTestExeInstaller.exe"; commandAlias = "testCommand.exe"; TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(testDir)); } /// /// Test install a package using a specific installer type. /// [Test] public void InstallWithInstallerTypeArgument() { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMultipleInstallers --silent -l {installDir} --installer-type exe"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); } /// /// Test install package with installer type preference settings. /// [Test] public void InstallWithInstallerTypePreference() { string[] installerTypePreference = { "nullsoft" }; WinGetSettingsHelper.ConfigureInstallBehaviorPreferences(Constants.InstallerTypes, installerTypePreference); string installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMultipleInstallers --silent -l {installDir}"); // Reset installer type preferences. WinGetSettingsHelper.ConfigureInstallBehaviorPreferences(Constants.InstallerTypes, Array.Empty()); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir), "/S"); } /// /// Test install package with installer type requirement settings. /// [Test] public void InstallWithInstallerTypeRequirement() { string[] installerTypeRequirement = { "inno" }; WinGetSettingsHelper.ConfigureInstallBehaviorRequirements(Constants.InstallerTypes, installerTypeRequirement); string installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMultipleInstallers --silent -l {installDir}"); // Reset installer type requirements. WinGetSettingsHelper.ConfigureInstallBehaviorRequirements(Constants.InstallerTypes, Array.Empty()); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICABLE_INSTALLER, result.ExitCode); Assert.True(result.StdOut.Contains("No applicable installer found; see logs for more details.")); } /// /// This test flow is intended to test an EXE that actually installs an MSIX internally, and whose name+publisher /// information resembles an existing installation. Given this, the goal is to get correlation to stick to the /// MSIX rather than the ARP entry that we would match with in the absence of the package family name being present. /// [Test] public void InstallExeThatInstallsMSIX() { string targetPackageIdentifier = "AppInstallerTest.TestExeInstallerInstallsMSIX"; string fakeProductCode = "e35f5799-cce3-41fd-886c-c36fcb7104fe"; // Insert fake ARP entry as if a non-MSIX version of the package is already installed. // The name here must not match the normalized name of the package, but be close enough to meet // the confidence requirements for correlation after an install operation (so we drop one word here). TestCommon.CreateARPEntry(fakeProductCode, new { DisplayName = "EXE Installer that Installs MSIX", Publisher = "AppInstallerTest", DisplayVersion = "1.0.0", }); // We should not find it before installing because the normalized name doesn't match var result = TestCommon.RunAICLICommand("list", targetPackageIdentifier); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); // Add the MSIX to simulate an installer doing it TestCommon.InstallMsix(TestIndex.MsixInstaller); // Install our exe that "installs" the MSIX result = TestCommon.RunAICLICommand("install", $"{targetPackageIdentifier} --force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); // We should find the package now, and it should be correlated to the MSIX (although we don't actually know that from this probe) result = TestCommon.RunAICLICommand("list", targetPackageIdentifier); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); // Remove the MSIX outside of winget's knowledge to keep the tracking data. TestCommon.RemoveMsix(Constants.MsixInstallerName); // We should not find the package now that the MSIX is gone, confirming that it was correlated result = TestCommon.RunAICLICommand("list", targetPackageIdentifier); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); TestCommon.RemoveARPEntry(fakeProductCode); } /// /// Test install source priority. /// [Test] public void InstallExeWithSourcePriority() { // This test source always returns a single package from search TestCommon.RunAICLICommand("source add", "dummyPackage \"{ \"\"ContainsPackage\"\": true }\" Microsoft.Test.Configurable --header \"{}\""); try { // Attempt install with equal (default) priorities var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_MULTIPLE_APPLICATIONS_FOUND, result.ExitCode); Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); // Change the priority of the primary test source to be higher TestCommon.RunAICLICommand("source edit", $"{Constants.TestSourceName} --priority 1"); result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExeInstaller")); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); } finally { this.ResetTestSource(); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/BaseInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System.Collections.Generic; using System.Linq; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; /// /// Base interop class. /// public abstract class BaseInterop { /// /// Initializes a new instance of the class. /// /// Initializer. public BaseInterop(IInstanceInitializer initializer) { this.TestFactory = new (initializer); } /// /// Gets the test factory. /// public WinGetProjectionFactory TestFactory { get; } /// /// Find one filtered package from a provided package catalog reference. /// /// Package catalog reference. /// Package match field. /// Package field match option. /// Package match value. /// List of matches. public MatchResult FindOnePackage( PackageCatalogReference packageCatalogReference, PackageMatchField field, PackageFieldMatchOption option, string value) { var findPackages = this.FindAllPackages(packageCatalogReference, field, option, value); Assert.AreEqual(1, findPackages.Count, $"Expected exactly one package but found {findPackages.Count}"); return findPackages.First(); } /// /// Find all filtered package from a provided package catalog reference. /// /// Package catalog reference. /// Package match field. /// Package field match option. /// Package match value. /// List of matches. protected IReadOnlyList FindAllPackages( PackageCatalogReference packageCatalogReference, PackageMatchField field, PackageFieldMatchOption option, string value) { Assert.NotNull(packageCatalogReference, "Package catalog reference cannot be null"); // Prepare filter var filter = this.TestFactory.CreatePackageMatchFilter(); filter.Field = field; filter.Option = option; filter.Value = value; // Add filter var findPackageOptions = this.TestFactory.CreateFindPackagesOptions(); findPackageOptions.Filters.Add(filter); // Connect and find package var source = packageCatalogReference.Connect().PackageCatalog; return source.FindPackages(findPackageOptions).Matches; } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.IO; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; using WinRT; /// /// Tests check installed status. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class CheckInstalledStatusInterop : BaseInterop { private string installDir; private string defaultInstallDir = Path.Combine(Path.GetTempPath(), "TestInstalledStatus"); private PackageManager packageManager; private PackageCatalogReference testSource; /// /// Initializes a new instance of the class. /// /// Initializer. public CheckInstalledStatusInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Test setup. /// [SetUp] public void SetUp() { this.packageManager = this.TestFactory.CreatePackageManager(); this.testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); this.installDir = TestCommon.GetRandomTestDir(); } /// /// Clean up. /// /// A representing the asynchronous operation. [TearDown] public async Task Cleanup() { // Find and uninstall the test package if applicable. var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); options.Catalogs.Add(this.testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; var compositeSource = this.packageManager.CreateCompositePackageCatalog(options); var searchResult = this.FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); if (searchResult.CatalogPackage.InstalledVersion != null) { var uninstallOptions = this.TestFactory.CreateUninstallOptions(); var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, uninstallOptions); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); } // Remove default install location if (Directory.Exists(this.defaultInstallDir)) { Directory.Delete(this.defaultInstallDir, true); } } /// /// Tests arp entries match. /// /// A representing the asynchronous operation. [Test] public async Task CheckInstalledStatusArpVersionMatched() { // Find and install the test package. var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.ReplacementInstallerArguments = $"/InstallDir {this.installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus /Version 1.0"; var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Add the data file listed in the manifest File.WriteAllText(Path.Combine(this.installDir, "data.txt"), "Test"); // Search from composite source again after installation var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); options.Catalogs.Add(this.testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; var compositeSource = this.packageManager.CreateCompositePackageCatalog(options); searchResult = this.FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); // Check installed status var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(); Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); // Installer info var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(Windows.System.ProcessorArchitecture.Neutral, installerInstalledStatus.InstallerInfo.Architecture); Assert.AreEqual(PackageInstallerType.Exe, installerInstalledStatus.InstallerInfo.InstallerType); Assert.AreEqual(PackageInstallerType.Unknown, installerInstalledStatus.InstallerInfo.NestedInstallerType); Assert.AreEqual(PackageInstallerScope.Unknown, installerInstalledStatus.InstallerInfo.Scope); // Installer status Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(this.installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(this.installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(this.installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[4].Type); Assert.AreEqual(this.defaultInstallDir, Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[4].Path)); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[4])); } /// /// Test arp entries no version matched. /// /// A representing the asynchronous operation. [Test] public async Task CheckInstalledStatusArpVersionNotMatched() { // Find and install the test package. var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.ReplacementInstallerArguments = $"/InstallDir {this.installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus /Version 2.0"; var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Add the data file listed in the manifest File.WriteAllText(Path.Combine(this.installDir, "data.txt"), "Test"); // Search from composite source again after installation var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); options.Catalogs.Add(this.testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; var compositeSource = this.packageManager.CreateCompositePackageCatalog(options); searchResult = this.FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); // Check installed status var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(InstalledStatusType.AllAppsAndFeaturesEntryChecks); Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(this.installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(this.installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(this.installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); } /// /// Test arp entries file not found. /// /// A representing the asynchronous operation. [Test] public async Task CheckInstalledStatusArpVersionMatchedFileNotFound() { // Find and install the test package. var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.ReplacementInstallerArguments = $"/InstallDir {this.installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus /Version 1.0"; var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Search from composite source again after installation var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); options.Catalogs.Add(this.testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; var compositeSource = this.packageManager.CreateCompositePackageCatalog(options); searchResult = this.FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); // Check installed status var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(InstalledStatusType.AllAppsAndFeaturesEntryChecks); Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(this.installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(this.installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(this.installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); } /// /// Test arp entries hash mismatch. /// /// A representing the asynchronous operation. [Test] public async Task CheckInstalledStatusArpVersionMatchedFileHashMisMatch() { // Find and install the test package. var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.ReplacementInstallerArguments = $"/InstallDir {this.installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus /Version 1.0"; var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Add the data file listed in the manifest File.WriteAllText(Path.Combine(this.installDir, "data.txt"), "WrongData"); // Search from composite source again after installation var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); options.Catalogs.Add(this.testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; var compositeSource = this.packageManager.CreateCompositePackageCatalog(options); searchResult = this.FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); // Check installed status var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(InstalledStatusType.AllAppsAndFeaturesEntryChecks); Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(this.installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(this.installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_HASH_MISMATCH, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(this.installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); } /// /// Test arp entries default install location not found. /// /// A representing the asynchronous operation. [Test] public async Task CheckInstalledStatusArpNotFoundDefaultInstallLocationFound() { // Add the data file listed in the manifest to default install location. Directory.CreateDirectory(this.defaultInstallDir); File.WriteAllText(Path.Combine(this.defaultInstallDir, "data.txt"), "Test"); // Search from composite source without installation var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); options.Catalogs.Add(this.testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; var compositeSource = this.packageManager.CreateCompositePackageCatalog(options); var searchResult = this.FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); // Check installed status var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(); Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(this.defaultInstallDir, Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[1].Path)); Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.DefaultInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(this.defaultInstallDir, "data.txt"), Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[2].Path)); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.DefaultInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(this.defaultInstallDir, "TestExeInstalled.txt"), Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[3].Path)); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); } // CsWinrt maps success error codes(e.g. INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE) to null exception. // In this case we cannot get the exact hresult by calling winrt projection api. // This method is created to directly get hresult from the InstalledStatus object for the tests to compare. private static unsafe int GetHResultFromInstalledStatus(InstalledStatus status) { if (status.Status != null) { return status.Status.HResult; } else { IObjectReference objRef = ((IWinRTObject)status).NativeObject; ABI.System.Exception exception = default; (*(delegate* unmanaged[Stdcall] **)objRef.ThisPtr)[8](objRef.ThisPtr, out exception); return exception.hr; } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/DownloadInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.IO; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; using Windows.System; /// /// Download interop. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class DownloadInterop : BaseInterop { private PackageManager packageManager; private PackageCatalogReference testSource; /// /// Initializes a new instance of the class. /// /// Initializer. public DownloadInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Set up. /// [SetUp] public void SetUp() { this.packageManager = this.TestFactory.CreatePackageManager(); this.testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); } /// /// Downloads the test installer and its package dependencies. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadDependencies() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.PackageDependency"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); var dependenciesDir = Path.Combine(downloadDir, Constants.Dependencies); TestCommon.AssertInstallerDownload(dependenciesDir, "TestPortableExe", "3.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Portable, "en-US"); TestCommon.AssertInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } /// /// Downloads the test installer and skips dependencies. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadDependencies_Skip() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.PackageDependency"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; downloadOptions.SkipDependencies = true; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); var dependenciesDir = Path.Combine(downloadDir, Constants.Dependencies); Assert.IsFalse(Directory.Exists(dependenciesDir)); TestCommon.AssertInstallerDownload(downloadDir, "TestPackageDependency", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "en-US"); } /// /// Download the installer to the default directory. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadToDefaultDirectory() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstaller"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert var packageVersion = "2.0.0.0"; Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); string downloadDir = Path.Combine(TestCommon.GetDefaultDownloadDirectory(), $"{Constants.ExeInstallerPackageId}_{packageVersion}"); TestCommon.AssertInstallerDownload(downloadDir, "TestExeInstaller", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.User, PackageInstallerType.Exe); } /// /// Download the installer to a specified directory. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadToDirectory() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstaller"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); TestCommon.AssertInstallerDownload(downloadDir, "TestExeInstaller", "2.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.User, PackageInstallerType.Exe); } /// /// Download the installer using the user scope argument. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadWithUserScope() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestMultipleInstallers"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; downloadOptions.Scope = PackageInstallScope.User; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.User, PackageInstallerType.Nullsoft, "en-US"); } /// /// Download the installer using the machine scope argument. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadWithMachineScope() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestMultipleInstallers"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; downloadOptions.Scope = PackageInstallScope.System; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// /// Download the test installer using the 'zip' installer type argument. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadWithZipInstallerTypeArg() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestMultipleInstallers"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; downloadOptions.InstallerType = PackageInstallerType.Zip; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true); } /// /// Downloads the test installer using the installer type argument. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadWithInstallerTypeArg() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestMultipleInstallers"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; downloadOptions.InstallerType = PackageInstallerType.Msi; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// /// Downloads the test installer using the architecture argument. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadWithArchitectureArg() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestMultipleInstallers"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; downloadOptions.Architecture = ProcessorArchitecture.X86; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X86, TestCommon.Scope.Machine, PackageInstallerType.Msi, "en-US"); } /// /// Downloads the test installer using the locale argument. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadWithLocaleArg() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestMultipleInstallers"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; downloadOptions.Locale = "zh-CN"; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); TestCommon.AssertInstallerDownload(downloadDir, "TestMultipleInstallers", "1.0.0.0", ProcessorArchitecture.X64, TestCommon.Scope.Unknown, PackageInstallerType.Exe, "zh-CN", true); } /// /// Downloads the test installer with a hash mismatch. /// /// A representing the asynchronous unit test. [Test] public async Task DownloadWithHashMismatch() { // Find package var downloadDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeSha256Mismatch"); // Configure installation var downloadOptions = this.TestFactory.CreateDownloadOptions(); downloadOptions.AcceptPackageAgreements = true; downloadOptions.DownloadDirectory = downloadDir; // Download var downloadResult = await this.packageManager.DownloadPackageAsync(searchResult.CatalogPackage, downloadOptions); // Assert Assert.AreEqual(DownloadResultStatus.DownloadError, downloadResult.Status); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/FindPackagesInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; /// /// Test find package interop. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class FindPackagesInterop : BaseInterop { private PackageManager packageManager; private PackageCatalogReference testSource; /// /// Initializes a new instance of the class. /// /// Initializer. public FindPackagesInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Set up. /// [SetUp] public void SetUp() { this.packageManager = this.TestFactory.CreatePackageManager(); this.testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); } /// /// Test find package. no package. /// [Test] public void FindPackageDoesNotExist() { // Find package var searchResult = this.FindAllPackages(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "DoesNotExist"); // Assert Assert.AreEqual(0, searchResult.Count); } /// /// Test find package with multiple match. /// [Test] public void FindPackagesMultipleMatchingQuery() { // Find package var searchResult = this.FindAllPackages(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestExeInstaller"); // Assert Assert.AreEqual(2, searchResult.Count); } /// /// Test to find a package and verify the CatalogPackageMetadata COM output. /// [Test] public void FindPackagesVerifyDefaultLocaleFields() { var searchResult = this.FindAllPackages(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.CatalogPackageMetadata"); Assert.AreEqual(1, searchResult.Count); var catalogPackage = searchResult[0].CatalogPackage; var packageVersionId = catalogPackage.AvailableVersions[0]; var packageVersionInfo = catalogPackage.GetPackageVersionInfo(packageVersionId); var catalogPackageMetadata = packageVersionInfo.GetCatalogPackageMetadata(); Assert.AreEqual("testAuthor", catalogPackageMetadata.Author); Assert.AreEqual("AppInstallerTest", catalogPackageMetadata.Publisher); Assert.AreEqual("https://testPublisherUrl.com", catalogPackageMetadata.PublisherUrl); Assert.AreEqual("https://testPublisherSupportUrl.com", catalogPackageMetadata.PublisherSupportUrl); Assert.AreEqual("https://testPrivacyUrl.com", catalogPackageMetadata.PrivacyUrl); Assert.AreEqual("https://testPackageUrl.com", catalogPackageMetadata.PackageUrl); Assert.AreEqual("testLicense", catalogPackageMetadata.License); Assert.AreEqual("https://testLicenseUrl.com", catalogPackageMetadata.LicenseUrl); Assert.AreEqual("testCopyright", catalogPackageMetadata.Copyright); Assert.AreEqual("https://testCopyrightUrl.com", catalogPackageMetadata.CopyrightUrl); Assert.AreEqual("testDescription", catalogPackageMetadata.Description); Assert.AreEqual("testShortDescription", catalogPackageMetadata.ShortDescription); Assert.AreEqual("https://testPurchaseUrl.com", catalogPackageMetadata.PurchaseUrl); var tags = catalogPackageMetadata.Tags; Assert.AreEqual(2, tags.Count); Assert.AreEqual("tag1", tags[0]); Assert.AreEqual("tag2", tags[1]); Assert.AreEqual("testReleaseNotes", catalogPackageMetadata.ReleaseNotes); Assert.AreEqual("https://testReleaseNotes.net", catalogPackageMetadata.ReleaseNotesUrl); Assert.AreEqual("testInstallationNotes", catalogPackageMetadata.InstallationNotes); var packageAgreements = catalogPackageMetadata.Agreements; Assert.AreEqual(1, packageAgreements.Count); var agreement = packageAgreements[0]; Assert.AreEqual("testAgreementLabel", agreement.Label); Assert.AreEqual("testAgreementText", agreement.Text); Assert.AreEqual("https://testAgreementUrl.net", agreement.Url); var documentations = catalogPackageMetadata.Documentations; Assert.AreEqual(1, documentations.Count); var documentation = documentations[0]; Assert.AreEqual("testDocumentLabel", documentation.DocumentLabel); Assert.AreEqual("https://testDocumentUrl.com", documentation.DocumentUrl); var icons = catalogPackageMetadata.Icons; Assert.AreEqual(1, icons.Count); var icon = icons[0]; Assert.AreEqual("https://testIcon", icon.Url); Assert.AreEqual(IconFileType.Ico, icon.FileType); Assert.AreEqual(IconTheme.Default, icon.Theme); Assert.AreEqual(IconResolution.Custom, icon.Resolution); Assert.AreEqual(Convert.FromHexString("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123"), icon.Sha256); } /// /// Verifies that an exception is thrown if the provided locale string is invalid. /// [Test] public void FindPackagesInvalidLocale() { var searchResult = this.FindAllPackages(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.CatalogPackageMetadata"); var catalogPackage = searchResult[0].CatalogPackage; var packageVersionId = catalogPackage.AvailableVersions[0]; var packageVersionInfo = catalogPackage.GetPackageVersionInfo(packageVersionId); Assert.Throws(() => packageVersionInfo.GetCatalogPackageMetadata("badLocale")); } /// /// Verifies that the correct CatalogPackageMetadata is exposed when specifying a locale. /// [Test] public void FindPackagesGetCatalogPackageMetadataLocale() { var searchResult = this.FindAllPackages(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.MultipleLocale"); Assert.AreEqual(1, searchResult.Count); var catalogPackage = searchResult[0].CatalogPackage; var packageVersionId = catalogPackage.AvailableVersions[0]; var packageVersionInfo = catalogPackage.GetPackageVersionInfo(packageVersionId); var catalogPackageMetadata = packageVersionInfo.GetCatalogPackageMetadata("zh-CN"); Assert.AreEqual("zh-CN", catalogPackageMetadata.Locale); Assert.AreEqual("localeLicense", catalogPackageMetadata.License); Assert.AreEqual("localePackageName", catalogPackageMetadata.PackageName); Assert.AreEqual("localePublisher", catalogPackageMetadata.Publisher); Assert.AreEqual("localeReleaseNotes", catalogPackageMetadata.ReleaseNotes); Assert.AreEqual("https://localeReleaseNotesUrl.com", catalogPackageMetadata.ReleaseNotesUrl); Assert.AreEqual("https://localePurchaseUrl.com", catalogPackageMetadata.PurchaseUrl); var tags = catalogPackageMetadata.Tags; Assert.AreEqual(2, tags.Count); Assert.AreEqual("tag1", tags[0]); Assert.AreEqual("tag2", tags[1]); var packageAgreements = catalogPackageMetadata.Agreements; Assert.AreEqual(1, packageAgreements.Count); var agreement = packageAgreements[0]; Assert.AreEqual("localeAgreementLabel", agreement.Label); Assert.AreEqual("localeAgreement", agreement.Text); Assert.AreEqual("https://localeAgreementUrl.net", agreement.Url); var documentations = catalogPackageMetadata.Documentations; Assert.AreEqual(1, documentations.Count); var documentation = documentations[0]; Assert.AreEqual("localeDocumentLabel", documentation.DocumentLabel); Assert.AreEqual("https://localeDocumentUrl.com", documentation.DocumentUrl); var icons = catalogPackageMetadata.Icons; Assert.AreEqual(1, icons.Count); var icon = icons[0]; Assert.AreEqual("https://localeTestIcon", icon.Url); Assert.AreEqual(IconFileType.Png, icon.FileType); Assert.AreEqual(IconTheme.Light, icon.Theme); Assert.AreEqual(IconResolution.Square32, icon.Resolution); Assert.AreEqual(Convert.FromHexString("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321"), icon.Sha256); } /// /// Verifies that GetCatalogPackageMetadata returns the correct metadata based on the specified locale. /// [Test] public void FindPackagesGetAllCatalogPackageMetadata() { var searchResult = this.FindAllPackages(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.MultipleLocale"); Assert.AreEqual(1, searchResult.Count); var catalogPackage = searchResult[0].CatalogPackage; var packageVersionId = catalogPackage.AvailableVersions[0]; var packageVersionInfo = catalogPackage.GetPackageVersionInfo(packageVersionId); var catalogPackageMetadata1 = packageVersionInfo.GetCatalogPackageMetadata("zh-CN"); Assert.AreEqual("zh-CN", catalogPackageMetadata1.Locale); var catalogPackageMetadata2 = packageVersionInfo.GetCatalogPackageMetadata("en-GB"); Assert.AreEqual("en-GB", catalogPackageMetadata2.Locale); Assert.AreEqual("packageNameUK", catalogPackageMetadata2.PackageName); } /// /// Verifies that GetCatalogPackageMetadata returns the correct metadata based on the specified locale. /// [Test] public void FindPackagesGetVersionMetadata() { var searchResult = this.FindAllPackages(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.MultipleLocale"); Assert.AreEqual(1, searchResult.Count); var catalogPackage = searchResult[0].CatalogPackage; var packageVersionId = catalogPackage.AvailableVersions[0]; var packageVersionInfo = catalogPackage.GetPackageVersionInfo(packageVersionId); string metadata = packageVersionInfo.GetMetadata(PackageVersionMetadataField.SilentUninstallCommand); Assert.IsEmpty(metadata); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/GroupPolicyForInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using Microsoft.WinGet.SharedLib.Exceptions; using NUnit.Framework; using Windows.System; /// /// Group Policy Tests for COM/WinRT Interop classes. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class GroupPolicyForInterop : BaseInterop { /// /// Initializes a new instance of the class. /// /// Initializer. public GroupPolicyForInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Test setup. /// [SetUp] public void SetUp() { GroupPolicyHelper.DeleteExistingPolicies(); } /// /// Clean up. /// [TearDown] public void CleanUp() { GroupPolicyHelper.DeleteExistingPolicies(); } /// /// Test class Tear down. /// [OneTimeTearDown] public void TestClassTearDown() { // Restore the tests source if it was removed as the affects subsequent tests. TestCommon.SetupTestSource(); } /// /// Validates disabling WinGetPolicy should block COM/WinRT Objects creation (InProcess and OutOfProcess). /// [Test] public void DisableWinGetPolicy() { GroupPolicyHelper.EnableWinget.Disable(); GroupPolicyException groupPolicyException = Assert.Catch(() => { PackageManager packageManager = this.TestFactory.CreatePackageManager(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { FindPackagesOptions findPackagesOptions = this.TestFactory.CreateFindPackagesOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { CreateCompositePackageCatalogOptions createCompositePackageCatalogOptions = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { InstallOptions installOptions = this.TestFactory.CreateInstallOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { UninstallOptions uninstallOptions = this.TestFactory.CreateUninstallOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { DownloadOptions downloadOptions = this.TestFactory.CreateDownloadOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { PackageMatchFilter packageMatchFilter = this.TestFactory.CreatePackageMatchFilter(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { RepairOptions repairOptions = this.TestFactory.CreateRepairOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { AddPackageCatalogOptions packageManagerSettings = this.TestFactory.CreateAddPackageCatalogOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { RemovePackageCatalogOptions packageManagerSettings = this.TestFactory.CreateRemovePackageCatalogOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); groupPolicyException = Assert.Catch(() => { EditPackageCatalogOptions packageManagerSettings = this.TestFactory.CreateEditPackageCatalogOptions(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); // PackageManagerSettings is not implemented in context OutOfProcDev if (this.TestFactory.Context == ClsidContext.InProc) { groupPolicyException = Assert.Catch(() => { PackageManagerSettings packageManagerSettings = this.TestFactory.CreatePackageManagerSettings(); }); Assert.AreEqual(Constants.BlockByWinGetPolicyErrorMessage, groupPolicyException.Message); Assert.AreEqual(Constants.ErrorCode.ERROR_BLOCKED_BY_POLICY, groupPolicyException.HResult); } } /// /// Validates disabling the EnableWindowsPackageManagerCommandLineInterfaces policy should still allow COM calls. /// /// A representing the asynchronous unit test. [Test] public async Task DisableWinGetCommandLineInterfacesPolicy() { GroupPolicyHelper.EnableWinGetCommandLineInterfaces.Disable(); PackageManager packageManager = this.TestFactory.CreatePackageManager(); string installDir = TestCommon.GetRandomTestDir(); // Create composite package catalog source var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); var testSource = packageManager.GetPackageCatalogByName(Constants.TestSourceName); Assert.NotNull(testSource, $"{Constants.TestSourceName} cannot be null"); options.Catalogs.Add(testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; PackageCatalogReference compositeSource = packageManager.CreateCompositePackageCatalog(options); // Find package var searchResult = this.FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.ModifyRepairInstaller); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.AcceptPackageAgreements = true; installOptions.ReplacementInstallerArguments = $"/InstallDir {installDir} /Version 2.0.0.0 /DisplayName TestModifyRepair /UseHKLM"; // Install var installResult = await packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.ModifyRepairInstaller); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Repair var repairOptions = this.TestFactory.CreateRepairOptions(); repairOptions.PackageRepairMode = PackageRepairMode.Silent; var repairResult = await packageManager.RepairPackageAsync(searchResult.CatalogPackage, repairOptions); Assert.AreEqual(RepairResultStatus.Ok, repairResult.Status); // Uninstall var uninstallResult = await packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); Assert.True(TestCommon.VerifyTestExeUninstalled(installDir)); // Clean up. if (Directory.Exists(installDir)) { Directory.Delete(installDir, true); } // Download var downloadResult = await packageManager.DownloadPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateDownloadOptions()); Assert.AreEqual(DownloadResultStatus.Ok, downloadResult.Status); var packageVersion = "2.0.0.0"; string downloadDir = Path.Combine(TestCommon.GetDefaultDownloadDirectory(), $"{Constants.ModifyRepairInstaller}_{packageVersion}"); TestCommon.AssertInstallerDownload(downloadDir, "TestModifyRepair", packageVersion, ProcessorArchitecture.X86, TestCommon.Scope.Unknown, PackageInstallerType.Burn, "en-US"); // Add, update and remove package catalog await this.AddUpdateRemovePackageCatalog(); } private async Task AddUpdateRemovePackageCatalog() { // Remove the tests source if it exists. await this.RemovePackageCatalog(); PackageManager packageManager = this.TestFactory.CreatePackageManager(); // Add package catalog AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = Constants.TestSourceUrl; options.Name = Constants.TestSourceName; options.TrustLevel = PackageCatalogTrustLevel.Trusted; var addCatalogResult = await packageManager.AddPackageCatalogAsync(options); Assert.IsNotNull(addCatalogResult); Assert.AreEqual(AddPackageCatalogStatus.Ok, addCatalogResult.Status); // Get package catalog var packageCatalog = packageManager.GetPackageCatalogByName(options.Name); Assert.IsNotNull(packageCatalog); Assert.AreEqual(options.Name, packageCatalog.Info.Name); Assert.AreEqual(options.SourceUri, packageCatalog.Info.Argument); var lastUpdatedTime = packageCatalog.Info.LastUpdateTime; // Update package catalog // Sleep for 30 seconds to make sure the last updated time is different after the refresh. Thread.Sleep(TimeSpan.FromSeconds(30)); var updateResult = await packageCatalog.RefreshPackageCatalogAsync(); Assert.IsNotNull(updateResult); Assert.AreEqual(RefreshPackageCatalogStatus.Ok, updateResult.Status); packageCatalog = packageManager.GetPackageCatalogByName(options.Name); Assert.IsTrue(packageCatalog.Info.LastUpdateTime > lastUpdatedTime); // Remove package catalog await this.RemovePackageCatalog(); } private async Task RemovePackageCatalog() { PackageManager packageManager = this.TestFactory.CreatePackageManager(); // Remove the tests source if it exists. RemovePackageCatalogOptions removePackageCatalogOptions = this.TestFactory.CreateRemovePackageCatalogOptions(); removePackageCatalogOptions.Name = Constants.TestSourceName; var removeCatalogResult = await packageManager.RemovePackageCatalogAsync(removePackageCatalogOptions); Assert.IsNotNull(removeCatalogResult); Assert.AreEqual(RemovePackageCatalogStatus.Ok, removeCatalogResult.Status); var packageCatalog = packageManager.GetPackageCatalogByName(removePackageCatalogOptions.Name); Assert.IsNull(packageCatalog); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/InstallInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.IO; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; /// /// Install interop. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class InstallInterop : BaseInterop { private string installDir; private PackageManager packageManager; private PackageCatalogReference testSource; /// /// Initializes a new instance of the class. /// /// Initializer. public InstallInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Set up. /// [SetUp] public void SetUp() { this.packageManager = this.TestFactory.CreatePackageManager(); this.testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); this.installDir = TestCommon.GetRandomTestDir(); } /// /// Install exe. /// /// A representing the asynchronous unit test. [Test] public async Task InstallExe() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; installOptions.AcceptPackageAgreements = true; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(this.installDir)); } /// /// Test install with inapplicable os version. /// /// A representing the asynchronous unit test. [Test] public async Task InstallExeWithInsufficientMinOsVersion() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "InapplicableOsVersion"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.NoApplicableInstallers, installResult.Status); Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(this.installDir)); } /// /// Test install with hash mismatch. /// /// A representing the asynchronous unit test. [Test] public async Task InstallExeWithHashMismatch() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestExeSha256Mismatch"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.DownloadError, installResult.Status); Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(this.installDir)); } /// /// Test installing inno installer. /// /// A representing the asynchronous unit test. [Test] public async Task InstallWithInno() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestInnoInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(this.installDir)); } /// /// Test installing burn installer. /// /// A representing the asynchronous unit test. [Test] public async Task InstallBurn() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestBurnInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(this.installDir)); } /// /// Test installing nullsoft installer. /// /// A representing the asynchronous unit test. [Test] public async Task InstallNullSoft() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestNullsoftInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(this.installDir)); } /// /// Test installing msi. /// /// A representing the asynchronous unit test. [Test] public async Task InstallMSI() { if (string.IsNullOrEmpty(TestIndex.MsiInstaller)) { Assert.Ignore("MSI installer not available"); } // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestMsiInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(this.installDir)); } /// /// Test installing an msix. /// /// A representing the asynchronous unit test. [Test] public async Task InstallMSIX() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestMsixInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test installing msix with machine scope. /// /// A representing the asynchronous unit test. [Test] public async Task InstallMSIXMachineScope() { // TODO: Provision and Deprovision api not supported in build server. Assert.Ignore(); // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestMsixInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallScope = PackageInstallScope.System; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup(true)); } /// /// Test installing msix with signature. /// /// A representing the asynchronous unit test. [Test] public async Task InstallMSIXWithSignature() { // Task to investigate installation error // TODO: https://task.ms/40489822 Assert.Ignore(); // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestMsixWithSignatureHash"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test installing msix with signature machine scope. /// /// A representing the asynchronous unit test. [Test] public async Task InstallMSIXWithSignatureMachineScope() { // TODO: Provision and Deprovision api not supported in build server. Assert.Ignore(); // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestMsixWithSignatureHash"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallScope = PackageInstallScope.System; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup(true)); } /// /// Test installing msix with signature hash mismatch. /// /// A representing the asynchronous unit test. [Test] public async Task InstallMSIXWithSignatureHashMismatch() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestMsixSignatureHashMismatch"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.DownloadError, installResult.Status); Assert.False(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test installing exe. /// [Test] public void InstallExeWithAlternateSourceFailure() { // Add mock source TestCommon.RunAICLICommand("source add", "failSearch \"{ \"\"SearchHR\"\": \"\"0x80070002\"\" }\" Microsoft.Test.Configurable --header \"{}\""); // Get mock source var failSearchSource = this.packageManager.GetPackageCatalogByName("failSearch"); // Find package var searchResult = this.FindAllPackages(failSearchSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstaller"); // Assert Assert.NotNull(failSearchSource); Assert.AreEqual(0, searchResult.Count); // Remove mock source TestCommon.RunAICLICommand("source remove", "failSearch"); } /// /// Test installing portable exe. /// /// A representing the asynchronous unit test. [Test] public async Task InstallPortableExe() { string installDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Packages"); string productCode = Constants.PortableExePackageDirName; string commandAlias = $"{Constants.ExeInstaller}.exe"; string fileName = $"{Constants.ExeInstaller}.exe"; // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.PortableExePackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); TestCommon.VerifyPortablePackage(Path.Combine(installDir, Constants.PortableExePackageDirName), commandAlias, fileName, productCode, true); } /// /// Test installing portable exe with command. /// /// A representing the asynchronous unit test. [Test] public async Task InstallPortableExeWithCommand() { string productCode = Constants.PortableExeWithCommandPackageDirName; string fileName = Constants.AppInstallerTestExeInstallerExe; string commandAlias = Constants.TestCommandExe; // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.PortableExeWithCommandPackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); TestCommon.VerifyPortablePackage(this.installDir, commandAlias, fileName, productCode, true); } /// /// Test installing portable package to existing directory. /// /// A representing the asynchronous unit test. [Test] public async Task InstallPortableToExistingDirectory() { var existingDir = Path.Combine(this.installDir, "testDirectory"); Directory.CreateDirectory(existingDir); string productCode = Constants.PortableExePackageDirName; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string fileName = Constants.AppInstallerTestExeInstallerExe; // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.PortableExePackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PreferredInstallLocation = existingDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); TestCommon.VerifyPortablePackage(existingDir, commandAlias, fileName, productCode, true); } /// /// Test installing portable package where it fails on clean up. /// /// A representing the asynchronous unit test. [Test] public async Task InstallPortableFailsWithCleanup() { if (this.TestFactory.Context == ClsidContext.InProc) { // Task to investigate validation error when running in-process // TODO: https://task.ms/40489822 Assert.Ignore(); } string winGetDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet"); string installDir = Path.Combine(winGetDir, "Packages"); string symlinkDirectory = Path.Combine(winGetDir, "Links"); string packageDirName = Constants.PortableExePackageDirName; string productCode = Constants.PortableExePackageDirName; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string fileName = Constants.AppInstallerTestExeInstallerExe; string conflictDirectory = Path.Combine(symlinkDirectory, commandAlias); // Create a directory with the same name as the symlink in order to cause install to fail. Directory.CreateDirectory(conflictDirectory); // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.PortableExePackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.InstallError, installResult.Status); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, false); Directory.Delete(conflictDirectory, true); } /// /// Test installing a package with user scope. /// /// A representing the asynchronous unit test. [Test] public async Task InstallRequireUserScope() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstallerNoScope"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; installOptions.PackageInstallScope = PackageInstallScope.User; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.NoApplicableInstallers, installResult.Status); } /// /// Test installing package with user scope or unknown. /// /// A representing the asynchronous unit test. [Test] public async Task InstallRequireUserScopeAndUnknown() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstallerNoScope"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; installOptions.PackageInstallScope = PackageInstallScope.UserOrUnknown; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); } /// /// Test installing package with agreements and accepting those agreements. /// /// A representing the asynchronous unit test. [Test] public async Task InstallWithAgreementsAccepted() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.CatalogPackageMetadata"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; installOptions.AcceptPackageAgreements = true; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); } /// /// Test installing package with agreements and not accepting those agreements. /// /// A representing the asynchronous unit test. [Test] public async Task InstallWithAgreementsNotAccepted() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.CatalogPackageMetadata"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; installOptions.AcceptPackageAgreements = false; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.PackageAgreementsNotAccepted, installResult.Status); } /// /// Test installing a package with a package dependency and passing in the 'skip-dependencies' install option. /// /// A representing the asynchronous unit test. [Test] public async Task InstallWithSkipDependencies() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.PackageDependency"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; installOptions.AcceptPackageAgreements = true; installOptions.SkipDependencies = true; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert that only the exe installer is installed and not the portable package dependency. Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(this.installDir)); string installDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Packages"); string productCode = Constants.PortableExePackageDirName; string commandAlias = $"{Constants.ExeInstaller}.exe"; string fileName = $"{Constants.ExeInstaller}.exe"; TestCommon.VerifyPortablePackage(Path.Combine(installDir, Constants.PortableExePackageDirName), commandAlias, fileName, productCode, false); } /// /// Test installing a package with a specific installer type install option. /// /// A representing the asynchronous unit test. [Test] public async Task InstallWithInstallerType() { // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestMultipleInstallers"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; installOptions.InstallerType = PackageInstallerType.Msi; installOptions.AcceptPackageAgreements = true; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(this.installDir)); } /// /// Test to verify the GetApplicableInstaller() COM call returns the correct manifest installer metadata. /// [Test] public void GetApplicableInstaller() { // Find package var searchResult = this.FindAllPackages(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.PackageInstallerInfo"); Assert.AreEqual(1, searchResult.Count); // Configure installation var catalogPackage = searchResult[0].CatalogPackage; var packageVersionId = catalogPackage.AvailableVersions[0]; var packageVersionInfo = catalogPackage.GetPackageVersionInfo(packageVersionId); // Use install options with no applicable match. var badInstallOptions = this.TestFactory.CreateInstallOptions(); badInstallOptions.PackageInstallScope = PackageInstallScope.System; Assert.IsNull(packageVersionInfo.GetApplicableInstaller(badInstallOptions)); // Use install options with valid applicable match. var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallScope = PackageInstallScope.User; var packageInstallerInfo = packageVersionInfo.GetApplicableInstaller(installOptions); // Assert Assert.IsNotNull(packageInstallerInfo); Assert.AreEqual(ElevationRequirement.ElevationRequired, packageInstallerInfo.ElevationRequirement); Assert.AreEqual(Windows.System.ProcessorArchitecture.X64, packageInstallerInfo.Architecture); Assert.AreEqual(PackageInstallerType.Zip, packageInstallerInfo.InstallerType); Assert.AreEqual(PackageInstallerType.Exe, packageInstallerInfo.NestedInstallerType); Assert.AreEqual(PackageInstallerScope.User, packageInstallerInfo.Scope); Assert.AreEqual("en-US", packageInstallerInfo.Locale); } /// /// Install exe and verify that we can find it as installed after. /// /// A representing the asynchronous unit test. [Test] public async Task InstallExe_VerifyInstalledCatalog() { var installedCatalogReference = this.packageManager.GetLocalPackageCatalog(LocalPackageCatalog.InstalledPackages); // Ensure package is not installed var installedResult = this.FindAllPackages(installedCatalogReference, PackageMatchField.ProductCode, PackageFieldMatchOption.Equals, Constants.ExeInstalledDefaultProductCode); Assert.IsNotNull(installedResult); Assert.AreEqual(0, installedResult.Count); // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; installOptions.AcceptPackageAgreements = true; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Check installed catalog after this.FindOnePackage(installedCatalogReference, PackageMatchField.ProductCode, PackageFieldMatchOption.Equals, Constants.ExeInstalledDefaultProductCode); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(this.installDir)); } /// /// Install msix and verify that we can find it as installed after. /// /// A representing the asynchronous unit test. [Test] public async Task InstallMSIX_VerifyInstalledCatalog() { var installedCatalogReference = this.packageManager.GetLocalPackageCatalog(LocalPackageCatalog.InstalledPackages); // Ensure package is not installed var installedResult = this.FindAllPackages(installedCatalogReference, PackageMatchField.PackageFamilyName, PackageFieldMatchOption.Equals, Constants.MsixInstallerPackageFamilyName); Assert.IsNotNull(installedResult); Assert.AreEqual(0, installedResult.Count); // Find package var searchResult = this.FindOnePackage(this.testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "TestMsixInstaller"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // Assert Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Check installed catalog after this.FindOnePackage(installedCatalogReference, PackageMatchField.PackageFamilyName, PackageFieldMatchOption.Equals, Constants.MsixInstallerPackageFamilyName); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/InstanceInitializersSource.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using Microsoft.Management.Deployment.Projection; /// /// Source class for running tests in-process and out-of-process. /// public class InstanceInitializersSource { /// /// List of in-process instance initializers passed as argument to the test class constructor. /// public static readonly IInstanceInitializer[] InProcess = { new ActivationFactoryInstanceInitializer(), }; /// /// List of out-of-process instance initializers passed as argument to the test class constructor. /// public static readonly IInstanceInitializer[] OutOfProcess = { new LocalServerInstanceInitializer() { AllowLowerTrustRegistration = true, UseDevClsids = true, }, }; } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/InteropSetUpFixture.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Interop set up fixture. /// [SetUpFixture] public class InteropSetUpFixture { /// /// One time set up. /// [OneTimeSetUp] public void Setup() { TestCommon.SetupTestSource(); } /// /// Tear down. /// [OneTimeTearDown] public void TearDown() { try { GC.Collect(); GC.WaitForPendingFinalizers(); TestCommon.TearDownTestSource(); } catch { // If the COM objects were not yet disposed and lock acquired // when connecting to test source was not released, then just // exit process since all tests have already executed. } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/PackageCatalogInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.IO; using System.Threading; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.CodeAnalysis; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; using Windows.System; /// /// Package catalog interop class. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class PackageCatalogInterop : BaseInterop { private PackageManager packageManager; /// /// Initializes a new instance of the class. /// /// initializer. public PackageCatalogInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Set up. /// [SetUp] public void Setup() { // Remove the tests source if it exists. TestCommon.RunAICLICommand("source remove", Constants.TestSourceName); this.packageManager = this.TestFactory.CreatePackageManager(); } /// /// Add and remove package catalog. /// /// representing the asynchronous unit test. [Test] public async Task AddRemovePackageCatalog() { AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = Constants.TestSourceUrl; options.Name = Constants.TestSourceName; options.TrustLevel = PackageCatalogTrustLevel.Trusted; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.Ok); RemovePackageCatalogOptions removePackageCatalogOptions = this.TestFactory.CreateRemovePackageCatalogOptions(); removePackageCatalogOptions.Name = Constants.TestSourceName; var removeCatalogResult = await this.packageManager.RemovePackageCatalogAsync(removePackageCatalogOptions); Assert.IsNotNull(removeCatalogResult); Assert.AreEqual(RemovePackageCatalogStatus.Ok, removeCatalogResult.Status); var testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); Assert.IsNull(testSource); } /// /// Add package catalog with invalid options. /// [Test] public void AddPackageCatalogWithInvalidOptions() { // Add package catalog with null options. Assert.ThrowsAsync(async () => await this.packageManager.AddPackageCatalogAsync(null)); // Add package catalog with empty options. Assert.ThrowsAsync(async () => await this.packageManager.AddPackageCatalogAsync(this.TestFactory.CreateAddPackageCatalogOptions())); } /// /// Add package catalog with a duplicate name and verify it returns SourceNameExists. /// /// representing the asynchronous unit test. [Test] public async Task AddPackageCatalog_DuplicateName_ReturnsSourceNameExists() { AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = Constants.TestSourceUrl; options.Name = Constants.TestSourceName; options.TrustLevel = PackageCatalogTrustLevel.Trusted; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.Ok); // Add the same package catalog again. await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.InvalidOptions, Constants.ErrorCode.ERROR_SOURCE_NAME_ALREADY_EXISTS); // Remove the tests source if it exists. RemovePackageCatalogOptions removePackageCatalogOptions = this.TestFactory.CreateRemovePackageCatalogOptions(); removePackageCatalogOptions.Name = Constants.TestSourceName; await this.RemoveAndValidatePackageCatalogAsync(removePackageCatalogOptions, RemovePackageCatalogStatus.Ok); } /// /// Add package catalog with a duplicate source uri and verify it returns SourceArg AlreadyExists. /// /// representing the asynchronous unit test. [Test] public async Task AddPackageCatalog_DuplicateSourceUri_ReturnSourceArgAlreadyExists() { AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = Constants.TestSourceUrl; options.Name = Constants.TestSourceName; options.TrustLevel = PackageCatalogTrustLevel.Trusted; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.Ok); // Add the same package catalog again. options.Name = "TestSource2"; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.InvalidOptions, Constants.ErrorCode.ERROR_SOURCE_ARG_ALREADY_EXISTS); // Remove the tests source if it exists. RemovePackageCatalogOptions removePackageCatalogOptions = this.TestFactory.CreateRemovePackageCatalogOptions(); removePackageCatalogOptions.Name = Constants.TestSourceName; await this.RemoveAndValidatePackageCatalogAsync(removePackageCatalogOptions, RemovePackageCatalogStatus.Ok); } /// /// Add package catalog with invalid source uri. /// /// representing the asynchronous unit test. [Test] public async Task AddPackageCatalogWithInvalidSourceUri() { AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = "InvalidUri"; options.Name = Constants.TestSourceName; options.TrustLevel = PackageCatalogTrustLevel.Trusted; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.InternalError); } /// /// Add package catalog with insecure source uri. /// /// representing the asynchronous unit test. [Test] public async Task AddPackageCatalogWithHttpSourceUri() { AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = "http://microsoft.com"; options.Name = "Insecure"; options.TrustLevel = PackageCatalogTrustLevel.Trusted; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.InvalidOptions, Constants.ErrorCode.ERROR_SOURCE_NOT_SECURE); } /// /// Add package catalog with invalid type. /// /// representing the asynchronous unit test. [Test] public async Task AddPackageCatalogWithInvalidType() { AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = Constants.TestSourceUrl; options.Name = Constants.TestSourceName; options.Type = "InvalidType"; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.InvalidOptions, Constants.ErrorCode.ERROR_INVALID_SOURCE_TYPE); } /// /// Add, update and remove package catalog. /// /// representing the asynchronous unit test. [Test] public async Task AddUpdateRemovePackageCatalog() { AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = Constants.TestSourceUrl; options.Name = Constants.TestSourceName; options.TrustLevel = PackageCatalogTrustLevel.Trusted; await this.AddCatalogAndVerifyStatusAsync(options, AddPackageCatalogStatus.Ok); var packageCatalog = this.GetAndValidatePackageCatalog(options); var lastUpdatedTime = packageCatalog.Info.LastUpdateTime; // Sleep for 30 seconds to make sure the last updated time is different after the refresh. Thread.Sleep(TimeSpan.FromSeconds(30)); var updateResult = await packageCatalog.RefreshPackageCatalogAsync(); Assert.IsNotNull(updateResult); Assert.AreEqual(RefreshPackageCatalogStatus.Ok, updateResult.Status); packageCatalog = this.GetAndValidatePackageCatalog(options); Assert.IsTrue(packageCatalog.Info.LastUpdateTime > lastUpdatedTime); RemovePackageCatalogOptions removePackageCatalogOptions = this.TestFactory.CreateRemovePackageCatalogOptions(); removePackageCatalogOptions.Name = Constants.TestSourceName; await this.RemoveAndValidatePackageCatalogAsync(removePackageCatalogOptions, RemovePackageCatalogStatus.Ok); } /// /// Add, remove package catalog with PreserveData filed set.. /// /// representing the asynchronous unit test. [Test] public async Task AddRemovePackageCatalogWithPreserveDataFiledSet() { AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = Constants.TestSourceUrl; options.Name = Constants.TestSourceName; options.TrustLevel = PackageCatalogTrustLevel.Trusted; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.Ok); RemovePackageCatalogOptions removePackageCatalogOptions = this.TestFactory.CreateRemovePackageCatalogOptions(); removePackageCatalogOptions.Name = Constants.TestSourceName; removePackageCatalogOptions.PreserveData = true; await this.RemoveAndValidatePackageCatalogAsync(removePackageCatalogOptions, RemovePackageCatalogStatus.Ok); } /// /// Remove package catalog with invalid options. /// [Test] public void RemovePackageCatalogWithInvalidOptions() { // Remove package catalog with null options. Assert.ThrowsAsync(async () => await this.packageManager.RemovePackageCatalogAsync(null)); // Remove package catalog with empty options. Assert.ThrowsAsync(async () => await this.packageManager.RemovePackageCatalogAsync(this.TestFactory.CreateRemovePackageCatalogOptions())); } /// /// Remove a package catalog that is not present. /// /// representing the asynchronous unit test. [Test] public async Task RemoveNonExistingPackageCatalog() { RemovePackageCatalogOptions removePackageCatalogOptions = this.TestFactory.CreateRemovePackageCatalogOptions(); removePackageCatalogOptions.Name = Constants.TestSourceName; await this.RemoveAndValidatePackageCatalogAsync(removePackageCatalogOptions, RemovePackageCatalogStatus.InvalidOptions, Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST); } /// /// Edit package catalog with invalid options. /// [Test] public void EditPackageCatalogWithInvalidOptions() { // Edit package catalog with null options. Assert.Throws(() => this.packageManager.EditPackageCatalog(null)); // Edit package catalog with empty options. Assert.Throws(() => this.packageManager.EditPackageCatalog(this.TestFactory.CreateEditPackageCatalogOptions())); } /// /// Edit a package catalog that is not present. /// [Test] public void EditNonExistingPackageCatalog() { EditPackageCatalogOptions editPackageCatalogOptions = this.TestFactory.CreateEditPackageCatalogOptions(); editPackageCatalogOptions.Name = Constants.TestSourceName; this.EditAndValidatePackageCatalog(editPackageCatalogOptions, EditPackageCatalogStatus.InvalidOptions, Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST); } /// /// Edit a package catalog that is not present. /// /// representing the asynchronous unit test. [Test] public async Task AddEditRemovePackageCatalog() { // Add AddPackageCatalogOptions options = this.TestFactory.CreateAddPackageCatalogOptions(); options.SourceUri = Constants.TestSourceUrl; options.Name = Constants.TestSourceName; options.TrustLevel = PackageCatalogTrustLevel.Trusted; options.Explicit = true; options.Priority = 12; await this.AddAndValidatePackageCatalogAsync(options, AddPackageCatalogStatus.Ok); // Edit EditPackageCatalogOptions editOptions = this.TestFactory.CreateEditPackageCatalogOptions(); editOptions.Name = Constants.TestSourceName; editOptions.Explicit = false; editOptions.Priority = 42; this.EditAndValidatePackageCatalog(editOptions, EditPackageCatalogStatus.Ok); // Remove RemovePackageCatalogOptions removePackageCatalogOptions = this.TestFactory.CreateRemovePackageCatalogOptions(); removePackageCatalogOptions.Name = Constants.TestSourceName; var removeCatalogResult = await this.packageManager.RemovePackageCatalogAsync(removePackageCatalogOptions); Assert.IsNotNull(removeCatalogResult); Assert.AreEqual(RemovePackageCatalogStatus.Ok, removeCatalogResult.Status); var testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); Assert.IsNull(testSource); } /// /// Test class Tear down. /// [OneTimeTearDown] public void TestClassTearDown() { // Restore the tests source if it was removed as the affects subsequent tests. TestCommon.SetupTestSource(); } private async Task AddCatalogAndVerifyStatusAsync(AddPackageCatalogOptions addPackageCatalogOptions, AddPackageCatalogStatus expectedStatus, int expectedErrorCode = 0) { var addCatalogResult = await this.packageManager.AddPackageCatalogAsync(addPackageCatalogOptions); Assert.IsNotNull(addCatalogResult); Assert.AreEqual(expectedStatus, addCatalogResult.Status); if (expectedStatus != AddPackageCatalogStatus.Ok && expectedErrorCode != 0) { Assert.AreEqual(expectedErrorCode, addCatalogResult.ExtendedErrorCode.HResult); } } private PackageCatalogReference GetAndValidatePackageCatalog(AddPackageCatalogOptions addPackageCatalogOptions) { var packageCatalog = this.packageManager.GetPackageCatalogByName(addPackageCatalogOptions.Name); Assert.IsNotNull(packageCatalog); Assert.AreEqual(addPackageCatalogOptions.Name, packageCatalog.Info.Name); Assert.AreEqual(addPackageCatalogOptions.SourceUri, packageCatalog.Info.Argument); Assert.AreEqual(addPackageCatalogOptions.Explicit, packageCatalog.Info.Explicit); Assert.AreEqual(addPackageCatalogOptions.Priority, packageCatalog.Info.Priority); return packageCatalog; } private async Task AddAndValidatePackageCatalogAsync(AddPackageCatalogOptions addPackageCatalogOptions, AddPackageCatalogStatus expectedStatus, int expectedErrorCode = 0) { // Add the package catalog and verify the status var addCatalogResult = await this.packageManager.AddPackageCatalogAsync(addPackageCatalogOptions); Assert.IsNotNull(addCatalogResult); Assert.AreEqual(expectedStatus, addCatalogResult.Status); if (expectedStatus != AddPackageCatalogStatus.Ok) { // Only validate the error code if the status is not Ok and the expected error code is not 0 if (expectedErrorCode != 0) { Assert.AreEqual(expectedErrorCode, addCatalogResult.ExtendedErrorCode.HResult); } return; } // Validate the added package catalog if the status is Ok this.GetAndValidatePackageCatalog(addPackageCatalogOptions); } private async Task RemoveAndValidatePackageCatalogAsync(RemovePackageCatalogOptions removePackageCatalogOptions, RemovePackageCatalogStatus expectedStatus, int expectedErrorCode = 0) { var removeCatalogResult = await this.packageManager.RemovePackageCatalogAsync(removePackageCatalogOptions); Assert.IsNotNull(removeCatalogResult); Assert.AreEqual(expectedStatus, removeCatalogResult.Status); if (expectedStatus != RemovePackageCatalogStatus.Ok && expectedErrorCode != 0) { Assert.AreEqual(expectedErrorCode, removeCatalogResult.ExtendedErrorCode.HResult); return; } var packageCatalog = this.packageManager.GetPackageCatalogByName(removePackageCatalogOptions.Name); Assert.IsNull(packageCatalog); } private void EditAndValidatePackageCatalog(EditPackageCatalogOptions editPackageCatalogOptions, EditPackageCatalogStatus expectedStatus, int expectedErrorCode = 0) { var editCatalogResult = this.packageManager.EditPackageCatalog(editPackageCatalogOptions); Assert.IsNotNull(editCatalogResult); Assert.AreEqual(expectedStatus, editCatalogResult.Status); if (expectedStatus != EditPackageCatalogStatus.Ok && expectedErrorCode != 0) { Assert.AreEqual(expectedErrorCode, editCatalogResult.ExtendedErrorCode.HResult); return; } // Verify edits are correct. var packageCatalog = this.packageManager.GetPackageCatalogByName(editPackageCatalogOptions.Name); if (editPackageCatalogOptions.Explicit != null) { Assert.AreEqual(packageCatalog.Info.Explicit, editPackageCatalogOptions.Explicit); } if (editPackageCatalogOptions.Priority != null) { Assert.AreEqual(packageCatalog.Info.Priority, editPackageCatalogOptions.Priority); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/RepairInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; /// /// Repair Interop Tests for COM/WinRT Interop classes. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class RepairInterop : BaseInterop { private string installDir; private PackageManager packageManager; private PackageCatalogReference compositeSource; /// /// Initializes a new instance of the class. /// /// Initializer. public RepairInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Test setup. /// [SetUp] public void Init() { this.packageManager = this.TestFactory.CreatePackageManager(); this.installDir = TestCommon.GetRandomTestDir(); // Create a composite source var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); var testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); Assert.NotNull(testSource, $"{Constants.TestSourceName} cannot be null"); options.Catalogs.Add(testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; this.compositeSource = this.packageManager.CreateCompositePackageCatalog(options); } /// /// Test Repair MSI Installer. /// /// A representing the asynchronous unit test. [Test] public async Task RepairMSIInstaller() { string msiInstallerPackageId = "AppInstallerTest.TestMsiRepair"; var searchResult = await this.FindAndInstallPackage(msiInstallerPackageId, this.installDir, null); // Note: The 'msiexec repair' command requires the original installer file to be present at the location registered in the ARP (Add/Remove Programs). // In our test scenario, the MSI installer file is initially placed in a temporary location and then deleted, which can cause the repair operation to fail. // To work around this, we copy the installer file to the ARP source directory before running the repair command. // A more permanent solution would be to modify the MSI installer to cache the installer file in a known location and register that location as the installer source. // This would allow the 'msiexec repair' command to function as expected. string installerSourceDir = TestCommon.CopyInstallerFileToARPInstallSourceDirectory(TestCommon.GetTestDataFile("AppInstallerTestMsiInstallerV2.msi"), Constants.MsiInstallerProductCode, true); // Repair the package await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, this.TestFactory.CreateRepairOptions(), RepairResultStatus.Ok); // Cleanup Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(this.installDir)); if (installerSourceDir != null && Directory.Exists(installerSourceDir)) { Directory.Delete(installerSourceDir, true); } } /// /// Test Repair MSIX Installer. /// /// representing the asynchronous unit test. [Test] public async Task RepairNonStoreMSIXPackage() { string msixPackageId = "AppInstallerTest.TestMsixInstaller"; var searchResult = await this.FindAndInstallPackage(msixPackageId, this.installDir, null); // Repair the package await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, this.TestFactory.CreateRepairOptions(), RepairResultStatus.Ok); // Cleanup Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test MSIX non-store package repair with machine scope. /// /// representing the asynchronous unit test. [Test] public async Task RepairNonStoreMsixPackageWithMachineScope() { var findPackages = this.FindAllPackages(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.ContainsCaseInsensitive, "Microsoft.Paint"); if (findPackages == null || findPackages.Count == 0) { Assert.Ignore("Test skipped as Microsoft.Paint_8wekyb3d8bbwe can't be found."); } var searchResult = findPackages.First(); if (searchResult == null || searchResult.CatalogPackage == null || searchResult.CatalogPackage.InstalledVersion == null) { Assert.Ignore("Test skipped as Microsoft.Paint_8wekyb3d8bbwe is not installed."); } // Repair the package var repairOptions = this.TestFactory.CreateRepairOptions(); repairOptions.PackageRepairScope = PackageRepairScope.System; await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, repairOptions, RepairResultStatus.RepairError, Constants.ErrorCode.ERROR_INSTALL_SYSTEM_NOT_SUPPORTED); } /// /// Test repair of a Burn installer that has a "modify" repair behavior specified in the manifest. /// /// representing the asynchronous unit test. [Test] public async Task RepairBurnInstallerWithModifyBehavior() { var replaceInstallerArguments = this.GetReplacementArguments(this.installDir, "2.0.0.0", "TestModifyRepair", useHKLM: true); var searchResult = await this.FindAndInstallPackage(Constants.ModifyRepairInstaller, this.installDir, replaceInstallerArguments.ToString()); // Repair the package var repairOptions = this.TestFactory.CreateRepairOptions(); repairOptions.PackageRepairMode = PackageRepairMode.Silent; await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, repairOptions, RepairResultStatus.Ok); // Cleanup Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(this.installDir, "Modify Repair operation")); } /// /// Tests the repair operation of a Burn installer that was installed in user scope but is being repaired in an admin context. /// /// representing the asynchronous unit test. [Test] public async Task RepairBurnInstallerInAdminContextWithUserScopeInstall() { if (this.TestFactory.Context != ClsidContext.InProc) { Assert.Ignore("Test is only applicable for InProc context."); } string replaceInstallerArguments = this.GetReplacementArguments(this.installDir, "2.0.0.0", "TestUserScopeInstallRepairInAdminContext"); var searchResult = await this.FindAndInstallPackage("AppInstallerTest.TestUserScopeInstallRepairInAdminContext", this.installDir, replaceInstallerArguments); // Repair the package var repairOptions = this.TestFactory.CreateRepairOptions(); repairOptions.PackageRepairMode = PackageRepairMode.Silent; await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, repairOptions, RepairResultStatus.RepairError, Constants.ErrorCode.ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED); // Cleanup TestCommon.CleanupTestExeAndDirectory(this.installDir); } /// /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest and NoModify ARP flag set. /// /// representing the asynchronous unit test. [Test] public async Task RepairBurnInstallerWithModifyBehaviorAndNoModifyFlag() { string replaceInstallerArguments = this.GetReplacementArguments(this.installDir, "2.0.0.0", "TestModifyRepairWithNoModify", useHKLM: true, noModify: true); var searchResult = await this.FindAndInstallPackage("AppInstallerTest.TestModifyRepairWithNoModify", this.installDir, replaceInstallerArguments); // Repair the package var repairOptions = this.TestFactory.CreateRepairOptions(); repairOptions.PackageRepairMode = PackageRepairMode.Silent; await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, repairOptions, RepairResultStatus.RepairError, Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED); // Cleanup TestCommon.CleanupTestExeAndDirectory(this.installDir); } /// /// Tests the scenario where the repair operation is not supported for Portable Installer type. /// /// representing the asynchronous unit test. [Test] public async Task RepairOperationNotSupportedForPortableInstaller() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var searchResult = await this.FindAndInstallPackage(packageId, null, null); // Repair the package var repairOptions = this.TestFactory.CreateRepairOptions(); repairOptions.PackageRepairMode = PackageRepairMode.Silent; await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, repairOptions, RepairResultStatus.RepairError, Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED); // If no location specified, default behavior is to create a package directory with the name "{packageId}_{sourceId}" TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest. /// /// representing the asynchronous unit test. [Test] public async Task RepairExeInstallerWithUninstallerBehavior() { string replaceInstallerArguments = this.GetReplacementArguments(this.installDir, "2.0.0.0", "UninstallerRepair", useHKLM: true); var searchResult = await this.FindAndInstallPackage("AppInstallerTest.UninstallerRepair", this.installDir, replaceInstallerArguments); // Repair the package await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, this.TestFactory.CreateRepairOptions(), RepairResultStatus.Ok); // Cleanup Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(this.installDir, "Uninstaller Repair operation")); } /// /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest and NoRepair ARP flag set. /// /// representing the asynchronous unit test. [Test] public async Task RepairExeInstallerWithUninstallerBehaviorAndNoRepairFlag() { string replaceInstallerArguments = this.GetReplacementArguments(this.installDir, "2.0.0.0", "UninstallerRepairWithNoRepair", useHKLM: true, noRepair: true); var searchResult = await this.FindAndInstallPackage("AppInstallerTest.UninstallerRepairWithNoRepair", this.installDir, replaceInstallerArguments); // Repair the package await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, this.TestFactory.CreateRepairOptions(), RepairResultStatus.RepairError, Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED); // Cleanup TestCommon.CleanupTestExeAndDirectory(this.installDir); } /// /// Test repair of a Nullsoft installer that has a "uninstaller" repair behavior specified in the manifest. /// /// representing the asynchronous unit test. [Test] public async Task RepairNullsoftInstallerWithUninstallerBehavior() { string replaceInstallerArguments = this.GetReplacementArguments(this.installDir, "2.0.0.0", "NullsoftUninstallerRepair", useHKLM: true); var searchResult = await this.FindAndInstallPackage("AppInstallerTest.NullsoftUninstallerRepair", this.installDir, replaceInstallerArguments.ToString()); // Repair the package await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, this.TestFactory.CreateRepairOptions(), RepairResultStatus.Ok); // Cleanup Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(this.installDir, "Uninstaller Repair operation")); } /// /// Test repair of a Inno installer that has a "installer" repair behavior specified in the manifest. /// /// representing the asynchronous unit test. [Test] public async Task RepairInnoInstallerWithInstallerRepairBehavior() { string replaceInstallerArguments = this.GetReplacementArguments(this.installDir, "2.0.0.0", "TestInstallerRepair", useHKLM: true); var searchResult = await this.FindAndInstallPackage("AppInstallerTest.TestInstallerRepair", this.installDir, replaceInstallerArguments); // Repair the package await this.RepairPackageAndValidateStatus(searchResult.CatalogPackage, this.TestFactory.CreateRepairOptions(), RepairResultStatus.Ok); // Cleanup Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(this.installDir, "Installer Repair operation")); } private async Task FindAndInstallPackage(string packageId, string installDir, string replacementInstallerArguments) { // Find a package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; if (!string.IsNullOrEmpty(installDir)) { installOptions.PreferredInstallLocation = installDir; } if (!string.IsNullOrEmpty(replacementInstallerArguments)) { installOptions.ReplacementInstallerArguments = replacementInstallerArguments; } // Install the package var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should be detected as installed searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Return the search result return searchResult; } private async Task RepairPackageAndValidateStatus(CatalogPackage package, RepairOptions repairOptions, RepairResultStatus expectedStatus, int expectedErrorCode = 0) { var repairResult = await this.packageManager.RepairPackageAsync(package, repairOptions); Assert.AreEqual(expectedStatus, repairResult.Status); if (expectedStatus != RepairResultStatus.Ok) { Assert.AreEqual(expectedErrorCode, repairResult.ExtendedErrorCode.HResult); } } private string GetReplacementArguments(string installDir, string version, string displayName, bool useHKLM = false, bool noModify = false, bool noRepair = false) { var replacementArguments = new StringBuilder($"/InstallDir {installDir} /Version {version} /DisplayName {displayName}"); // Machine scope install. if (useHKLM) { replacementArguments.Append($" /UseHKLM"); } // Instructs test installer to set NoModify ARP flag. if (noModify) { replacementArguments.Append($" /NoModify"); } // Instructs test installer to set NoRepair ARP flag. if (noRepair) { replacementArguments.Append($" /NoRepair"); } return replacementArguments.ToString(); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/Shutdown.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; using WinGetTestCommon; /// /// Shutdown testing. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class Shutdown : BaseInterop { /// /// Initializes a new instance of the class. /// /// Initializer. public Shutdown(IInstanceInitializer initializer) : base(initializer) { } /// /// Checks that shutdown will proceed even though an object is active. /// [Test] public void NoActiveOperations() { var packageManager = this.TestFactory.CreatePackageManager(); var servers = WinGetServerInstance.GetInstances(); Assert.AreEqual(1, servers.Count); var server = servers[0]; Assert.IsTrue(server.HasWindow); // This is the call pattern from Windows this.SendMessageAndLog(server, WindowMessage.QueryEndSession); this.SendMessageAndLog(server, WindowMessage.EndSession); this.SendMessageAndLog(server, WindowMessage.Close); Assert.IsTrue(server.Process.WaitForExit(5000)); } /// /// Checks that shutdown will proceed even though an operation is active. /// /// The task. [Test] public async Task ActiveInstallOperation() { var packageManager = this.TestFactory.CreatePackageManager(); var testSource = packageManager.GetPackageCatalogByName(Constants.TestSourceName); var installDir = TestCommon.GetRandomTestDir(); var servers = WinGetServerInstance.GetInstances(); Assert.AreEqual(1, servers.Count); var server = servers[0]; Assert.IsTrue(server.HasWindow); // Find package var searchResult = this.FindOnePackage(testSource, PackageMatchField.Name, PackageFieldMatchOption.Equals, "InapplicableOsVersion"); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = installDir; // Install var installOperation = packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); // This is the call pattern from Windows this.SendMessageAndLog(server, WindowMessage.QueryEndSession); this.SendMessageAndLog(server, WindowMessage.EndSession); this.SendMessageAndLog(server, WindowMessage.Close); Assert.IsTrue(server.Process.WaitForExit(5000)); InstallResult installResult = null; Exception exception = null; try { installResult = await installOperation; } catch (Exception ex) { exception = ex; } // We just expect some kind of signal to indicate the failed attempt. if (installResult != null) { Assert.AreNotEqual(InstallResultStatus.Ok, installResult.Status); } else { Assert.NotNull(exception); } Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); } private void SendMessageAndLog(WinGetServerInstance server, WindowMessage message) { TestContext.Out.WriteLine($"Sending message {message} to process {server.Process.Id}..."); try { if (server.SendMessage(message)) { TestContext.Out.WriteLine("... succeeded."); } else { TestContext.Out.WriteLine("... failed."); } } catch (Exception e) { TestContext.Out.WriteLine($"... had exception: {e.Message}"); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/UninstallInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.IO; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; /// /// Test uninstall interop. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class UninstallInterop : BaseInterop { private string installDir; private PackageManager packageManager; private PackageCatalogReference compositeSource; /// /// Initializes a new instance of the class. /// /// Initializer. public UninstallInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Set up. /// [SetUp] public void Init() { this.packageManager = this.TestFactory.CreatePackageManager(); this.installDir = TestCommon.GetRandomTestDir(); // Create composite package catalog source var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); var testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); Assert.NotNull(testSource, $"{Constants.TestSourceName} cannot be null"); options.Catalogs.Add(testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; this.compositeSource = this.packageManager.CreateCompositePackageCatalog(options); } /// /// Test uninstall exe. /// /// A representing the asynchronous unit test. [Test] public async Task UninstallTestExe() { // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.ExeInstallerPackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallMode = PackageInstallMode.Silent; installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.ExeInstallerPackageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Uninstall var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); Assert.True(TestCommon.VerifyTestExeUninstalled(this.installDir)); } /// /// Test uninstall msi. /// /// A representing the asynchronous unit test. [Test] public async Task UninstallTestMsi() { if (string.IsNullOrEmpty(TestIndex.MsiInstaller)) { Assert.Ignore("MSI installer not available"); } // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.MsiInstallerPackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PreferredInstallLocation = this.installDir; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.MsiInstallerPackageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Uninstall var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); Assert.True(TestCommon.VerifyTestMsiUninstalled(this.installDir)); } /// /// Test uninstall msix. /// /// A representing the asynchronous unit test. [Test] public async Task UninstallTestMsix() { // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.MsixInstallerPackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.MsixInstallerPackageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Uninstall var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); Assert.True(TestCommon.VerifyTestMsixUninstalled()); } /// /// Test uninstall msix with machine scope. /// /// A representing the asynchronous unit test. [Test] public async Task UninstallTestMsixMachineScope() { // TODO: Provision and Deprovision api not supported in build server. Assert.Ignore(); // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.MsixInstallerPackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageInstallScope = PackageInstallScope.System; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.MsixInstallerPackageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Uninstall var uninstallOptions = this.TestFactory.CreateUninstallOptions(); uninstallOptions.PackageUninstallScope = PackageUninstallScope.System; var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, uninstallOptions); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); Assert.True(TestCommon.VerifyTestMsixUninstalled(true)); } /// /// Test uninstall portable package. /// /// A representing the asynchronous unit test. [Test] public async Task UninstallPortable() { string installDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Packages"); string packageDirName = Constants.PortableExePackageDirName; string productCode = Constants.PortableExePackageDirName; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string fileName = Constants.AppInstallerTestExeInstallerExe; // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.PortableExePackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.PortableExePackageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Uninstall var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, false); } /// /// Test uninstall portable package with product code. /// /// A representing the asynchronous unit test. [Test] public async Task UninstallPortableWithProductCode() { string installDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Packages"); string packageDirName = Constants.PortableExePackageDirName; string productCode = Constants.PortableExePackageDirName; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string fileName = Constants.AppInstallerTestExeInstallerExe; // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.PortableExePackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.ProductCode, PackageFieldMatchOption.Equals, productCode); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Uninstall var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, false); } /// /// Test uninstall portable package modified symlink. /// /// A representing the asynchronous unit test. [Test] public async Task UninstallPortableModifiedSymlink() { string packageId = Constants.PortableExePackageId; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string symlinkDirectory = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Links"); string symlinkPath = Path.Combine(symlinkDirectory, commandAlias); // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Replace symlink with modified symlink File.Delete(symlinkPath); FileSystemInfo modifiedSymlinkInfo = File.CreateSymbolicLink(symlinkPath, "fakeTargetExe"); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Uninstall var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); Assert.AreEqual(UninstallResultStatus.UninstallError, uninstallResult.Status); Assert.True(modifiedSymlinkInfo.Exists, "Modified symlink should still exist"); // Remove modified symlink as to not interfere with other tests modifiedSymlinkInfo.Delete(); // Uninstall again to clean up. await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); } /// /// Test uninstall not indexed. /// /// A representing the asynchronous unit test. [Test] public async Task UninstallNotIndexed() { const string customProductCode = "{f08fc03c-0b7e-4fca-9b3c-3a384d18a9f3}"; // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, Constants.ExeInstallerPackageId); // Configure installation var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.ReplacementInstallerArguments = $"/ProductID {customProductCode} /InstallDir {this.installDir}"; // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.ProductCode, PackageFieldMatchOption.Equals, customProductCode); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Uninstall var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, this.TestFactory.CreateUninstallOptions()); Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); Assert.True(TestCommon.VerifyTestExeUninstalled(this.installDir)); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Interop/UpgradeInterop.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.Interop { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using AppInstallerCLIE2ETests.Helpers; using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; using NUnit.Framework; /// /// Test upgrade interop. /// [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class UpgradeInterop : BaseInterop { private PackageManager packageManager; private PackageCatalogReference compositeSource; /// /// Initializes a new instance of the class. /// /// Initializer. public UpgradeInterop(IInstanceInitializer initializer) : base(initializer) { } /// /// Set up. /// [SetUp] public void Init() { this.packageManager = this.TestFactory.CreatePackageManager(); // Create composite package catalog source var options = this.TestFactory.CreateCreateCompositePackageCatalogOptions(); var testSource = this.packageManager.GetPackageCatalogByName(Constants.TestSourceName); Assert.NotNull(testSource, $"{Constants.TestSourceName} cannot be null"); options.Catalogs.Add(testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; this.compositeSource = this.packageManager.CreateCompositePackageCatalog(options); } /// /// Tests upgrade portable package. /// /// A representing the asynchronous unit test. [Test] public async Task UpgradePortable() { string installDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Packages"); string packageId = Constants.PortableExePackageId; string packageDirName = Constants.PortableExePackageDirName; string productCode = Constants.PortableExePackageDirName; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string fileName = Constants.AppInstallerTestExeInstallerExe; // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); // Configure install options var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "1.0.0.0"); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.AreEqual("1.0.0.0", searchResult.CatalogPackage.InstalledVersion?.Version); // Configure upgrade options var upgradeOptions = this.TestFactory.CreateInstallOptions(); upgradeOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "2.0.0.0"); // Upgrade var upgradeResult = await this.packageManager.UpgradePackageAsync(searchResult.CatalogPackage, upgradeOptions); Assert.AreEqual(InstallResultStatus.Ok, upgradeResult.Status); // Find package again, but this time it should detect the upgraded installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.AreEqual("2.0.0.0", searchResult.CatalogPackage.InstalledVersion?.Version); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test upgrade portable package with arp mismatch. /// /// A representing the asynchronous unit test. [Test] public async Task UpgradePortableARPMismatch() { string installDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Packages"); string packageId = Constants.PortableExePackageId; string packageDirName = Constants.PortableExePackageDirName; string productCode = Constants.PortableExePackageDirName; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string fileName = Constants.AppInstallerTestExeInstallerExe; // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); // Configure install options var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "1.0.0.0"); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Modify packageId to cause mismatch. TestCommon.ModifyPortableARPEntryValue(productCode, Constants.WinGetPackageIdentifier, "testPackageId"); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Configure upgrade options var upgradeOptions = this.TestFactory.CreateInstallOptions(); upgradeOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "2.0.0.0"); // Upgrade var upgradeResult = await this.packageManager.UpgradePackageAsync(searchResult.CatalogPackage, upgradeOptions); Assert.AreEqual(InstallResultStatus.InstallError, upgradeResult.Status); Assert.AreEqual(Constants.ErrorCode.ERROR_PORTABLE_PACKAGE_ALREADY_EXISTS, upgradeResult.ExtendedErrorCode.HResult); // Find package again, it should have not been upgraded searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.AreEqual("1.0.0.0", searchResult.CatalogPackage.InstalledVersion.Version); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test upgrade portable package force. /// /// A representing the asynchronous unit test. [Test] public async Task UpgradePortableForcedOverride() { string installDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Packages"); string packageId = Constants.PortableExePackageId; string packageDirName = Constants.PortableExePackageDirName; string productCode = Constants.PortableExePackageDirName; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string fileName = Constants.AppInstallerTestExeInstallerExe; // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); // Configure install options var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "1.0.0.0"); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Modify packageId and sourceId to cause mismatch. TestCommon.ModifyPortableARPEntryValue(productCode, Constants.WinGetPackageIdentifier, "testPackageId"); TestCommon.ModifyPortableARPEntryValue(productCode, Constants.WinGetSourceIdentifier, "testSourceId"); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.NotNull(searchResult.CatalogPackage.InstalledVersion); // Configure upgrade options var upgradeOptions = this.TestFactory.CreateInstallOptions(); upgradeOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "2.0.0.0"); upgradeOptions.Force = true; // Upgrade var upgradeResult = await this.packageManager.UpgradePackageAsync(searchResult.CatalogPackage, upgradeOptions); Assert.AreEqual(InstallResultStatus.Ok, upgradeResult.Status); // Find package again, but this time it should detect the upgraded installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.AreEqual("2.0.0.0", searchResult.CatalogPackage.InstalledVersion.Version); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test upgrade portable package uninstall previous. /// /// A representing the asynchronous unit test. [Test] public async Task UpgradePortableUninstallPrevious() { string installDir = Path.Combine(Environment.GetEnvironmentVariable(Constants.LocalAppData), "Microsoft", "WinGet", "Packages"); string packageId = Constants.PortableExePackageId; string packageDirName = Constants.PortableExePackageDirName; string productCode = Constants.PortableExePackageDirName; string commandAlias = Constants.AppInstallerTestExeInstallerExe; string fileName = Constants.AppInstallerTestExeInstallerExe; // Find package var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); // Configure install options var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "1.0.0.0"); // Install var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.AreEqual("1.0.0.0", searchResult.CatalogPackage.InstalledVersion.Version); // Configure upgrade options var upgradeOptions = this.TestFactory.CreateInstallOptions(); upgradeOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "3.0.0.0"); // Upgrade var upgradeResult = await this.packageManager.UpgradePackageAsync(searchResult.CatalogPackage, upgradeOptions); Assert.AreEqual(InstallResultStatus.Ok, upgradeResult.Status); // Find package again, but this time it should detect the upgraded installed version searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, packageId); Assert.AreEqual("3.0.0.0", searchResult.CatalogPackage.InstalledVersion.Version); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Tests IsUpdateAvailable. /// /// A representing the asynchronous unit test. [Test] public async Task TestIsUpdateAvailable_ApplicableTrue() { // Find and install the test package. Install the version 1.0.0.0. var installDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstaller"); var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PreferredInstallLocation = installDir; installOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "1.0.0.0"); var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version. searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestExeInstaller"); // The installed version is 1.0.0.0. Assert.AreEqual("1.0.0.0", searchResult.CatalogPackage.InstalledVersion.Version); // IsUpdateAvailable is true. Assert.True(searchResult.CatalogPackage.IsUpdateAvailable); // Uninstall to clean up. var uninstallOptions = this.TestFactory.CreateUninstallOptions(); var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, uninstallOptions); } /// /// Tests applicability check is performed for IsUpdateAvailable api. /// /// A representing the asynchronous unit test. [Test] public async Task TestIsUpdateAvailable_ApplicableFalse() { // Find and install the test package. Install the version 1.0.0.0. var installDir = TestCommon.GetRandomTestDir(); var searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestUpgradeApplicability"); var installOptions = this.TestFactory.CreateInstallOptions(); installOptions.PreferredInstallLocation = installDir; installOptions.PackageVersionId = First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "1.0.0.0"); var installResult = await this.packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); // Find package again, but this time it should detect the installed version. searchResult = this.FindOnePackage(this.compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestUpgradeApplicability"); // The installed version is 1.0.0.0. Assert.AreEqual("1.0.0.0", searchResult.CatalogPackage.InstalledVersion.Version); // There is version 2.0.0.0 in the package available versions. Assert.NotNull(First(searchResult.CatalogPackage.AvailableVersions, i => i.Version == "2.0.0.0")); // IsUpdateAvailable is false due to applicability check. Only arm64 in version 2.0.0.0. Assert.False(searchResult.CatalogPackage.IsUpdateAvailable); // Uninstall to clean up. var uninstallOptions = this.TestFactory.CreateUninstallOptions(); var uninstallResult = await this.packageManager.UninstallPackageAsync(searchResult.CatalogPackage, uninstallOptions); } // Cannot use foreach or Linq for out-of-process IVector // Bug: https://github.com/microsoft/CsWinRT/issues/1205 private static T First(IReadOnlyList list, Func condition) { if (list == null || condition == null) { throw new ArgumentNullException(); } for (int i = 0; i < list.Count; ++i) { var item = list[i]; if (condition(item)) { return item; } } throw new InvalidOperationException("No element satisfies the condition"); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ListCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// List command tests. /// public class ListCommand : BaseCommand { /// /// Test list winget. /// [Test] public void ListSelf() { var result = TestCommon.RunAICLICommand("list", Constants.AICLIPackageFamilyName); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.AICLIPackageName)); Assert.True(result.StdOut.Contains(Constants.AICLIPackagePublisherHash)); } /// /// Test list after installing a package. /// [Test] public void ListAfterInstall() { System.Guid guid = System.Guid.NewGuid(); string productCode = guid.ToString(); var installDir = TestCommon.GetRandomTestDir(); // DisplayName must be set to avoid conflicts with other packages that use the same exe installer. string displayName = "TestExeInstaller"; string localAppDataPath = System.Environment.GetEnvironmentVariable(Constants.LocalAppData); string logFilePath = System.IO.Path.Combine(localAppDataPath, Constants.E2ETestLogsPathPackaged); logFilePath = System.IO.Path.Combine(logFilePath, "ListAfterInstall-" + System.IO.Path.GetRandomFileName() + ".log"); var result = TestCommon.RunAICLICommand("list", productCode); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --override \"/InstallDir {installDir} /ProductID {productCode} /LogFile {logFilePath} /DisplayName {displayName}\""); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); result = TestCommon.RunAICLICommand("list", productCode); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExeInstaller")); Assert.True(result.StdOut.Contains("1.0.0.0")); Assert.True(result.StdOut.Contains("2.0.0.0")); } /// /// Test expected entries after list. /// [Test] public void ListWithArpVersionMapping() { // No mapping performed this.ArpVersionMappingTest("AppInstallerTest.TestArpVersionSameVersion", "TestArpVersionSameVersion", "0.5", "0.5", "< 1.0"); // Partial mapping performed(i.e. only if version falls within arp version range) this.ArpVersionMappingTest("AppInstallerTest.TestArpVersionOppositeOrder", "TestArpVersionOppositeOrder", "10.1", "1.0", "10.1"); this.ArpVersionMappingTest("AppInstallerTest.TestArpVersionOppositeOrder", "TestArpVersionOppositeOrder", "9.9", "9.9", "> 2.0"); // Full mapping performed this.ArpVersionMappingTest("AppInstallerTest.TestArpVersionSameOrder", "TestArpVersionSameOrder", "7.0", "< 1.0", "7.0"); this.ArpVersionMappingTest("AppInstallerTest.TestArpVersionSameOrder", "TestArpVersionSameOrder", "10.1", "1.0", "10.1"); this.ArpVersionMappingTest("AppInstallerTest.TestArpVersionSameOrder", "TestArpVersionSameOrder", "10.7", "< 2.0", "10.7"); this.ArpVersionMappingTest("AppInstallerTest.TestArpVersionSameOrder", "TestArpVersionSameOrder", "11.1", "2.0", "11.1"); this.ArpVersionMappingTest("AppInstallerTest.TestArpVersionSameOrder", "TestArpVersionSameOrder", "12.0", "> 2.0", "12.0"); } /// /// Test list with upgrade code. /// [Test] public void ListWithUpgradeCode() { // Installs the MSI installer using the TestMsiInstaller package. // Then tries listing the TestMsiInstallerUpgradeCode package, which should // be correlated to it by the UpgradeCode. if (string.IsNullOrEmpty(TestIndex.MsiInstaller)) { Assert.Ignore("MSI installer not available"); } var installDir = TestCommon.GetRandomTestDir(); Assert.AreEqual(Constants.ErrorCode.S_OK, TestCommon.RunAICLICommand("install", $"TestMsiInstaller --silent -l {installDir}").ExitCode); var result = TestCommon.RunAICLICommand("list", "TestMsiInstallerUpgradeCode"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("AppInstallerTest.TestMsiInstallerUpgradeCode")); } /// /// Test list with exe installed with machine scope. /// [Test] public void ListWithScopeExeInstalledAsMachine() { System.Guid guid = System.Guid.NewGuid(); string identifier = "AppInstallerTest.TestExeInstaller"; string productCode = guid.ToString(); var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"{identifier} --override \"/InstallDir {installDir} /ProductID {productCode} /UseHKLM\""); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); // List with user scope will not find the package result = TestCommon.RunAICLICommand("list", $"{productCode} --scope user"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.False(result.StdOut.Contains(identifier)); // List with machine scope will find the package result = TestCommon.RunAICLICommand("list", $"{productCode} --scope machine"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(identifier)); TestCommon.RunCommand(Path.Combine(installDir, Constants.TestExeUninstallerFileName)); } /// /// Test list with exe installed with user scope. /// [Test] public void ListWithScopeExeInstalledAsUser() { System.Guid guid = System.Guid.NewGuid(); string identifier = "AppInstallerTest.TestExeInstaller"; string productCode = guid.ToString(); var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"{identifier} --override \"/InstallDir {installDir} /ProductID {productCode}\""); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); // List with user scope will find the package result = TestCommon.RunAICLICommand("list", $"{productCode} --scope user"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(identifier)); // List with machine scope will not find the package result = TestCommon.RunAICLICommand("list", $"{productCode} --scope machine"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.False(result.StdOut.Contains(identifier)); TestCommon.RunCommand(Path.Combine(installDir, Constants.TestExeUninstallerFileName)); } /// /// Test list with msix installed with machine scope. /// [Test] public void ListWithScopeMsixInstalledAsMachine() { // TODO: Provision and Deprovision api not supported in build server. Assert.Ignore(); var result = TestCommon.RunAICLICommand("install", $"{Constants.MsixInstallerPackageId} --scope machine"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); // List with user scope will also find the package because msix is provisioned for all users result = TestCommon.RunAICLICommand("list", $"{Constants.MsixInstallerPackageId} --scope user"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.MsixInstallerPackageId)); // List with machine scope will find the package result = TestCommon.RunAICLICommand("list", $"{Constants.MsixInstallerPackageId} --scope machine"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.MsixInstallerPackageId)); TestCommon.RemoveMsix(Constants.MsixInstallerName, true); } /// /// Test list with msix installed with user scope. /// [Test] public void ListWithScopeMsixInstalledAsUser() { var result = TestCommon.RunAICLICommand("install", $"{Constants.MsixInstallerPackageId} --scope user"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); // List with user scope will find the package result = TestCommon.RunAICLICommand("list", $"{Constants.MsixInstallerPackageId} --scope user"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.MsixInstallerPackageId)); // List with machine scope will not find the package result = TestCommon.RunAICLICommand("list", $"{Constants.MsixInstallerPackageId} --scope machine"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.False(result.StdOut.Contains(Constants.MsixInstallerPackageId)); TestCommon.RemoveMsix(Constants.MsixInstallerName); } /// /// Test package correlation with same package name but different architecture is correct. /// [Test] public void ListWithMappingWithArchitecture() { var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMappingWithArchitectureX86 -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); // List with AppInstallerTest.TestMappingWithArchitectureX64 (from available to installed scenario) will not find the package. result = TestCommon.RunAICLICommand("list", "AppInstallerTest.TestMappingWithArchitectureX64"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.False(result.StdOut.Contains("AppInstallerTest.TestMappingWithArchitectureX64")); // List with AppInstallerTest.TestMappingWithArchitectureX86 (from available to installed scenario) will find the package. result = TestCommon.RunAICLICommand("list", "AppInstallerTest.TestMappingWithArchitectureX86"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("AppInstallerTest.TestMappingWithArchitectureX86")); // List with product code (from installed to available scenario) will find the AppInstallerTest.TestMappingWithArchitectureX86 package. result = TestCommon.RunAICLICommand("list", "{0e426f01-b682-4e67-a357-52f9ecb4590d}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("AppInstallerTest.TestMappingWithArchitectureX86")); // best effort clean up TestCommon.RunCommand(Path.Combine(installDir, Constants.TestExeUninstallerFileName)); } private void ArpVersionMappingTest(string packageIdentifier, string displayNameOverride, string displayVersionOverride, string expectedListVersion, string notExpectedListVersion = "") { System.Guid guid = System.Guid.NewGuid(); string productCode = guid.ToString(); var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("list", productCode); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); result = TestCommon.RunAICLICommand("install", $"{packageIdentifier} --override \"/InstallDir {installDir} /ProductID {productCode} /DisplayName {displayNameOverride} /Version {displayVersionOverride}\""); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); result = TestCommon.RunAICLICommand("list", productCode); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(packageIdentifier)); Assert.True(result.StdOut.Contains(expectedListVersion)); if (!string.IsNullOrEmpty(notExpectedListVersion)) { Assert.False(result.StdOut.Contains(notExpectedListVersion)); } // Try clean up if (File.Exists(Path.Combine(installDir, Constants.TestExeInstalledFileName))) { TestCommon.RunCommand(Path.Combine(installDir, Constants.TestExeUninstallerFileName)); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Pinning.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; using static AppInstallerCLIE2ETests.Helpers.TestCommon; /// /// Test upgrading pinned packages. /// public class Pinning : BaseCommand { /// /// Set up for all tests. /// [SetUp] public void Setup() { // All tests use TestExeInstaller; try to clean it up for failure cases, // then install the base version for pinning TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestExeInstaller"); TestCommon.RunAICLICommand("install", "AppInstallerTest.TestExeInstaller -v 1.0.0.0"); TestCommon.RunAICLICommand("pin remove", "AppInstallerTest.TestExeInstaller"); } /// /// Clean up done after all the tests here. /// [OneTimeTearDown] public void OneTimeTearDown() { TestCommon.RunAICLICommand("pin remove", "AppInstallerTest.TestExeInstaller"); TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestExeInstaller"); } // All tests do roughly the same with different types of pins: // * Check that the available version shown by list is the latest // * Check that the available version shown by upgrade is appropriate for the pin, // including checks with flags to include pinned. // * Check that an upgrade installs the right version /// /// Tests upgrading a package when there are no pins on it. /// [Test] public void UpgradeWithNoPins() { RunCommandResult result; result = TestCommon.RunAICLICommand("list", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "List shows the latest available version"); result = TestCommon.RunAICLICommand("upgrade", string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "The latest upgrade-able version is the same if there are no pins"); } /// /// Tests upgrading a package when it has a pinning pin. /// [Test] public void UpgradeWithPinningPin() { RunCommandResult result; string installDir = Path.GetTempPath(); // The base version of this app does not log /Version, but it still includes the version number in the log file name. Assert.True(TestCommon.VerifyTestExeInstalled(installDir, "1.0.0.0"), "Base version installed"); result = TestCommon.RunAICLICommand("pin add", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); result = TestCommon.RunAICLICommand("list", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "List shows the latest available version"); result = TestCommon.RunAICLICommand("upgrade", string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsFalse(result.StdOut.Contains("2.0.0.0"), "Pin hides latest available version"); Assert.IsTrue(result.StdOut.Contains("package(s) have pins that prevent upgrade")); result = TestCommon.RunAICLICommand("upgrade", "--all"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode, "Upgrade succeeds with nothing to upgrade"); Assert.True(TestCommon.VerifyTestExeInstalled(installDir, "1.0.0.0"), "No newer version installed"); result = TestCommon.RunAICLICommand("upgrade", "--include-pinned"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "Argument makes available version show up"); result = TestCommon.RunAICLICommand("upgrade", "--all --include-pinned"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode, "Upgrade succeeds"); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/Version 2.0.0.0")); } /// /// Tests upgrading a package when it has a gating pin that allows updating to another version. /// [Test] public void UpgradeWithGatingPin() { RunCommandResult result; string installDir = Path.GetTempPath(); var pinResult = TestCommon.RunAICLICommand("pin add", "AppInstallerTest.TestExeInstaller --version 1.0.*"); Assert.AreEqual(Constants.ErrorCode.S_OK, pinResult.ExitCode); result = TestCommon.RunAICLICommand("list", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "List shows the latest available version"); result = TestCommon.RunAICLICommand("upgrade", string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsFalse(result.StdOut.Contains("2.0.0.0"), "Pin hides latest available version"); Assert.IsTrue(result.StdOut.Contains("1.0.1.0"), "Version matching pin gated version shows up"); result = TestCommon.RunAICLICommand("upgrade", "--all"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode, "Upgrade succeeds"); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/Version 1.0.1.0")); } /// /// Tests upgrading a package when it has a gating pin that blocks all other versions. /// [Test] public void UpgradeWithGatingPinToCurrent() { RunCommandResult result; result = TestCommon.RunAICLICommand("pin add", "AppInstallerTest.TestExeInstaller --version 1.0.0.*"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); result = TestCommon.RunAICLICommand("list", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "List shows the latest available version"); result = TestCommon.RunAICLICommand("upgrade", string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsFalse(result.StdOut.Contains("2.0.0.0"), "Pin hides latest available version"); Assert.IsTrue(result.StdOut.Contains("package(s) have pins that prevent upgrade")); result = TestCommon.RunAICLICommand("upgrade", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_PACKAGE_IS_PINNED, result.ExitCode, "No upgrades available due to pin"); } /// /// Tests upgrading a package when it has a blocking pin. /// [Test] public void UpgradeWithBlockingPin() { RunCommandResult result; var pinResult = TestCommon.RunAICLICommand("pin add", "AppInstallerTest.TestExeInstaller --blocking"); Assert.AreEqual(Constants.ErrorCode.S_OK, pinResult.ExitCode); result = TestCommon.RunAICLICommand("list", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "List shows the latest available version"); result = TestCommon.RunAICLICommand("upgrade", string.Empty); Assert.IsFalse(result.StdOut.Contains("2.0.0.0"), "Pin hides latest available version"); Assert.IsTrue(result.StdOut.Contains("package(s) have pins that prevent upgrade")); result = TestCommon.RunAICLICommand("upgrade", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_PACKAGE_IS_PINNED, result.ExitCode, "No upgrades available due to pin"); } /// /// Tests upgrading a package when it has a pinning pin and the --force flag is used. /// [Test] public void ForceUpgradeWithPinningPin() { RunCommandResult result; string installDir = Path.GetTempPath(); result = TestCommon.RunAICLICommand("pin add", "AppInstallerTest.TestExeInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); result = TestCommon.RunAICLICommand("upgrade", "--force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "--force argument shows latest version"); result = TestCommon.RunAICLICommand("upgrade", "--all --force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/Version 2.0.0.0"), "--force argument installs last version despite pin"); } /// /// Tests upgrading a package when it has a gating pin and the --force flag is used. /// [Test] public void ForceUpgradeWithGatingPin() { RunCommandResult result; string installDir = Path.GetTempPath(); var pinResult = TestCommon.RunAICLICommand("pin add", "AppInstallerTest.TestExeInstaller --version 1.0.*"); Assert.AreEqual(Constants.ErrorCode.S_OK, pinResult.ExitCode); result = TestCommon.RunAICLICommand("upgrade", "--force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "--force argument shows latest version"); result = TestCommon.RunAICLICommand("upgrade", "--all --force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode, "Upgrade succeeds"); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/Version 2.0.0.0")); } /// /// Tests upgrading a package when it has a blocking pin and the --force flag is used. /// [Test] public void ForceUpgradeWithBlockingPin() { RunCommandResult result; string installDir = Path.GetTempPath(); var pinResult = TestCommon.RunAICLICommand("pin add", "AppInstallerTest.TestExeInstaller --blocking"); Assert.AreEqual(Constants.ErrorCode.S_OK, pinResult.ExitCode); result = TestCommon.RunAICLICommand("upgrade", "--force"); Assert.IsTrue(result.StdOut.Contains("2.0.0.0"), "--force argument shows latest version"); result = TestCommon.RunAICLICommand("upgrade", "--all --force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode, "Upgrade succeeds"); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/Version 2.0.0.0")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/PowerShell/PowerShellHost.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.PowerShell { using System; using System.Collections; using System.Management.Automation; using System.Management.Automation.Runspaces; using Microsoft.PowerShell; using NUnit.Framework; /// /// Helper class to run powershell commands. /// internal class PowerShellHost : IDisposable { private readonly Runspace runspace = null; private bool disposed = false; /// /// Initializes a new instance of the class. /// public PowerShellHost() { InitialSessionState initialSessionState = InitialSessionState.CreateDefault(); initialSessionState.ExecutionPolicy = ExecutionPolicy.Unrestricted; this.runspace = RunspaceFactory.CreateRunspace(initialSessionState); this.runspace.Open(); this.VerifyErrorState(); this.PowerShell = PowerShell.Create(this.runspace); } /// /// Finalizes an instance of the class. /// ~PowerShellHost() => this.Dispose(false); /// /// Gets PowerShell. /// public PowerShell PowerShell { get; private set; } = null; /// /// Dispose. /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } /// /// Add module path. /// /// Path. public void AddModulePath(string path) { var newModulePath = this.PowerShell.Runspace.SessionStateProxy.PSVariable.GetValue("env:PSModulePath") + $";{path}"; this.PowerShell.Runspace.SessionStateProxy.PSVariable.Set("env:PSModulePath", newModulePath); } /// /// Protected implementation of dispose pattern. /// /// Dispose. protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { this.PowerShell.Dispose(); this.runspace.Dispose(); } this.disposed = true; } } /// /// The most common error is that the module was not found. /// private void VerifyErrorState() { var errors = (ArrayList)this.runspace.SessionStateProxy.PSVariable.GetValue("Error"); if (errors.Count > 0) { string errorMessage = "PSVariable Error:"; foreach (var error in errors) { errorMessage += Environment.NewLine + ((ErrorRecord)error).Exception.Message; } TestContext.Error.WriteLine(errorMessage); throw new Exception(errorMessage); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Properties/AssemblyInfo.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.17763")] ================================================ FILE: src/AppInstallerCLIE2ETests/README.md ================================================ # How to Run End-To-End Tests for Windows Package Manager Client Most of the tests require having the local test source added into winget. The local test source must be hosted in a localhost web server. ## Run locally The E2E tests are built on the nunit testing framework and can be configured with a Test.runsettings file. The project has its own default parameters but typically you will want to expand it by modifying **src\AppInstallerCLIE2ETests\Test.runsettings** with the parameters that you want and set it up by opening **Test Explorer - Settings - Configure Run Settings point to file: src\AppInstallerCLIE2ETests\Test.runsettings** If your tests uses the test source see the [LocalhostWebServer](#LocalhostWebServer) and [WinGetSourceCreator](#WinGetSourceCreator) sections. ### Run settings. |Parameter| Description | |--|--| | PackagedContext | Indicates if the test should be run under packaged context | | VerboseLogging | Turn on/off verbose logging in the test result | | AICLIPath | The AICLI executable under test. If using loose file registration and using Invoke-Command when AppExecutionAlias is not available, this will be relative path to package root. | | AICLIPackagePath | Used in packaged context. Path to the package containing executable under test. If LooseFileRegistration is true, this should be path to unpackaged root. | | LooseFileRegistration | Bool to set if loose file registration should be used. | | InvokeCommandInDesktopPackage | Bool to indicate using Invoke-CommandInDesktopPackage for test execution. This is used when AppExecutionAlias is not available, or disabled. | | StaticFileRootPath | Path to the set of static test files that will be served as the source for testing purposes. This path should be identical to the one provided to the LocalHostWebServer| | MsixTestInstallerPath | The MSIX (or APPX) Installer executable under test. | | ExeTestInstallerPath |The Exe Installer executable under test. | | MsiTestInstallerPath | The MSI Installer executable under test. | | PackageCertificatePath | Signing Certificate Path used to certify test index source package | | PowerShellModulePath | Path to the PowerShell module manifest file under test | | PowerShellModulePath | The local server cert file | | SkipTestSource | I solemnly swear the test won't use the local test source or the source is already set up. | #### Example of Test.runsettings with completed parameters: Assuming you clone winget-cli in c:\dev, the localhost web server is running in c:\dev\TestLocalIndex (without creating the test source) and you built x64 Debug, this should cover most of the tests. The easiest way to generate AppInstallerTestMsixInstaller.msix is by running makeappx pack for c:\dev\winget-cli\src\AppInstallerTestMsixInstaller\bin\x64\Debug. #### Log Files After running the E2E Tests, the logs can be found in the following paths: - **%LOCALAPPDATA%\Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\DiagOutputDir** - **%LOCALAPPDATA%\E2ETestLogs** - **%TEMP%\WinGet\defaultState** ## LocalhostWebServer The src\Tool\LocalhostWebServer project generates an executable that serves static test files from a given directory path through a HTTPS local loopback server in order to maintain a closed and controlled repository for resources used for testing purposes. ### Start localhost web server The localhost web server needs to be running for the duration of the tests. The easiest way to run it is to use src\Tool\LocalhostWebServer\Run-LocalhostWebServer.ps1 in a different PowerShell session. |Parameter | Type | Description | |--|--|--| | **BuildRoot** | Mandatory | The output path of the LocalhostWebServer project. Normally something like \src\\\LocalhostWebServer | **StaticFileRoot** | Mandatory | Path to serve static root directory. If the directory path does not exist, a new directory will be created for you. | | **CertPath** | Mandatory | Path to HTTPS Developer Certificate. A self signed developer certificate will need to be created in order to verify localhost https. | | **CertPassword** | Mandatory | HTTPS Developer Certificate Password | | **Port** | Optional | Port number [Default Port Number: 5001] | | **OutCertFile** | Optional | The exported certificate used | | **LocalSourceJson** | Optional | The local source definition. If set generates the source. | | **ForcedExperimentalFeatures** | Optional | Experimental features that should be forcibly enabled always. | ### How to create and trust an ASP.NET Core HTTPS Development Certificate Windows Package Manager Client (WinGet.exe) requires new sources added to the WinGet repositories be securely accessed through HTTPS. Therefore, in order to verify the LocalhostWebServer, you will need to create a self-signed development certificate to verify the localhost address. - Open command prompt in administrator mode - Run **dotnet dev-certs https --trust** in the command line - Open up **certmgr** (search Manage User Certificates in Windows search bar) - Locate the newly created localhost certificate in the Personal/Certificates folder with a friendly name of "ASP.NET Core HTTPS development certificate" - Right click on the certificate --> All Tasks --> Export.. - Click Yes to export the private key - Export file using Personal Information Exchange (.pfx) file format - Create and confirm password using SHA256 encryption (any password will work, just make sure to remember it for later) - Save HTTPS development certificate and refer its certificate path and password when launching the Localhost Webserver ## WinGetSourceCreator The src\WinGetSourceCreator is a project that helps generate a new winget source for local development. It is consumed by IndexCreationTool, LocalhostWebServer and AppInstallerCLIE2ETests projects. It supports: - Prepare installers by signing them and placing then in the working directory and computing their hashes. For msix, signature hash is also supported. - Generate zip installers. - Generate the signed source.msix and index.db. LocalSource is the object that contains the definition of the source. A json serialized version of it is the input for IndexCreationTool and LocalhostWebServer. Example: ``` { # If running E2E this is must be the StaticFileRoot used for the localhost web server "WorkingDirectory": "c:/dev/temp/TestLocalIndex", # The appx manifest to generate the source.msix file. "AppxManifest": "c:/dev/winget-cli/src/AppInstallerCLIE2ETests/TestData/Package/AppxManifest.xml", # A list of directories or files to copy. If a directory, it copies all the *.yaml files preserving subdirectories. "LocalManifests": [ "c:/dev/winget-cli/src/x86/Release/AppInstallerCLIE2ETests/TestData/Manifests" ], # The signature to use. "Signature": { "CertFile": "cert.pfx", "Password": "1324", # If set it will modify the Package Identity Publisher in the AppxManifest.xml "Publisher": "CN:ThousandSunny" } # Installers that are already present in the machine, by default the installers will be signed using their Signature # property if set or the top level one. "LocalInstallers": [ { "Type": "exe", "Input": "c:/dev/winget-cli/src/x64/Debug/AppInstallerTestExeInstaller\AppInstallerTestExeInstaller.exe", # Name of the installer to be copied and signed if needed. "Name": "AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe", # The token in the manifests for this installer. This will be replaces at copy manifests time. "HashToken": "" # Overrides top level one. "Signature": { "CertFile": "cert2.pfx", "Password": "2345", } }, { "Type": "msi", "Input": "c:/dev/winget-cli/src/AppInstallerCLIE2ETests/TestData/AppInstallerTestMsiInstaller.msi", "Name": "AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.msi", "HashToken": "" # Don't sign this. "SkipSignature": true }, { "Type": "msix", "Input": "D:/dev/temp/AppInstallerTestMsixInstaller.msix", "Name": "AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.msix", "HashToken": "", # Only supported by where type is msix. Package must be signed, either already signed or signed when copied. "SignatureToken": "", } ], # These are installers that are generated on the go. Currently only zip is supported. "DynamicInstallers": [ { # Zip installers are never signed. "Type": "zip", # List of files to zip. Does not preserve subdirectories. "Input": [ "D:/dev/temp/TestLocalIndex/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe", "D:/dev/temp/TestLocalIndex/AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.msi", "D:/dev/temp/TestLocalIndex/AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.msix" ], "Name": "AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip", "HashToken": "" } ], } ``` ================================================ FILE: src/AppInstallerCLIE2ETests/RepairCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test Repair command. /// public class RepairCommand : BaseCommand { /// /// One time setup. /// [OneTimeSetUp] public void OneTimeSetup() { // Try clean up AppInstallerTest.TestMsiInstaller for failure cases where cleanup is not successful TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestMsiInstaller"); } /// /// Test MSI installer repair. /// [Test] public void RepairMSIInstaller() { if (string.IsNullOrEmpty(TestIndex.MsiInstallerV2)) { Assert.Ignore("MSI installer not available"); } var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMsiRepair --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); // Note: The 'msiexec repair' command requires the original installer file to be present at the location registered in the ARP (Add/Remove Programs). // In our test scenario, the MSI installer file is initially placed in a temporary location and then deleted, which can cause the repair operation to fail. // To work around this, we copy the installer file to the ARP source directory before running the repair command. // A more permanent solution would be to modify the MSI installer to cache the installer file in a known location and register that location as the installer source. // This would allow the 'msiexec repair' command to function as expected. string installerSourceDir = TestCommon.CopyInstallerFileToARPInstallSourceDirectory(TestCommon.GetTestDataFile("AppInstallerTestMsiInstallerV2.msi"), Constants.MsiInstallerProductCode, true); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestMsiRepair"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Repair operation completed successfully")); Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(installDir)); if (installerSourceDir != null && Directory.Exists(installerSourceDir)) { Directory.Delete(installerSourceDir, true); } } /// /// Test MSIX non-store package repair. /// [Test] public void RepairNonStoreMSIXPackage() { // install a test msix package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMsixInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestMsixInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Repair operation completed successfully")); Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); } /// /// Test MSIX non-store package repair with machine scope. /// [Test] public void RepairNonStoreMsixPackageWithMachineScope() { // Selecting Microsoft.Paint_8wekyb3d8bbwe because it's a system package suitable for this scenario. // First, we need to ensure this package is installed, otherwise, we skip the test. var result = TestCommon.RunAICLICommand("list", "Microsoft.Paint_8wekyb3d8bbwe"); if (result.ExitCode != Constants.ErrorCode.S_OK) { Assert.Ignore("Test skipped as Microsoft.Paint_8wekyb3d8bbwe is not installed."); } Assert.True(result.StdOut.Contains("Microsoft.Paint")); result = TestCommon.RunAICLICommand("repair", "Microsoft.Paint_8wekyb3d8bbwe --scope machine"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_SYSTEM_NOT_SUPPORTED, result.ExitCode); Assert.True(result.StdOut.Contains("The current system configuration does not support the repair of this package.")); } /// /// Test repair of a Burn installer that has a "modify" repair behavior specified in the manifest. /// [Test] public void RepairBurnInstallerWithModifyBehavior() { // install a test burn package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestModifyRepair -v 2.0.0.0 --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestModifyRepair"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Repair operation completed successfully")); Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(installDir, "Modify Repair operation")); } /// /// Tests the repair operation of a Burn installer that was installed in user scope but is being repaired in an admin context. /// [Test] public void RepairBurnInstallerInAdminContextWithUserScopeInstall() { // install a test burn package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestUserScopeInstallRepairInAdminContext -v 2.0.0.0 --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestUserScopeInstallRepairInAdminContext"); Assert.AreEqual(Constants.ErrorCode.ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED, result.ExitCode); Assert.True(result.StdOut.Contains("The package installed for user scope cannot be repaired when running with administrator privileges.")); TestCommon.CleanupTestExeAndDirectory(installDir); } /// /// Tests the repair operation of a Burn installer that lacks a repair behavior. /// [Test] public void RepairBurnInstallerMissingRepairBehavior() { // install a test burn package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMissingRepairBehavior -v 2.0.0.0 --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestMissingRepairBehavior"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_REPAIR_INFO_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("The repair command for this package cannot be found. Please reach out to the package publisher for support.")); TestCommon.CleanupTestExeAndDirectory(installDir); } /// /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest and NoModify ARP flag set. /// [Test] public void RepairBurnInstallerWithWithModifyBehaviorAndNoModifyFlag() { // install a test Exe package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestModifyRepairWithNoModify -v 2.0.0.0 --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestModifyRepairWithNoModify"); Assert.AreEqual(Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED, result.ExitCode); Assert.True(result.StdOut.Contains("The installer technology in use does not support repair.")); TestCommon.CleanupTestExeAndDirectory(installDir); } /// /// Tests the scenario where the repair operation is not supported for Portable Installer type. /// [Test] public void RepairOperationNotSupportedForPortableInstaller() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestPortableExe"); Assert.AreEqual(Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED, result.ExitCode); Assert.True(result.StdOut.Contains("The installer technology in use does not support repair.")); // If no location specified, default behavior is to create a package directory with the name "{packageId}_{sourceId}" TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest. /// [Test] public void RepairExeInstallerWithUninstallerBehavior() { // install a test Exe package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.UninstallerRepair -v 2.0.0.0 --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.UninstallerRepair"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Repair operation completed successfully")); Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(installDir, "Uninstaller Repair operation")); } /// /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest and NoRepair ARP flag set. /// [Test] public void RepairExeInstallerWithUninstallerBehaviorAndNoRepairFlag() { // install a test Exe package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.UninstallerRepairWithNoRepair -v 2.0.0.0 --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.UninstallerRepairWithNoRepair"); Assert.AreEqual(Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED, result.ExitCode); Assert.True(result.StdOut.Contains("The installer technology in use does not support repair.")); TestCommon.CleanupTestExeAndDirectory(installDir); } /// /// Test repair of a Nullsoft installer that has a "uninstaller" repair behavior specified in the manifest. /// [Test] public void RepairNullsoftInstallerWithUninstallerBehavior() { // install a test Nullsoft package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.UninstallerRepair -v 2.0.0.0 --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.UninstallerRepair"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Repair operation completed successfully")); Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(installDir, "Uninstaller Repair operation")); } /// /// Test repair of a Inno installer that has a "installer" repair behavior specified in the manifest. /// [Test] public void RepairInnoInstallerWithInstallerBehavior() { // install a test Inno package from TestSource and then repair it. var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestInstallerRepair -v 2.0.0.0 --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestInstallerRepair"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Repair operation completed successfully")); Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(installDir, "Installer Repair operation")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ResumeCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using System.Linq; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test download command. /// public class ResumeCommand : BaseCommand { /// /// One time setup. /// [OneTimeSetUp] public void OneTimeSetup() { WinGetSettingsHelper.ConfigureFeature("resume", true); } /// /// One time teardown. /// [OneTimeTearDown] public void OneTimeTearDown() { WinGetSettingsHelper.ConfigureFeature("resume", false); } /// /// Installs a test exe installer and verifies that the checkpoint index is cleaned up. /// [Test] public void InstallExe_VerifyIndexDoesNotExist() { var checkpointsDir = TestCommon.GetCheckpointsDirectory(); // If the checkpoints directory does not yet exist, set to 0. The directory should be created when the command is invoked. int initialCheckpointsCount = Directory.Exists(checkpointsDir) ? Directory.GetFiles(checkpointsDir).Length : 0; var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); int actualCheckpointsCount = Directory.GetFiles(checkpointsDir).Length; // The checkpoints count should not change as the index file should be cleaned up after a successful install. Assert.AreEqual(initialCheckpointsCount, actualCheckpointsCount); } /// /// Verifies that an error message is shown when a resume id does not exist. /// [Test] public void ResumeIdNotFound() { var resumeResult = TestCommon.RunAICLICommand("resume", "-g invalidResumeId"); Assert.AreEqual(Constants.ErrorCode.ERROR_RESUME_ID_NOT_FOUND, resumeResult.ExitCode); } /// /// Test install a package that returns REBOOT_REQUIRED_TO_FINISH and verify that the checkpoint database is properly cleaned up. /// [Test] public void InstallRequiresRebootToFinish() { var checkpointsDir = TestCommon.GetCheckpointsDirectory(); int initialCheckpointsCount = Directory.Exists(checkpointsDir) ? Directory.GetDirectories(checkpointsDir).Length : 0; var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"TestRebootRequired --custom \"/ExitCode 9\" -l {installDir}"); // REBOOT_REQUIRED_TO_FINISH is treated as a success. Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Restart your PC to finish installation.")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); int actualCheckpointsCount = Directory.GetDirectories(checkpointsDir).Length; // Checkpoint database should be cleaned up since resume is not needed to complete installation. Assert.AreEqual(initialCheckpointsCount, actualCheckpointsCount); } /// /// Test install a package that returns REBOOT_REQUIRED_FOR_INSTALL and verify that resume command can be called successfully. /// [Test] public void InstallRequiresRebootForInstall() { var checkpointsDir = TestCommon.GetCheckpointsDirectory(); int initialCheckpointsCount = Directory.Exists(checkpointsDir) ? Directory.GetDirectories(checkpointsDir).Length : 0; var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"TestRebootRequired --custom \"/ExitCode 10\" -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL, result.ExitCode); Assert.True(result.StdOut.Contains("Your PC will restart to finish installation.")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); int actualCheckpointsCount = Directory.GetDirectories(checkpointsDir).Length; Assert.AreEqual(initialCheckpointsCount + 1, actualCheckpointsCount); var checkpointsDirectoryInfo = new DirectoryInfo(checkpointsDir); var checkpoint = checkpointsDirectoryInfo.GetDirectories() .OrderByDescending(f => f.LastWriteTime) .First(); // Resume output should be the same as the install result. var resumeResult = TestCommon.RunAICLICommand("resume", $"-g {checkpoint.Name}"); Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL, resumeResult.ExitCode); Assert.True(resumeResult.StdOut.Contains("Your PC will restart to finish installation.")); Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/RunCommandException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System; using static AppInstallerCLIE2ETests.Helpers.TestCommon; /// /// An exception that occurred when running a command. /// internal class RunCommandException : Exception { /// /// Initializes a new instance of the class. /// /// The file name of the command. /// The arguments for the command. /// The `RunCommand` result. public RunCommandException(string fileName, string args, RunCommandResult result) : base($"Command `{fileName} {args}` failed with: {result.ExitCode}\nOut: {result.StdOut}\nErr: {result.StdErr}") { this.FileName = fileName; this.Arguments = args; this.Result = result; } /// /// Gets or initializes the file name. /// public string FileName { get; private init; } /// /// Gets or initializes the arguments. /// public string Arguments { get; private init; } /// /// Gets or initializes the result object. /// public RunCommandResult Result { get; private init; } } } ================================================ FILE: src/AppInstallerCLIE2ETests/SearchCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test search command. /// public class SearchCommand : BaseCommand { /// /// Test search without args. /// [Test] public void SearchWithoutArgs() { var result = TestCommon.RunAICLICommand("search", string.Empty); Assert.AreEqual(Constants.ErrorCode.ERROR_INVALID_CL_ARGUMENTS, result.ExitCode); } /// /// Test search with query. /// [Test] public void SearchQuery() { var result = TestCommon.RunAICLICommand("search", "TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test search with alias. /// public void SearchUsingAlias() { var result = TestCommon.RunAICLICommand("find", "TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test search with name. /// [Test] public void SearchWithName() { var result = TestCommon.RunAICLICommand("search", "--name testexampleinstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test search with Id. /// [Test] public void SearchWithID() { var result = TestCommon.RunAICLICommand("search", "--id appinstallertest.testexampleinstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test search with invalid name. /// [Test] public void SearchWithInvalidName() { var result = TestCommon.RunAICLICommand("search", "--name InvalidName"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("No package found matching input criteria.")); } /// /// Test search where it returns multiple results. /// [Test] public void SearchReturnsMultiple() { // Search Microsoft should return multiple var result = TestCommon.RunAICLICommand("search", "AppInstallerTest"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExeInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestBurnInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test search with exact name. /// [Test] public void SearchWithExactName() { var result = TestCommon.RunAICLICommand("search", "--exact TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test search with exact ID. /// [Test] public void SearchWithExactID() { var result = TestCommon.RunAICLICommand("search", "--exact AppInstallerTest.TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test search with exact case-sensitive. /// [Test] public void SearchWithExactArgCaseSensitivity() { var result = TestCommon.RunAICLICommand("search", "--exact testexampleinstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("No package found matching input criteria.")); } /// /// Test search with a failed source. /// [Test] public void SearchWithSingleSourceFailure() { TestCommon.RunAICLICommand("source add", "failSearch \"{ \"\"OpenHR\"\": \"\"0x80070002\"\" }\" Microsoft.Test.Configurable --header \"{}\""); try { var result = TestCommon.RunAICLICommand("search", "--exact AppInstallerTest.TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Failed when searching source; results will not be included: failSearch")); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } finally { this.ResetTestSource(); } } /// /// Test search with bad pin. /// [Test] public void SearchStoreWithBadPin() { // Configure as close as possible to the real chain but use the test cert for everything // This will at least force the public key to be checked rather than simply failing based on chain length GroupPolicyHelper.EnableAdditionalSources.SetEnabledList(new GroupPolicyHelper.GroupPolicySource[] { new GroupPolicyHelper.GroupPolicySource { Name = Constants.TestAlternateSourceName, Arg = Constants.DefaultMSStoreSourceUrl, Type = Constants.DefaultMSStoreSourceType, Data = string.Empty, Identifier = Constants.DefaultMSStoreSourceIdentifier, CertificatePinning = new GroupPolicyHelper.GroupPolicyCertificatePinning { Chains = new GroupPolicyHelper.GroupPolicyCertificatePinningChain[] { new GroupPolicyHelper.GroupPolicyCertificatePinningChain { Chain = new GroupPolicyHelper.GroupPolicyCertificatePinningDetails[] { new GroupPolicyHelper.GroupPolicyCertificatePinningDetails { Validation = new string[] { "publickey" }, EmbeddedCertificate = TestCommon.GetTestServerCertificateHexString(), }, new GroupPolicyHelper.GroupPolicyCertificatePinningDetails { Validation = new string[] { "subject", "issuer" }, EmbeddedCertificate = TestCommon.GetTestServerCertificateHexString(), }, new GroupPolicyHelper.GroupPolicyCertificatePinningDetails { Validation = new string[] { "subject", "issuer" }, EmbeddedCertificate = TestCommon.GetTestServerCertificateHexString(), }, }, }, }, }, TrustLevel = new string[] { "None" }, Explicit = false, }, }); try { var result = TestCommon.RunAICLICommand("search", $"-s {Constants.TestAlternateSourceName} foo --verbose-logs"); Assert.AreEqual(Constants.ErrorCode.ERROR_PINNED_CERTIFICATE_MISMATCH, result.ExitCode); } finally { this.ResetTestSource(); } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/SetUpFixture.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using Microsoft.Win32; using NUnit.Framework; /// /// Set up fixture. /// [SetUpFixture] public class SetUpFixture { private static bool shouldDisableDevModeOnExit = true; private static bool shouldRevertDefaultFileTypeRiskOnExit = true; private static bool shouldDoAnyTeardown = true; private static string defaultFileTypes = string.Empty; /// /// Set up. /// [OneTimeSetUp] public void Setup() { var testParams = TestSetup.Parameters; if (testParams.IsDefault) { // If no parameters are provided, use defaults that work locally. // This allows the user to assume responsibility for setup. shouldDoAnyTeardown = false; } else { shouldDisableDevModeOnExit = this.EnableDevMode(true); shouldRevertDefaultFileTypeRiskOnExit = this.DecreaseFileTypeRisk(".exe;.msi", false); if (testParams.PackagedContext) { if (testParams.LooseFileRegistration) { Assert.True(TestCommon.InstallMsixRegister(testParams.AICLIPackagePath), $"InstallMsixRegister : {testParams.AICLIPackagePath}"); } else { Assert.True(TestCommon.InstallMsix(testParams.AICLIPackagePath), $"InstallMsix : {testParams.AICLIPackagePath}"); } } } if (!testParams.SkipTestSource) { TestIndex.GenerateE2ESource(); } WinGetSettingsHelper.ForcedExperimentalFeatures = testParams.ForcedExperimentalFeatures; WinGetSettingsHelper.InitializeWingetSettings(); } /// /// Tear down. /// [OneTimeTearDown] public void TearDown() { if (shouldDoAnyTeardown) { if (shouldDisableDevModeOnExit) { this.EnableDevMode(false); } if (shouldRevertDefaultFileTypeRiskOnExit) { this.DecreaseFileTypeRisk(defaultFileTypes, true); } TestCommon.PublishE2ETestLogs(); if (TestSetup.Parameters.PackagedContext) { TestCommon.RemoveMsix(Constants.AICLIPackageName); } } } // Returns whether there's a change to the dev mode state after execution private bool EnableDevMode(bool enable) { var appModelUnlockKey = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"); if (enable) { var value = appModelUnlockKey.GetValue("AllowDevelopmentWithoutDevLicense"); if (value == null || (int)value == 0) { appModelUnlockKey.SetValue("AllowDevelopmentWithoutDevLicense", 1, RegistryValueKind.DWord); return true; } } else { var value = appModelUnlockKey.GetValue("AllowDevelopmentWithoutDevLicense"); if (value != null && ((int)value) != 0) { appModelUnlockKey.SetValue("AllowDevelopmentWithoutDevLicense", 0, RegistryValueKind.DWord); return true; } } return false; } private bool DecreaseFileTypeRisk(string fileTypes, bool revert) { var defaultFileTypeRiskKey = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Associations"); string value = (string)defaultFileTypeRiskKey.GetValue("DefaultFileTypeRisk"); if (revert) { defaultFileTypeRiskKey.SetValue("LowRiskFileTypes", fileTypes); return false; } else { if (string.IsNullOrEmpty(value)) { defaultFileTypes = string.Empty; defaultFileTypeRiskKey.SetValue("LowRiskFileTypes", fileTypes); } else { defaultFileTypes = value; defaultFileTypeRiskKey.SetValue("LowRiskFileTypes", string.Concat(value, fileTypes)); } return true; } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ShowCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test show command. /// public class ShowCommand : BaseCommand { /// /// Test show with no args. /// [Test] public void ShowWithNoArgs() { var result = TestCommon.RunAICLICommand("show", string.Empty); Assert.AreEqual(Constants.ErrorCode.ERROR_INVALID_CL_ARGUMENTS, result.ExitCode); } /// /// Test show no match. /// [Test] public void ShowWithNoMatches() { // Show with 0 search match shows a "please refine input" var result = TestCommon.RunAICLICommand("show", $"DoesNotExist"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("No package found matching input criteria.")); } /// /// Test show with substring match. /// [Test] public void ShowWithSubstringMatch() { // Show with a substring match still returns 0 results var result = TestCommon.RunAICLICommand("show", $"AppInstallerTest"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("No package found matching input criteria.")); } /// /// Test show with name match. /// [Test] public void ShowWithNameMatch() { var result = TestCommon.RunAICLICommand("show", $"--name testexampleinstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Found TestExampleInstaller [AppInstallerTest.TestExampleInstaller]")); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test show with id match. /// [Test] public void ShowWithIDMatch() { var result = TestCommon.RunAICLICommand("show", $"--id appinstallertest.testexampleinstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Found TestExampleInstaller [AppInstallerTest.TestExampleInstaller]")); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test show versions. /// [Test] public void ShowWithVersions() { // Show with --versions list the versions var result = TestCommon.RunAICLICommand("show", $"TestExampleInstaller --versions"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); Assert.True(result.StdOut.Contains("1.2.3.4")); } /// /// Test show with exact match name. /// [Test] public void ShowWithExactName() { var result = TestCommon.RunAICLICommand("show", $"--exact TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Found TestExampleInstaller [AppInstallerTest.TestExampleInstaller]")); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test show with exact id. /// [Test] public void ShowWithExactID() { var result = TestCommon.RunAICLICommand("show", $"--exact AppInstallerTest.TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Found TestExampleInstaller [AppInstallerTest.TestExampleInstaller]")); Assert.True(result.StdOut.Contains("TestExampleInstaller")); Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); } /// /// Test show with exact args. /// [Test] public void ShowWithExactArgCaseSensitivity() { var result = TestCommon.RunAICLICommand("show", $"--exact testexampleinstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("No package found matching input criteria.")); } /// /// Test show with installer type. /// [Test] public void ShowWithInstallerTypeArg() { var result = TestCommon.RunAICLICommand("show", $"--id AppInstallerTest.TestMultipleInstallers --installer-type msi"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Found TestMultipleInstallers [AppInstallerTest.TestMultipleInstallers]")); Assert.True(result.StdOut.Contains("Installer Type: msi")); } /// /// Test show with an archive installer type. /// [Test] public void ShowWithZipInstallerTypeArg() { var result = TestCommon.RunAICLICommand("show", $"--id AppInstallerTest.TestMultipleInstallers --installer-type zip"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Found TestMultipleInstallers [AppInstallerTest.TestMultipleInstallers]")); Assert.True(result.StdOut.Contains("Installer Type: exe (zip)")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/SourceCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test source command. /// public class SourceCommand : BaseCommand { /// /// One time set up. /// [OneTimeSetUp] public void OneTimeSetup() { WinGetSettingsHelper.ConfigureFeature("sourcePriority", true); } /// /// Test set up. /// [SetUp] public void Setup() { this.ResetTestSource(false); } /// /// Test source add. /// [Test] public void SourceAdd() { // TODO: Our test source package is being rejected by SmartScreen on the build server. // Reenable when SmartScreen issue is solved or removed. Assert.Ignore(); var result = TestCommon.RunAICLICommand("source add", $"SourceTest {Constants.TestSourceUrl}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Done")); TestCommon.RunAICLICommand("source remove", $"-n SourceTest"); } /// /// Test source add with trust level. /// [Test] public void SourceAddWithTrustLevel() { // Remove the test source. TestCommon.RunAICLICommand("source remove", Constants.TestSourceName); var result = TestCommon.RunAICLICommand("source add", $"SourceTest {Constants.TestSourceUrl} --trust-level trusted"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Done")); var listResult = TestCommon.RunAICLICommand("source list", $"-n SourceTest"); Assert.AreEqual(Constants.ErrorCode.S_OK, listResult.ExitCode); Assert.True(listResult.StdOut.Contains("Trust Level")); Assert.True(listResult.StdOut.Contains("Trusted")); TestCommon.RunAICLICommand("source remove", $"-n SourceTest"); } /// /// Test source add with store origin trust level. /// [Test] public void SourceAddWithStoreOriginTrustLevel() { // Remove the test source. TestCommon.RunAICLICommand("source remove", Constants.TestSourceName); var result = TestCommon.RunAICLICommand("source add", $"SourceTest {Constants.TestSourceUrl} --trust-level storeOrigin"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_DATA_INTEGRITY_FAILURE, result.ExitCode); Assert.True(result.StdOut.Contains("The source data is corrupted or tampered")); } /// /// Test source add with explicit flag. Packages should only appear if the source is explicitly declared. /// [Test] public void SourceAddWithExplicit() { // Remove the test source. TestCommon.RunAICLICommand("source remove", Constants.TestSourceName); var result = TestCommon.RunAICLICommand("source add", $"SourceTest {Constants.TestSourceUrl} --trust-level trusted --explicit"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Done")); var searchResult = TestCommon.RunAICLICommand("search", "TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_SOURCES_DEFINED, searchResult.ExitCode); Assert.True(searchResult.StdOut.Contains("No sources defined; add one with 'source add' or reset to defaults with 'source reset'")); var searchResult2 = TestCommon.RunAICLICommand("search", "TestExampleInstaller --source SourceTest"); Assert.AreEqual(Constants.ErrorCode.S_OK, searchResult2.ExitCode); Assert.True(searchResult2.StdOut.Contains("TestExampleInstaller")); Assert.True(searchResult2.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); TestCommon.RunAICLICommand("source remove", $"-n SourceTest"); } /// /// Test source add with a priority value. /// [Test] public void SourceAddWithPriority() { // Remove the test source. TestCommon.RunAICLICommand("source remove", Constants.TestSourceName); var result = TestCommon.RunAICLICommand("source add", $"SourceTest {Constants.TestSourceUrl} --priority 42"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Done")); var listResult = TestCommon.RunAICLICommand("source list", "SourceTest"); Assert.AreEqual(Constants.ErrorCode.S_OK, listResult.ExitCode); Assert.True(listResult.StdOut.Contains("42")); var exportResult = TestCommon.RunAICLICommand("source export", string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, listResult.ExitCode); Assert.True(exportResult.StdOut.Contains("42")); } /// /// Test source add with duplicate name. /// [Test] public void SourceAddWithDuplicateName() { // Add source with duplicate name should fail var result = TestCommon.RunAICLICommand("source add", $"{Constants.TestSourceName} https://microsoft.com"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_ALREADY_EXISTS, result.ExitCode); Assert.True(result.StdOut.Contains("A source with the given name already exists and refers to a different location")); } /// /// Test source add with duplicate source url. /// [Test] public void SourceAddWithDuplicateSourceUrl() { // Add source with duplicate url should fail var result = TestCommon.RunAICLICommand("source add", $"TestSource2 {Constants.TestSourceUrl}"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_ARG_ALREADY_EXISTS, result.ExitCode); Assert.True(result.StdOut.Contains("A source with a different name already refers to this location")); } /// /// Test source add with invalid url. /// [Test] public void SourceAddWithInvalidURL() { // Add source with invalid url should fail var result = TestCommon.RunAICLICommand("source add", $"AnotherSource {Constants.TestSourceUrl}/Invalid/Directory/Dont/Add/Me"); Assert.AreEqual(Constants.ErrorCode.HTTP_E_STATUS_NOT_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("An unexpected error occurred while executing the command")); } /// /// Test source add with http url. /// [Test] public void SourceAddWithHttpURL() { // Add source with an HTTP url should fail var result = TestCommon.RunAICLICommand("source add", "Insecure http://microsoft.com"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NOT_SECURE, result.ExitCode); Assert.True(result.StdOut.Contains("error occurred while executing the command")); } /// /// Test source list with no args. /// [Test] public void SourceListWithNoArgs() { // List with no args should list all available sources var result = TestCommon.RunAICLICommand("source list", string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.TestSourceUrl)); } /// /// Test source list with name. /// [Test] public void SourceListWithName() { var result = TestCommon.RunAICLICommand("source list", $"-n {Constants.TestSourceName}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains(Constants.TestSourceName)); Assert.True(result.StdOut.Contains(Constants.TestSourceUrl)); Assert.True(result.StdOut.Contains("Microsoft.PreIndexed.Package")); Assert.True(result.StdOut.Contains("Trust Level")); Assert.True(result.StdOut.Contains("Updated")); } /// /// Test source list name mismatch. /// [Test] public void SourceListNameMismatch() { var result = TestCommon.RunAICLICommand("source list", "-n UnknownName"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); Assert.True(result.StdOut.Contains("Did not find a source named")); } /// /// Test source update. /// [Test] public void SourceUpdate() { var result = TestCommon.RunAICLICommand("source update", $"-n {Constants.TestSourceName}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Done")); } /// /// Test source update with invalid name. /// [Test] public void SourceUpdateWithInvalidName() { var result = TestCommon.RunAICLICommand("source update", "-n UnknownName"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); Assert.True(result.StdOut.Contains("Did not find a source named: UnknownName")); } /// /// Test source remove by name. /// [Test] public void SourceRemoveValidName() { var result = TestCommon.RunAICLICommand("source remove", $"-n {Constants.TestSourceName}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Done")); this.ResetTestSource(false); } /// /// Test source remove with invalid name. /// [Test] public void SourceRemoveInvalidName() { var result = TestCommon.RunAICLICommand("source remove", "-n UnknownName"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode); Assert.True(result.StdOut.Contains("Did not find a source named: UnknownName")); } /// /// Test source reset. /// [Test] public void SourceReset() { var result = TestCommon.RunAICLICommand("source reset", string.Empty); Assert.True(result.StdOut.Contains("The following sources will be reset if the --force option is given:")); Assert.True(result.StdOut.Contains(Constants.TestSourceName)); Assert.True(result.StdOut.Contains(Constants.TestSourceUrl)); } /// /// Test source reset force. /// [Test] public void SourceForceReset() { // Force Reset Sources var result = TestCommon.RunAICLICommand("source reset", "--force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Resetting all sources...Done")); // Verify sources have been reset result = TestCommon.RunAICLICommand("source list", string.Empty); Assert.True(result.StdOut.Contains("winget")); Assert.True(result.StdOut.Contains("https://cdn.winget.microsoft.com/cache")); Assert.False(result.StdOut.Contains(Constants.TestSourceName)); Assert.False(result.StdOut.Contains(Constants.TestSourceUrl)); } /// /// Test source edit with explicit flag, edit the source to not be explicit. /// [Test] public void SourceEdit() { // Remove the test source. TestCommon.RunAICLICommand("source remove", Constants.TestSourceName); // Add source as explicit and verify it is explicit. var addResult = TestCommon.RunAICLICommand("source add", $"SourceTest {Constants.TestSourceUrl} --trust-level trusted --explicit"); Assert.AreEqual(Constants.ErrorCode.S_OK, addResult.ExitCode); Assert.True(addResult.StdOut.Contains("Done")); var searchResult = TestCommon.RunAICLICommand("search", "TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_SOURCES_DEFINED, searchResult.ExitCode); Assert.True(searchResult.StdOut.Contains("No sources defined; add one with 'source add' or reset to defaults with 'source reset'")); // Run the edit, this should be S_OK with "Done" as it changed the state to not-explicit. var editResult = TestCommon.RunAICLICommand("source edit", $"SourceTest --explicit false"); Assert.AreEqual(Constants.ErrorCode.S_OK, editResult.ExitCode); Assert.True(editResult.StdOut.Contains("Explicit")); // Run it again, this should result in S_OK with no changes and a message that the source is already in that state. var editResult2 = TestCommon.RunAICLICommand("source edit", $"SourceTest --explicit false"); Assert.AreEqual(Constants.ErrorCode.S_OK, editResult2.ExitCode); Assert.True(editResult2.StdOut.Contains("The source named 'SourceTest' is already in the desired state.")); // Now verify it is no longer explicit by running the search again without adding the source parameter. var searchResult2 = TestCommon.RunAICLICommand("search", "TestExampleInstaller"); Assert.AreEqual(Constants.ErrorCode.S_OK, searchResult2.ExitCode); Assert.True(searchResult2.StdOut.Contains("TestExampleInstaller")); Assert.True(searchResult2.StdOut.Contains("AppInstallerTest.TestExampleInstaller")); TestCommon.RunAICLICommand("source remove", $"-n SourceTest"); } /// /// Test source edit with priority. /// [Test] public void SourceEdit_Priority() { // Remove the test source. TestCommon.RunAICLICommand("source remove", Constants.TestSourceName); var addResult = TestCommon.RunAICLICommand("source add", $"SourceTest {Constants.TestSourceUrl}"); Assert.AreEqual(Constants.ErrorCode.S_OK, addResult.ExitCode); Assert.True(addResult.StdOut.Contains("Done")); // Run the edit, this should be S_OK with "Done" as it changed the state var editResult = TestCommon.RunAICLICommand("source edit", $"SourceTest --priority 14"); Assert.AreEqual(Constants.ErrorCode.S_OK, editResult.ExitCode); Assert.True(editResult.StdOut.Contains("14")); // Run it again, this should result in S_OK with no changes and a message that the source is already in that state. var editResult2 = TestCommon.RunAICLICommand("source edit", $"SourceTest --priority 14"); Assert.AreEqual(Constants.ErrorCode.S_OK, editResult2.ExitCode); Assert.True(editResult2.StdOut.Contains("The source named 'SourceTest' is already in the desired state.")); } /// /// Test override of a default source via edit command. /// [Test] public void SourceEditOverrideDefault() { // Force Reset Sources var resetResult = TestCommon.RunAICLICommand("source reset", "--force"); Assert.AreEqual(Constants.ErrorCode.S_OK, resetResult.ExitCode); // Verify it is explicit true. Explicit is the only boolean value in the output. var listResult = TestCommon.RunAICLICommand("source list", "winget-font"); Assert.AreEqual(Constants.ErrorCode.S_OK, listResult.ExitCode); Assert.True(listResult.StdOut.Contains("true")); var editResult = TestCommon.RunAICLICommand("source edit", "winget-font -e false"); Assert.AreEqual(Constants.ErrorCode.S_OK, editResult.ExitCode); Assert.True(editResult.StdOut.Contains("Explicit")); // Verify that after edit it is now explicit false. var listResult2 = TestCommon.RunAICLICommand("source list", "winget-font"); Assert.AreEqual(Constants.ErrorCode.S_OK, listResult2.ExitCode); Assert.True(listResult2.StdOut.Contains("false")); // Remove the source. This should correctly tombstone it, even though it is overridden. var removeResult = TestCommon.RunAICLICommand("source remove", "winget-font"); Assert.AreEqual(Constants.ErrorCode.S_OK, removeResult.ExitCode); Assert.True(removeResult.StdOut.Contains("Done")); var listResult3 = TestCommon.RunAICLICommand("source list", "winget-font"); Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, listResult3.ExitCode); // Force Reset Sources var resetResult2 = TestCommon.RunAICLICommand("source reset", "--force"); Assert.AreEqual(Constants.ErrorCode.S_OK, resetResult2.ExitCode); // Verify it is back to being explicit true. var listResult4 = TestCommon.RunAICLICommand("source list", "winget-font"); Assert.AreEqual(Constants.ErrorCode.S_OK, listResult4.ExitCode); Assert.True(listResult4.StdOut.Contains("true")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/Test.runsettings ================================================ ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/ConfigServerUnexpectedExit.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/E2ETestResourceCrash id: first directives: repository: AppInstallerCLIE2ETestsRepo settings: key: Foo - resource: xE2ETestResource/E2EFileResource dependsOn: - first directives: repository: AppInstallerCLIE2ETestsRepo settings: Path: ${WinGetConfigRoot}\ConfigServerUnexpectedExit.txt Content: Contents! ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Configure_TestRepo.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/E2EFileResource directives: repository: AppInstallerCLIE2ETestsRepo settings: Path: ${WinGetConfigRoot}\Configure_TestRepo.txt Content: Contents! ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Configure_TestRepo_DSCv3.yml ================================================ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: winget: processor: dscv3 resources: - name: Test File type: xE2ETestResource/E2EFileResource properties: Path: ${WinGetConfigRoot}\Configure_TestRepo.txt Content: Contents! ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Configure_TestRepo_Location.yml ================================================ properties: configurationVersion: 0.2 assertions: - resource: xE2ETestResource/E2ETestResource directives: repository: AppInstallerCLIE2ETestsRepo settings: SecretCode: 4815162342 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/DependencyCycle.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/E2ETestResourceThrows id: A dependsOn: - B directives: repository: AppInstallerCLIE2ETestsRepo settings: key: Foo - resource: xE2ETestResource/E2EFileResource id: B dependsOn: - A directives: repository: AppInstallerCLIE2ETestsRepo settings: Path: ${WinGetConfigRoot}\IndependentResources_OneFailure.txt Content: Contents! - resource: NotMentioned settings: A: B ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/DependentResources_Failure.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/E2ETestResourceThrows id: first directives: repository: AppInstallerCLIE2ETestsRepo settings: key: Foo - resource: xE2ETestResource/E2EFileResource dependsOn: - first directives: repository: AppInstallerCLIE2ETestsRepo settings: Path: ${WinGetConfigRoot}\DependentResources_Failure.txt Content: Contents! ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/DuplicateIdentifiers.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/E2ETestResourceThrows id: same directives: repository: AppInstallerCLIE2ETestsRepo settings: key: Foo - resource: xE2ETestResource/E2EFileResource id: same directives: repository: AppInstallerCLIE2ETestsRepo settings: Path: ${WinGetConfigRoot}\IndependentResources_OneFailure.txt Content: Contents! - resource: NotMentioned settings: A: B ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Empty.yml ================================================ ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/GetPSModulePath.yml ================================================ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: 1e62d683-2999-44e7-81f7-6f8f35e8d731: true resources: - name: Name1 type: xE2ETestResource/E2ETestResourcePSModulePath metadata: repository: AppInstallerCLIE2ETestsRepo securityContext: elevated properties: outputPath: ${WinGetConfigRoot}\PSModulePath.txt ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/IndependentResources_OneFailure.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/E2ETestResourceThrows directives: repository: AppInstallerCLIE2ETestsRepo settings: key: Foo - resource: xE2ETestResource/E2EFileResource directives: repository: AppInstallerCLIE2ETestsRepo settings: Path: ${WinGetConfigRoot}\IndependentResources_OneFailure.txt Content: Contents! ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Init-TestRepository.ps1 ================================================ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. [CmdletBinding()] param( [string]$ModulesPath, [string]$RepositoryPath, [string]$RepositoryName, [switch]$Force ) if ([System.String]::IsNullOrEmpty($ModulesPath)) { $ModulesPath = Join-Path $PSScriptRoot "Modules" } if ([System.String]::IsNullOrEmpty($RepositoryPath)) { $RepositoryPath = Join-Path ([System.IO.Path]::GetTempPath()) (New-Guid) } if ([System.String]::IsNullOrEmpty($RepositoryName)) { $RepositoryName = "AppInstallerCLIE2ETestsRepo" } if ($Force) { $null = New-Item -Path $RepositoryPath -ItemType Directory -Force } else { $null = New-Item -Path $RepositoryPath -ItemType Directory -ErrorAction Inquire } $Local:existingRepository = Get-PSRepository -Name $RepositoryName -ErrorAction Ignore if ($Local:existingRepository) { if ($Force) { Unregister-PSRepository -Name $RepositoryName } else { throw "Repository named $RepositoryName is already registered. Use -Force to overwrite it." } } $null = Register-PSRepository -Name $RepositoryName -SourceLocation $RepositoryPath -ScriptSourceLocation $RepositoryPath $Local:allItems = Get-ChildItem $ModulesPath $Local:progressActivity = "Publishing modules to $RepositoryPath" Write-Progress -Activity $Local:progressActivity [Int32]$Local:modulesPublished = 0 $Local:allItems | ForEach-Object -Process { $Local:modulePath = $_.FullName Write-Verbose "Publishing $Local:modulePath" Publish-Module -Path $Local:modulePath -Repository $RepositoryName -Force $Local:modulesPublished += 1 Write-Progress -Activity $Local:progressActivity -PercentComplete (($Local:modulesPublished * 100) / $Local:allItems.Count) } Write-Progress -Activity $Local:progressActivity -Completed ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/LargeContentStrings.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2EMalicious/E2EMalicious id: firstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirst directives: repository: AppInstallerCLIE2ETestsRepo description: "Line1\nLine2\nLine3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3\nLine4\nLine5\nLine6" settings: key: Foo description: "Line1\nLine2\nLine3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3\nLine4\nLine5\nLine6" - resource: Unknown dependsOn: - firstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirstfirst directives: module: UnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknownUnknown repository: AppInstallerCLIE2ETestsRepo description: "Line1\nLine2\nLine3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3\nLine4\nLine5\nLine6" settings: Path: ${WinGetConfigRoot}\ConfigServerUnexpectedExit.txt Content: Contents! description: "Line1\nLine2\nLine3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3Line3\nLine4\nLine5\nLine6" ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/MissingDependency.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/E2ETestResourceThrows id: same directives: repository: AppInstallerCLIE2ETestsRepo settings: key: Foo - resource: xE2ETestResource/E2EFileResource dependsOn: - same directives: repository: AppInstallerCLIE2ETestsRepo settings: Path: ${WinGetConfigRoot}\IndependentResources_OneFailure.txt Content: Contents! - resource: MissingDependency dependsOn: - missing settings: A: B ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/ModuleMismatch.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: Module/Resource id: Identifier directives: module: DifferentModule settings: SettingInt: 1 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Modules/xE2EMalicious/xE2EMalicious.psd1 ================================================ # # Module manifest for module 'xE2ETestResource' # @{ RootModule = 'xE2EMalicious.psm1' ModuleVersion = '0.0.0.1' GUID = 'a0be43e8-ac22-4244-8efc-7263dfa50b92' CompatiblePSEditions = 'Core' Author = "WinGet Dev Team" CompanyName = 'Microsoft Corporation' Copyright = '(c) Microsoft Corporation. All rights reserved.' Description = "PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests | PowerShell module with DSC resources for unit tests" PowerShellVersion = '7.2' FunctionsToExport = @() CmdletsToExport = @() DscResourcesToExport = @( 'E2EMalicious' ) HelpInfoURI = 'https://www.contoso.com/help' # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ ProjectUri = 'https://github.com/microsoft/winget-cli' IconUri = 'https://www.contoso.com/icons/icon.png' } } } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Modules/xE2EMalicious/xE2EMalicious.psm1 ================================================ # E2E module with resources. enum Ensure { Absent Present } # This resource just checks if a file is there or not with and if its with the specified content. [DscResource()] class E2EMalicious { [DscProperty(Key)] [string] $Path [DscProperty()] [Ensure] $Ensure = [Ensure]::Present [DscProperty()] [string] $Content = $null [E2EFileResource] Get() { if ([string]::IsNullOrEmpty($this.Path)) { throw } $fileContent = $null if (Test-Path -Path $this.Path -PathType Leaf) { $fileContent = Get-Content $this.Path -Raw } $result = @{ Path = $this.Path Content = $fileContent } return $result } [bool] Test() { $get = $this.Get() if (Test-Path -Path $this.Path -PathType Leaf) { if ($this.Ensure -eq [Ensure]::Present) { return $this.Content -eq $get.Content } } elseif ($this.Ensure -eq [Ensure]::Absent) { return $true } return $false } [void] Set() { if (-not $this.Test()) { if (Test-Path -Path $this.Path -PathType Leaf) { if ($this.Ensure -eq [Ensure]::Present) { Set-Content $this.Path $this.Content -NoNewline } else { Remove-Item $this.Path } } else { if ($this.Ensure -eq [Ensure]::Present) { Set-Content $this.Path $this.Content -NoNewline } } } } } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Modules/xE2ETestResource/xE2ETestResource.psd1 ================================================ # # Module manifest for module 'xE2ETestResource' # @{ RootModule = 'xE2ETestResource.psm1' ModuleVersion = '0.0.0.1' GUID = 'a0be43e8-ac22-4244-8efc-7263dfa50b8c' CompatiblePSEditions = 'Core' Author = 'WinGet Dev Team' CompanyName = 'Microsoft Corporation' Copyright = '(c) Microsoft Corporation. All rights reserved.' Description = 'PowerShell module with DSC resources for unit tests' PowerShellVersion = '7.2' FunctionsToExport = @() CmdletsToExport = @() DscResourcesToExport = @( 'E2EFileResource' 'E2ETestResource' 'E2ETestResourceThrows' 'E2ETestResourceError' 'E2ETestResourceTypes' 'E2ETestResourceCrash' 'E2ETestResourcePID' 'E2ETestResourcePSModulePath' ) HelpInfoURI = 'https://www.contoso.com/help' # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ ProjectUri = 'https://github.com/microsoft/winget-cli' IconUri = 'https://www.contoso.com/icons/icon.png' } } } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Modules/xE2ETestResource/xE2ETestResource.psm1 ================================================ # E2E module with resources. enum Ensure { Absent Present } # This resource just checks if a file is there or not with and if its with the specified content. [DscResource()] class E2EFileResource { [DscProperty(Key)] [string] $Path [DscProperty()] [Ensure] $Ensure = [Ensure]::Present [DscProperty()] [string] $Content = $null [E2EFileResource] Get() { if ([string]::IsNullOrEmpty($this.Path)) { throw } $fileContent = $null if (Test-Path -Path $this.Path -PathType Leaf) { $fileContent = Get-Content $this.Path -Raw } $result = @{ Path = $this.Path Content = $fileContent } return $result } [bool] Test() { $get = $this.Get() if (Test-Path -Path $this.Path -PathType Leaf) { if ($this.Ensure -eq [Ensure]::Present) { return $this.Content -eq $get.Content } } elseif ($this.Ensure -eq [Ensure]::Absent) { return $true } return $false } [void] Set() { if (-not $this.Test()) { if (Test-Path -Path $this.Path -PathType Leaf) { if ($this.Ensure -eq [Ensure]::Present) { Set-Content $this.Path $this.Content -NoNewline } else { Remove-Item $this.Path } } else { if ($this.Ensure -eq [Ensure]::Present) { Set-Content $this.Path $this.Content -NoNewline } } } } } [DscResource()] class E2ETestResource { [DscProperty(Key)] [string] $key [DscProperty(Mandatory)] [string] $secretCode [E2ETestResource] Get() { $result = @{ key = "E2ETestResourceKey" } return $result } [bool] Test() { return $this.secretCode -eq "4815162342" } [void] Set() { if (-not $this.Test()) { $global:DSCMachineStatus = 1 } } } [DscResource()] class E2ETestResourceThrows { [DscProperty(Key)] [string] $key [E2ETestResourceThrows] Get() { $result = @{ key = "E2ETestResourceThrowsKey" } throw "throws in Get" return $result } [bool] Test() { throw "throws in Test" return $false } [void] Set() { throw "throws in Set" } } [DscResource()] class E2ETestResourceError { [DscProperty(Key)] [string] $key [E2ETestResourceError] Get() { $result = @{ key = "E2ETestResourceErrorKey" } Write-Error "Error in Get" return $result } [bool] Test() { Write-Error "Error in Test" return $true } [void] Set() { Write-Error "Error in Set" } } [DscResource()] class E2ETestResourceTypes { [DscProperty(Key)] [string] $key [DscProperty()] [boolean] $boolProperty [DscProperty()] [int] $intProperty; [DscProperty()] [double] $doubleProperty; [DscProperty()] [char] $charProperty; [DscProperty()] [Hashtable] $hashtableProperty; [E2ETestResourceTypes] Get() { $result = @{ key = "E2ETestResourceTypesKey" boolProperty = $false intProperty = 0 doubleProperty = 0.0 charProperty = 'z' hashtableProperty = @{} } return $result } [bool] Test() { # Because we can't get the error stream from a class based resource, I throw so is easier to know if # there's something wrong. if ($this.boolProperty -ne $true) { throw "Failed boolProperty" } if ($this.intProperty -ne 3) { throw "Failed intProperty. Got $($this.intProperty)" } if ($this.doubleProperty -ne -9.876) { throw "Failed doubleProperty Got $($this.doubleProperty)" } if ($this.charProperty -ne 'f') { throw "Failed charProperty Got $($this.charProperty)" } if ($this.hashtableProperty.ContainsKey("secretStringKey")) { if ($this.hashtableProperty["secretStringKey"] -ne "secretCode") { throw "Failed comparing value of `$hashtableProperty.secretStringKey Got $($this.hashtableProperty["secretStringKey"])" } } else { throw "Failed finding secretStringKey in hashtableProperty" } if ($this.hashtableProperty.ContainsKey("secretIntKey")) { if ($this.hashtableProperty["secretIntKey"] -ne 123456) { throw "Failed comparing value of `$hashtableProperty.secretIntKey Got $($this.hashtableProperty["secretIntKey"])" } } else { throw "Failed finding secretIntKey in hashtableProperty" } return $true } [void] Set() { # no-op } } # This resource "crashes" the containing process (really it just exits) [DscResource()] class E2ETestResourceCrash { [DscProperty(Key)] [string] $key [E2ETestResourceCrash] Get() { $result = @{ key = "E2ETestResourceCrashKey" } [System.Environment]::Exit(0) return $result } [bool] Test() { [System.Environment]::Exit(0) return $true } [void] Set() { [System.Environment]::Exit(0) } } # This resource writes the current PID to the provided file path. [DscResource()] class E2ETestResourcePID { [DscProperty(Key)] [string] $key [DscProperty(Mandatory)] [string] $directoryPath [E2ETestResourcePID] Get() { $result = @{ key = "E2ETestResourcePID" directoryPath = $this.directoryPath } return $result } [bool] Test() { return $false } [void] Set() { if (Test-Path -Path $this.directoryPath) { $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id $filePath = Join-Path -Path $this.directoryPath -ChildPath "$processId.txt" New-Item -Path $filePath -ItemType File -Force } } } # This resource writes the current PSModulePath to the provided file path. [DscResource()] class E2ETestResourcePSModulePath { [DscProperty(Key)] [string] $key [DscProperty(Mandatory)] [string] $outputPath [E2ETestResourcePSModulePath] Get() { $result = @{ key = "E2ETestResourcePSModulePath" outputPath = $this.outputPath } return $result } [bool] Test() { return $false } [void] Set() { Set-Content -Path $this.outputPath -Value $env:PSModulePath -Force } } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/NoResourceName.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: Module/ id: Identifier settings: SettingInt: 1 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/NoVersion.yml ================================================ properties: value: 1 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/NotConfig.yml ================================================ yaml: true ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/PSGallery_NoModule_NoSettings.yml ================================================ properties: configurationVersion: 0.1 resources: - resource: XmlFileContentResource directives: description: Set XML file contents ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/PSGallery_NoSettings.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: XmlContentDsc/XmlFileContentResource directives: description: Set XML file contents ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/ResourceCaseInsensitive.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/e2efileresource directives: repository: AppInstallerCLIE2ETestsRepo settings: Path: ${WinGetConfigRoot}\ResourceCaseInsensitive.txt Content: Contents! ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/ResourceNotFound.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: moduleThatDoesNotExist/resourceThatDoesNotExist directives: repository: AppInstallerCLIE2ETestsRepo settings: key: Foo ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/ResourcesNotASequence.yml ================================================ properties: configurationVersion: 0.1 resources: 1 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/RunCommandOnSet.yml ================================================ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: winget: processor: dscv3 resources: - name: Test RunCommandOnSet type: Microsoft.DSC.Transitional/RunCommandOnSet properties: executable: pwsh arguments: - -NoProfile - -NoLogo - -Command - | Set-Content -Path \TestFile.txt -Value 'TestContent' ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/ShowDetails_DSCv3.yml ================================================ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: winget: processor: dscv3 resources: - name: Test File type: Microsoft.WinGet.Dev/TestFile metadata: description: Description 1. properties: path: ${WinGetConfigRoot}\ShowDetails_DSCv3.txt content: DSCv3 Contents! ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/ShowDetails_TestRepo.yml ================================================ properties: configurationVersion: 0.2 resources: - resource: xE2ETestResource/E2EFileResource directives: repository: AppInstallerCLIE2ETestsRepo ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/ShowDetails_TestRepo_0_3.yml ================================================ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json resources: - name: Name1 type: xE2ETestResource/E2EFileResource metadata: repository: AppInstallerCLIE2ETestsRepo properties: prop1: 3 prop2: '4' ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/UnitNotAMap.yml ================================================ properties: configurationVersion: 0.1 resources: - string ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/UnknownVersion.yml ================================================ properties: configurationVersion: 99999999 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/Unknown_Processor.yml ================================================ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: winget: processor: unknown resources: - name: Name1 type: xE2ETestResource/E2EFileResource metadata: repository: AppInstallerCLIE2ETestsRepo properties: prop1: 3 prop2: '4' ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_DependencySourceMissing.yml ================================================ properties: configurationVersion: 0.2.0 resources: - resource: Microsoft.WinGet.DSC/WinGetPackage id: testPackage directives: description: Install Test Package allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSource version: "1.0.1.0" ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_Good.yml ================================================ properties: configurationVersion: 0.2.0 resources: - resource: Microsoft.WinGet.DSC/WinGetSource id: configureSource directives: description: Add test source allowPrerelease: true settings: Name: TestSource Argument: "https://localhost:5001/TestKit" - resource: Microsoft.WinGet.DSC/WinGetPackage id: testPackage dependsOn: - configureSource directives: description: Install Test Package allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSource version: "1.0.1.0" ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageNotFound.yml ================================================ properties: configurationVersion: 0.2.0 resources: - resource: Microsoft.WinGet.DSC/WinGetSource id: configureSource directives: description: Add test source allowPrerelease: true settings: Name: TestSource Argument: "https://localhost:5001/TestKit" - resource: Microsoft.WinGet.DSC/WinGetPackage id: testPackage dependsOn: - configureSource directives: description: Install Test Package allowPrerelease: true settings: id: AppInstallerTest.DoesNotExist source: TestSource version: "1.0.1.0" ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_PackageVersionNotFound.yml ================================================ properties: configurationVersion: 0.2.0 resources: - resource: Microsoft.WinGet.DSC/WinGetSource id: configureSource directives: description: Add test source allowPrerelease: true settings: Name: TestSource Argument: "https://localhost:5001/TestKit" - resource: Microsoft.WinGet.DSC/WinGetPackage id: testPackage dependsOn: - configureSource directives: description: Install Test Package allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSource version: "101.0.101.0" ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_SourceOpenFailed.yml ================================================ properties: configurationVersion: 0.2.0 resources: - resource: Microsoft.WinGet.DSC/WinGetSource id: configureSource directives: description: Add test source allowPrerelease: true settings: Name: TestSourceV2 Argument: "https://localhost:5001/TestKit" - resource: Microsoft.WinGet.DSC/WinGetPackage id: testPackage dependsOn: - configureSource directives: description: Install Test Package allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSourceV2 version: "1.0.1.0" ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithOnlyOneVersionAvailable.yml ================================================ properties: configurationVersion: 0.2.0 resources: - resource: Microsoft.WinGet.DSC/WinGetSource id: configureSource directives: description: Add test source allowPrerelease: true settings: Name: TestSource Argument: "https://localhost:5001/TestKit" - resource: Microsoft.WinGet.DSC/WinGetPackage id: testPackage dependsOn: - configureSource directives: description: Install Test Package allowPrerelease: true settings: id: AppInstallerTest.TestValidManifest source: TestSource version: "1.0.0.0" ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/WinGetDscResourceValidate_VersionSpecifiedWithUseLatest.yml ================================================ properties: configurationVersion: 0.2.0 resources: - resource: Microsoft.WinGet.DSC/WinGetSource id: configureSource directives: description: Add test source allowPrerelease: true settings: Name: TestSource Argument: "https://localhost:5001/TestKit" - resource: Microsoft.WinGet.DSC/WinGetPackage id: testPackage dependsOn: - configureSource directives: description: Install Test Package allowPrerelease: true settings: id: AppInstallerTest.TestExeInstaller source: TestSource version: "1.0.1.0" useLatest: true ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Configuration/WithParameters_0_3.yml ================================================ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json parameters: param1: type: string defaultValue: value resources: - name: Name1 type: xE2ETestResource/E2EFileResource metadata: repository: AppInstallerCLIE2ETestsRepo properties: prop1: 3 prop2: '4' ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/ImportFiles/ImportFile-Bad-Invalid.json ================================================ "A valid JSON file that does not conform to the schema" ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/ImportFiles/ImportFile-Bad-UnknownPackage.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "MissingPackage", "Version": "1.0.0.0" } ], "SourceDetails": { "Name": "TestSource", "Argument": "https://localhost:5001/TestKit", "Identifier": "WingetE2E.Tests_8wekyb3d8bbwe", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/ImportFiles/ImportFile-Bad-UnknownPackageVersion.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.2.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "PackageIdentifier": "AppInstallerTest.TestExeInstaller", "Version": "4.3.2.1" } ], "SourceDetails": { "Name": "TestSource", "Argument": "https://localhost:5001/TestKit", "Identifier": "WingetE2E.Tests_8wekyb3d8bbwe", "Type": "Microsoft.PreIndexed.Package" } } ] } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/ImportFiles/ImportFile-Bad-UnknownSource.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "AppInstallerTest.TestExeInstaller", "Version": "1.0.0.0" } ], "SourceDetails": { "Name": "TestSource", "Argument": "https://localhost", "Identifier": "WingetE2E.UnknownTestSource_8wekyb3d8bbwe", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/ImportFiles/ImportFile-Good.1.0.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "AppInstallerTest.TestExeInstaller", "Version": "1.0.0.0" } ], "SourceDetails": { "Name": "TestSource", "Argument": "https://localhost:5001/TestKit", "Identifier": "WingetE2E.Tests_8wekyb3d8bbwe", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/ImportFiles/ImportFile-Good.2.0.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.2.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "PackageIdentifier": "AppInstallerTest.TestExeInstaller", "Version": "1.0.0.0" } ], "SourceDetails": { "Name": "TestSource", "Argument": "https://localhost:5001/TestKit", "Identifier": "WingetE2E.Tests_8wekyb3d8bbwe", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/IndexPackageManifest.xml ================================================ Microsoft WinGet Source Microsoft Corporation Assets\AppPackageStoreLogo.png ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestArpVersionMapping_OppositeOrder_1.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestArpVersionOppositeOrder PackageVersion: '1.0' PackageName: TestArpVersionOppositeOrder PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for arp version test. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: AppsAndFeaturesEntries: - DisplayVersion: "10.0" - DisplayVersion: "10.5" ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestArpVersionMapping_OppositeOrder_2.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestArpVersionOppositeOrder PackageVersion: '2.0' PackageName: TestArpVersionOppositeOrder PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for arp version test. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: AppsAndFeaturesEntries: - DisplayVersion: "9.0" - DisplayVersion: "9.5" ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestArpVersionMapping_SameAsPackageVersion.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestArpVersionSameVersion PackageVersion: '1.0' PackageName: TestArpVersionSameVersion PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for arp version test. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: AppsAndFeaturesEntries: - DisplayVersion: "1.0" ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestArpVersionMapping_SameOrder_1.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestArpVersionSameOrder PackageVersion: '1.0' PackageName: TestArpVersionSameOrder PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for arp version test. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: AppsAndFeaturesEntries: - DisplayVersion: "10.0" - DisplayVersion: "10.5" ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestArpVersionMapping_SameOrder_2.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestArpVersionSameOrder PackageVersion: '2.0' PackageName: TestArpVersionSameOrder PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for arp version test. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: AppsAndFeaturesEntries: - DisplayVersion: "11.0" - DisplayVersion: "11.5" ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestBurnInstaller.MissingRepairBehavior.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.TestMissingRepairBehavior PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: TestMissingRepairBehavior Publisher: AppInstallerTest Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: burn InstallerSha256: ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' InstallerSwitches: InstallLocation: /InstallDir Custom: /Version 2.0.0.0 /DisplayName TestMissingRepairBehavior /UseHKLM Repair: /repair ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestBurnInstaller.ModifyRepair.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.TestModifyRepair PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: TestModifyRepair Publisher: AppInstallerTest RepairBehavior: modify Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: burn InstallerSha256: ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' InstallerSwitches: InstallLocation: /InstallDir Custom: /Version 2.0.0.0 /DisplayName TestModifyRepair /UseHKLM Repair: /repair ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestBurnInstaller.ModifyRepairWithNoModify.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.TestModifyRepairWithNoModify PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: TestModifyRepairWithNoModify Publisher: AppInstallerTest RepairBehavior: modify Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: burn InstallerSha256: ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' InstallerSwitches: InstallLocation: /InstallDir Custom: /Version 2.0.0.0 /DisplayName TestModifyRepairWithNoModify /UseHKLM /NoModify Repair: /repair ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestBurnInstaller.UserScopeInstallRepairInAdminContext.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.TestUserScopeInstallRepairInAdminContext PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: TestUserScopeInstallRepairInAdminContext Publisher: AppInstallerTest RepairBehavior: modify Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: burn InstallerSha256: ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' ElevationRequirement: elevationRequired InstallerSwitches: InstallLocation: /InstallDir Custom: /Version 2.0.0.0 /DisplayName TestUserScopeInstallRepairInAdminContext Repair: /repair ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestBurnInstaller.yaml ================================================ Id: AppInstallerTest.TestBurnInstaller Name: TestBurnInstaller Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: burn Switches: InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExampleInstaller.yaml ================================================ Id: AppInstallerTest.TestExampleInstaller Name: TestExampleInstaller Version: 1.2.3.4 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.1.0.1.0.yaml ================================================ Id: AppInstallerTest.TestExeInstaller Name: TestExeInstaller Version: 1.0.1.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Scope: user Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom /Version 1.0.1.0 SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir - Arch: x86 Scope: machine Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom /Version 1.0.1.0 /UseHKLM SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.1.1.0.0.yaml ================================================ Id: AppInstallerTest.TestExeInstaller Name: TestExeInstaller Version: 1.1.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Scope: user Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom /Version 1.1.0.0 SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir - Arch: x86 Scope: machine Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom /Version 1.1.0.0 /UseHKLM SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.2.0.0.0.yaml ================================================ Id: AppInstallerTest.TestExeInstaller Name: TestExeInstaller Version: 2.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Scope: user Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom /Version 2.0.0.0 SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir - Arch: x86 Scope: machine Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom /Version 2.0.0.0 /UseHKLM SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.RebootRequired.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.6.0.schema.json PackageIdentifier: AppInstallerTest.RebootRequired PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: TestRebootRequired ShortDescription: Emulates an installer that requires a reboot to finish. Publisher: Microsoft Corporation License: Test Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: InstallerSwitches: SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ExpectedReturnCodes: - InstallerReturnCode: 9 ReturnResponse: rebootRequiredToFinish ReturnResponseUrl: https://DefaultReturnResponseUrl.com - InstallerReturnCode: 10 ReturnResponse: rebootRequiredForInstall ReturnResponseUrl: https://DefaultReturnResponseUrl.com ManifestType: singleton ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.UninstallerRepair.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.UninstallerRepair PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: UninstallerRepair Publisher: AppInstallerTest RepairBehavior: uninstaller Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' InstallerSwitches: InstallLocation: /InstallDir Custom: /Version 2.0.0.0 /DisplayName UninstallerRepair /UseHKLM Repair: /repair ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.UninstallerRepairWithNoRepair.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.UninstallerRepairWithNoRepair PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: UninstallerRepairWithNoRepair Publisher: AppInstallerTest RepairBehavior: uninstaller Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' InstallerSwitches: InstallLocation: /InstallDir Custom: /Version 2.0.0.0 /DisplayName UninstallerRepairWithNoRepair /UseHKLM /NoRepair Repair: /repair ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.WindowsFeature.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.WindowsFeature PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: TestWindowsFeature ShortDescription: Installs exe installer with valid Windows Features dependencies Publisher: Microsoft Corporation License: Test Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: InstallerSwitches: SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir Dependencies: WindowsFeatures: - netfx3 - invalidFeature ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.yaml ================================================ Id: AppInstallerTest.TestExeInstaller Name: TestExeInstaller Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Scope: user Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir - Arch: x86 Scope: machine Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom /UseHKLM SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstallerForExport.yaml ================================================ Id: AppInstallerTest.TestPackageExport Name: TestPackageExport Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{92e3d4e5-6e3d-4ae4-b9f0-b7e0a5f25b91}' Switches: Custom: '/ProductID {92e3d4e5-6e3d-4ae4-b9f0-b7e0a5f25b91} /DisplayName TestPackageExport /GenerateDscResourceFiles' SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_CatalogPackageMetadata.yaml ================================================ # Manifest for verifying the output of the CatalogPackageMetadata COM object. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.5.0.schema.json PackageIdentifier: AppInstallerTest.CatalogPackageMetadata PackageVersion: 1.0.0.0 PackageName: TestCatalogPackageMetadata PackageLocale: en-US Publisher: AppInstallerTest License: testLicense ShortDescription: testShortDescription Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: InstallerSwitches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir Agreements: - AgreementLabel: testAgreementLabel Agreement: testAgreementText AgreementUrl: https://testAgreementUrl.net PublisherUrl: https://testPublisherUrl.com PublisherSupportUrl: https://testPublisherSupportUrl.com PrivacyUrl: https://testPrivacyUrl.com Author: testAuthor PackageUrl: https://testPackageUrl.com LicenseUrl: https://testLicenseUrl.com Copyright: testCopyright CopyrightUrl: https://testCopyrightUrl.com Description: testDescription Moniker: testMoniker PurchaseUrl: https://testPurchaseUrl.com Tags: - "tag1" - "tag2" ReleaseNotes: testReleaseNotes ReleaseNotesUrl: https://testReleaseNotes.net InstallationNotes: testInstallationNotes Documentations: - DocumentLabel: testDocumentLabel DocumentUrl: https://testDocumentUrl.com Icons: - IconUrl: https://testIcon IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 ManifestType: singleton ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_InapplicableOsVersion.yaml ================================================ Id: AppInstallerTest.InapplicableOsVersion Name: InapplicableOsVersion Version: 1.0.0.0 Publisher: AppInstallerTest MinOSVersion: 11.0.0.0 License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_InstallMSIX.yaml ================================================ Id: AppInstallerTest.TestExeInstallerInstallsMSIX Name: EXE Installer that Installs MSIX - extra Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe PackageFamilyName: 6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e_8wekyb3d8bbwe Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_MultipleLocale.yaml ================================================ # Manifest for verifying multiple locales PackageIdentifier: AppInstallerTest.MultipleLocale PackageVersion: 1.0.0.0 PackageName: TestMultipleLocale PackageLocale: en-US Publisher: AppInstallerTest License: testLicense LicenseUrl: https://testLicenseUrl.com ShortDescription: testShortDescription Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: InstallerSwitches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir Agreements: - AgreementLabel: testAgreementLabel Agreement: testAgreementText AgreementUrl: https://testAgreementUrl.net Localization: - Author: localeAuthor Copyright: localeCopyright License: localeLicense LicenseUrl: https://localeLicenseUrl.com PackageLocale: zh-CN PackageName: localePackageName PackageUrl: https://localePackageUrl.com Publisher: localePublisher PublisherSupportUrl: https://localePublisherSupportUrl.com PublisherUrl: https://localePublisherUrl.com Moniker: localeMoniker PurchaseUrl: https://localePurchaseUrl.com ReleaseNotes: localeReleaseNotes ReleaseNotesUrl: https://localeReleaseNotesUrl.com Agreements: - AgreementLabel: localeAgreementLabel Agreement: localeAgreement AgreementUrl: https://localeAgreementUrl.net Documentations: - DocumentLabel: localeDocumentLabel DocumentUrl: https://localeDocumentUrl.com Icons: - IconUrl: https://localeTestIcon IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 Tags: - tag1 - tag2 - PackageLocale: en-GB PackageName: packageNameUK License: licenseUK Publisher: publisherUK ManifestType: merged ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_NoScope.yaml ================================================ Id: AppInstallerTest.TestExeInstallerNoScope Name: TestExeInstallerNoScope Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_PackageDependency.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.PackageDependency PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: TestPackageDependency ShortDescription: Installs a package with a package dependency. Publisher: AppInstallerTest License: testLicense Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: InstallerSwitches: Silent: /exesilent SilentWithProgress: /exeswp Log: /LogFile InstallLocation: /InstallDir Dependencies: PackageDependencies: - PackageIdentifier: AppInstallerTest.TestPortableExe ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_PackageDependencyRequiresPathRefresh.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.PackageDependencyRequiresPathRefresh PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: TestPackageDependencyWithPathRefresh ShortDescription: Installs a portable package dependency that modifies the PATH environment variable during installation, which is then invoked by the main installer. Publisher: AppInstallerTest License: testLicense Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: InstallerSwitches: Custom: /AliasToExecute testCommand /AliasArguments /NoOperation Silent: /exesilent SilentWithProgress: /exeswp Log: /LogFile InstallLocation: /InstallDir Dependencies: PackageDependencies: - PackageIdentifier: AppInstallerTest.TestPortableExeWithCommand ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_PathVariableRefresh.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.PathVariableRefresh PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: TestPathVariableRefresh ShortDescription: Emulates an installer that invokes a command that only becomes available once the PATH environment variable is refreshed. Publisher: Microsoft Corporation License: Test Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: InstallerSwitches: Custom: /AliasToExecute testCommand.exe /AliasArguments /NoOperation SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_Sha256Mismatch.yaml ================================================ Id: AppInstallerTest.TestExeSha256Mismatch Name: TestExeSha256Mismatch Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: 0000000000000000000000000000000000000000000000000000000000000000 InstallerType: exe Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestFont.yaml ================================================ PackageIdentifier: AppInstallerTest.TestFont PackageVersion: 1.0.0.0 PackageName: TestFont PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for font install. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestFont/AppInstallerTestFont.ttf InstallerType: font InstallerSha256: ManifestType: singleton ManifestVersion: 1.12.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestGoodManifestV1_10-SchemaHeader.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeader PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header Publisher: Microsoft Corporation License: Test ShortDescription: This manifest with schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestInnoInstaller.InstallerRepair.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.TestInstallerRepair PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: TestInstallerRepair Publisher: AppInstallerTest RepairBehavior: installer Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: inno InstallerSha256: ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' InstallerSwitches: InstallLocation: /InstallDir Custom: /Version 2.0.0.0 /DisplayName TestInstallerRepair /UseHKLM Repair: /repair /UseHKLM ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestInnoInstaller.yaml ================================================ Id: AppInstallerTest.TestInnoInstaller Name: TestInnoInstaller Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: inno Switches: InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestCheckInstalledStatus PackageVersion: '1.0' PackageName: TestCheckInstalledStatus PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for check installed status test. Installers: - Architecture: neutral InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: ProductCode: CheckInstalledStatusProductId InstallationMetadata: DefaultInstallLocation: "%TEMP%\\TestInstalledStatus" Files: - RelativeFilePath: "data.txt" # Hash value is for a txt file with "Test" as content in utf8 FileSha256: 532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25 FileType: other - RelativeFilePath: "TestExeInstalled.txt" FileType: other ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestInvalidFont.yaml ================================================ PackageIdentifier: AppInstallerTest.TestInvalidFont PackageVersion: 1.0.0.0 PackageName: TestInvalidFont PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for installing an invalid font. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: font InstallerSha256: ManifestType: singleton ManifestVersion: 1.11.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestInvalidManifest.yaml ================================================ Id: TestInvalidManifest TestInvalidManifest Name: TestInvalidManifest Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMappingWithArchitectureX64.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestMappingWithArchitectureX64 PackageVersion: '1.0' PackageName: TestMappingWithArchitecture PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for mapping with architecture. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: AppsAndFeaturesEntries: - DisplayName: "TestMappingWithArchitecture(X64)" InstallerSwitches: Custom: '/DisplayName TestMappingWithArchitecture(X64) /ProductID {0e426f01-b682-4e67-a357-52f9ecb4590d}' InstallLocation: /InstallDir ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMappingWithArchitectureX86.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestMappingWithArchitectureX86 PackageVersion: 1.0 PackageName: TestMappingWithArchitecture PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for mapping with architecture. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: AppsAndFeaturesEntries: - DisplayName: "TestMappingWithArchitecture(X86)" InstallerSwitches: Custom: '/DisplayName TestMappingWithArchitecture(X86) /ProductID {0e426f01-b682-4e67-a357-52f9ecb4590d}' InstallLocation: /InstallDir ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.Repair.yaml ================================================ # Uses the MSI installer; # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.TestMsiRepair PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: TestMsiInstallerV2 Publisher: AppInstallerTest Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestMsiInstaller/AppInstallerTestMsiInstallerV2.msi InstallerType: msi InstallerSha256: ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.yaml ================================================ Id: AppInstallerTest.TestMsiInstaller Name: TestMsiInstaller Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.msi Sha256: InstallerType: msi ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller_UpgradeCode.yaml ================================================ # Uses the MSI installer; doesn't list the ProductCode, only the UpgradeCode # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: AppInstallerTest.TestMsiInstallerUpgradeCode PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: TestMsiInstallerUpgradeCode Publisher: AppInstallerTest License: Test ShortDescription: Test Installer Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.msi InstallerType: msi InstallerSha256: AppsAndFeaturesEntries: - UpgradeCode: '{B9CF9DD5-D46F-4CE0-BFC9-633BF9D3A6F4}' ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller.yaml ================================================ Id: AppInstallerTest.TestMsixInstaller Name: TestMsixInstaller Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.msix Sha256: InstallerType: msix PackageFamilyName: 6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e_8wekyb3d8bbwe ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller_SignatureHashMismatch.yaml ================================================ Id: AppInstallerTest.TestMsixSignatureHashMismatch Name: TestMsixSignatureHashMismatch Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.msix Sha256: SignatureSha256: 0000000000000000000000000000000000000000000000000000000000000000 InstallerType: msix ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller_WithSignatureHash.yaml ================================================ Id: AppInstallerTest.TestMsixWithSignatureHash Name: TestMsixWithSignatureHash Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.msix Sha256: SignatureSha256: InstallerType: msix ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestMultipleInstallers.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestMultipleInstallers PackageVersion: 1.0.0.0 PackageName: TestMultipleInstallers PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test manifest with multiple installers Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: nullsoft InstallerSha256: Scope: user InstallerSwitches: InstallLocation: /InstallDir - Architecture: arm64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: nullsoft InstallerSha256: Scope: user InstallerSwitches: InstallLocation: /InstallDir - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.msi InstallerSha256: InstallerType: msi ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' Scope: machine - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip InstallerLocale: zh-CN ProductCode: '{E1880465-8CC2-4033-90AE-DE4E7FDBA26E}' InstallerSha256: NestedInstallerType: exe NestedInstallerFiles: - RelativeFilePath: AppInstallerTestExeInstaller.exe InstallerSwitches: Custom: /execustom /productID {E1880465-8CC2-4033-90AE-DE4E7FDBA26E} SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestNullsoftInstaller.UninstallerRepair.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: AppInstallerTest.NullsoftUninstallerRepair PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: NullsoftUninstallerRepair Publisher: AppInstallerTest RepairBehavior: uninstaller Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' InstallerSwitches: InstallLocation: /InstallDir Custom: /Version 2.0.0.0 /DisplayName NullsoftUninstallerRepair /UseHKLM Repair: /repair ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestNullsoftInstaller.yaml ================================================ Id: AppInstallerTest.TestNullsoftInstaller Name: TestNullsoftInstaller Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: nullsoft Switches: InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestPortableInstaller.2.0.0.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestPortableExe PackageVersion: 2.0.0.0 PackageName: TestPortableExe PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for portable install. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: portable InstallerSha256: ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestPortableInstaller.3.0.0.0_UninstallPrevious.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestPortableExe PackageVersion: 3.0.0.0 PackageName: TestPortableExe PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for portable install. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: portable InstallerSha256: UpgradeBehavior: uninstallPrevious ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestPortableInstaller.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestPortableExe PackageVersion: 1.0.0.0 PackageName: TestPortableExe PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for portable install. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: portable InstallerSha256: ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestPortableInstallerUninstallPrevious.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestPortableExe PackageVersion: 1.1.0.0 PackageName: TestPortableExe PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for portable install. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: portable InstallerSha256: UpgradeBehavior: uninstallPrevious ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestPortableInstaller_WithCommand.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerTest.TestPortableExeWithCommand PackageVersion: 1.0.0.0 PackageName: TestPortableExeWithCommand PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for portable install with command value. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: portable InstallerSha256: Commands: - testCommand ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestUpgradeAddsDependency.1.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestUpgradeAddsDependency PackageVersion: '1.0' PackageName: TestUpgradeAddsDependency PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for adding a new package dependency during an upgrade. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: ProductCode: '{fda6c0e2-0977-482f-bda1-9bd025457b81}' InstallerSwitches: Custom: '/ProductID {fda6c0e2-0977-482f-bda1-9bd025457b81}' InstallLocation: /InstallDir ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestUpgradeAddsDependency.2.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestUpgradeAddsDependency PackageVersion: '2.0' PackageName: TestUpgradeAddsDependency PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for adding a new package dependency during an upgrade. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: ProductCode: '{fda6c0e2-0977-482f-bda1-9bd025457b81}' InstallerSwitches: Custom: '/ProductID {fda6c0e2-0977-482f-bda1-9bd025457b81}' InstallLocation: /InstallDir Dependencies: PackageDependencies: - PackageIdentifier: AppInstallerTest.TestUpgradeAddsDependencyDependent ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestUpgradeAddsDependencyDependent.1.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestUpgradeAddsDependencyDependent PackageVersion: '1.0' PackageName: TestUpgradeAddsDependencyDependent PackageLocale: en-US Publisher: Microsoft License: Test ShortDescription: E2E test for adding a new package dependency during an upgrade. Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: exe InstallerSha256: UpgradeBehavior: uninstallPrevious ProductCode: '{648274a3-17aa-4731-8bf1-5854ca61e475}' InstallerSwitches: Custom: '/ProductID {648274a3-17aa-4731-8bf1-5854ca61e475}' InstallLocation: /InstallDir ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestUpgradeAvailableApi.1.0.0.0.yaml ================================================ Id: AppInstallerTest.TestUpgradeApplicability Name: TestUpgradeApplicability Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{bfb0f666-99d5-433d-8a2e-32f31d4f8e48}' Switches: Custom: '/ProductID {bfb0f666-99d5-433d-8a2e-32f31d4f8e48} /DisplayName TestUpgradeApplicability' SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestUpgradeAvailableApi.2.0.0.0.yaml ================================================ Id: AppInstallerTest.TestUpgradeApplicability Name: TestUpgradeApplicability Version: 2.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: arm64 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe ProductCode: '{bfb0f666-99d5-433d-8a2e-32f31d4f8e48}' Switches: Custom: '/ProductID {bfb0f666-99d5-433d-8a2e-32f31d4f8e48} /DisplayName TestUpgradeApplicability' SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestUpgradeDeny.1.0.0.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.6.0.schema.json PackageIdentifier: AppInstallerTest.TestUpgradeDeny PackageVersion: 1.0.0.0 PackageName: TestUpgradeDeny PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for upgrade deny. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: portable InstallerSha256: UpgradeBehavior: deny ManifestType: singleton ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestUpgradeDeny.2.0.0.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.6.0.schema.json PackageIdentifier: AppInstallerTest.TestUpgradeDeny PackageVersion: 2.0.0.0 PackageName: TestUpgradeDeny PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for upgrade deny. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: portable InstallerSha256: UpgradeBehavior: deny ManifestType: singleton ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestValidManifest.yaml ================================================ Id: AppInstallerTest.TestValidManifest Name: TestValidManifest Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: 0000000000000000000000000000000000000000000000000000000000000000 InstallerType: exe Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestWarningManifest.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.6.0.schema.json PackageIdentifier: TestWarning.Manifest PackageName: TestWarningManifest PackageVersion: 1.0.0.0 PackageLocale: en-US Publisher: AppInstallerTest License: Test Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 InstallerType: exe ShortDescription: This manifest should have only warnings, no errors ManifestType: singleton ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestWarningManifestV1_10-SchemaHeaderInvalid.yaml ================================================ # yaml-language-server= $schema=https://aka.ms/winget-manifest.singleton.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeaderInvalid PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header Invalid Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has an invalid schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestWarningManifestV1_10-SchemaHeaderManifestTypeMismatch.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeaderManifestTypeMismatch PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header ManifestType Mismatch Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has a mismatched ManisfestType in the schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestWarningManifestV1_10-SchemaHeaderNotFound.yaml ================================================ PackageIdentifier: AppInstallerCliTest.SchemaHeaderNotFound PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header Not Found Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has a missing schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestWarningManifestV1_10-SchemaHeaderURLPatternMismatch.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest-invalid.singleton.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeaderURLPatternMismatch PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header URL Pattern Mismatch Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has a mismatched schema header URL pattern Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestWarningManifestV1_10-SchemaHeaderVersionMismatch.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.9.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeaderVersionMismatch PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header ManifestVersion Mismatch Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has a mismatched ManisfestVersion in the schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestZipInstaller_Exe.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestZipInstallerWithExe PackageVersion: 1.0.0.0 PackageName: TestZipInstallerWithExe PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for installing a zip with exe. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip ProductCode: '{E1880465-8CC2-4033-90AE-DE4E7FDBA26E}' InstallerSha256: NestedInstallerType: exe NestedInstallerFiles: - RelativeFilePath: AppInstallerTestExeInstaller.exe InstallerSwitches: Custom: /execustom /productID {E1880465-8CC2-4033-90AE-DE4E7FDBA26E} SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /LogFile InstallLocation: /InstallDir ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestZipInstaller_Exe_InvalidRelativeFilePath.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestZipInvalidRelativePath PackageVersion: 1.0.0.0 PackageName: TestZipInvalidRelativePath PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for installing a zip with a bad relative file path. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip ProductCode: '{E1880465-8CC2-4033-90AE-DE4E7FDBA26E}' InstallerSha256: NestedInstallerType: exe NestedInstallerFiles: - RelativeFilePath: ../../AppInstallerTestExeInstaller.exe InstallerSwitches: Custom: /productID {E1880465-8CC2-4033-90AE-DE4E7FDBA26E} ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestZipInstaller_Msi.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestZipInstallerWithMsi PackageVersion: 1.0.0.0 PackageName: TestZipInstallerWithMsi PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for installing a zip with msi. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' InstallerSha256: NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: AppInstallerTestMsiInstaller.msi ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestZipInstaller_Msix.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestZipInstallerWithMsix PackageVersion: 1.0.0.0 PackageName: TestZipInstallerWithMsix PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for installing a zip with msix. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' InstallerSha256: NestedInstallerType: msix NestedInstallerFiles: - RelativeFilePath: AppInstallerTestMsixInstaller.msix ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestZipInstaller_PackageInstallerInfo.yaml ================================================ # Test manifest for verifying the behavior of retrieving a PackageInstallerInfo COM object. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.PackageInstallerInfo PackageVersion: 1.0.0.0 PackageName: TestPackageInstallerInfo PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for PackageInstallerInfo COM object. Installers: - Architecture: x64 Scope: user InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip InstallerSha256: NestedInstallerType: exe NestedInstallerFiles: - RelativeFilePath: AppInstallerTestExeInstaller.exe ElevationRequirement: elevationRequired InstallerLocale: en-US ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestZipInstaller_Portable.2.0.0.0.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestZipInstallerWithPortable PackageVersion: 2.0.0.0 PackageName: TestZipInstallerWithPortable PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for installing a zip with portable. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip InstallerSha256: NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: AppInstallerTestExeInstaller.exe PortableCommandAlias: TestPortable ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestZipInstaller_Portable.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerTest.TestZipInstallerWithPortable PackageVersion: 1.0.0.0 PackageName: TestZipInstallerWithPortable PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for installing a zip with portable. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip InstallerSha256: NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: AppInstallerTestExeInstaller.exe PortableCommandAlias: TestPortable ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TestZipInstaller_Portable_BinariesDependOnPath.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.9.0.schema.json PackageIdentifier: AppInstallerTest.ArchivePortableWithBinariesDependentOnPath PackageVersion: 1.0.0.0 PackageName: TestArchivePortableWithBinariesDependentOnPath PackageLocale: en-US Publisher: AppInstallerTest License: Test ShortDescription: E2E test for installing a zip containing a portable with binaries that depend on the PATH variable. Installers: - Architecture: x64 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip InstallerType: zip InstallerSha256: NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: AppInstallerTestExeInstaller.exe PortableCommandAlias: TestPortable ArchiveBinariesDependOnPath: true ManifestType: singleton ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/TëstExeInstaller.yaml ================================================ Id: AppInstallerTest.TëstExeInstaller Name: TestExeInstaller Version: 1.0.0.0 Publisher: AppInstallerTest License: Test Installers: - Arch: x86 Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: 0000000000000000000000000000000000000000000000000000000000000000 InstallerType: exe Switches: Custom: /execustom SilentWithProgress: /exeswp Silent: /exesilent Interactive: /exeinteractive Language: /exeenus Log: /exelog InstallLocation: /InstallDir ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_CorrectHash.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.6.0.schema.json PackageIdentifier: ZeroByteFile.CorrectHash PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: ZeroByteFile-CorrectHash ShortDescription: Points to a zero byte installer file using the correct hash Publisher: Microsoft Corporation License: Test Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/TestData/empty InstallerType: exe InstallerSha256: E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 ManifestType: singleton ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Manifests/ZeroByteFile_IncorrectHash.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.6.0.schema.json PackageIdentifier: ZeroByteFile.IncorrectHash PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: ZeroByteFile-IncorrectHash ShortDescription: Points to a zero byte installer file using the incorrect hash Publisher: Microsoft Corporation License: Test Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/TestData/empty InstallerType: exe InstallerSha256: BAD0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852BBAD ManifestType: singleton ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/Package/AppxManifest.xml ================================================ WinGet E2E Source Microsoft Corporation Assets\AppPackageStoreLogo.png ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/README.md ================================================ ## AppInstallerTestMsiInstaller.msi Due to MSI projects requiring a special extension, we have simply checked in a built version of the test MSI. To create a new one, which may be needed if the test EXE inside it needs to be updated: 1. Ensure that the extension is installed locally a. [Microsoft Visual Studio Installer Projects 2022](https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects) 2. Set configuration to "Release|x86" 3. Build AppInstallerTestMsiInstaller project 4. Check in new MSI over the one in TestData ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/WinGetUtil/InstallerMetadata/MergeSubmissionMismatch.json ================================================ { "version": "1.0", "metadatas": [ { "version": "1.0", "productVersionMin": "1.0", "productVersionMax": "1.0", "metadata": [ { "installerHash": "ABCD", "submissionIdentifier": "1", "AppsAndFeaturesEntries": [ { "DisplayName": "Name", "Publisher": "Publisher", "DisplayVersion": "1.0", "ProductCode": "{46210E3D-EBB3-43D4-B9BB-48A7DB0F9B93}", "InstallerType": 3 } ] } ] }, { "version": "1.0", "productVersionMin": "1.0", "productVersionMax": "1.0", "metadata": [ { "installerHash": "EFGH", "submissionIdentifier": "2", "AppsAndFeaturesEntries": [ { "DisplayName": "Name", "Publisher": "Publisher", "DisplayVersion": "1.0", "ProductCode": "{46210E3D-EBB3-43D4-B9BB-48A7DB0F9B93}", "InstallerType": 3 } ] } ] } ] } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/WinGetUtil/InstallerMetadata/MergeValid.json ================================================ { "version": "1.0", "metadatas": [ { "version": "1.0", "productVersionMin": "1.0", "productVersionMax": "1.0", "metadata": [ { "installerHash": "ABCD", "submissionIdentifier": "1", "AppsAndFeaturesEntries": [ { "DisplayName": "Name", "Publisher": "Publisher", "DisplayVersion": "1.0", "ProductCode": "{46210E3D-EBB3-43D4-B9BB-48A7DB0F9B93}", "InstallerType": 3 } ] } ] }, { "version": "1.0", "productVersionMin": "1.0", "productVersionMax": "1.0", "metadata": [ { "installerHash": "EFGH", "submissionIdentifier": "1", "AppsAndFeaturesEntries": [ { "DisplayName": "Name", "Publisher": "Publisher", "DisplayVersion": "1.0", "ProductCode": "{46210E3D-EBB3-43D4-B9BB-48A7DB0F9B93}", "InstallerType": 3 } ] } ] } ] } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/WinGetUtil/InstallerMetadata/Minimal.json ================================================ { "version": "1.0", "supportedMetadataVersion": "1.0", "submissionData": { "submissionIdentifier": "1" }, "packageData": { "installerHash": "ABCD", "DefaultLocale": { "PackageLocale": "en-us", "PackageName": "Name", "Publisher": "Publisher" } } } ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/WinGetUtil/Manifests/Merged/WinGetUtilTest.Add.yaml ================================================ Installers: - Architecture: x86 InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 InstallerType: exe InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe License: Test ManifestType: merged ManifestVersion: 1.1.0 MinimumOSVersion: 10.0.0.0 PackageIdentifier: AppInstallerTest.WinGetUtilTest PackageLocale: en-US PackageName: AppInstallerTest PackageVersion: 1.0.0.0 Publisher: AppInstallerTest Tags: - add ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/WinGetUtil/Manifests/Merged/WinGetUtilTest.Update.yaml ================================================ Installers: - Architecture: x86 InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 InstallerType: exe InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe License: Test ManifestType: merged ManifestVersion: 1.1.0 MinimumOSVersion: 10.0.0.0 PackageIdentifier: AppInstallerTest.WinGetUtilTest PackageLocale: en-US PackageName: AppInstallerTest PackageVersion: 1.0.0.0 Publisher: AppInstallerTest Tags: - update ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/WinGetUtil/Manifests/Unmerged/ValidateManifest/WinGetUtilTest.installer.yaml ================================================ PackageIdentifier: AppInstallerTest.WinGetUtilTest PackageVersion: "1.0.0.0" MinimumOSVersion: 10.0.0.0 Installers: - Architecture: x86 InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe InstallerType: nullsoft InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 ManifestType: installer ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/WinGetUtil/Manifests/Unmerged/ValidateManifest/WinGetUtilTest.locale.en-US.yaml ================================================ PackageIdentifier: AppInstallerTest.WinGetUtilTest PackageVersion: "1.0.0.0" PackageLocale: en-US Publisher: AppInstallerTest License: Test PackageName: AppInstallerTest ShortDescription: WinGetUtilTest ManifestType: defaultLocale ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/WinGetUtil/Manifests/Unmerged/ValidateManifest/WinGetUtilTest.yaml ================================================ PackageIdentifier: AppInstallerTest.WinGetUtilTest PackageVersion: "1.0.0.0" DefaultLocale: en-US ManifestType: version ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/empty ================================================ ================================================ FILE: src/AppInstallerCLIE2ETests/TestData/localsource.json ================================================ { "AppxManifest": "%BUILD_SOURCESDIRECTORY%/src/AppInstallerCLIE2ETests/TestData/Package/AppxManifest.xml", "WorkingDirectory": "%AGENT_TEMPDIRECTORY%/TestLocalIndex", "LocalManifests": [ "%BUILD_SOURCESDIRECTORY%/src/AppInstallerCLIE2ETests/TestData/Manifests" ], "LocalInstallers": [ { "Type": "exe", "Name": "AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe", "Input": "%BUILDOUTDIR%/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe", "HashToken": "" }, { "Type": "msi", "Name": "AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.msi", "Input": "%BUILD_SOURCESDIRECTORY%/src/AppInstallerCLIE2ETests/TestData/AppInstallerTestMsiInstaller.msi", "HashToken": "" }, { "Type": "msi", "Name": "AppInstallerTestMsiInstaller/AppInstallerTestMsiInstallerV2.msi", "Input": "%BUILD_SOURCESDIRECTORY%/src/AppInstallerCLIE2ETests/TestData/AppInstallerTestMsiInstallerV2.msi", "HashToken": "" }, { "Type": "msix", "Name": "AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.msix", "Input": "%BUILD_ARTIFACTSTAGINGDIRECTORY%/AppInstallerTestMsixInstaller.msix", "HashToken": "", "SignatureToken": "" }, { "Type": "font", "Name": "AppInstallerTestFont/AppInstallerTestFont.ttf", "Input": "%BUILD_SOURCESDIRECTORY%/src/AppInstallerCLIE2ETests/TestData/AppInstallerTestFont.ttf", "HashToken": "" } ], "DynamicInstallers": [ { "Type": "zip", "Name": "AppInstallerTestZipInstaller/AppInstallerTestZipInstaller.zip", "Input": [ "%AGENT_TEMPDIRECTORY%/TestLocalIndex/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe", "%AGENT_TEMPDIRECTORY%/TestLocalIndex/AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.msi", "%AGENT_TEMPDIRECTORY%/TestLocalIndex/AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.msix" ], "HashToken": "" } ], "Signature": { "CertFile": "%TestSigningCert_PfxPath%", "Password": "%TestSigningCert_Password%" } } ================================================ FILE: src/AppInstallerCLIE2ETests/UninstallCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test uninstall command. /// public class UninstallCommand : BaseCommand { // Custom product code for overriding the default in the test exe private const string CustomProductCode = "{f08fc03c-0b7e-4fca-9b3c-3a384d18a9f3}"; // File written when uninstalling the test exe private const string UninstallTestExeUninstalledFile = "TestExeUninstalled.txt"; // Name of a file installed by the MSI that will be removed during uninstall private const string UninstallTestMsiInstalledFile = "AppInstallerTestExeInstaller.exe"; // Package name of the test MSIX package private const string UninstallTestMsixName = "6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e"; /// /// Test uninstall exe. /// [Test] public void UninstallTestExe() { // Uninstall an Exe var installDir = TestCommon.GetRandomTestDir(); TestCommon.RunAICLICommand("install", $"{Constants.ExeInstallerPackageId} --silent -l {installDir}"); var result = TestCommon.RunAICLICommand("uninstall", Constants.ExeInstallerPackageId); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully uninstalled")); Assert.True(TestCommon.VerifyTestExeUninstalled(installDir)); } /// /// Test uninstall msi. /// [Test] public void UninstallTestMsi() { if (string.IsNullOrEmpty(TestIndex.MsiInstaller)) { Assert.Ignore("MSI installer not available"); } // Uninstall an MSI var installDir = TestCommon.GetRandomTestDir(); TestCommon.RunAICLICommand("install", $"{Constants.MsiInstallerPackageId} -l {installDir}"); var result = TestCommon.RunAICLICommand("uninstall", Constants.MsiInstallerPackageId); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully uninstalled")); Assert.True(TestCommon.VerifyTestMsiUninstalled(installDir)); } /// /// Test uninstall msix. /// [Test] public void UninstallTestMsix() { // Uninstall an MSIX TestCommon.RunAICLICommand("install", Constants.MsixInstallerPackageId); var result = TestCommon.RunAICLICommand("uninstall", Constants.MsixInstallerPackageId); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully uninstalled")); Assert.True(TestCommon.VerifyTestMsixUninstalled()); } /// /// Test uninstall msix package with machine scope. /// [Test] public void UninstallTestMsixMachineScope() { // TODO: Provision and Deprovision api not supported in build server. Assert.Ignore(); // Uninstall an MSIX TestCommon.RunAICLICommand("install", $"{Constants.MsixInstallerPackageId} --scope machine"); var result = TestCommon.RunAICLICommand("uninstall", $"{Constants.MsixInstallerPackageId} --scope machine"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully uninstalled")); Assert.True(TestCommon.VerifyTestMsixUninstalled(true)); } /// /// Test uninstall portable package. /// [Test] public void UninstallPortable() { // Uninstall a Portable string installDir = Path.Combine(System.Environment.GetEnvironmentVariable("LocalAppData"), "Microsoft", "WinGet", "Packages"); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; TestCommon.RunAICLICommand("install", $"{packageId}"); var result = TestCommon.RunAICLICommand("uninstall", $"{packageId}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully uninstalled")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, false); } /// /// Test uninstall portable package with product code. /// [Test] public void UninstallPortableWithProductCode() { // Uninstall a Portable with ProductCode string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; TestCommon.RunAICLICommand("install", $"{packageId}"); var result = TestCommon.RunAICLICommand("uninstall", $"--product-code {productCode}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully uninstalled")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, false); } /// /// Test uninstall portable package with modified symlink. /// [Test] public void UninstallPortableModifiedSymlink() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; TestCommon.RunAICLICommand("install", $"{packageId}"); string symlinkDirectory = TestCommon.GetPortableSymlinkDirectory(TestCommon.Scope.User); string symlinkPath = Path.Combine(symlinkDirectory, commandAlias); // Replace symlink with modified symlink File.Delete(symlinkPath); FileSystemInfo modifiedSymlinkInfo = File.CreateSymbolicLink(symlinkPath, "fakeTargetExe"); var result = TestCommon.RunAICLICommand("uninstall", $"{packageId}"); Assert.AreEqual(Constants.ErrorCode.ERROR_PORTABLE_UNINSTALL_FAILED, result.ExitCode); Assert.True(result.StdOut.Contains("Unable to remove Portable package as it has been modified; to override this check use --force")); Assert.True(modifiedSymlinkInfo.Exists, "Modified symlink should still exist"); // Try again with --force var result2 = TestCommon.RunAICLICommand("uninstall", $"{packageId} --force"); Assert.AreEqual(Constants.ErrorCode.S_OK, result2.ExitCode); Assert.True(result2.StdOut.Contains("Portable package has been modified; proceeding due to --force")); Assert.True(result2.StdOut.Contains("Successfully uninstalled")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, false); } /// /// Test uninstall zip portable package. /// [Test] public void UninstallZip_Portable() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestZipInstallerWithPortable"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = "TestPortable.exe"; fileName = "AppInstallerTestExeInstaller.exe"; var testResult = TestCommon.RunAICLICommand("install", $"{packageId}"); var result = TestCommon.RunAICLICommand("uninstall", $"{packageId}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully uninstalled")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, false); } /// /// Test uninstall not indexed. /// [Test] public void UninstallNotIndexed() { // Uninstalls a package found with ARP not matching any known manifest. // Install the test EXE providing a custom Product Code so that it cannot be mapped // back to its manifest, then uninstall it using its Product Code var installDir = TestCommon.GetRandomTestDir(); TestCommon.RunAICLICommand("install", $"{Constants.ExeInstallerPackageId} --override \"/ProductID {CustomProductCode} /InstallDir {installDir}"); var result = TestCommon.RunAICLICommand("uninstall", CustomProductCode); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully uninstalled")); Assert.True(TestCommon.VerifyTestExeUninstalled(installDir)); } /// /// Test uninstalled app not found. /// [Test] public void UninstallAppNotInstalled() { // Verify failure when trying to uninstall an app that is not installed. var result = TestCommon.RunAICLICommand("uninstall", $"TestMsixInstaller"); Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("No installed package found matching input criteria.")); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/UpgradeCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test upgrade command. /// public class UpgradeCommand : BaseCommand { private static readonly string DenyUpgradePackage = "AppInstallerTest.TestUpgradeDeny"; /// /// Tear down. /// [TearDown] public void TearDown() { // Due to its properties, this being present is problematic. TestCommon.RunAICLICommand("uninstall", DenyUpgradePackage); } /// /// Test upgrade portable package. /// [Test] public void UpgradePortable() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", "AppInstallerTest.TestPortableExe -v 1.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); var result2 = TestCommon.RunAICLICommand("upgrade", $"{packageId} -v 2.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, result2.ExitCode); Assert.True(result2.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test upgrade portable package. /// [Test] public void UpgradePortableNonAsciiPath() { string installDir = Path.Combine(TestCommon.GetRandomTestDir(), "Tést"); string packageId, commandAlias, fileName, productCode; packageId = "AppInstallerTest.TestPortableExe"; productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestPortableExe -v 1.0.0.0 -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); var result2 = TestCommon.RunAICLICommand("upgrade", $"{packageId} -v 2.0.0.0 -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result2.ExitCode); Assert.True(result2.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(installDir, commandAlias, fileName, productCode, true); } /// /// Test upgrade portable package with arp mismatch. /// [Test] public void UpgradePortableARPMismatch() { string packageId = "AppInstallerTest.TestPortableExe"; string productCode = packageId + "_" + Constants.TestSourceIdentifier; var installResult = TestCommon.RunAICLICommand("install", "AppInstallerTest.TestPortableExe -v 1.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("Successfully installed")); // Modify packageId to cause mismatch. TestCommon.ModifyPortableARPEntryValue(productCode, Constants.WinGetPackageIdentifier, "testPackageId"); var upgradeResult = TestCommon.RunAICLICommand("upgrade", $"{packageId} -v 2.0.0.0"); // Reset and perform uninstall cleanup TestCommon.ModifyPortableARPEntryValue(productCode, Constants.WinGetPackageIdentifier, packageId); TestCommon.RunAICLICommand("uninstall", $"--product-code {productCode}"); Assert.AreNotEqual(Constants.ErrorCode.S_OK, upgradeResult.ExitCode); Assert.True(upgradeResult.StdOut.Contains("Portable package from a different source already exists")); } /// /// Test upgrade portable package force override. /// [Test] public void UpgradePortableForcedOverride() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var installResult = TestCommon.RunAICLICommand("install", "AppInstallerTest.TestPortableExe -v 1.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); Assert.True(installResult.StdOut.Contains("Successfully installed")); // Modify packageId and sourceId to cause mismatch. TestCommon.ModifyPortableARPEntryValue(productCode, Constants.WinGetPackageIdentifier, "testPackageId"); TestCommon.ModifyPortableARPEntryValue(productCode, Constants.WinGetSourceIdentifier, "testSourceId"); var upgradeResult = TestCommon.RunAICLICommand("upgrade", $"{packageId} -v 2.0.0.0 --force"); Assert.AreEqual(Constants.ErrorCode.S_OK, upgradeResult.ExitCode); Assert.True(upgradeResult.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test upgrade portable package uninstall previous version. /// [Test] public void UpgradePortableUninstallPrevious() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId} -v 1.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); var result2 = TestCommon.RunAICLICommand("upgrade", $"{packageId} -v 3.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, result2.ExitCode); Assert.True(result2.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); } /// /// Test upgrade with deny behavior. /// [Test] public void UpgradeBehaviorDeny() { string packageId = DenyUpgradePackage; var result = TestCommon.RunAICLICommand("install", $"{packageId} -v 1.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); var result2 = TestCommon.RunAICLICommand("upgrade", $"{packageId} -v 2.0.0.0"); Assert.AreEqual(Constants.ErrorCode.APPINSTALLER_CLI_ERROR_INSTALL_UPGRADE_NOT_SUPPORTED, result2.ExitCode); Assert.True(result2.StdOut.Contains("package cannot be upgraded using WinGet")); } /// /// Test upgrade portable package machine scope. /// [Test] public void UpgradePortableMachineScope() { string installDir = TestCommon.GetRandomTestDir(); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, installDir); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestPortableExe"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"{packageId} -v 1.0.0.0 --scope machine"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); var result2 = TestCommon.RunAICLICommand("upgrade", $"{packageId} -v 2.0.0.0"); WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, string.Empty); Assert.AreEqual(Constants.ErrorCode.S_OK, result2.ExitCode); Assert.True(result2.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.Machine); } /// /// Test upgrade zip portable package. /// [Test] public void UpgradeZip_Portable() { string installDir = TestCommon.GetPortablePackagesDirectory(); string packageId, commandAlias, fileName, packageDirName, productCode; packageId = "AppInstallerTest.TestZipInstallerWithPortable"; packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; commandAlias = "TestPortable.exe"; fileName = "AppInstallerTestExeInstaller.exe"; var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithPortable -v 1.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Successfully installed")); var result2 = TestCommon.RunAICLICommand("upgrade", $"{packageId} -v 2.0.0.0"); Assert.AreEqual(Constants.ErrorCode.S_OK, result2.ExitCode); Assert.True(result2.StdOut.Contains("Successfully installed")); TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.User); } /// /// Test upgrade when a new dependency is added that is not installed. /// [Test] public void UpgradeAddsDependency() { var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestUpgradeAddsDependency -v 1.0 --verbose"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); result = TestCommon.RunAICLICommand("upgrade", $"AppInstallerTest.TestUpgradeAddsDependency"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/ValidateCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests { using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test validate command. /// public class ValidateCommand : BaseCommand { /// /// Test validate manifest. /// [Test] public void ValidateManifest() { var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestValidManifest.yaml")); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded.")); } /// /// Test validate manifest with extended characters. /// [Test] public void ValidateManifestWithExtendedCharacter() { var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TstExeInstaller.yaml")); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded.")); } /// /// Test validate invalid manifest. /// [Test] public void ValidateInvalidManifest() { var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestInvalidManifest.yaml")); Assert.AreEqual(Constants.ErrorCode.ERROR_MANIFEST_VALIDATION_FAILURE, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation failed.")); } /// /// Test validate manifest with warnings. /// [Test] public void ValidateManifestWithWarnings() { var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestWarningManifest.yaml")); Assert.AreEqual(Constants.ErrorCode.ERROR_MANIFEST_VALIDATION_WARNING, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded with warnings.")); } /// /// Test validate manifest with warnings suppressed. /// [Test] public void ValidateManifestSuppressWarnings() { var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestWarningManifest.yaml") + " --ignore-warnings"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.False(result.StdOut.Contains("Manifest validation succeeded with warnings.")); Assert.True(result.StdOut.Contains("Manifest validation succeeded.")); } /// /// Test validate manifest that doesn't exist. /// [Test] public void ValidateManifestDoesNotExist() { var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\DoesNotExist")); Assert.AreEqual(Constants.ErrorCode.ERROR_PATH_NOT_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("Path does not exist")); } /// /// Test validate manifest with invalid schema and expect warnings. /// [Test] public void ValidateManifestV1_10_SchemaHeaderExpectWarnings() { var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestWarningManifestV1_10-SchemaHeaderNotFound.yaml")); Assert.AreEqual(Constants.ErrorCode.ERROR_MANIFEST_VALIDATION_WARNING, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded with warnings.")); Assert.True(result.StdOut.Contains("Manifest Warning: Schema header not found.")); result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestWarningManifestV1_10-SchemaHeaderInvalid.yaml")); Assert.AreEqual(Constants.ErrorCode.ERROR_MANIFEST_VALIDATION_WARNING, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded with warnings.")); Assert.True(result.StdOut.Contains("Manifest Warning: The schema header is invalid. Please verify that the schema header is present and formatted correctly.")); result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestWarningManifestV1_10-SchemaHeaderURLPatternMismatch.yaml")); Assert.AreEqual(Constants.ErrorCode.ERROR_MANIFEST_VALIDATION_WARNING, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded with warnings.")); Assert.True(result.StdOut.Contains("Manifest Warning: The schema header URL does not match the expected pattern")); result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestWarningManifestV1_10-SchemaHeaderManifestTypeMismatch.yaml")); Assert.AreEqual(Constants.ErrorCode.ERROR_MANIFEST_VALIDATION_WARNING, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded with warnings.")); Assert.True(result.StdOut.Contains("Manifest Warning: The manifest type in the schema header does not match the ManifestType property value in the manifest.")); result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestWarningManifestV1_10-SchemaHeaderVersionMismatch.yaml")); Assert.AreEqual(Constants.ErrorCode.ERROR_MANIFEST_VALIDATION_WARNING, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded with warnings.")); Assert.True(result.StdOut.Contains("Manifest Warning: The manifest version in the schema header does not match the ManifestVersion property value in the manifest.")); } /// /// Test validate manifest with valid schema header. /// [Test] public void ValidateManifestV1_10_SchemaHeaderExpectNoWarning() { var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestGoodManifestV1_10-SchemaHeader.yaml")); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/WinGetUtil/WinGetUtilCompareVersions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.WinGetUtil { using NUnit.Framework; /// /// Test winget util compare versions. /// public class WinGetUtilCompareVersions { /// /// Test compare versions. /// /// Version 1. /// Version 2. /// Expected result. [Test] //// V1 = V2 [TestCase("1.0.0.0", "1.0.0.0", 0)] [TestCase("1.0.0", "1.0.0.0", 0)] [TestCase("1.0", "1.0.0.0", 0)] [TestCase("1", "1.0.0.0", 0)] //// V1 > V2 [TestCase("1.0.0.1", "1.0.0.0", 1)] [TestCase("1.0.1.0", "1.0.0.0", 1)] [TestCase("1.1.0.0", "1.0.0.0", 1)] [TestCase("2.0.0.0", "1.0.0.0", 1)] //// V1 < V2 [TestCase("1.0.0.0", "1.0.0.1", -1)] [TestCase("1.0.0.0", "1.0.1.0", -1)] [TestCase("1.0.0.0", "1.1.0.0", -1)] [TestCase("1.0.0.0", "2.0.0.0", -1)] public void WinGetUtil_CompareVersions(string version1, string version2, int expectedResult) { // Compare versions WinGetUtilWrapper.WinGetCompareVersions(version1, version2, out int result); Assert.AreEqual(expectedResult, result); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/WinGetUtil/WinGetUtilDownload.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.WinGetUtil { using System.IO; using System.Linq; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test winget util download. /// public class WinGetUtilDownload { /// /// Test download. /// [Test] public void WinGetUtil_Download() { uint hashSize = 32; byte[] sha256Hash = new byte[hashSize]; string installerUrl = @"https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe"; string filePath = TestCommon.GetRandomTestFile(".exe"); // Download WinGetUtilWrapper.WinGetDownload(installerUrl, filePath, sha256Hash, hashSize); Assert.True(File.Exists(filePath)); Assert.False(sha256Hash.All(byteVal => byteVal == 0)); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/WinGetUtil/WinGetUtilInstallerMetadataCollection.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.WinGetUtil { using System; using System.IO; using System.Runtime.InteropServices; using AppInstallerCLIE2ETests.Helpers; using Newtonsoft.Json; using NUnit.Framework; /// /// Test winget util installer metadata. /// public class WinGetUtilInstallerMetadataCollection { /// /// Test begin complete installer metadata. /// [Test] public void WinGetUtil_BeginCompleteInstallerMetadataCollection() { string logFilePath = TestCommon.GetRandomTestFile(".log"); string inputJson = TestCommon.GetTestDataFile(@"WinGetUtil\InstallerMetadata\Minimal.json"); string outputFilePath = TestCommon.GetRandomTestFile(".json"); WinGetUtilWrapper.WinGetBeginInstallerMetadataCollection( inputJson, logFilePath, WinGetUtilWrapper.WinGetBeginInstallerMetadataCollectionOptions.WinGetBeginInstallerMetadataCollectionOption_InputIsFilePath, out IntPtr collectionHandle); Assert.AreNotEqual(IntPtr.Zero, collectionHandle); Assert.True(File.Exists(logFilePath)); WinGetUtilWrapper.WinGetCompleteInstallerMetadataCollection( collectionHandle, outputFilePath, WinGetUtilWrapper.WinGetCompleteInstallerMetadataCollectionOptions.WinGetCompleteInstallerMetadataCollectionOption_None); string outputJson = File.ReadAllText(outputFilePath); Assert.IsNotEmpty(JsonConvert.DeserializeObject(outputJson).ToString()); } /// /// Test merge installer metadata. /// [Test] public void WinGetUtil_MergeInstallerMetadata_Success() { string logFilePath = TestCommon.GetRandomTestFile(".log"); string inputJsonPath = TestCommon.GetTestDataFile(@"WinGetUtil\InstallerMetadata\MergeValid.json"); string inputJson = File.ReadAllText(inputJsonPath); WinGetUtilWrapper.WinGetMergeInstallerMetadata( inputJson, out string outputJson, 0, logFilePath, WinGetUtilWrapper.WinGetMergeInstallerMetadataOptions.WinGetMergeInstallerMetadataOptions_None); Assert.True(File.Exists(logFilePath)); Assert.IsNotEmpty(JsonConvert.DeserializeObject(outputJson).ToString()); } /// /// Test merge installer metadata failed. /// [Test] public void WinGetUtil_MergeInstallerMetadata_Fail_SubmissionMismatch() { string logFilePath = TestCommon.GetRandomTestFile(".log"); string inputJsonPath = TestCommon.GetTestDataFile(@"WinGetUtil\InstallerMetadata\MergeSubmissionMismatch.json"); string inputJson = File.ReadAllText(inputJsonPath); Assert.Throws(() => { WinGetUtilWrapper.WinGetMergeInstallerMetadata( inputJson, out string outputJson, 0, logFilePath, WinGetUtilWrapper.WinGetMergeInstallerMetadataOptions.WinGetMergeInstallerMetadataOptions_None); }); Assert.True(File.Exists(logFilePath)); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/WinGetUtil/WinGetUtilLog.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.WinGetUtil { using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test winget util log. /// public class WinGetUtilLog { /// /// Test logging functions. /// [Test] public void WinGetUtil_Logging() { string filePath = TestCommon.GetRandomTestFile(".log"); // Init logging WinGetUtilWrapper.WinGetLoggingInit(filePath); Assert.True(File.Exists(filePath)); // Terminate logging WinGetUtilWrapper.WinGetLoggingTerm(filePath); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/WinGetUtil/WinGetUtilManifest.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.WinGetUtil { using System; using System.IO; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// Test winget util manifest. /// public class WinGetUtilManifest { private IntPtr indexHandle; /// /// Set up. /// [SetUp] public void SetUp() { this.indexHandle = IntPtr.Zero; var sqliteFile = TestCommon.GetRandomTestFile(".db"); uint majorVersion = 1; uint minorVersion = 2; WinGetUtilWrapper.WinGetSQLiteIndexCreate(sqliteFile, majorVersion, minorVersion, out this.indexHandle); } /// /// Tear down. /// [TearDown] public void TearDown() { WinGetUtilWrapper.WinGetSQLiteIndexClose(this.indexHandle); } /// /// Test validate manifest. /// /// Create manifest options. [Test] [TestCase(WinGetUtilWrapper.CreateManifestOption.NoValidation)] [TestCase(WinGetUtilWrapper.CreateManifestOption.SchemaAndSemanticValidation)] public void WinGetUtil_ValidateManifest_Success(WinGetUtilWrapper.CreateManifestOption createManifestOption) { string manifestsDir = TestCommon.GetTestDataFile(@"WinGetUtil\Manifests\Unmerged\ValidateManifest"); string mergedManifestPath = TestCommon.GetRandomTestFile(".yaml"); // Create manifest WinGetUtilWrapper.WinGetCreateManifest( manifestsDir, out bool succeeded, out IntPtr manifestHandle, out string createFailureMessage, mergedManifestPath, createManifestOption); Assert.True(succeeded); Assert.AreNotEqual(IntPtr.Zero, manifestHandle); Assert.IsNull(createFailureMessage); Assert.True(File.Exists(mergedManifestPath)); // Validate manifest WinGetUtilWrapper.WinGetValidateManifestV3( manifestHandle, this.indexHandle, out WinGetUtilWrapper.ValidateManifestResultCode resultCode, out string validateFailureMessage, WinGetUtilWrapper.ValidateManifestOptionV2.ArpVersionValidation, WinGetUtilWrapper.ValidateManifestOperationType.Add); Assert.AreEqual(WinGetUtilWrapper.ValidateManifestResultCode.Success, resultCode); Assert.IsEmpty(validateFailureMessage); // Close manifest WinGetUtilWrapper.WinGetCloseManifest(manifestHandle); } /// /// Test validate manifest with schema header. /// /// Create manifest options. [Test] [TestCase(WinGetUtilWrapper.CreateManifestOption.NoValidation)] [TestCase(WinGetUtilWrapper.CreateManifestOption.SchemaAndSemanticValidation)] public void WinGetUtil_ValidateManifest_V1_10_WithSchemaHeader_Success(WinGetUtilWrapper.CreateManifestOption createManifestOption) { string manifestsFilePath = TestCommon.GetTestDataFile(@"Manifests\TestGoodManifestV1_10-SchemaHeader.yaml"); // Create manifest WinGetUtilWrapper.WinGetCreateManifest( manifestsFilePath, out bool succeeded, out IntPtr manifestHandle, out string createFailureMessage, string.Empty, createManifestOption); Assert.True(succeeded); Assert.AreNotEqual(IntPtr.Zero, manifestHandle); Assert.IsNull(createFailureMessage); // Close manifest WinGetUtilWrapper.WinGetCloseManifest(manifestHandle); } /// /// Test validate manifest with schema header for failure scenarios. /// /// Create manifest options. [Test] [TestCase(WinGetUtilWrapper.CreateManifestOption.SchemaAndSemanticValidation)] public void WinGetUtil_ValidateManifest_V1_10_WithSchemaHeader_Failure(WinGetUtilWrapper.CreateManifestOption createManifestOption) { // Schema header not found string manifestsFilePath = TestCommon.GetTestDataFile(@"Manifests\TestWarningManifestV1_10-SchemaHeaderNotFound.yaml"); string expectedError = "Manifest Error: Schema header not found."; ValidateSchemaHeaderFailure(manifestsFilePath, createManifestOption, expectedError); // Schema header invalid manifestsFilePath = TestCommon.GetTestDataFile(@"Manifests\TestWarningManifestV1_10-SchemaHeaderInvalid.yaml"); expectedError = "Manifest Error: The schema header is invalid. Please verify that the schema header is present and formatted correctly."; ValidateSchemaHeaderFailure(manifestsFilePath, createManifestOption, expectedError); // Schema header URL pattern mismatch manifestsFilePath = TestCommon.GetTestDataFile(@"Manifests\TestWarningManifestV1_10-SchemaHeaderURLPatternMismatch.yaml"); expectedError = "Manifest Error: The schema header URL does not match the expected pattern."; ValidateSchemaHeaderFailure(manifestsFilePath, createManifestOption, expectedError); // Schema header manifest type mismatch manifestsFilePath = TestCommon.GetTestDataFile(@"Manifests\TestWarningManifestV1_10-SchemaHeaderManifestTypeMismatch.yaml"); expectedError = "Manifest Error: The manifest type in the schema header does not match the ManifestType property value in the manifest."; ValidateSchemaHeaderFailure(manifestsFilePath, createManifestOption, expectedError); // Schema header version mismatch manifestsFilePath = TestCommon.GetTestDataFile(@"Manifests\TestWarningManifestV1_10-SchemaHeaderVersionMismatch.yaml"); expectedError = "Manifest Error: The manifest version in the schema header does not match the ManifestVersion property value in the manifest."; ValidateSchemaHeaderFailure(manifestsFilePath, createManifestOption, expectedError); } private static void ValidateSchemaHeaderFailure(string manifestsFilePath, WinGetUtilWrapper.CreateManifestOption createManifestOption, string expectedError) { // Create manifest WinGetUtilWrapper.WinGetCreateManifest( manifestsFilePath, out bool succeeded, out IntPtr manifestHandle, out string createFailureMessage, string.Empty, createManifestOption); Assert.False(succeeded); Assert.AreEqual(IntPtr.Zero, manifestHandle); Assert.IsNotNull(createFailureMessage); Assert.IsTrue(createFailureMessage.Contains(expectedError)); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/WinGetUtil/WinGetUtilSQLiteIndex.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.WinGetUtil { using System; using System.IO; using System.Runtime.InteropServices; using AppInstallerCLIE2ETests.Helpers; using NUnit.Framework; /// /// WinGetUtil sql index tests. /// public class WinGetUtilSQLiteIndex { private readonly uint majorVersion = 1; private readonly uint minorVersion = 2; // Manifest example 1 private readonly string addManifestsFile = TestCommon.GetTestDataFile(@"WinGetUtil\Manifests\Merged\WinGetUtilTest.Add.yaml"); private readonly string updateManifestsFile = TestCommon.GetTestDataFile(@"WinGetUtil\Manifests\Merged\WinGetUtilTest.Update.yaml"); private readonly string relativePath = @"manifests\a\AppInstallerTest\WinGetUtilTest\1.0.0.0\WinGetTest.yaml"; private string sqlitePath; /// /// Set up. /// [SetUp] public void SetUp() { this.sqlitePath = TestCommon.GetRandomTestFile(".sql"); } /// /// Test add manifest. /// [Test] public void WinGetUtil_SQLiteIndex_AddManifest() { this.SQLiteIndex((indexHandle) => { // Add manifest WinGetUtilWrapper.WinGetSQLiteIndexAddManifest(indexHandle, this.addManifestsFile, this.relativePath); }); } /// /// Test update manifest. /// [Test] public void WinGetUtil_SQLiteIndex_UpdateManifest_Success() { this.SQLiteIndex((indexHandle) => { // Add manifest WinGetUtilWrapper.WinGetSQLiteIndexAddManifest(indexHandle, this.addManifestsFile, this.relativePath); // Update manifest WinGetUtilWrapper.WinGetSQLiteIndexUpdateManifest(indexHandle, this.updateManifestsFile, this.relativePath, out bool indexModified); Assert.True(indexModified); }); } /// /// Test update manifest file not found. /// [Test] public void WinGetUtil_SQLiteIndex_UpdateManifest_Fail_NotFound() { this.SQLiteIndex((indexHandle) => { // Update non-existing manifest Assert.Throws(() => { WinGetUtilWrapper.WinGetSQLiteIndexUpdateManifest(indexHandle, this.updateManifestsFile, this.relativePath, out bool indexModified); }); }); } /// /// Test remove manifest. /// [Test] public void WinGetUtil_SQLiteIndex_RemoveManifest_Success() { this.SQLiteIndex((indexHandle) => { // Add manifest WinGetUtilWrapper.WinGetSQLiteIndexAddManifest(indexHandle, this.addManifestsFile, this.relativePath); // Remove manifest WinGetUtilWrapper.WinGetSQLiteIndexRemoveManifest(indexHandle, this.addManifestsFile, this.relativePath); }); } /// /// Test remove manifest file not found. /// [Test] public void WinGetUtil_SQLiteIndex_RemoveManifest_Fail_NotFound() { this.SQLiteIndex((indexHandle) => { // Remove non-existing manifest Assert.Throws(() => { WinGetUtilWrapper.WinGetSQLiteIndexRemoveManifest(indexHandle, this.addManifestsFile, this.relativePath); }); }); } /// /// Test open and closing index. /// [Test] public void WinGetUtil_SQLiteIndex_OpenClose() { this.SQLiteIndex((_) => { // Open WinGetUtilWrapper.WinGetSQLiteIndexOpen(this.sqlitePath, out IntPtr indexHandle); // Add manifest WinGetUtilWrapper.WinGetSQLiteIndexAddManifest(indexHandle, this.addManifestsFile, this.relativePath); // Close WinGetUtilWrapper.WinGetSQLiteIndexClose(indexHandle); }); } /// /// Test check consistency. /// [Test] public void WinGetUtil_SQLiteIndex_CheckConsistency() { this.SQLiteIndex((indexHandle) => { // Add manifest WinGetUtilWrapper.WinGetSQLiteIndexAddManifest(indexHandle, this.addManifestsFile, this.relativePath); // Prepare for packaging WinGetUtilWrapper.WinGetSQLiteIndexPrepareForPackaging(indexHandle); // Check consistency WinGetUtilWrapper.WinGetSQLiteIndexCheckConsistency(indexHandle, out bool succeeded); Assert.True(succeeded); }); } /// /// Create and close an sqlite index file. /// /// Function to execute. private void SQLiteIndex(Action execute) { // Create WinGetUtilWrapper.WinGetSQLiteIndexCreate(this.sqlitePath, this.majorVersion, this.minorVersion, out IntPtr indexHandle); Assert.True(File.Exists(this.sqlitePath)); Assert.AreNotEqual(IntPtr.Zero, indexHandle); // Execute provided function execute(indexHandle); // Close WinGetUtilWrapper.WinGetSQLiteIndexClose(indexHandle); } } } ================================================ FILE: src/AppInstallerCLIE2ETests/WinGetUtil/WinGetUtilWrapper.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace AppInstallerCLIE2ETests.WinGetUtil { using System; using System.Runtime.InteropServices; /// /// Wrapper class for WinGetUtil exports. /// For more details about methods in this class visit WinGetUtil.h file. /// public class WinGetUtilWrapper { private const string DllName = @"WinGetUtil.dll"; /// /// Create manifest flags. /// [Flags] public enum CreateManifestOption { /// /// No validation. /// NoValidation = 0, /// /// Schema validation. /// SchemaValidation = 0x1, /// /// Schema and semantic validation. /// SchemaAndSemanticValidation = 0x2, /// /// Return error on verified publisher. /// ReturnErrorOnVerifiedPublisherFields = 0x1000, } /// /// Validate manifests results. /// [Flags] public enum ValidateManifestResultCode { /// /// Success. /// Success = 0, /// /// Dependencies validation failure. /// DependenciesValidationFailure = 0x1, /// /// Arp version validation failure. /// ArpVersionValidationFailure = 0x2, /// /// Installer validation failure. /// InstallerValidationFailure = 0x4, /// /// Single manifest package has dependencies. /// SingleManifestPackageHasDependencies = 0x10000, /// /// Multi manifest package has dependencies. /// MultiManifestPackageHasDependencies = 0x20000, /// /// Missing manifest dependencies. /// MissingManifestDependenciesNode = 0x40000, /// /// No suitable min version dependencies. /// NoSuitableMinVersionDependency = 0x80000, /// /// Found dependency loop. /// FoundDependencyLoop = 0x100000, /// /// Internal error. /// InternalError = 0x1000, } /// /// ValidateManifestOptionV2 flags. /// [Flags] public enum ValidateManifestOptionV2 { /// /// None. /// None = 0, /// /// Dependencies validation. /// DependenciesValidation = 0x1, /// /// Arp version validation. /// ArpVersionValidation = 0x2, /// /// Installer validation. /// InstallerValidation = 0x4, } /// /// Validate manifest operation type. /// public enum ValidateManifestOperationType { /// /// Add. /// Add = 0, /// /// Update. /// Update = 1, /// /// Delete. /// Delete = 2, } /// /// Begin installer metadata collection options. /// public enum WinGetBeginInstallerMetadataCollectionOptions { /// /// None. /// WinGetBeginInstallerMetadataCollectionOption_None = 0, /// /// Input is file path. /// WinGetBeginInstallerMetadataCollectionOption_InputIsFilePath = 0x1, /// /// Input is URI. /// WinGetBeginInstallerMetadataCollectionOption_InputIsURI = 0x2, } /// /// Complete installer metadata collection. /// public enum WinGetCompleteInstallerMetadataCollectionOptions { /// /// None. /// WinGetCompleteInstallerMetadataCollectionOption_None = 0, /// /// Abandon. /// WinGetCompleteInstallerMetadataCollectionOption_Abandon = 0x1, } /// /// Merge installer metadata. /// public enum WinGetMergeInstallerMetadataOptions { /// /// None. /// WinGetMergeInstallerMetadataOptions_None = 0, } /// /// WinGetCompareVersion from wingetutil.dll . /// /// Version. /// Other version. /// Result of comparison. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetCompareVersions(string version1, string version2, [MarshalAs(UnmanagedType.U4)] out int comparisonResult); /// /// WinGetDownload from wingetutil.dll . /// /// Url. /// File path where to download. /// SHA256 hash. /// SHA256 hash length. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetDownload(string url, string filePath, [MarshalAs(UnmanagedType.LPArray)] byte[] sha256Hash, uint sha256HashLength); /// /// WinGetLoggingInit from wingetutil.dll . /// /// Log path. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetLoggingInit(string logPath); /// /// WinGetLoggingTerm from wingetutil.dll . /// /// Log path. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetLoggingTerm(string logPath); /// /// WinGetCreateManifest from wingetutil.dll . /// /// Input path. /// Succeeded. /// Manifest handle. /// Failure message. /// Merge manifest path. /// Option. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetCreateManifest( string inputPath, [MarshalAs(UnmanagedType.U1)] out bool succeeded, out IntPtr manifestHandle, [MarshalAs(UnmanagedType.BStr)] out string failureMessage, string mergedManifestPath, CreateManifestOption option); /// /// WinGetCloseManifest from wingetutil.dll . /// /// Manifest. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetCloseManifest(IntPtr manifest); /// /// WinGetValidateManifestV3 from wingetutil.dll . /// /// Manifest handle. /// Index handle. /// Result. /// Failure message. /// Option. /// Operation type. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetValidateManifestV3( IntPtr manifestHandle, IntPtr indexHandle, out ValidateManifestResultCode result, [MarshalAs(UnmanagedType.BStr)] out string failureMessage, ValidateManifestOptionV2 option, ValidateManifestOperationType operationType); /// /// WinGetSQLiteIndexCreate from wingetutil.dll . /// /// File path. /// Major version. /// Minor version. /// Index. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetSQLiteIndexCreate(string filePath, uint majorVersion, uint minorVersion, out IntPtr index); /// /// WinGetSQLiteIndexOpen from wingetutil.dll . /// /// File path. /// Index. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetSQLiteIndexOpen(string filePath, out IntPtr index); /// /// WinGetSQLiteIndexClose from wingetutil.dll . /// /// Index. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetSQLiteIndexClose(IntPtr index); /// /// WinGetSQLiteIndexAddManifest from wingetutil.dll . /// /// Index. /// Manifest path. /// Relative path. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetSQLiteIndexAddManifest(IntPtr index, string manifestPath, string relativePath); /// /// WinGetSQLiteIndexUpdateManifest from wingetutil.dll . /// /// Index. /// Manifest path. /// Relative path. /// Index modified. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetSQLiteIndexUpdateManifest( IntPtr index, string manifestPath, string relativePath, [MarshalAs(UnmanagedType.U1)] out bool indexModified); /// /// WinGetSQLiteIndexRemoveManifest from wingetutil.dll . /// /// Index. /// Manifest path. /// Relative path. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetSQLiteIndexRemoveManifest(IntPtr index, string manifestPath, string relativePath); /// /// WinGetSQLiteIndexPrepareForPackaging from wingetutil.dll . /// /// Index. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetSQLiteIndexPrepareForPackaging(IntPtr index); /// /// WinGetSQLiteIndexCheckConsistency from wingetutil.dll . /// /// Index. /// Succeeded. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetSQLiteIndexCheckConsistency(IntPtr index, [MarshalAs(UnmanagedType.U1)] out bool succeeded); /// /// WinGetBeginInstallerMetadataCollection from wingetutil.dll . /// /// Input json. /// Log file path. /// Options. /// Collection handle. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetBeginInstallerMetadataCollection( string inputJSON, string logFilePath, WinGetBeginInstallerMetadataCollectionOptions options, out IntPtr collectionHandle); /// /// WinGetCompleteInstallerMetadataCollection from wingetutil.dll . /// /// Collection handle. /// Output file path. /// Options. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetCompleteInstallerMetadataCollection( IntPtr collectionHandle, string outputFilePath, WinGetCompleteInstallerMetadataCollectionOptions options); /// /// WinGetMergeInstallerMetadata from wingetutil.dll . /// /// Input json. /// Output json. /// Maximum output size in bytes. /// Log file path. /// Options. [DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)] public static extern void WinGetMergeInstallerMetadata( string inputJSON, [MarshalAs(UnmanagedType.BStr)] out string outputJSON, uint maximumOutputSizeInBytes, string logFilePath, WinGetMergeInstallerMetadataOptions options); } } ================================================ FILE: src/AppInstallerCLIPackage/AppInstallerCLIPackage.wapproj ================================================ 15.0 Debug x86 Release x86 Debug x64 Release x64 Debug ARM64 Release ARM64 Debug AnyCPU Release AnyCPU $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\ 6aa3791a-0713-4548-a357-87a323e7ac3a 10.0.26100.0 10.0.17763.0 en-US false ..\AppInstallerCLI\AppInstallerCLI.vcxproj Designer Designer copy "$(TargetDir)\resources.pri" "$(ProjectDir)\..\$(Platform)\$(Configuration)\AppInstallerCLI\resources.pri" copy "$(TargetDir)\resources.pri" "$(TargetDir)\AppInstallerCLI\resources.pri" win-arm64 win-x64 win-x86 $(SolutionDir) DotNet WindowsPackageManager.dll Microsoft.Management.Deployment.winmd Microsoft.Management.Configuration.dll Microsoft.Management.Configuration.winmd $(WinGetDotNetDirectoryName) true $(WinGetDotNetDirectoryName)\Microsoft.Management.Deployment.dll ExternalModules true . true %(WinGetAdditionalPackageFile.PackagePath) %(WinGetAdditionalPackageFile.PackagePath)\%(WinGetAdditionalPackageFile.RecursiveDir)%(WinGetAdditionalPackageFile.Filename)%(WinGetAdditionalPackageFile.Extension) ================================================ FILE: src/AppInstallerCLIPackage/Execute-AppxRecipe.ps1 ================================================ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=0)] [string]$AppxRecipePath, [string]$LayoutPath, [switch]$Force ) [xml]$Local:recipe = Get-Content $AppxRecipePath if ([System.String]::IsNullOrEmpty($LayoutPath)) { $LayoutPath = $Local:recipe.Project.PropertyGroup.LayoutDir } $Local:namespace = @{ ns = "http://schemas.microsoft.com/developer/msbuild/2003" } $Local:manifestElement = Select-Xml -Xml $Local:recipe -Namespace $Local:namespace -XPath "//ns:AppXManifest" | Select-Object -ExpandProperty Node $Local:packageFileElements = Select-Xml -Xml $Local:recipe -Namespace $Local:namespace -XPath "//ns:AppxPackagedFile" | Select-Object -ExpandProperty Node $Local:allItems = $Local:packageFileElements + $Local:manifestElement $Local:progressActivity = "Copying files to $LayoutPath" Write-Progress -Activity $Local:progressActivity if ($Force) { $null = New-Item -Path $LayoutPath -ItemType Directory -Force } else { New-Item -Path $LayoutPath -ItemType Directory -ErrorAction Inquire } [Int32]$Local:filesCopied = 0 $Local:allItems | ForEach-Object -Process { $Local:sourcePath = $_.Include $Local:destinationPath = Join-Path $LayoutPath $_.PackagePath Write-Verbose "$Local:sourcePath => $Local:destinationPath" $null = New-Item -Path ([System.IO.Path]::GetDirectoryName($Local:destinationPath)) -ItemType Directory -Force Copy-Item -Path $Local:sourcePath -Destination $Local:destinationPath $Local:filesCopied += 1 Write-Progress -Activity $Local:progressActivity -PercentComplete (($Local:filesCopied * 100) / $Local:allItems.Count) } Write-Progress -Activity $Local:progressActivity -Completed ================================================ FILE: src/AppInstallerCLIPackage/Package.appxmanifest ================================================ WinGet Dev CLI Microsoft Corporation Images\StoreLogo.png disabled disabled com.microsoft.winget.source .wingetdev WinGetDev configuration file Microsoft.Management.Configuration.winmd Microsoft.Management.Configuration.dll ================================================ FILE: src/AppInstallerCLIPackage/Register-WingetdevAutoComplete.ps1 ================================================ Register-ArgumentCompleter -Native -CommandName wingetdev -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) [Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new() $Local:word = $wordToComplete.Replace('"', '""') $Local:ast = $commandAst.ToString().Replace('"', '""') wingetdev complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } ================================================ FILE: src/AppInstallerCLIPackage/Shared/Strings/en-us/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Not Localized {Locked} This file is required to establish the basic expected subresource in the resource map. It is not used to facilitate localization with other projects. ================================================ FILE: src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Adjoined alias is not a flag: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Adjoined flag alias not found: '{0}' {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). The following arguments are available: Message displayed to inform the user about the available command line arguments. The following command aliases are available: Message displayed to inform the user about the available command line alias arguments. The following commands are available: Title displayed to inform the user about the available commands. Available As in "a new version is available to upgrade to". The following options are available: Message displayed to inform the user about the available options. The following sub-commands are available: Message displayed to inform the user about the available nested commands that run in context of the selected command. {0} upgrades available. {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Use the specified channel; default is general audience command Label displayed for a command to give the software. Filter results by command Description message displayed to inform the user about filtering the search results by a package command. The full command line for completion This command requires administrator privileges to execute. This command can be used to request context sensitive command line completion. The command line, cursor position, and word to be completed are passed in. The output is a set of potential values based on the inputs, with one possible value per line. Enables context sensitive command line completion Show no more than specified number of results (between 1 and 1000) Done Label displayed when an operation completes or is done executing. Find package using exact match Description message displayed to inform the user about finding an application package using an exact matching criteria. Experimental argument for demonstration purposes This command is an example on how to implement an experimental feature. To turn on go to 'winget settings' and enable experimentalCmd or experimentalArg features. {Locked="winget settings"} Experimental feature example Found a positional argument when none was expected: '{0}' {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. This feature is a work in progress, and may be changed dramatically or removed altogether in the future. To enable it, edit your settings ('winget settings') to include the experimental feature: '{0}' {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Shows the status of experimental features. Experimental features can be turned on via 'winget settings'. {Locked="winget settings"} Shows the status of experimental features Disabled Enabled Feature Link The following experimental features are in progress. They can be configured through the settings file 'winget settings'. {Locked="winget settings"} Property Status File to be hashed Flag argument cannot contain adjoined value: '{0}' {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Computes the hash of a local file, appropriate for entry into a manifest. It can also compute the hash of the signature file of an MSIX package to enable streaming installations. Helper to hash installer files Shows help about the selected command For more details on a specific command, pass it the help argument. More help can be found at: {0} {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Filter results by id Suppresses warning outputs This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. This package is provided through Microsoft Store. WinGet may need to acquire the package from Microsoft Store on behalf of the current user. {Locked="WinGet"} Installs the selected package, either found by searching a configured source or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. By default, install command will check package installed status and try to perform an upgrade if applicable. Override with --force to perform a direct install. {Locked="--force"}; id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Installs the given package Installer hash does not match; this cannot be overridden when running as admin Installer hash does not match; proceeding due to --ignore-security-hash {Locked="--ignore-security-hash"} Installer hash does not match; to override this check use --ignore-security-hash {Locked="--ignore-security-hash"} Successfully verified installer hash Successfully installed Starting package install... Ignore the installer hash check failure Ignore the malware scan performed as part of installing an archive type package from local manifest Request interactive installation; user input may be needed Argument alias was not recognized for the current command: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Invalid argument specifier: '{0}' {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). Argument name was not recognized for the current command: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). WinGet Directories {Locked="WinGet"} Header for a table detailing the directories WinGet uses for key operations like logging and portable installs Locale to use (BCP47 format) {Locked="BCP47"} License Agreement Links Links to different webpages The list command displays the packages installed on the system, as well as whether an upgrade is available. Additional options can be provided to filter the output, much like the search command. {Locked="list","search"} Display installed packages Location to install to (if supported) Log location (if supported) © 2026 Microsoft. All rights reserved. Homepage The primary webpage for the software The path to the manifest of the package Manifest validation failed. Manifest validation succeeded. Manifest validation succeeded with warnings. Argument value required, but none found: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Filter results by moniker Input file will be treated as msix; signature hash will be provided if signed Failed to calculate MSIX signature hash. Failed to install or upgrade Microsoft Store package because the specific app is blocked by policy Failed to install or upgrade Microsoft Store package. Error code: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Failed to install or upgrade Microsoft Store package because Microsoft Store client is blocked by policy Verifying/Requesting package acquisition... Multiple installed packages found matching input criteria. Please refine the input. Multiple packages found matching input criteria. Please refine the input. Filter results by name No applicable installer found; see logs for more details. There are currently no experimental features available. No installed package found matching input criteria. No package found matching input criteria. Disables VirtualTerminal display {Locked="VirtualTerminal"} Open the default logs location options Options to change how a command works Override arguments to be passed on to the installer Package: {0} {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. Oops, we forgot to do this... The position of the cursor within the command line Privacy Statement The query used to search for a package Progress display a rainbow of colors Required argument not provided: '{0}' {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Progress display as the default color Searches for packages from configured sources. Find and show basic info of packages Id Abbreviation of Identifier. Match Name Source additional entries truncated due to result limit Version The following failures were found validating the settings: Open settings in the default json text editor. If no editor is configured, opens settings in notepad. For available settings see https://aka.ms/winget-settings This command can also be used to set administrator settings by providing the --enable or --disable arguments {Locked="--enable"} {Locked="--disable"} Open settings or set administrator settings Unexpected error while loading settings. Please verify your settings by running the 'settings' command. {Locked="'settings'"} Channel Shows information on a specific package. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Shows information about a package Version Request silent installation Only the single character alias can occur after a single -: '{0}' {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. A source with the given name already exists and refers to a different location: A source with a different name already refers to this location: A source with the given name already exists and refers to the same location: Adding source: Add a new source. A source provides the data for you to discover and install packages. Only add a new source if you trust it as a secure location. Add a new source Argument given to the source Find package using the specified source Manage sources with the sub-commands. A source provides the data for you to discover and install packages. Only add a new source if you trust it as a secure location. Manage sources of packages Edit properties of an existing source. A source provides the data for you to discover and install packages. Edit properties of a source Editing source: {0} {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. The source named '{0}' is already in the desired state. {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being edited. {0} is a placeholder replaced by the repository source name. Argument Value given to source. List all current sources, or full details of a specific source. List current sources Data Data stored by the source. Field The name of a piece of information about a source. Name The name of the source. Identifier The source's unique identifier. Did not find a source named: {0} Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. There are no sources configured. Type The kind of source. Updated The last time the source was updated. never The source has never been updated. Value The value of information about a source. Name of the source Failed when opening source(s); try the 'source reset' command if the problem persists. {Locked="source reset"} Failed to open the predefined source; please report to WinGet maintainers. {Locked="WinGet"} Removing all sources... Remove a specific source. Remove current sources Removing source: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Resetting all sources... This command drops existing sources, potentially leaving any local data behind. Without any argument, it will drop all sources and add the defaults. If a named source is provided, only that source will be dropped. Reset sources Forces the reset of the sources The following sources will be reset if the --force option is given: {Locked="--force"} Resetting source: {0}... {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Type of the source Updating all sources... Update all sources, or only a specific source. Update current sources Updating source: {0}... {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Filter results by tag Thank you for using WinGet {Locked="WinGet"} Third Party Notices The winget command line utility enables installing applications and other packages from the command line. Display general info of the tool Display the version of the tool Argument provided more times than allowed: '{0}' {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. More than one execution behavior argument provided: '{0}' {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). An unexpected error occurred while executing the command: Uninstall the previous version of the package during upgrade Unrecognized command: '{0}' {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Upgrade all installed packages to latest if available No applicable upgrade found. A newer package version is available in a configured source, but it does not apply to your system or requirements. No available upgrade found. No newer package versions are available from the configured sources. Upgrades the selected package, either found by searching the installed packages list or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. When no arguments are given, shows the packages with upgrades available id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Shows and performs available upgrades usage: {0} {1} {Locked="{0} {1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Validates a manifest using a strict set of guidelines. This is intended to enable you to check your manifest before submitting to a repo. Validates a manifest file The path to the manifest to be validated Enables verbose logging for winget Please verify that the input file is a valid, signed MSIX. Use the specified version; default is the latest version Show available versions of the package The value provided before completion is requested No version found matching: {0} {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. No sources match the given value: {0} {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. The configured sources are: No sources defined; add one with 'source add' or reset to defaults with 'source reset' {Locked="source add","source reset"} Found Path is a directory: {0} {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. File does not exist: {0} {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Both local manifest and search query arguments are provided Logs Label displayed for diagnostic files containing information about the application use. The installer is blocked by policy The installer failed security check An anti-virus product reports an infection in the installer Failed in attempting to update the source: {0} {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Uninstalls the selected package, either found by searching the installed packages list or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Uninstalls the given package Starting package uninstall... Successfully uninstalled WinGet cannot locate the uninstall command for this package. Please reach out to the package publisher for support. {Locked="WinGet"} Uninstallation abandoned Uninstall failed with exit code: {0} {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Exports a list of the installed packages Installs all the packages listed in a file. Installs all the packages in a file File where the result is to be written File describing the packages to install Export packages from the specified source Writes a list of the installed packages to a file. The packages can then be installed with the import command. {Locked="import"} One or more imported packages failed to install Source required for import is not installed: {0} {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. Installed package is not available from any source: {0} {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. Installed version of package is not available from any source: {0} {1} {2} {Locked="{0} {1} {2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. No packages found in import file JSON file is not valid Package is already installed: {0} {Locked="{0}"} Message displayed to inform the user that an application package is already installed. {0} is a placeholder replaced by the package identifier or search query. Ignore unavailable packages Include package versions in export file Ignore package versions from import file Path does not exist: {0} {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. The JSON file does not specify a recognized schema. Select install scope (user or machine) {Locked="user","machine"} This argument allows the user to select between installing for just the user or for the entire machine. The value provided for the `{0}` argument is invalid; valid values are: {1} {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. This operation is disabled by Group Policy: {0} {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Enable Additional Windows App Installer Sources Enable Windows App Installer Allowed Sources Enable Windows App Installer Default Source Enable Windows App Installer Experimental Features Enable Windows App Installer Microsoft Store Source Enable Windows App Installer Font Source Enable Windows Package Manager Settings Enable Windows Package Manager Enable Windows Package Manager command line interfaces Set Windows Package Manager Source Auto Update Interval In Minutes Group Policy Header for a table listing active Group Policies Enable Windows App Installer Local Manifest Files Enable Windows App Installer Microsoft Store Source Pinned Certificate Bypass Enable Windows App Installer Local Archive Malware Scan Override Field: {0} {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. Invalid field format. Invalid field value. Invalid setting from Group Policy. Loaded settings from backup file. Error parsing file: Value: {0} {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. The following experimental features are in progress. Configuration is disabled due to Group Policy. Installer hash does not match. Enable Windows App Installer Hash Override Export current sources as JSON for Group Policy. Export current sources Additional source An additional source required by policy. Allowed source A source that the user is allowed to add. The value provided for the `{0}` argument is invalid {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Cancelled External The packages found in this import have the following dependencies: Import command sentence showed before reporting dependencies This package requires the following dependencies: Message shown before reporting dependencies Installing dependencies: Dependency source not found Package search yield more than one result: {0} {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. Latest version not found for package: {0} {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. No installers found: {0} {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Minimum required version not available for package: {0} {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. No matches When package search yields no matches Has loop Dependency graph has loop No suitable installer found for manifest: {0} version {1} {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Error processing package dependencies. Do you wish to continue installation? Prompt message shown when dependencies processing yields errors. Error processing package dependencies. Exiting... Packages This package had dependencies that may not be needed anymore: Uninstall command sentence showed before reporting dependencies Manifest has the following dependencies that were not validated; ensure that they are valid: Validate command sentence showed before reporting dependencies Windows Features Windows Libraries Find package dependencies using the specified source For getting package type dependencies when installing from a local manifest Windows Store Terms Accept all license agreements for packages Exported package requires license agreement to install: {0} {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. The publisher requires that you view the above information and accept the agreements before installing. Do you agree to the terms? Package agreements were not agreed to. Operation cancelled. Agreements: Author: Description: Installer: Installer Locale: Store Product Id: Installer SHA256: Installer Type: Installer Url: License: License Url: Moniker: Homepage: Publisher: Tags: Version: Dependencies: Windows Features: Windows Libraries: Package Dependencies: External Dependencies: No Yes Failed to open the added source. Accept all source agreements during source operations The `{0}` source requires that you view the following agreements before using. {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. Do you agree to all the source agreements terms? One or more of the source agreements were not agreed to. Operation cancelled. Please accept the source agreements or remove the corresponding sources. The source requires the current machine's 2-letter geographic region to be sent to the backend service to function properly (ex. "US"). Successfully installed. Restart the application to complete the upgrade. Optional Windows-Package-Manager REST source HTTP header Ignoring the optional header as it is not applicable for this source. The optional header is not applicable without specifying a source: '{0}' {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Release Date: Offline Distribution Supported: Publisher Url: Purchase Url: Publisher Support Url: Privacy Url: Copyright: Copyright Url: Release Notes: Release Notes Url: Failed when searching source; results will not be included: {0} {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Failed when searching source: {0} {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. This feature needs to be enabled by administrators. To enable it, run 'winget settings --enable {0}' as administrator. {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Enables the specific administrator setting Disables the specific administrator setting Enabled admin setting '{0}'. {Locked="{0}"} Message displayed when the user enables an admin setting. {0} is a placeholder replaced by the setting name. Disabled admin setting '{0}'. {Locked="{0}"} Message displayed when the user disables an admin setting. {0} is a placeholder replaced by the setting name. Admin Setting Header for a table displaying admin settings. Application is currently running. Exit the application then try again. Files modified by the installer are currently in use by a different application. Exit the applications then try again. Another installation is already in progress. Try again later. One or more files are being used. Exit the application then try again. This package has a dependency missing from your system. There's no more space on your PC. Make space, then try again. There's not enough memory available to install. Close other applications then try again. One of the installation parameters is invalid. The package installation log may have additional details. This application requires internet connectivity. Connect to a network then try again. This application encountered an error during installation. Contact support. Restart your PC to finish installation. Your PC will restart to finish installation. Installation failed. Restart your PC then try again. You cancelled the installation. Another version of this application is already installed. A higher version of this application is already installed. Organization policies are preventing installation. Contact your admin. The current system configuration does not support the installation of this package. Installation failed with a custom installer error. Contact package support. Installation abandoned Installer failed with exit code: {0} {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. Installer log is available at: {0} {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. The following packages were found among the working sources. Please specify one of them using the --source option to proceed. {Locked="--source"} "working sources" as in "sources that are working correctly" No packages were found among the working sources. "working sources" as in "sources that are working correctly" This is a stable release of the Windows Package Manager. If you would like to try experimental features, please install a pre-release build. Instructions are available on GitHub at https://github.com/microsoft/winget-cli. {Locked="https://github.com/microsoft/winget-cli"} Windows Package Manager v{0} {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. Ignore package versions in import file The requested number of results must be between 1 and 1000. Arguments to be passed on to the installer in addition to the defaults A newer version was found, but the install technology is different from the current version installed. Please uninstall the package and install the newer version. The install technology of the newer version specified is different from the current version installed. Please uninstall the package and install the newer version. Windows Package Manager (Preview) v{0} {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Select the architecture Upgrade packages even if their current version cannot be determined List packages even if their current version cannot be determined. Can only be used with the --upgrade-available argument {Locked="--upgrade-available"} This package's version number cannot be determined. To upgrade it anyway, add the argument --include-unknown to your previous command. {Locked="--include-unknown"} {0} package(s) have version numbers that cannot be determined. Use --include-unknown to see all results. {Locked="{0}","--include-unknown"} {0} is a placeholder that is replaced by an integer number of packages that do not have notated versions. {0} package(s) are pinned and need to be explicitly upgraded. {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages that require explicit upgrades. The arguments provided can only be used with a query. System Architecture: {0} {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). Retains all files and directories created by the package (portable) Deletes all files and directories in the package directory (portable) Press Enter to continue . . . The value to rename the executable file (portable) Prompts the user to press any key before exiting The installer will request to run as administrator. Expect a prompt. The installer cannot be run from an administrator context. Path environment variable modified; restart your shell to use the new value. Filters using the product code A portable package with the same name but from a different source already exists; proceeding due to --force {Locked="--force"} The volume does not support reparse points The specified filename is not a valid filename Overwriting existing file: {0} {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. No package selection argument was provided; see the help for details about finding a package. Command line alias added: Portable Links Directory (Machine) Portable Links Directory (User) Portable Package Root (User) Portable Package Root Portable Package Root (x86) Portable install failed; Cleaning up... Portable package has been modified; proceeding due to --force {Locked="--force"} Unable to remove Portable package as it has been modified; to override this check use --force {Locked="--force"} Files remain in install directory: {0} {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Purging install directory... Cannot purge install directory as it was not created by WinGet {Locked="WinGet"} Related Link Documentation: Notes: {0} {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Installation Notes: A provided argument is not supported for this package Failed to extract the contents of the archive Nested installer file does not exist. Ensure the specified relative path of the nested installer matches: {0} {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Invalid relative file path to the nested installer; path points to a location outside of the install directory No nested installers specified for this package Only one nested installer can be specified for an archive installer unless it is a portable or font nested installer Incompatible command line arguments provided Lists only packages which have an upgrade available The following packages have an upgrade available, but require explicit targeting for upgrade: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly Downloading Label displayed while downloading an application installer. The nested installer type is not supported This installer is known to restart the terminal or shell This package requires an install location The following installers are known to restart the terminal or shell: The following installers require an install location: Specify the install root: Do you want to proceed? Agreements for This will be followed by a package name, and then a list of license agreements Install location is required by the package but it was not provided Disable interactive prompts Description for a command line argument, shown next to it in the help Found an existing package already installed. Trying to upgrade the installed package... Direct run the command and continue with non security related issues Description for a command line argument, shown next to it in the help Portable package from a different source already exists Successfully extracted archive Extracting archive... Archive scan detected malware. To override this check use --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} Archive scan detected malware. Proceeding due to --ignore-local-archive-malware-scan {Locked="--ignore-local-archive-malware-scan"} Skips upgrade if an installed version already exists A package version is already installed. Installation cancelled. Cannot enable {0}. This setting is controlled by policy. For more information contact your system administrator. {Locked="{0}"} The value will be replaced with the feature name Cannot disable {0}. This setting is controlled by policy. For more information contact your system administrator. {Locked="{0}"} The value will be replaced with the feature name Add a new pin. A pin can limit the Windows Package Manager from upgrade a package to specific ranges of versions, or it can prevent it from upgrading the package altogether. A pinned package may still upgrade on its own and be upgraded from outside the Windows Package Manager. By default, a pinned package can be upgraded by mentioning it explicitly in the 'upgrade' command or by adding the '--include-pinned' flag to 'winget upgrade --all'. {Locked{"'upgrade'"} Locked{"--include-pinned"} Locked{"winget upgrade --all"} Add a new pin Manage package pins with the sub-commands. A pin can limit the Windows Package Manager from upgrading a package to specific ranges of versions, or it can prevent it from upgrading the package altogether. A pinned package may still upgrade on its own and be upgraded from outside the Windows Package Manager. Manage package pins List all current pins, or full details of a specific pin. List current pins Remove a specific package pin. Remove a package pin Reset all existing pins. Reset pins Version to which to pin the package. The wildcard '*' can be used as the last version part Block from upgrading until the pin is removed, preventing override arguments Pin a specific installed version Installed Value used in a table to indicate that a package comes from the list of packages installed in the machine Export settings as JSON Export settings User Settings Label displayed for the file containing the user settings. Settings file couldn't load. Using default values. Select installed package scope filter (user or machine) {Locked="user","machine"} This argument allows the user to select installed packages for just the user or for the entire machine. Pin added successfully There is already a pin for package {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to add a pin for a package that is already pinned. A pin already exists for package {0}. Overwriting due to the --force argument. {Locked="--force"}{Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. A pin already exists for package {0}. Use the --force argument to overwrite it. {Locked="--force","{0}"} {0} is a placeholder that will be replaced by a package name. Resetting all current pins. Use the --force argument to reset all pins. The following pins would be removed: {Locked="--force"} Pin type There is no pin for package {0} {Locked="{0}"} {0} is a placeholder that will be replaced by a package name. The message is shown when attempting to delete a pin for a package that is not pinned. There are no pins configured. Shown when listing or modifying existing pins if there are none. Pins reset successfully Shown after resetting (deleting) all the pins Unable to open pin database. Error message for when we cannot open the database containing package pins. An argument was provided that can only be used for single package Multiple mutually exclusive arguments provided: {0} {Locked="{0}"} Error message shown when mutually incompatible command line arguments are used. {0} is a placeholder replaced by the arguments that cannot be specified together Argument {0} can only be used with {1} {Locked="{0}","{1}"} Error message shown when having an argument needs another to be present, but that is missing. {0} and {1} are replaced by the arguments. For example "Argument --include-unknown can only be used with --upgrade" Disabled As in enabled/disabled Enabled As in enabled/disabled State Header for a table listing the state (enabled/disabled) of Group Policies and Settings The query used to search for a package Package not found: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns no results. {0} is a placeholder replaced by the package name or query. Multiple packages found for: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches returns more than one result. {0} is a placeholder replaced by the package name or query. Search failed for: {0} {Locked="{0}"} Error message displayed when the user attempts to search for multiple application packages and one of the searches fails. This message is for generic failures, we have more specific messages for when the search returns no results, or when it returns more than one result. {0} is a placeholder replaced by the package name or query. {0} package(s) have a pin that needs to be removed before upgrade {Locked="{0}"} {0} is a placeholder that is replaced by an integer number of packages with pins that prevent upgrade The package cannot be upgraded using WinGet. Please use the method provided by the publisher for upgrading this package. {Locked="WinGet"} Upgrade packages even if they have a non-blocking pin List packages even if they have a pin that prevents upgrade. Can only be used with the --upgrade-available argument {Locked="--upgrade-available"} Pin removed successfully A newer version was found, but the package has a pin that prevents from upgrading it. The package is pinned and cannot be upgraded. Use the 'winget pin' command to view and edit pins. Some pin types can be bypassed with the --include-pinned argument. {Locked="winget pin","--include-pinned"} Error shown when we block an upgrade due to the package being pinned {0} package(s) have pins that prevent upgrade. Use the 'winget pin' command to view and edit pins. Using the '--include-pinned' argument may show more results. {Locked="winget pin","--include-pinned"} {0} is a placeholder replaced by an integer number of packages Ensures that the system matches the desired state as described by the provided configuration. May download/execute processors in order to achieve the desired state. The configuration and the processors should be checked to ensure that they are trustworthy before applying them. Configures the system into a desired state Shows details of the provided configuration. By default, will not modify the system, but some options will cause files to be downloaded and/or loaded. Shows details of a configuration Checks that the system matches the desired state as described by the provided configuration. May download/execute processors in order to test the desired state. The configuration and the processors should be checked to ensure that they are trustworthy before executing them. Checks the system against a desired state Validates a configuration file for correctness. Validates a configuration file The field '{0}' in the configuration file is the wrong type. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. The path to the configuration file The configuration file is invalid. Configuration file version {0} is not known. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the version of the configuration file. Accepts the configuration warning, preventing an interactive prompt Apply Indicates that this item is used to write state Assert Indicates that this item is used to check/assert the state rather than write to it Dependencies:{0} {Locked="{0}"} Label displaying a list of dependencies. {0} is replaced with a space separated list of identifiers referencing other items. Some of the configuration was not applied successfully. Failed to get detailed information about the configuration. Inform Indicates that this item is used to retrieve values for future use rather than writing them Local Used to indicate that the item is present on the device. Module: {0} {Locked="{0}"} Label displaying a module name. {0} is replaced with the name of the module from the user input file. Module: {0} by {1} [{2}] {Locked="{0}","{1}","{2}"} Label displaying module information. {0} is replaced by the module name. {1} is replaced by the module author. {2} is replaced by a string indicating the source of the module. Settings: Label for the values that are used as inputs for this item when applying state Configuration successfully applied. Unit successfully applied. Another configuration is being applied to the system. This configuration will continue as soon as is possible... You are responsible for understanding the configuration settings you are choosing to execute. Microsoft is not responsible for the configuration file you have authored or imported. This configuration may change settings in Windows, install software, change software settings (including security settings), and accept user agreements to third-party packages and services on your behalf.  By running this configuration file, you acknowledge that you understand and agree to these resources and settings. Any applications installed are licensed to you by their owners. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages or services. Legal approved. Do not change without approval. Have you reviewed the configuration and would you like to proceed applying it to the system? PM approved. The configuration is empty. The feature [{0}] was not found. {Locked="{0}"} Error message displayed when a Windows feature was not found on the system. Failed to enable Windows Feature dependencies. To proceed with installation, use '--force'. {Locked="--force"} Reboot required to fully enable the Windows Feature(s); to override this check use '--force'. "Windows Feature(s)" is the name of the options Windows features setting. Failed to enable Windows Feature dependencies; proceeding due to --force "Windows Feature(s)" is the name of the options Windows features setting. {Locked="--force"} Enabling [{0}]... {Locked="{0}"} Message displayed to the user regarding which Windows Feature is being enabled. Failed to enable [{0}] feature: {1} {Locked="{0}","{1}"} An error when enabling a Windows Feature. {0} is a placeholder for the name of the Windows Feature. {1} is a placeholder for the unrecognized error code. Reboot required to fully enable the Windows Feature(s); proceeding due to --force "Windows Feature(s)" is the name of the options Windows features setting. Waiting for another install/uninstall to complete... Pinned version Table header for the version to which a package is pinned; meaning it should not update from that version. <See the log file for additional details> The brackets are intended to make the value stand out from other text which it will follow. Any locale appropriate mechanism that achieves this is acceptable. Retrieving configuration details Initializing configuration system Reading configuration file The system is not in the desired state asserted by the configuration. This configuration unit failed for an unknown reason: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. The configuration unit failed due to the configuration: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. The configuration unit failed while attempting to get the current system state. The configuration unit failed while attempting to apply the desired state. The configuration unit failed while attempting to test the current system state. The configuration unit failed due to an internal error: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. The configuration unit failed due to a precondition not being valid: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. The configuration unit failed due to the system state: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. The configuration unit failed while attempting to run: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. The configuration contains the identifier `{0}` multiple times. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. The dependency `{0}` was not found within the configuration. {Locked="{0}"} {0} is a placeholder that is replaced by the identifier string from the user input file. This configuration unit was manually skipped. The module for the configuration unit is available in multiple locations with the same version. Loading the module for the configuration unit failed. Multiple matches were found for the configuration unit; specify the module to select the correct one. The configuration unit could not be found. The configuration unit was not in the module as expected. This configuration unit was not run because a dependency failed or was not run. This configuration unit was not run because an assert failed or was false. The configuration unit returned an unexpected result during execution. This configuration unit was not run for an unknown reason: {0} {Locked="{0}"} {0} is a placeholder for the unrecognized error code. The field '{0}' has an invalid value: {1} {Locked="{0}","{1}"} An error in reading a configuration file. {0} is a placeholder replaced by the field name from the file. {1} is a placeholder for the invalid value. The field '{0}' is missing or empty. {Locked="{0}"} An error in reading a configuration file. {0} is a placeholder replaced by the expected field name from the file. See line {0}, column {1} in the file. {Locked="{0}","{1}"} Indicates the file location of the error, {0} and {1} are placeholders for numbers of the line and column, respectively. Cancelling operation Install the stub package for AppInstaller Install the full package for AppInstaller Enable extended features. Requires store access. Option '--enable' and '--disable' cannot be used with other arguments. {Locked="--enable", "--disable"} Enabling extended features. Requires store access. Extended features are not enabled. Run `winget configure --enable` to enable them. {Locked="winget configure --enable"} Extended features are enabled. Disable extended features. Requires store access. Disabling extended features. Requires store access. Extended features are disabled. Skips processing package dependencies and Windows features Installs only the dependencies of the package Installing dependencies only. The package itself will not be installed. Dependencies skipped. Failed to refresh PATH variable for process. Subsequent installs that depend on changes to the PATH variable may fail. {Locked="PATH"} Some of the configuration units failed while testing their state. System is in the described configuration state. Configuration state was not tested. System is not in the described configuration state. Unexpected test result: {0} {Locked="{0}"} Error message. {0} will be replaced with the unexpected value (a number). Have you reviewed the configuration and would you like to proceed verifying it against the system? The configuration file is not a valid YAML file. {Locked="YAML"} YAML is a file format name. This configuration unit is part of a dependency cycle. Validation found no issues. The configuration unit is only available as a prerelease, but it is not marked that way in the configuration. Add `allowPrerelease: true` to the `directives`. {Locked="allowPrerelease: true","directives"} These are values in the configuration file that are not localized. The configuration unit was found locally, but could not be found in any configured catalog. Ensure that it is present on any system before applying the configuration. The configuration unit is not available publicly; ensure that anyone who will use this configuration has access to it. The module was not provided. Specifying the module improves performance and prevents future name collisions. Downloads the installer from the selected package, either found by searching a configured source or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. By default, download command will download the appropriate installer to the user's Downloads folder. Downloads the installer from a given package Directory where the installers are downloaded to Downloading dependencies: Installer downloaded: {0} {Locked="{0}"} Full path of the downloaded installer. Select the installer type Installer Downloads Installer is prohibited from being downloaded for later offline installation. `--module-path allusers` requires administrator privileges to execute. {Locked="--module-path allusers"} Specifies the location on the local computer to store modules. Default %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules {Locked="%LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules"} `--module-path` value must be `currentuser`, `allusers`, `default`, or an absolute path. {Locked="{--module-path}, {currentuser}, {allusers}, {default}} Enable Windows Package Manager Configuration Retrieve information about errors. Given a number, the output will contain details about the error, including the symbol name if it is a WinGet specific error. Given a string, the WinGet specific errors are searched for this value. {Locked="WinGet"} Get information on errors A value to search within the error information The given number is too large to be an HRESULT. Unknown error code Internal Error Invalid command line arguments Executing command failed Opening manifest failed Cancellation signal received Running ShellExecute failed Cannot process manifest. The manifest version is higher than supported. Please update the client. Downloading installer failed Cannot write to index; it is a higher schema version The index is corrupt The configured source information is corrupt The source name is already configured The source type is invalid The MSIX file is a bundle, not a package Data required by the source is missing None of the installers are applicable for the current system The installer file's hash does not match the manifest The source name does not exist The source location is already configured under another name No packages found No sources are configured Multiple packages found matching the criteria No manifest found matching the criteria Failed to get Public folder from source package Command requires administrator privileges to run The source location is not secure The Microsoft Store client is blocked by policy The Microsoft Store app is blocked by policy The feature is currently under development. It can be enabled using `winget settings`. {Locked="winget settings"} Failed to install the Microsoft Store app Failed to perform auto complete Failed to initialize YAML parser Encountered an invalid YAML key Encountered a duplicate YAML key Invalid YAML operation Failed to build YAML doc Invalid YAML emitter state Invalid YAML data LibYAML error Manifest validation succeeded with warning Manifest validation failed Manifest is invalid No applicable update found winget upgrade --all completed with failures Installer failed security check Download size does not match expected content length Uninstall command not found Running uninstall command failed ICU break iterator error ICU casemap error ICU regex error Failed to install one or more imported packages Could not find one or more requested packages Json file is invalid The source location is not remote The configured rest source is not supported Invalid data returned by rest source Operation is blocked by Group Policy Rest API internal error Invalid rest source url Unsupported MIME type returned by rest API Invalid rest source contract version The source data is corrupted or tampered Error reading from the stream Package agreements were not agreed to Error reading input in prompt The search request is not supported by one or more sources The rest API endpoint is not found. Failed to open the source. Source agreements were not agreed to Header size exceeds the allowable limit of 1024 characters. Please reduce the size and try again. Missing resource file Running MSI install failed Arguments for msiexec are invalid Failed to open one or more sources Failed to validate dependencies One or more package is missing Invalid table column The upgrade version is not newer than the installed version Upgrade version is unknown and override is not specified ICU conversion error Failed to install portable package Volume does not support reparse points Portable package from a different source already exists. Unable to create symlink. Path points to a directory. The installer cannot be run from an administrator context. Failed to uninstall portable package Failed to validate DisplayVersion values against index. One or more arguments are not supported. Embedded null characters are disallowed for SQLite Failed to find the nested installer in the archive. Failed to extract archive. Invalid relative file path to nested installer provided. The server certificate did not match any of the expected values. Install location must be provided. Archive malware scan failed. Found at least one version of the package installed. A pin already exists for the package. There is no pin for the package. Unable to open the pin database. One or more applications failed to install One or more applications failed to uninstall One or more queries did not return exactly one match The package has a pin that prevents upgrade. The package currently installed is the stub package Application shutdown signal received Failed to download package dependencies. Failed to download package. Download for offline installation is prohibited. A required service is busy or unavailable. Try again later. The guid provided does not correspond to a valid resume state. The current client version did not match the client version of the saved state. The resume state data is invalid. Unable to open the checkpoint database. Exceeded max resume limit. Invalid authentication info. Authentication method not supported. Authentication failed. Authentication failed. Interactive authentication required. Authentication failed. User cancelled. Authentication failed. Authenticated account is not the desired account. Application is currently running. Exit the application then try again. Another installation is already in progress. Try again later. One or more file is being used. Exit the application then try again. This package has a dependency missing from your system. There's no more space on your PC. Make space, then try again. There's not enough memory available to install. Close other applications then try again. This application requires internet connectivity. Connect to a network then try again. This application encountered an error during installation. Contact support. Restart your PC to finish installation. Installation failed. Restart your PC then try again. Your PC will restart to finish installation. You cancelled the installation. Another version of this application is already installed. A higher version of this application is already installed. Organization policies are preventing installation. Contact your admin. Failed to install package dependencies. Application is currently in use by another application. Invalid parameter. Package not supported by the system. The installer does not support upgrading an existing package. Installation failed with a custom installer error. The Apps and Features Entry for the package could not be found. The install location is not applicable. The install location could not be found. The hash of the existing file did not match. File not found. The file was found but the hash was not checked. The file could not be accessed. The configuration file is invalid. The YAML syntax is invalid. A configuration field has an invalid type. The configuration has an unknown version. An error occurred while applying the configuration. The configuration contains a duplicate identifier. The configuration is missing a dependency. The configuration has an unsatisfied dependency. An assertion for the configuration unit failed. The configuration was manually skipped. The user declined to continue execution. The dependency graph contains a cycle which cannot be resolved. The configuration has an invalid field value. The configuration is missing a field. Some of the configuration units failed while testing their state. Configuration state was not tested. The configuration unit was not installed. The configuration unit could not be found. Multiple matches were found for the configuration unit; specify the module to select the correct one. The configuration unit failed while attempting to get the current system state. The configuration unit failed while attempting to test the current system state. The configuration unit failed while attempting to apply the desired state. The module for the configuration unit is available in multiple locations with the same version. Loading the module for the configuration unit failed. The configuration unit returned an unexpected result during execution. A unit contains a setting that requires the config root. Operation is not supported by the configuration processor. Unavailable Successfully enabled Windows Features dependencies Loading the module for the configuration unit failed because it requires administrator privileges to run. A unit contains a setting that requires the config root. Loading the module for the configuration unit failed because it requires administrator privileges to run. Resumes execution of a previously saved command by passing in the unique identifier of the saved command. This is used to resume an executed command that may have been terminated due to a reboot. Resumes execution of a previously saved command. The unique identifier of the saved state to resume Resuming the state from a different client version is not supported: {0} {Locked= "{0}"} Message displayed to inform the user that the client version of the resume state does not match the current client version. {0} is a placeholder for the client version that created the resume state. The resume state does not exist: {0} {Locked="{0}"} Error message displayed when the user provides a guid that does not correspond to a valid saved state. {0} is a placeholder replaced by the provided guid string. No data found in the resume state. This command does not support resuming. Allows a reboot if applicable Initiating reboot to complete operation... Failed to initiate a reboot. Resume operation exceeds the allowable limit of {0} resume(s). To manually resume, run the command `{1}`. {Locked="{0}", "{1}"} {0} is a placeholder that is replaced by an integer number of the number of allowed resumes. {1} is a placeholder for the command to run to perform a manual resume. Ignore the limit on resuming a saved state 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. {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}"} Specify authentication window preference (silent, silentPreferred, or interactive) {Locked="silent","silentPreferred","interactive"} This argument allows the user to select authentication window popup behavior. Specify the account to be used for authentication Failed to add source. This WinGet version does not support the source's authentication method. Try upgrading to latest WinGet version. {Locked="WinGet"} The {0} source requires authentication. Authentication prompt may appear when necessary. Authenticated information will be shared with the source for access authorization. {Locked="{0}"} Repairs the selected package, either found by searching the installed packages list or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. Repairs the selected package The repair command for this package cannot be found. Please reach out to the package publisher for support. The installer technology in use does not match the version currently installed. Repair command not found. The installer technology in use doesn't support repair. Repair operation completed successfully. Repair abandoned Starting package repair... Failed to repair Microsoft Store package. Error code: {0} {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to repair. {0} is a placeholder replaced by an error code. Repair operation failed. Repair operation is not applicable. No matching package versions are available from the configured sources. The current system configuration does not support the repair of this package. The installer technology in use does not support repair. The package installed for user scope cannot be repaired when running with administrator privileges. Repair operations involving administrator privileges are not permitted on packages installed within the user scope. Repair failed with exit code: {0} {Locked="{0}"} Error message displayed when an attempt to repair an application package fails. {0} is a placeholder replaced by an error code. The SQLite connection was terminated to prevent corruption. Uninstall all versions The version to act upon Multiple versions of this package are installed. Either refine the search, pass the `--version` argument to select one, or pass the `--all-versions` flag to uninstall all of them. {Locked="--version,--all-versions"} Enable Windows Package Manager proxy command line options Describes a Group Policy that can enable the use of the --proxy option to set a proxy Set a proxy to use for this execution Disable the use of proxy for this execution Disables the progress bar and spinner Cannot reset {0}. This setting is controlled by policy. For more information contact your system administrator. {Locked="{0}"} The value will be replaced with the feature name Reset admin setting '{0}'. {Locked="{0}"} Message displayed after the user resets an admin setting to its default value. Reset is used as verb in past tense. {0} is a placeholder replaced by the setting name. Cannot set {0}. This setting is controlled by policy. For more information contact your system administrator. {Locked="{0}"} The value will be replaced with the feature name Set admin setting '{0}' to '{1}'. {Locked="{0}"} Message displayed after the user sets the value of an admin setting. Set is used as a verb in past tense. {0} is a placeholder replaced by the setting name. {1} is a placeholder replaced Name of the setting to modify Value to set for the setting. Resets an admin setting to its default value. Resets an admin setting to its default value. Sets the value of an admin setting. Sets the value of an admin setting. Excludes a source from discovery unless specified Excludes a source from discovery (true or false) Explicit Used as a header in a table that shows the properties of a "source". In this context, "explicit" means that the source needs to be explicitly specified or directly designated to be used, as opposed to being implicitly included. Trust level of the source (none or trusted) Trust Level Failed to get Microsoft Store package catalog. No applicable Microsoft Store package found from Microsoft Store package catalog. Failed to get Microsoft Store package download information. No applicable Microsoft Store package found for download. Failed to retrieve Microsoft Store package license. No applicable Microsoft Store package found. Successfully verified Microsoft Store package hash Microsoft Store package hash mismatch Microsoft Store package downloaded: {0} {Locked="{0}"} Full path of the downloaded package. Microsoft Store package download failed: {0} {Locked="{0}"} Package name. Microsoft Store package download completed Downloading main packages from Microsoft Store... Downloading dependency packages from Microsoft Store... Retrieving Microsoft Store package download information Failed to retrieve Microsoft Store package download information Retrieving Microsoft Store package license Microsoft Store package license saved: {0} {Locked="{0}"} License file full path. Failed to retrieve Microsoft Store package license Microsoft Store package download does not support --rename argument. Microsoft Store package will use names provided by Microsoft Store catalog. {Locked="--rename"} Microsoft Store package download requires Microsoft Entra Id authentication. Authentication prompt may appear when necessary. Authenticated information will be shared with Microsoft services for access authorization. For Microsoft Store package licensing, the Microsoft Entra Id account needs to be a member of Global Administrator, User Administrator, or License Administrator. {Locked="Global Administrator,User Administrator,License Administrator"} Skips retrieving Microsoft Store package offline license Select the target platform Adding configuration file: {0} {Locked="{0}"} Successfully exported Getting configuration settings... At least --packageId and/or --module with --resource must be provided. Or use --all to export all package configurations. {Locked="--packageId,--module, --resource, --all"} Arguments --packageId, --module and --resource cannot be used with --all. {Locked="--packageId,--module, --resource, --all"} Exports configuration resources to a configuration file. When used with --all, exports all package configurations. When used with --packageId, exports a WinGetPackage resource of the given package id. When used with --module and --resource, gets the settings of the resource and exports it to the configuration file. If the output configuration file already exists, appends the exported configuration resources. {Locked="WinGetPackage,--packageId,--module, --resource"} Exports configuration resources to a configuration file. The module of the resource to export. The package identifier to export. The configuration resource to export. Exports all package configurations. The configuration unit failed getting its properties. Failed exporting configuration. Configure {0} {Locked="{0}"} Install {0} {Locked="{0}"} Some of the data present in the configuration file was truncated for this output; inspect the file contents for the complete content. <this value has been truncated; inspect the file contents for the complete text> Keep some form of separator like the "<>" around the text so that it stands out from the preceding text. Shows the high level details for configurations that have been applied to the system. This data can then be used with `configure` commands to get more details. {Locked="`configure`"} Shows configuration history There are no configurations in the history. Select items from history No single configuration could be found that matched the provided data. Provide either the full name or part of the identifier that unambiguously matches the desired configuration. Remove the item from history First Applied Column header for date values indicating when a configuration was first applied to the system. Identifier Name Origin Path The specified configuration could not be found. Completed The state of processing an item. In progress The state of processing an item. Pending The state of processing an item. Unknown The state of processing an item. Monitor configuration status. As in "to monitor the status of a configuration being applied". Completed The state of processing an item. In progress The state of processing an item. Pending The state of processing an item. Skipped The state of processing an item. Unknown The state of processing an item. Apply Started When the configuration application started. Apply Ended When the configuration application ended. Result Details State The state of processing an item. Unit The package is not compatible with the current Windows version or platform. Suppress showing initial configuration details when possible Multiple Microsoft Store packages may be downloaded targeting different platforms and architectures. --platform, --architecture can be used to filter the packages. {Locked="--platform--architecture"} Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account is not a member of Global Administrator, User Administrator, or License Administrator. Use --skip-license to skip retrieving Microsoft Store package license. {Locked="Global Administrator,User Administrator,License Administrator,--skip-license"} The Microsoft Store package does not support download. The Microsoft Store package does not support download. Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account does not have required privilege. Parameter cannot be passed across integrity boundary. Downloaded zero byte installer; ensure that your network connection is working properly. Downloaded zero byte installer; ensure that your network connection is working properly. Manage fonts Manage fonts with sub-commands. Fonts can be installed, upgraded, or uninstalled similar to packages. Family "Family" represents the font family such as 'Arial' or 'Times New Roman'. Faces "Faces" represents the typeface of the font family such as 'Bold' or 'Italic' Filter results by family name "Family" represents the font family such as 'Arial' or 'Times New Roman'. Face "Face" represents the typeface of a font family such as 'Bold' or 'Italic' Paths No installed font found matching input criteria. List installed fonts List all installed fonts, all font files, or full details of a specific font. Version Configuration Modules PowerShell Modules that are used for the Configuration feature The package installer requires authentication. Authentication prompt may appear when necessary. Authenticated information will be shared with the installer download url. Failed to download installer. This WinGet version does not support the installer download authentication method. Try upgrading to latest WinGet version. {Locked="WinGet"} Failed to download installer. Authentication failed. Specify the path to the configuration processor DSC v3 resource commands DSC stands for "Desired State Configuration". It should already have a locked translation. The sub-commands here implement Desired State Configuration (DSC) v3 resources for configuring WinGet and packages. {Locked="WinGet"} Get the resource state Set the resource state Describe required state changes Test the resource state Delete the resource state Get all state instances Validate group contents Resolve external state Run the adapter Get the resource schema Get the resource manifest Manage package state Manage packages through WinGet. {Locked="WinGet"} Indicates whether an instance should or does exist. Indicates whether an instance is in the desired state. The identifier of the package. The source of the package. The version of the package. The method for matching the identifier with a package. Indicate that the latest available version of the package should be installed. The install mode to use if needed. The target scope of the package. Indicates whether to accept agreements for sources and packages. Manage user settings file Manage the user settings of WinGet. {Locked="WinGet"} The settings json file content. The action used to apply the settings. Value is logged for correlation Manage source configuration Manage the sources of WinGet. {Locked="WinGet"} The name to use for the source. The argument for the source. The type of the source. The trust level of the source. Whether the source is included when calls don't specify a source. Applying configuration unit... Exporting configuration unit... Getting configuration unit processors... Ensure required module for export [{0}] {Locked="{0}"} Failed to test or acquire required module. Related settings will not be exported. Export [{0}] {Locked="{0}"} Failed to export the resource. Failed to get unit processors. Individual package settings will not be exported. Directory where the results are to be written Desired State Configuration package not found on the system. Installing the package... Failed to install Desired State Configuration package. Install the package manually or provide the path to dsc.exe through --processor-path argument. {Locked="dsc.exe","--processor-path"} An object containing the administrator settings and their values. Manage administrator settings Manage the administrator settings of WinGet. {Locked="WinGet"} Resets all admin settings All admin settings reset. MCP information MCP stands for Model Context Protocol and should probably remain as-is MCP (Model Context Protocol) information for the Windows Package Manager. To manually configure the Windows Package Manager MCP server with your MCP client, use the following JSON fragment in the `servers` object: {Locked="`servers`"} MCP stands for Model Context Protocol and should probably remain as-is. An unlocalized JSON fragment will follow on another line. Enable Windows Package Manager MCP Server Target OS version Failed installing one or more fonts. Font file is not supported and cannot be installed. One or more fonts in the font package is not supported and cannot be installed. Font install failed; cleaning up. Font already installed. Package Id WinGet Supported Title Font file not found. Font uninstall failed. The font may not be in a good state. Try uninstalling after a restart. Font validation failed. Font rollback failed. The font may not be in a good state. Try uninstalling after a restart. Show font file detailed information. Validation of the font package failed. Rollback attempt for failed font install was unsuccessful. A restart may be required to successfully uninstall the font. Font package uninstall failed. This is often due to the fonts being in use by the system or an application. Uninstall may be successful after restarting your computer. OK "OK" means the font is in a good healthy state Corrupt "Corrupt" refers to an install that is in a corrupted or bad state, and needs repair. Status Unknown Font package is already installed. Show detailed information about packages Providing this argument causes the CLI to output additional details about installed application packages. Channel: Precedes a string value that names the delivery channel for the software package (ex. stable, beta). Local Identifier: Precedes a value that is the unique identifier for the installed package on the local system. Package Family Name: Precedes a value that is the APPX/MSIX package family name of the installed package. Product Code: Precedes a value that is the Add/Remove Programs identifier in the registry. This is also the Product Code value as defined in MSI installers. Upgrade Code: Precedes a value that is the MSI Upgrade Code for the installed package. Installed Scope: Precedes a value that is the scope of the installation of the package (ex. user, machine). Installed Architecture: Precedes a value that is the installed architecture of the package (ex. x86, x64, ARM64). Installed Locale: Precedes a value that is the locale of the installed package. Installed Location: Precedes a value that is the directory path to the installed package. Origin Source: Precedes a value that names the package source where the installed package originated from. Available Upgrades: Precedes a list of package upgrades available for the installed package. Installer Category: Precedes a value that indicates the category of the installer for the installed package (ex. exe, msi, msix). Old Value Column title for listing edit changes. New Value Column title for listing the new value. Priority; higher numbers first This argument sets the numerical priority of the source. Higher values will be first in priority. Priority Label for the priority of the source with respect to other sources. Higher values come first in the order. The priority of the source. Higher values are sorted first in the order. Results have been filtered to the highest matched source priority. A warning message to indicate to the user that the results of a search have been filtered by choosing only those matching the highest source priority amongst the results. ================================================ FILE: src/AppInstallerCLITests/ARPChanges.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSource.h" #include "TestHooks.h" #include #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Logging; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Correlation; struct TestTelemetry : public TelemetryTraceLogger { void LogSuccessfulInstallARPChange( std::string_view sourceIdentifier, std::string_view packageIdentifier, std::string_view packageVersion, std::string_view packageChannel, size_t changesToARP, size_t matchesInARP, size_t countOfIntersectionOfChangesAndMatches, std::string_view arpName, std::string_view arpVersion, std::string_view arpPublisher, std::string_view arpLanguage) const noexcept override { WasLogSuccessfulInstallARPChangeCalled = true; if (OnLogSuccessfulInstallARPChange) { OnLogSuccessfulInstallARPChange( sourceIdentifier, packageIdentifier, packageVersion, packageChannel, changesToARP, matchesInARP, countOfIntersectionOfChangesAndMatches, arpName, arpVersion, arpPublisher, arpLanguage); } } std::function OnLogSuccessfulInstallARPChange; mutable bool WasLogSuccessfulInstallARPChangeCalled = false; }; struct ARPTestContext : public Context { ARPTestContext(Manifest::InstallerTypeEnum installerType = Manifest::InstallerTypeEnum::Exe) : Context(OStream, IStream), SourceFactory([this](const SourceDetails&) { return Source; }) { // Put installer in to control whether arp change code cares to run Manifest::ManifestInstaller installer; installer.BaseInstallerType = installerType; Add(std::move(installer)); // Put in an empty manifest by default Manifest::Manifest manifest; manifest.Id = "Installing.Id"; manifest.Version = "Installing.Version"; manifest.Channel = "Installing.Channel"; manifest.DefaultLocalization.Add("Installing.Name"); Add(std::move(manifest)); // Set up logger to intercept event Logger = std::make_shared(); TestHook_SetTelemetryOverride(Logger); Logger->OnLogSuccessfulInstallARPChange = [this]( std::string_view sourceIdentifier, std::string_view packageIdentifier, std::string_view packageVersion, std::string_view packageChannel, size_t changesToARP, size_t matchesInARP, size_t countOfIntersectionOfChangesAndMatches, std::string_view arpName, std::string_view arpVersion, std::string_view arpPublisher, std::string_view arpLanguage) { SourceIdentifier = sourceIdentifier; PackageIdentifier = packageIdentifier; PackageVersion = packageVersion; PackageChannel = packageChannel; ChangesToARP = changesToARP; MatchesInARP = matchesInARP; CountOfIntersectionOfChangesAndMatches = countOfIntersectionOfChangesAndMatches; ARPName = arpName; ARPVersion = arpVersion; ARPPublisher = arpPublisher; ARPLanguage = arpLanguage; }; // Inject our source TestHook_SetSourceFactoryOverride(std::string{ Repository::Microsoft::PredefinedInstalledSourceFactory::Type() }, SourceFactory); Source = std::make_shared(); Source->SearchFunction = [&](const SearchRequest& request) { return request.IsForEverything() ? EverythingResult : MatchResult; }; // The package version is used to get the source identifier Add(TestPackageVersion::Make(Get(), Source)); // Populate everything result with a few items AddEverythingResult("Id1", "Name1", "Publisher1", "1.0"); AddEverythingResult("Id2", "Name2", "Publisher2", "2.0"); } ~ARPTestContext() { TestHook_ClearSourceFactoryOverrides(); TestHook_SetTelemetryOverride({}); } void AddEverythingResult(std::string_view id, std::string_view name, std::string_view publisher, std::string_view version) { AddResult(EverythingResult, id, name, publisher, version); } void AddMatchResult(std::string_view id, std::string_view name, std::string_view publisher, std::string_view version) { AddResult(MatchResult, id, name, publisher, version); } void ExpectEvent(size_t arpChanges, size_t matches, size_t overlap, const std::shared_ptr& arpEntry = nullptr) { REQUIRE(Logger->WasLogSuccessfulInstallARPChangeCalled); const auto& manifest = Get(); REQUIRE(Source->GetIdentifier() == SourceIdentifier); REQUIRE(manifest.Id == PackageIdentifier); REQUIRE(manifest.Version == PackageVersion); REQUIRE(manifest.Channel == PackageChannel); REQUIRE(arpChanges == ChangesToARP); REQUIRE(matches == MatchesInARP); REQUIRE(overlap == CountOfIntersectionOfChangesAndMatches); if (arpEntry) { auto version = GetInstalledVersion(arpEntry); REQUIRE(version->GetProperty(PackageVersionProperty::Name) == ARPName); REQUIRE(version->GetProperty(PackageVersionProperty::Version) == ARPVersion); auto metadata = version->GetMetadata(); REQUIRE(metadata[PackageVersionMetadata::Publisher] == ARPPublisher); REQUIRE(metadata[PackageVersionMetadata::InstalledLocale] == ARPLanguage); } else { REQUIRE(ARPName.empty()); REQUIRE(ARPVersion.empty()); REQUIRE(ARPPublisher.empty()); REQUIRE(ARPLanguage.empty()); } } std::ostringstream OStream; std::istringstream IStream; std::shared_ptr Logger; TestSourceFactory SourceFactory; std::shared_ptr Source; SearchResult EverythingResult; SearchResult MatchResult; // EventData std::string SourceIdentifier; std::string PackageIdentifier; std::string PackageVersion; std::string PackageChannel; size_t ChangesToARP; size_t MatchesInARP; size_t CountOfIntersectionOfChangesAndMatches; std::string ARPName; std::string ARPVersion; std::string ARPPublisher; std::string ARPLanguage; private: void AddResult(SearchResult& result, std::string_view id, std::string_view name, std::string_view publisher, std::string_view version) { PackageMatchFilter defaultFilter{ PackageMatchField::Id, MatchType::Exact }; Manifest::Manifest manifest; manifest.Id = id; manifest.DefaultLocalization.Add(name); manifest.DefaultLocalization.Add(publisher); manifest.Version = version; manifest.Installers.push_back({}); TestPackage::MetadataMap metadata; metadata[PackageVersionMetadata::Publisher] = publisher; result.Matches.emplace_back(TestCompositePackage::Make(manifest, std::move(metadata), std::vector{}, Source), defaultFilter); } }; // Override the correlation heuristic by an empty one to ensure that these tests // consider only the exact matching. struct TestHeuristicOverride { TestHeuristicOverride() { IARPMatchConfidenceAlgorithm::OverrideInstance(&m_algorithm); } ~TestHeuristicOverride() { IARPMatchConfidenceAlgorithm::ResetInstance(); } private: EmptyMatchConfidenceAlgorithm m_algorithm; }; TEST_CASE("ARPChanges_MSIX_Ignored", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context(Manifest::InstallerTypeEnum::Msix); context << SnapshotARPEntries; REQUIRE(!context.Contains(Data::ARPCorrelationData)); context << ReportARPChanges; REQUIRE(!context.Logger->WasLogSuccessfulInstallARPChangeCalled); } TEST_CASE("ARPChanges_CheckSnapshot", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); auto snapshot = context.Get().GetPreInstallSnapshot(); REQUIRE(context.EverythingResult.Matches.size() == snapshot.size()); // Destructively match for (const auto& match : context.EverythingResult.Matches) { bool found = false; for (auto itr = snapshot.begin(); itr != snapshot.end(); ++itr) { if (match.Package->GetProperty(PackageProperty::Id) == std::get<0>(*itr)) { REQUIRE(GetInstalledVersion(match.Package)->GetProperty(PackageVersionProperty::Version) == std::get<1>(*itr)); REQUIRE(GetInstalledVersion(match.Package)->GetProperty(PackageVersionProperty::Channel) == std::get<2>(*itr)); snapshot.erase(itr); found = true; break; } } REQUIRE(found); } REQUIRE(snapshot.empty()); } TEST_CASE("ARPChanges_NoChange_NoMatch", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context << ReportARPChanges; context.ExpectEvent(0, 0, 0); } TEST_CASE("ARPChanges_NoChange_SingleMatch", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context << ReportARPChanges; context.ExpectEvent(0, 1, 0, context.MatchResult.Matches[0].Package); } TEST_CASE("ARPChanges_NoChange_MultiMatch", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context.AddMatchResult("MatchId2", "MatchName2", "MatchPublisher2", "MatchVersion2"); context << ReportARPChanges; context.ExpectEvent(0, 2, 0); } TEST_CASE("ARPChanges_SingleChange_NoMatch", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context << ReportARPChanges; context.ExpectEvent(1, 0, 0); } TEST_CASE("ARPChanges_SingleChange_SingleMatch", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context << ReportARPChanges; context.ExpectEvent(1, 1, 0, context.MatchResult.Matches.back().Package); } TEST_CASE("ARPChanges_SingleChange_MultiMatch", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context.MatchResult.Matches.emplace_back(context.EverythingResult.Matches.back()); context << ReportARPChanges; context.ExpectEvent(1, 2, 1, context.EverythingResult.Matches.back().Package); } TEST_CASE("ARPChanges_MultiChange_NoMatch", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context.AddEverythingResult("EverythingId2", "EverythingName2", "EverythingPublisher2", "EverythingVersion2"); context << ReportARPChanges; context.ExpectEvent(2, 0, 0); } TEST_CASE("ARPChanges_MultiChange_SingleMatch_NoOverlap", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context.AddEverythingResult("EverythingId2", "EverythingName2", "EverythingPublisher2", "EverythingVersion2"); context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context << ReportARPChanges; context.ExpectEvent(2, 1, 0, context.MatchResult.Matches.back().Package); } TEST_CASE("ARPChanges_MultiChange_SingleMatch_Overlap", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context.AddEverythingResult("EverythingId2", "EverythingName2", "EverythingPublisher2", "EverythingVersion2"); context.MatchResult.Matches.emplace_back(context.EverythingResult.Matches.back()); context << ReportARPChanges; context.ExpectEvent(2, 1, 1, context.MatchResult.Matches.back().Package); } TEST_CASE("ARPChanges_MultiChange_MultiMatch_NoOverlap", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context.AddEverythingResult("EverythingId2", "EverythingName2", "EverythingPublisher2", "EverythingVersion2"); context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context.AddMatchResult("MatchId2", "MatchName2", "MatchPublisher2", "MatchVersion2"); context << ReportARPChanges; context.ExpectEvent(2, 2, 0); } TEST_CASE("ARPChanges_MultiChange_MultiMatch_SingleOverlap", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context.AddEverythingResult("EverythingId2", "EverythingName2", "EverythingPublisher2", "EverythingVersion2"); context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context.MatchResult.Matches.emplace_back(context.EverythingResult.Matches.back()); context << ReportARPChanges; context.ExpectEvent(2, 2, 1, context.MatchResult.Matches.back().Package); } TEST_CASE("ARPChanges_MultiChange_MultiMatch_MultiOverlap", "[ARPChanges][workflow]") { TestHeuristicOverride heuristicOverride; ARPTestContext context; context << SnapshotARPEntries; REQUIRE(context.Contains(Data::ARPCorrelationData)); context.AddEverythingResult("EverythingId1", "EverythingName1", "EverythingPublisher1", "EverythingVersion1"); context.MatchResult.Matches.emplace_back(context.EverythingResult.Matches.back()); context.AddEverythingResult("EverythingId2", "EverythingName2", "EverythingPublisher2", "EverythingVersion2"); context.MatchResult.Matches.emplace_back(context.EverythingResult.Matches.back()); context << ReportARPChanges; context.ExpectEvent(2, 2, 2); } ================================================ FILE: src/AppInstallerCLITests/AdminSettings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSettings.h" #include using namespace AppInstaller::Settings; using namespace TestCommon; TEST_CASE("AdminSetting_Enable", "[adminSettings]") { WHEN("Group policy") { SECTION("Not configured") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::NotConfigured); REQUIRE(EnableAdminSetting(BoolAdminSetting::LocalManifestFiles)); } SECTION("Enabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::Enabled); REQUIRE(EnableAdminSetting(BoolAdminSetting::LocalManifestFiles)); } SECTION("Disabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::Disabled); REQUIRE_FALSE(EnableAdminSetting(BoolAdminSetting::LocalManifestFiles)); } } } TEST_CASE("AdminSetting_Disable", "[adminSettings]") { WHEN("Group policy") { SECTION("Not configured") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::NotConfigured); REQUIRE(DisableAdminSetting(BoolAdminSetting::LocalManifestFiles)); } SECTION("Enabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::Enabled); REQUIRE_FALSE(DisableAdminSetting(BoolAdminSetting::LocalManifestFiles)); } SECTION("Disabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::Disabled); REQUIRE(DisableAdminSetting(BoolAdminSetting::LocalManifestFiles)); } } } TEST_CASE("AdminSetting_AllSettingsAreImplemented", "[adminSettings]") { for (auto adminSetting : GetAllBoolAdminSettings()) { // If we forget to add it to the conversion, it returns Unknown/None REQUIRE(AdminSettingToString(adminSetting) != AdminSettingToString(BoolAdminSetting::Unknown)); REQUIRE(StringToBoolAdminSetting(AdminSettingToString(adminSetting)) != BoolAdminSetting::Unknown); REQUIRE(GetAdminSettingPolicy(adminSetting) != TogglePolicy::Policy::None); GroupPolicyTestOverride policies; policies.SetState(GetAdminSettingPolicy(adminSetting), PolicyState::NotConfigured); // We should be able to configure the state. // If we forget to add it, it won't persist. REQUIRE(EnableAdminSetting(adminSetting)); REQUIRE(IsAdminSettingEnabled(adminSetting)); REQUIRE(DisableAdminSetting(adminSetting)); REQUIRE_FALSE(IsAdminSettingEnabled(adminSetting)); } } ================================================ FILE: src/AppInstallerCLITests/AppInstallerCLITests.vcxproj ================================================ true true true true 15.0 {89b1aab4-2bbc-4b65-9ed7-a01d5cf88230} Win32Proj AppInstallerCLITests 10.0.26100.0 10.0.17763.0 AppInstallerCLITests Debug ARM64 Debug Win32 Release ARM64 Release Win32 Debug x64 Release x64 Application true true false true false true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false Use pch.h $(IntDir)pch.pch _CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) Level4 5321;%(DisableSpecificWarnings) %(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING Disabled _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;_DEBUG;%(PreprocessorDefinitions) $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) true true false false Console false wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;gdi32.lib;%(AdditionalDependencies) wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;gdi32.lib;%(AdditionalDependencies) gdi32.dll gdi32.dll $(ProjectDir)..\manifest\shared.manifest $(ProjectDir)..\manifest\shared.manifest copy "$(ProjectDir)..\AppInstallerCLIPackage\bin\$(PlatformTarget)\$(configuration)\resources.pri" "$(TargetDir)\resources.pri" copy "$(ProjectDir)..\AppInstallerCLIPackage\bin\$(PlatformTarget)\$(configuration)\resources.pri" "$(TargetDir)\resources.pri" _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;WIN32;%(PreprocessorDefinitions) $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) true false wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;gdi32.lib;%(AdditionalDependencies) gdi32.dll $(ProjectDir)..\manifest\shared.manifest copy "$(ProjectDir)..\AppInstallerCLIPackage\bin\$(PlatformTarget)\$(configuration)\resources.pri" "$(TargetDir)\resources.pri" MaxSpeed true true _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions) $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) true true true false false false Console true true false wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;gdi32.lib;%(AdditionalDependencies) wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;gdi32.lib;%(AdditionalDependencies) wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;gdi32.lib;%(AdditionalDependencies) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) gdi32.dll gdi32.dll gdi32.dll $(ProjectDir)..\manifest\shared.manifest $(ProjectDir)..\manifest\shared.manifest $(ProjectDir)..\manifest\shared.manifest copy "$(ProjectDir)..\AppInstallerCLIPackage\bin\$(PlatformTarget)\$(configuration)\resources.pri" "$(TargetDir)\resources.pri" copy "$(ProjectDir)..\AppInstallerCLIPackage\bin\$(PlatformTarget)\$(configuration)\resources.pri" "$(TargetDir)\resources.pri" copy "$(ProjectDir)..\AppInstallerCLIPackage\bin\$(PlatformTarget)\$(configuration)\resources.pri" "$(TargetDir)\resources.pri" NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing Create true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true {1c6e0108-2860-4b17-9f7e-fa5c6c1f3d3d} {5890d6ed-7c3b-40f3-b436-b54f640d9e65} {5eb88068-5fb9-4e69-89b2-72dbc5e068f9} {ca460806-5e41-4e97-9a3d-1d74b433b663} {1b9077b3-8923-4ecd-8fc9-b3190fcbe4d4} This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters ================================================ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {d5cac203-3846-4b39-a1cd-8de9303757b4} {69fcd25c-e737-4d28-a6d1-39ce491bf293} {81fadc81-4327-4b9e-b588-97155b770aa3} {0215df7f-7a73-4cc0-9589-adf401329e59} {52b26063-ccff-40ae-a420-243327dcca25} {488e9b7a-d72e-470c-b73a-5abcb83ca2c6} {82dd5390-16ce-4a4c-a61a-148013890cc4} {3e7d4dff-5513-46dd-95d2-f616c867ebe4} {3499d391-99c1-4fa3-ba8d-0ce846c2497b} {69069174-d4b1-4070-80c0-37d416b07128} {d6d7e50d-394d-4486-8f93-4a5312ca4bf6} {b35e1a8b-1961-46d5-98e5-adc8e52c54a9} {d055e02a-59c9-4ae4-9405-579dac6ff59b} {13d4d227-0f04-4e57-a663-c3c535438ab3} {6f8102b7-ea5f-4379-bb59-067ced63d970} {6bd68492-3f96-4023-af35-63249cd18158} {0e5f9ff4-6f22-46eb-8538-1c560a008f06} Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files\Repository Source Files\CLI Source Files\CLI Source Files\Repository Source Files\Repository Source Files\Repository Source Files\Common Source Files\Common Source Files\Common Source Files\Common Source Files\CLI Source Files\Repository Source Files\Repository Source Files\Common Source Files\Common Source Files\CLI Source Files\Common Source Files\Common Source Files\Common Source Files\Common Source Files\CLI Source Files\Repository Source Files\Repository Source Files\Repository Source Files\Repository Source Files\Common Source Files\Common Source Files\Common Source Files\Repository Source Files\Repository Source Files\Repository Source Files\CLI Source Files\Repository Source Files\Common Source Files\Repository Source Files\Repository Source Files\Repository Source Files\Repository Source Files\Common Source Files\Common Source Files Source Files\Common Source Files\Common Source Files\CLI Source Files\CLI Source Files\Common Source Files\Common Source Files\Common Source Files\Common Source Files\Common Source Files\Common Source Files\Common Source Files\Repository Source Files\Common Source Files\CLI Source Files\CLI Source Files\CLI Source Files\CLI Source Files\CLI Source Files\CLI Source Files\CLI Source Files\CLI Source Files\CLI Source Files\CLI Source Files\Repository Source Files\CLI Source Files\CLI Source Files Source Files\CLI Source Files\Common Source Files\Repository Source Files\Repository Source Files\Repository Source Files\CLI Source Files\Repository Source Files\CLI Source Files\Common Source Files\Repository Source Files\Common Source Files\Repository Source Files\Common Source Files\Repository Source Files\Common Source Files\Common Source Files\Common Source Files\Repository Source Files\CLI Source Files\Repository Source Files\Common Source Files\Repository Source Files\Repository Source Files\Common Source Files\CLI Source Files\Repository Source Files\Common Source Files\Repository TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData\MultiFileManifestV1 TestData\MultiFileManifestV1 TestData\MultiFileManifestV1 TestData\MultiFileManifestV1 TestData\MultiFileManifestV1_1 TestData\MultiFileManifestV1_1 TestData\MultiFileManifestV1_1 TestData\MultiFileManifestV1_1 TestData\MultiFileManifestV1_2 TestData\MultiFileManifestV1_2 TestData\MultiFileManifestV1_2 TestData\MultiFileManifestV1_2 TestData\MultiFileManifestV1_4 TestData\MultiFileManifestV1_4 TestData\MultiFileManifestV1_4 TestData\MultiFileManifestV1_4 TestData\MultiFileManifestV1_5 TestData\MultiFileManifestV1_5 TestData\MultiFileManifestV1_5 TestData\MultiFileManifestV1_5 TestData\MultiFileManifestV1_6 TestData\MultiFileManifestV1_6 TestData\MultiFileManifestV1_6 TestData\MultiFileManifestV1_6 TestData\MultiFileManifestV1_7 TestData\MultiFileManifestV1_7 TestData\MultiFileManifestV1_7 TestData\MultiFileManifestV1_7 TestData\MultiFileManifestV1_9 TestData\MultiFileManifestV1_9 TestData\MultiFileManifestV1_9 TestData\MultiFileManifestV1_9 TestData\MultiFileManifestV1_10 TestData\MultiFileManifestV1_10 TestData\MultiFileManifestV1_10 TestData\MultiFileManifestV1_10 TestData\MultiFileManifestV1_12 TestData\MultiFileManifestV1_12 TestData\MultiFileManifestV1_12 TestData\MultiFileManifestV1_12 TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData\Shadow\V1_5 TestData\Shadow\V1_5 TestData\Shadow\V1_5 TestData\Shadow\V1_5 TestData\Shadow\V1_5 TestData\Shadow\V1_5 TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData TestData\MultiFileManifestV1_28 TestData\MultiFileManifestV1_28 TestData\MultiFileManifestV1_28 TestData\MultiFileManifestV1_28 TestData TestData TestData TestData ================================================ FILE: src/AppInstallerCLITests/AppShutdown.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "Commands/TestCommand.h" using namespace AppInstaller::CLI; TEST_CASE("AppShutdown_WindowMessage", "[appShutdown]") { if (AppInstaller::Runtime::IsRunningAsAdmin() && AppInstaller::Runtime::IsRunningInPackagedContext()) { WARN("Test can't run as admin in package context"); return; } std::ostringstream output; Execution::Context context{ output, std::cin }; context.Args.AddArg(Execution::Args::Type::Force); TestAppShutdownCommand appShutdownCmd({}); appShutdownCmd.Execute(context); REQUIRE(context.IsTerminated()); REQUIRE(S_OK == context.GetTerminationHR()); } ================================================ FILE: src/AppInstallerCLITests/Archive.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace AppInstaller::Archive; using namespace TestCommon; constexpr std::string_view s_ZipFile = "TestZip.zip"; TEST_CASE("Extract_ZipArchive", "[archive]") { TestCommon::TempDirectory tempDirectory("TempDirectory"); TestDataFile testZip(s_ZipFile); const auto& testZipPath = testZip.GetPath(); const auto& tempDirectoryPath = tempDirectory.GetPath(); HRESULT hr = TryExtractArchive(testZipPath, tempDirectoryPath); std::filesystem::path expectedPath = tempDirectoryPath / "test.txt"; REQUIRE(SUCCEEDED(hr)); REQUIRE(std::filesystem::exists(expectedPath)); } TEST_CASE("Scan_ZipArchive", "[archive]") { TestDataFile testZip(s_ZipFile); const auto& testZipPath = testZip.GetPath(); bool result = ScanZipFile(testZipPath); REQUIRE(result); } ================================================ FILE: src/AppInstallerCLITests/Argument.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace AppInstaller::CLI; TEST_CASE("EnsureAllArgumentsDefined", "[argument]") { using Arg_t = std::underlying_type_t; for (Arg_t i = static_cast(0); i < static_cast(Execution::Args::Type::Max); ++i) { REQUIRE_NOTHROW(ArgumentCommon::ForType(static_cast(i))); } } ================================================ FILE: src/AppInstallerCLITests/ArpHelper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestHooks.h" #include "Microsoft/ARPHelper.h" #include "AppInstallerStrings.h" using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Utility; using namespace AppInstaller::Registry; TEST_CASE("ARPHelper_Watcher", "[ARPHelper]") { ARPHelper helper; wil::unique_event callbackEvent; callbackEvent.create(); ScopeEnum scopeCallback = ScopeEnum::Unknown; Architecture architectureCallback = Architecture::Unknown; ScopeEnum scopeTarget = ScopeEnum::Machine; Architecture architectureTarget = Architecture::X86; auto fakeRoot = TestCommon::RegCreateVolatileTestRoot(); TestHook::SetGetARPKey_Override arpOverride([&](ScopeEnum scope, Architecture arch) { if (scope == scopeTarget && arch == architectureTarget) { return Key(fakeRoot.get(), L""); } else { return Key{}; } }); auto watchers = helper.CreateRegistryWatchers(scopeTarget, [&](ScopeEnum scope, Architecture arch, wil::RegistryChangeKind) { scopeCallback = scope; architectureCallback = arch; callbackEvent.SetEvent(); }); auto arpKey = helper.GetARPKey(scopeTarget, architectureTarget); REQUIRE(!!arpKey); GUID guid; std::ignore = CoCreateGuid(&guid); std::ostringstream stream; stream << guid; auto testKey = TestCommon::RegCreateVolatileSubKey(arpKey, ConvertToUTF16(stream.str())); REQUIRE(callbackEvent.wait(1000)); REQUIRE(scopeTarget == scopeCallback); REQUIRE(architectureTarget == architectureCallback); // Reset for changing a value scopeCallback = ScopeEnum::Unknown; architectureCallback = Architecture::Unknown; TestCommon::SetRegistryValue(testKey.get(), L"testValue", L"valueValue"); REQUIRE(callbackEvent.wait(1000)); REQUIRE(scopeTarget == scopeCallback); REQUIRE(architectureTarget == architectureCallback); } ================================================ FILE: src/AppInstallerCLITests/Certificates.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Certificates; TEST_CASE("Certificates_NoPinningSucceeds", "[certificates]") { PinningDetails expected; expected.LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::None); PinningDetails actual; actual.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(CertificatePinningValidationResult::Accepted == expected.Validate(actual.GetCertificate(), CertificateChainPosition::Leaf)); } TEST_CASE("Certificates_PublicKeyMismatch", "[certificates]") { PinningDetails expected; expected.LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); PinningDetails actual; actual.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(CertificatePinningValidationResult::Rejected == expected.Validate(actual.GetCertificate(), CertificateChainPosition::Leaf)); } TEST_CASE("Certificates_PublicKeyMatch", "[certificates]") { PinningDetails expected; expected.LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); PinningDetails actual; actual.LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(CertificatePinningValidationResult::Accepted == expected.Validate(actual.GetCertificate(), CertificateChainPosition::Root)); } TEST_CASE("Certificates_SubjectMismatch", "[certificates]") { PinningDetails expected; expected.LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject); PinningDetails actual; actual.LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(CertificatePinningValidationResult::Rejected == expected.Validate(actual.GetCertificate(), CertificateChainPosition::Intermediate)); } TEST_CASE("Certificates_SubjectMatch", "[certificates]") { PinningDetails expected; expected.LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject); PinningDetails actual; actual.LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(CertificatePinningValidationResult::Accepted == expected.Validate(actual.GetCertificate(), CertificateChainPosition::Intermediate)); } TEST_CASE("Certificates_IssuerMismatch", "[certificates]") { PinningDetails expected; expected.LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Issuer); PinningDetails actual; actual.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(CertificatePinningValidationResult::Rejected == expected.Validate(actual.GetCertificate(), CertificateChainPosition::Leaf)); } TEST_CASE("Certificates_IssuerMatch", "[certificates]") { PinningDetails expected; expected.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Issuer); PinningDetails actual; actual.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(CertificatePinningValidationResult::Accepted == expected.Validate(actual.GetCertificate(), CertificateChainPosition::Leaf)); } TEST_CASE("Certificates_ChainLengthDiffers", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(!config.Validate(details.GetCertificate())); } TEST_CASE("Certificates_ChainLengthDiffers_Partial", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chain.PartialChain(); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(config.Validate(details.GetCertificate())); } TEST_CASE("CertificateChain_AnyIssuer_Intermediate", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey | PinningVerificationType::AnyIssuer | PinningVerificationType::RequireNonLeaf); chain.PartialChain(); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(config.Validate(details.GetCertificate())); } TEST_CASE("CertificateChain_AnyIssuer_IntermediateDiffers", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_1, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey | PinningVerificationType::AnyIssuer | PinningVerificationType::RequireNonLeaf); chain.PartialChain(); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(!config.Validate(details.GetCertificate())); } TEST_CASE("CertificateChain_AnyIssuer_IntermediateAndLeaf", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey | PinningVerificationType::AnyIssuer | PinningVerificationType::RequireNonLeaf); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chain.PartialChain(); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(config.Validate(details.GetCertificate())); } TEST_CASE("CertificateChain_AnyIssuer_Leaf", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey | PinningVerificationType::AnyIssuer); chain.PartialChain(); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(config.Validate(details.GetCertificate())); } TEST_CASE("CertificateChain_AnyIssuer_LeafDiffers", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_1, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey | PinningVerificationType::AnyIssuer); chain.PartialChain(); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(!config.Validate(details.GetCertificate())); } TEST_CASE("CertificateChain_AnyIssuer_SameLeaf_RequireNonLeaf", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey | PinningVerificationType::AnyIssuer | PinningVerificationType::RequireNonLeaf); chain.PartialChain(); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(!config.Validate(details.GetCertificate())); } TEST_CASE("Certificates_EmptyChainRejects", "[certificates]") { PinningChain chain; PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(!config.Validate(details.GetCertificate())); } TEST_CASE("Certificates_ChainOrderDiffers", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(!config.Validate(details.GetCertificate())); } TEST_CASE("Certificates_StoreChain_BuiltInTest", "[certificates]") { PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(config.Validate(details.GetCertificate())); } TEST_CASE("Certificates_MultipleChains_Success", "[certificates]") { PinningChain chainOutOfOrder; auto chainElement = chainOutOfOrder.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); PinningConfiguration config; config.AddChain(chainOutOfOrder); PinningChain chain; chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(config.Validate(details.GetCertificate())); } TEST_CASE("CertificateChain_Position", "[certificates]") { CertificateChainPosition positions[3]{ CertificateChainPosition::Unknown, CertificateChainPosition::Unknown, CertificateChainPosition::Unknown }; PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetCustomValidationFunction([&](const PinningDetails&, PCCERT_CONTEXT, CertificateChainPosition position) { positions[0] = position; return true; }); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetCustomValidationFunction([&](const PinningDetails&, PCCERT_CONTEXT, CertificateChainPosition position) { positions[1] = position; return true; }); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetCustomValidationFunction([&](const PinningDetails&, PCCERT_CONTEXT, CertificateChainPosition position) { positions[2] = position; return true; }); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(config.Validate(details.GetCertificate())); REQUIRE(CertificateChainPosition::Root == positions[0]); REQUIRE(CertificateChainPosition::Intermediate == positions[1]); REQUIRE(CertificateChainPosition::Leaf == positions[2]); } TEST_CASE("CertificateChain_Position_SelfSigned", "[certificates]") { CertificateChainPosition positions[1]{ CertificateChainPosition::Unknown }; PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetCustomValidationFunction([&](const PinningDetails&, PCCERT_CONTEXT, CertificateChainPosition position) { positions[0] = position; return true; }); PinningConfiguration config; config.AddChain(chain); PinningDetails details; details.LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE); REQUIRE(config.Validate(details.GetCertificate())); REQUIRE((CertificateChainPosition::Root | CertificateChainPosition::Leaf) == positions[0]); } ================================================ FILE: src/AppInstallerCLITests/CheckpointDatabase.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; using namespace AppInstaller::Checkpoints; TEST_CASE("CheckpointDatabaseCreateLatestAndReopen", "[checkpointDatabase]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Version versionCreated; // Create the database { std::shared_ptr database = CheckpointDatabase::CreateNew(tempFile, Version::Latest()); versionCreated = database->GetVersion(); } // Reopen the database { INFO("Trying with ReadWrite"); std::shared_ptr database = CheckpointDatabase::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); Version versionRead = database->GetVersion(); REQUIRE(versionRead == versionCreated); } } TEST_CASE("CheckpointDatabase_WriteAndRemoveMetadata", "[checkpointDatabase]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string_view testCheckpointName = "testCheckpoint"sv; std::string testCommand = "install"; std::string testClientVersion = "1.20.1234"; { std::shared_ptr database = CheckpointDatabase::CreateNew(tempFile, { 1, 0 }); CheckpointDatabase::IdType checkpointId = database->AddCheckpoint(testCheckpointName); database->SetDataValue(checkpointId, AutomaticCheckpointData::Command, {}, { testCommand }); database->SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { testClientVersion }); } { std::shared_ptr database = CheckpointDatabase::Open(tempFile); const auto& checkpointIds = database->GetCheckpointIds(); REQUIRE_FALSE(checkpointIds.empty()); auto checkpointId = checkpointIds[0]; REQUIRE(testCommand == database->GetDataFieldSingleValue(checkpointId, AutomaticCheckpointData::Command, {})); REQUIRE(testClientVersion == database->GetDataFieldSingleValue(checkpointId, AutomaticCheckpointData::ClientVersion, {})); database->RemoveDataType(checkpointId, AutomaticCheckpointData::Command); database->RemoveDataType(checkpointId, AutomaticCheckpointData::ClientVersion); } { std::shared_ptr database = CheckpointDatabase::Open(tempFile); const auto& checkpointIds = database->GetCheckpointIds(); REQUIRE_FALSE(checkpointIds.empty()); auto checkpointId = checkpointIds[0]; REQUIRE(database->GetDataTypes(checkpointId).empty()); } } TEST_CASE("CheckpointDatabase_WriteContextData", "[checkpointDatabase]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string_view testCheckpoint = "testCheckpoint"sv; std::string fieldName1 = "field1"; std::string fieldName2 = "field2"; std::string testValue1 = "value1"; std::string testValue2 = "value2"; std::string testValue3 = "value3"; { std::shared_ptr database = CheckpointDatabase::CreateNew(tempFile, { 1, 0 }); CheckpointDatabase::IdType checkpointId = database->AddCheckpoint(testCheckpoint); // Add multiple fields. database->SetDataValue(checkpointId, AutomaticCheckpointData::Arguments, fieldName1, { testValue1 }); database->SetDataValue(checkpointId, AutomaticCheckpointData::Arguments, fieldName2, { testValue2, testValue3 }); } { std::shared_ptr database = CheckpointDatabase::Open(tempFile); const auto& checkpointIds = database->GetCheckpointIds(); REQUIRE_FALSE(checkpointIds.empty()); auto automaticCheckpointId = checkpointIds.back(); const auto& fieldNames = database->GetDataFieldNames(automaticCheckpointId, AutomaticCheckpointData::Arguments); REQUIRE(fieldNames[0] == fieldName1); REQUIRE(fieldNames[1] == fieldName2); REQUIRE(testValue1 == database->GetDataFieldSingleValue(automaticCheckpointId, AutomaticCheckpointData::Arguments, fieldName1)); const auto& multiValues = database->GetDataFieldMultiValue(automaticCheckpointId, AutomaticCheckpointData::Arguments, fieldName2); REQUIRE(testValue2 == multiValues[0]); REQUIRE(testValue3 == multiValues[1]); } } TEST_CASE("CheckpointDatabase_CheckpointOrder", "[checkpointDatabase]") { // Verifies that the checkpoints are shown in reverse order (latest first). TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); { std::shared_ptr database = CheckpointDatabase::CreateNew(tempFile, { 1, 0 }); database->AddCheckpoint("firstCheckpoint"sv); database->AddCheckpoint("secondCheckpoint"sv); database->AddCheckpoint("thirdCheckpoint"sv); } { std::shared_ptr database = CheckpointDatabase::Open(tempFile); const auto& checkpointIds = database->GetCheckpointIds(); REQUIRE(checkpointIds.size() == 3); REQUIRE(checkpointIds[0] == 3); REQUIRE(checkpointIds[1] == 2); REQUIRE(checkpointIds[2] == 1); } } ================================================ FILE: src/AppInstallerCLITests/Command.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; std::string GetCommandName(const std::unique_ptr& command) { return std::string{ command->FullName() }; } std::vector GetCommandAliases(const std::unique_ptr& command) { return std::vector { command->Aliases() }; } std::string GetArgumentName(const Argument& arg) { return std::string{ arg.Name() }; } std::string GetArgumentAlternateName(const Argument& arg) { if (arg.AlternateName() == Argument::NoAlternateName) { return {}; } else { return std::string{ arg.AlternateName() }; } } std::string GetArgumentAlias(const Argument& arg) { if (arg.Alias() == ArgumentCommon::NoAlias) { return {}; } else { return std::string(1, arg.Alias()); } } bool StringIsLowercase(const std::string& s) { return Utility::ToLower(s) == s; } template void EnsureStringsAreLowercaseAndNoCollisions(const std::string& info, const Enumerable& e, Op& op, std::unordered_set& values, bool requireLower = true) { INFO(info); for (const auto& val : e) { std::string valString = op(val); if (valString.empty()) { continue; } INFO(valString); if (requireLower) { REQUIRE(StringIsLowercase(valString)); } REQUIRE(values.find(valString) == values.end()); values.emplace(std::move(valString)); } } template void EnsureStringsAreLowercaseAndNoCollisions(const std::string& info, const Enumerable& e, Op& op, bool requireLower = true) { std::unordered_set values; EnsureStringsAreLowercaseAndNoCollisions(info, e, op, values, requireLower); } template void EnsureVectorStringViewsAreLowercaseAndNoCollisions(const std::string& info, const Enumerable& e, Op& op, std::unordered_set& values, bool requireLower = true) { INFO(info); for (const auto& val : e) { std::vector aliasVector = op(val); std::vector valVector(aliasVector.begin(), aliasVector.end()); if (valVector.empty()) { continue; } // When op returns a vector, we need to ensure every value in the vector does not cause a collision for (auto& valString : valVector) { INFO(valString); if (requireLower) { REQUIRE(StringIsLowercase(valString)); } REQUIRE(values.find(valString) == values.end()); values.emplace(std::move(valString)); } } } void EnsureCommandConsistency(const Command& command) { // Command names and aliases exist in the same space, so both need to be checked as a set // However, collisions do not occur between levels, so the full name must be used to check for collision std::unordered_set allCommandAliasNames; EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " commands", command.GetCommands(), GetCommandName, allCommandAliasNames); EnsureVectorStringViewsAreLowercaseAndNoCollisions(command.FullName() + " aliases", command.GetCommands(), GetCommandAliases, allCommandAliasNames); auto args = command.GetArguments(); Argument::GetCommon(args); // Argument names and alternate names exist in the same space, so both need to be checked as a set std::unordered_set allArgumentNames; EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " argument names", args, GetArgumentName, allArgumentNames); EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " argument alternate name", args, GetArgumentAlternateName, allArgumentNames); // Argument aliases use a different space than the names and can be checked separately EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " argument alias", args, GetArgumentAlias, false); // No : allowed in commands for (const auto& comm : command.GetCommands()) { INFO(command.FullName()); INFO(comm->Name()); REQUIRE(comm->Name().find_first_of(Command::ParentSplitChar) == std::string_view::npos); } // No = allowed in arguments // All positional args should be listed first bool foundNonPositional = false; bool queryArgPresent = false; bool multiQueryArgPresent = false; for (const auto& arg : command.GetArguments()) { INFO(command.FullName()); INFO(arg.Name()); REQUIRE(arg.Name().find_first_of(APPINSTALLER_CLI_ARGUMENT_SPLIT_CHAR) == std::string_view::npos); if (arg.Type() == ArgumentType::Positional) { REQUIRE(!foundNonPositional); } else { foundNonPositional = true; } if (arg.ExecArgType() == Execution::Args::Type::Query) { queryArgPresent = true; } if (arg.ExecArgType() == Execution::Args::Type::MultiQuery) { multiQueryArgPresent = true; } } REQUIRE((!queryArgPresent || !multiQueryArgPresent)); // Recurse for all subcommands for (const auto& sub : command.GetCommands()) { EnsureCommandConsistency(*sub.get()); } } // This test ensure that the command tree we expose does not have any inconsistencies. // 1. No command name collisions // 2. All command names are lower cased // 3. No argument name collisions // 4. All arguments are lower cased // 5. No argument alias collisions // 6. All argument alias are lower cased // 7. No argument names contain '=' // 8. All positional arguments are first in the list // 9. No command includes both Query and MultiQuery arguments TEST_CASE("EnsureCommandTreeConsistency", "[command]") { RootCommand root; EnsureCommandConsistency(root); } struct TestCommand : public Command { TestCommand(std::vector args) : Command("test", ""), m_args(std::move(args)) {} std::vector GetArguments() const override { return m_args; } CLI::Resource::LocString ShortDescription() const override { return {}; } CLI::Resource::LocString LongDescription() const override { return {}; } std::vector m_args; }; // Matcher that lets us verify CommandExceptions. struct CommandExceptionMatcher : public Catch::Matchers::MatcherBase { CommandExceptionMatcher(CLI::Resource::LocString message) : m_expectedMessage(std::move(message)) {} bool match(const CommandException& ce) const override { return ce.Message() == m_expectedMessage; } std::string describe() const override { std::ostringstream result; result << "has message == " << m_expectedMessage; return result.str(); } private: CLI::Resource::LocString m_expectedMessage; }; namespace Catch { template<> struct StringMaker { static std::string convert(CommandException const& ce) { return Utility::Format("CommandException{ '{0}' }", ce.Message().get()); } }; } #define REQUIRE_COMMAND_EXCEPTION(_expr_, _arg_) REQUIRE_THROWS_MATCHES(_expr_, CommandException, CommandExceptionMatcher(_arg_)) void RequireValueParsedToArg(const std::string& value, const Argument& arg, const Args& args) { REQUIRE(args.Contains(arg.ExecArgType())); REQUIRE(value == args.GetArg(arg.ExecArgType())); } void RequireValuesParsedToArg(const std::vector& values, Args::Type execArgType, const Args& args) { REQUIRE(args.Contains(execArgType)); REQUIRE(args.GetCount(execArgType) == values.size()); auto argValues = args.GetArgs(execArgType); for (size_t i = 0; i < values.size(); ++i) { REQUIRE(argValues->at(i) == values[i]); } } void RequireValuesParsedToArg(const std::vector& values, const Argument& arg, const Args& args) { RequireValuesParsedToArg(values, arg.ExecArgType(), args); } // Description used for tests; doesn't need to be anything in particular. static constexpr CLI::Resource::StringId DefaultDesc{ L""sv }; TEST_CASE("ParseArguments_MultiplePositional", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "val1", "val2" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[0], command.m_args[0], args); RequireValueParsedToArg(values[1], command.m_args[2], args); } TEST_CASE("ParseArguments_ForcePositional", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "val1", "--", "-std1" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[0], command.m_args[0], args); RequireValueParsedToArg(values[2], command.m_args[2], args); } TEST_CASE("ParseArguments_TooManyPositional", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, }); std::vector values{ "val1", "--", "-std1" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::ExtraPositionalError(Utility::LocIndView{ values[2] })); } TEST_CASE("ParseArguments_InvalidChar", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "val1", "-", "-std1" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidArgumentSpecifierError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_InvalidAlias", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "val1", "-b", "-std1" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidAliasError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_MultiFlag", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Flag }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag2", 't', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "val1", "-st", "val2" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); REQUIRE(args.Contains(command.m_args[1].ExecArgType())); REQUIRE(args.Contains(command.m_args[3].ExecArgType())); } TEST_CASE("ParseArguments_FlagThenUnknown", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Flag }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag2", 't', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "val1", "-sr", "val2" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFoundError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_FlagThenNonFlag", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Flag }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag2", 't', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "val1", "-sp", "val2" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFlagError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_NameUsingAliasSpecifier", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 'f', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "another", "-flag1" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFoundError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_AliasWithAdjoinedValue", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "-s=Val1" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[0].substr(3), command.m_args[1], args); } TEST_CASE("ParseArguments_AliasWithSeparatedValue", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "-s", "Val1" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[1], command.m_args[1], args); } TEST_CASE("ParseArguments_AliasWithSeparatedValueMissing", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "-s" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::MissingArgumentError(Utility::LocIndView{ values[0] })); } TEST_CASE("ParseArguments_NameWithAdjoinedValue", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "--pos1=Val1" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[0].substr(7), command.m_args[0], args); } TEST_CASE("ParseArguments_AlternateNameWithAdjoinedValue", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', "p1", Args::Type::Channel, DefaultDesc, ArgumentType::Positional}, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, }); std::vector values{ "--p1=Val1" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[0].substr(5), command.m_args[0], args); } TEST_CASE("ParseArguments_NameFlag", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 'f', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "--flag1", "arbitrary" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[1], command.m_args[0], args); REQUIRE(args.Contains(command.m_args[3].ExecArgType())); } TEST_CASE("ParseArguments_NameFlagWithAdjoinedValue", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 'f', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "another", "--flag1=arbitrary" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::FlagContainAdjoinedError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_NameWithSeparatedValue", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 'f', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "--pos2", "arbitrary" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[1], command.m_args[2], args); } TEST_CASE("ParseArguments_NameWithSeparatedValueMissing", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 'f', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "--pos2" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::MissingArgumentError(Utility::LocIndView{ values[0] })); } TEST_CASE("ParseArguments_UnknownName", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Channel, DefaultDesc, ArgumentType::Positional }, Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, Argument{ "flag1", 'f', Args::Type::Exact, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "another", "--nope" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidNameError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_PositionalWithMultipleValues", "[command]") { Args args; TestCommand command({ Argument{ "multi", 'm', Args::Type::MultiQuery, DefaultDesc, ArgumentType::Positional }.SetCountLimit(5), }); std::vector values{ "value1" "value2", "value3" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValuesParsedToArg(values, command.m_args[0], args); } TEST_CASE("ParseArguments_PositionalWithMultipleValuesAndOtherArgs", "[command]") { Args args; TestCommand command({ Argument{ "pos1", 'p', Args::Type::Source, DefaultDesc, ArgumentType::Positional }, Argument{ "pos2", 'q', Args::Type::All, DefaultDesc, ArgumentType::Positional }, Argument{ "multi", 'm', Args::Type::MultiQuery, DefaultDesc, ArgumentType::Positional }.SetCountLimit(5), Argument{ "flag", 'f', Args::Type::BlockingPin, DefaultDesc, ArgumentType::Flag }, }); std::vector values{ "positional", "-q", "anotherPos", "multiValue1", "multiValue2", "-f" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValueParsedToArg(values[0], command.m_args[0], args); RequireValueParsedToArg(values[2], command.m_args[1], args); RequireValuesParsedToArg({ values[3], values[4] }, command.m_args[2], args); REQUIRE(args.Contains(command.m_args[3].ExecArgType())); } TEST_CASE("ParseArguments_PositionalWithMultipleValuesAndName", "[command]") { Args args; TestCommand command({ Argument{ "multi", 'm', Args::Type::MultiQuery, DefaultDesc, ArgumentType::Positional }.SetCountLimit(5), }); std::vector values{ "--multi", "one", "two", "three" }; Invocation inv{ std::vector(values) }; command.ParseArguments(inv, args); RequireValuesParsedToArg({ values[1], values[2], values[3] }, command.m_args[0], args); } TEST_CASE("ParseArguments_MultiQueryConvertedToSingleQuery", "[command]") { Args args; TestCommand command({ Argument{ "multi", 'm', Args::Type::MultiQuery, DefaultDesc, ArgumentType::Positional }.SetCountLimit(5), }); std::vector values{ "singleValue" }; Invocation inv{ std::vector(values) }; // ParseArguments converts MultiQuery args with a single value into Query args command.ParseArguments(inv, args); RequireValuesParsedToArg({ values[0] }, Args::Type::Query, args); } TEST_CASE("ParseArguments_PositionalWithTooManyValues", "[command]") { Args args; TestCommand command({ Argument{ "multi", 'm', Args::Type::MultiQuery, DefaultDesc, ArgumentType::Positional }.SetCountLimit(5), }); std::vector values{ "1", "2", "3", "4", "5", "tooMany" }; Invocation inv{ std::vector(values) }; REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::ExtraPositionalError(Utility::LocIndView{ values.back() })); } ================================================ FILE: src/AppInstallerCLITests/Completion.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; TEST_CASE("CompletionData_EmptyWord_PositionAtEnd", "[complete]") { CompletionData cd{ "", "winget ", "7" }; REQUIRE(cd.Word() == ""); REQUIRE(cd.BeforeWord().size() == 0); REQUIRE(cd.AfterWord().size() == 0); } TEST_CASE("CompletionData_EmptyWord_PositionPastEnd", "[complete]") { CompletionData cd{ "", "winget ", "8" }; REQUIRE(cd.Word() == ""); REQUIRE(cd.BeforeWord().size() == 0); REQUIRE(cd.AfterWord().size() == 0); } TEST_CASE("CompletionData_EmptyWord_PositionCorrect", "[complete]") { CompletionData cd{ "", "winget install", "7" }; REQUIRE(cd.Word() == ""); REQUIRE(cd.BeforeWord().size() == 0); REQUIRE(cd.AfterWord().size() == 1); } TEST_CASE("CompletionData_EmptyWord_PositionOffset", "[complete]") { CompletionData cd{ "", "winget install PowerToys --version", "17" }; REQUIRE(cd.Word() == ""); REQUIRE(cd.BeforeWord().size() == 1); REQUIRE(cd.AfterWord().size() == 2); } TEST_CASE("CompletionData_Word_NoMatch", "[complete]") { auto lambda = []() { CompletionData cd{ "foo", "winget install PowerToys --version", "17" }; }; REQUIRE_THROWS_HR(lambda(), APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD); } TEST_CASE("CompletionData_Word_SingleMatch", "[complete]") { CompletionData cd{ "power", "winget install power --version", "17" }; REQUIRE(cd.Word() == "power"); REQUIRE(cd.BeforeWord().size() == 1); REQUIRE(cd.AfterWord().size() == 1); } TEST_CASE("CompletionData_Word_MultiMatch_PositionCorrect", "[complete]") { CompletionData cd{ "power", "winget install power --id power", "27" }; REQUIRE(cd.Word() == "power"); REQUIRE(cd.BeforeWord().size() == 3); REQUIRE(cd.AfterWord().size() == 0); } TEST_CASE("CompletionData_Word_MultiMatch_PositionOffset", "[complete]") { CompletionData cd{ "power", "winget install power --id power", "21" }; REQUIRE(cd.Word() == "power"); REQUIRE(cd.BeforeWord().size() == 1); REQUIRE(cd.AfterWord().size() == 2); } TEST_CASE("CompletionData_UTF8_EmptyWord_End", "[complete]") { CompletionData cd{ "", u8"winget install \x175\x12b\x14b\x1e5\x229\x288 --version ", "32" }; REQUIRE(cd.Word() == ""); REQUIRE(cd.BeforeWord().size() == 3); REQUIRE(cd.AfterWord().size() == 0); } TEST_CASE("CompletionData_UTF8_EmptyWord_Middle", "[complete]") { CompletionData cd{ "", u8"winget install \x175\x12b\x14b\x1e5\x229\x288 --version ", "22" }; REQUIRE(cd.Word() == ""); REQUIRE(cd.BeforeWord().size() == 2); REQUIRE(cd.AfterWord().size() == 1); } TEST_CASE("CompletionData_UTF8_UTF8Word", "[complete]") { CompletionData cd{ u8"\x175\x12b\x14b\x1e5\x229\x288", u8"winget install \x175\x12b\x14b\x1e5\x229\x288 --version ", "18" }; REQUIRE(cd.Word() == u8"\x175\x12b\x14b\x1e5\x229\x288"); REQUIRE(cd.BeforeWord().size() == 1); REQUIRE(cd.AfterWord().size() == 1); } void OutputAllSubCommands(Command& command, std::ostream& out, std::string_view filter = {}) { for (const auto& c : command.GetCommands()) { if (Utility::CaseInsensitiveStartsWith(c->Name(), filter)) { out << c->Name() << std::endl; } } } void OutputAllArgumentNames(Command& command, std::ostream& out, std::string_view filter = {}, bool includeCommon = true) { auto args = command.GetArguments(); if (includeCommon) { Argument::GetCommon(args); } for (const auto& a : args) { if (Utility::CaseInsensitiveStartsWith(a.Name(), filter)) { out << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << a.Name() << std::endl; } } } void OutputAllArgumentAliases(Command& command, std::ostream& out, bool includeCommon = true) { auto args = command.GetArguments(); if (includeCommon) { Argument::GetCommon(args); } for (const auto& a : args) { if (a.Alias() != ArgumentCommon::NoAlias) { out << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << a.Alias() << std::endl; } } } TEST_CASE("CompleteCommand_FindRoot", "[complete]") { std::stringstream out, in; Context context{ out, in }; CompleteCommand command{ "test" }; context.Args.AddArg(Args::Type::Word, ""sv); context.Args.AddArg(Args::Type::CommandLine, "winget "sv); context.Args.AddArg(Args::Type::Position, "7"sv); command.Execute(context); // Create expected values RootCommand expectedCommand; std::stringstream expected; OutputAllSubCommands(expectedCommand, expected); OutputAllArgumentNames(expectedCommand, expected); REQUIRE(out.str() == expected.str()); } TEST_CASE("CompleteCommand_FindSource", "[complete]") { std::stringstream out, in; Context context{ out, in }; CompleteCommand command{ "test" }; context.Args.AddArg(Args::Type::Word, ""sv); context.Args.AddArg(Args::Type::CommandLine, "winget source "sv); context.Args.AddArg(Args::Type::Position, "14"sv); command.Execute(context); // Create expected values SourceCommand expectedCommand{ "test" }; std::stringstream expected; OutputAllSubCommands(expectedCommand, expected); OutputAllArgumentNames(expectedCommand, expected); REQUIRE(out.str() == expected.str()); } TEST_CASE("CompleteCommand_FindSourceAdd", "[complete]") { std::stringstream out, in; Context context{ out, in }; CompleteCommand command{ "test" }; context.Args.AddArg(Args::Type::Word, ""sv); context.Args.AddArg(Args::Type::CommandLine, "winget source add "sv); context.Args.AddArg(Args::Type::Position, "18"sv); command.Execute(context); // Create expected values SourceAddCommand expectedCommand{ "test" }; std::stringstream expected; OutputAllSubCommands(expectedCommand, expected); OutputAllArgumentNames(expectedCommand, expected); REQUIRE(out.str() == expected.str()); } struct CompletionTestCommand : public Command { CompletionTestCommand() : Command("test", "") {} CompletionTestCommand(std::string_view name) : Command(name, "") {} std::vector> GetCommands() const override { std::vector> result; for (const auto& sc : SubCommandNames) { result.emplace_back(std::make_unique(sc)); } return result; } std::vector GetArguments() const override { return Arguments; } CLI::Resource::LocString ShortDescription() const override { return {}; } CLI::Resource::LocString LongDescription() const override { return {}; } using Command::Complete; void Complete(Execution::Context& context, Execution::Args::Type valueType) const override { if (ArgumentValueCallback) { ArgumentValueCallback(context, valueType); } } std::vector SubCommandNames; std::vector Arguments; std::function ArgumentValueCallback; }; struct CompletionTestContext { CompletionTestContext(std::string_view word, std::string_view commandLine, std::string_view position) : context(out, in) { context.Reporter.SetChannel(Execution::Reporter::Channel::Completion); context.Add(CompletionData{ word, commandLine, position }); } std::stringstream out; std::stringstream in; Context context; }; TEST_CASE("CommandComplete_Simple", "[complete]") { CompletionTestContext ctc{ "", "winget ", "7" }; CompletionTestCommand command; command.SubCommandNames = { "test1", "test2" }; command.Arguments = { Argument{ "arg1", 'a', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard } }; command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllSubCommands(command, expected); OutputAllArgumentNames(command, expected); REQUIRE(ctc.out.str() == expected.str()); } TEST_CASE("CommandComplete_PartialCommandMatch", "[complete]") { CompletionTestContext ctc{ "cart", "winget cart", "11" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllSubCommands(command, expected, "cart"); OutputAllArgumentNames(command, expected, "cart"); REQUIRE(ctc.out.str() == expected.str()); } TEST_CASE("CommandComplete_CommandsNotAllowed", "[complete]") { CompletionTestContext ctc{ "", "winget foobar ", "14" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllArgumentNames(command, expected); REQUIRE(ctc.out.str() == expected.str()); } TEST_CASE("CommandComplete_Routing1", "[complete]") { CompletionTestContext ctc{ "", "winget --arg1 ", "14" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values std::stringstream expected; REQUIRE(ctc.out.str() == expected.str()); REQUIRE(argType == command.Arguments[0].ExecArgType()); } TEST_CASE("CommandComplete_Routing2", "[complete]") { CompletionTestContext ctc{ "", "winget --arg2 ", "14" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values std::stringstream expected; REQUIRE(ctc.out.str() == expected.str()); REQUIRE(argType == command.Arguments[1].ExecArgType()); } TEST_CASE("CommandComplete_PositionalRouting", "[complete]") { CompletionTestContext ctc{ "", "winget ", "7" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllSubCommands(command, expected); OutputAllArgumentNames(command, expected); REQUIRE(ctc.out.str() == expected.str()); REQUIRE(argType == command.Arguments[1].ExecArgType()); } TEST_CASE("CommandComplete_PositionalRoutingAfterArgs", "[complete]") { CompletionTestContext ctc{ "", "winget --arg1 value ", "20" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllArgumentNames(command, expected); REQUIRE(ctc.out.str() == expected.str()); REQUIRE(argType == command.Arguments[1].ExecArgType()); } TEST_CASE("CommandComplete_PositionalRoutingAfterDoubleDash", "[complete]") { CompletionTestContext ctc{ "", "winget -- ", "10" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values std::stringstream expected; REQUIRE(ctc.out.str() == expected.str()); REQUIRE(argType == command.Arguments[1].ExecArgType()); } TEST_CASE("CommandComplete_ArgNamesAfterDash", "[complete]") { CompletionTestContext ctc{ "-", "winget -", "8" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllArgumentNames(command, expected); REQUIRE(ctc.out.str() == expected.str()); } TEST_CASE("CommandComplete_AliasNames", "[complete]") { CompletionTestContext ctc{ "-a", "winget -a", "9" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllArgumentAliases(command, expected); REQUIRE(ctc.out.str() == expected.str()); } TEST_CASE("CommandComplete_ArgNamesFilter", "[complete]") { CompletionTestContext ctc{ "--a", "winget --a", "10" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, Argument{ "foo1", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllArgumentNames(command, expected, "a"); REQUIRE(ctc.out.str() == expected.str()); } TEST_CASE("CommandComplete_IgnoreBadArgs", "[complete]") { CompletionTestContext ctc{ "", "winget foo bar --arg1 ", "22" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values std::stringstream expected; REQUIRE(ctc.out.str() == expected.str()); REQUIRE(argType == command.Arguments[0].ExecArgType()); } TEST_CASE("CommandComplete_OtherArgsParsed", "[complete]") { CompletionTestContext ctc{ "", "winget --arg1 value1 --arg2 value2", "21" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values std::stringstream expected; OutputAllArgumentNames(command, expected); REQUIRE(ctc.out.str() == expected.str()); REQUIRE(ctc.context.Args.Contains(command.Arguments[0].ExecArgType())); REQUIRE(ctc.context.Args.GetArg(command.Arguments[0].ExecArgType()) == "value1"); REQUIRE(ctc.context.Args.Contains(command.Arguments[1].ExecArgType())); REQUIRE(ctc.context.Args.GetArg(command.Arguments[1].ExecArgType()) == "value2"); } TEST_CASE("CommandComplete_Complex", "[complete]") { CompletionTestContext ctc{ "", "winget foo --arg1 value1 bar junk --arg2 ", "41" }; CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; command.Arguments = { Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values std::stringstream expected; REQUIRE(ctc.out.str() == expected.str()); REQUIRE(argType == command.Arguments[1].ExecArgType()); REQUIRE(ctc.context.Args.Contains(command.Arguments[0].ExecArgType())); REQUIRE(ctc.context.Args.GetArg(command.Arguments[0].ExecArgType()) == "value1"); } ================================================ FILE: src/AppInstallerCLITests/CompositeSource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSource.h" #include "TestHooks.h" #include #include #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Pinning; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Utility; constexpr std::string_view s_Everything_Query = "everything"sv; // A test source that has two modes: // 1. A request that IsForEverything returns the stored result. This models the // incoming search request to a CompositeSource. // 2. A request that is not for everything invokes TestSource::SearchFunction to // enable verification of expectations. struct ComponentTestSource : public TestSource { ComponentTestSource() = default; ComponentTestSource(std::string_view identifier, SourceOrigin origin = SourceOrigin::Default) { Details.Identifier = identifier; Details.Origin = origin; } SearchResult Search(const SearchRequest& request) const override { if (request.Query && request.Query.value().Value == s_Everything_Query) { return Everything; } else { return TestSource::Search(request); } } SearchResult Everything; }; // A helper to make matches. struct Criteria : public PackageMatchFilter { Criteria() : PackageMatchFilter(PackageMatchField::Id, MatchType::Wildcard, ""sv) {} Criteria(PackageMatchField field) : PackageMatchFilter(field, MatchType::Wildcard, ""sv) {} }; Manifest::Manifest MakeDefaultManifest(std::string_view version = "1.0"sv) { Manifest::Manifest result; result.Id = "Id"; result.DefaultLocalization.Add("Name"); result.DefaultLocalization.Add("Publisher"); result.Version = version; result.Installers.push_back({}); return result; } struct TestManifestHelper { TestManifestHelper() : m_manifest(MakeDefaultManifest()) {} TestManifestHelper& WithId(const std::string& id) { m_manifest.Id = id; return *this; } TestManifestHelper& WithVersion(std::string_view version) { m_manifest.Version = version; return *this; } TestManifestHelper& WithChannel(const std::string& channel) { m_manifest.Channel = channel; return *this; } TestManifestHelper& WithDefaultName(std::string_view name) { m_manifest.DefaultLocalization.Add(std::string{ name }); return *this; } TestManifestHelper& WithPFN(const std::string& pfn) { m_manifest.Installers[0].PackageFamilyName = pfn; return *this; } TestManifestHelper& WithPC(const std::string& pc) { m_manifest.Installers[0].ProductCode = pc; return *this; } TestManifestHelper& WithType(Manifest::InstallerTypeEnum type) { m_manifest.Installers[0].BaseInstallerType = type; return *this; } TestManifestHelper& WithDisplayVersion(std::string_view version) { if (m_manifest.Installers[0].AppsAndFeaturesEntries.empty()) { m_manifest.Installers[0].AppsAndFeaturesEntries.emplace_back(); } m_manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = version; return *this; } operator const Manifest::Manifest& () const { return m_manifest; } private: Manifest::Manifest m_manifest; }; struct TestPackageHelper { TestPackageHelper(bool isInstalled, std::shared_ptr source = {}) : m_isInstalled(isInstalled), m_source(source) { m_manifestHelpers.emplace_back(); } TestPackageHelper& HideSRS(bool value = true) { m_hideSystemReferenceStrings = value; return *this; } std::shared_ptr ToPackage() { if (!m_package) { if (m_isInstalled) { m_package = TestCompositePackage::Make(this->operator const Manifest::Manifest&(), m_metadata, std::vector(), m_source); } else { std::vector manifests; for (const auto& helper : m_manifestHelpers) { manifests.emplace_back(helper.operator const AppInstaller::Manifest::Manifest &()); } m_package = TestCompositePackage::Make(manifests, m_source, m_hideSystemReferenceStrings); } } return m_package; } operator std::shared_ptr() { return ToPackage(); } TestPackageHelper& WithId(const std::string& id) { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); m_manifestHelpers[0].WithId(id); return *this; } TestPackageHelper& WithVersion(std::string_view version) { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); m_manifestHelpers[0].WithVersion(version); return *this; } TestPackageHelper& WithChannel(const std::string& channel) { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); m_manifestHelpers[0].WithChannel(channel); return *this; } TestPackageHelper& WithDefaultName(std::string_view name) { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); m_manifestHelpers[0].WithDefaultName(name); return *this; } TestPackageHelper& WithPFN(const std::string& pfn) { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); m_manifestHelpers[0].WithPFN(pfn); return *this; } TestPackageHelper& WithPC(const std::string& pc) { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); m_manifestHelpers[0].WithPC(pc); return *this; } TestPackageHelper& WithType(Manifest::InstallerTypeEnum type) { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); m_manifestHelpers[0].WithType(type); return *this; } TestPackageHelper& WithDisplayVersion(std::string_view version) { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); m_manifestHelpers[0].WithDisplayVersion(version); return *this; } TestPackageHelper& WithMetadata(PackageVersionMetadata metadata, const std::string& value) { THROW_HR_IF(E_UNEXPECTED, !m_isInstalled); m_metadata[metadata] = value; return *this; } operator const Manifest::Manifest& () const { THROW_HR_IF(E_UNEXPECTED, m_manifestHelpers.size() != 1); return m_manifestHelpers[0]; } TestManifestHelper& MakeManifest() { THROW_HR_IF(E_UNEXPECTED, m_isInstalled); m_manifestHelpers.emplace_back(); return m_manifestHelpers.back(); } private: bool m_isInstalled; std::vector m_manifestHelpers; std::shared_ptr m_source; std::shared_ptr m_package; bool m_hideSystemReferenceStrings = false; TestCompositePackage::MetadataMap m_metadata; }; // A helper to create the sources used by the majority of tests in this file. struct CompositeTestSetup { CompositeTestSetup(CompositeSearchBehavior behavior = CompositeSearchBehavior::Installed) : Composite("*Tests") { Installed = std::make_shared("InstalledTestSource1", SourceOrigin::Predefined); Available = std::make_shared("AvailableTestSource1"); Composite.SetInstalledSource(Source{ Installed }, behavior); Composite.AddAvailableSource(Source{ Available }); } SearchResult Search(bool disableDataChecks = false) { size_t initialCountOfCallsRequiringVersionData = Available->CountOfCallsRequiringVersionData; size_t initialCountOfCallsRequiringManifestData = Available->CountOfCallsRequiringManifestData; SearchRequest request; request.Query = RequestMatch(MatchType::Exact, s_Everything_Query); auto result = Composite.Search(request); // We want to prevent calls to these functions for Search as they can require network or I/O activity. size_t countOfCallsRequiringVersionData = Available->CountOfCallsRequiringVersionData - initialCountOfCallsRequiringVersionData; size_t countOfCallsRequiringManifestData = Available->CountOfCallsRequiringManifestData - initialCountOfCallsRequiringManifestData; if (!disableDataChecks && (countOfCallsRequiringVersionData || countOfCallsRequiringManifestData)) { std::ostringstream stream; stream << "Version data calls [" << countOfCallsRequiringVersionData << "] : Manifest data calls [" << countOfCallsRequiringManifestData << "]"; FAIL(stream.str()); } return result; } TestPackageHelper MakeInstalled(std::shared_ptr source) { return { /* isInstalled */ true, std::move(source)}; } TestPackageHelper MakeInstalled() { return MakeInstalled(Installed); } TestPackageHelper MakeAvailable(std::shared_ptr source) { return { /* isInstalled */ false, std::move(source) }; } TestPackageHelper MakeAvailable() { return MakeAvailable(Available); } std::shared_ptr Installed; std::shared_ptr Available; CompositeSource Composite; }; // A helper to create the sources used by the majority of tests in this file. struct CompositeWithTrackingTestSetup : public CompositeTestSetup { CompositeWithTrackingTestSetup() : TrackingFactory([&](const SourceDetails&) { return Tracking; }) { Tracking = std::make_shared(SourceDetails{}, SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET)); TestHook_SetSourceFactoryOverride(std::string{ PackageTrackingCatalogSourceFactory::Type() }, TrackingFactory); } ~CompositeWithTrackingTestSetup() { TestHook_ClearSourceFactoryOverrides(); } TestSourceFactory TrackingFactory; std::shared_ptr Tracking; }; bool SearchRequestIncludes(const std::vector& filters, PackageMatchField field, MatchType type, std::optional value = {}) { bool found = false; for (const PackageMatchFilter& filter : filters) { if (filter.Field == field && filter.Type == type && (!value || filter.Value == value.value())) { found = true; } } return found; } void RequireSearchRequestIncludes(const std::vector& filters, PackageMatchField field, MatchType type, std::optional value = {}) { REQUIRE(SearchRequestIncludes(filters, field, type, value)); } TEST_CASE("CompositeSource_PackageFamilyName_NotAvailable", "[CompositeSource]") { // Pre-folded for easier == std::string pfn = "sortof_apfn"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn), Criteria()); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().empty()); } TEST_CASE("CompositeSource_PackageFamilyName_Available", "[CompositeSource]") { std::string pfn = "sortof_apfn"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithPFN(pfn), Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_ProductCode_NotAvailable", "[CompositeSource]") { std::string pc = "thiscouldbeapc"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPC(pc), Criteria()); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().empty()); } TEST_CASE("CompositeSource_ProductCode_Available", "[CompositeSource]") { std::string pc = "thiscouldbeapc"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPC(pc), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::ProductCode, MatchType::Exact, pc); SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithPC(pc), Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_NameAndPublisher_Match", "[CompositeSource]") { CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled(), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact); SearchResult result; result.Matches.emplace_back(setup.MakeAvailable(), Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_MultiMatch_FindsStrongMatch", "[CompositeSource]") { std::string name = "MatchingName"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN("sortof_apfn"), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithId("A different ID"), Criteria(PackageMatchField::NormalizedNameAndPublisher)); result.Matches.emplace_back(setup.MakeAvailable().WithDefaultName(name), Criteria(PackageMatchField::PackageFamilyName)); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); auto version = result.Matches[0].Package->GetAvailable()[0]->GetLatestVersion(); REQUIRE(version->GetProperty(PackageVersionProperty::Name).get() == name); REQUIRE(!Version(version->GetProperty(PackageVersionProperty::Version)).IsUnknown()); } TEST_CASE("CompositeSource_MultiMatch_DoesNotFindStrongMatch", "[CompositeSource]") { CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN("sortof_apfn"), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithId("A different ID"), Criteria(PackageMatchField::NormalizedNameAndPublisher)); result.Matches.emplace_back(setup.MakeAvailable().WithId("Another diff ID"), Criteria(PackageMatchField::NormalizedNameAndPublisher)); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().empty()); } TEST_CASE("CompositeSource_FoundByBothRootSearches", "[CompositeSource]") { std::string pfn = "sortof_apfn"; CompositeTestSetup setup; auto installedPackage = setup.MakeInstalled().WithPFN(pfn); auto availablePackage = setup.MakeAvailable().WithPFN(pfn); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Installed->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(installedPackage, Criteria()); return result; }; setup.Available->Everything.Matches.emplace_back(availablePackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(availablePackage, Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_OnlyAvailableFoundByRootSearch", "[CompositeSource]") { std::string pfn = "sortof_apfn"; CompositeTestSetup setup; setup.Installed->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn), Criteria(PackageMatchField::PackageFamilyName)); return result; }; std::shared_ptr availablePackage = setup.MakeAvailable().WithPFN(pfn).ToPackage(); setup.Available->Everything.Matches.emplace_back(availablePackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(availablePackage, Criteria(PackageMatchField::PackageFamilyName)); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_FoundByAvailableRootSearch_NotInstalled", "[CompositeSource]") { std::string pfn = "sortof_apfn"; CompositeTestSetup setup; setup.Available->Everything.Matches.emplace_back(setup.MakeAvailable().WithPFN(pfn), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithPFN(pfn), Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.empty()); } TEST_CASE("CompositeSource_UpdateWithBetterMatchCriteria", "[CompositeSource]") { std::string pfn = "sortof_apfn"; MatchType originalType = MatchType::Wildcard; MatchType type = MatchType::Exact; CompositeTestSetup setup; auto installedPackage = setup.MakeInstalled().WithPFN(pfn); auto availablePackage = setup.MakeAvailable().WithPFN(pfn); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(availablePackage, Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); REQUIRE(result.Matches[0].MatchCriteria.Type == originalType); // Now make the source root search find it with a better criteria setup.Installed->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(installedPackage, Criteria()); return result; }; setup.Available->Everything.Matches.emplace_back(availablePackage, PackageMatchFilter(PackageMatchField::Id, type, ""sv)); result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); REQUIRE(result.Matches[0].MatchCriteria.Type == type); } TEST_CASE("CompositePackage_PropertyFromInstalled", "[CompositeSource]") { std::string id = "Special test ID"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithId(id), Criteria()); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetProperty(PackageProperty::Id) == id); } TEST_CASE("CompositePackage_PropertyFromAvailable", "[CompositeSource]") { std::string id = "Special test ID"; std::string pfn = "sortof_apfn"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithId(id), Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetProperty(PackageProperty::Id) == id); } TEST_CASE("CompositePackage_AvailableVersions_ChannelFilteredOut", "[CompositeSource]") { std::string pfn = "sortof_apfn"; std::string channel = "Channel"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { Manifest::Manifest noChannel = MakeDefaultManifest(); noChannel.Version = "1.0"; Manifest::Manifest hasChannel = MakeDefaultManifest(); hasChannel.Channel = channel; hasChannel.Version = "2.0"; SearchResult result; result.Matches.emplace_back(TestCompositePackage::Make(std::vector{ noChannel, hasChannel }, setup.Available), Criteria()); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 2); return result; }; // Disable data checks as we call one of the data methods as validation in the search SearchResult result = setup.Search(true); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); auto package = result.Matches[0].Package->GetAvailable()[0]; auto versionKeys = package->GetVersionKeys(); REQUIRE(versionKeys.size() == 2); auto availableVersions = GetAvailableVersionsForInstalledVersion(result.Matches[0].Package); auto availableVersionKeys = availableVersions->GetVersionKeys(); REQUIRE(availableVersionKeys.size() == 1); REQUIRE(availableVersionKeys[0].Channel.empty()); auto latestVersion = availableVersions->GetLatestVersion(); REQUIRE(latestVersion); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Channel).get().empty()); } TEST_CASE("CompositePackage_AvailableVersions_NoChannelFilteredOut", "[CompositeSource]") { std::string pfn = "sortof_apfn"; std::string channel = "Channel"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn).WithChannel(channel), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { Manifest::Manifest noChannel = MakeDefaultManifest(); noChannel.Version = "1.0"; Manifest::Manifest hasChannel = MakeDefaultManifest(); hasChannel.Channel = channel; hasChannel.Version = "2.0"; SearchResult result; result.Matches.emplace_back(TestCompositePackage::Make(std::vector{ noChannel, hasChannel }, setup.Available), Criteria()); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 2); return result; }; // Disable data checks as we call one of the data methods as validation in the search SearchResult result = setup.Search(true); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); auto package = result.Matches[0].Package->GetAvailable()[0]; auto versionKeys = package->GetVersionKeys(); REQUIRE(versionKeys.size() == 2); auto availableVersions = GetAvailableVersionsForInstalledVersion(result.Matches[0].Package); auto availableVersionKeys = availableVersions->GetVersionKeys(); REQUIRE(availableVersionKeys.size() == 1); REQUIRE(availableVersionKeys[0].Channel == channel); auto latestVersion = availableVersions->GetLatestVersion(); REQUIRE(latestVersion); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Channel).get() == channel); } TEST_CASE("CompositeSource_MultipleAvailableSources_MatchAll", "[CompositeSource]") { TestCommon::TestUserSettings testSettings; std::string pfn = "sortof_apfn"; std::string firstName = "Name1"; std::string secondName = "Name2"; CompositeTestSetup setup; std::shared_ptr secondAvailable = std::make_shared(); setup.Composite.AddAvailableSource(Source{ secondAvailable }); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithDefaultName(firstName), Criteria()); return result; }; secondAvailable->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(setup.MakeAvailable(secondAvailable).WithDefaultName(secondName), Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 2); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetProperty(PackageProperty::Name).get() == firstName); REQUIRE(result.Matches[0].Package->GetAvailable()[1]->GetProperty(PackageProperty::Name).get() == secondName); } TEST_CASE("CompositeSource_MultipleAvailableSources_MatchSecond", "[CompositeSource]") { std::string pfn = "sortof_apfn"; std::string firstName = "Name1"; std::string secondName = "Name2"; CompositeTestSetup setup; std::shared_ptr secondAvailable = std::make_shared(); setup.Composite.AddAvailableSource(Source{ secondAvailable }); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn), Criteria()); secondAvailable->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithDefaultName(secondName), Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetProperty(PackageProperty::Name).get() == secondName); } TEST_CASE("CompositeSource_MultipleAvailableSources_ReverseMatchBoth", "[CompositeSource]") { std::string pfn = "sortof_apfn"; CompositeTestSetup setup; auto installedPackage = setup.MakeInstalled().WithPFN(pfn); std::shared_ptr secondAvailable = std::make_shared(); setup.Composite.AddAvailableSource(Source{ secondAvailable }); setup.Installed->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(installedPackage, Criteria(PackageMatchField::PackageFamilyName)); return result; }; setup.Available->Everything.Matches.emplace_back(setup.MakeAvailable().WithPFN(pfn), Criteria()); secondAvailable->Everything.Matches.emplace_back(setup.MakeAvailable().WithPFN(pfn), Criteria()); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 2); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[1]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_IsSame", "[CompositeSource]") { CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN("sortof_apfn"), Criteria()); SearchResult result1 = setup.Search(); REQUIRE(result1.Matches.size() == 1); SearchResult result2 = setup.Search(); REQUIRE(result2.Matches.size() == 1); REQUIRE(result1.Matches[0].Package->GetInstalled()); REQUIRE(result1.Matches[0].Package->GetInstalled()->IsSame(result1.Matches[0].Package->GetInstalled().get())); } TEST_CASE("CompositeSource_AvailableSearchFailure", "[CompositeSource]") { HRESULT expectedHR = E_BLUETOOTH_ATT_ATTRIBUTE_NOT_FOUND; std::string pfn = "sortof_apfn"; std::shared_ptr AvailableSucceeds = std::make_shared(); AvailableSucceeds->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(TestPackageHelper{ /* isInstalled */ false }.WithPFN(pfn), Criteria()); return result; }; std::shared_ptr AvailableFails = std::make_shared(); AvailableFails->SearchFunction = [&](const SearchRequest&) -> SearchResult { THROW_HR(expectedHR); }; AvailableFails->Details.Name = "The one that fails"; CompositeSource Composite("*CompositeSource_AvailableSearchFailure"); Composite.AddAvailableSource(Source{ AvailableSucceeds }); Composite.AddAvailableSource(Source{ AvailableFails }); SearchResult result = Composite.Search({}); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); auto pfns = result.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); REQUIRE(pfns.size() == 1); REQUIRE(pfns[0] == pfn); REQUIRE(result.Failures.size() == 1); REQUIRE(result.Failures[0].SourceName == AvailableFails->Details.Name); HRESULT searchFailure = S_OK; try { std::rethrow_exception(result.Failures[0].Exception); } catch (const wil::ResultException& re) { searchFailure = re.GetErrorCode(); } catch (...) {} REQUIRE(searchFailure == expectedHR); } TEST_CASE("CompositeSource_InstalledToAvailableCorrelationSearchFailure", "[CompositeSource]") { HRESULT expectedHR = E_BLUETOOTH_ATT_ATTRIBUTE_NOT_LONG; std::string pfn = "sortof_apfn"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithPFN(pfn), Criteria()); setup.Available->Everything.Matches.emplace_back(setup.MakeAvailable().WithPFN(pfn), Criteria()); std::shared_ptr AvailableFails = std::make_shared(); AvailableFails->SearchFunction = [&](const SearchRequest&) -> SearchResult { THROW_HR(expectedHR); }; AvailableFails->Details.Name = "The one that fails"; setup.Composite.AddAvailableSource(Source{ AvailableFails }); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Failures.size() == 1); REQUIRE(result.Failures[0].SourceName == AvailableFails->Details.Name); HRESULT searchFailure = S_OK; try { std::rethrow_exception(result.Failures[0].Exception); } catch (const wil::ResultException& re) { searchFailure = re.GetErrorCode(); } catch (...) {} REQUIRE(searchFailure == expectedHR); } TEST_CASE("CompositeSource_InstalledAvailableSearchFailure", "[CompositeSource]") { HRESULT expectedHR = E_BLUETOOTH_ATT_ATTRIBUTE_NOT_LONG; std::string pfn = "sortof_apfn"; CompositeTestSetup setup; setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithPFN(pfn), Criteria()); return result; }; std::shared_ptr AvailableFails = std::make_shared(); AvailableFails->SearchFunction = [&](const SearchRequest&) -> SearchResult { THROW_HR(expectedHR); }; AvailableFails->Details.Name = "The one that fails"; setup.Composite.AddAvailableSource(Source{ AvailableFails }); setup.Composite.SetInstalledSource(Source{ setup.Installed }, CompositeSearchBehavior::AvailablePackages); SearchRequest request; request.Query = RequestMatch{ MatchType::Exact, "whatever" }; SearchResult result = setup.Composite.Search(request); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Failures.size() == 1); REQUIRE(result.Failures[0].SourceName == AvailableFails->Details.Name); HRESULT searchFailure = S_OK; try { std::rethrow_exception(result.Failures[0].Exception); } catch (const wil::ResultException& re) { searchFailure = re.GetErrorCode(); } catch (...) {} REQUIRE(searchFailure == expectedHR); } TEST_CASE("CompositeSource_TrackingPackageFound", "[CompositeSource]") { std::string availableID = "Available.ID"; std::string pfn = "sortof_apfn"; CompositeWithTrackingTestSetup setup; auto installedPackage = setup.MakeInstalled().WithPFN(pfn); auto availablePackage = setup.MakeAvailable().WithPFN(pfn).WithId(availableID).WithDefaultName(s_Everything_Query); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Installed->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(installedPackage, Criteria()); return result; }; setup.Available->Everything.Matches.emplace_back(availablePackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { if (request.Filters.empty()) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); } else { REQUIRE(request.Filters.size() == 1); RequireSearchRequestIncludes(request.Filters, PackageMatchField::Id, MatchType::CaseInsensitive, availableID); } SearchResult result; result.Matches.emplace_back(availablePackage, Criteria()); return result; }; setup.Tracking->GetIndex().AddManifest(availablePackage); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetInstalled()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); REQUIRE(GetInstalledVersion(result.Matches[0].Package)->GetSource().GetIdentifier() == setup.Available->Details.Identifier); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()); } TEST_CASE("CompositeSource_TrackingPackageFound_MetadataPopulatedFromTracking", "[CompositeSource]") { std::string availableID = "Available.ID"; std::string pfn = "sortof_apfn"; CompositeWithTrackingTestSetup setup; auto installedPackage = setup.MakeInstalled().WithPFN(pfn); auto availablePackage = setup.MakeAvailable().WithPFN(pfn).WithId(availableID).WithDefaultName(s_Everything_Query); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Installed->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(installedPackage, Criteria()); return result; }; setup.Available->Everything.Matches.emplace_back(availablePackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { if (request.Filters.empty()) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); } else { REQUIRE(request.Filters.size() == 1); RequireSearchRequestIncludes(request.Filters, PackageMatchField::Id, MatchType::CaseInsensitive, availableID); } SearchResult result; result.Matches.emplace_back(availablePackage, Criteria()); return result; }; auto manifestId = setup.Tracking->GetIndex().AddManifest(availablePackage); // Add test PackageVersionMetadata to be populated to InstalledVersion metadata setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::TrackingWriteTime, "100"); setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::UserIntentArchitecture, "X86"); setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::UserIntentLocale, "en-US"); setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::InstalledArchitecture, "X86"); setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::InstalledLocale, "en-US"); setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::PinnedState, "PinnedByManifest"); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); auto metadata = GetInstalledVersion(result.Matches[0].Package)->GetMetadata(); REQUIRE(metadata[Repository::PackageVersionMetadata::UserIntentArchitecture] == "X86"); REQUIRE(metadata[Repository::PackageVersionMetadata::UserIntentLocale] == "en-US"); REQUIRE(metadata[Repository::PackageVersionMetadata::InstalledArchitecture] == "X86"); REQUIRE(metadata[Repository::PackageVersionMetadata::InstalledLocale] == "en-US"); REQUIRE(metadata[Repository::PackageVersionMetadata::PinnedState] == "PinnedByManifest"); } TEST_CASE("CompositeSource_TrackingFound_AvailableNot", "[CompositeSource]") { std::string availableID = "Available.ID"; std::string pfn = "sortof_apfn"; CompositeWithTrackingTestSetup setup; auto installedPackage = setup.MakeInstalled().WithPFN(pfn); auto availablePackage = setup.MakeAvailable().WithPFN(pfn).WithId(availableID).WithDefaultName(s_Everything_Query); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Installed->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(installedPackage, Criteria()); return result; }; setup.Tracking->GetIndex().AddManifest(availablePackage); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetInstalled()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); REQUIRE(GetInstalledVersion(result.Matches[0].Package)->GetSource().GetIdentifier() == setup.Available->Details.Identifier); REQUIRE(result.Matches[0].Package->GetAvailable().empty()); } TEST_CASE("CompositeSource_TrackingFound_AvailablePath", "[CompositeSource]") { CompositeWithTrackingTestSetup setup; std::string availableID = "Available.ID"; std::string pfn = "sortof_apfn"; auto installedPackage = setup.MakeInstalled().WithPFN(pfn); auto availablePackage = setup.MakeAvailable().WithPFN(pfn).WithId(availableID).WithDefaultName(s_Everything_Query); setup.Installed->SearchFunction = [&](const SearchRequest& request) { RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); SearchResult result; result.Matches.emplace_back(installedPackage, Criteria(PackageMatchField::PackageFamilyName)); return result; }; setup.Available->Everything.Matches.emplace_back(availablePackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { REQUIRE(request.Filters.size() == 1); RequireSearchRequestIncludes(request.Filters, PackageMatchField::Id, MatchType::CaseInsensitive, availableID); SearchResult result; result.Matches.emplace_back(availablePackage, Criteria()); return result; }; setup.Tracking->GetIndex().AddManifest(availablePackage); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package); REQUIRE(GetInstalledVersion(result.Matches[0].Package)); REQUIRE(result.Matches[0].Package->GetInstalled()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); REQUIRE(GetInstalledVersion(result.Matches[0].Package)->GetSource().GetIdentifier() == setup.Available->Details.Identifier); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()); } TEST_CASE("CompositeSource_TrackingFound_NotInstalled", "[CompositeSource]") { std::string availableID = "Available.ID"; std::string pfn = "sortof_apfn"; CompositeWithTrackingTestSetup setup; auto installedPackage = setup.MakeInstalled().WithPFN(pfn); auto availablePackage = setup.MakeAvailable().WithPFN(pfn).WithId(availableID).WithDefaultName(s_Everything_Query); setup.Available->Everything.Matches.emplace_back(availablePackage, Criteria()); setup.Tracking->GetIndex().AddManifest(availablePackage); SearchResult result = setup.Search(); REQUIRE(result.Matches.empty()); } TEST_CASE("CompositeSource_NullInstalledVersion", "[CompositeSource]") { CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeAvailable(), Criteria()); // We are mostly testing to see if a null installed version causes an AV or not SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 0); } TEST_CASE("CompositeSource_NullAvailableVersion", "[CompositeSource]") { CompositeTestSetup setup{ CompositeSearchBehavior::AvailablePackages }; setup.Available->Everything.Matches.emplace_back(setup.MakeInstalled(), Criteria()); // We are mostly testing to see if a null available version causes an AV or not REQUIRE_THROWS_HR(setup.Search(), E_UNEXPECTED); } struct ExpectedResultForPinBehavior { ExpectedResultForPinBehavior(bool isUpdateAvailable, std::optional latestAvailableVersion) : IsUpdateAvailable(isUpdateAvailable), LatestAvailableVersion(latestAvailableVersion) {} ExpectedResultForPinBehavior() {} bool IsUpdateAvailable = false; std::optional LatestAvailableVersion; }; struct ExpectedPackageVersionKey : public PackageVersionKey { ExpectedPackageVersionKey(Utility::NormalizedString sourceId, Utility::NormalizedString version, Utility::NormalizedString channel, PinType pinType) : PackageVersionKey(sourceId, version, channel), PinnedState(pinType) {} PinType PinnedState; }; struct ExpectedResultsForPinning { std::map ResultsForPinBehavior; std::vector AvailableVersions; }; void RequireExpectedResultsWithPin(std::shared_ptr package, const ExpectedResultsForPinning& expectedResult, std::shared_ptr packageVersion = {}) { PinningData pinningData{ PinningData::Disposition::ReadOnly }; auto availableVersions = GetAvailableVersionsForInstalledVersion(package); if (!packageVersion) { packageVersion = GetInstalledVersion(package); } for (const auto& entry : expectedResult.ResultsForPinBehavior) { auto pinBehavior = entry.first; const auto& result = entry.second; auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, packageVersion); auto latestAvailable = evaluator.GetLatestAvailableVersionForPins(availableVersions); REQUIRE(evaluator.IsUpdate(latestAvailable) == result.IsUpdateAvailable); if (result.LatestAvailableVersion.has_value()) { REQUIRE(latestAvailable); REQUIRE(latestAvailable->GetManifest().Version == result.LatestAvailableVersion.value()); } else { REQUIRE(!latestAvailable); } } auto availableVersionKeys = availableVersions->GetVersionKeys(); REQUIRE(availableVersionKeys.size() == expectedResult.AvailableVersions.size()); for (size_t i = 0; i < availableVersionKeys.size(); ++i) { auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, packageVersion); auto availableVersion = availableVersions->GetVersion(expectedResult.AvailableVersions[i]); REQUIRE(availableVersion); REQUIRE(availableVersionKeys[i].SourceId == expectedResult.AvailableVersions[i].SourceId); REQUIRE(availableVersionKeys[i].Version == expectedResult.AvailableVersions[i].Version); REQUIRE(evaluator.EvaluatePinType(availableVersion) == expectedResult.AvailableVersions[i].PinnedState); } } TEST_CASE("CompositeSource_Pinning_AvailableVersionPinned", "[CompositeSource][PinFlow]") { // We use an installed package that has 3 available versions: v1.0.0, v1.0.1 and v1.1.0. // Installed is v1.0.1 // We then test the 4 possible pin states (unpinned, Pinned, Blocked, Gated) // with the 3 possible pin search behaviors (ignore, consider, include pinned) TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); TestUserSettings userSettings; CompositeTestSetup setup; auto installedPackage = setup.MakeInstalled().WithVersion("1.0.1"sv); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { auto manifest1 = MakeDefaultManifest("1.0.0"sv); auto manifest2 = MakeDefaultManifest("1.0.1"sv); auto manifest3 = MakeDefaultManifest("1.1.0"sv); auto package = TestCompositePackage::Make( std::vector{ manifest3, manifest2, manifest1 }, setup.Available); SearchResult result; result.Matches.emplace_back(package, Criteria()); return result; }; ExpectedResultsForPinning expectedResult; // The result when ignoring pins is always the same expectedResult.ResultsForPinBehavior[PinBehavior::IgnorePins] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "1.1.0" }; PinKey pinKey("Id", setup.Available->Details.Identifier); auto pinningIndex = PinningIndex::OpenOrCreateDefault(); REQUIRE(pinningIndex); SECTION("Unpinned") { // If there are no pins, the result should not change if we consider them expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins] = expectedResult.ResultsForPinBehavior[PinBehavior::IgnorePins]; expectedResult.ResultsForPinBehavior[PinBehavior::IncludePinned] = expectedResult.ResultsForPinBehavior[PinBehavior::IgnorePins]; expectedResult.AvailableVersions = { { "AvailableTestSource1", "1.1.0", "", Pinning::PinType::Unknown }, { "AvailableTestSource1", "1.0.1", "", Pinning::PinType::Unknown }, { "AvailableTestSource1", "1.0.0", "", Pinning::PinType::Unknown }, }; } SECTION("Pinned") { pinningIndex->AddPin(Pin::CreatePinningPin(PinKey{ pinKey })); // Pinning pins are ignored with --include-pinned expectedResult.ResultsForPinBehavior[PinBehavior::IncludePinned] = expectedResult.ResultsForPinBehavior[PinBehavior::IgnorePins]; expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins] = { /* IsUpdateAvailable */ false, /* LatestAvailableVersion */ {} }; expectedResult.AvailableVersions = { { "AvailableTestSource1", "1.1.0", "", Pinning::PinType::Pinning }, { "AvailableTestSource1", "1.0.1", "", Pinning::PinType::Pinning }, { "AvailableTestSource1", "1.0.0", "", Pinning::PinType::Pinning }, }; } SECTION("Blocked") { pinningIndex->AddPin(Pin::CreateBlockingPin(PinKey{ pinKey })); expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins] = { /* IsUpdateAvailable */ false, /* LatestAvailableVersion */ {} }; // Blocking pins are not affected by --include-pinned expectedResult.ResultsForPinBehavior[PinBehavior::IncludePinned] = expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins]; expectedResult.AvailableVersions = { { "AvailableTestSource1", "1.1.0", "", Pinning::PinType::Blocking }, { "AvailableTestSource1", "1.0.1", "", Pinning::PinType::Blocking }, { "AvailableTestSource1", "1.0.0", "", Pinning::PinType::Blocking }, }; } SECTION("Gated to 1.*") { pinningIndex->AddPin(Pin::CreateGatingPin(PinKey{ pinKey }, GatedVersion{ "1.*"sv })); expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "1.1.0" }; // Gating pins are not affected by --include-pinned expectedResult.ResultsForPinBehavior[PinBehavior::IncludePinned] = expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins]; expectedResult.AvailableVersions = { { "AvailableTestSource1", "1.1.0", "", Pinning::PinType::Unknown }, { "AvailableTestSource1", "1.0.1", "", Pinning::PinType::Unknown }, { "AvailableTestSource1", "1.0.0", "", Pinning::PinType::Unknown }, }; } SECTION("Gated to 1.0.*") { pinningIndex->AddPin(Pin::CreateGatingPin(PinKey{ pinKey }, GatedVersion{ "1.0.*"sv })); expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins] = { /* IsUpdateAvailable */ false, /* LatestAvailableVersion */ "1.0.1" }; // Gating pins are not affected by --include-pinned expectedResult.ResultsForPinBehavior[PinBehavior::IncludePinned] = expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins]; expectedResult.AvailableVersions = { { "AvailableTestSource1", "1.1.0", "", Pinning::PinType::Gating }, { "AvailableTestSource1", "1.0.1", "", Pinning::PinType::Unknown }, { "AvailableTestSource1", "1.0.0", "", Pinning::PinType::Unknown }, }; } SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); auto package = result.Matches[0].Package; REQUIRE(package); RequireExpectedResultsWithPin(package, expectedResult); } TEST_CASE("CompositeSource_Pinning_OneSourcePinned", "[CompositeSource][PinFlow]") { // We use an installed package that has 2 available sources. // If one of them is pinned, we should still get the updates from the other one. TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); TestUserSettings userSettings; CompositeTestSetup setup; auto installedPackage = setup.MakeInstalled().WithVersion("1.0"sv); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { auto package = TestCompositePackage::Make(std::vector{ MakeDefaultManifest("2.0"sv) }, setup.Available); SearchResult result; result.Matches.emplace_back(package, Criteria()); return result; }; std::shared_ptr secondAvailable = std::make_shared("SecondTestSource"); setup.Composite.AddAvailableSource(Source{ secondAvailable }); secondAvailable->SearchFunction = [&](const SearchRequest&) { auto package = TestCompositePackage::Make(std::vector{ MakeDefaultManifest("1.1"sv) }, secondAvailable); SearchResult result; result.Matches.emplace_back(package, Criteria()); return result; }; { PinKey pinKey("Id", setup.Available->Details.Identifier); auto pinningIndex = PinningIndex::OpenOrCreateDefault(); REQUIRE(pinningIndex); pinningIndex->AddPin(Pin::CreatePinningPin(PinKey{ pinKey })); } ExpectedResultsForPinning expectedResult; expectedResult.ResultsForPinBehavior[PinBehavior::IgnorePins] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "2.0" }; expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "1.1" }; expectedResult.ResultsForPinBehavior[PinBehavior::IncludePinned] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "2.0" }; expectedResult.AvailableVersions = { { "AvailableTestSource1", "2.0", "", Pinning::PinType::Pinning }, { "SecondTestSource", "1.1", "", Pinning::PinType::Unknown }, }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); auto package = result.Matches[0].Package; REQUIRE(package); RequireExpectedResultsWithPin(package, expectedResult); } TEST_CASE("CompositeSource_Pinning_OneSourceGated", "[CompositeSource][PinFlow]") { // We use an installed package that has 2 available sources. // If one of them has a gating pin, we should still get the updates from it TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); TestUserSettings userSettings; CompositeTestSetup setup; auto installedPackage = setup.MakeInstalled().WithVersion("1.0.1"sv); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { auto package = TestCompositePackage::Make( std::vector{ MakeDefaultManifest("2.0"sv), MakeDefaultManifest("1.2"sv), }, setup.Available); SearchResult result; result.Matches.emplace_back(package, Criteria()); return result; }; std::shared_ptr secondAvailable = std::make_shared("SecondTestSource"); setup.Composite.AddAvailableSource(Source{ secondAvailable }); secondAvailable->SearchFunction = [&](const SearchRequest&) { auto package = TestCompositePackage::Make(std::vector{ MakeDefaultManifest("1.1"sv) }, secondAvailable); SearchResult result; result.Matches.emplace_back(package, Criteria()); return result; }; { PinKey pinKey("Id", setup.Available->Details.Identifier); auto pinningIndex = PinningIndex::OpenOrCreateDefault(); REQUIRE(pinningIndex); pinningIndex->AddPin(Pin::CreateGatingPin(PinKey{ pinKey }, GatedVersion{ "1.*"sv })); } ExpectedResultsForPinning expectedResult; expectedResult.ResultsForPinBehavior[PinBehavior::IgnorePins] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "2.0" }; expectedResult.ResultsForPinBehavior[PinBehavior::ConsiderPins] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "1.2" }; expectedResult.ResultsForPinBehavior[PinBehavior::IncludePinned] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "1.2" }; expectedResult.AvailableVersions = { { "AvailableTestSource1", "2.0", "", Pinning::PinType::Gating }, { "AvailableTestSource1", "1.2", "", Pinning::PinType::Unknown }, { "SecondTestSource", "1.1", "", Pinning::PinType::Unknown }, }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); auto package = result.Matches[0].Package; REQUIRE(package); RequireExpectedResultsWithPin(package, expectedResult); } TEST_CASE("CompositeSource_Pinning_MultipleInstalled", "[CompositeSource][PinFlow]") { // Tests the case where multiple installed packages match to a single available package. // If one of the two installed packages is pinned, when searching we should get // two Composite packages, with only one of them pinned. TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); TestUserSettings userSettings; std::string packageId = "packageId"; std::string productCode1 = "product-code1"; std::string productCode2 = "product-code2"; CompositeTestSetup setup; // Installed packages differ in product code and version auto installedPackage1 = setup.MakeInstalled().WithId(productCode1).WithPC(productCode1).WithVersion("1.1"sv); auto installedPackage2 = setup.MakeInstalled().WithId(productCode2).WithPC(productCode2).WithVersion("1.2"sv); setup.Installed->SearchFunction = [&](const SearchRequest& request) { bool isSearchById = SearchRequestIncludes(request.Inclusions, PackageMatchField::Id, MatchType::Exact, packageId); SearchResult result; if (isSearchById || SearchRequestIncludes(request.Inclusions, PackageMatchField::ProductCode, MatchType::Exact, productCode1)) { result.Matches.emplace_back(installedPackage1, Criteria(request.Inclusions[0].Field)); } if (isSearchById || SearchRequestIncludes(request.Inclusions, PackageMatchField::ProductCode, MatchType::Exact, productCode2)) { result.Matches.emplace_back(installedPackage2, Criteria(request.Inclusions[0].Field)); } return result; }; // Available package has the same ID, no product code, and different version from both the installed packages; setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithId(packageId).WithVersion("2.0"sv), Criteria()); return result; }; // We will pin the first package only PinKey pinKey = PinKey::GetPinKeyForInstalled(productCode1); auto pinningIndex = PinningIndex::OpenOrCreateDefault(); REQUIRE(pinningIndex); // We will check the pinning status for both installed packages ExpectedResultsForPinning expectedResult1; ExpectedResultsForPinning expectedResult2; expectedResult1.ResultsForPinBehavior[PinBehavior::IgnorePins] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "2.0" }; // The second package is never pinned, so its result is always the same expectedResult2.ResultsForPinBehavior[PinBehavior::IgnorePins] = expectedResult2.ResultsForPinBehavior[PinBehavior::ConsiderPins] = expectedResult2.ResultsForPinBehavior[PinBehavior::IncludePinned] = { /* IsUpdateAvailable */ true, /* LatestAvailableVersion */ "2.0" }; expectedResult2.AvailableVersions = { { "AvailableTestSource1", "2.0", "", Pinning::PinType::Unknown }, }; SECTION("Unpinned") { // If there are no pins, the result should not change if we consider them expectedResult1.ResultsForPinBehavior[PinBehavior::ConsiderPins] = expectedResult1.ResultsForPinBehavior[PinBehavior::IncludePinned] = expectedResult1.ResultsForPinBehavior[PinBehavior::IgnorePins]; expectedResult1.AvailableVersions = { { "AvailableTestSource1", "2.0", "", Pinning::PinType::Unknown }, }; } SECTION("Pinned") { pinningIndex->AddPin(Pin::CreatePinningPin(PinKey{ pinKey })); // Pinning pins are ignored with --include-pinned expectedResult1.ResultsForPinBehavior[PinBehavior::IncludePinned] = expectedResult1.ResultsForPinBehavior[PinBehavior::IgnorePins]; expectedResult1.ResultsForPinBehavior[PinBehavior::ConsiderPins] = { /* IsUpdateAvailable */ false, /* LatestAvailableVersion */ {} }; expectedResult1.AvailableVersions = { { "AvailableTestSource1", "2.0", "", Pinning::PinType::Pinning }, }; } SECTION("Blocked") { pinningIndex->AddPin(Pin::CreateBlockingPin(PinKey{ pinKey })); expectedResult1.ResultsForPinBehavior[PinBehavior::ConsiderPins] = { /* IsUpdateAvailable */ false, /* LatestAvailableVersion */ {} }; // Blocking pins are not affected by --include-pinned expectedResult1.ResultsForPinBehavior[PinBehavior::IncludePinned] = expectedResult1.ResultsForPinBehavior[PinBehavior::ConsiderPins]; expectedResult1.AvailableVersions = { { "AvailableTestSource1", "2.0", "", Pinning::PinType::Blocking }, }; } SearchRequest searchRequest; searchRequest.Inclusions.emplace_back(PackageMatchField::Id, MatchType::Exact, packageId); SearchResult result = setup.Composite.Search(searchRequest); REQUIRE(result.Matches.size() == 1); auto installedPackage = result.Matches[0].Package->GetInstalled(); REQUIRE(installedPackage); auto installedVersions = installedPackage->GetVersionKeys(); REQUIRE(installedVersions.size() == 2); // Here we assume that the order we return the packages in the installed source // search is preserved. We'll need to change it if that stops being the case. auto packageVersion1 = installedPackage->GetVersion(installedVersions[1]); REQUIRE(packageVersion1); auto packageVersion2 = installedPackage->GetVersion(installedVersions[0]); REQUIRE(packageVersion2); RequireExpectedResultsWithPin(result.Matches[0].Package, expectedResult1, packageVersion1); RequireExpectedResultsWithPin(result.Matches[0].Package, expectedResult2, packageVersion2); } TEST_CASE("CompositeSource_CorrelateToInstalledContainsManifestData", "[CompositeSource]") { CompositeTestSetup setup; setup.Installed->SearchFunction = [&](const SearchRequest& request) { if (request.Purpose == SearchPurpose::CorrelationToInstalled) { bool expectedSearchFound = false; for (const auto& inclusion : request.Inclusions) { if (inclusion.Field == PackageMatchField::ProductCode && inclusion.Value == "hello") { expectedSearchFound = true; break; } } REQUIRE(expectedSearchFound); } SearchResult result; return result; }; setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithPC("hello"), Criteria()); return result; }; SearchRequest request; request.Query = RequestMatch(MatchType::Exact, "NotForEverything"); SearchResult result = setup.Composite.Search(request); } TEST_CASE("CompositeSource_Respects_FeatureFlag_ManifestMayContainAdditionalSystemReferenceStrings", "[CompositeSource]") { std::string id = "Special test ID"; std::string productCode1 = "product-code1"; CompositeTestSetup setup; bool productCodeSearched = false; setup.Installed->SearchFunction = [&](const SearchRequest& request) { for (const auto& inclusion : request.Inclusions) { if (inclusion.Field == PackageMatchField::ProductCode) { productCodeSearched = true; } } return SearchResult{}; }; setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(setup.MakeAvailable().WithId(id).WithPC(productCode1).HideSRS(), Criteria()); return result; }; SECTION("Feature false") { SearchRequest request; request.Query = RequestMatch(MatchType::Exact, "NotForEverything"); SearchResult result = setup.Composite.Search(request); REQUIRE(!productCodeSearched); } SECTION("Feature true") { setup.Available->QueryFeatureFlagFunction = [](SourceFeatureFlag flag) { return (flag == SourceFeatureFlag::ManifestMayContainAdditionalSystemReferenceStrings); }; SearchRequest request; request.Query = RequestMatch(MatchType::Exact, "NotForEverything"); SearchResult result = setup.Composite.Search(request); REQUIRE(productCodeSearched); } } TEST_CASE("CompositeSource_SxS_TwoVersions_NoAvailable", "[CompositeSource][SideBySide]") { std::string productCode1 = "PC1"; std::string productCode2 = "PC2"; CompositeTestSetup setup; auto availablePackage = setup.MakeAvailable(); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion("1.0").WithPC(productCode1), Criteria()); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion("2.0").WithPC(productCode2), Criteria()); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 2); } TEST_CASE("CompositeSource_SxS_TwoVersions_DifferentAvailable", "[CompositeSource][SideBySide]") { std::string productCode1 = "PC1"; std::string productCode2 = "PC2"; CompositeTestSetup setup; auto availablePackage1 = setup.MakeAvailable().ToPackage(); auto availablePackage2 = setup.MakeAvailable().ToPackage(); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion("1.0").WithPC(productCode1), Criteria()); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion("2.0").WithPC(productCode2), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest& request) { SearchResult result; std::string productCode; for (const auto& item : request.Inclusions) { if (item.Field == PackageMatchField::ProductCode) { productCode = item.Value; break; } } if (productCode == productCode1) { result.Matches.emplace_back(availablePackage1, Criteria()); } else if (productCode == productCode1) { result.Matches.emplace_back(availablePackage2, Criteria()); } return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 2); } TEST_CASE("CompositeSource_SxS_TwoVersions_SameAvailable", "[CompositeSource][SideBySide]") { std::string version1 = "1.0"; std::string version2 = "2.0"; std::string productCode1 = "PC1"; std::string productCode2 = "PC2"; CompositeTestSetup setup; auto availablePackage = setup.MakeAvailable().ToPackage(); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion(version1).WithPC(productCode1), Criteria()); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion(version2).WithPC(productCode2), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(availablePackage, Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); auto package = result.Matches[0].Package; REQUIRE(package); auto installedPackage = package->GetInstalled(); REQUIRE(installedPackage); auto installedVersions = installedPackage->GetVersionKeys(); REQUIRE(installedVersions.size() == 2); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version1; })); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version2; })); auto availablePackages = package->GetAvailable(); REQUIRE(availablePackages.size() == 1); REQUIRE(availablePackages[0]->IsSame(availablePackage->Available[0].get())); } TEST_CASE("CompositeSource_SxS_ThreeVersions_SameAvailable", "[CompositeSource][SideBySide]") { std::string version1 = "1.0"; std::string version2 = "2.0"; std::string version3 = "3.0"; std::string productCode1 = "PC1"; std::string productCode2 = "PC2"; std::string productCode3 = "PC3"; CompositeTestSetup setup; auto availablePackage = setup.MakeAvailable().ToPackage(); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion(version1).WithPC(productCode1), Criteria()); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion(version2).WithPC(productCode2), Criteria()); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion(version3).WithPC(productCode3), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(availablePackage, Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); auto package = result.Matches[0].Package; REQUIRE(package); auto installedPackage = package->GetInstalled(); REQUIRE(installedPackage); auto installedVersions = installedPackage->GetVersionKeys(); REQUIRE(installedVersions.size() == 3); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version1; })); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version2; })); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version3; })); auto availablePackages = package->GetAvailable(); REQUIRE(availablePackages.size() == 1); REQUIRE(availablePackages[0]->IsSame(availablePackage->Available[0].get())); } TEST_CASE("CompositeSource_SxS_TwoVersions_SameAvailable_Tracking", "[CompositeSource][SideBySide]") { std::string version1 = "1.0"; std::string version2 = "2.0"; std::string productCode1 = "PC1"; std::string productCode2 = "PC2"; CompositeWithTrackingTestSetup setup; auto installedPackage1 = setup.MakeInstalled().WithVersion(version1).WithPC(productCode1); auto availablePackage = setup.MakeAvailable().ToPackage(); setup.Installed->Everything.Matches.emplace_back(installedPackage1, Criteria()); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithVersion(version2).WithPC(productCode2), Criteria()); setup.Tracking->GetIndex().AddManifest(installedPackage1); setup.Available->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(availablePackage, Criteria()); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); auto package = result.Matches[0].Package; REQUIRE(package); auto installedPackage = package->GetInstalled(); REQUIRE(installedPackage); auto installedVersions = installedPackage->GetVersionKeys(); REQUIRE(installedVersions.size() == 2); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version1; })); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version2; })); auto availablePackages = package->GetAvailable(); REQUIRE(availablePackages.size() == 1); REQUIRE(availablePackages[0]->IsSame(availablePackage->Available[0].get())); } TEST_CASE("CompositeSource_SxS_Available_TwoVersions_SameAvailable", "[CompositeSource][SideBySide]") { std::string version1 = "1.0"; std::string version2 = "2.0"; std::string productCode1 = "PC1"; std::string productCode2 = "PC2"; CompositeTestSetup setup; auto availablePackage = setup.MakeAvailable().ToPackage(); setup.Installed->SearchFunction = [&](const SearchRequest&) { SearchResult result; result.Matches.emplace_back(setup.MakeInstalled().WithVersion(version1).WithPC(productCode1), Criteria()); result.Matches.emplace_back(setup.MakeInstalled().WithVersion(version2).WithPC(productCode2), Criteria()); return result; }; setup.Available->Everything.Matches.emplace_back(availablePackage, Criteria()); SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); auto package = result.Matches[0].Package; REQUIRE(package); auto installedPackage = package->GetInstalled(); REQUIRE(installedPackage); auto installedVersions = installedPackage->GetVersionKeys(); REQUIRE(installedVersions.size() == 2); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version1; })); REQUIRE(std::any_of(installedVersions.begin(), installedVersions.end(), [&](const PackageVersionKey& key) { return key.Version == version2; })); auto availablePackages = package->GetAvailable(); REQUIRE(availablePackages.size() == 1); REQUIRE(availablePackages[0]->IsSame(availablePackage->Available[0].get())); } TEST_CASE("CompositeSource_MappedVersions_ProperSorting", "[CompositeSource]") { std::string installedID = "Installed.Id"; std::string availableID = "Available.Id"; auto type = Manifest::InstallerTypeEnum::Exe; std::string pfn = "MY_PFN"; std::string version1 = "1000.0"; std::string version2 = "2000.0"; std::string versionMapped1 = "1.0"; std::string versionMapped2 = "2.0"; CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithId(installedID).WithPFN(pfn).WithVersion(version1).WithMetadata(PackageVersionMetadata::InstalledType, "exe"), Criteria()); setup.Installed->Everything.Matches.emplace_back(setup.MakeInstalled().WithId(installedID).WithPFN(pfn).WithVersion(version2).WithMetadata(PackageVersionMetadata::InstalledType, "exe"), Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { auto package = setup.MakeAvailable(); package.WithId(availableID).WithType(type).WithPFN(pfn).WithVersion(versionMapped1).WithDisplayVersion(version1); package.MakeManifest().WithId(availableID).WithType(type).WithPFN(pfn).WithVersion(versionMapped2).WithDisplayVersion(version2); SearchResult result; result.Matches.emplace_back(package, Criteria()); return result; }; SearchResult result = setup.Search(true); REQUIRE(result.Matches.size() == 1); auto package = result.Matches[0].Package; REQUIRE(package); auto installedPackage = package->GetInstalled(); REQUIRE(installedPackage); auto installedVersions = installedPackage->GetVersionKeys(); REQUIRE(installedVersions.size() == 2); REQUIRE(installedVersions[0].Version == versionMapped2); REQUIRE(installedVersions[1].Version == versionMapped1); } ================================================ FILE: src/AppInstallerCLITests/ContextOrchestrator.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Manifest; static constexpr DWORD c_DefaultWaitInMs = 5000; struct TestCOMContext : public COMContext { TestCOMContext() = default; std::function DownloadCallback; void InvokeDownload() { if (DownloadCallback) { DownloadCallback(); } } std::function OperationCallback; void InvokeOperation() { if (OperationCallback) { OperationCallback(); } } }; struct TestDownloadCommand : public Command { TestDownloadCommand() : Command("download", "tests") {} Resource::LocString ShortDescription() const override { return {}; } Resource::LocString LongDescription() const override { return {}; } void ValidateArguments(Execution::Args&) const override {} void Execute(Context& context) const override { static_cast(&context)->InvokeDownload(); } }; struct TestOperationCommand : public Command { TestOperationCommand() : Command("operation", "tests") {} Resource::LocString ShortDescription() const override { return {}; } Resource::LocString LongDescription() const override { return {}; } void ValidateArguments(Execution::Args&) const override {} void Execute(Context& context) const override { static_cast(&context)->InvokeOperation(); } }; struct TestQueueItem { TestCOMContext* Context = nullptr; std::shared_ptr QueueItem; }; TestQueueItem CreateTestItem(std::optional packageName = std::nullopt) { TestQueueItem result; std::unique_ptr context = std::make_unique(); // Forcibly initialize the thread globals objects context->GetThreadGlobals().SetForCurrentThread(); TestDataFile testManifest("Manifest-Good.yaml"); auto manifest = YamlParser::CreateFromPath(testManifest); if (packageName) { manifest.Id = packageName.value(); } context->Add(std::move(manifest)); result.Context = context.get(); // Marking it an uninstall removes the extra work adding the items to the installing index result.QueueItem = std::make_shared(OrchestratorQueueItemId(AppInstaller::Utility::ConvertToUTF16(packageName.value_or("package")), L"source"), std::move(context), PackageOperationType::Uninstall); result.QueueItem->AddCommand(std::make_unique()); result.QueueItem->AddCommand(std::make_unique()); return result; } // Runs an item through the orchestrator to ensure the basic functionality TEST_CASE("ContextOrchestrator_UnitTestExecution", "[context_orchestrator]") { ContextOrchestrator orchestrator; auto testItem = CreateTestItem(); wil::slim_event_manual_reset operationEvent; testItem.Context->OperationCallback = [&]() { operationEvent.SetEvent(); }; orchestrator.EnqueueAndRunItem(testItem.QueueItem); REQUIRE(operationEvent.wait(c_DefaultWaitInMs)); REQUIRE(testItem.QueueItem->GetCompletedEvent().wait(c_DefaultWaitInMs)); REQUIRE(orchestrator.WaitForRunningItems(c_DefaultWaitInMs)); } TEST_CASE("ContextOrchestrator_Disabled_NewEnqueue", "[context_orchestrator]") { ContextOrchestrator orchestrator; auto testItem = CreateTestItem(); auto reason = AppInstaller::CancelReason::AppShutdown; orchestrator.Disable(reason); REQUIRE_THROWS_HR(orchestrator.EnqueueAndRunItem(testItem.QueueItem), AppInstaller::ToHRESULT(reason)); } TEST_CASE("ContextOrchestrator_Disabled_QueueTransition", "[context_orchestrator]") { ContextOrchestrator orchestrator; auto testItem = CreateTestItem(); wil::slim_event_manual_reset downloadEvent; testItem.Context->DownloadCallback = [&]() { downloadEvent.wait(); }; orchestrator.EnqueueAndRunItem(testItem.QueueItem); auto reason = AppInstaller::CancelReason::AppShutdown; orchestrator.Disable(reason); downloadEvent.SetEvent(); REQUIRE(testItem.QueueItem->GetCompletedEvent().wait(c_DefaultWaitInMs)); REQUIRE(testItem.Context->IsTerminated()); // Context translates our shutdown HRs to E_ABORT REQUIRE(E_ABORT == testItem.Context->GetTerminationHR()); } // While item in { Queued, Running } in both queues, cancel everything TEST_CASE("ContextOrchestrator_CancelAllItems", "[context_orchestrator]") { // Limit to one thread for downloads so we can get a queued item ContextOrchestrator orchestrator{ 1 }; auto downloadQueued = CreateTestItem("downloadQueued"); auto downloadRunning = CreateTestItem("downloadRunning"); wil::slim_event_manual_reset downloadBegunEvent; wil::slim_event_manual_reset downloadWaitingEvent; downloadRunning.Context->DownloadCallback = [&]() { downloadBegunEvent.SetEvent(); downloadWaitingEvent.wait(); }; auto operationQueued = CreateTestItem("operationQueued"); auto operationRunning = CreateTestItem("operationRunning"); wil::slim_event_manual_reset operationBegunEvent; wil::slim_event_manual_reset operationWaitingEvent; operationRunning.Context->OperationCallback = [&]() { operationBegunEvent.SetEvent(); operationWaitingEvent.wait(); }; orchestrator.EnqueueAndRunItem(operationRunning.QueueItem); orchestrator.EnqueueAndRunItem(operationQueued.QueueItem); orchestrator.EnqueueAndRunItem(downloadRunning.QueueItem); orchestrator.EnqueueAndRunItem(downloadQueued.QueueItem); operationBegunEvent.wait(c_DefaultWaitInMs); downloadBegunEvent.wait(c_DefaultWaitInMs); INFO("Pre-shutdown state: \n" << orchestrator.GetStatusString()); auto reason = AppInstaller::CancelReason::AppShutdown; orchestrator.Disable(reason); orchestrator.CancelQueuedItems(reason); operationWaitingEvent.SetEvent(); downloadWaitingEvent.SetEvent(); if (!orchestrator.WaitForRunningItems(c_DefaultWaitInMs)) { INFO("Post-wait state: \n" << orchestrator.GetStatusString()); FAIL("Timed out waiting for orchestrator to empty"); } auto checkQueueItem = [](TestQueueItem& item) { REQUIRE(item.QueueItem->GetCompletedEvent().wait(0)); REQUIRE(item.Context->IsTerminated()); REQUIRE(E_ABORT == item.Context->GetTerminationHR()); }; checkQueueItem(downloadQueued); checkQueueItem(downloadRunning); checkQueueItem(operationQueued); checkQueueItem(operationRunning); } ================================================ FILE: src/AppInstallerCLITests/Correlation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSource.h" #include #include #include #include using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Correlation; using namespace AppInstaller::Utility; using namespace TestCommon; // Data for defining a test case struct TestCase { // Actual app data std::string AppName; std::string AppPublisher; // Data in ARP std::string ARPName; std::string ARPPublisher; bool IsMatch; }; // Definition of a collection of test cases that we evaluate // together to get a single aggregate result struct DataSet { // Details about the apps we are trying to correlate std::vector TestCases; // Additional ARP entries to use as "noise" for the correlation std::vector ARPNoise; // Thresholds for considering a run of an heuristic against // this data set "good". // Values are ratios to the total number of test cases double RequiredTrueMatchRatio; double RequiredTrueMismatchRatio; double RequiredFalseMatchRatio; double RequiredFalseMismatchRatio; }; // Aggregate result of running an heuristic against a data set. struct ResultSummary { size_t TrueMatches; size_t TrueMismatches; size_t FalseMatches; size_t FalseMismatches; std::chrono::milliseconds TotalTime; size_t TotalCases() const { return TrueMatches + TrueMismatches + FalseMatches + FalseMismatches; } auto AverageMatchingTime() const { return TotalTime / TotalCases(); } }; Manifest GetManifestFromTestCase(const TestCase& testCase) { Manifest manifest; manifest.DefaultLocalization.Add(testCase.AppName); manifest.DefaultLocalization.Add(testCase.AppPublisher); manifest.Localizations.push_back(manifest.DefaultLocalization); return manifest; } ARPEntry GetARPEntryFromTestCase(const TestCase& testCase, bool isNew) { Manifest arpManifest; arpManifest.DefaultLocalization.Add(testCase.ARPName); arpManifest.DefaultLocalization.Add(testCase.ARPPublisher); arpManifest.Localizations.push_back(arpManifest.DefaultLocalization); return ARPEntry{ TestPackage::Make(arpManifest, TestPackage::MetadataMap{}), isNew }; } ARPEntry GetExistingARPEntryFromTestCase(const TestCase& testCase) { return GetARPEntryFromTestCase(testCase, /* isNew */ false); } void ReportMatch(std::string_view label, std::string_view appName, std::string_view appPublisher, std::string_view arpName, std::string_view arpPublisher) { WARN(label << '\n' << "\tApp name = " << appName << '\n' << "\tApp publisher = " << appPublisher << '\n' << "\tARP name = " << arpName << '\n' << "\tARP publisher = " << arpPublisher); } ResultSummary EvaluateDataSetWithHeuristic(const DataSet& dataSet, IARPMatchConfidenceAlgorithm& correlationAlgorithm, bool reportErrors = false) { ResultSummary result{}; auto startTime = std::chrono::steady_clock::now(); // Each entry under test will be pushed at the end of this // and removed at the end. auto arpEntries = dataSet.ARPNoise; for (const auto& testCase : dataSet.TestCases) { arpEntries.push_back(GetARPEntryFromTestCase(testCase, /* isNew */ true)); ARPHeuristicsCorrelationResult correlationResult = FindARPEntryForNewlyInstalledPackageWithHeuristics(GetManifestFromTestCase(testCase), arpEntries, correlationAlgorithm); auto match = correlationResult.Package; arpEntries.pop_back(); if (match) { auto matchName = match->GetProperty(PackageVersionProperty::Name); auto matchPublisher = match->GetProperty(PackageVersionProperty::Publisher); // The strings get normalized when added to the manifest, so we have // to normalize for the comparison. if (matchName == NormalizedString(testCase.ARPName) && matchPublisher == NormalizedString(testCase.ARPPublisher)) { ++result.TrueMatches; } else { ++result.FalseMatches; if (reportErrors) { ReportMatch("False match", testCase.AppName, testCase.AppPublisher, matchName, matchPublisher); } } } else { if (testCase.IsMatch) { ++result.FalseMismatches; if (reportErrors) { ReportMatch("False mismatch", testCase.AppName, testCase.AppPublisher, testCase.ARPName, testCase.ARPPublisher); } } else { ++result.TrueMismatches; } } } auto endTime = std::chrono::steady_clock::now(); result.TotalTime = std::chrono::duration_cast(endTime - startTime); return result; } void ReportResults(ResultSummary results) { // This uses WARN to report as that is always shown regardless of the test result. // We may want to re-consider reporting in some other way WARN("Total cases: " << results.TotalCases() << '\n' << "True matches: " << results.TrueMatches << '\n' << "False matches: " << results.FalseMatches << '\n' << "True mismatches: " << results.TrueMismatches << '\n' << "False mismatches: " << results.FalseMismatches << '\n' << "Total matching time: " << results.TotalTime.count() << "ms\n" << "Average matching time: " << results.AverageMatchingTime().count() << "ms"); } void ReportAndEvaluateResults(ResultSummary results, const DataSet& dataSet) { ReportResults(results); // Required True ratio is a lower limit. The more results we get right, the better. // Required False ratio is an upper limit. The fewer results we get wrong, the better. REQUIRE(results.TrueMatches >= results.TotalCases() * dataSet.RequiredTrueMatchRatio); REQUIRE(results.TrueMismatches >= results.TotalCases() * dataSet.RequiredTrueMismatchRatio); REQUIRE(results.FalseMatches <= results.TotalCases() * dataSet.RequiredFalseMatchRatio); REQUIRE(results.FalseMismatches <= results.TotalCases()* dataSet.RequiredFalseMismatchRatio); } // TODO: Define multiple data sets // - Data set with many apps. // - Data set with popular apps. The match requirements should be higher // - Data set(s) in other languages. // - Data set where not everything has a match std::vector LoadTestData() { // Creates test cases from the test data file. // The format of the file is one case per line, each with pipe (|) separated values. // Each row contains: AppId, AppName, AppPublisher, ARPDisplayName, ARPDisplayVersion, ARPPublisherName, ARPProductCode // TODO: Add more test cases; particularly for non-matches std::ifstream testDataStream(TestCommon::TestDataFile("InputARPData.txt").GetPath()); REQUIRE(testDataStream); std::vector testCases; std::string line; while (std::getline(testDataStream, line)) { std::stringstream ss{ line }; TestCase testCase; std::string appId; std::string arpDisplayVersion; std::string arpProductCode; std::getline(ss, appId, '|'); std::getline(ss, testCase.AppName, '|'); std::getline(ss, testCase.AppPublisher, '|'); std::getline(ss, testCase.ARPName, '|'); std::getline(ss, arpDisplayVersion, '|'); std::getline(ss, testCase.ARPPublisher, '|'); std::getline(ss, arpProductCode, '|'); testCase.IsMatch = true; testCases.push_back(std::move(testCase)); } return testCases; } DataSet GetDataSet_NoNoise() { DataSet dataSet; dataSet.TestCases = LoadTestData(); // Arbitrary values. We should refine them as the algorithm gets better. dataSet.RequiredTrueMatchRatio = 0.81; dataSet.RequiredFalseMatchRatio = 0; dataSet.RequiredTrueMismatchRatio = 0; // There are no expected mismatches in this data set dataSet.RequiredFalseMismatchRatio = 0.25; return dataSet; } DataSet GetDataSet_WithNoise() { DataSet dataSet; auto baseTestCases = LoadTestData(); std::transform(baseTestCases.begin(), baseTestCases.end(), std::back_inserter(dataSet.ARPNoise), GetExistingARPEntryFromTestCase); dataSet.TestCases = std::move(baseTestCases); // Arbitrary values. We should refine them as the algorithm gets better. dataSet.RequiredTrueMatchRatio = 0.81; dataSet.RequiredFalseMatchRatio = 0; // This should always stay at 0 dataSet.RequiredTrueMismatchRatio = 0; // There are no expected mismatches in this data set dataSet.RequiredFalseMismatchRatio = 0.25; return dataSet; } // Hide this test as it takes too long to run. // It is useful for comparing multiple algorithms, but for // regular testing we need only check that the chosen algorithm // performs well. TEMPLATE_TEST_CASE("Correlation_MeasureAlgorithmPerformance", "[correlation][.]", EmptyMatchConfidenceAlgorithm, WordsEditDistanceMatchConfidenceAlgorithm) { // Each section loads a different data set, // and then they are all handled the same DataSet dataSet; SECTION("No ARP noise") { dataSet = GetDataSet_NoNoise(); } SECTION("With ARP noise") { dataSet = GetDataSet_WithNoise(); } TestType measure; auto results = EvaluateDataSetWithHeuristic(dataSet, measure); ReportResults(results); } TEST_CASE("Correlation_ChosenHeuristicIsGood", "[correlation]") { // Each section loads a different data set, // and then they are all handled the same DataSet dataSet; SECTION("No ARP noise") { dataSet = GetDataSet_NoNoise(); } SECTION("With ARP noise") { dataSet = GetDataSet_WithNoise(); } // Use only the measure we ultimately pick auto& algorithm = IARPMatchConfidenceAlgorithm::Instance(); auto results = EvaluateDataSetWithHeuristic(dataSet, algorithm, /* reportErrors */ true); ReportAndEvaluateResults(results, dataSet); } ================================================ FILE: src/AppInstallerCLITests/CustomHeader.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestHooks.h" #include "TestSettings.h" #include "TestSource.h" #include "TestRestRequestHandler.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Http; using namespace AppInstaller::Settings; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_0; namespace { utility::string_t CustomHeaderName = L"Windows-Package-Manager"; constexpr std::string_view s_EmptySources = R"( Sources: )"sv; utility::string_t sampleSearchResponse = _XPLATSTR( R"delimiter({ "Data" : [ { "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": [ { "PackageVersion": "1.0.0" }] }] })delimiter"); } // In RestClient.cpp tests extern RestClient CreateRestClient( const std::string& restApi, const std::optional& customHeader, std::string_view caller, const Http::HttpClientHelper& helper, const Authentication::AuthenticationArguments& authArgs = {}); TEST_CASE("RestClient_CustomHeader", "[RestSource][CustomHeader]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.0.0", "2.0.0"] }})delimiter"); std::optional customHeader = "Testing custom header"; auto header = std::make_pair<>(CustomHeaderName, JSON::GetUtilityString(customHeader.value())); HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sample, header) }; RestClient client = CreateRestClient(utility::conversions::to_utf8string("https://restsource.com/api"), customHeader, {}, helper); REQUIRE(client.GetSourceIdentifier() == "Source123"); } TEST_CASE("RestSourceSearch_CustomHeader", "[RestSource][CustomHeader]") { utility::string_t customHeader = L"Testing custom header"; auto header = std::make_pair<>(CustomHeaderName, customHeader); HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) }; std::unordered_map headers; headers.emplace(CustomHeaderName, customHeader); V1_1::Interface v1_1{ "https://restsource.com/api", std::move(helper) , {}, headers}; Schema::IRestClient::SearchResult searchResponse = v1_1.Search({}); REQUIRE(searchResponse.Matches.size() == 1); Schema::IRestClient::Package package = searchResponse.Matches.at(0); } TEST_CASE("RestSourceSearch_WhitespaceCustomHeader", "[RestSource][CustomHeader]") { utility::string_t customHeader = L" "; auto header = std::make_pair<>(CustomHeaderName, customHeader); HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) }; std::unordered_map headers; headers.emplace(CustomHeaderName, customHeader); V1_1::Interface v1_1{ "https://restsource.com/api", std::move(helper), {}, headers }; Schema::IRestClient::SearchResult searchResponse = v1_1.Search({}); REQUIRE(searchResponse.Matches.size() == 1); } TEST_CASE("RestSourceSearch_NoCustomHeader", "[RestSource][CustomHeader]") { utility::string_t customHeader = L" "; auto header = std::make_pair<>(CustomHeaderName, customHeader); HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) }; std::unordered_map headers; headers.emplace(CustomHeaderName, customHeader); V1_1::Interface v1_1{ "https://restsource.com/api", std::move(helper), {}, {} }; REQUIRE_THROWS_HR(v1_1.Search({}), APPINSTALLER_CLI_ERROR_RESTAPI_INTERNAL_ERROR); } TEST_CASE("RestSourceSearch_CustomHeaderExceedingSize", "[RestSource][CustomHeader]") { std::string customHeader = "This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. This is a custom header that is longer than 1024 characters. "; auto header = std::make_pair<>(CustomHeaderName, JSON::GetUtilityString(customHeader)); HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sampleSearchResponse, header) }; REQUIRE_THROWS_HR(CreateRestClient(utility::conversions::to_utf8string("https://restsource.com/api"), customHeader, {}, helper), APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH); } TEST_CASE("RestClient_CustomUserAgentHeader", "[RestSource][CustomHeader]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.0.0", "2.0.0"] }})delimiter"); std::string testCaller = "TestCaller"; auto header = std::make_pair<>(web::http::header_names::user_agent, JSON::GetUtilityString(Runtime::GetUserAgent(testCaller))); HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sample, header) }; RestClient client = CreateRestClient(utility::conversions::to_utf8string("https://restsource.com/api"), {}, testCaller, helper); REQUIRE(client.GetSourceIdentifier() == "Source123"); } TEST_CASE("RestClient_DefaultUserAgentHeader", "[RestSource][CustomHeader]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.0.0", "2.0.0"] }})delimiter"); auto header = std::make_pair<>(web::http::header_names::user_agent, JSON::GetUtilityString(Runtime::GetDefaultUserAgent())); HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sample, header) }; RestClient client = CreateRestClient(utility::conversions::to_utf8string("https://restsource.com/api"), {}, {}, helper); REQUIRE(client.GetSourceIdentifier() == "Source123"); } ================================================ FILE: src/AppInstallerCLITests/DateTime.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace AppInstaller::Utility; using namespace TestCommon; using namespace std::chrono; namespace Catch { template<> struct StringMaker { static std::string convert(const std::chrono::system_clock::time_point& value) { std::ostringstream stream; OutputTimePoint(stream, value); return std::move(stream).str(); } }; } void VerifyGetTimePointFromVersion(std::string_view version, int year, int month, int day, int hour, int minute) { system_clock::time_point result = GetTimePointFromVersion(UInt64Version{ std::string{ version } }); tm time{}; auto tt = system_clock::to_time_t(result); _gmtime64_s(&time, &tt); REQUIRE(year == time.tm_year + 1900); REQUIRE(month == time.tm_mon + 1); REQUIRE(day == time.tm_mday); REQUIRE(hour == time.tm_hour); REQUIRE(minute == time.tm_min); } std::string StringFromTimePoint(system_clock::time_point input) { tm time{}; auto tt = system_clock::to_time_t(input); _gmtime64_s(&time, &tt); std::ostringstream stream; stream << time.tm_year + 1900 << '.' << ((time.tm_mon + 1) * 100) + time.tm_mday << '.' << ((time.tm_hour + 1) * 100) + time.tm_min; return std::move(stream).str(); } TEST_CASE("GetTimePointFromVersion", "[datetime]") { // Years out of range REQUIRE(GetTimePointFromVersion(UInt64Version{ "1969.1231.2459.0" }) == system_clock::time_point::min()); REQUIRE(GetTimePointFromVersion(UInt64Version{ "3001.101.100.0" }) == system_clock::time_point::min()); // Months out of range REQUIRE(GetTimePointFromVersion(UInt64Version{ "2023.1.100.0" }) == system_clock::time_point::min()); REQUIRE(GetTimePointFromVersion(UInt64Version{ "2023.1301.100.0" }) == system_clock::time_point::min()); // Days out of range REQUIRE(GetTimePointFromVersion(UInt64Version{ "2023.100.100.0" }) == system_clock::time_point::min()); REQUIRE(GetTimePointFromVersion(UInt64Version{ "2023.132.100.0" }) == system_clock::time_point::min()); // Hours out of range REQUIRE(GetTimePointFromVersion(UInt64Version{ "2023.101.0.0" }) == system_clock::time_point::min()); REQUIRE(GetTimePointFromVersion(UInt64Version{ "2023.101.2500.0" }) == system_clock::time_point::min()); // Minutes out of range REQUIRE(GetTimePointFromVersion(UInt64Version{ "2023.101.160.0" }) == system_clock::time_point::min()); // In range baseline VerifyGetTimePointFromVersion("2023.101.100.0", 2023, 1, 1, 0, 0); // Time for presents! VerifyGetTimePointFromVersion("2023.1225.814.0", 2023, 12, 25, 7, 14); // Epoch time REQUIRE(GetTimePointFromVersion(UInt64Version{ "1970.101.100.0" }) == system_clock::time_point{}); // Round trip now system_clock::time_point now = system_clock::now(); REQUIRE(GetTimePointFromVersion(UInt64Version{ StringFromTimePoint(now) }) == time_point_cast(now)); } TEST_CASE("ShortFileTime", "[datetime]") { auto shortTime = GetCurrentTimeForFilename(true); auto longTime = GetCurrentTimeForFilename(false); INFO(shortTime); INFO(longTime); REQUIRE(shortTime.length() < longTime.length()); } ================================================ FILE: src/AppInstallerCLITests/Dependencies.cpp ================================================ #include "pch.h" #include "TestCommon.h" #include "TestSource.h" #include "DependenciesTestSource.h" #include #include #include #include #include #include #include #include #include #include #include using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Management::Deployment; using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Logging; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; TEST_CASE("DependencyGraph_BFirst", "[dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::vector installationOrder; const auto& manifest = CreateFakeManifestWithDependencies("NeedsToInstallBFirst"); const auto& installers = manifest.Installers; const Dependency& rootAsDependency = Dependency(DependencyType::Package, manifest.Id); DependencyList rootDependencies; std::for_each(installers.begin(), installers.end(), [&](ManifestInstaller installer) { rootDependencies.Add(installer.Dependencies); }); DependencyGraph graph(rootAsDependency, rootDependencies, [&](Dependency) { DependencyList dependencyList; auto dependencyManifest = CreateFakeManifestWithDependencies(manifest.Id); for (auto installer : dependencyManifest.Installers) { dependencyList.Add(installer.Dependencies); } return dependencyList; }); graph.BuildGraph(); installationOrder = graph.GetInstallationOrder(); REQUIRE(installationOrder.size() == 3); REQUIRE(installationOrder.at(0).Id() == "C"); REQUIRE(installationOrder.at(1).Id() == "B"); REQUIRE(installationOrder.at(2).Id() == "NeedsToInstallBFirst"); } TEST_CASE("DependencyGraph_InStackNoLoop", "[dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::vector installationOrder; const auto& manifest = CreateFakeManifestWithDependencies("DependencyAlreadyInStackButNoLoop"); const auto& installers = manifest.Installers; const Dependency& rootAsDependency = Dependency(DependencyType::Package, manifest.Id); DependencyList rootDependencies; std::for_each(installers.begin(), installers.end(), [&](ManifestInstaller installer) { rootDependencies.Add(installer.Dependencies); }); DependencyGraph graph(rootAsDependency, rootDependencies, [&](Dependency) { DependencyList dependencyList; auto dependencyManifest = CreateFakeManifestWithDependencies(manifest.Id); for (auto installer : dependencyManifest.Installers) { dependencyList.Add(installer.Dependencies); } return dependencyList; }); graph.BuildGraph(); installationOrder = graph.GetInstallationOrder(); REQUIRE(installationOrder.size() == 3); REQUIRE(installationOrder.at(0).Id() == "F"); REQUIRE(installationOrder.at(1).Id() == "C"); REQUIRE(installationOrder.at(2).Id() == "DependencyAlreadyInStackButNoLoop"); } TEST_CASE("DependencyGraph_EasyToSeeLoop", "[dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::vector installationOrder; const auto& manifest = CreateFakeManifestWithDependencies("EasyToSeeLoop"); const auto& installers = manifest.Installers; const Dependency& rootAsDependency = Dependency(DependencyType::Package, manifest.Id); DependencyList rootDependencies; std::for_each(installers.begin(), installers.end(), [&](ManifestInstaller installer) { rootDependencies.Add(installer.Dependencies); }); DependencyGraph graph(rootAsDependency, rootDependencies, [&](Dependency) { DependencyList dependencyList; auto dependencyManifest = CreateFakeManifestWithDependencies(manifest.Id); for (auto installer : dependencyManifest.Installers) { dependencyList.Add(installer.Dependencies); } return dependencyList; }); graph.BuildGraph(); installationOrder = graph.GetInstallationOrder(); bool hasLoop = graph.HasLoop(); REQUIRE(hasLoop); REQUIRE(installationOrder.size() == 2); REQUIRE(installationOrder.at(0).Id() == "D"); REQUIRE(installationOrder.at(1).Id() == "EasyToSeeLoop"); } TEST_CASE("DependencyNodeProcessor_SkipInstalled", "[dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; Context context{ installOutput, std::cin }; Manifest manifest = CreateFakeManifestWithDependencies("installed1"); context.Add(Source{ std::make_shared() }); DependencyNodeProcessor nodeProcessor(context); Dependency rootAsDependency(DependencyType::Package, manifest.Id); DependencyNodeProcessorResult result = nodeProcessor.EvaluateDependencies(rootAsDependency); REQUIRE(result == DependencyNodeProcessorResult::Skipped); } TEST_CASE("DependencyNodeProcessor_NoInstallers", "[dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; Context context { installOutput, std::cin }; Manifest manifest = CreateFakeManifestWithDependencies("withoutInstallers"); context.Add(Source{ std::make_shared() }); DependencyNodeProcessor nodeProcessor(context); Dependency rootAsDependency(DependencyType::Package, manifest.Id); DependencyNodeProcessorResult result = nodeProcessor.EvaluateDependencies(rootAsDependency); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowNoInstallerFound("withoutInstallers"_liv))) != std::string::npos); REQUIRE(result == DependencyNodeProcessorResult::Error); } TEST_CASE("DependencyNodeProcessor_StackOrderIsOk", "[dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; Context context{ installOutput, std::cin }; Manifest manifest = CreateFakeManifestWithDependencies("StackOrderIsOk"); context.Add(Source{ std::make_shared() }); DependencyNodeProcessor nodeProcessor(context); Dependency rootAsDependency(DependencyType::Package, manifest.Id); DependencyNodeProcessorResult result = nodeProcessor.EvaluateDependencies(rootAsDependency); auto dependencyList = nodeProcessor.GetDependencyList(); REQUIRE(dependencyList.Size() == 1); REQUIRE(dependencyList.HasDependency(Dependency(DependencyType::Package, "C"))); REQUIRE(result == DependencyNodeProcessorResult::Success); } TEST_CASE("DependencyNodeProcessor_NoMatches", "[dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; Context context{ installOutput, std::cin }; Manifest manifest = CreateFakeManifestWithDependencies("NoMatches"); context.Add(Source{ std::make_shared() }); DependencyNodeProcessor nodeProcessor(context); Dependency rootAsDependency(DependencyType::Package, manifest.Id); DependencyNodeProcessorResult result = nodeProcessor.EvaluateDependencies(rootAsDependency); auto dependencyList = nodeProcessor.GetDependencyList(); REQUIRE(dependencyList.Size() == 0); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowNoMatches)) != std::string::npos); REQUIRE(result == DependencyNodeProcessorResult::Error); } TEST_CASE("DependencyList_Add_MinVersion", "[dependencies]") { DependencyType type = DependencyType::Package; std::string identifier = "Identifier"; DependencyList list; Dependency dependencyWithoutMinVersion{ type, identifier }; Dependency dependencyWithLowerMinVersion{ type, identifier, "1.0" }; Dependency dependencyWithHigherMinVersion{ type, identifier, "3.0" }; Dependency dependencyToAdd{ type, identifier, "2.0" }; SECTION("Existing dependency has no min version, added does") { list.Add(dependencyWithoutMinVersion); list.Add(dependencyToAdd); const Dependency* dependency = list.HasDependency(dependencyToAdd); REQUIRE(dependency != nullptr); REQUIRE(dependency->MinVersion.has_value()); REQUIRE(dependency->MinVersion == dependencyToAdd.MinVersion); } SECTION("Existing dependency has lower min version") { list.Add(dependencyWithLowerMinVersion); list.Add(dependencyToAdd); const Dependency* dependency = list.HasDependency(dependencyToAdd); REQUIRE(dependency != nullptr); REQUIRE(dependency->MinVersion.has_value()); REQUIRE(dependency->MinVersion == dependencyToAdd.MinVersion); } SECTION("Existing dependency has higher min version") { list.Add(dependencyWithHigherMinVersion); list.Add(dependencyToAdd); const Dependency* dependency = list.HasDependency(dependencyToAdd); REQUIRE(dependency != nullptr); REQUIRE(dependency->MinVersion.has_value()); REQUIRE(dependency->MinVersion == dependencyWithHigherMinVersion.MinVersion); } SECTION("Existing dependency has no min version, neither does added") { list.Add(dependencyWithoutMinVersion); list.Add(dependencyWithoutMinVersion); const Dependency* dependency = list.HasDependency(dependencyToAdd); REQUIRE(dependency != nullptr); REQUIRE(!dependency->MinVersion.has_value()); } SECTION("Existing dependency has min version, added does not") { list.Add(dependencyWithHigherMinVersion); list.Add(dependencyWithoutMinVersion); const Dependency* dependency = list.HasDependency(dependencyToAdd); REQUIRE(dependency != nullptr); REQUIRE(dependency->MinVersion.has_value()); REQUIRE(dependency->MinVersion == dependencyWithHigherMinVersion.MinVersion); } } ================================================ FILE: src/AppInstallerCLITests/DependenciesTestSource.h ================================================ #pragma once #include "pch.h" #include "TestSource.h" #include "TestCommon.h" #include "AppInstallerVersions.h" #include #include using namespace AppInstaller::Repository; using namespace AppInstaller::Manifest; using namespace AppInstaller::Utility; namespace TestCommon { namespace { Manifest CreateFakeManifestWithDependencies(std::string input) { auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Exe_Dependencies.yaml")); manifest.Id = input; manifest.Moniker = input; auto& installer = manifest.Installers.at(0); installer.ProductId = input; installer.Dependencies.Clear(); string_t defaultFakeVersion("0.0.1-defaultFakeVersion"); if (input == "withoutInstallers") { manifest.Installers.clear(); return manifest; } /* * Dependencies: * "A": Depends on the test * B: NoDependency * C: B * D: E * E: D * F: B * G: C * H: G, B * * installed1 * minVersion1.0 * minVersion1.5 * requires1.5: minVersion1.5 * minVersion2.0 //invalid version (not returned as result) */ //-- predefined if (input == "C") { installer.Dependencies.Add(Dependency(DependencyType::Package, "B", defaultFakeVersion)); } if (input == "D") { installer.Dependencies.Add(Dependency(DependencyType::Package, "E", defaultFakeVersion)); } if (input == "E") { installer.Dependencies.Add(Dependency(DependencyType::Package, "D", defaultFakeVersion)); } if (input == "F") { installer.Dependencies.Add(Dependency(DependencyType::Package, "B", defaultFakeVersion)); } if (input == "G") { installer.Dependencies.Add(Dependency(DependencyType::Package, "C", defaultFakeVersion)); } if (input == "H") { installer.Dependencies.Add(Dependency(DependencyType::Package, "G", defaultFakeVersion)); installer.Dependencies.Add(Dependency(DependencyType::Package, "B", defaultFakeVersion)); } if (input == "installed1") { installer.Dependencies.Add(Dependency(DependencyType::Package, "installed1Dep", defaultFakeVersion)); } if (input == "minVersion1.0") { manifest.Id = "minVersion"; manifest.Version = "1.0"; } if (input == "minVersion1.5") { manifest.Id = "minVersion"; manifest.Version = "1.5"; } if (input == "requires1.5") { installer.Dependencies.Add(Dependency(DependencyType::Package, "minVersion", "1.5")); } // depends on test if (input == "StackOrderIsOk") { installer.Dependencies.Add(Dependency(DependencyType::Package, "C", defaultFakeVersion)); } if (input == "NeedsToInstallBFirst") { installer.Dependencies.Add(Dependency(DependencyType::Package, "B", defaultFakeVersion)); installer.Dependencies.Add(Dependency(DependencyType::Package, "C", defaultFakeVersion)); } if (input == "EasyToSeeLoop") { installer.Dependencies.Add(Dependency(DependencyType::Package, "D", defaultFakeVersion)); } if (input == "DependencyAlreadyInStackButNoLoop") { installer.Dependencies.Add(Dependency(DependencyType::Package, "C", defaultFakeVersion)); installer.Dependencies.Add(Dependency(DependencyType::Package, "F", defaultFakeVersion)); } if (input == "PathBetweenBranchesButNoLoop") { installer.Dependencies.Add(Dependency(DependencyType::Package, "C", defaultFakeVersion)); installer.Dependencies.Add(Dependency(DependencyType::Package, "H", defaultFakeVersion)); } if (input == "DependenciesInstalled") { installer.Dependencies.Add(Dependency(DependencyType::Package, "installed1", defaultFakeVersion)); } if (input == "DependenciesValidMinVersions") { installer.Dependencies.Add(Dependency(DependencyType::Package, "minVersion", "1.0")); } if (input == "DependenciesValidMinVersionsMultiple") { installer.Dependencies.Add(Dependency(DependencyType::Package, "minVersion", "1.0")); installer.Dependencies.Add(Dependency(DependencyType::Package, "requires1.5")); } return manifest; } } struct DependenciesTestSource : public TestSource { SearchResult Search(const SearchRequest& request) const override { SearchResult result; std::string input; if (request.Query) { input = request.Query->Value; } else if (!request.Inclusions.empty()) { input = request.Inclusions[0].Value; } else if (!request.Filters.empty()) { input = request.Filters[0].Value; } bool installed = false; if (input == "installed1") { installed = true; } if (input == "NoMatches") { return result; } Manifest manifest; if (input == "MultipleDependenciesFromManifest") { manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_MultipleDependencies.yaml")); } else { manifest = CreateFakeManifestWithDependencies(input); } //TODO: // test for installed packages and packages that need upgrades // test for different min Version of dependencies if (installed) { //auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); result.Matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest }, shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); } else { result.Matches.emplace_back( ResultMatch( TestCompositePackage::Make( std::vector{ manifest }, shared_from_this() ), PackageMatchFilter(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id))); } return result; } }; } ================================================ FILE: src/AppInstallerCLITests/DownloadFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestHooks.h" #include "AppInstallerRuntime.h" #include "WorkflowCommon.h" #include using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Authentication; using namespace AppInstaller::CLI; TEST_CASE("DownloadFlow_DownloadCommandProhibited", "[DownloadFlow][workflow]") { std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_DownloadCommandProhibited.yaml").GetPath().u8string()); DownloadCommand download({}); download.Execute(context); INFO(downloadOutput.str()); // Verify AppInfo is printed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED); REQUIRE(downloadOutput.str().find(CLI::Resource::LocString(CLI::Resource::String::InstallerDownloadCommandProhibited).get()) != std::string::npos); } AppInstaller::Utility::DownloadResult ValidateAzureBlobStorageAuthHeaders( const std::string&, const std::filesystem::path& dest, AppInstaller::Utility::DownloadType, AppInstaller::IProgressCallback&, std::optional info) { REQUIRE(info); REQUIRE(info->RequestHeaders.size() > 0); REQUIRE(info->RequestHeaders[0].IsAuth); REQUIRE(info->RequestHeaders[0].Name == "Authorization"); REQUIRE(info->RequestHeaders[0].Value == "Bearer TestToken"); REQUIRE_FALSE(info->RequestHeaders[1].IsAuth); REQUIRE(info->RequestHeaders[1].Name == "x-ms-version"); // Not validating x-ms-version value std::ofstream file(dest, std::ofstream::out); file << "test"; file.close(); AppInstaller::Utility::DownloadResult result; result.Sha256Hash = AppInstaller::Utility::SHA256::ConvertToBytes("65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B"); return result; } TEST_CASE("DownloadFlow_DownloadWithInstallerAuthenticationSuccess", "[DownloadFlow][workflow]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } // Set authentication success result override std::string expectedToken = "TestToken"; AuthenticationResult authResultOverride; authResultOverride.Status = S_OK; authResultOverride.Token = expectedToken; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); // Set auth header validation override TestHook::SetDownloadResult_Function_Override downloadFunctionOverride({ &ValidateAzureBlobStorageAuthHeaders }); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("ManifestV1_10-InstallerAuthentication.yaml").GetPath().u8string()); TestCommon::TempDirectory tempDirectory("TempDownload"); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory.GetPath().u8string()); DownloadCommand download({}); download.Execute(context); INFO(downloadOutput.str()); // Verify success REQUIRE_FALSE(context.IsTerminated()); REQUIRE(context.GetTerminationHR() == S_OK); } TEST_CASE("DownloadFlow_DownloadWithInstallerAuthenticationNotSupported", "[DownloadFlow][workflow]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } // Set authentication failed result AuthenticationResult authResultOverride; authResultOverride.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("ManifestV1_10-InstallerAuthentication.yaml").GetPath().u8string()); DownloadCommand download({}); download.Execute(context); INFO(downloadOutput.str()); // Verify AppInfo is printed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED); REQUIRE(downloadOutput.str().find(CLI::Resource::LocString(CLI::Resource::String::InstallerDownloadAuthenticationNotSupported).get()) != std::string::npos); } TEST_CASE("DownloadFlow_DownloadWithInstallerAuthenticationFailed", "[DownloadFlow][workflow]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } // Set authentication failed result AuthenticationResult authResultOverride; authResultOverride.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("ManifestV1_10-InstallerAuthentication.yaml").GetPath().u8string()); DownloadCommand download({}); download.Execute(context); INFO(downloadOutput.str()); // Verify AppInfo is printed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED); REQUIRE(downloadOutput.str().find(CLI::Resource::LocString(CLI::Resource::String::InstallerDownloadAuthenticationFailed).get()) != std::string::npos); } ================================================ FILE: src/AppInstallerCLITests/Downloader.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "AppInstallerDownloader.h" #include "AppInstallerSHA256.h" #include "HttpStream/HttpLocalCache.h" using namespace AppInstaller; using namespace AppInstaller::Utility; using namespace std::string_literals; TEST_CASE("DownloadValidFileAndVerifyHash", "[Downloader]") { TestCommon::TempFile tempFile("downloader_test"s, ".test"s); INFO("Using temporary file named: " << tempFile.GetPath()); // Todo: point to files from our repo when the repo goes public ProgressCallback callback; auto result = Download("https://raw.githubusercontent.com/microsoft/msix-packaging/master/LICENSE", tempFile.GetPath(), DownloadType::Manifest, callback); REQUIRE(!result.Sha256Hash.empty()); auto resultHash = result.Sha256Hash; auto expectedHash = SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); REQUIRE(std::equal( expectedHash.begin(), expectedHash.end(), resultHash.begin())); uint64_t expectedFileSize = 1119; REQUIRE(result.SizeInBytes == expectedFileSize); REQUIRE(std::filesystem::file_size(tempFile.GetPath()) == expectedFileSize); REQUIRE(result.ContentType); REQUIRE(!result.ContentType.value().empty()); // Verify motw content std::filesystem::path motwFile(tempFile); motwFile += ":Zone.Identifier:$data"; std::ifstream motwStream(motwFile); std::stringstream motwContent; motwContent << motwStream.rdbuf(); std::string motwContentStr = motwContent.str(); REQUIRE(motwContentStr.find("ZoneId=3") != std::string::npos); } TEST_CASE("DownloadValidFileAndCancel", "[Downloader]") { TestCommon::TempFile tempFile("downloader_test"s, ".test"s); INFO("Using temporary file named: " << tempFile.GetPath()); ProgressCallback callback; DownloadResult waitResult; std::thread waitThread([&] { waitResult = Download("https://aka.ms/win32-x64-user-stable", tempFile.GetPath(), DownloadType::Installer, callback); }); callback.Cancel(); waitThread.join(); REQUIRE(waitResult.Sha256Hash.empty()); } TEST_CASE("DownloadInvalidUrl", "[Downloader]") { TestCommon::TempFile tempFile("downloader_test"s, ".test"s); INFO("Using temporary file named: " << tempFile.GetPath()); ProgressCallback callback; REQUIRE_THROWS(Download("blargle-flargle-fluff", tempFile.GetPath(), DownloadType::Installer, callback)); } TEST_CASE("HttpStream_ReadLastFullPage", "[HttpStream]") { Microsoft::WRL::ComPtr stream; STATSTG stat = { 0 }; for (size_t i = 0; i < 10; ++i) { stream = GetReadOnlyStreamFromURI("https://aka.ms/win32-x64-user-stable"); stat = { 0 }; REQUIRE(stream->Stat(&stat, STATFLAG_NONAME) == S_OK); if (stat.cbSize.QuadPart > 0) { break; } Sleep(500); } { INFO("https://aka.ms/win32-x64-user-stable gave back a 0 byte file"); REQUIRE(stream); } LARGE_INTEGER seek; seek.QuadPart = (stat.cbSize.QuadPart / HttpStream::HttpLocalCache::PAGE_SIZE) * HttpStream::HttpLocalCache::PAGE_SIZE; REQUIRE(stream->Seek(seek, STREAM_SEEK_SET, nullptr) == S_OK); std::unique_ptr buffer = std::make_unique(HttpStream::HttpLocalCache::PAGE_SIZE); ULONG read = 0; REQUIRE(stream->Read(buffer.get(), static_cast(HttpStream::HttpLocalCache::PAGE_SIZE), &read) >= S_OK); REQUIRE(read == (stat.cbSize.QuadPart % HttpStream::HttpLocalCache::PAGE_SIZE)); } TEST_CASE("CacheControl", "[Downloader]") { SECTION("Empty") { CacheControlPolicy test{ L"" }; REQUIRE(!test.Present); } SECTION("Space") { CacheControlPolicy test{ L" " }; REQUIRE(!test.Present); } SECTION("Standard") { CacheControlPolicy test{ L"public, max-age=77287" }; REQUIRE(test.Present); REQUIRE(test.Public); REQUIRE(!test.NoCache); REQUIRE(!test.NoStore); REQUIRE(test.MaxAge == 77287); } SECTION("All") { CacheControlPolicy test{ L"public, no-cache, no-store, max-age = 77" }; REQUIRE(test.Present); REQUIRE(test.Public); REQUIRE(test.NoCache); REQUIRE(test.NoStore); REQUIRE(test.MaxAge == 77); } SECTION("Casing") { CacheControlPolicy test{ L"Public, Max-Age=42" }; REQUIRE(test.Present); REQUIRE(test.Public); REQUIRE(!test.NoCache); REQUIRE(!test.NoStore); REQUIRE(test.MaxAge == 42); } SECTION("Unknown") { CacheControlPolicy test{ L"public, max-age=77287, not-real" }; REQUIRE(test.Present); REQUIRE(test.Public); REQUIRE(!test.NoCache); REQUIRE(!test.NoStore); REQUIRE(test.MaxAge == 77287); } SECTION("MaxAge Negative") { CacheControlPolicy test{ L"max-age=-1" }; REQUIRE(test.MaxAge == CacheControlPolicy::MaximumMaxAge); } SECTION("MaxAge not a number") { CacheControlPolicy test{ L"max-age=FOO" }; REQUIRE(test.MaxAge == 0); } } ================================================ FILE: src/AppInstallerCLITests/Errors.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace AppInstaller; using namespace AppInstaller::Utility; using namespace std::string_literals; TEST_CASE("EnsureSortedErrorList", "[errors]") { auto errors = Errors::GetWinGetErrors(); for (size_t i = 1; i < errors.size(); ++i) { INFO(errors[i - 1]->Symbol() << " then " << errors[i]->Symbol()); REQUIRE(errors[i]->Value() > errors[i - 1]->Value()); } } ================================================ FILE: src/AppInstallerCLITests/ExperimentalFeature.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSettings.h" #include #include #include using namespace AppInstaller::Settings; using namespace TestCommon; TEST_CASE("ExperimentalFeature None", "[experimentalFeature]") { // Make sure Feature::None is always enabled. REQUIRE(ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::None)); // Make sure to throw requesting Feature::None REQUIRE_THROWS_HR(ExperimentalFeature::GetFeature(ExperimentalFeature::Feature::None), E_UNEXPECTED); // Make sure Feature::None is not disabled by Group Policy auto policiesKey = RegCreateVolatileTestRoot(); SetRegistryValue(policiesKey.get(), ExperimentalFeaturesPolicyValueName, false); GroupPolicyTestOverride policies{ policiesKey.get() }; REQUIRE(ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::None)); } TEST_CASE("ExperimentalFeature ExperimentalCmd", "[experimentalFeature]") { auto again = DeleteUserSettingsFiles(); SECTION("Feature off default") { UserSettingsTest userSettingTest; REQUIRE_FALSE(ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::ExperimentalCmd, userSettingTest)); } SECTION("Feature on") { std::string_view json = R"({ "experimentalFeatures": { "experimentalCmd": true } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::ExperimentalCmd, userSettingTest)); } SECTION("Feature off") { std::string_view json = R"({ "experimentalFeatures": { "experimentalCmd": false } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE_FALSE(ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::ExperimentalCmd, userSettingTest)); } SECTION("Invalid value") { std::string_view json = R"({ "experimentalFeatures": { "experimentalCmd": "string" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE_FALSE(ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::ExperimentalCmd, userSettingTest)); } SECTION("Disabled by group policy") { auto policiesKey = RegCreateVolatileTestRoot(); SetRegistryValue(policiesKey.get(), ExperimentalFeaturesPolicyValueName, false); GroupPolicyTestOverride policies{ policiesKey.get() }; std::string_view json = R"({ "experimentalFeatures": { "experimentalCmd": true } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE_FALSE(ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::ExperimentalCmd, userSettingTest)); } } ================================================ FILE: src/AppInstallerCLITests/ExportFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include using namespace TestCommon; using namespace AppInstaller::CLI; TEST_CASE("ExportFlow_ExportAll", "[ExportFlow][workflow]") { TestCommon::TempFile exportResultPath("TestExport.json"); std::ostringstream exportOutput; TestContext context{ exportOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); context.Args.AddArg(Execution::Args::Type::OutputFile, exportResultPath); ExportCommand exportCommand({}); exportCommand.Execute(context); INFO(exportOutput.str()); // Verify contents of exported collection const auto& exportedCollection = context.Get(); REQUIRE(exportedCollection.Sources.size() == 1); REQUIRE(exportedCollection.Sources[0].Details.Identifier == "*TestSource"); const auto& exportedPackages = exportedCollection.Sources[0].Packages; REQUIRE(exportedPackages.size() == 6); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestExeInstaller" && p.VersionAndChannel.GetVersion().ToString().empty(); })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestMsixInstaller" && p.VersionAndChannel.GetVersion().ToString().empty(); })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestMSStoreInstaller" && p.VersionAndChannel.GetVersion().ToString().empty(); })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestPortableInstaller" && p.VersionAndChannel.GetVersion().ToString().empty(); })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestZipInstaller" && p.VersionAndChannel.GetVersion().ToString().empty(); })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestExeUnknownVersion" && p.VersionAndChannel.GetVersion().ToString().empty(); })); } TEST_CASE("ExportFlow_ExportAll_WithVersions", "[ExportFlow][workflow]") { TestCommon::TempFile exportResultPath("TestExport.json"); std::ostringstream exportOutput; TestContext context{ exportOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); context.Args.AddArg(Execution::Args::Type::OutputFile, exportResultPath); context.Args.AddArg(Execution::Args::Type::IncludeVersions); ExportCommand exportCommand({}); exportCommand.Execute(context); INFO(exportOutput.str()); // Verify contents of exported collection const auto& exportedCollection = context.Get(); REQUIRE(exportedCollection.Sources.size() == 1); REQUIRE(exportedCollection.Sources[0].Details.Identifier == "*TestSource"); const auto& exportedPackages = exportedCollection.Sources[0].Packages; REQUIRE(exportedPackages.size() == 6); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestExeInstaller" && p.VersionAndChannel.GetVersion().ToString() == "1.0.0.0"; })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestMsixInstaller" && p.VersionAndChannel.GetVersion().ToString() == "1.0.0.0"; })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestMSStoreInstaller" && p.VersionAndChannel.GetVersion().ToString() == "1.0.0.0"; })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestPortableInstaller" && p.VersionAndChannel.GetVersion().ToString() == "1.0.0.0"; })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestZipInstaller" && p.VersionAndChannel.GetVersion().ToString() == "1.0.0.0"; })); REQUIRE(exportedPackages.end() != std::find_if(exportedPackages.begin(), exportedPackages.end(), [](const auto& p) { return p.Id == "AppInstallerCliTest.TestExeUnknownVersion" && p.VersionAndChannel.GetVersion().ToString() == "unknown"; })); } ================================================ FILE: src/AppInstallerCLITests/FileCache.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace AppInstaller::Caching; using namespace AppInstaller::Utility; using namespace TestCommon; struct TestFileCache { struct UpstreamFileInfo { TestDataFile OriginalFile; std::filesystem::path Offset; std::filesystem::path UpstreamPath; std::vector Contents; SHA256::HashBuffer ContentHash; }; TestFileCache(std::string identifier = {}, size_t upstreamCount = 1) { if (identifier.empty()) { identifier = ConvertToUTF8(CreateNewGuidNameWString()); } std::vector upstreamStrings; for (size_t i = 0; i < upstreamCount; ++i) { UpstreamSources.emplace_back("TestFileCache"); upstreamStrings.emplace_back(UpstreamSources.back().GetPath().u8string()); } CachePtr = std::make_unique(FileCache::Type::Tests, std::move(identifier), std::move(upstreamStrings)); } FileCache& Cache() { return *CachePtr; } FileCache* operator->() { return CachePtr.get(); } UpstreamFileInfo PrepareUpstreamFile(const std::filesystem::path& testDataFile, const std::filesystem::path& offset = {}, size_t index = 0) { UpstreamFileInfo result{ testDataFile }; auto dataFilePath = result.OriginalFile.GetPath(); result.Offset = offset.empty() ? dataFilePath.filename() : offset; result.UpstreamPath = UpstreamSources[index].GetPath() / result.Offset; std::filesystem::copy_file(dataFilePath, result.UpstreamPath); std::ifstream fileStream{ dataFilePath, std::ios_base::in | std::ios_base::binary }; result.Contents = ReadEntireStreamAsByteArray(fileStream); result.ContentHash = SHA256::ComputeHash(result.Contents); return result; } std::filesystem::path GetCacheFilePath(const UpstreamFileInfo& upstreamFileInfo) { std::filesystem::path result = CachePtr->GetDetails().GetCachePath() / upstreamFileInfo.Offset; std::filesystem::create_directories(result.parent_path()); return result; } std::unique_ptr GetFile(const UpstreamFileInfo& upstreamFileInfo) { return CachePtr->GetFile(upstreamFileInfo.Offset, upstreamFileInfo.ContentHash); } void RequireCachedFile(const UpstreamFileInfo& upstreamFileInfo) { std::filesystem::path cachedFilePath = GetCacheFilePath(upstreamFileInfo); REQUIRE(std::filesystem::is_regular_file(cachedFilePath)); REQUIRE(SHA256::AreEqual(upstreamFileInfo.ContentHash, SHA256::ComputeHashFromFile(cachedFilePath))); } std::unique_ptr CachePtr; std::vector UpstreamSources; }; TEST_CASE("FileCache_TypeLocationsDiffer", "[file_cache]") { std::string identifier = "identifier"; std::string identifier2 = "identifier2"; REQUIRE(FileCache(FileCache::Type::IndexV1_Manifest, identifier, {}).GetDetails().GetCachePath() != FileCache(FileCache::Type::IndexV2_Manifest, identifier, {}).GetDetails().GetCachePath()); REQUIRE(FileCache(FileCache::Type::IndexV1_Manifest, identifier, {}).GetDetails().GetCachePath() != FileCache(FileCache::Type::IndexV2_PackageVersionData, identifier, {}).GetDetails().GetCachePath()); REQUIRE(FileCache(FileCache::Type::IndexV2_Manifest, identifier, {}).GetDetails().GetCachePath() != FileCache(FileCache::Type::IndexV2_PackageVersionData, identifier, {}).GetDetails().GetCachePath()); REQUIRE(FileCache(FileCache::Type::IndexV1_Manifest, identifier, {}).GetDetails().GetCachePath() != FileCache(FileCache::Type::IndexV1_Manifest, identifier2, {}).GetDetails().GetCachePath()); REQUIRE(FileCache(FileCache::Type::IndexV2_Manifest, identifier, {}).GetDetails().GetCachePath() != FileCache(FileCache::Type::IndexV2_Manifest, identifier2, {}).GetDetails().GetCachePath()); REQUIRE(FileCache(FileCache::Type::IndexV2_PackageVersionData, identifier, {}).GetDetails().GetCachePath() != FileCache(FileCache::Type::IndexV2_PackageVersionData, identifier2, {}).GetDetails().GetCachePath()); } TEST_CASE("FileCache_TypeLocationsSame", "[file_cache]") { std::string identifier = "identifier"; std::string source = "source"; REQUIRE(FileCache(FileCache::Type::IndexV1_Manifest, identifier, {}).GetDetails().GetCachePath() == FileCache(FileCache::Type::IndexV1_Manifest, identifier, { source }).GetDetails().GetCachePath()); REQUIRE(FileCache(FileCache::Type::IndexV2_Manifest, identifier, {}).GetDetails().GetCachePath() == FileCache(FileCache::Type::IndexV2_Manifest, identifier, { source }).GetDetails().GetCachePath()); REQUIRE(FileCache(FileCache::Type::IndexV2_PackageVersionData, identifier, {}).GetDetails().GetCachePath() == FileCache(FileCache::Type::IndexV2_PackageVersionData, identifier, { source }).GetDetails().GetCachePath()); } TEST_CASE("FileCache_NoCachedFile", "[file_cache]") { TestFileCache testFileCache; INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); auto sourceFile = testFileCache.PrepareUpstreamFile("Manifest-Good-SystemReferenceComplex.yaml"); auto cachedStream = testFileCache.GetFile(sourceFile); REQUIRE(cachedStream); REQUIRE(SHA256::AreEqual(sourceFile.ContentHash, SHA256::ComputeHash(ReadEntireStreamAsByteArray(*cachedStream)))); testFileCache.RequireCachedFile(sourceFile); } TEST_CASE("FileCache_CachedFileIsDirectory", "[file_cache]") { TestFileCache testFileCache; INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); auto sourceFile = testFileCache.PrepareUpstreamFile("Manifest-Good.yaml"); std::filesystem::create_directories(testFileCache.GetCacheFilePath(sourceFile)); auto cachedStream = testFileCache.GetFile(sourceFile); REQUIRE(cachedStream); REQUIRE(SHA256::AreEqual(sourceFile.ContentHash, SHA256::ComputeHash(ReadEntireStreamAsByteArray(*cachedStream)))); testFileCache.RequireCachedFile(sourceFile); } TEST_CASE("FileCache_CachedFileGoodHash", "[file_cache]") { TestFileCache testFileCache; INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); auto sourceFile = testFileCache.PrepareUpstreamFile("InstallFlowTest_MSStore.yaml"); std::filesystem::copy_file(sourceFile.OriginalFile, testFileCache.GetCacheFilePath(sourceFile)); auto cachedStream = testFileCache.GetFile(sourceFile); REQUIRE(cachedStream); REQUIRE(SHA256::AreEqual(sourceFile.ContentHash, SHA256::ComputeHash(ReadEntireStreamAsByteArray(*cachedStream)))); testFileCache.RequireCachedFile(sourceFile); } TEST_CASE("FileCache_CachedFileBadHash", "[file_cache]") { TestFileCache testFileCache; INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); auto sourceFile = testFileCache.PrepareUpstreamFile("ManifestV1-MultiFile-Version.yaml"); std::filesystem::copy_file(TestDataFile("Manifest-Bad-ProductCodeOnMSIX.yaml"), testFileCache.GetCacheFilePath(sourceFile)); auto cachedStream = testFileCache.GetFile(sourceFile); REQUIRE(cachedStream); REQUIRE(SHA256::AreEqual(sourceFile.ContentHash, SHA256::ComputeHash(ReadEntireStreamAsByteArray(*cachedStream)))); testFileCache.RequireCachedFile(sourceFile); } TEST_CASE("FileCache_CachedFileLockedExclusive", "[file_cache]") { TestFileCache testFileCache; INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); auto sourceFile = testFileCache.PrepareUpstreamFile("ManifestV1-MultiFile-Installer.yaml"); TestDataFile wrongFileOriginal = TestDataFile("Manifest-Bad-InvalidLocale.yaml"); std::filesystem::path wrongFilePath = testFileCache.GetCacheFilePath(sourceFile); std::filesystem::copy_file(wrongFileOriginal, wrongFilePath); wil::unique_handle exclusiveFileHandle{ CreateFileW(wrongFilePath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) }; auto cachedStream = testFileCache.GetFile(sourceFile); REQUIRE(cachedStream); REQUIRE(SHA256::AreEqual(sourceFile.ContentHash, SHA256::ComputeHash(ReadEntireStreamAsByteArray(*cachedStream)))); } TEST_CASE("FileCache_FirstUpstreamDoesNotHaveFile", "[file_cache]") { TestFileCache testFileCache({}, 2); INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); auto sourceFile = testFileCache.PrepareUpstreamFile("Manifest-Good-MultiLocale.yaml", {}, 1); auto cachedStream = testFileCache.GetFile(sourceFile); REQUIRE(cachedStream); REQUIRE(SHA256::AreEqual(sourceFile.ContentHash, SHA256::ComputeHash(ReadEntireStreamAsByteArray(*cachedStream)))); testFileCache.RequireCachedFile(sourceFile); } TEST_CASE("FileCache_FirstUpstreamHasBadHash", "[file_cache]") { TestFileCache testFileCache({}, 2); INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); auto badFile = testFileCache.PrepareUpstreamFile("Manifest-Bad-VersionMissing.yaml", {}, 0); auto sourceFile = testFileCache.PrepareUpstreamFile("Manifest-Good-MultiLocale.yaml", {}, 1); auto cachedStream = testFileCache.GetFile(sourceFile); REQUIRE(cachedStream); REQUIRE(SHA256::AreEqual(sourceFile.ContentHash, SHA256::ComputeHash(ReadEntireStreamAsByteArray(*cachedStream)))); testFileCache.RequireCachedFile(sourceFile); } TEST_CASE("FileCache_NoUpstreamSources", "[file_cache]") { TestFileCache testFileCache("", 0); INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); REQUIRE_THROWS_HR(testFileCache->GetFile("any_file", SHA256::ComputeHash("garbage")), E_NOT_SET); } TEST_CASE("FileCache_PathTooLong", "[file_cache]") { TestFileCache testFileCache(std::string(260, 'a')); INFO("Cache location: " << testFileCache->GetDetails().GetCachePath().u8string()); auto sourceFile = testFileCache.PrepareUpstreamFile("Manifest-Good-SystemReferenceComplex.yaml"); auto cachedStream = testFileCache.GetFile(sourceFile); REQUIRE(cachedStream); REQUIRE(SHA256::AreEqual(sourceFile.ContentHash, SHA256::ComputeHash(ReadEntireStreamAsByteArray(*cachedStream)))); } ================================================ FILE: src/AppInstallerCLITests/FileLogger.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace AppInstaller::Logging; using namespace AppInstaller::Utility; using namespace TestCommon; std::string GetHeaderString() { return "TIME [CHAN] Header Message"; } std::string GetLargeString() { return "[===|Clearly defined start to large string|===]\r\n" "While this string does not need to be particularly unique, it is still good if it is not easily duplicated by any other random set of data.\r\n" "It should also end in a character that is not used in any other way within these tests, so please don't include that character when writing tests.\r\n" "That character is &"; } namespace { #define WINGET_DEFINE_STRING_ENUM(_enum_,_value_) constexpr std::string_view _enum_##_##_value_ = #_value_##sv WINGET_DEFINE_STRING_ENUM(TagState, Unset); WINGET_DEFINE_STRING_ENUM(TagState, SetAtStart); WINGET_DEFINE_STRING_ENUM(TagState, SetAfterLogging); WINGET_DEFINE_STRING_ENUM(MaximumSizeState, Zero); WINGET_DEFINE_STRING_ENUM(MaximumSizeState, SmallerThanLargeString); WINGET_DEFINE_STRING_ENUM(MaximumSizeState, EqualToLargeString); WINGET_DEFINE_STRING_ENUM(MaximumSizeState, SlightlyLargerThanLargeString); WINGET_DEFINE_STRING_ENUM(MaximumSizeState, MuchLargerThanLargeString); constexpr std::string_view WrapIndicator = "--- log file has wrapped ---"sv; // The amount of extra size that is allowed (total indicator size + newline + newlines from test strings) constexpr size_t ExtraAllowedSize = 68; constexpr size_t NewLineCharacterCount = 2; constexpr size_t SmallDifferenceSize = 10; constexpr AppInstaller::Logging::Channel DefaultChannel = AppInstaller::Logging::Channel::Core; constexpr AppInstaller::Logging::Level DefaultLevel = AppInstaller::Logging::Level::Info; void ValidateFileContents(const std::filesystem::path& file, const std::vector& expectedContents, size_t maximumSize) { std::ifstream fileStream{ file, std::ios::binary }; auto fileContents = ReadEntireStream(fileStream); std::string_view fileContentsView = fileContents; std::string fileContentsCopy = fileContents; FindAndReplace(fileContentsCopy, "\r", "\\r"); FindAndReplace(fileContentsCopy, "\n", "\\n"); INFO("File contents:\n" << fileContentsCopy); if (maximumSize) { REQUIRE(maximumSize + ExtraAllowedSize >= fileContents.size()); } size_t currentPosition = 0; for (std::string_view expectedContent : expectedContents) { REQUIRE(currentPosition < fileContents.size()); if (expectedContent == WrapIndicator) { auto endLinePosition = fileContentsView.find('\n', currentPosition); REQUIRE(endLinePosition != -1); REQUIRE(endLinePosition >= expectedContent.size() + NewLineCharacterCount); auto actualContent = fileContentsView.substr(endLinePosition + 1 - expectedContent.size() - NewLineCharacterCount, expectedContent.size()); REQUIRE(expectedContent == actualContent); currentPosition = endLinePosition + 1; } else { auto actualContent = fileContentsView.substr(currentPosition, expectedContent.size()); REQUIRE(expectedContent == actualContent); currentPosition += expectedContent.size() + NewLineCharacterCount; } } } void FileLogger_MaximumSize_Test(std::string_view tagState, std::string_view sizeState) { auto headerString = GetHeaderString(); auto largeString = GetLargeString(); // Determine maximum size size_t maximumSize = 0; if (sizeState == MaximumSizeState_SmallerThanLargeString) { maximumSize = largeString.size() - SmallDifferenceSize; } else if (sizeState == MaximumSizeState_EqualToLargeString) { maximumSize = largeString.size(); } else if (sizeState == MaximumSizeState_SlightlyLargerThanLargeString) { maximumSize = largeString.size() + SmallDifferenceSize; } else if (sizeState == MaximumSizeState_MuchLargerThanLargeString) { maximumSize = largeString.size() * 2; } INFO("Tag State: " << tagState << ", Size State: " << sizeState << "[" << maximumSize << "]"); TempFile tempFile{ "FileLogger_MaximumSize", ".log" }; FileLogger logger{ tempFile }; INFO("File: " << tempFile.GetPath().u8string()); logger.SetMaximumSize(wil::safe_cast(maximumSize)); // Set tag and log strings size_t tagPosition = 0; if (tagState == TagState_SetAtStart) { logger.SetTag(Tag::HeadersComplete); } logger.WriteDirect(DefaultChannel, DefaultLevel, headerString); if (tagState == TagState_SetAfterLogging) { logger.SetTag(Tag::HeadersComplete); tagPosition = headerString.size() + NewLineCharacterCount; } // Due to text output in the logger, log with \n only std::string largeStringWithoutCarriageReturn = largeString; FindAndReplace(largeStringWithoutCarriageReturn, "\r\n", "\n"); logger.WriteDirect(DefaultChannel, DefaultLevel, largeStringWithoutCarriageReturn); // Calculate current state size_t maximumAvailableSpace = std::numeric_limits::max(); size_t currentAvailableSpace = std::numeric_limits::max(); if (maximumSize) { maximumAvailableSpace = maximumSize - tagPosition; currentAvailableSpace = maximumSize - headerString.size() - NewLineCharacterCount; } bool shouldWrap = largeString.size() > currentAvailableSpace; INFO("Maximum Available: " << maximumAvailableSpace << ", Current Available: " << currentAvailableSpace << ", ShouldWrap: " << shouldWrap); std::vector expectedFileContents; if (tagPosition || !shouldWrap) { expectedFileContents.push_back(headerString); } if (shouldWrap) { expectedFileContents.push_back(WrapIndicator); } std::string_view largeStringView = largeString; expectedFileContents.push_back(largeStringView.substr(0, std::min(largeString.size(), maximumAvailableSpace))); ValidateFileContents(tempFile, expectedFileContents, maximumSize); // Log again INFO("Second time logging large string"); logger.WriteDirect(DefaultChannel, DefaultLevel, largeStringWithoutCarriageReturn); // The maximum size is twice the large log, so anything with a limit will wrap shouldWrap = maximumSize != 0; expectedFileContents.clear(); if (tagPosition || !shouldWrap) { expectedFileContents.push_back(headerString); } if (shouldWrap) { expectedFileContents.push_back(WrapIndicator); } else { expectedFileContents.push_back(largeStringView); } expectedFileContents.push_back(largeStringView.substr(0, std::min(largeString.size(), maximumAvailableSpace))); ValidateFileContents(tempFile, expectedFileContents, maximumSize); } } TEST_CASE("FileLogger_MaximumSize", "[logging]") { auto tagState = GENERATE(TagState_Unset, TagState_SetAtStart, TagState_SetAfterLogging); auto sizeState = GENERATE(MaximumSizeState_Zero, MaximumSizeState_SmallerThanLargeString, MaximumSizeState_EqualToLargeString, MaximumSizeState_SlightlyLargerThanLargeString, MaximumSizeState_MuchLargerThanLargeString); FileLogger_MaximumSize_Test(tagState, sizeState); } TEST_CASE("FileLogger_MaximumSize_ManyWraps", "[logging]") { TempFile tempFile{ "FileLogger_ManyWraps", ".log" }; FileLogger logger{ tempFile }; INFO("File: " << tempFile.GetPath().u8string()); size_t maximumSize = 1000; logger.SetMaximumSize(static_cast(maximumSize)); std::string header = GetHeaderString(); header += " !Now with more header!"; std::string largeString = "[*=INIT=*]Now we just need another few dozen characters, which shouldn't be that hard to get. Wow, made it already."; std::string_view largeStringView = largeString; size_t initSize = 10; logger.WriteDirect(DefaultChannel, DefaultLevel, header); logger.SetTag(Tag::HeadersComplete); // Use the default seed value as we want arbitrary but reproducible results std::default_random_engine randomEngine; std::uniform_int_distribution<> sizeDistribution(static_cast(initSize), 100); // We should expect ~500 wraps on average for (size_t i = 0; i < 9999; ++i) { logger.WriteDirect(DefaultChannel, DefaultLevel, largeStringView.substr(0, sizeDistribution(randomEngine))); } // We want the header to be preserved, followed by the wrap indicator, and at a minimum we should see the first few characters in the log string std::vector expectedFileContents; expectedFileContents.push_back(header); expectedFileContents.push_back(WrapIndicator); expectedFileContents.push_back(largeStringView.substr(0, initSize)); ValidateFileContents(tempFile, expectedFileContents, maximumSize); } ================================================ FILE: src/AppInstallerCLITests/Filesystem.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include using namespace AppInstaller::Utility; using namespace AppInstaller::Filesystem; using namespace TestCommon; TEST_CASE("PathEscapesDirectory", "[filesystem]") { TestCommon::TempDirectory tempDirectory("TempDirectory"); const std::filesystem::path& basePath = tempDirectory.GetPath(); std::string badRelativePath = "../../target.exe"; std::string badRelativePath2 = "test/../../target.exe"; std::string goodRelativePath = "target.exe"; std::string goodRelativePath2 = "test/../test1/target.exe"; std::filesystem::path badPath = basePath / badRelativePath; std::filesystem::path badPath2 = basePath / badRelativePath2; std::filesystem::path goodPath = basePath / goodRelativePath; std::filesystem::path goodPath2 = basePath / goodRelativePath2; REQUIRE(PathEscapesBaseDirectory(badPath, basePath)); REQUIRE(PathEscapesBaseDirectory(badPath2, basePath)); REQUIRE_FALSE(PathEscapesBaseDirectory(goodPath, basePath)); REQUIRE_FALSE(PathEscapesBaseDirectory(goodPath2, basePath)); } TEST_CASE("VerifySymlink", "[filesystem]") { TestCommon::TempDirectory tempDirectory("TempDirectory"); const std::filesystem::path& basePath = tempDirectory.GetPath(); std::filesystem::path testFilePath = basePath / "testFile.txt"; std::filesystem::path symlinkPath = basePath / "symlink.exe"; TestCommon::TempFile testFile(testFilePath); std::ofstream file2(testFile, std::ofstream::out); file2.close(); std::filesystem::create_symlink(testFile.GetPath(), symlinkPath); REQUIRE(SymlinkExists(symlinkPath)); REQUIRE(VerifySymlink(symlinkPath, testFilePath)); REQUIRE_FALSE(VerifySymlink(symlinkPath, "badPath")); std::filesystem::remove(testFilePath); // Ensure that symlink existence does not check the target REQUIRE(SymlinkExists(symlinkPath)); std::filesystem::remove(symlinkPath); REQUIRE_FALSE(SymlinkExists(symlinkPath)); } TEST_CASE("VerifyIsSameVolume", "[filesystem]") { // Note: Pipeline build machine uses 'D:\' as the volume. std::filesystem::path path1 = L"C:\\Program Files\\WinGet\\Packages"; std::filesystem::path path2 = L"c:\\Users\\testUser\\AppData\\Local\\Microsoft\\WinGet\\Packages"; std::filesystem::path path3 = L"localPath\\test\\folder"; std::filesystem::path path4 = L"test\\folder"; std::filesystem::path path5 = L"D:\\test\\folder"; std::filesystem::path path6 = L"F:\\test\\folder"; std::filesystem::path path7 = L"d:\\randomFolder"; std::filesystem::path path8 = L"f:\\randomFolder"; std::filesystem::path path9 = L"a"; std::filesystem::path path10 = L"b"; REQUIRE(IsSameVolume(path1, path2)); if (IsSameVolume(path5, path5)) { REQUIRE(IsSameVolume(path5, path7)); } REQUIRE(IsSameVolume(path3, path4)); REQUIRE(IsSameVolume(path9, path10)); REQUIRE_FALSE(IsSameVolume(path1, path5)); REQUIRE_FALSE(IsSameVolume(path1, path6)); REQUIRE_FALSE(IsSameVolume(path2, path5)); REQUIRE_FALSE(IsSameVolume(path2, path6)); REQUIRE_FALSE(IsSameVolume(path3, path6)); REQUIRE_FALSE(IsSameVolume(path5, path6)); REQUIRE_FALSE(IsSameVolume(path4, path6)); REQUIRE_FALSE(IsSameVolume(path6, path8)); } TEST_CASE("ReplaceCommonPathPrefix", "[filesystem]") { std::filesystem::path prefix = "C:\\test1\\test2"; std::string replacement = "%TEST%"; std::filesystem::path shouldReplace = "C:\\test1\\test2\\subdir1\\subdir2"; REQUIRE(ReplaceCommonPathPrefix(shouldReplace, prefix, replacement)); REQUIRE(shouldReplace.u8string() == (replacement + "\\subdir1\\subdir2")); std::filesystem::path shouldNotReplace = "C:\\test1\\test3\\subdir1\\subdir2"; REQUIRE(!ReplaceCommonPathPrefix(shouldNotReplace, prefix, replacement)); REQUIRE(shouldNotReplace.u8string() == "C:\\test1\\test3\\subdir1\\subdir2"); } TEST_CASE("GetExecutablePathForProcess", "[filesystem]") { std::filesystem::path thisExecutable = GetExecutablePathForProcess(GetCurrentProcess()); REQUIRE(!thisExecutable.empty()); REQUIRE(thisExecutable.is_absolute()); REQUIRE(thisExecutable.has_filename()); REQUIRE(thisExecutable.has_extension()); REQUIRE(thisExecutable.filename() == L"AppInstallerCLITests.exe"); } TEST_CASE("PathTree_InsertAndFind", "[filesystem][pathtree]") { PathTree pathTree; std::filesystem::path path1 = L"C:\\test"; std::filesystem::path path1sub = L"C:\\test\\sub"; std::filesystem::path path2 = L"C:\\diff"; std::filesystem::path path3 = L"D:\\test"; REQUIRE(nullptr == pathTree.Find(path1)); pathTree.FindOrInsert(path1) = true; REQUIRE(nullptr != pathTree.Find(path1)); REQUIRE(*pathTree.Find(path1)); REQUIRE(nullptr == pathTree.Find(path1sub)); REQUIRE(nullptr == pathTree.Find(path2)); REQUIRE(nullptr == pathTree.Find(path3)); } TEST_CASE("PathTree_InsertAndFind_Negative", "[filesystem][pathtree]") { PathTree pathTree; pathTree.FindOrInsert(L"C:\\a\\aa\\aaa"); REQUIRE(nullptr == pathTree.Find({})); REQUIRE_THROWS_HR(pathTree.FindOrInsert({}), E_INVALIDARG); } size_t CountVisited(const PathTree& pathTree, const std::filesystem::path& path, std::function predicate) { size_t result = 0; pathTree.VisitIf(path, [&](const bool&) { ++result; }, predicate); return result; } TEST_CASE("PathTree_VisitIf_Count", "[filesystem][pathtree]") { PathTree pathTree; pathTree.FindOrInsert(L"C:\\a\\aa\\aaa") = true; pathTree.FindOrInsert(L"C:\\a\\aa\\bbb") = true; pathTree.FindOrInsert(L"C:\\a\\aa\\ccc") = false; pathTree.FindOrInsert(L"C:\\a\\aa") = true; pathTree.FindOrInsert(L"C:\\a\\bb\\aaa") = false; pathTree.FindOrInsert(L"C:\\a\\bb\\bbb") = true; pathTree.FindOrInsert(L"C:\\a\\bb\\ccc") = false; pathTree.FindOrInsert(L"C:\\a\\bb") = true; pathTree.FindOrInsert(L"C:\\a\\cc\\aaa") = true; pathTree.FindOrInsert(L"C:\\a\\cc\\bbb") = false; pathTree.FindOrInsert(L"C:\\a\\cc\\ccc") = false; pathTree.FindOrInsert(L"C:\\a\\cc") = false; pathTree.FindOrInsert(L"C:\\a") = true; pathTree.FindOrInsert(L"C:\\b") = false; pathTree.FindOrInsert(L"D:\\a") = false; auto always = [](const bool&) { return true; }; auto never = [](const bool&) { return false; }; auto if_input = [](const bool& b) { return b; }; REQUIRE(0 == CountVisited(pathTree, {}, always)); REQUIRE(15 == CountVisited(pathTree, L"C:\\", always)); REQUIRE(2 == CountVisited(pathTree, L"D:\\", always)); REQUIRE(0 == CountVisited(pathTree, L"E:\\", always)); REQUIRE(1 == CountVisited(pathTree, L"C:\\", never)); REQUIRE(1 == CountVisited(pathTree, L"D:\\", never)); REQUIRE(0 == CountVisited(pathTree, L"E:\\", never)); REQUIRE(7 == CountVisited(pathTree, L"C:\\", if_input)); REQUIRE(6 == CountVisited(pathTree, L"C:\\a", if_input)); REQUIRE(2 == CountVisited(pathTree, L"C:\\a\\cc", if_input)); REQUIRE(1 == CountVisited(pathTree, L"D:\\", if_input)); REQUIRE(0 == CountVisited(pathTree, L"E:\\", if_input)); } TEST_CASE("PathTree_VisitIf_Correct", "[filesystem][pathtree]") { PathTree> pathTree; pathTree.FindOrInsert(L"C:\\a\\aa\\aaa") = { true, true }; pathTree.FindOrInsert(L"C:\\a\\aa\\bbb") = { true, true }; pathTree.FindOrInsert(L"C:\\a\\aa\\ccc") = { false, false }; pathTree.FindOrInsert(L"C:\\a\\aa") = { true, true }; pathTree.FindOrInsert(L"C:\\a\\bb\\aaa") = { false, false }; pathTree.FindOrInsert(L"C:\\a\\bb\\bbb") = { true, true }; pathTree.FindOrInsert(L"C:\\a\\bb\\ccc") = { false, false }; pathTree.FindOrInsert(L"C:\\a\\bb") = { true, true }; pathTree.FindOrInsert(L"C:\\a\\cc\\aaa") = { true, true }; pathTree.FindOrInsert(L"C:\\a\\cc\\bbb") = { false, false }; pathTree.FindOrInsert(L"C:\\a\\cc\\ccc") = { false, false }; pathTree.FindOrInsert(L"C:\\a\\cc") = { false, false }; pathTree.FindOrInsert(L"C:\\a") = { true, true }; pathTree.FindOrInsert(L"C:\\b") = { false, false }; pathTree.FindOrInsert(L"C:") = { true, false }; auto check_input = [](const std::pair& p) { REQUIRE(p.first); }; auto if_input = [](const std::pair& p) { return p.second; }; pathTree.VisitIf(L"C:", check_input, if_input); } TEST_CASE("GetFileInfoFor", "[filesystem]") { TestCommon::TempDirectory tempDirectory{ "GetFileInfoFor" }; auto now = std::filesystem::file_time_type::clock::now(); std::this_thread::sleep_for(1s); auto file1 = tempDirectory.CreateTempFile("c.txt"); std::string file1Content = "File 1 Content!"; std::ofstream{ file1 } << file1Content; std::this_thread::sleep_for(1s); auto file2 = tempDirectory.CreateTempFile("b.txt"); std::string file2Content = "More Content! Better Content!"; std::ofstream{ file2 } << file2Content; std::this_thread::sleep_for(1s); auto file3 = tempDirectory.CreateTempFile("a.txt"); std::string file3Content = "Maybe less is better?"; std::ofstream{ file3 } << file3Content; auto fileInfo = GetFileInfoFor(tempDirectory); REQUIRE(3 == fileInfo.size()); // Sort with oldest first std::sort(fileInfo.begin(), fileInfo.end(), [](const FileInfo& a, const FileInfo& b) { return a.LastWriteTime < b.LastWriteTime; }); REQUIRE(fileInfo[0].Path == file1.GetPath()); REQUIRE(fileInfo[0].LastWriteTime > now); REQUIRE(fileInfo[0].Size == file1Content.size()); REQUIRE(fileInfo[1].Path == file2.GetPath()); REQUIRE(fileInfo[1].LastWriteTime > fileInfo[0].LastWriteTime); REQUIRE(fileInfo[1].Size == file2Content.size()); REQUIRE(fileInfo[2].Path == file3.GetPath()); REQUIRE(fileInfo[2].LastWriteTime > fileInfo[1].LastWriteTime); REQUIRE(fileInfo[2].Size == file3Content.size()); } void RequireFilePaths(const std::vector& files, std::initializer_list paths) { REQUIRE(paths.size() == files.size()); size_t i = 0; for (const char* val : paths) { REQUIRE(val == files[i++].Path.u8string()); } } TEST_CASE("FilterToFilesExceedingLimits", "[filesystem]") { auto now = std::filesystem::file_time_type::clock::now(); std::vector files { { "a", now, 42 }, { "b", now - 32min, 45321 }, { "c", now - 84min, 24567 }, { "d", now - 4h, 876312 }, { "e", now - 18h, 2908534 }, { "f", now - 47h, 312 }, { "g", now - 132h, 74321 }, { "h", now - 4567h, 6573423 }, }; // Give the sort inside FilterToFilesExceedingLimits something to do std::shuffle(files.begin(), files.end(), std::mt19937{}); FileLimits limits{}; SECTION("No limits") { FilterToFilesExceedingLimits(files, limits); // Without limits, nothing exceeds them REQUIRE(files.empty()); } SECTION("Age - 1h") { limits.Age = 1h; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e", "d", "c" }); } SECTION("Age - 2h") { limits.Age = 2h; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e", "d" }); } SECTION("Age - 24h") { limits.Age = 24h; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f" }); } SECTION("Age - 7d") { limits.Age = 7 * 24h; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h" }); } SECTION("Age - 365d") { limits.Age = 365 * 24h; FilterToFilesExceedingLimits(files, limits); REQUIRE(files.empty()); } SECTION("Size - 1MB") { limits.TotalSizeInMB = 1; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e" }); } SECTION("Size - 2MB") { limits.TotalSizeInMB = 2; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e" }); } SECTION("Size - 3MB") { limits.TotalSizeInMB = 3; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e" }); } SECTION("Size - 4MB") { limits.TotalSizeInMB = 4; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h" }); } SECTION("Size - 100MB") { limits.TotalSizeInMB = 100; FilterToFilesExceedingLimits(files, limits); REQUIRE(files.empty()); } SECTION("Count - 1") { limits.Count = 1; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e", "d", "c", "b" }); } SECTION("Count - 2") { limits.Count = 2; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e", "d", "c" }); } SECTION("Count - 4") { limits.Count = 4; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e" }); } SECTION("Count - 7") { limits.Count = 7; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h" }); } SECTION("Count - 8") { limits.Count = 8; FilterToFilesExceedingLimits(files, limits); REQUIRE(files.empty()); } SECTION("Count - 100") { limits.Count = 100; FilterToFilesExceedingLimits(files, limits); REQUIRE(files.empty()); } SECTION("Mix - 24h - 2MB - 4") { limits.Age = 24h; limits.TotalSizeInMB = 2; limits.Count = 4; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e" }); } SECTION("Mix - 2h - 2MB - 4") { limits.Age = 2h; limits.TotalSizeInMB = 2; limits.Count = 4; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e", "d" }); } SECTION("Mix - 24h - 1MB - 4") { limits.Age = 24h; limits.TotalSizeInMB = 1; limits.Count = 4; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e" }); } SECTION("Mix - 24h - 2MB - 2") { limits.Age = 24h; limits.TotalSizeInMB = 2; limits.Count = 2; FilterToFilesExceedingLimits(files, limits); RequireFilePaths(files, { "h", "g", "f", "e", "d", "c" }); } } ================================================ FILE: src/AppInstallerCLITests/FolderFileWatcher.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace TestCommon; using namespace AppInstaller; namespace { void WriteText(const std::filesystem::path& path) { std::ofstream fileStream{ path }; fileStream << "text"; } std::filesystem::path RemoveRoot(const std::filesystem::path& prefix, const std::filesystem::path& source) { auto prefixItr = prefix.begin(); auto sourceItr = source.begin(); while (prefixItr != prefix.end() && sourceItr != source.end()) { if (*prefixItr != *sourceItr) { break; } ++prefixItr; ++sourceItr; } std::filesystem::path result{}; if (prefixItr == prefix.end()) { for (; sourceItr != source.end(); ++sourceItr) { if (result.empty()) { result = *sourceItr; } else { result /= *sourceItr; } } } return result; } } TEST_CASE("FolderFileWatcher_CreateNewFiles", "[FolderFileWatcher]") { TempDirectory dirToWatch("FolderFileWatcher_CreateNewFiles_", true); Utility::FolderFileWatcher folderFileWatcher(dirToWatch.GetPath()); folderFileWatcher.Start(); TempFile tempFile1(dirToWatch.GetPath(), "file1_", ".txt"); WriteText(tempFile1.GetPath()); TempFile tempFile2(dirToWatch.GetPath(), "file2_", ".txt"); WriteText(tempFile2.GetPath()); std::filesystem::path newTestDir = dirToWatch.GetPath(); newTestDir /= "testDir"; std::filesystem::create_directories(newTestDir); TempFile tempFile3(newTestDir, "file3_", ".txt"); WriteText(tempFile3.GetPath()); std::this_thread::sleep_for(100ms); folderFileWatcher.Stop(); auto& watchedFiles = folderFileWatcher.Files(); auto tempFile1RelativePath = RemoveRoot(dirToWatch, tempFile1.GetPath()); auto foundTempFile1 = watchedFiles.find(tempFile1RelativePath); REQUIRE(foundTempFile1 != watchedFiles.cend()); auto tempFile2RelativePath = RemoveRoot(dirToWatch, tempFile2.GetPath()); auto foundTempFile2 = watchedFiles.find(tempFile2RelativePath); REQUIRE(foundTempFile2 != watchedFiles.cend()); auto tempFile3RelativePath = RemoveRoot(dirToWatch, tempFile3.GetPath()); auto foundTempFile3 = watchedFiles.find(tempFile3RelativePath); REQUIRE(foundTempFile3 != watchedFiles.cend()); } TEST_CASE("FolderFileWatcher_CreateAfterStop", "[FolderFileWatcher]") { TempDirectory dirToWatch("FolderFileWatcher_CreateAfterStop_", true); Utility::FolderFileWatcher folderFileWatcher(dirToWatch.GetPath()); folderFileWatcher.Start(); TempFile tempFile1(dirToWatch.GetPath(), "file1_", ".txt"); WriteText(tempFile1.GetPath()); std::this_thread::sleep_for(100ms); folderFileWatcher.Stop(); TempFile tempFile2(dirToWatch.GetPath(), "file2_", ".txt"); WriteText(tempFile2.GetPath()); auto& watchedFiles = folderFileWatcher.Files(); auto tempFile1RelativePath = RemoveRoot(dirToWatch, tempFile1.GetPath()); auto foundTempFile1 = watchedFiles.find(tempFile1RelativePath); REQUIRE(foundTempFile1 != watchedFiles.cend()); auto tempFile2RelativePath = RemoveRoot(dirToWatch, tempFile2.GetPath()); auto foundTempFile2 = watchedFiles.find(tempFile2RelativePath); REQUIRE(foundTempFile2 == watchedFiles.cend()); } TEST_CASE("FolderFileWatcher_CreateNewFilesAndRename", "[FolderFileWatcher]") { TempDirectory dirToWatch("FolderFileWatcher_CreateNewFilesAndRename_", true); Utility::FolderFileWatcher folderFileWatcher(dirToWatch.GetPath()); folderFileWatcher.Start(); std::filesystem::path tempFile1Path = dirToWatch.GetPath() / "file1.txt"; TempFile tempFile1(tempFile1Path); WriteText(tempFile1Path); std::filesystem::path newTestDir = dirToWatch.GetPath(); newTestDir /= "testDir"; std::filesystem::create_directories(newTestDir); std::filesystem::path tempFile2Path = newTestDir / "file2.txt"; TempFile tempFile2(tempFile2Path); WriteText(tempFile2Path); std::filesystem::path tempFile1PathRenamed = dirToWatch.GetPath() / "file1_renamed.txt"; std::filesystem::path tempFile2PathRenamed = newTestDir / "file2_renamed.txt"; tempFile1.Rename(tempFile1PathRenamed); tempFile2.Rename(tempFile2PathRenamed); std::this_thread::sleep_for(100ms); folderFileWatcher.Stop(); auto& watchedFiles = folderFileWatcher.Files(); auto tempFile1RelativePath = RemoveRoot(dirToWatch, tempFile1Path); auto foundTempFile1 = watchedFiles.find(tempFile1RelativePath); REQUIRE(foundTempFile1 == watchedFiles.cend()); auto tempFile1RenamedRelativePath = RemoveRoot(dirToWatch, tempFile1PathRenamed); auto foundTempFile1Renamed = watchedFiles.find(tempFile1RenamedRelativePath); REQUIRE(foundTempFile1Renamed != watchedFiles.cend()); auto tempFile2RelativePath = RemoveRoot(dirToWatch, tempFile2Path); auto foundTempFile2 = watchedFiles.find(tempFile2RelativePath); REQUIRE(foundTempFile2 == watchedFiles.cend()); auto tempFile2RenamedRelativePath = RemoveRoot(dirToWatch, tempFile2PathRenamed); auto foundTempFile2Renamed = watchedFiles.find(tempFile2RenamedRelativePath); REQUIRE(foundTempFile2Renamed != watchedFiles.cend()); } TEST_CASE("FolderFileWatcher_CreateNewFilesAndDelete", "[FolderFileWatcher]") { TempDirectory dirToWatch("FolderFileWatcher_CreateNewFilesAndDelete_", true); Utility::FolderFileWatcher folderFileWatcher(dirToWatch.GetPath()); folderFileWatcher.Start(); TempFile tempFile1(dirToWatch.GetPath(), "file1_", ".txt"); WriteText(tempFile1.GetPath()); std::filesystem::path newTestDir = dirToWatch.GetPath(); newTestDir /= "testDir"; std::filesystem::create_directories(newTestDir); TempFile tempFile2(newTestDir, "file2_", ".txt"); WriteText(tempFile2.GetPath()); // Create files and delete them. std::filesystem::path tempFile3Path; std::filesystem::path tempFile4Path; { TempFile tempFile3(dirToWatch.GetPath(), "file3_", ".txt"); tempFile3Path = tempFile3.GetPath(); WriteText(tempFile3Path); std::filesystem::remove(tempFile3Path); TempFile tempFile4(newTestDir, "file4_", ".txt"); tempFile4Path = tempFile4.GetPath(); WriteText(tempFile4Path); std::filesystem::remove(tempFile4Path); } std::this_thread::sleep_for(100ms); folderFileWatcher.Stop(); auto& watchedFiles = folderFileWatcher.Files(); auto tempFile1RelativePath = RemoveRoot(dirToWatch, tempFile1.GetPath()); auto foundTempFile1 = watchedFiles.find(tempFile1RelativePath); REQUIRE(foundTempFile1 != watchedFiles.cend()); auto tempFile2RelativePath = RemoveRoot(dirToWatch, tempFile2.GetPath()); auto foundTempFile2 = watchedFiles.find(tempFile2RelativePath); REQUIRE(foundTempFile2 != watchedFiles.cend()); auto tempFile3RelativePath = RemoveRoot(dirToWatch, tempFile3Path); auto foundTempFile3 = watchedFiles.find(tempFile3RelativePath); REQUIRE(foundTempFile3 == watchedFiles.cend()); auto tempFile4RelativePath = RemoveRoot(dirToWatch, tempFile4Path); auto foundTempFile4 = watchedFiles.find(tempFile4RelativePath); REQUIRE(foundTempFile4 == watchedFiles.cend()); } TEST_CASE("FolderFileWatcher_Extension_CreateNewFiles", "[FolderFileWatcher]") { TempDirectory dirToWatch("FolderFileWatcher_Extension_CreateNewFiles", true); Utility::FolderFileWatcher folderFileExtensionWatcher(dirToWatch.GetPath(), ".yaml"); folderFileExtensionWatcher.Start(); TempFile tempFile1(dirToWatch.GetPath(), "file1_", ".txt"); WriteText(tempFile1.GetPath()); TempFile tempFile2(dirToWatch.GetPath(), "file2_", ".yaml"); WriteText(tempFile2.GetPath()); std::filesystem::path newTestDir = dirToWatch.GetPath(); newTestDir /= "testDir"; std::filesystem::create_directories(newTestDir); TempFile tempFile3(newTestDir, "file3_", ".txt"); WriteText(tempFile3.GetPath()); TempFile tempFile4(newTestDir, "file4_", ".yaml"); WriteText(tempFile4.GetPath()); std::this_thread::sleep_for(100ms); folderFileExtensionWatcher.Stop(); auto& watchedFiles = folderFileExtensionWatcher.Files(); auto tempFile1RelativePath = RemoveRoot(dirToWatch, tempFile1.GetPath()); auto foundTempFile1 = watchedFiles.find(tempFile1RelativePath); REQUIRE(foundTempFile1 == watchedFiles.cend()); auto tempFile2RelativePath = RemoveRoot(dirToWatch, tempFile2.GetPath()); auto foundTempFile2 = watchedFiles.find(tempFile2RelativePath); REQUIRE(foundTempFile2 != watchedFiles.cend()); auto tempFile3RelativePath = RemoveRoot(dirToWatch, tempFile3.GetPath()); auto foundTempFile3 = watchedFiles.find(tempFile3RelativePath); REQUIRE(foundTempFile3 == watchedFiles.cend()); auto tempFile4RelativePath = RemoveRoot(dirToWatch, tempFile4.GetPath()); auto foundTempFile4 = watchedFiles.find(tempFile4RelativePath); REQUIRE(foundTempFile4 != watchedFiles.cend()); } TEST_CASE("FolderFileWatcher_Extension_CreateAfterStop", "[FolderFileWatcher]") { TempDirectory dirToWatch("FolderFileWatcher_Extension_CreateAfterStop", true); Utility::FolderFileWatcher folderFileExtensionWatcher(dirToWatch.GetPath(), ".txt"); folderFileExtensionWatcher.Start(); TempFile tempFile1(dirToWatch.GetPath(), "file1_", ".txt"); WriteText(tempFile1.GetPath()); std::this_thread::sleep_for(100ms); folderFileExtensionWatcher.Stop(); TempFile tempFile2(dirToWatch.GetPath(), "file2_", ".txt"); WriteText(tempFile2.GetPath()); auto& watchedFiles = folderFileExtensionWatcher.Files(); auto tempFile1RelativePath = RemoveRoot(dirToWatch, tempFile1.GetPath()); auto foundTempFile1 = watchedFiles.find(tempFile1RelativePath); REQUIRE(foundTempFile1 != watchedFiles.cend()); auto tempFile2RelativePath = RemoveRoot(dirToWatch, tempFile2.GetPath()); auto foundTempFile2 = watchedFiles.find(tempFile2RelativePath); REQUIRE(foundTempFile2 == watchedFiles.cend()); } TEST_CASE("FolderFileWatcher_Extension_CreateNewFilesAndRename", "[FolderFileWatcher]") { TempDirectory dirToWatch("FolderFileWatcher_Extension_CreateNewFilesAndRename", true); Utility::FolderFileWatcher folderFileExtensionWatcher(dirToWatch.GetPath(), ".txt"); folderFileExtensionWatcher.Start(); std::filesystem::path tempFile1Path = dirToWatch.GetPath() / "file1.txt"; TempFile tempFile1(tempFile1Path); WriteText(tempFile1Path); std::filesystem::path newTestDir = dirToWatch.GetPath(); newTestDir /= "testDir"; std::filesystem::create_directories(newTestDir); std::filesystem::path tempFile2Path = newTestDir / "file2.txt"; TempFile tempFile2(tempFile2Path); WriteText(tempFile2Path); std::filesystem::path tempFile1PathRenamed = dirToWatch.GetPath() / "file1_renamed.txt"; std::filesystem::path tempFile2PathRenamed = newTestDir / "file2_renamed.txt"; tempFile1.Rename(tempFile1PathRenamed); tempFile2.Rename(tempFile2PathRenamed); std::this_thread::sleep_for(100ms); folderFileExtensionWatcher.Stop(); auto& watchedFiles = folderFileExtensionWatcher.Files(); auto tempFile1RelativePath = RemoveRoot(dirToWatch, tempFile1Path); auto foundTempFile1 = watchedFiles.find(tempFile1RelativePath); REQUIRE(foundTempFile1 == watchedFiles.cend()); auto tempFile1RenamedRelativePath = RemoveRoot(dirToWatch, tempFile1PathRenamed); auto foundTempFile1Renamed = watchedFiles.find(tempFile1RenamedRelativePath); REQUIRE(foundTempFile1Renamed != watchedFiles.cend()); auto tempFile2RelativePath = RemoveRoot(dirToWatch, tempFile2Path); auto foundTempFile2 = watchedFiles.find(tempFile2RelativePath); REQUIRE(foundTempFile2 == watchedFiles.cend()); auto tempFile2RenamedRelativePath = RemoveRoot(dirToWatch, tempFile2PathRenamed); auto foundTempFile2Renamed = watchedFiles.find(tempFile2RenamedRelativePath); REQUIRE(foundTempFile2Renamed != watchedFiles.cend()); } TEST_CASE("FolderFileWatcher_Extension_CreateNewFilesAndDelete", "[FolderFileWatcher]") { TempDirectory dirToWatch("FolderFileWatcher_Extension_CreateNewFilesAndDelete", true); Utility::FolderFileWatcher folderFileExtensionWatcher(dirToWatch.GetPath(), ".txt"); folderFileExtensionWatcher.Start(); TempFile tempFile1(dirToWatch.GetPath(), "file1_", ".txt"); WriteText(tempFile1.GetPath()); std::filesystem::path newTestDir = dirToWatch.GetPath(); newTestDir /= "testDir"; std::filesystem::create_directories(newTestDir); TempFile tempFile2(newTestDir, "file2_", ".txt"); WriteText(tempFile2.GetPath()); // Create files and delete them. std::filesystem::path tempFile3Path; std::filesystem::path tempFile4Path; { TempFile tempFile3(dirToWatch.GetPath(), "file3_", ".txt"); tempFile3Path = tempFile3.GetPath(); WriteText(tempFile3Path); std::filesystem::remove(tempFile3Path); TempFile tempFile4(newTestDir, "file4_", ".txt"); tempFile4Path = tempFile4.GetPath(); WriteText(tempFile4Path); std::filesystem::remove(tempFile4Path); } std::this_thread::sleep_for(100ms); folderFileExtensionWatcher.Stop(); auto& watchedFiles = folderFileExtensionWatcher.Files(); auto tempFile1RelativePath = RemoveRoot(dirToWatch, tempFile1.GetPath()); auto foundTempFile1 = watchedFiles.find(tempFile1RelativePath); REQUIRE(foundTempFile1 != watchedFiles.cend()); auto tempFile2RelativePath = RemoveRoot(dirToWatch, tempFile2.GetPath()); auto foundTempFile2 = watchedFiles.find(tempFile2RelativePath); REQUIRE(foundTempFile2 != watchedFiles.cend()); auto tempFile3RelativePath = RemoveRoot(dirToWatch, tempFile3Path); auto foundTempFile3 = watchedFiles.find(tempFile3RelativePath); REQUIRE(foundTempFile3 == watchedFiles.cend()); auto tempFile4RelativePath = RemoveRoot(dirToWatch, tempFile4Path); auto foundTempFile4 = watchedFiles.find(tempFile4RelativePath); REQUIRE(foundTempFile4 == watchedFiles.cend()); } ================================================ FILE: src/AppInstallerCLITests/FontHelper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestHooks.h" #include "AppInstallerStrings.h" #include "Microsoft/FontHelper.h" using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Utility; using namespace AppInstaller::Registry; TEST_CASE("FontHelper_Watcher", "[FontHelper]") { FontHelper helper; wil::unique_event callbackEvent; callbackEvent.create(); ScopeEnum scopeCallback = ScopeEnum::Unknown; ScopeEnum scopeTarget = ScopeEnum::Machine; auto fakeRoot = TestCommon::RegCreateVolatileTestRoot(); TestHook::SetGetFontRegistryRoot_Override fontRootOverride([&](ScopeEnum scope) { if (scope == scopeTarget) { return Key(fakeRoot.get(), L""); } else { return Key{}; } }); auto watchers = std::vector(); helper.AddRegistryWatchers(scopeTarget, [&](ScopeEnum scope, wil::RegistryChangeKind) { scopeCallback = scope; callbackEvent.SetEvent(); }, watchers); GUID guid; std::ignore = CoCreateGuid(&guid); std::ostringstream stream; stream << guid; auto testKey = TestCommon::RegCreateVolatileSubKey(fakeRoot.get(), ConvertToUTF16(stream.str())); REQUIRE(callbackEvent.wait(1000)); REQUIRE(scopeTarget == scopeCallback); // Reset for changing a value scopeCallback = ScopeEnum::Unknown; TestCommon::SetRegistryValue(testKey.get(), L"testValue", L"valueValue"); REQUIRE(callbackEvent.wait(1000)); REQUIRE(scopeTarget == scopeCallback); } ================================================ FILE: src/AppInstallerCLITests/Fonts.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace AppInstaller::Fonts; using namespace AppInstaller::Manifest; using namespace TestCommon; constexpr std::wstring_view s_testFontName = L"Times New Roman"; constexpr std::string_view s_FontFile = "TestFont.ttf"; constexpr std::string_view s_FontFileTemp = "TestFont_temp.ttf"; constexpr std::string_view s_InvalidFontFile = "Installer-Good.msix"; TEST_CASE("GetInstalledFonts", "[fonts]") { FontCatalog fontCatalog; std::vector installedFontFamilies; REQUIRE_NOTHROW(installedFontFamilies = fontCatalog.GetInstalledFontFamilies()); REQUIRE(installedFontFamilies.size() > 0); } TEST_CASE("GetSingleFontFamily", "[fonts]") { FontCatalog fontCatalog; std::vector fontFamily; REQUIRE_NOTHROW(fontFamily = fontCatalog.GetInstalledFontFamilies(std::wstring(s_testFontName))); REQUIRE_FALSE(fontFamily.empty()); FontFamily singleFontFamily = fontFamily[0]; REQUIRE(AppInstaller::Utility::CaseInsensitiveEquals(singleFontFamily.Name, s_testFontName)); REQUIRE(singleFontFamily.Faces.size() > 0); } TEST_CASE("GetInvalidFontFamily", "[fonts]") { FontCatalog fontCatalog; std::vector fontFamily; REQUIRE_NOTHROW(fontFamily = fontCatalog.GetInstalledFontFamilies(L"Invalid Font")); REQUIRE(fontFamily.empty()); } TEST_CASE("ValidFontFile", "[fonts]") { TestDataFile testFont(s_FontFile); const auto& testFontPath = testFont.GetPath(); FontCatalog fontCatalog; DWRITE_FONT_FILE_TYPE fontFileType; REQUIRE(fontCatalog.IsFontFileSupported(testFontPath, fontFileType)); REQUIRE(fontFileType == DWRITE_FONT_FILE_TYPE::DWRITE_FONT_FILE_TYPE_TRUETYPE); } TEST_CASE("GetFontFileInfo", "[fonts]") { TestDataFile testFont(s_FontFile); auto context = FontContext(); context.Scope = ScopeEnum::User; context.PackageId = L"TestPackage"; context.PackageVersion = L"1.0.0.0"; context.InstallerSource = InstallerSource::WinGet; const auto& fontFileInfo = CreateFontFileInfo(context, testFont.GetPath()); REQUIRE(fontFileInfo.Status == FontStatus::Absent); } TEST_CASE("InvalidFontFile", "[fonts]") { TestDataFile testFont(s_InvalidFontFile); const auto& testFontPath = testFont.GetPath(); FontCatalog fontCatalog; DWRITE_FONT_FILE_TYPE fontFileType; REQUIRE_FALSE(fontCatalog.IsFontFileSupported(testFontPath, fontFileType)); REQUIRE(fontFileType == DWRITE_FONT_FILE_TYPE::DWRITE_FONT_FILE_TYPE_UNKNOWN); } TEST_CASE("GetInstalledFontFiles", "[fonts]") { const auto& fontFiles = GetInstalledFontFiles(); // There should be at least one font installed on the system. REQUIRE(fontFiles.size() > 0); } TEST_CASE("ValidateInvalidFontPackage", "[fonts]") { TestDataFile testFont(s_InvalidFontFile); auto context = FontContext(); context.Scope = ScopeEnum::User; context.PackageId = L"TestPackage"; context.PackageVersion = L"1.0.0.0"; context.InstallerSource = InstallerSource::WinGet; context.AddPackageFile(testFont.GetPath()); const auto& fontValidationResult = ValidateFontPackage(context); REQUIRE(fontValidationResult.HResult == S_OK); REQUIRE(fontValidationResult.Result == FontResult::Success); REQUIRE(fontValidationResult.HasUnsupportedFonts == true); REQUIRE(fontValidationResult.Status == FontStatus::Absent); } TEST_CASE("ValidateValidFontPackage", "[fonts]") { TestDataFile testFont(s_FontFile); auto context = FontContext(); context.Scope = ScopeEnum::User; context.PackageId = L"TestPackage"; context.PackageVersion = L"1.0.0.0"; context.InstallerSource = InstallerSource::WinGet; context.AddPackageFile(testFont.GetPath()); const auto& fontValidationResult = ValidateFontPackage(context); REQUIRE(fontValidationResult.HResult == S_OK); REQUIRE(fontValidationResult.Result == FontResult::Success); REQUIRE(fontValidationResult.HasUnsupportedFonts == false); REQUIRE(fontValidationResult.Status == FontStatus::Absent); } TEST_CASE("InstallInvalidFontPackageUser", "[fonts]") { TestDataFile testFont(s_InvalidFontFile); auto context = FontContext(); context.PackageId = L"TestPackage"; context.PackageVersion = L"1.0.0.0"; context.InstallerSource = InstallerSource::WinGet; context.Scope = ScopeEnum::User; context.AddPackageFile(testFont.GetPath()); const auto& result = InstallFontPackage(context); REQUIRE(result.HResult == APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_SUPPORTED); } TEST_CASE("RemoveFontPackageUser", "[fonts]") { TestDataFile testFont(s_FontFile); // Calling remove should always be successful when font doesn't exist. auto context = FontContext(); context.PackageId = L"TestPackage"; context.PackageVersion = L"1.0.0.0"; context.InstallerSource = InstallerSource::WinGet; context.Scope = ScopeEnum::User; context.AddPackageFile(testFont.GetPath()); const auto& result = UninstallFontPackage(context); REQUIRE(result.HResult == S_OK); auto fontValidationResult = ValidateFontPackage(context); REQUIRE(fontValidationResult.Status == FontStatus::Absent); } TEST_CASE("InstallValidFontPackageUser", "[fonts]") { // The test will move the file, so for idempotency we need // to use a file that can be replaced. TestDataFile testFontBase(s_FontFile); TestCommon::TempDirectory tempDirectory("TempDirectory"); std::filesystem::path testFontCopyPath = tempDirectory.GetPath() / s_FontFileTemp; if (!std::filesystem::exists(testFontCopyPath)) { std::filesystem::copy(testFontBase.GetPath(), testFontCopyPath); } // Use the copied font for the test TestDataFile testFont(testFontCopyPath); auto context = FontContext(); context.PackageId = L"TestPackage"; context.PackageVersion = L"1.0.0.0"; context.InstallerSource = InstallerSource::WinGet; context.Scope = ScopeEnum::User; context.Force = true; context.AddPackageFile(testFont.GetPath()); auto preinstallValidation = ValidateFontPackage(context); REQUIRE(preinstallValidation.Status == FontStatus::Absent); auto installResult = InstallFontPackage(context); REQUIRE(installResult.HResult == S_OK); auto postinstallValidation = ValidateFontPackage(context); REQUIRE(postinstallValidation.Status == FontStatus::OK); auto uninstallResult = UninstallFontPackage(context); REQUIRE(uninstallResult.HResult == S_OK); auto postuninstallValidation = ValidateFontPackage(context); REQUIRE(postuninstallValidation.Status == FontStatus::Absent); } TEST_CASE("RemoveFontPackageMachine", "[fonts]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestDataFile testFont(s_FontFile); // Calling remove should always be successful when font doesn't exist. auto context = FontContext(); context.PackageId = L"TestPackage"; context.PackageVersion = L"1.0.0.0"; context.InstallerSource = InstallerSource::WinGet; context.Scope = ScopeEnum::Machine; context.AddPackageFile(testFont.GetPath()); const auto& result = UninstallFontPackage(context); REQUIRE(result.HResult == S_OK); auto fontValidationResult = ValidateFontPackage(context); REQUIRE(fontValidationResult.Status == FontStatus::Absent); } TEST_CASE("InstallValidFontPackageMachine", "[fonts]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } // The test will move the file, so for idempotency we need // to use a file that can be replaced. TestDataFile testFontBase(s_FontFile); TestCommon::TempDirectory tempDirectory("TempDirectory"); std::filesystem::path testFontCopyPath = tempDirectory.GetPath() / s_FontFileTemp; if (!std::filesystem::exists(testFontCopyPath)) { std::filesystem::copy(testFontBase.GetPath(), testFontCopyPath); } // Use the copied font for the test TestDataFile testFont(testFontCopyPath); auto context = FontContext(); context.PackageId = L"TestPackage"; context.PackageVersion = L"1.0.0.0"; context.InstallerSource = InstallerSource::WinGet; context.Scope = ScopeEnum::Machine; context.Force = true; context.AddPackageFile(testFont.GetPath()); auto preinstallValidation = ValidateFontPackage(context); REQUIRE(preinstallValidation.Status == FontStatus::Absent); auto installResult = InstallFontPackage(context); REQUIRE(installResult.HResult == S_OK); auto postinstallValidation = ValidateFontPackage(context); REQUIRE(postinstallValidation.Status == FontStatus::OK); auto uninstallResult = UninstallFontPackage(context); REQUIRE(uninstallResult.HResult == S_OK); auto postuninstallValidation = ValidateFontPackage(context); REQUIRE(postuninstallValidation.Status == FontStatus::Absent); } ================================================ FILE: src/AppInstallerCLITests/GroupPolicy.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSettings.h" #include "winget/GroupPolicy.h" #include #include using namespace TestCommon; using namespace AppInstaller::Settings; using namespace std::string_view_literals; namespace { std::wstring GetSourceJson(std::wstring_view name, std::wstring_view arg, std::wstring_view type, std::wstring_view data, std::wstring_view identifier, std::wstring_view trustLevel, std::wstring_view isExplicit, std::wstring_view pinningConfig = {}) { std::wstringstream json; json << L"{ \"Name\":\"" << name << L"\", \"Arg\":\"" << arg << L"\", \"Type\":\"" << type << L"\", \"Data\":\"" << data << L"\", \"Identifier\":\"" << identifier << L"\", \"TrustLevel\":" << trustLevel << L", \"Explicit\":" << isExplicit; if (!pinningConfig.empty()) { json << L", \"CertificatePinning\":" << pinningConfig; } json << " }"; return json.str(); } } TEST_CASE("GroupPolicy_NoPolicies", "[groupPolicy]") { auto policiesKey = RegCreateVolatileTestRoot(); GroupPolicy groupPolicy{ policiesKey.get() }; // Policies setting a value should be empty REQUIRE(!groupPolicy.GetValue().has_value()); REQUIRE(!groupPolicy.GetValue().has_value()); REQUIRE(!groupPolicy.GetValue().has_value()); // Everything should be not configured for (const auto& policy : TogglePolicy::GetAllPolicies()) { REQUIRE(groupPolicy.GetState(policy.GetPolicy()) == PolicyState::NotConfigured); } } TEST_CASE("GroupPolicy_UpdateInterval", "[groupPolicy]") { auto policiesKey = RegCreateVolatileTestRoot(); SECTION("Good value") { SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyValueName, 5); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(*policy == 5); } SECTION("Wrong type") { SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyValueName, L"Wrong"); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(!policy.has_value()); } } TEST_CASE("GroupPolicy_UpdateInterval_OldName", "[groupPolicy]") { auto policiesKey = RegCreateVolatileTestRoot(); SECTION("New name shadows old") { SECTION("When old is valid") { SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyOldValueName, 3); } SECTION("When old is invalid") { SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyOldValueName, L"Invalid type"); } SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyValueName, 1); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(*policy == 1); } SECTION("Fallback to old name") { SECTION("When new name has invalid data") { SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyValueName, L"Wrong type"); SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyOldValueName, 20); GroupPolicy groupPolicy{ policiesKey.get() }; // We should not fall back on this case auto policy = groupPolicy.GetValue(); REQUIRE(!policy.has_value()); } SECTION("When new name is missing") { // Don't add the registry value with the new name SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyOldValueName, 20); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(*policy == 20); } } } TEST_CASE("GroupPolicy_Sources", "[groupPolicy]") { auto policiesKey = RegCreateVolatileTestRoot(); // Note that the following tests mix using Additional/Allowed sources policy. SECTION("Single source") { // We can read single source correctly auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AdditionalSourcesPolicyKeyName); SetRegistryValue(additionalSourcesKey.get(), L"0", GetSourceJson(L"source-name", L"source-arg", L"source-type", L"source-data", L"source-identifier", L"[\"Trusted\", \"StoreOrigin\"]", L"true"), REG_SZ); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->size() == 1); REQUIRE(policy.value()[0].Name == "source-name"); REQUIRE(policy.value()[0].Arg == "source-arg"); REQUIRE(policy.value()[0].Type == "source-type"); REQUIRE(policy.value()[0].Data == "source-data"); REQUIRE(policy.value()[0].Identifier == "source-identifier"); REQUIRE(policy.value()[0].TrustLevel[0] == "Trusted"); REQUIRE(policy.value()[0].TrustLevel[1] == "StoreOrigin"); REQUIRE(policy.value()[0].Explicit == true); } SECTION("Missing field") { // A single missing field causes the source to not be read. // "Type" is missing here. std::wstring sourceJson = L"{ \"Name\":\"source_name\", \"Arg\":\"source_arg\", \"Data\":\"source_data\", \"Identifier\":\"source_identifier\" }"; auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AllowedSourcesPolicyKeyName); SetRegistryValue(additionalSourcesKey.get(), L"0", sourceJson, REG_SZ); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->empty()); } SECTION("Invalid field") { // A single invalid field causes the source to not be read. // "Data" is invalid as it is an object, not a string. std::wstring sourceJson = L"{ \"Name\":\"source_name\", \"Arg\":\"source_arg\", \"Data\":{}, \"Type\":\"source_type\", \"Identifier\":\"source_identifier\" }"; auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AdditionalSourcesPolicyKeyName); SetRegistryValue(additionalSourcesKey.get(), L"0", sourceJson, REG_SZ); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->empty()); } SECTION("Invalid source JSON") { // An invalid source JSON causes the source to not be read. auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AllowedSourcesPolicyKeyName); SetRegistryValue(additionalSourcesKey.get(), L"0", L"not a JSON", REG_SZ); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->empty()); } SECTION("Missing key") { // If the key does not exist we should not get anything. GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE_FALSE(policy.has_value()); } SECTION("Empty key") { // If the key is empty we should get an empty list. // Note that the policy editor doesn't actually create empty keys. auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AllowedSourcesPolicyKeyName); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->empty()); } SECTION("Valid list") { // We should be able to read multiple values. // No specific order is required, but it will likely be the same. auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AdditionalSourcesPolicyKeyName); SetRegistryValue(additionalSourcesKey.get(), L"0", GetSourceJson(L"s0-name", L"s0-arg", L"s0-type", L"s0-data", L"s0-identifier", L"[\"None\"]", L"true"), REG_SZ); SetRegistryValue(additionalSourcesKey.get(), L"1", GetSourceJson(L"s1-name", L"s1-arg", L"s1-type", L"s1-data", L"s1-identifier", L"[\"Trusted\", \"StoreOrigin\"]", L"false"), REG_SZ); SetRegistryValue(additionalSourcesKey.get(), L"2", GetSourceJson(L"s2-name", L"s2-arg", L"s2-type", L"s2-data", L"s2-identifier", L"[\"StoreOrigin\", \"Trusted\"]", L"true"), REG_SZ); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->size() == 3); REQUIRE(policy.value()[0].Name == "s0-name"); REQUIRE(policy.value()[0].Arg == "s0-arg"); REQUIRE(policy.value()[0].Type == "s0-type"); REQUIRE(policy.value()[0].Data == "s0-data"); REQUIRE(policy.value()[0].Identifier == "s0-identifier"); REQUIRE(policy.value()[0].TrustLevel[0] == "None"); REQUIRE(policy.value()[0].Explicit == true); REQUIRE(policy.value()[1].Name == "s1-name"); REQUIRE(policy.value()[1].Arg == "s1-arg"); REQUIRE(policy.value()[1].Type == "s1-type"); REQUIRE(policy.value()[1].Data == "s1-data"); REQUIRE(policy.value()[1].Identifier == "s1-identifier"); REQUIRE(policy.value()[1].TrustLevel[0] == "Trusted"); REQUIRE(policy.value()[1].TrustLevel[1] == "StoreOrigin"); REQUIRE(policy.value()[1].Explicit == false); REQUIRE(policy.value()[2].Name == "s2-name"); REQUIRE(policy.value()[2].Arg == "s2-arg"); REQUIRE(policy.value()[2].Type == "s2-type"); REQUIRE(policy.value()[2].Data == "s2-data"); REQUIRE(policy.value()[2].Identifier == "s2-identifier"); REQUIRE(policy.value()[2].TrustLevel[0] == "StoreOrigin"); REQUIRE(policy.value()[2].TrustLevel[1] == "Trusted"); REQUIRE(policy.value()[2].Explicit == true); } SECTION("Invalid source in list") { // If a single source is invalid we should still get all others auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AdditionalSourcesPolicyKeyName); SetRegistryValue(additionalSourcesKey.get(), L"0", GetSourceJson(L"s0-name", L"s0-arg", L"s0-type", L"s0-data", L"s0-identifier", L"[\"Trusted\", \"StoreOrigin\"]", L"false"), REG_SZ); SetRegistryValue(additionalSourcesKey.get(), L"1", L"not a source", REG_SZ); SetRegistryValue(additionalSourcesKey.get(), L"2", GetSourceJson(L"s2-name", L"s2-arg", L"s2-type", L"s2-data", L"s2-identifier", L"[\"StoreOrigin\", \"Trusted\"]", L"true"), REG_SZ); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->size() == 2); REQUIRE(policy.value()[0].Name == "s0-name"); REQUIRE(policy.value()[0].Arg == "s0-arg"); REQUIRE(policy.value()[0].Type == "s0-type"); REQUIRE(policy.value()[0].Data == "s0-data"); REQUIRE(policy.value()[0].Identifier == "s0-identifier"); REQUIRE(policy.value()[0].TrustLevel[0] == "Trusted"); REQUIRE(policy.value()[0].TrustLevel[1] == "StoreOrigin"); REQUIRE(policy.value()[0].Explicit == false); REQUIRE(policy.value()[1].Name == "s2-name"); REQUIRE(policy.value()[1].Arg == "s2-arg"); REQUIRE(policy.value()[1].Type == "s2-type"); REQUIRE(policy.value()[1].Data == "s2-data"); REQUIRE(policy.value()[1].Identifier == "s2-identifier"); REQUIRE(policy.value()[1].TrustLevel[0] == "StoreOrigin"); REQUIRE(policy.value()[1].TrustLevel[1] == "Trusted"); REQUIRE(policy.value()[1].Explicit == true); } SECTION("Exported JSON") { // Policy should be able to use an exported JSON strings SourceFromPolicy source; source.Name = "json-name"; source.Type = "json-type"; source.Arg = "json-arg"; source.Data = "json-data"; source.Identifier = "json-id"; source.TrustLevel = {"Trusted", "StoreOrigin"}; source.Explicit = false; auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AllowedSourcesPolicyKeyName); SetRegistryValue(additionalSourcesKey.get(), L"0", AppInstaller::Utility::ConvertToUTF16(source.ToJsonString())); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->size() == 1); REQUIRE(policy.value()[0].Name == source.Name); REQUIRE(policy.value()[0].Arg == source.Arg); REQUIRE(policy.value()[0].Type == source.Type); REQUIRE(policy.value()[0].Data == source.Data); REQUIRE(policy.value()[0].Identifier == source.Identifier); REQUIRE(policy.value()[0].TrustLevel[0] == source.TrustLevel[0]); // Trusted REQUIRE(policy.value()[0].TrustLevel[1] == source.TrustLevel[1]); // StoreOrigin REQUIRE(policy.value()[0].Explicit == source.Explicit); } SECTION("Source with PinningConfiguration") { using namespace AppInstaller::Certificates; auto additionalSourcesKey = RegCreateVolatileSubKey(policiesKey.get(), AdditionalSourcesPolicyKeyName); PinningDetails rootCert; rootCert.LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE); PinningDetails intermediateCert; intermediateCert.LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE); PinningDetails leafCert; leafCert.LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE); auto getBytesString = [](const PinningDetails& details) { std::vector bytes; bytes.assign(details.GetCertificate()->pbCertEncoded, details.GetCertificate()->pbCertEncoded + details.GetCertificate()->cbCertEncoded); return AppInstaller::Utility::ConvertToUTF16(AppInstaller::Utility::ConvertToHexString(bytes)); }; std::wostringstream pinningConfig; pinningConfig << LR"({ "Chains": [{ "Chain":[ { "Validation": ["publickey"], "EmbeddedCertificate": ")" << getBytesString(rootCert) << LR"(" }, { "Validation": ["subject","issuer"], "EmbeddedCertificate": ")" << getBytesString(intermediateCert) << LR"(" }, { "Validation": ["subject","issuer"], "EmbeddedCertificate": ")" << getBytesString(leafCert) << LR"(" } ] }] })"; SetRegistryValue(additionalSourcesKey.get(), L"0", GetSourceJson(L"source-name", L"source-arg", L"source-type", L"source-data", L"source-identifier", L"[\"Trusted\", \"StoreOrigin\"]", L"true", pinningConfig.str()), REG_SZ); GroupPolicy groupPolicy{ policiesKey.get() }; auto policy = groupPolicy.GetValue(); REQUIRE(policy.has_value()); REQUIRE(policy->size() == 1); const auto& sourceInfo = policy.value()[0]; REQUIRE(sourceInfo.Name == "source-name"); REQUIRE(sourceInfo.Arg == "source-arg"); REQUIRE(sourceInfo.Type == "source-type"); REQUIRE(sourceInfo.Data == "source-data"); REQUIRE(sourceInfo.Identifier == "source-identifier"); REQUIRE(sourceInfo.TrustLevel[0] == "Trusted"); REQUIRE(sourceInfo.TrustLevel[1] == "StoreOrigin"); REQUIRE(sourceInfo.Explicit == true); // Use loaded pinning config and validate against leaf certificate REQUIRE(!sourceInfo.PinningConfiguration.IsEmpty()); REQUIRE(sourceInfo.PinningConfiguration.Validate(leafCert.GetCertificate())); } } TEST_CASE("GroupPolicy_Toggle", "[groupPolicy]") { auto policiesKey = RegCreateVolatileTestRoot(); SECTION("'None' is not configured") { GroupPolicy groupPolicy{ policiesKey.get() }; REQUIRE(groupPolicy.GetState(TogglePolicy::Policy::None) == PolicyState::NotConfigured); REQUIRE(groupPolicy.IsEnabled(TogglePolicy::Policy::None)); } SECTION("Enabled") { SetRegistryValue(policiesKey.get(), WinGetPolicyValueName, 1); GroupPolicy groupPolicy{ policiesKey.get() }; REQUIRE(groupPolicy.GetState(TogglePolicy::Policy::WinGet) == PolicyState::Enabled); REQUIRE(groupPolicy.IsEnabled(TogglePolicy::Policy::WinGet)); } SECTION("Disabled") { SetRegistryValue(policiesKey.get(), LocalManifestsPolicyValueName, 0); GroupPolicy groupPolicy{ policiesKey.get() }; REQUIRE(groupPolicy.GetState(TogglePolicy::Policy::LocalManifestFiles) == PolicyState::Disabled); REQUIRE_FALSE(groupPolicy.IsEnabled(TogglePolicy::Policy::LocalManifestFiles)); } SECTION("Wrong type") { SetRegistryValue(policiesKey.get(), ExperimentalFeaturesPolicyValueName, L"Wrong"); GroupPolicy groupPolicy{ policiesKey.get() }; REQUIRE(groupPolicy.GetState(TogglePolicy::Policy::DefaultSource) == PolicyState::NotConfigured); REQUIRE(groupPolicy.IsEnabled(TogglePolicy::Policy::DefaultSource)); } } TEST_CASE("GroupPolicy_AllEnabled", "[groupPolicy]") { auto policiesKey = RegCreateVolatileTestRoot(); SetRegistryValue(policiesKey.get(), WinGetPolicyValueName, 1); SetRegistryValue(policiesKey.get(), WinGetSettingsPolicyValueName, 1); SetRegistryValue(policiesKey.get(), ExperimentalFeaturesPolicyValueName, 1); SetRegistryValue(policiesKey.get(), LocalManifestsPolicyValueName, 1); SetRegistryValue(policiesKey.get(), EnableHashOverridePolicyValueName, 1); SetRegistryValue(policiesKey.get(), EnableLocalArchiveMalwareScanOverridePolicyValueName, 1); SetRegistryValue(policiesKey.get(), DefaultSourcePolicyValueName, 1); SetRegistryValue(policiesKey.get(), MSStoreSourcePolicyValueName, 1); SetRegistryValue(policiesKey.get(), FontSourcePolicyValueName, 1); SetRegistryValue(policiesKey.get(), AdditionalSourcesPolicyValueName, 1); SetRegistryValue(policiesKey.get(), AllowedSourcesPolicyValueName, 1); SetRegistryValue(policiesKey.get(), BypassCertificatePinningForMicrosoftStoreValueName, 1); SetRegistryValue(policiesKey.get(), EnableWindowsPackageManagerCommandLineInterfaces, 1); SetRegistryValue(policiesKey.get(), ConfigurationPolicyValueName, 1); SetRegistryValue(policiesKey.get(), ProxyCommandLineOptionsPolicyValueName, 1); SetRegistryValue(policiesKey.get(), McpServerValueName, 1); GroupPolicy groupPolicy{ policiesKey.get() }; for (const auto& policy : TogglePolicy::GetAllPolicies()) { REQUIRE(groupPolicy.GetState(policy.GetPolicy()) == PolicyState::Enabled); } } ================================================ FILE: src/AppInstallerCLITests/HashCommand.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "Commands/HashCommand.h" using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::CLI; TEST_CASE("HashCommandWithTestMsix", "[Sha256Hash]") { std::ostringstream hashOutput; Execution::Context context{ hashOutput, std::cin }; context.Args.AddArg(Execution::Args::Type::HashFile, TestDataFile("TestSignedApp.msix").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Msix); HashCommand hashCommand({}); hashCommand.Execute(context); REQUIRE(hashOutput.str().find("Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea") != std::string::npos); REQUIRE(hashOutput.str().find("SignatureSha256: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07") != std::string::npos); } ================================================ FILE: src/AppInstallerCLITests/HttpClientHelper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include #include #include #include using namespace AppInstaller::Http; using namespace AppInstaller::Runtime; using namespace AppInstaller::Utility; using namespace AppInstaller::Certificates; TEST_CASE("ExtractJsonResponse_UnsupportedMimeType", "[RestSource][RestSearch]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, L"", web::http::details::mime_types::text_plain) }; REQUIRE_THROWS_HR(helper.HandleGet(L"https://testUri"), APPINSTALLER_CLI_ERROR_RESTAPI_UNSUPPORTED_MIME_TYPE); } TEST_CASE("ValidateAndExtractResponse_ServiceUnavailable", "[RestSource]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::ServiceUnavailable) }; REQUIRE_THROWS_HR(helper.HandleGet(L"https://testUri"), APPINSTALLER_CLI_ERROR_SERVICE_UNAVAILABLE); } TEST_CASE("ValidateAndExtractResponse_NotFound", "[RestSource]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound) }; REQUIRE_THROWS_HR(helper.HandleGet(L"https://testUri"), APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND); } TEST_CASE("EnsureDefaultUserAgent", "[RestSource]") { HttpClientHelper helper{ GetTestRestRequestHandler([](const web::http::http_request& request) { auto itr = request.headers().find(web::http::header_names::user_agent); if (itr != request.headers().end() && itr->second.find(ConvertToUTF16(GetClientVersion())) != utility::string_t::npos && itr->second.find(ConvertToUTF16(GetPackageVersion())) != utility::string_t::npos) { return web::http::status_codes::OK; } else { return web::http::status_codes::BadRequest; } }) }; SECTION("GET") { REQUIRE_NOTHROW(helper.HandleGet(L"https://testUri")); } SECTION("POST") { REQUIRE_NOTHROW(helper.HandlePost(L"https://testUri", {})); } } TEST_CASE("HttpClientHelper_PinningConfiguration", "[RestSource]") { // Create the Store chain config PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); PinningConfiguration config; config.AddChain(chain); HttpClientHelper helper; helper.SetPinningConfiguration(config); REQUIRE_THROWS_HR(helper.HandleGet(L"https://github.com"), APPINSTALLER_CLI_ERROR_PINNED_CERTIFICATE_MISMATCH); } TEST_CASE("HttpClientHelper_CallerCharacters", "[RestSource]") { HttpClientHelper::HttpRequestHeaders headers; headers.emplace(web::http::header_names::user_agent, AppInstaller::JSON::GetUtilityString(AppInstaller::Runtime::GetUserAgent("\xe6\xb5\x8b\xe8\xaf\x95"))); HttpClientHelper helper; REQUIRE_THROWS_HR(helper.HandleGet(L"https://github.com", headers), APPINSTALLER_CLI_ERROR_RESTAPI_UNSUPPORTED_MIME_TYPE); } ================================================ FILE: src/AppInstallerCLITests/IconExtraction.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace AppInstaller::Repository; TEST_CASE("ExtractIconFromBinaryFile", "IconExtraction") { auto extracted = ExtractIconFromBinaryFile(TestCommon::TestDataFile{ "notepad.exe" }.GetPath()); std::ifstream expectedIconFile{ TestCommon::TestDataFile{ "notepad.ico" }.GetPath(), std::ios::in | std::ios::binary}; auto expected = AppInstaller::Utility::ReadEntireStreamAsByteArray(expectedIconFile); REQUIRE(expected == extracted); } ================================================ FILE: src/AppInstallerCLITests/ImportFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestHooks.h" #include "WorkflowCommon.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility::literals; void OverrideForImportSource(TestContext& context, bool useTestCompositeSource = false) { auto testCompositeSource = CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Exe_Dependencies, TSR::TestInstaller_Exe_LicenseAgreement, TSR::TestInstaller_Exe_NothingInstalled, TSR::TestInstaller_Msix, TSR::TestInstaller_Msix_WFDependency, }); context.Override({ "OpenPredefinedSource", [=](TestContext& context) { auto installedSource = useTestCompositeSource ? testCompositeSource : std::make_shared(); context.Add(Source{ installedSource }); } }); context.Override({ Workflow::OpenSourcesForImport, [=](TestContext& context) { context.Add(std::vector{ Source{ testCompositeSource } }); } }); } TEST_CASE("ImportFlow_Successful", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); TestCommon::TempFile msixInstallResultPath("TestMsixInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForMSIX(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Verify all packages were installed REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); REQUIRE(std::filesystem::exists(msixInstallResultPath.GetPath())); } TEST_CASE("ImportFlow_PackageAlreadyInstalled", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context, true); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-AlreadyInstalled.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Exe should not have been installed again REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::MultiQueryPackageAlreadyInstalled("AppInstallerCliTest.TestExeInstaller"_liv)).get()) != std::string::npos); } TEST_CASE("ImportFlow_IgnoreVersions", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-AlreadyInstalled.json").GetPath().string()); context.Args.AddArg(Execution::Args::Type::IgnoreVersions); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Specified version is already installed. It should have been updated since we ignored the version. REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); } TEST_CASE("ImportFlow_MissingSource", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-UnknownSource.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSourceNotInstalled("TestSource"_liv)).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } TEST_CASE("ImportFlow_MissingPackage", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-UnknownPackage.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::MultiQueryPackageNotFound("MissingPackage"_liv)).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE); } TEST_CASE("ImportFlow_IgnoreMissingPackage", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-UnknownPackage.json").GetPath().string()); context.Args.AddArg(Execution::Args::Type::IgnoreUnavailable); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Verify installer was called for the package that was available. REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::MultiQueryPackageNotFound("MissingPackage"_liv)).get()) != std::string::npos); } TEST_CASE("ImportFlow_MissingVersion", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-UnknownPackageVersion.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::MultiQuerySearchFailed("AppInstallerCliTest.TestExeInstaller"_liv)).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE); } TEST_CASE("ImportFlow_MalformedJsonFile", "[ImportFlow][workflow]") { std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-Malformed.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Command should have failed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE); } TEST_CASE("ImportFlow_InvalidJsonFile", "[ImportFlow][workflow]") { std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Bad-Invalid.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Command should have failed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE); } TEST_CASE("ImportFlow_MachineScope", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-MachineScope.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Verify all packages were installed REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); std::ifstream installResultFile(exeInstallResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/scope=machine") != std::string::npos); } TEST_CASE("ImportFlow_Dependencies", "[ImportFlow][workflow][dependencies]") { std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForMSIX(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-Dependencies.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Verify dependencies for all packages are informed REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportCommandReportDependencies).get()) != std::string::npos); REQUIRE(importOutput.str().find("PreviewIIS") != std::string::npos); REQUIRE(importOutput.str().find("Preview VC Runtime") != std::string::npos); REQUIRE(importOutput.str().find("Hyper-V") != std::string::npos); } TEST_CASE("ImportFlow_LicenseAgreement", "[ImportFlow][workflow]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); std::ostringstream importOutput; TestContext context{ importOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-WithLicenseAgreement.json").GetPath().string()); context.Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Verify agreements are shown REQUIRE(importOutput.str().find("Agreement for EXE") != std::string::npos); REQUIRE(importOutput.str().find("This is the agreement for the EXE") != std::string::npos); // Verify all packages were installed REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); } TEST_CASE("ImportFlow_LicenseAgreement_NotAccepted", "[ImportFlow][workflow]") { // Say "No" at the agreements prompt std::istringstream importInput{ "n" }; std::ostringstream importOutput; TestContext context{ importOutput, importInput }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForImportSource(context); context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-WithLicenseAgreement.json").GetPath().string()); ImportCommand importCommand({}); importCommand.Execute(context); INFO(importOutput.str()); // Verify agreements are shown REQUIRE(importOutput.str().find("Agreement for EXE") != std::string::npos); REQUIRE(importOutput.str().find("This is the agreement for the EXE") != std::string::npos); // Command should have failed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED); } ================================================ FILE: src/AppInstallerCLITests/InstallDependenciesFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include "DependenciesTestSource.h" #include #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Repository; void OverrideOpenSourceForDependencies(TestContext& context) { context.Override({ "OpenSource", [](TestContext& context) { context.Add(Source{ std::make_shared() }); } }); context.Override({ Workflow::OpenDependencySource, [](TestContext& context) { context.Add(Source{ std::make_shared() }); } }); } void OverrideForProcessMultiplePackages(TestContext& context) { context.Override({ ProcessMultiplePackages( Resource::String::PackageRequiresDependencies, APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES, ProcessMultiplePackages::Flags::SkipPackageAgreements | ProcessMultiplePackages::Flags::IgnoreDependencies), [](TestContext&) { } }); } void OverrideShellExecute(TestContext& context, std::vector& installationOrder) { context.Override({ ShellExecuteInstallImpl, [&installationOrder](TestContext& c) { installationOrder.push_back(c.Get().Id); c.Add(0); } }); } TEST_CASE("DependencyGraph_SkipInstalled", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); Manifest manifest = CreateFakeManifestWithDependencies("DependenciesInstalled"); OverrideOpenDependencySource(context); context.Add(Source{ std::make_shared() }); context.Add(manifest); context.Add(manifest.Installers[0]); context << CreateDependencySubContexts(Resource::String::PackageRequiresDependencies); auto& dependencyPackages = context.Get(); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); REQUIRE(dependencyPackages.size() == 0); } TEST_CASE("DependencyGraph_validMinVersions", "[InstallFlow][workflow][dependencyGraph][dependencies]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); Manifest manifest = CreateFakeManifestWithDependencies("DependenciesValidMinVersions"); OverrideOpenDependencySource(context); context.Add(Source{ std::make_shared() }); context.Add(manifest); context.Add(manifest.Installers[0]); context << CreateDependencySubContexts(Resource::String::PackageRequiresDependencies); auto& dependencyPackages = context.Get(); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); REQUIRE(dependencyPackages.size() == 1); REQUIRE(dependencyPackages.at(0)->Get().Id == "minVersion"); // minVersion 1.5 is available but this requires 1.0 so that version is installed REQUIRE(dependencyPackages.at(0)->Get().Version == "1.0"); } TEST_CASE("DependencyGraph_PathNoLoop", "[InstallFlow][workflow][dependencyGraph][dependencies]", ) { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); Manifest manifest = CreateFakeManifestWithDependencies("PathBetweenBranchesButNoLoop"); OverrideOpenDependencySource(context); context.Add(Source{ std::make_shared() }); context.Add(manifest); context.Add(manifest.Installers[0]); context << CreateDependencySubContexts(Resource::String::PackageRequiresDependencies); auto& dependencyPackages = context.Get(); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); // Verify installers are called in order REQUIRE(dependencyPackages.size() == 4); REQUIRE(dependencyPackages.at(0)->Get().Id == "B"); REQUIRE(dependencyPackages.at(1)->Get().Id == "C"); REQUIRE(dependencyPackages.at(2)->Get().Id == "G"); REQUIRE(dependencyPackages.at(3)->Get().Id == "H"); } TEST_CASE("DependencyGraph_StackOrderIsOk", "[InstallFlow][workflow][dependencyGraph][dependencies]") { std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context, installationOrder); context.Args.AddArg(Execution::Args::Type::Query, "StackOrderIsOk"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); // Verify installers are called in order REQUIRE(installationOrder.size() == 3); REQUIRE(installationOrder.at(0).Id() == "B"); REQUIRE(installationOrder.at(1).Id() == "C"); REQUIRE(installationOrder.at(2).Id() == "StackOrderIsOk"); } TEST_CASE("DependencyGraph_MultipleDependenciesFromManifest", "[InstallFlow][workflow][dependencyGraph][dependencies]") { std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideOpenSourceForDependencies(context); OverrideForShellExecute(context, installationOrder); OverrideEnableWindowsFeaturesDependencies(context); context.Args.AddArg(Execution::Args::Type::Query, "MultipleDependenciesFromManifest"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowContainsLoop)) == std::string::npos); // Verify installers are called in order REQUIRE(installationOrder.size() == 3); REQUIRE(installationOrder.at(0).Id() == "Dependency1"); REQUIRE(installationOrder.at(1).Id() == "Dependency2"); REQUIRE(installationOrder.at(2).Id() == "AppInstallerCliTest.TestExeInstaller.MultipleDependencies"); } TEST_CASE("InstallerWithoutDependencies_RootDependenciesAreUsed", "[dependencies]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideOpenDependencySource(context); OverrideEnableWindowsFeaturesDependencies(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesOnRoot.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify root dependencies are shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageRequiresDependencies).get()) != std::string::npos); REQUIRE(installOutput.str().find("PreviewIISOnRoot") != std::string::npos); } TEST_CASE("InstallerWithDependencies_SkipDependencies", "[dependencies]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::SkipDependencies); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesSkippedMessage).get()) != std::string::npos); REQUIRE_FALSE(installOutput.str().find(Resource::LocString(Resource::String::PackageRequiresDependencies).get()) != std::string::npos); REQUIRE_FALSE(installOutput.str().find("PreviewIIS") != std::string::npos); } TEST_CASE("InstallerWithDependencies_IgnoreDependenciesSetting", "[dependencies]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); TestUserSettings settings; settings.Set({ true }); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesSkippedMessage).get()) != std::string::npos); REQUIRE_FALSE(installOutput.str().find(Resource::LocString(Resource::String::PackageRequiresDependencies).get()) != std::string::npos); REQUIRE_FALSE(installOutput.str().find("PreviewIIS") != std::string::npos); } TEST_CASE("InstallerWithDependencies_DependenciesOnly", "[dependencies]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideOpenDependencySource(context); OverrideEnableWindowsFeaturesDependencies(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DependenciesOnly); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Dependencies should be reported and installed REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageRequiresDependencies).get()) != std::string::npos); REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); // DependenciesOnly message should be shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesOnlyMessage).get()) != std::string::npos); } TEST_CASE("DependenciesMultideclaration_InstallerDependenciesPreference", "[dependencies]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideOpenDependencySource(context); OverrideEnableWindowsFeaturesDependencies(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_DependenciesMultideclaration.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify installer dependencies are shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageRequiresDependencies).get()) != std::string::npos); REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); // and root dependencies are not REQUIRE(installOutput.str().find("PreviewIISOnRoot") == std::string::npos); } TEST_CASE("InstallFlow_Dependencies", "[InstallFlow][workflow][dependencies]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideOpenDependencySource(context); OverrideEnableWindowsFeaturesDependencies(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Installer_Exe_Dependencies.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify all types of dependencies are printed REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageRequiresDependencies).get()) != std::string::npos); REQUIRE(installOutput.str().find("PreviewIIS") != std::string::npos); } TEST_CASE("InstallFlow_Dependencies_COM", "[InstallFlow][workflow][dependencies]") { std::vector installationOrder; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideShellExecute(context, installationOrder); OverrideOpenDependencySource(context); OverrideEnableWindowsFeaturesDependencies(context); context.Override({ ReverifyInstallerHash, [](TestContext&) {} }); context.Add(YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_MultipleDependencies.yaml"))); COMDownloadCommand download({}); download.Execute(context); REQUIRE(installationOrder.size() == 0); COMInstallCommand install({}); REQUIRE_NOTHROW(install.Execute(context)); REQUIRE(context.GetTerminationHR() == S_OK); // Verify installers are called in order REQUIRE(installationOrder.size() == 3); REQUIRE(installationOrder.at(0) == "Dependency1"); REQUIRE(installationOrder.at(1) == "Dependency2"); REQUIRE(installationOrder.at(2) == "AppInstallerCliTest.TestExeInstaller.MultipleDependencies"); } // TODO: // add dependencies for installer tests to DependenciesTestSource (or a new one) // add tests for min version dependency solving // add tests that check for correct installation of dependencies (not only the order) ================================================ FILE: src/AppInstallerCLITests/InstallFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include "TestHooks.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace winrt::Windows::Foundation; using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Logging; using namespace AppInstaller::Manifest; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; void OverrideForDirectMsi(TestContext& context) { OverrideForCheckExistingInstaller(context); context.Override({ DownloadInstallerFile, [](TestContext& context) { context.Add({ {}, {} }); // We don't have an msi installer for tests, but we won't execute it anyway context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); } }); context.Override({ RenameDownloadedInstaller, [](TestContext&) { } }); OverrideForUpdateInstallerMotw(context); context.Override({ DirectMSIInstallImpl, [](TestContext& context) { // Write out the install command std::filesystem::path temp = std::filesystem::temp_directory_path(); temp /= "TestMsiInstalled.txt"; std::ofstream file(temp, std::ofstream::out); file << context.Get(); file.close(); context.Add(0); } }); } TEST_CASE("ExeInstallFlowWithTestManifest", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("InstallFlow_RenameFromEncodedUrl", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCheckExistingInstaller(context); context.Override({ DownloadInstallerFile, [](TestContext& context) { context.Add({ {}, {} }); auto installerPath = std::filesystem::temp_directory_path(); installerPath /= "EncodedUrlTest.exe"; std::filesystem::copy(TestDataFile("AppInstallerTestExeInstaller.exe"), installerPath, std::filesystem::copy_options::overwrite_existing); context.Add(installerPath); } }); OverrideForUpdateInstallerMotw(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_EncodedUrl.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/encodedUrl") != std::string::npos); } TEST_CASE("InstallFlow_RenameFromInvalidFileCharacterUrl", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCheckExistingInstaller(context); context.Override({ DownloadInstallerFile, [](TestContext& context) { context.Add({ {}, {} }); auto installerPath = std::filesystem::temp_directory_path(); installerPath /= "InvalidFileCharacterUrlTest.exe"; std::filesystem::copy(TestDataFile("AppInstallerTestExeInstaller.exe"), installerPath, std::filesystem::copy_options::overwrite_existing); context.Add(installerPath); } }); OverrideForUpdateInstallerMotw(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_InvalidFileCharacterUrl.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/invalidFileCharacterUrl") != std::string::npos); } TEST_CASE("InstallFlowNonZeroExitCode", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_NonZeroExitCode.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(context.GetTerminationHR() == S_OK); REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/ExitCode 0x80070005") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("InstallFlow_InstallationNotes", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_InstallationNotes.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify installation notes are displayed REQUIRE(context.GetTerminationHR() == S_OK); REQUIRE(std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find("testInstallationNotes") != std::string::npos); } TEST_CASE("InstallFlow_UnsupportedArguments_Warn", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TempDirectory tempDirectory("TempDirectory", false); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Log, tempDirectory); InstallCommand install({}); context.SetExecutingCommand(&install); install.Execute(context); INFO(installOutput.str()); // Verify unsupported arguments warn message is shown REQUIRE(context.GetTerminationHR() == S_OK); REQUIRE(std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UnsupportedArgument).get()) != std::string::npos); REQUIRE(installOutput.str().find("-o,--log") != std::string::npos); } TEST_CASE("InstallFlow_UnsupportedArguments_Error", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TempDirectory tempDirectory("TempDirectory", false); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); InstallCommand install({}); context.SetExecutingCommand(&install); install.Execute(context); INFO(installOutput.str()); // Verify unsupported arguments error message is shown REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UnsupportedArgument).get()) != std::string::npos); REQUIRE(installOutput.str().find("-l,--location") != std::string::npos); } TEST_CASE("InstallFlow_UnsupportedArguments_NotProvided") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string()); InstallCommand install({}); context.SetExecutingCommand(&install); install.Execute(context); INFO(installOutput.str()); // Verify unsupported arguments error message is not shown when not provided REQUIRE(context.GetTerminationHR() == S_OK); REQUIRE(std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UnsupportedArgument).get()) == std::string::npos); REQUIRE(installOutput.str().find("-o,--log") == std::string::npos); REQUIRE(installOutput.str().find("-l,--location") == std::string::npos); } TEST_CASE("InstallFlow_ExpectedReturnCodes", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_ExpectedReturnCodes.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Override, "/ExitCode 8"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify install failed with the right message REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT); REQUIRE(std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallFlowReturnCodeContactSupport).get()) != std::string::npos); REQUIRE(installOutput.str().find("https://TestReturnResponseUrl") != std::string::npos); } TEST_CASE("InstallFlowWithNonApplicableArchitecture", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_NoApplicableArchitecture.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER); // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); } TEST_CASE("InstallFlow_Zip_Exe", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideForExtractInstallerFromArchive(context); OverrideForVerifyAndSetNestedInstaller(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); TestHook::SetScanArchiveResult_Override scanArchiveResultOverride(true); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("InstallFlow_Zip_BadRelativePath", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideForExtractInstallerFromArchive(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); TestHook::SetScanArchiveResult_Override scanArchiveResultOverride(true); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND); // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); auto relativePath = context.Get().parent_path() / "extracted" / "relativeFilePath"; auto expectedMessage = Resource::String::NestedInstallerNotFound(AppInstaller::Utility::LocIndString{ relativePath.u8string()}); REQUIRE(installOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_MissingNestedInstaller", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_MissingNestedInstaller.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INVALID_MANIFEST); // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::NestedInstallerNotSpecified).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_UnsupportedNestedInstaller", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_UnsupportedNestedInstaller.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, ERROR_NOT_SUPPORTED); // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::NestedInstallerNotSupported).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_MultipleNonPortableNestedInstallers", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_MultipleNonPortableNestedInstallers.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INVALID_MANIFEST); // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::MultipleUnsupportedNestedInstallersSpecified).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_ArchiveScanFailed", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); TestHook::SetScanArchiveResult_Override scanArchiveResultOverride(false); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_ARCHIVE_SCAN_FAILED); // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::ArchiveFailedMalwareScan).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_ArchiveScanOverride_AdminSettingDisabled", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::IgnoreLocalArchiveMalwareScan); DisableAdminSetting(AppInstaller::Settings::BoolAdminSetting::LocalArchiveMalwareScanOverride); TestHook::SetScanArchiveResult_Override scanArchiveResultOverride(false); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_ARCHIVE_SCAN_FAILED); // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::ArchiveFailedMalwareScan).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_ArchiveScanOverride_AdminSettingEnabled", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideForExtractInstallerFromArchive(context); OverrideForVerifyAndSetNestedInstaller(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::IgnoreLocalArchiveMalwareScan); EnableAdminSetting(AppInstaller::Settings::BoolAdminSetting::LocalArchiveMalwareScanOverride); TestHook::SetScanArchiveResult_Override scanArchiveResultOverride(false); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify override message is displayed to the user. REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::ArchiveFailedMalwareScanOverridden).get()) != std::string::npos); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("ExtractInstallerFromArchive_InvalidZip", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Zip_Exe.yaml")); context.Add(manifest); context.Add(manifest.Installers.at(0)); // Provide an invalid zip file which should be handled appropriately. context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); context << ExtractFilesFromArchive; REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_EXTRACT_ARCHIVE_FAILED); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::ExtractArchiveFailed).get()) != std::string::npos); } TEST_CASE("ExtractInstallerFromArchiveWithTar", "[InstallFlow][workflow]") { TestCommon::TestUserSettings testSettings; testSettings.Set(AppInstaller::Archive::ExtractionMethod::Tar); TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideForVerifyAndSetNestedInstaller(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); TestHook::SetScanArchiveResult_Override scanArchiveResultOverride(true); TestHook::SetExtractArchiveWithTarResult_Override setExtractArchiveWithTarResultOverride(ERROR_SUCCESS); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::ExtractArchiveSucceeded).get()) != std::string::npos); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("ExtractInstallerFromArchiveWithTar_InvalidZip", "[InstallFlow][workflow]") { TestCommon::TestUserSettings testSettings; testSettings.Set(AppInstaller::Archive::ExtractionMethod::Tar); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Zip_Exe.yaml")); context.Add(manifest); context.Add(manifest.Installers.at(0)); // Provide an invalid zip file which should be handled appropriately. context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); context << ExtractFilesFromArchive; REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_EXTRACT_ARCHIVE_FAILED); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::ExtractArchiveFailed).get()) != std::string::npos); } TEST_CASE("MSStoreInstallFlowWithTestManifest", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestMSStoreInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForMSStore(context, false); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_MSStore.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("9WZDNCRFJ364") != std::string::npos); } TEST_CASE("MsixInstallFlow_DownloadFlow", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestMsixInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForMSIX(context); OverrideForUpdateInstallerMotw(context); // Todo: point to files from our repo when the repo goes public context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Msix_DownloadFlow.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and a local file is used as package Uri. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); Uri uri = Uri(ConvertToUTF16(installResultStr)); REQUIRE(uri.SchemeName() == L"file"); } TEST_CASE("MsixInstallFlow_StreamingFlow", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestMsixInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForMSIX(context); OverrideForCheckExistingInstaller(context); // Todo: point to files from our repo when the repo goes public context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Msix_StreamingFlow.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and a http address is used as package Uri. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); Uri uri = Uri(ConvertToUTF16(installResultStr)); REQUIRE(uri.SchemeName() == L"https"); } TEST_CASE("MsiInstallFlow_DirectMsi", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestMsiInstalled.txt"); TestCommon::TestUserSettings testSettings; testSettings.Set(true); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForDirectMsi(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallerArgTest_Msi_NoSwitches.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Silent); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/quiet") != std::string::npos); } TEST_CASE("InstallFlow_Portable", "[InstallFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestPortableInstallRoot", false); TestCommon::TempFile portableInstallResultPath("TestPortableInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForPortableInstallFlow(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Portable.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(std::filesystem::exists(portableInstallResultPath.GetPath())); } TEST_CASE("InstallFlow_Portable_SymlinkCreationFail", "[InstallFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestPortableInstallRoot", false); std::ostringstream installOutput; TestContext installContext{ installOutput, std::cin }; auto PreviousThreadGlobals = installContext.SetForCurrentThread(); OverridePortableInstaller(installContext); TestHook::SetCreateSymlinkResult_Override createSymlinkResultOverride(false); const auto& targetDirectory = tempDirectory.GetPath(); const auto& portableTargetPath = targetDirectory / "AppInstallerTestExeInstaller.exe"; installContext.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Portable.yaml").GetPath().u8string()); installContext.Args.AddArg(Execution::Args::Type::InstallLocation, targetDirectory.u8string()); installContext.Args.AddArg(Execution::Args::Type::InstallScope, "user"sv); InstallCommand install({}); install.Execute(installContext); { INFO(installOutput.str()); // Use CHECK to allow the uninstall to still occur CHECK(std::filesystem::exists(portableTargetPath)); CHECK(AppInstaller::Registry::Environment::PathVariable(AppInstaller::Manifest::ScopeEnum::User).Contains(targetDirectory)); } // Perform uninstall std::ostringstream uninstallOutput; TestContext uninstallContext{ uninstallOutput, std::cin }; auto previousThreadGlobals = uninstallContext.SetForCurrentThread(); uninstallContext.Args.AddArg(Execution::Args::Type::Name, "AppInstaller Test Portable Exe"sv); uninstallContext.Args.AddArg(Execution::Args::Type::AcceptSourceAgreements); UninstallCommand uninstall({}); uninstall.Execute(uninstallContext); INFO(uninstallOutput.str()); REQUIRE_FALSE(std::filesystem::exists(portableTargetPath)); } TEST_CASE("PortableInstallFlow_UserScope", "[InstallFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestPortableInstallRoot", false); TestCommon::TempFile portableInstallResultPath("TestPortableInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForPortableInstallFlow(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Portable.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); context.Args.AddArg(Execution::Args::Type::InstallScope, "user"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(std::filesystem::exists(portableInstallResultPath.GetPath())); } TEST_CASE("PortableInstallFlow_MachineScope", "[InstallFlow][workflow]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestCommon::TempDirectory tempDirectory("TestPortableInstallRoot", false); TestCommon::TempFile portableInstallResultPath("TestPortableInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForPortableInstallFlow(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Portable.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); context.Args.AddArg(Execution::Args::Type::InstallScope, "machine"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(std::filesystem::exists(portableInstallResultPath.GetPath())); } TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow][workflow]") { { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Default Msi type with no args passed in, no switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Msi_NoSwitches.yaml")); context.Add(manifest); context.Add(manifest.Installers.at(0)); context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs.find("/passive") != std::string::npos); REQUIRE(installerArgs.find(FileLogger::DefaultPrefix()) != std::string::npos); REQUIRE(installerArgs.find(manifest.Id) != std::string::npos); REQUIRE(installerArgs.find(manifest.Version) != std::string::npos); } { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Msi type with /silent and /log and /custom and /installlocation, no switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Msi_NoSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"sv); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"sv); context.Add(manifest); context.Add(manifest.Installers.at(0)); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs.find("/quiet") != std::string::npos); REQUIRE(installerArgs.find("/log \"MyLog.log\"") != std::string::npos); REQUIRE(installerArgs.find("TARGETDIR=\"MyDir\"") != std::string::npos); } { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Msi type with /silent and /log and /custom and /installlocation, switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Msi_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"sv); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"sv); context.Add(manifest); context.Add(manifest.Installers.at(0)); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs.find("/mysilent") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/mylog=\"MyLog.log\"") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/mycustom") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/myinstalldir=\"MyDir\"") != std::string::npos); // Use declaration in manifest } { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Default Inno type with no args passed in, no switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml")); context.Add(manifest); context.Add(manifest.Installers.at(0)); context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs.find("/SILENT") != std::string::npos); REQUIRE(installerArgs.find(FileLogger::DefaultPrefix()) != std::string::npos); REQUIRE(installerArgs.find(manifest.Id) != std::string::npos); REQUIRE(installerArgs.find(manifest.Version) != std::string::npos); } { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Inno type with /silent and /log and /custom and /installlocation, no switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"sv); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"sv); context.Add(manifest); context.Add(manifest.Installers.at(0)); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs.find("/VERYSILENT") != std::string::npos); REQUIRE(installerArgs.find("/LOG=\"MyLog.log\"") != std::string::npos); REQUIRE(installerArgs.find("/DIR=\"MyDir\"") != std::string::npos); } { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"sv); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"sv); context.Add(manifest); context.Add(manifest.Installers.at(0)); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs.find("/mysilent") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/mylog=\"MyLog.log\"") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/mycustom") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/myinstalldir=\"MyDir\"") != std::string::npos); // Use declaration in manifest } { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest and --custom argument used in cli auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"sv); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"sv); context.Args.AddArg(Execution::Args::Type::CustomSwitches, "/MyAppendedSwitch"sv); context.Add(manifest); context.Add(manifest.Installers.at(0)); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs.find("/mysilent") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/mylog=\"MyLog.log\"") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/mycustom") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/myinstalldir=\"MyDir\"") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("/MyAppendedSwitch") != std::string::npos); // Use declaration from argument } { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest and whitespace-only --custom argument used in cli auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::CustomSwitches, "\t"sv); context.Add(manifest); context.Add(manifest.Installers.at(0)); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs.find("/mysilent") != std::string::npos); // Use declaration in manifest REQUIRE(installerArgs.find("\t") == std::string::npos); // Whitespace only Custom switches should not be appended } { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Override switch specified. The whole arg passed to installer is overridden. auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"sv); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"sv); context.Args.AddArg(Execution::Args::Type::Override, "/OverrideEverything"sv); context.Add(manifest); context.Add(manifest.Installers.at(0)); context << GetInstallerArgs; std::string installerArgs = context.Get(); REQUIRE(installerArgs == "/OverrideEverything"); // Use value specified in override switch } } TEST_CASE("InstallFlow_SearchAndInstall", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context, CreateTestSource({ TSR::TestQuery_ReturnOne }), true); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestQuery_ReturnOne.Query); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("InstallFlow_SearchFoundNoApp", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context, CreateTestSource({}), true); context.Args.AddArg(Execution::Args::Type::Query, "TestQueryReturnZero"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify proper message is printed REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::NoPackageFound).get()) != std::string::npos); } TEST_CASE("InstallFlow_SearchFoundMultipleApp", "[InstallFlow][workflow]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context, CreateTestSource({ TSR::TestQuery_ReturnTwo }), true); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestQuery_ReturnTwo.Query); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify proper message is printed REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::MultiplePackagesFound).get()) != std::string::npos); } TEST_CASE("InstallFlow_LicenseAgreement", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_LicenseAgreement.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify agreements are shown REQUIRE(installOutput.str().find("Agreement with text") != std::string::npos); REQUIRE(installOutput.str().find("This is the text of the agreement.") != std::string::npos); REQUIRE(installOutput.str().find("Agreement with URL") != std::string::npos); REQUIRE(installOutput.str().find("https://TestAgreementUrl") != std::string::npos); // Verify Installer is called. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); } TEST_CASE("InstallFlow_LicenseAgreement_Prompt", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); // Accept the agreements by saying "Yes" at the prompt std::istringstream installInput{ "y" }; std::ostringstream installOutput; TestContext context{ installOutput, installInput }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_LicenseAgreement.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify prompt was shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageAgreementsPrompt).get()) != std::string::npos); // Verify agreements are shown REQUIRE(installOutput.str().find("Agreement with text") != std::string::npos); REQUIRE(installOutput.str().find("This is the text of the agreement.") != std::string::npos); REQUIRE(installOutput.str().find("Agreement with URL") != std::string::npos); REQUIRE(installOutput.str().find("https://TestAgreementUrl") != std::string::npos); // Verify Installer is called. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); } TEST_CASE("InstallFlow_LicenseAgreement_NotAccepted", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); // Say "No" at the agreements prompt std::istringstream installInput{ "n" }; std::ostringstream installOutput; TestContext context{ installOutput, installInput }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_LicenseAgreement.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify agreements are shown REQUIRE(installOutput.str().find("Agreement with text") != std::string::npos); REQUIRE(installOutput.str().find("This is the text of the agreement.") != std::string::npos); REQUIRE(installOutput.str().find("Agreement with URL") != std::string::npos); REQUIRE(installOutput.str().find("https://TestAgreementUrl") != std::string::npos); // Verify installation failed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED); REQUIRE_FALSE(std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageAgreementsNotAgreedTo).get()) != std::string::npos); } TEST_CASE("InstallFlowMultiLocale_RequirementNotSatisfied", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER); // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); } TEST_CASE("InstallFlowMultiLocale_RequirementSatisfied", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Locale, "fr-FR"sv); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/fr-FR") != std::string::npos); } TEST_CASE("InstallFlowMultiLocale_PreferenceNoBetterLocale", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); TestUserSettings settings; settings.Set({ "zh-CN" }); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/unknown") != std::string::npos); } TEST_CASE("InstallFlowMultiLocale_PreferenceWithBetterLocale", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-MultiLocale.yaml").GetPath().u8string()); TestUserSettings settings; settings.Set({ "en-US" }); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/en-GB") != std::string::npos); } TEST_CASE("InstallFlow_InstallMultiple", "[InstallFlow][workflow][MultiQuery]") { TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); TestCommon::TempFile msixInstallResultPath("TestMsixInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForMSIX(context); OverrideForShellExecute(context); OverrideForOpenSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Msix }), true); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query); InstallCommand installCommand({}); installCommand.Execute(context); INFO(installOutput.str()); // Verify all packages were installed REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); REQUIRE(std::filesystem::exists(msixInstallResultPath.GetPath())); } TEST_CASE("InstallFlow_InstallMultiple_SearchFailed", "[InstallFlow][workflow][MultiQuery]") { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context, CreateTestSource({ TSR::TestInstaller_Exe }), true); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query); InstallCommand installCommand({}); installCommand.Execute(context); INFO(installOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE); } TEST_CASE("InstallFlow_InstallAcquiresLock", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context, CreateTestSource({ TSR::TestQuery_ReturnOne }), true); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestQuery_ReturnOne.Query); wil::unique_event enteredShellExecute; enteredShellExecute.create(); wil::unique_event canLeaveShellExecute; canLeaveShellExecute.create(); AppInstaller::ProgressCallback progress; context.Override({ ShellExecuteInstallImpl, [&](TestContext& context) { enteredShellExecute.SetEvent(); canLeaveShellExecute.wait(500); ShellExecuteInstallImpl(context); }}); { std::thread otherThread([&]() { InstallCommand install({}); install.Execute(context); }); REQUIRE(enteredShellExecute.wait(5000)); AppInstaller::Synchronization::CrossProcessInstallLock mainThreadLock; REQUIRE(!mainThreadLock.TryAcquireNoWait()); canLeaveShellExecute.SetEvent(); otherThread.join(); } INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("InstallFlow_InstallWithReboot", "[InstallFlow][workflow][reboot]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TestUserSettings testSettings; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_ExpectedReturnCodes.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::AllowReboot); context.Override({ ShellExecuteInstallImpl, [&](TestContext& context) { // APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_INSTALL (should be treated as an installer error) context.Add(10); } }); SECTION("Reboot success") { TestHook::SetInitiateRebootResult_Override initiateRebootResultOverride(true); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(context.IsTerminated()); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InitiatingReboot).get()) != std::string::npos); REQUIRE_FALSE(installOutput.str().find(Resource::LocString(Resource::String::FailedToInitiateReboot).get()) != std::string::npos); } SECTION("Reboot failed") { TestHook::SetInitiateRebootResult_Override initiateRebootResultOverride(false); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(context.IsTerminated()); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InitiatingReboot).get()) != std::string::npos); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToInitiateReboot).get()) != std::string::npos); } } ================================================ FILE: src/AppInstallerCLITests/InstallerMetadataCollectionContext.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSource.h" #include "TestHooks.h" #include using namespace AppInstaller; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Correlation; using namespace AppInstaller::Repository::Metadata; using namespace TestCommon; namespace { // Indicates to set minimal defaults on TestInput struct MinimalDefaults_t {} MinimalDefaults; struct TestInput { TestInput() = default; TestInput(MinimalDefaults_t) { Version = "1.0"; SupportedMetadataVersion = "1.0"; SubmissionIdentifier = "1"; InstallerHash = "ABCD"; PackageData = std::make_optional(); PackageData->DefaultLocalization.Locale = "en-us"; PackageData->DefaultLocalization.Add("Name"); PackageData->DefaultLocalization.Add("Publisher"); } TestInput(MinimalDefaults_t, const std::string& productVersion, const std::string& productCode, Manifest::InstallerTypeEnum installerType) : TestInput(MinimalDefaults) { CurrentMetadata = std::make_optional(); CurrentMetadata->SchemaVersion.Assign("1.0"); CurrentMetadata->ProductVersionMin.Assign(productVersion); CurrentMetadata->ProductVersionMax.Assign(productVersion); auto& installerMetadata = CurrentMetadata->InstallerMetadataMap[InstallerHash.value()]; installerMetadata.SubmissionIdentifier = SubmissionIdentifier.value(); installerMetadata.AppsAndFeaturesEntries.push_back({}); auto& entry = installerMetadata.AppsAndFeaturesEntries.back(); entry.DisplayName = PackageData->DefaultLocalization.Get(); entry.Publisher = PackageData->DefaultLocalization.Get(); entry.DisplayVersion = productVersion; entry.ProductCode = productCode; entry.InstallerType = installerType; } std::optional Version; std::optional SupportedMetadataVersion; std::optional CurrentMetadata; std::optional SubmissionData; std::optional SubmissionIdentifier; std::optional InstallerHash; // Schema 1.0 only cares about DefaultLocale and Locales std::optional PackageData; std::wstring ToJSON() { web::json::value json; if (Version) { json[L"version"] = JSON::GetStringValue(Version.value()); } if (SupportedMetadataVersion) { json[L"supportedMetadataVersion"] = JSON::GetStringValue(SupportedMetadataVersion.value()); } if (CurrentMetadata) { json[L"currentMetadata"] = CurrentMetadata->ToJson(CurrentMetadata->SchemaVersion, 0); } if (SubmissionData) { json[L"submissionData"] = SubmissionData.value(); } else if (SubmissionIdentifier) { web::json::value submissionData; submissionData[L"submissionIdentifier"] = JSON::GetStringValue(SubmissionIdentifier.value()); json[L"submissionData"] = std::move(submissionData); } if (InstallerHash || PackageData) { web::json::value packageData; if (InstallerHash) { packageData[L"installerHash"] = JSON::GetStringValue(InstallerHash.value()); } if (PackageData) { packageData[L"DefaultLocale"] = LocaleToJSON(PackageData->DefaultLocalization); // TODO: Implement other locales } json[L"packageData"] = std::move(packageData); } return json.serialize(); } private: web::json::value LocaleToJSON(const Manifest::ManifestLocalization& localization) const { web::json::value locale; locale[L"PackageLocale"] = JSON::GetStringValue(localization.Locale); if (localization.Contains(Manifest::Localization::PackageName)) { locale[L"PackageName"] = JSON::GetStringValue(localization.Get()); } if (localization.Contains(Manifest::Localization::Publisher)) { locale[L"Publisher"] = JSON::GetStringValue(localization.Get()); } // TODO: Implement any other needed fields return locale; } }; struct TestOutput { TestOutput(const std::string& json) : OriginalJSON(json) { web::json::value input = web::json::value::parse(Utility::ConvertToUTF16(json)); auto versionString = JSON::GetRawStringValueFromJsonNode(input, L"version"); if (versionString) { Version = std::move(versionString); } auto submissionDataValue = JSON::GetJsonValueFromNode(input, L"submissionData"); if (submissionDataValue) { SubmissionData = submissionDataValue.value(); } auto installerHashString = JSON::GetRawStringValueFromJsonNode(input, L"installerHash"); if (installerHashString) { InstallerHash = std::move(installerHashString); } auto statusString = JSON::GetRawStringValueFromJsonNode(input, L"status"); if (statusString) { Status = std::move(statusString); } auto metadataValue = JSON::GetJsonValueFromNode(input, L"metadata"); if (metadataValue && !metadataValue->get().is_null()) { Metadata = std::make_optional(); Metadata->FromJson(metadataValue->get()); } auto diagnosticsValue = JSON::GetJsonValueFromNode(input, L"diagnostics"); if (diagnosticsValue) { auto errorHRNumber = JSON::GetRawIntValueFromJsonNode(diagnosticsValue.value(), L"errorHR"); if (errorHRNumber) { ErrorHR = std::move(errorHRNumber); } auto errorTextString = JSON::GetRawStringValueFromJsonNode(diagnosticsValue.value(), L"errorText"); if (errorTextString) { ErrorText = std::move(errorTextString); } } } std::string OriginalJSON; std::optional Version; std::optional SubmissionData; std::optional InstallerHash; std::optional Status; std::optional Metadata; std::optional ErrorHR; std::optional ErrorText; bool IsError() const { return Status && Status.value() == "Error"; } bool IsSuccess() const { return Status && Status.value() == "Success"; } bool IsLowConfidence() const { return Status && Status.value() == "LowConfidence"; } void ValidateFieldPresence() const { REQUIRE(Version); REQUIRE(SubmissionData); REQUIRE(InstallerHash); REQUIRE(Status); REQUIRE(IsSuccess() == Metadata.has_value()); REQUIRE(IsError() == ErrorHR.has_value()); REQUIRE(IsError() == ErrorText.has_value()); } }; struct TestARPCorrelationData : public ARPCorrelationData { ARPCorrelationResult CorrelateForNewlyInstalled(const Manifest::Manifest&, const ARPCorrelationSettings&) override { return CorrelateForNewlyInstalledResult; } ARPCorrelationResult CorrelateForNewlyInstalledResult; }; struct TestInstalledFilesCorrelation : public InstalledFilesCorrelation { Correlation::InstallationMetadata CorrelateForNewlyInstalled(const Manifest::Manifest&, const std::string&) override { return InstallationMetadata; } void StartFileWatcher() override {} void StopFileWatcher() override {} Correlation::InstallationMetadata InstallationMetadata; }; InstallerMetadataCollectionContext CreateTestContext( std::unique_ptr&& data, std::unique_ptr&& installedFiles, TestInput& input) { return { std::move(data), std::move(installedFiles), input.ToJSON() }; } InstallerMetadataCollectionContext CreateTestContext(std::unique_ptr&& data, TestInput& input) { return { std::move(data), std::make_unique(), input.ToJSON()}; } InstallerMetadataCollectionContext CreateTestContext(TestInput& input) { return CreateTestContext(std::make_unique(), input); } TestOutput GetOutput(InstallerMetadataCollectionContext& context) { std::ostringstream strstr; context.Complete(strstr); return { strstr.str() }; } TestOutput GetOutput(TestInput& input) { InstallerMetadataCollectionContext context = CreateTestContext(input); return GetOutput(context); } void BadInputTest(TestInput& input) { TestOutput output = GetOutput(input); REQUIRE(output.IsError()); output.ValidateFieldPresence(); } ProductMetadata MakeProductMetadata(std::string_view submissionIdentifier = "Submission 1", const std::string& installerHash = "ABCD") { ProductMetadata result; result.SchemaVersion.Assign("1.0"); result.ProductVersionMin.Assign("1.0"); result.ProductVersionMax.Assign("1.0"); auto& installerMetadata = result.InstallerMetadataMap[installerHash]; installerMetadata.SubmissionIdentifier = submissionIdentifier; installerMetadata.AppsAndFeaturesEntries.push_back({}); auto& entry = installerMetadata.AppsAndFeaturesEntries.back(); entry.DisplayName = "Name"; entry.Publisher = "Publisher"; entry.DisplayVersion = "1.0"; entry.ProductCode = "{guid}"; entry.InstallerType = Manifest::InstallerTypeEnum::Msi; return result; } struct TestMerge { TestMerge() = default; TestMerge(MinimalDefaults_t) { Version = "1.0"; Metadatas = std::make_optional>(); Metadatas->emplace_back(MakeProductMetadata()); } std::optional Version; std::optional> Metadatas; std::wstring ToJSON() { web::json::value json; if (Version) { json[L"version"] = JSON::GetStringValue(Version.value()); } if (Metadatas) { web::json::value metadatasArray; if (Metadatas->empty()) { metadatasArray = web::json::value::array(); } else { size_t index = 0; for (auto& value : Metadatas.value()) { metadatasArray[index++] = value.ToJson(value.SchemaVersion, 0); } } json[L"metadatas"] = std::move(metadatasArray); } return json.serialize(); } }; } TEST_CASE("MetadataCollection_MinimumInput", "[metadata_collection]") { TestInput input(MinimalDefaults); TestOutput output = GetOutput(input); REQUIRE(!output.IsError()); output.ValidateFieldPresence(); REQUIRE(output.Version.value() == input.Version.value()); REQUIRE(output.InstallerHash.value() == input.InstallerHash.value()); } TEST_CASE("MetadataCollection_SubmissionDataCopied", "[metadata_collection]") { TestInput input(MinimalDefaults); web::json::value submissionData; std::wstring testValueName = L"testValueName"; std::string testValueValue = "Test value value"; submissionData[L"submissionIdentifier"] = JSON::GetStringValue("Required identifier"); submissionData[testValueName] = JSON::GetStringValue(testValueValue); input.SubmissionData = submissionData; TestOutput output = GetOutput(input); REQUIRE(!output.IsError()); output.ValidateFieldPresence(); REQUIRE(output.Version.value() == input.Version.value()); REQUIRE(output.InstallerHash.value() == input.InstallerHash.value()); auto outputValue = JSON::GetRawStringValueFromJsonNode(output.SubmissionData.value(), testValueName); REQUIRE(outputValue); REQUIRE(outputValue.value() == testValueValue); } TEST_CASE("MetadataCollection_BadInput", "[metadata_collection]") { TestInput input(MinimalDefaults); #define RESET_FIELD_SECTION(_field_) \ SECTION("No " #_field_) \ { \ input._field_.reset(); \ BadInputTest(input); \ } RESET_FIELD_SECTION(Version); RESET_FIELD_SECTION(SupportedMetadataVersion); RESET_FIELD_SECTION(SubmissionIdentifier); RESET_FIELD_SECTION(InstallerHash); RESET_FIELD_SECTION(PackageData); #undef RESET_FIELD_SECTION } TEST_CASE("MetadataCollection_LowConfidence", "[metadata_collection]") { TestInput input(MinimalDefaults); // The default test correlation object won't have a package correlation set TestOutput output = GetOutput(input); REQUIRE(output.IsLowConfidence()); REQUIRE(!output.Metadata); output.ValidateFieldPresence(); } TEST_CASE("MetadataCollection_NewPackage", "[metadata_collection]") { TestInput input(MinimalDefaults); auto correlationData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Test Package Name"); manifest.DefaultLocalization.Add("Test Publisher"); manifest.Version = "1.2.3"; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = "{guid}"; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(Manifest::InstallerTypeEnum::Msi); metadata[PackageVersionMetadata::InstalledScope] = Manifest::ScopeToString(Manifest::ScopeEnum::User); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->ProductVersionMin.ToString() == output.Metadata->ProductVersionMax.ToString()); REQUIRE(output.Metadata->ProductVersionMin.ToString() == manifest.Version); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; REQUIRE(entry.SubmissionIdentifier == input.SubmissionIdentifier.value()); REQUIRE(entry.Scope.empty()); REQUIRE(entry.AppsAndFeaturesEntries.size() == 1); REQUIRE(entry.AppsAndFeaturesEntries[0].DisplayName == manifest.DefaultLocalization.Get()); REQUIRE(entry.AppsAndFeaturesEntries[0].Publisher == manifest.DefaultLocalization.Get()); REQUIRE(entry.AppsAndFeaturesEntries[0].DisplayVersion == manifest.Version); REQUIRE(entry.AppsAndFeaturesEntries[0].ProductCode == manifest.Installers[0].ProductCode); REQUIRE(entry.AppsAndFeaturesEntries[0].InstallerType == Manifest::InstallerTypeEnum::Msi); REQUIRE(output.Metadata->HistoricalMetadataList.empty()); } TEST_CASE("MetadataCollection_SameSubmission_SameInstaller", "[metadata_collection]") { std::string version = "1.3.5"; std::string productCode = "{guid}"; Manifest::InstallerTypeEnum installerType = Manifest::InstallerTypeEnum::Msi; TestInput input(MinimalDefaults, version, productCode, installerType); auto correlationData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Different Language Name"); // Same publisher manifest.DefaultLocalization.Add(input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries[0].Publisher); manifest.Version = version; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = productCode; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(installerType); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->ProductVersionMin.ToString() == output.Metadata->ProductVersionMax.ToString()); REQUIRE(output.Metadata->ProductVersionMin.ToString() == manifest.Version); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; REQUIRE(entry.SubmissionIdentifier == input.SubmissionIdentifier.value()); REQUIRE(entry.AppsAndFeaturesEntries.size() == 2); // One should have all values, and the other should have just a different name // Base which one is which off of whether Publisher is set for (const auto& featureEntry : entry.AppsAndFeaturesEntries) { if (featureEntry.Publisher.empty()) { REQUIRE(featureEntry.DisplayName == manifest.DefaultLocalization.Get()); REQUIRE(featureEntry.DisplayVersion.empty()); REQUIRE(featureEntry.ProductCode.empty()); REQUIRE(featureEntry.InstallerType == Manifest::InstallerTypeEnum::Unknown); } else { REQUIRE(featureEntry.DisplayName == input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries[0].DisplayName); REQUIRE(featureEntry.Publisher == manifest.DefaultLocalization.Get()); REQUIRE(featureEntry.DisplayVersion == manifest.Version); REQUIRE(featureEntry.ProductCode == manifest.Installers[0].ProductCode); REQUIRE(featureEntry.InstallerType == Manifest::InstallerTypeEnum::Msi); } } REQUIRE(output.Metadata->HistoricalMetadataList.empty()); } TEST_CASE("MetadataCollection_SameSubmission_NewInstaller", "[metadata_collection]") { std::string versionPresent = "1.3.5"; std::string versionIncoming = "1.3.5.1"; std::string productCodePresent = "{guid}"; std::string productCodeIncoming = "{guid_different}"; Manifest::InstallerTypeEnum installerType = Manifest::InstallerTypeEnum::Msi; TestInput input(MinimalDefaults, versionPresent, productCodePresent, installerType); // Change the incoming hash to be new input.InstallerHash = input.InstallerHash.value() + "_DIFFERENT"; auto correlationData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Name (but different architecture)"); // Same publisher manifest.DefaultLocalization.Add(input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries[0].Publisher); manifest.Version = versionIncoming; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = productCodeIncoming; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(installerType); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->ProductVersionMin.ToString() == versionPresent); REQUIRE(output.Metadata->ProductVersionMax.ToString() == versionIncoming); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 2); for (const auto& installerMetadata : output.Metadata->InstallerMetadataMap) { const auto& entry = installerMetadata.second; REQUIRE(entry.SubmissionIdentifier == input.SubmissionIdentifier.value()); REQUIRE(entry.AppsAndFeaturesEntries.size() == 1); const auto& featureEntry = entry.AppsAndFeaturesEntries.front(); REQUIRE(featureEntry.Publisher == manifest.DefaultLocalization.Get()); if (featureEntry.ProductCode == productCodePresent) { REQUIRE(featureEntry.DisplayName == input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries[0].DisplayName); REQUIRE(featureEntry.DisplayVersion == versionPresent); REQUIRE(featureEntry.InstallerType == Manifest::InstallerTypeEnum::Msi); } else { REQUIRE(featureEntry.DisplayName == manifest.DefaultLocalization.Get()); REQUIRE(featureEntry.DisplayVersion == versionIncoming); REQUIRE(featureEntry.InstallerType == Manifest::InstallerTypeEnum::Msi); REQUIRE(featureEntry.ProductCode == productCodeIncoming); } } REQUIRE(output.Metadata->HistoricalMetadataList.empty()); } TEST_CASE("MetadataCollection_NewSubmission", "[metadata_collection]") { std::string versionPresent = "1.3.5"; std::string versionIncoming = "1.4.0"; std::string productCodePresent = "{guid}"; std::string productCodeIncoming = "{guid_different}"; Manifest::InstallerTypeEnum installerType = Manifest::InstallerTypeEnum::Msi; TestInput input(MinimalDefaults, versionPresent, productCodePresent, installerType); input.SubmissionIdentifier = input.SubmissionIdentifier.value() + "_NEW"; input.InstallerHash = input.InstallerHash.value() + "_DIFFERENT"; auto correlationData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add(input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries[0].DisplayName); manifest.DefaultLocalization.Add(input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries[0].Publisher); manifest.Version = versionIncoming; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = productCodeIncoming; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(installerType); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->ProductVersionMin.ToString() == output.Metadata->ProductVersionMax.ToString()); REQUIRE(output.Metadata->ProductVersionMin.ToString() == manifest.Version); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; REQUIRE(entry.SubmissionIdentifier == input.SubmissionIdentifier.value()); REQUIRE(entry.AppsAndFeaturesEntries.size() == 1); REQUIRE(entry.AppsAndFeaturesEntries[0].DisplayName == manifest.DefaultLocalization.Get()); REQUIRE(entry.AppsAndFeaturesEntries[0].Publisher == manifest.DefaultLocalization.Get()); REQUIRE(entry.AppsAndFeaturesEntries[0].DisplayVersion == manifest.Version); REQUIRE(entry.AppsAndFeaturesEntries[0].ProductCode == manifest.Installers[0].ProductCode); REQUIRE(entry.AppsAndFeaturesEntries[0].InstallerType == Manifest::InstallerTypeEnum::Msi); REQUIRE(output.Metadata->HistoricalMetadataList.size() == 1); const auto& historicalEntry = output.Metadata->HistoricalMetadataList[0]; REQUIRE(historicalEntry.ProductVersionMin.ToString() == input.CurrentMetadata->ProductVersionMin.ToString()); REQUIRE(historicalEntry.ProductVersionMax.ToString() == input.CurrentMetadata->ProductVersionMax.ToString()); const auto& appsAndFeaturesEntry = input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries.front(); REQUIRE(historicalEntry.Names.size() == 1); REQUIRE(*historicalEntry.Names.begin() == appsAndFeaturesEntry.DisplayName); REQUIRE(historicalEntry.ProductCodes.size() == 1); REQUIRE(*historicalEntry.ProductCodes.begin() == appsAndFeaturesEntry.ProductCode); REQUIRE(historicalEntry.Publishers.size() == 1); REQUIRE(*historicalEntry.Publishers.begin() == appsAndFeaturesEntry.Publisher); } TEST_CASE("MetadataCollection_Merge_Empty", "[metadata_collection]") { TestMerge mergeData{ MinimalDefaults }; mergeData.Metadatas->clear(); REQUIRE_THROWS_HR(InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}), E_NOT_SET); } TEST_CASE("MetadataCollection_Merge_SubmissionMismatch", "[metadata_collection]") { TestMerge mergeData{ MinimalDefaults }; mergeData.Metadatas->emplace_back(MakeProductMetadata("Submission 2")); REQUIRE_THROWS_HR(InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}), E_NOT_VALID_STATE); } TEST_CASE("MetadataCollection_Merge_DifferentInstallers", "[metadata_collection]") { TestMerge mergeData{ MinimalDefaults }; mergeData.Metadatas->emplace_back(MakeProductMetadata(mergeData.Metadatas->at(0).InstallerMetadataMap.begin()->second.SubmissionIdentifier, "EFGH")); std::wstring mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); ProductMetadata mergeMetadata; mergeMetadata.FromJson(web::json::value::parse(mergeResult)); REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 2); for (const auto& item : mergeMetadata.InstallerMetadataMap) { REQUIRE(item.second.AppsAndFeaturesEntries.size() == 1); REQUIRE(!item.second.AppsAndFeaturesEntries[0].DisplayName.empty()); REQUIRE(!item.second.AppsAndFeaturesEntries[0].Publisher.empty()); REQUIRE(!item.second.AppsAndFeaturesEntries[0].DisplayVersion.empty()); REQUIRE(!item.second.AppsAndFeaturesEntries[0].ProductCode.empty()); } } TEST_CASE("MetadataCollection_Merge_SameInstaller", "[metadata_collection]") { TestMerge mergeData{ MinimalDefaults }; mergeData.Metadatas->emplace_back(MakeProductMetadata()); std::wstring mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); ProductMetadata mergeMetadata; mergeMetadata.FromJson(web::json::value::parse(mergeResult)); REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 1); for (const auto& item : mergeMetadata.InstallerMetadataMap) { REQUIRE(item.second.AppsAndFeaturesEntries.size() == 1); REQUIRE(!item.second.AppsAndFeaturesEntries[0].DisplayName.empty()); REQUIRE(!item.second.AppsAndFeaturesEntries[0].Publisher.empty()); REQUIRE(!item.second.AppsAndFeaturesEntries[0].DisplayVersion.empty()); REQUIRE(!item.second.AppsAndFeaturesEntries[0].ProductCode.empty()); } } TEST_CASE("MetadataCollection_NewPackage_1_1", "[metadata_collection]") { TestInput input(MinimalDefaults); input.SupportedMetadataVersion = "1.1"; auto correlationData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Test Package Name"); manifest.DefaultLocalization.Add("Test Publisher"); manifest.Version = "1.2.3"; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = "{guid}"; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(Manifest::InstallerTypeEnum::Msi); metadata[PackageVersionMetadata::InstalledScope] = Manifest::ScopeToString(Manifest::ScopeEnum::User); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->ProductVersionMin.ToString() == output.Metadata->ProductVersionMax.ToString()); REQUIRE(output.Metadata->ProductVersionMin.ToString() == manifest.Version); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; REQUIRE(entry.SubmissionIdentifier == input.SubmissionIdentifier.value()); REQUIRE(entry.Scope == metadata[PackageVersionMetadata::InstalledScope]); REQUIRE(entry.AppsAndFeaturesEntries.size() == 1); REQUIRE(entry.AppsAndFeaturesEntries[0].DisplayName == manifest.DefaultLocalization.Get()); REQUIRE(entry.AppsAndFeaturesEntries[0].Publisher == manifest.DefaultLocalization.Get()); REQUIRE(entry.AppsAndFeaturesEntries[0].DisplayVersion == manifest.Version); REQUIRE(entry.AppsAndFeaturesEntries[0].ProductCode == manifest.Installers[0].ProductCode); REQUIRE(entry.AppsAndFeaturesEntries[0].InstallerType == Manifest::InstallerTypeEnum::Msi); REQUIRE(output.Metadata->HistoricalMetadataList.empty()); } TEST_CASE("MetadataCollection_NewPackage_NoScope", "[metadata_collection]") { TestInput input(MinimalDefaults); input.SupportedMetadataVersion = "1.1"; auto correlationData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Test Package Name"); manifest.DefaultLocalization.Add("Test Publisher"); manifest.Version = "1.2.3"; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = "{guid}"; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(Manifest::InstallerTypeEnum::Msi); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; REQUIRE(entry.Scope.empty()); } TEST_CASE("MetadataCollection_SameSubmission_SameInstaller_Scopes", "[metadata_collection]") { std::string version = "1.3.5"; std::string productCode = "{guid}"; Manifest::InstallerTypeEnum installerType = Manifest::InstallerTypeEnum::Msi; std::string currentScope = GENERATE(std::string{}, Manifest::ScopeToString(Manifest::ScopeEnum::Unknown), Manifest::ScopeToString(Manifest::ScopeEnum::User), Manifest::ScopeToString(Manifest::ScopeEnum::Machine)); std::string newScope{ Manifest::ScopeToString(Manifest::ScopeEnum::User) }; INFO(currentScope); TestInput input(MinimalDefaults, version, productCode, installerType); input.SupportedMetadataVersion = "1.1"; input.CurrentMetadata->SchemaVersion = { "1.1" }; input.CurrentMetadata->InstallerMetadataMap.begin()->second.Scope = currentScope; auto correlationData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Different Language Name"); // Same publisher manifest.DefaultLocalization.Add(input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries[0].Publisher); manifest.Version = version; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = productCode; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(installerType); metadata[PackageVersionMetadata::InstalledScope] = newScope; correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; if (currentScope.empty()) { REQUIRE(entry.Scope == newScope); } else if (currentScope != newScope) { // If Unknown, should stay Unknown // If different, should become Unknown REQUIRE(entry.Scope == Manifest::ScopeToString(Manifest::ScopeEnum::Unknown)); } else { // If same, should not change REQUIRE(entry.Scope == currentScope); } } TEST_CASE("MetadataCollection_Merge_SameInstaller_Scopes", "[metadata_collection]") { TestMerge mergeData{ MinimalDefaults }; mergeData.Metadatas->emplace_back(MakeProductMetadata()); std::string currentScope = GENERATE(std::string{}, Manifest::ScopeToString(Manifest::ScopeEnum::Unknown), Manifest::ScopeToString(Manifest::ScopeEnum::User), Manifest::ScopeToString(Manifest::ScopeEnum::Machine)); std::string newScope{ Manifest::ScopeToString(Manifest::ScopeEnum::Machine) }; INFO(currentScope); mergeData.Metadatas->at(0).SchemaVersion = { "1.1" }; mergeData.Metadatas->at(0).InstallerMetadataMap.begin()->second.Scope = currentScope; mergeData.Metadatas->at(1).SchemaVersion = { "1.1" }; mergeData.Metadatas->at(1).InstallerMetadataMap.begin()->second.Scope = newScope; std::wstring mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); ProductMetadata mergeMetadata; mergeMetadata.FromJson(web::json::value::parse(mergeResult)); REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 1); for (const auto& item : mergeMetadata.InstallerMetadataMap) { if (currentScope.empty()) { REQUIRE(item.second.Scope == newScope); } else if (currentScope != newScope) { // If Unknown, should stay Unknown // If different, should become Unknown REQUIRE(item.second.Scope == Manifest::ScopeToString(Manifest::ScopeEnum::Unknown)); } else { // If same, should not change REQUIRE(item.second.Scope == currentScope); } } } TEST_CASE("MetadataCollection_NewPackage_1_2", "[metadata_collection]") { TestInput input(MinimalDefaults); input.SupportedMetadataVersion = "1.2"; auto correlationData = std::make_unique(); auto installedFilesData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Test Package Name"); manifest.DefaultLocalization.Add("Test Publisher"); manifest.Version = "1.2.3"; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = "{guid}"; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(Manifest::InstallerTypeEnum::Msi); metadata[PackageVersionMetadata::InstalledScope] = Manifest::ScopeToString(Manifest::ScopeEnum::User); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); Correlation::InstallationMetadata installedFiles; installedFiles.InstalledFiles.DefaultInstallLocation = "%TEMP%\\TestApp"; Manifest::InstalledFile installedFile; installedFile.RelativeFilePath = "test.exe"; installedFile.FileSha256 = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); installedFile.FileType = Manifest::InstalledFileTypeEnum::Launch; installedFile.InvocationParameter = "invocation"; installedFile.DisplayName = "name"; installedFiles.InstalledFiles.Files.emplace_back(std::move(installedFile)); Correlation::InstalledStartupLinkFile startupLink; startupLink.RelativeFilePath = "TestApp.lnk"; startupLink.FileType = Manifest::InstalledFileTypeEnum::Launch; installedFiles.StartupLinkFiles.emplace_back(std::move(startupLink)); installedFilesData->InstallationMetadata = std::move(installedFiles); std::vector testIcons; AppInstaller::Repository::ExtractedIconInfo iconInfo; iconInfo.IconContent = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b700"); iconInfo.IconSha256 = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); iconInfo.IconFileType = Manifest::IconFileTypeEnum::Ico; iconInfo.IconResolution = Manifest::IconResolutionEnum::Custom; iconInfo.IconTheme = Manifest::IconThemeEnum::Default; testIcons.emplace_back(std::move(iconInfo)); TestHook::SetExtractIconFromArpEntryResult_Override iconsOverride{ testIcons }; InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), std::move(installedFilesData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->ProductVersionMin.ToString() == output.Metadata->ProductVersionMax.ToString()); REQUIRE(output.Metadata->ProductVersionMin.ToString() == manifest.Version); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; REQUIRE(entry.SubmissionIdentifier == input.SubmissionIdentifier.value()); REQUIRE(entry.Scope == metadata[PackageVersionMetadata::InstalledScope]); REQUIRE(entry.AppsAndFeaturesEntries.size() == 1); REQUIRE(entry.AppsAndFeaturesEntries[0].DisplayName == manifest.DefaultLocalization.Get()); REQUIRE(entry.AppsAndFeaturesEntries[0].Publisher == manifest.DefaultLocalization.Get()); REQUIRE(entry.AppsAndFeaturesEntries[0].DisplayVersion == manifest.Version); REQUIRE(entry.AppsAndFeaturesEntries[0].ProductCode == manifest.Installers[0].ProductCode); REQUIRE(entry.AppsAndFeaturesEntries[0].InstallerType == Manifest::InstallerTypeEnum::Msi); REQUIRE(entry.InstalledFiles.has_value()); REQUIRE(entry.InstalledFiles->DefaultInstallLocation == "%TEMP%\\TestApp"); REQUIRE(entry.InstalledFiles->Files.size() == 1); REQUIRE(entry.InstalledFiles->Files[0].RelativeFilePath == "test.exe"); REQUIRE(entry.InstalledFiles->Files[0].FileSha256 == Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759")); REQUIRE(entry.InstalledFiles->Files[0].FileType == Manifest::InstalledFileTypeEnum::Launch); REQUIRE(entry.InstalledFiles->Files[0].InvocationParameter == "invocation"); REQUIRE(entry.InstalledFiles->Files[0].DisplayName == "name"); REQUIRE(entry.StartupLinkFiles.has_value()); REQUIRE(entry.StartupLinkFiles->size() == 1); REQUIRE(entry.StartupLinkFiles->at(0).RelativeFilePath == "TestApp.lnk"); REQUIRE(entry.StartupLinkFiles->at(0).FileType == Manifest::InstalledFileTypeEnum::Launch); REQUIRE(entry.Icons.size() == 1); REQUIRE(entry.Icons[0].IconContent == Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b700")); REQUIRE(entry.Icons[0].IconSha256 == Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759")); REQUIRE(entry.Icons[0].IconFileType == Manifest::IconFileTypeEnum::Ico); REQUIRE(entry.Icons[0].IconResolution == Manifest::IconResolutionEnum::Custom); REQUIRE(entry.Icons[0].IconTheme == Manifest::IconThemeEnum::Default); REQUIRE(output.Metadata->HistoricalMetadataList.empty()); } TEST_CASE("MetadataCollection_NewPackage_NoInstallationMetadata_NoIcons", "[metadata_collection]") { TestInput input(MinimalDefaults); input.SupportedMetadataVersion = "1.2"; auto correlationData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Test Package Name"); manifest.DefaultLocalization.Add("Test Publisher"); manifest.Version = "1.2.3"; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = "{guid}"; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(Manifest::InstallerTypeEnum::Msi); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); std::vector testIcons; TestHook::SetExtractIconFromArpEntryResult_Override iconsOverride{ testIcons }; InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; REQUIRE(entry.Scope.empty()); REQUIRE_FALSE(entry.InstalledFiles.has_value()); REQUIRE_FALSE(entry.StartupLinkFiles.has_value()); REQUIRE(entry.Icons.size() == 0); } TEST_CASE("MetadataCollection_SameSubmission_SameInstaller_InstallationMetadata_Icons", "[metadata_collection]") { std::string version = "1.3.5"; std::string productCode = "{guid}"; Manifest::InstallerTypeEnum installerType = Manifest::InstallerTypeEnum::Msi; TestInput input(MinimalDefaults, version, productCode, installerType); input.SupportedMetadataVersion = "1.2"; input.CurrentMetadata->SchemaVersion = { "1.2" }; Manifest::InstallationMetadataInfo installedFiles; installedFiles.DefaultInstallLocation = "%TEMP%\\TestApp"; Manifest::InstalledFile installedFile; installedFile.RelativeFilePath = "test.exe"; installedFile.FileSha256 = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); installedFile.FileType = Manifest::InstalledFileTypeEnum::Launch; installedFile.InvocationParameter = "invocation"; installedFile.DisplayName = "name"; installedFiles.Files.emplace_back(std::move(installedFile)); input.CurrentMetadata->InstallerMetadataMap.begin()->second.InstalledFiles = std::move(installedFiles); std::vector startupLinkFiles; Correlation::InstalledStartupLinkFile startupLink; startupLink.RelativeFilePath = "TestApp.lnk"; startupLink.FileType = Manifest::InstalledFileTypeEnum::Launch; startupLinkFiles.emplace_back(std::move(startupLink)); input.CurrentMetadata->InstallerMetadataMap.begin()->second.StartupLinkFiles = std::move(startupLinkFiles); std::vector testIcons; AppInstaller::Repository::ExtractedIconInfo iconInfo; iconInfo.IconContent = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b700"); iconInfo.IconSha256 = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); iconInfo.IconFileType = Manifest::IconFileTypeEnum::Ico; iconInfo.IconResolution = Manifest::IconResolutionEnum::Custom; iconInfo.IconTheme = Manifest::IconThemeEnum::Default; testIcons.emplace_back(std::move(iconInfo)); input.CurrentMetadata->InstallerMetadataMap.begin()->second.Icons = std::move(testIcons); auto correlationData = std::make_unique(); auto installedFilesData = std::make_unique(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add("Different Language Name"); // Same publisher manifest.DefaultLocalization.Add(input.CurrentMetadata->InstallerMetadataMap.begin()->second.AppsAndFeaturesEntries[0].Publisher); manifest.Version = version; manifest.Installers.push_back({}); manifest.Installers[0].ProductCode = productCode; IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = Manifest::InstallerTypeToString(installerType); correlationData->CorrelateForNewlyInstalledResult.Package = std::make_shared(manifest, metadata); Correlation::InstallationMetadata newInstalledFiles; newInstalledFiles.InstalledFiles.DefaultInstallLocation = "%TEMP%\\NewTestApp"; Manifest::InstalledFile newInstalledFile; newInstalledFile.RelativeFilePath = "test.exe"; newInstalledFile.FileSha256 = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); newInstalledFile.FileType = Manifest::InstalledFileTypeEnum::Launch; newInstalledFile.InvocationParameter = "invocation"; newInstalledFile.DisplayName = "name"; newInstalledFiles.InstalledFiles.Files.emplace_back(std::move(newInstalledFile)); Correlation::InstalledStartupLinkFile newStartupLink; newStartupLink.RelativeFilePath = "NewTestApp.lnk"; newStartupLink.FileType = Manifest::InstalledFileTypeEnum::Launch; newInstalledFiles.StartupLinkFiles.emplace_back(std::move(newStartupLink)); installedFilesData->InstallationMetadata = std::move(newInstalledFiles); std::vector newTestIcons; AppInstaller::Repository::ExtractedIconInfo newIconInfo; newIconInfo.IconContent = Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6"); newIconInfo.IconSha256 = Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df8499"); newIconInfo.IconFileType = Manifest::IconFileTypeEnum::Jpeg; newIconInfo.IconResolution = Manifest::IconResolutionEnum::Square16; newIconInfo.IconTheme = Manifest::IconThemeEnum::Light; newTestIcons.emplace_back(std::move(newIconInfo)); TestHook::SetExtractIconFromArpEntryResult_Override iconsOverride{ newTestIcons }; InstallerMetadataCollectionContext context = CreateTestContext(std::move(correlationData), std::move(installedFilesData), input); TestOutput output = GetOutput(context); REQUIRE(output.IsSuccess()); output.ValidateFieldPresence(); REQUIRE(output.Metadata->InstallerMetadataMap.size() == 1); REQUIRE(output.Metadata->InstallerMetadataMap.count(input.InstallerHash.value()) == 1); const auto& entry = output.Metadata->InstallerMetadataMap[input.InstallerHash.value()]; // Conflicting installed files entries get removed. REQUIRE(entry.InstalledFiles.has_value()); REQUIRE_FALSE(entry.InstalledFiles->HasData()); // Non duplicate startup links get added. REQUIRE(entry.StartupLinkFiles.has_value()); REQUIRE(entry.StartupLinkFiles->size() == 2); // New detected icons always take over REQUIRE(entry.Icons.size() == 1); REQUIRE(entry.Icons[0].IconContent == Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(entry.Icons[0].IconSha256 == Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df8499")); REQUIRE(entry.Icons[0].IconFileType == Manifest::IconFileTypeEnum::Jpeg); REQUIRE(entry.Icons[0].IconResolution == Manifest::IconResolutionEnum::Square16); REQUIRE(entry.Icons[0].IconTheme == Manifest::IconThemeEnum::Light); } TEST_CASE("MetadataCollection_Merge_SameInstaller_InstalledFiles", "[metadata_collection]") { TestMerge mergeData{ MinimalDefaults }; mergeData.Metadatas->emplace_back(MakeProductMetadata()); Manifest::InstallationMetadataInfo installedFiles; installedFiles.DefaultInstallLocation = "%TEMP%\\TestApp"; Manifest::InstalledFile installedFile; installedFile.RelativeFilePath = "test.exe"; installedFile.FileSha256 = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); installedFile.FileType = Manifest::InstalledFileTypeEnum::Launch; installedFile.InvocationParameter = "invocation"; installedFile.DisplayName = "name"; installedFiles.Files.emplace_back(std::move(installedFile)); mergeData.Metadatas->at(0).SchemaVersion = { "1.2" }; mergeData.Metadatas->at(0).InstallerMetadataMap.begin()->second.InstalledFiles = installedFiles; // Different default install location clears whole data Manifest::InstallationMetadataInfo newInstalledFiles = installedFiles; newInstalledFiles.DefaultInstallLocation = "%TEMP%\\NewTestApp"; mergeData.Metadatas->at(1).SchemaVersion = { "1.2" }; mergeData.Metadatas->at(1).InstallerMetadataMap.begin()->second.InstalledFiles = newInstalledFiles; std::wstring mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); ProductMetadata mergeMetadata; mergeMetadata.FromJson(web::json::value::parse(mergeResult)); REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles.has_value()); REQUIRE_FALSE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->HasData()); // Different RelativeFilePath clears the file entry Manifest::InstallationMetadataInfo newInstalledFiles2 = installedFiles; newInstalledFiles2.Files[0].RelativeFilePath = "test2.exe"; mergeData.Metadatas->at(1).InstallerMetadataMap.begin()->second.InstalledFiles = newInstalledFiles2; mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); mergeMetadata.FromJson(web::json::value::parse(mergeResult)); REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles.has_value()); REQUIRE_FALSE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->DefaultInstallLocation.empty()); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->Files.empty()); // Different other fields clears the fields themselves Manifest::InstallationMetadataInfo newInstalledFiles3 = installedFiles; newInstalledFiles3.Files[0].DisplayName = "name2"; newInstalledFiles3.Files[0].FileSha256 = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b756"); newInstalledFiles3.Files[0].InvocationParameter = "invocation2"; newInstalledFiles3.Files[0].FileType = Manifest::InstalledFileTypeEnum::Uninstall; mergeData.Metadatas->at(1).InstallerMetadataMap.begin()->second.InstalledFiles = newInstalledFiles3; mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); mergeMetadata.FromJson(web::json::value::parse(mergeResult)); REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles.has_value()); REQUIRE_FALSE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->DefaultInstallLocation.empty()); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->Files.size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->Files[0].RelativeFilePath == "test.exe"); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->Files[0].DisplayName == ""); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->Files[0].InvocationParameter == ""); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->Files[0].FileType == Manifest::InstalledFileTypeEnum::Unknown); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.InstalledFiles->Files[0].FileSha256.empty()); } TEST_CASE("MetadataCollection_Merge_SameInstaller_StartupLinkFiles", "[metadata_collection]") { TestMerge mergeData{ MinimalDefaults }; mergeData.Metadatas->emplace_back(MakeProductMetadata()); std::vector startupLinkFiles; Correlation::InstalledStartupLinkFile startupLink; startupLink.RelativeFilePath = "TestApp.lnk"; startupLink.FileType = Manifest::InstalledFileTypeEnum::Launch; startupLinkFiles.emplace_back(std::move(startupLink)); mergeData.Metadatas->at(0).SchemaVersion = { "1.2" }; mergeData.Metadatas->at(0).InstallerMetadataMap.begin()->second.StartupLinkFiles = startupLinkFiles; // Different relative file path gets added std::vector newStartupLinkFiles = startupLinkFiles; newStartupLinkFiles[0].RelativeFilePath = "TestApp2.lnk"; mergeData.Metadatas->at(1).SchemaVersion = { "1.2" }; mergeData.Metadatas->at(1).InstallerMetadataMap.begin()->second.StartupLinkFiles = newStartupLinkFiles; std::wstring mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); ProductMetadata mergeMetadata; mergeMetadata.FromJson(web::json::value::parse(mergeResult)); REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.StartupLinkFiles.has_value()); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.StartupLinkFiles->size() == 2); // Different other fields clears the fields themselves std::vector newStartupLinkFiles2 = startupLinkFiles; newStartupLinkFiles2[0].FileType = Manifest::InstalledFileTypeEnum::Uninstall; mergeData.Metadatas->at(1).InstallerMetadataMap.begin()->second.StartupLinkFiles = newStartupLinkFiles2; mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); mergeMetadata.FromJson(web::json::value::parse(mergeResult)); REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.StartupLinkFiles.has_value()); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.StartupLinkFiles->size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.StartupLinkFiles->at(0).RelativeFilePath == "TestApp.lnk"); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.StartupLinkFiles->at(0).FileType == Manifest::InstalledFileTypeEnum::Unknown); } TEST_CASE("MetadataCollection_Merge_SameInstaller_Icons", "[metadata_collection]") { TestMerge mergeData{ MinimalDefaults }; mergeData.Metadatas->emplace_back(MakeProductMetadata()); std::vector testIcons; AppInstaller::Repository::ExtractedIconInfo iconInfo; iconInfo.IconContent = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b700"); iconInfo.IconSha256 = Utility::SHA256::ConvertToBytes("d2a45116709136462ee7a1c42f0e75f0efa258fe959b1504dc8ea4573451b759"); iconInfo.IconFileType = Manifest::IconFileTypeEnum::Ico; iconInfo.IconResolution = Manifest::IconResolutionEnum::Custom; iconInfo.IconTheme = Manifest::IconThemeEnum::Default; testIcons.emplace_back(std::move(iconInfo)); mergeData.Metadatas->at(0).SchemaVersion = { "1.2" }; mergeData.Metadatas->at(0).InstallerMetadataMap.begin()->second.Icons = testIcons; // Different test icons std::vector newTestIcons; AppInstaller::Repository::ExtractedIconInfo newIconInfo; newIconInfo.IconContent = Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6"); newIconInfo.IconSha256 = Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df8499"); newIconInfo.IconFileType = Manifest::IconFileTypeEnum::Jpeg; newIconInfo.IconResolution = Manifest::IconResolutionEnum::Square16; newIconInfo.IconTheme = Manifest::IconThemeEnum::Light; newTestIcons.emplace_back(std::move(newIconInfo)); mergeData.Metadatas->at(1).SchemaVersion = { "1.2" }; mergeData.Metadatas->at(1).InstallerMetadataMap.begin()->second.Icons = newTestIcons; std::wstring mergeResult = InstallerMetadataCollectionContext::Merge(mergeData.ToJSON(), 0, {}); REQUIRE(!mergeResult.empty()); ProductMetadata mergeMetadata; mergeMetadata.FromJson(web::json::value::parse(mergeResult)); // New data always take over REQUIRE(mergeMetadata.InstallerMetadataMap.size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.Icons.size() == 1); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.Icons[0].IconContent == Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.Icons[0].IconSha256 == Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df8499")); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.Icons[0].IconFileType == Manifest::IconFileTypeEnum::Jpeg); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.Icons[0].IconResolution == Manifest::IconResolutionEnum::Square16); REQUIRE(mergeMetadata.InstallerMetadataMap.begin()->second.Icons[0].IconTheme == Manifest::IconThemeEnum::Light); } ================================================ FILE: src/AppInstallerCLITests/JsonHelper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include "cpprest/json.h" using namespace AppInstaller; web::json::value GetTestJsonObject() { web::json::value jsonObject = web::json::value::object(); jsonObject[L"Key1"] = web::json::value::string(L"Value1"); jsonObject[L"Key2"] = web::json::value::string(L"Value2"); jsonObject[L"IntKey"] = 100; web::json::value arrayValue = web::json::value::array(); arrayValue[0] = web::json::value::string(L"ArrayValue1"); arrayValue[1] = web::json::value::string(L"ArrayValue2"); arrayValue[2] = web::json::value::string(L"ArrayValue3"); jsonObject[L"Array"] = arrayValue; return jsonObject; } TEST_CASE("GetUtilityString", "[RestSource]") { REQUIRE(JSON::GetUtilityString("cpprest") == L"cpprest"); REQUIRE(JSON::GetUtilityString(" ") == L" "); } TEST_CASE("GetJsonValueFromNode", "[RestSource]") { web::json::value jsonObject = GetTestJsonObject(); std::optional> actual = JSON::GetJsonValueFromNode(jsonObject, L"Key1"); REQUIRE(actual); REQUIRE(actual.value().get().as_string() == L"Value1"); std::optional> absentKey = JSON::GetJsonValueFromNode(jsonObject, L"Key3"); REQUIRE(!absentKey); web::json::value emptyObject; std::optional> empty = JSON::GetJsonValueFromNode(emptyObject, L"Key1"); REQUIRE(!empty); } TEST_CASE("GetRawStringValueFromJsonValue", "[RestSource]") { std::optional stringTest = JSON::GetRawStringValueFromJsonValue(web::json::value::string(L"cpprest ")); REQUIRE(stringTest); REQUIRE(stringTest.value() == "cpprest "); std::optional emptyTest = JSON::GetRawStringValueFromJsonValue(web::json::value::string(L" ")); REQUIRE(emptyTest); REQUIRE(emptyTest.value() == " "); web::json::value obj; std::optional nullTest = JSON::GetRawStringValueFromJsonValue(obj); REQUIRE(!nullTest); web::json::value integer = 100; std::optional mismatchFieldTest = JSON::GetRawStringValueFromJsonValue(integer); REQUIRE(!mismatchFieldTest); } TEST_CASE("GetRawStringValueFromJsonNode", "[RestSource]") { web::json::value jsonObject = GetTestJsonObject(); std::optional stringTest = JSON::GetRawStringValueFromJsonNode(jsonObject, L"Key1"); REQUIRE(stringTest); REQUIRE(stringTest.value() == "Value1"); std::optional emptyTest = JSON::GetRawStringValueFromJsonNode(jsonObject, L"Key3"); REQUIRE(!emptyTest); std::optional mismatchFieldTest = JSON::GetRawStringValueFromJsonNode(jsonObject, L"IntKey"); REQUIRE(!mismatchFieldTest); } TEST_CASE("GetRawIntValueFromJsonValue", "[RestSource]") { web::json::value jsonObject = 100; std::optional expected = JSON::GetRawIntValueFromJsonValue(jsonObject); REQUIRE(expected); REQUIRE(expected.value() == 100); std::optional mismatchFieldTest = JSON::GetRawIntValueFromJsonValue(web::json::value::string(L"cpprest")); REQUIRE(!mismatchFieldTest); } TEST_CASE("GetRawJsonArrayFromJsonNode", "[RestSource]") { web::json::value jsonObject = GetTestJsonObject(); std::optional> expected = JSON::GetRawJsonArrayFromJsonNode(jsonObject, L"Array"); REQUIRE(expected); REQUIRE(expected.value().get().size() == 3); REQUIRE(expected.value().get().at(0).as_string() == L"ArrayValue1"); std::optional> mismatchFieldTest = JSON::GetRawJsonArrayFromJsonNode(jsonObject, L"Keyword"); REQUIRE(!mismatchFieldTest); } TEST_CASE("GetRawStringArrayFromJsonNode", "[RestSource]") { web::json::value jsonObject = GetTestJsonObject(); std::vector expected = JSON::GetRawStringArrayFromJsonNode(jsonObject, L"Array"); REQUIRE(expected.size() == 3); REQUIRE(expected[0] == "ArrayValue1"); std::vector mismatchFieldTest = JSON::GetRawStringArrayFromJsonNode(jsonObject, L"Keyword"); REQUIRE(mismatchFieldTest.size() == 0); } ================================================ FILE: src/AppInstallerCLITests/LanguageUtilities.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace AppInstaller; TEST_CASE("DestructionToken", "[langutil]") { DestructionToken beginToken = true; DestructionToken endToken = false; REQUIRE(beginToken); REQUIRE(!endToken); endToken = std::move(beginToken); REQUIRE(!beginToken); REQUIRE(endToken); } ================================================ FILE: src/AppInstallerCLITests/MSStoreDownloadFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestHooks.h" #include "TestRestRequestHandler.h" #include "WorkflowCommon.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility::literals; utility::string_t TestDisplayCatalogResponse = _XPLATSTR( R"delimiter( { "Product": { "DisplaySkuAvailabilities": [ { "Sku": { "SkuId": "0015", "Properties": { "Packages": [ { "PackageId": "PackageEnglish", "Architectures": [ "x64", "arm" ], "Languages": [ "en-US", "en-GB" ], "PackageFormat": "Appx", "ContentId": "LicenseContentId", "FulfillmentData": { "WuCategoryId": "TestCategoryIdEnglish" } }, { "PackageId": "PackageFrench", "Architectures": [ "x64", "arm" ], "Languages": [ "fr-FR" ], "PackageFormat": "Appx", "ContentId": "LicenseContentId", "FulfillmentData": { "WuCategoryId": "TestCategoryIdFrench" } } ] } } } ] } })delimiter"); utility::string_t TestLicensingResponseRaw = _XPLATSTR( R"delimiter( { "license": { "keys": [ { "value": "" } ] } })delimiter"); std::string LicenseContent = "TestLicense"; utility::string_t TestLicensingResponse = AppInstaller::Utility::ReplaceWhileCopying( TestLicensingResponseRaw, L"", AppInstaller::Utility::ConvertToUTF16(AppInstaller::JSON::Base64Encode(std::vector{ LicenseContent.begin(), LicenseContent.end() }))); utility::string_t TestDisplayCatalogResponse_TargetSkuNotFound = _XPLATSTR( R"delimiter( { "Product": { "DisplaySkuAvailabilities": [ { "Sku": { "SkuId": "0011", "Properties": { "Packages": [ { "PackageId": "PackageEnglish", "Architectures": [ "x64", "arm" ], "Languages": [ "en-US", "en-GB" ], "PackageFormat": "Appx", "ContentId": "LicenseContentId", "FulfillmentData": { "WuCategoryId": "TestCategoryIdEnglish" } } ] } } } ] } })delimiter"); std::vector GetSfsAppContentsOverrideFunction(std::string_view wuCategoryId) { std::string wuCategoryIdStr{ wuCategoryId }; std::vector result; std::unique_ptr contentId; std::vector dependencies; std::vector packages; std::unique_ptr appContent; std::vector sha256Bytes = AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798223CD4FEBAB1D734A07C2E51E56A28E0DF8123"); std::string base64EncodedSha256 = AppInstaller::JSON::Base64Encode(sha256Bytes); { // Create dependencies content std::unique_ptr dependencyContentId; std::vector dependencyPackages; std::unique_ptr dependencyContent; std::ignore = SFS::ContentId::Make("testDependency", "testDependency", "1.0.0.0", dependencyContentId); std::unique_ptr dependencyX64; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/dependency/x64", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Amd64 }, { "Universal=10.0.0.0" }, wuCategoryIdStr + ".Dependency_1.2.3.4_x64__8wekyb3d8bbwe", dependencyX64); dependencyPackages.emplace_back(std::move(*dependencyX64)); // Lower target OS dependency std::unique_ptr dependencyX64_lower; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/dependency/x64", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Amd64 }, { "Universal=9.0.0.0" }, wuCategoryIdStr + ".Dependency_0.9.3.4_x64__8wekyb3d8bbwe", dependencyX64_lower); dependencyPackages.emplace_back(std::move(*dependencyX64_lower)); std::unique_ptr dependencyArm; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/dependency/arm", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Arm }, { "Universal=10.0.0.0" }, wuCategoryIdStr + ".Dependency_1.2.3.4_arm__8wekyb3d8bbwe", dependencyArm); dependencyPackages.emplace_back(std::move(*dependencyArm)); std::ignore = SFS::AppPrerequisiteContent::Make(std::move(dependencyContentId), std::move(dependencyPackages), dependencyContent); dependencies.emplace_back(std::move(*dependencyContent)); } { // Create main packages // Good candidate x64 std::unique_ptr packageX64; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/x64", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Amd64 }, { "Desktop=10.0.0.0" }, wuCategoryIdStr + "_1.0.0.0_x64__8wekyb3d8bbwe", packageX64); packages.emplace_back(std::move(*packageX64)); // Good candidate x64, lower minimum OS version, lower package version std::unique_ptr packageX64_lower; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/x64", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Amd64 }, { "Desktop=9.0.0.0" }, wuCategoryIdStr + "_0.9.0.0_x64__8wekyb3d8bbwe", packageX64_lower); packages.emplace_back(std::move(*packageX64_lower)); // Good candidate arm std::unique_ptr packageArm; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/arm", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Arm }, { "Desktop=10.0.0.0" }, wuCategoryIdStr + "_1.0.0.0_arm__8wekyb3d8bbwe", packageArm); packages.emplace_back(std::move(*packageArm)); // Good candidate IoT std::unique_ptr packageIoT; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/IoT/arm", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Arm }, { "IoT=10.0.0.0" }, wuCategoryIdStr + ".IoT_1.0.0.0_arm__8wekyb3d8bbwe", packageIoT); packages.emplace_back(std::move(*packageIoT)); // Good candidate IoT has newer version std::unique_ptr packageIoT2; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/IoT/arm/2.0", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Arm }, { "IoT=10.0.0.0" }, wuCategoryIdStr + ".IoT_2.0.0.0_arm__8wekyb3d8bbwe", packageIoT2); packages.emplace_back(std::move(*packageIoT2)); // Candidate unsupported platform std::unique_ptr packageXbox; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".appx", "https://NotUsed/" + wuCategoryIdStr + "/Xbox/arm", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Arm }, { "Xbox=10.0.0.0" }, wuCategoryIdStr + ".Xbox_1.0.0.0_arm__8wekyb3d8bbwe", packageXbox); packages.emplace_back(std::move(*packageXbox)); // Candidate unsupported filetype std::unique_ptr packageData; std::ignore = SFS::AppFile::Make( wuCategoryIdStr + ".cab", "https://NotUsed/" + wuCategoryIdStr + "/cab", 100, { { SFS::HashType::Sha256, base64EncodedSha256 } }, { SFS::Architecture::Arm }, { "Desktop=10.0.0.0" }, wuCategoryIdStr + ".Data_1.0.0.0_arm__8wekyb3d8bbwe", packageData); packages.emplace_back(std::move(*packageData)); } std::ignore = SFS::ContentId::Make("test", "test", "1.0.0.0", contentId); std::ignore = SFS::AppContent::Make(std::move(contentId), "updateId", std::move(dependencies), std::move(packages), appContent); result.emplace_back(std::move(*appContent)); return result; } TEST_CASE("MSStoreDownloadFlow_Success", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); DownloadCommand download({}); download.Execute(context); REQUIRE(context.GetTerminationHR() == S_OK); INFO(downloadOutput.str()); // Verify downloaded files REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); // Verify license REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); REQUIRE(licenseFile.is_open()); std::string licenseFileStr; std::getline(licenseFile, licenseFileStr); REQUIRE(licenseFileStr == LicenseContent); // Verify unsupported packages filtered out REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_1.0.0.0_IoT_Arm.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.Xbox_1.0.0.0_Xbox_Arm.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.Data_1.0.0.0_Desktop_Arm.cab")); } TEST_CASE("MSStoreDownloadFlow_Success_SkipDependencies", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); context.Args.AddArg(Execution::Args::Type::SkipDependencies); DownloadCommand download({}); download.Execute(context); REQUIRE(context.GetTerminationHR() == S_OK); INFO(downloadOutput.str()); // Verify downloaded files REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); // Verify license REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); REQUIRE(licenseFile.is_open()); std::string licenseFileStr; std::getline(licenseFile, licenseFileStr); REQUIRE(licenseFileStr == LicenseContent); } TEST_CASE("MSStoreDownloadFlow_Success_SkipLicense", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); context.Args.AddArg(Execution::Args::Type::SkipMicrosoftStorePackageLicense); DownloadCommand download({}); download.Execute(context); REQUIRE(context.GetTerminationHR() == S_OK); INFO(downloadOutput.str()); // Verify downloaded files REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); // Verify license REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); } TEST_CASE("MSStoreDownloadFlow_Success_SpecificLocale", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "fr-FR"sv); DownloadCommand download({}); download.Execute(context); REQUIRE(context.GetTerminationHR() == S_OK); INFO(downloadOutput.str()); // Verify downloaded files REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdFrench.Dependency_1.2.3.4_Universal_X64.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdFrench.Dependency_1.2.3.4_Universal_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench_1.0.0.0_Desktop_X64.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench_1.0.0.0_Desktop_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench.IoT_2.0.0.0_IoT_Arm.appx")); // Verify license REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); REQUIRE(licenseFile.is_open()); std::string licenseFileStr; std::getline(licenseFile, licenseFileStr); REQUIRE(licenseFileStr == LicenseContent); } TEST_CASE("MSStoreDownloadFlow_Success_SpecificArchitecture", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); context.Args.AddArg(Execution::Args::Type::InstallerArchitecture, "x64"sv); DownloadCommand download({}); download.Execute(context); REQUIRE(context.GetTerminationHR() == S_OK); INFO(downloadOutput.str()); // Verify downloaded files REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_x64.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); // Verify license REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); REQUIRE(licenseFile.is_open()); std::string licenseFileStr; std::getline(licenseFile, licenseFileStr); REQUIRE(licenseFileStr == LicenseContent); } TEST_CASE("MSStoreDownloadFlow_Success_SpecificPlatform", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); context.Args.AddArg(Execution::Args::Type::Platform, "Windows.IoT"sv); DownloadCommand download({}); download.Execute(context); REQUIRE(context.GetTerminationHR() == S_OK); INFO(downloadOutput.str()); // Verify downloaded files REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); // Verify license REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); REQUIRE(licenseFile.is_open()); std::string licenseFileStr; std::getline(licenseFile, licenseFileStr); REQUIRE(licenseFileStr == LicenseContent); } TEST_CASE("MSStoreDownloadFlow_Fail_TargetSkuNotFound", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse_TargetSkuNotFound)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); DownloadCommand download({}); download.Execute(context); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE); INFO(downloadOutput.str()); } TEST_CASE("MSStoreDownloadFlow_Fail_LocaleNotApplicable", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "ja-JP"sv); DownloadCommand download({}); download.Execute(context); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE); INFO(downloadOutput.str()); } TEST_CASE("MSStoreDownloadFlow_Fail_ArchitectureNotApplicable", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); context.Args.AddArg(Execution::Args::Type::InstallerArchitecture, "arm64"sv); DownloadCommand download({}); download.Execute(context); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE); INFO(downloadOutput.str()); } TEST_CASE("MSStoreDownloadFlow_Fail_PlatformNotApplicable", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); context.Args.AddArg(Execution::Args::Type::Platform, "Windows.Holographic"sv); DownloadCommand download({}); download.Execute(context); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE); INFO(downloadOutput.str()); } TEST_CASE("MSStoreDownloadFlow_Fail_Licensing", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::InternalError)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); DownloadCommand download({}); download.Execute(context); REQUIRE_TERMINATED_WITH(context, MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, web::http::status_codes::InternalError)); INFO(downloadOutput.str()); } TEST_CASE("MSStoreDownloadFlow_Fail_Licensing_Forbidden", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::Forbidden)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); DownloadCommand download({}); download.Execute(context); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN); INFO(downloadOutput.str()); } TEST_CASE("MSStoreDownloadFlow_Success_TargetOSVersion", "[MSStoreDownloadFlow][workflow]") { TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false); std::ostringstream downloadOutput; TestContext context{ downloadOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideDownloadInstallerFileForMSStoreDownload(context); TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse)); TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction }); TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestLicensingResponse)); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory); context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv); context.Args.AddArg(Execution::Args::Type::Platform, "Windows.Desktop"sv); context.Args.AddArg(Execution::Args::Type::OSVersion, "9.0.0.0"sv); DownloadCommand download({}); download.Execute(context); REQUIRE(context.GetTerminationHR() == S_OK); INFO(downloadOutput.str()); // Verify downloaded files REQUIRE(std::filesystem::exists(tempDirectory.GetPath())); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_0.9.3.4_Universal_X64.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx")); REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_0.9.0.0_Desktop_X64.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx")); REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx")); // Verify license REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml")); std::ifstream licenseFile(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"); REQUIRE(licenseFile.is_open()); std::string licenseFileStr; std::getline(licenseFile, licenseFileStr); REQUIRE(licenseFileStr == LicenseContent); } ================================================ FILE: src/AppInstallerCLITests/ManifestComparator.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace TestCommon; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; using Manifest = ::AppInstaller::Manifest::Manifest; struct ManifestComparatorTestContext : public NullStream, Context { ManifestComparatorTestContext() : NullStream(), Context(*m_nullOut, *m_nullIn) {} }; const ManifestInstaller& AddInstaller( Manifest& manifest, Architecture architecture, InstallerTypeEnum installerType, ScopeEnum scope = ScopeEnum::Unknown, std::string minOSVersion = {}, std::string locale = {}, std::vector unsupportedOSArchitectures = {}, MarketsInfo markets = {}) { ManifestInstaller toAdd; toAdd.Arch = architecture; toAdd.BaseInstallerType = installerType; toAdd.Scope = scope; toAdd.MinOSVersion = minOSVersion; toAdd.Locale = locale; toAdd.UnsupportedOSArchitectures = unsupportedOSArchitectures; toAdd.Markets = markets; manifest.Installers.emplace_back(std::move(toAdd)); return manifest.Installers.back(); } template void RequireVectorsEqual(const std::vector& actual, const std::vector& expected) { REQUIRE(actual.size() == expected.size()); for (std::size_t i = 0; i < actual.size(); ++i) { REQUIRE(actual[i] == expected[i]); } } void RequireInstaller(const std::optional& actual, const ManifestInstaller& expected) { REQUIRE(actual); REQUIRE(actual->Arch == expected.Arch); REQUIRE(actual->EffectiveInstallerType() == expected.EffectiveInstallerType()); REQUIRE(actual->Scope == expected.Scope); REQUIRE(actual->MinOSVersion == expected.MinOSVersion); REQUIRE(actual->Locale == expected.Locale); RequireVectorsEqual(actual->Markets.AllowedMarkets, expected.Markets.AllowedMarkets); RequireVectorsEqual(actual->Markets.ExcludedMarkets, expected.Markets.ExcludedMarkets); } void RequireInapplicabilities(const std::vector& inapplicabilities, const std::vector& expected) { RequireVectorsEqual(inapplicabilities, expected); } void RequireInapplicabilityType(const std::vector& inapplicabilities, InapplicabilityFlags expected) { REQUIRE(!inapplicabilities.empty()); for (std::size_t i = 0; i < inapplicabilities.size(); ++i) { REQUIRE(inapplicabilities[i] == expected); } } TEST_CASE("ManifestComparator_OSFilter_Low", "[manifest_comparator]") { Manifest manifest; AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown, "10.0.99999.0"); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::OSVersion }); } TEST_CASE("ManifestComparator_OSFilter_High", "[manifest_comparator]") { Manifest manifest; ManifestInstaller expected = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown, "10.0.0.0"); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, expected); REQUIRE(inapplicabilities.size() == 0); } TEST_CASE("ManifestComparator_InstalledScopeFilter_Unknown", "[manifest_comparator]") { Manifest manifest; ManifestInstaller unknown = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Unknown); SECTION("Nothing Installed") { ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); // Only because it is first RequireInstaller(result, unknown); REQUIRE(inapplicabilities.size() == 0); } SECTION("User Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::User); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, unknown); REQUIRE(inapplicabilities.size() == 0); } SECTION("Machine Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::Machine); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, unknown); REQUIRE(inapplicabilities.size() == 0); } } TEST_CASE("ManifestComparator_InstalledScopeFilter", "[manifest_comparator]") { Manifest manifest; ManifestInstaller user = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User); ManifestInstaller machine = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Machine); SECTION("Nothing Installed") { ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); // Only because it is first RequireInstaller(result, user); REQUIRE(inapplicabilities.size() == 0); } SECTION("User Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::User); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, user); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledScope }); } SECTION("Machine Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::Machine); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, machine); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledScope }); } } TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]") { Manifest manifest; ManifestInstaller msi = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi); ManifestInstaller msix = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msix); SECTION("Nothing Installed") { ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); // Only because it is first RequireInstaller(result, msi); REQUIRE(inapplicabilities.size() == 0); } SECTION("MSI Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msi); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, msi); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledType }); } SECTION("MSIX Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msix); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, msix); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledType }); } } TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]") { Manifest manifest; ManifestInstaller burn = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Burn); ManifestInstaller exe = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe); SECTION("Nothing Installed") { ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); // Only because it is first RequireInstaller(result, burn); REQUIRE(inapplicabilities.size() == 0); } SECTION("Exe Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Exe); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, burn); REQUIRE(inapplicabilities.size() == 0); } SECTION("Inno Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Inno); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, burn); REQUIRE(inapplicabilities.size() == 0); } } TEST_CASE("ManifestComparator_ScopeFilter", "[manifest_comparator]") { Manifest manifest; ManifestInstaller user = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User); ManifestInstaller machine = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Machine); SECTION("Nothing Required") { ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); // Only because it is first RequireInstaller(result, user); REQUIRE(inapplicabilities.size() == 0); } SECTION("User Required") { ManifestComparatorTestContext context; context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::User)); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, user); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Scope }); } SECTION("Machine Required") { ManifestComparatorTestContext context; context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::Machine)); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, machine); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Scope }); } } TEST_CASE("ManifestComparator_ScopeCompare", "[manifest_comparator]") { Manifest manifest; ManifestInstaller machine = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Machine); ManifestInstaller user = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User); SECTION("No Preference") { ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); // The default preference is user RequireInstaller(result, user); REQUIRE(inapplicabilities.size() == 0); } SECTION("User Preference") { TestUserSettings settings; settings.Set(ScopeEnum::User); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, user); REQUIRE(inapplicabilities.size() == 0); } SECTION("Machine Preference") { TestUserSettings settings; settings.Set(ScopeEnum::Machine); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, machine); REQUIRE(inapplicabilities.size() == 0); } } TEST_CASE("ManifestComparator_LocaleComparator_Installed_WithUnknown", "[manifest_comparator]") { Manifest manifest; ManifestInstaller unknown = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); ManifestInstaller enGB = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "en-GB"); SECTION("Nothing Installed en-US preference") { TestUserSettings settings; settings.Set({ "en-US" }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); // Only because it is first RequireInstaller(result, enGB); REQUIRE(inapplicabilities.size() == 0); } SECTION("en-US Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "en-US"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, enGB); REQUIRE(inapplicabilities.size() == 0); } SECTION("zh-CN Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "zh-CN"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, unknown); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale }); } } TEST_CASE("ManifestComparator_LocaleComparator_Installed", "[manifest_comparator]") { Manifest manifest; ManifestInstaller frFR = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "fr-FR"); ManifestInstaller enGB = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "en-GB"); SECTION("Nothing Installed en-US preference") { TestUserSettings settings; settings.Set({ "en-US" }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); // Only because it is first RequireInstaller(result, enGB); REQUIRE(inapplicabilities.size() == 0); } SECTION("en-US Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "en-US"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, enGB); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale }); } SECTION("zh-CN Installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "zh-CN"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale, InapplicabilityFlags::InstalledLocale }); } SECTION("en-US installed but fr-fr as user intent") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "en-US"; metadata[PackageVersionMetadata::UserIntentLocale] = "fr-FR"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, frFR); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale }); // en-US inapplicable } SECTION("en-US installed but zh-CN as user intent") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledLocale] = "en-US"; metadata[PackageVersionMetadata::UserIntentLocale] = "zh-CN"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale, InapplicabilityFlags::InstalledLocale }); } } TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]") { Manifest manifest; ManifestInstaller unknown = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); ManifestInstaller frFR = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "fr-FR"); ManifestInstaller enGB = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "en-GB"); SECTION("en-GB Required") { ManifestComparatorTestContext context; context.Args.AddArg(Args::Type::Locale, "en-GB"s); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, enGB); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Locale , InapplicabilityFlags::Locale }); } SECTION("zh-CN Required") { ManifestComparatorTestContext context; context.Args.AddArg(Args::Type::Locale, "zh-CN"s); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Locale , InapplicabilityFlags::Locale, InapplicabilityFlags::Locale }); } SECTION("en-US Preference") { TestUserSettings settings; settings.Set({ "en-US" }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, enGB); REQUIRE(inapplicabilities.size() == 0); } SECTION("zh-CN Preference") { TestUserSettings settings; settings.Set({ "zh-CN" }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, unknown); REQUIRE(inapplicabilities.size() == 0); } } TEST_CASE("ManifestComparator_AllowedArchitecture_x64_only", "[manifest_comparator]") { Manifest manifest; ManifestInstaller x86 = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); ManifestComparatorTestContext context; context.Add({ Architecture::X64 }); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture }); } TEST_CASE("ManifestComparator_AllowedArchitecture", "[manifest_comparator]") { Manifest manifest; ManifestInstaller x86 = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); ManifestInstaller x64 = AddInstaller(manifest, Architecture::X64, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); ManifestInstaller arm = AddInstaller(manifest, Architecture::Arm, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); ManifestInstaller arm64 = AddInstaller(manifest, Architecture::Arm64, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); SECTION("x86 Preference") { ManifestComparatorTestContext context; context.Add({ Architecture::X86, Architecture::X64 }); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, x86); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture, InapplicabilityFlags::MachineArchitecture }); } SECTION("Unknown") { ManifestComparatorTestContext context; context.Add({ Architecture::Unknown }); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(result); REQUIRE(result->Arch == GetApplicableArchitectures()[0]); RequireInapplicabilityType(inapplicabilities, InapplicabilityFlags::MachineArchitecture); } SECTION("x86 and Unknown") { ManifestComparatorTestContext context; context.Add({ Architecture::X86, Architecture::Unknown }); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, x86); RequireInapplicabilityType(inapplicabilities, InapplicabilityFlags::MachineArchitecture); } } TEST_CASE("ManifestComparator_Architectures_WithUserIntent", "[manifest_comparator]") { Manifest manifest; ManifestInstaller x86 = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); ManifestInstaller x64 = AddInstaller(manifest, Architecture::X64, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); SECTION("x86 installed") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledArchitecture] = "x86"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, x86); REQUIRE(inapplicabilities.size() == 0); } SECTION("x86 installed but x64 as user intent") { IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledArchitecture] = "x86"; metadata[PackageVersionMetadata::UserIntentArchitecture] = "x64"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, x64); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture }); } SECTION("x86 installed but x64 as user intent") { Manifest x86OnlyManifest; AddInstaller(x86OnlyManifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledArchitecture] = "x86"; metadata[PackageVersionMetadata::UserIntentArchitecture] = "x64"; ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(x86OnlyManifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture }); } } TEST_CASE("ManifestComparator_UnsupportedOSArchitecture", "[manifest_comparator]") { auto systemArchitecture = GetSystemArchitecture(); auto applicableArchitectures = GetApplicableArchitectures(); // Try to find an applicable architecture that is not the system architecture auto itr = std::find_if(applicableArchitectures.begin(), applicableArchitectures.end(), [&](const auto& arch) { return arch != systemArchitecture; }); if (itr == applicableArchitectures.end()) { // This test requires having an applicable architecture different from the system one. // TODO: Does this actually happen in any arch we use? return; } auto applicableArchitecture = *itr; Manifest manifest; SECTION("System is unsupported") { ManifestInstaller installer = AddInstaller(manifest, applicableArchitecture, InstallerTypeEnum::Msi, {}, {}, {}, { systemArchitecture }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture }); } SECTION("Other is unsupported") { ManifestInstaller installer = AddInstaller(manifest, applicableArchitecture, InstallerTypeEnum::Msi, {}, {}, {}, { applicableArchitecture }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, installer); REQUIRE(inapplicabilities.empty()); } } TEST_CASE("ManifestComparator_Inapplicabilities", "[manifest_comparator]") { Manifest manifest; ManifestInstaller installer = AddInstaller(manifest, Architecture::Arm64, InstallerTypeEnum::Exe, ScopeEnum::User, "10.0.99999.0", "es-MX"); ManifestComparatorTestContext context; context.Add({ Architecture::X86 }); context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::Machine)); context.Args.AddArg(Args::Type::Locale, "en-GB"s); IPackageVersion::Metadata metadata; metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msix); ManifestComparator mc(GetManifestComparatorOptions(context, metadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities( inapplicabilities, { InapplicabilityFlags::OSVersion | InapplicabilityFlags::InstalledType | InapplicabilityFlags::Locale | InapplicabilityFlags::Scope | InapplicabilityFlags::MachineArchitecture }); } TEST_CASE("ManifestComparator_MarketFilter", "[manifest_comparator]") { Manifest manifest; // Get current market. winrt::Windows::Globalization::GeographicRegion region; Manifest::string_t currentMarket{ region.CodeTwoLetter() }; SECTION("Applicable") { MarketsInfo markets; SECTION("Allowed") { markets.AllowedMarkets = { currentMarket }; } SECTION("Not excluded") { markets.ExcludedMarkets = { "XX" }; } ManifestInstaller installer = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Exe, {}, {}, {}, {}, markets); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, installer); REQUIRE(inapplicabilities.empty()); } SECTION("Inapplicable") { MarketsInfo markets; SECTION("Excluded") { markets.ExcludedMarkets = { currentMarket }; } SECTION("Not allowed") { markets.AllowedMarkets = { "XX" }; } ManifestInstaller installer = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Exe, {}, {}, {}, {}, markets); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Market}); } } TEST_CASE("ManifestComparator_Scope_AllowUnknown", "[manifest_comparator]") { Manifest manifest; ManifestInstaller expected = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown); ManifestComparatorTestContext testContext; testContext.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::User)); SECTION("Default") { ManifestComparator mc(GetManifestComparatorOptions(testContext, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Scope }); } SECTION("Allow Unknown") { testContext.Add(true); ManifestComparator mc(GetManifestComparatorOptions(testContext, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, expected); REQUIRE(inapplicabilities.size() == 0); } } TEST_CASE("ManifestComparator_InstallerType", "[manifest_comparator]") { Manifest manifest; ManifestInstaller msi = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User); ManifestInstaller exe = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::User); ManifestInstaller msix = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msix, ScopeEnum::User); SECTION("Msi arg requirement") { ManifestComparatorTestContext context; context.Args.AddArg(Args::Type::InstallerType, "msi"s); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, msi); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType }); } SECTION("Msix arg requirement") { ManifestComparatorTestContext context; context.Args.AddArg(Args::Type::InstallerType, "msix"s); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, msix); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType }); } SECTION("Portable arg requirement") { ManifestComparatorTestContext context; context.Args.AddArg(Args::Type::InstallerType, "portable"s); ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType }); } SECTION("Exe preference") { TestUserSettings settings; settings.Set({ InstallerTypeEnum::Exe }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, exe); REQUIRE(inapplicabilities.size() == 0); } SECTION("Preference does not exist") { TestUserSettings settings; settings.Set({ InstallerTypeEnum::Portable }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, msi); REQUIRE(inapplicabilities.size() == 0); } SECTION("Multiple preferences") { TestUserSettings settings; settings.Set({ InstallerTypeEnum::Exe, InstallerTypeEnum::Msix }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, exe); REQUIRE(inapplicabilities.size() == 0); } SECTION("Multiple preferences alternate order") { TestUserSettings settings; settings.Set({ InstallerTypeEnum::Msix, InstallerTypeEnum::Exe }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, msix); REQUIRE(inapplicabilities.size() == 0); } SECTION("Exe requirement") { TestUserSettings settings; settings.Set({ InstallerTypeEnum::Exe }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, exe); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType }); } SECTION("Inno requirement") { TestUserSettings settings; settings.Set({ InstallerTypeEnum::Inno }); ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); REQUIRE(!result); RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType }); } } TEST_CASE("ManifestComparator_MachineArchitecture_Strong_Scope_Weak", "[manifest_comparator]") { Manifest manifest; ManifestInstaller system = AddInstaller(manifest, GetSystemArchitecture(), InstallerTypeEnum::Msi, ScopeEnum::Unknown, "", ""); ManifestInstaller user = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", ""); ManifestComparatorTestContext context; ManifestComparator mc(GetManifestComparatorOptions(context, {})); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, system); } TEST_CASE("ManifestComparator_InstallerCompatibilitySet_Weaker_Than_Architecture", "[manifest_comparator]") { Manifest manifest; ManifestInstaller target = AddInstaller(manifest, GetSystemArchitecture(), InstallerTypeEnum::Wix, ScopeEnum::Unknown, "", ""); ManifestInstaller foil = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Unknown, "", ""); ManifestComparatorTestContext context; IPackageVersion::Metadata installationMetadata; installationMetadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(foil.EffectiveInstallerType()); ManifestComparator mc(GetManifestComparatorOptions(context, installationMetadata)); auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest); RequireInstaller(result, target); } ================================================ FILE: src/AppInstallerCLITests/MatchCriteriaResolver.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestHooks.h" #include "TestSource.h" #include "AppInstallerStrings.h" #include "MatchCriteriaResolver.h" using namespace AppInstaller; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility; using namespace TestCommon; void RequireMatchCriteria(const PackageMatchFilter& expected, const PackageMatchFilter& actual) { REQUIRE(expected.Field == actual.Field); REQUIRE(expected.Type == actual.Type); REQUIRE(expected.Value == actual.Value); } TEST_CASE("MatchCriteriaResolver_MatchType", "[MatchCriteriaResolver]") { Manifest::Manifest manifest; PackageMatchFilter expected{ PackageMatchField::Id, MatchType::Wildcard, "Not set by test" }; std::string searchString = "Search"; SECTION("Exact") { manifest.Id = searchString; expected.Type = MatchType::Exact; } SECTION("Case Insensitive") { manifest.Id = "search"; expected.Type = MatchType::CaseInsensitive; } SECTION("Starts With") { manifest.Id = "Search Result"; expected.Type = MatchType::StartsWith; } SECTION("Substring") { manifest.Id = "Contains searches within"; expected.Type = MatchType::Substring; } SECTION("None") { expected.Field = PackageMatchField::Unknown; } expected.Value = manifest.Id; SearchRequest request; request.Query = RequestMatch{ MatchType::Substring, searchString }; TestPackageVersion packageVersion(manifest); PackageMatchFilter actual = FindBestMatchCriteria(request, &packageVersion); RequireMatchCriteria(expected, actual); } TEST_CASE("MatchCriteriaResolver_MatchField", "[MatchCriteriaResolver]") { Manifest::Manifest manifest; Utility::NormalizedString searchString = "Search"; auto foldedSearchString = Utility::FoldCase(searchString); PackageMatchFilter expected{ PackageMatchField::Unknown, MatchType::Exact, searchString }; SECTION("Identifier") { manifest.Id = searchString; expected.Field = PackageMatchField::Id; } SECTION("Name") { manifest.DefaultLocalization.Add(searchString); expected.Field = PackageMatchField::Name; } SECTION("Moniker") { manifest.Moniker = searchString; expected.Field = PackageMatchField::Moniker; } SECTION("Command") { manifest.Installers.emplace_back().Commands.emplace_back(searchString); expected.Field = PackageMatchField::Command; } SECTION("Tag") { manifest.DefaultLocalization.Add({ searchString }); expected.Field = PackageMatchField::Tag; } SECTION("Package Family Name") { manifest.Installers.emplace_back().PackageFamilyName = searchString; expected.Field = PackageMatchField::PackageFamilyName; // Folded by test package version expected.Type = MatchType::CaseInsensitive; expected.Value = foldedSearchString; } SECTION("Product Code") { manifest.Installers.emplace_back().ProductCode = searchString; expected.Field = PackageMatchField::ProductCode; // Folded by test package version expected.Type = MatchType::CaseInsensitive; expected.Value = foldedSearchString; } SECTION("Upgrade Code") { manifest.Installers.emplace_back().AppsAndFeaturesEntries.emplace_back().UpgradeCode = searchString; expected.Field = PackageMatchField::UpgradeCode; // Folded by test package version expected.Type = MatchType::CaseInsensitive; expected.Value = foldedSearchString; } SearchRequest request; request.Query = RequestMatch{ MatchType::Substring, searchString }; TestPackageVersion packageVersion(manifest); PackageMatchFilter actual = FindBestMatchCriteria(request, &packageVersion); RequireMatchCriteria(expected, actual); } TEST_CASE("MatchCriteriaResolver_Complex", "[MatchCriteriaResolver]") { Manifest::Manifest manifest; Utility::NormalizedString searchString = "Search"; auto foldedSearchString = Utility::FoldCase(searchString); PackageMatchFilter expected{ PackageMatchField::Tag, MatchType::Exact, searchString }; manifest.Id = "Identifer search substring"; manifest.DefaultLocalization.Add("Search name starts"); manifest.Moniker = foldedSearchString; manifest.Installers.emplace_back().Commands.emplace_back("Command search string"); manifest.DefaultLocalization.Add({ searchString }); manifest.Installers.emplace_back().PackageFamilyName = searchString; manifest.Installers.emplace_back().ProductCode = searchString; manifest.Installers.emplace_back().AppsAndFeaturesEntries.emplace_back().UpgradeCode = searchString; SearchRequest request; request.Query = RequestMatch{ MatchType::Substring, searchString }; TestPackageVersion packageVersion(manifest); PackageMatchFilter actual = FindBestMatchCriteria(request, &packageVersion); RequireMatchCriteria(expected, actual); } ================================================ FILE: src/AppInstallerCLITests/MsiExecArguments.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace std::string_view_literals; using namespace AppInstaller; TEST_CASE("MsiExecArgs_ParseEmpty", "[msiexec]") { std::vector emptyArguments = { ""sv, " ", "\t" }; for (const auto argString : emptyArguments) { auto args = Msi::ParseMSIArguments(argString); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } } TEST_CASE("MsiExecArgs_ParseUILevel", "[msiexec]") { { auto args = Msi::ParseMSIArguments("/qn"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == (INSTALLUILEVEL_NONE | INSTALLUILEVEL_UACONLY)); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/qb+"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == (INSTALLUILEVEL_BASIC | INSTALLUILEVEL_ENDDIALOG)); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/q"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == (INSTALLUILEVEL_NONE | INSTALLUILEVEL_UACONLY)); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/qr"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == (INSTALLUILEVEL_REDUCED)); REQUIRE(args.Properties.empty()); } REQUIRE_THROWS_HR(Msi::ParseMSIArguments("/qr-"sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); REQUIRE_THROWS_HR(Msi::ParseMSIArguments("/q arg"sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } TEST_CASE("MsiExecArgs_ParseLogMode", "[msiexec]") { { auto args = Msi::ParseMSIArguments("/l file.txt"sv); REQUIRE(args.LogMode == Msi::DefaultLogMode); REQUIRE(args.LogAttributes == 0); REQUIRE(args.LogFile == L"file.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/le errors.txt"sv); REQUIRE(args.LogMode == INSTALLLOGMODE_ERROR); REQUIRE(args.LogAttributes == 0); REQUIRE(args.LogFile == L"errors.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/l! flush.txt"sv); REQUIRE(args.LogMode == Msi::DefaultLogMode); REQUIRE(args.LogAttributes == INSTALLLOGATTRIBUTES_FLUSHEACHLINE); REQUIRE(args.LogFile == L"flush.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/l\"i\" \"quoted path.txt\""sv); REQUIRE(args.LogMode == INSTALLLOGMODE_INFO); REQUIRE(args.LogAttributes == 0); REQUIRE(args.LogFile == L"quoted path.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/liwpx+! log.txt"sv); REQUIRE(args.LogMode == (INSTALLLOGMODE_INFO | INSTALLLOGMODE_WARNING | INSTALLLOGMODE_PROPERTYDUMP | INSTALLLOGMODE_EXTRADEBUG)); REQUIRE(args.LogAttributes == (INSTALLLOGATTRIBUTES_FLUSHEACHLINE | INSTALLLOGATTRIBUTES_APPEND)); REQUIRE(args.LogFile == L"log.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/l* all.txt"sv); REQUIRE(args.LogMode == Msi::AllLogMode); REQUIRE(args.LogAttributes == 0); REQUIRE(args.LogFile == L"all.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/l* \"without closing quote.txt"sv); REQUIRE(args.LogMode == Msi::AllLogMode); REQUIRE(args.LogAttributes == 0); REQUIRE(args.LogFile == L"without closing quote.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } REQUIRE_THROWS_HR(Msi::ParseMSIArguments("/l"sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); REQUIRE_THROWS_HR(Msi::ParseMSIArguments("/lz log.txt"sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } TEST_CASE("MsiExecArgs_ParseProperties", "[msiexec]") { { auto args = Msi::ParseMSIArguments("PROPERTY=value"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties == L" PROPERTY=value"sv); } { auto args = Msi::ParseMSIArguments("EMPTY="sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties == L" EMPTY="sv); } { auto args = Msi::ParseMSIArguments("PROPERTY=\"quoted value\""sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties == L" PROPERTY=\"quoted value\""sv); } { auto args = Msi::ParseMSIArguments("PROPERTY=\"escaped \"\" quotes\""sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties == L" PROPERTY=\"escaped \"\" quotes\""sv); } { auto args = Msi::ParseMSIArguments("PROPERTY1=value1 PROPERTY2=value2"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties == L" PROPERTY1=value1 PROPERTY2=value2"sv); } REQUIRE_THROWS_HR(Msi::ParseMSIArguments("NOSEPARATOR"sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); REQUIRE_THROWS_HR(Msi::ParseMSIArguments("$NOTAPROPERTY=value"sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); REQUIRE_THROWS_HR(Msi::ParseMSIArguments("PROPERTY=not quoted"sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); REQUIRE_THROWS_HR(Msi::ParseMSIArguments("PROPERTY=\"bad \"internal\" quotes\""sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); REQUIRE_THROWS_HR(Msi::ParseMSIArguments("PROPERTY=\"mismatched quote"sv), APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } TEST_CASE("MsiExecArgs_ParseMultipleOptions", "[msiexec]") { { auto args = Msi::ParseMSIArguments("/li first.txt /le second.txt"sv); REQUIRE(args.LogMode == INSTALLLOGMODE_ERROR); REQUIRE(args.LogAttributes == 0); REQUIRE(args.LogFile == L"second.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("PROPERTY1=value1 /qb PROPERTY2= /lw file.txt"sv); REQUIRE(args.LogMode == INSTALLLOGMODE_WARNING); REQUIRE(args.LogAttributes == 0); REQUIRE(args.LogFile == L"file.txt"sv); REQUIRE(args.UILevel == INSTALLUILEVEL_BASIC); REQUIRE(args.Properties == L" PROPERTY1=value1 PROPERTY2="); } } TEST_CASE("MsiExecArgs_ParseLongOptions", "[msiexec]") { { auto args = Msi::ParseMSIArguments("/quiet"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == (INSTALLUILEVEL_NONE | INSTALLUILEVEL_UACONLY)); REQUIRE(args.Properties.empty()); } { auto args = Msi::ParseMSIArguments("/passive"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == (INSTALLUILEVEL_BASIC | INSTALLUILEVEL_PROGRESSONLY | INSTALLUILEVEL_HIDECANCEL)); REQUIRE(args.Properties == L" REBOOTPROMPT=S"sv); } { auto args = Msi::ParseMSIArguments("/NoRestart"sv); REQUIRE(!args.LogFile.has_value()); REQUIRE(args.UILevel == INSTALLUILEVEL_DEFAULT); REQUIRE(args.Properties == L" REBOOT=ReallySuppress"sv); } } ================================================ FILE: src/AppInstallerCLITests/MsixInfo.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace TestCommon; using namespace AppInstaller; constexpr std::string_view s_MsixFile_1 = "index.1.0.0.0.msix"; constexpr std::string_view s_MsixFile_2 = "index.2.0.0.0.msix"; constexpr std::string_view s_MsixFileSigned_1 = "index.1.0.0.0.signed.msix"; TEST_CASE("MsixInfo_GetPackageFullName", "[msixinfo]") { TestDataFile index(s_MsixFile_1); Msix::MsixInfo msix(index.GetPath()); std::string expectedFullName = "AppInstallerCLITestsFakeIndex_1.0.0.0_neutral__125rzkzqaqjwj"; std::string actualFullName = msix.GetPackageFullName(); REQUIRE(expectedFullName == actualFullName); } TEST_CASE("MsixInfo_CompareToSelf", "[msixinfo]") { TestDataFile index(s_MsixFile_1); Msix::MsixInfo msix(index.GetPath()); REQUIRE(!msix.IsNewerThan(index.GetPath().u8string())); } TEST_CASE("MsixInfo_CompareToOlder", "[msixinfo]") { TestDataFile index1(s_MsixFile_1); TestDataFile index2(s_MsixFile_2); Msix::MsixInfo msix2(index2.GetPath()); REQUIRE(msix2.IsNewerThan(index1)); } TEST_CASE("MsixInfo_WriteFile", "[msixinfo]") { TestDataFile index(s_MsixFile_1); Msix::MsixInfo msix(index.GetPath()); TempFile file{ "msixtest_file"s, ".bin"s }; ProgressCallback callback; msix.WriteToFile("Public\\index.db", file, callback); REQUIRE(1 == std::filesystem::file_size(file)); } TEST_CASE("MsixInfo_ValidateMsixTrustInfo", "[msixinfo]") { if (!Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestDataFile notSigned{ s_MsixFile_1 }; Msix::WriteLockedMsixFile notSignedWriteLocked{ notSigned }; REQUIRE_FALSE(notSignedWriteLocked.ValidateTrustInfo(false)); TestDataFile testSigned{ s_MsixFileSigned_1 }; Msix::WriteLockedMsixFile testSignedWriteLocked{ testSigned }; // Remove the cert if already trusted bool certExistsBeforeTest = UninstallCertFromSignedPackage(testSigned); REQUIRE_FALSE(testSignedWriteLocked.ValidateTrustInfo(false)); // Add the cert to trusted InstallCertFromSignedPackage(testSigned); REQUIRE(testSignedWriteLocked.ValidateTrustInfo(false)); REQUIRE_FALSE(testSignedWriteLocked.ValidateTrustInfo(true)); TestCommon::TempFile microsoftSigned{ "testIndex"s, ".msix"s }; ProgressCallback callback; Utility::Download("https://cdn.winget.microsoft.com/cache/source2.msix", microsoftSigned.GetPath(), Utility::DownloadType::Index, callback); Msix::WriteLockedMsixFile microsoftSignedWriteLocked{ microsoftSigned }; REQUIRE(microsoftSignedWriteLocked.ValidateTrustInfo(true)); if (!certExistsBeforeTest) { UninstallCertFromSignedPackage(testSigned); } } TEST_CASE("MsixInfo_GetPackageIdInfoFromFullName", "[msixinfo]") { auto testPackageIdInfo = Msix::GetPackageIdInfoFromFullName("Microsoft.NET.Native.Framework.2.2_2.2.29512.0_arm64__8wekyb3d8bbwe"); REQUIRE(testPackageIdInfo.Name == "Microsoft.NET.Native.Framework.2.2"); REQUIRE(testPackageIdInfo.Version == Utility::UInt64Version{ "2.2.29512.0" }); auto testPackageIdInfo2 = Msix::GetPackageIdInfoFromFullName("Microsoft.DoesNotExist_1.2.3.4_neutral_~_8wekyb3d8bbwe"); REQUIRE(testPackageIdInfo2.Name == "Microsoft.DoesNotExist"); REQUIRE(testPackageIdInfo2.Version == Utility::UInt64Version{ "1.2.3.4" }); } ================================================ FILE: src/AppInstallerCLITests/MsixManifest.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Msix; using namespace Microsoft::WRL; namespace { // Input values constexpr std::string_view installerGoodMsix = "Installer-Good.msix"; constexpr std::string_view installerGoodMsixBundle = "Installer-Good.msixbundle"; // Expected constexpr std::string_view expectedFamilyName = "FakeInstallerForTesting_125rzkzqaqjwj"; PackageVersion expectedPackageVersion { 0xAAAABBBBCCCCDDDD }; constexpr std::string_view expectedWindowsDesktopName = "Windows.Desktop"; OSVersion expectedWindowsDesktopMinVersion { "10.0.16299.0" }; OSVersion expectedWindowsUniversalMinVersion { "10.0.0.0" }; } TEST_CASE("MsixManifest_ValidateFieldsParsedFromManifestReader", "[MsixManifest]") { ComPtr manifestReader; REQUIRE(GetMsixPackageManifestReader(installerGoodMsix, &manifestReader)); Msix::MsixPackageManifest msixManifest(manifestReader); REQUIRE(expectedFamilyName == msixManifest.GetIdentity().GetPackageFamilyName()); REQUIRE(expectedPackageVersion == msixManifest.GetIdentity().GetVersion()); REQUIRE(2 == msixManifest.GetTargetDeviceFamilies().size()); REQUIRE(expectedWindowsUniversalMinVersion == msixManifest.GetMinimumOSVersionForSupportedPlatforms().value()); auto targets = msixManifest.GetTargetDeviceFamilies(); auto windowsDesktop = std::find_if(targets.begin(), targets.end(), [](auto& t) { return t.GetMinVersion() == expectedWindowsDesktopMinVersion; }); REQUIRE(windowsDesktop != targets.end()); auto windowsUniversal = std::find_if(targets.begin(), targets.end(), [](auto& t) { return t.GetMinVersion() == expectedWindowsUniversalMinVersion; }); REQUIRE(windowsUniversal != targets.end()); } TEST_CASE("MsixManifest_ValidateFieldsParsedFromMsix", "[MsixManifest]") { TestDataFile testFile(installerGoodMsix); MsixInfo msixInfo(testFile.GetPath()); auto appPackageManifests = msixInfo.GetAppPackageManifests(); REQUIRE(1 == appPackageManifests.size()); auto &appPackageManifest = appPackageManifests[0]; REQUIRE(expectedFamilyName == appPackageManifest.GetIdentity().GetPackageFamilyName()); REQUIRE(expectedPackageVersion == appPackageManifest.GetIdentity().GetVersion()); REQUIRE(2 == appPackageManifest.GetTargetDeviceFamilies().size()); REQUIRE(expectedWindowsUniversalMinVersion == appPackageManifest.GetMinimumOSVersionForSupportedPlatforms().value()); auto targets = appPackageManifest.GetTargetDeviceFamilies(); auto windowsDesktop = std::find_if(targets.begin(), targets.end(), [](auto& t) { return t.GetMinVersion() == expectedWindowsDesktopMinVersion; }); REQUIRE(windowsDesktop != targets.end()); auto windowsUniversal = std::find_if(targets.begin(), targets.end(), [](auto& t) { return t.GetMinVersion() == expectedWindowsUniversalMinVersion; }); REQUIRE(windowsUniversal != targets.end()); } TEST_CASE("MsixManifest_ValidateFieldsParsedFromMsixBundle", "[MsixManifest]") { TestDataFile testFile(installerGoodMsixBundle); MsixInfo msixInfo(testFile.GetPath()); auto appPackageManifests = msixInfo.GetAppPackageManifests(); REQUIRE(2 == appPackageManifests.size()); for (auto& appPackageManifest : appPackageManifests) { REQUIRE(expectedFamilyName == appPackageManifest.GetIdentity().GetPackageFamilyName()); REQUIRE(expectedPackageVersion == appPackageManifest.GetIdentity().GetVersion()); REQUIRE(1 == appPackageManifest.GetTargetDeviceFamilies().size()); REQUIRE(expectedWindowsDesktopName == appPackageManifest.GetTargetDeviceFamilies().front().GetName()); REQUIRE(expectedWindowsDesktopMinVersion == appPackageManifest.GetTargetDeviceFamilies().front().GetMinVersion()); REQUIRE(expectedWindowsDesktopMinVersion == appPackageManifest.GetMinimumOSVersionForSupportedPlatforms().value()); } } ================================================ FILE: src/AppInstallerCLITests/NameNormalization.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace std::string_view_literals; using namespace AppInstaller::Utility; // This skipped test case can be used to update the test file. // It writes back to the output content location, so you must manually // copy the file(s) back to the git managed location to update. TEST_CASE("NameNorm_Update_Database_Initial", "[.]") { std::ifstream namesStream(TestCommon::TestDataFile("InputNames.txt").GetPath()); REQUIRE(namesStream); std::ifstream publishersStream(TestCommon::TestDataFile("InputPublishers.txt").GetPath()); REQUIRE(publishersStream); std::ofstream resultsStream(TestCommon::TestDataFile("NormalizationInitialIdsUpdate.txt").GetPath(), std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); REQUIRE(resultsStream); // Far larger than any one value; hopefully char name[4096]{}; char publisher[4096]{}; NameNormalizer normer(NormalizationVersion::Initial); for (;;) { namesStream.getline(name, ARRAYSIZE(name)); publishersStream.getline(publisher, ARRAYSIZE(publisher)); if (namesStream || publishersStream) { REQUIRE(namesStream); REQUIRE(publishersStream); INFO("Name[" << name << "], Publisher[" << publisher << "]"); auto normalized = normer.Normalize(name, publisher); std::string normalizedId = normalized.Publisher(); normalizedId += '.'; normalizedId += normalized.Name(); resultsStream << normalizedId << std::endl; REQUIRE(resultsStream); } else { break; } } } // If this test is failing, either changes to winget code or the ICU binaries have caused it. // This will impact the functionality of the PreIndexedPackageSource, as it is the primary // mechanism used to cross reference packages installed outside of winget with those in the // source. TEST_CASE("NameNorm_Database_Initial", "[name_norm]") { std::ifstream namesStream(TestCommon::TestDataFile("InputNames.txt").GetPath()); REQUIRE(namesStream); std::ifstream publishersStream(TestCommon::TestDataFile("InputPublishers.txt").GetPath()); REQUIRE(publishersStream); std::ifstream resultsStream(TestCommon::TestDataFile("NormalizationInitialIds.txt").GetPath()); REQUIRE(resultsStream); // Far larger than any one value; hopefully char name[4096]{}; char publisher[4096]{}; char expectedId[4096]{}; NameNormalizer normer(NormalizationVersion::Initial); for (;;) { namesStream.getline(name, ARRAYSIZE(name)); publishersStream.getline(publisher, ARRAYSIZE(publisher)); resultsStream.getline(expectedId, ARRAYSIZE(expectedId)); if (namesStream || publishersStream || resultsStream) { REQUIRE(namesStream); REQUIRE(publishersStream); REQUIRE(resultsStream); INFO("Name[" << name << "], Publisher[" << publisher << "]"); auto normalized = normer.Normalize(name, publisher); std::string normalizedId = normalized.Publisher(); normalizedId += '.'; normalizedId += normalized.Name(); REQUIRE(expectedId == normalizedId); } else { break; } } } TEST_CASE("NameNorm_Architecture", "[name_norm]") { NameNormalizer normer(NormalizationVersion::Initial); REQUIRE(normer.Normalize("Name", {}).Architecture() == Architecture::Unknown); REQUIRE(normer.Normalize("Name x86", {}).Architecture() == Architecture::X86); REQUIRE(normer.Normalize("Name x86_64", {}).Architecture() == Architecture::X64); REQUIRE(normer.Normalize("Name (64 bit)", {}).Architecture() == Architecture::X64); REQUIRE(normer.Normalize("Name 32/64 bit", {}).Architecture() == Architecture::Unknown); REQUIRE(normer.Normalize("Fox86", {}).Architecture() == Architecture::Unknown); } TEST_CASE("NameNorm_Locale", "[name_norm]") { NameNormalizer normer(NormalizationVersion::Initial); REQUIRE(normer.Normalize("Name", {}).Locale() == ""); REQUIRE(normer.Normalize("Name en-US", {}).Locale() == "en-us"); REQUIRE(normer.Normalize("Name (es-mx)", {}).Locale() == "es-mx"); REQUIRE(normer.Normalize("Names-mx", {}).Locale() == ""); } TEST_CASE("NameNorm_KBNumbers", "[name_norm]") { NameNormalizer normer(NormalizationVersion::Initial); REQUIRE(normer.Normalize("Fix for (KB42)", {}).Name() == "FixforKB42"); } TEST_CASE("NameNorm_Initial_PreserveWhitespace", "[name_norm]") { NameNormalizer normer(NormalizationVersion::InitialPreserveWhiteSpace); REQUIRE(normer.NormalizeName("Some Name").Name() == "Some Name"); REQUIRE(normer.NormalizePublisher("Some Publisher Corp") == "Some Publisher"); } TEST_CASE("NameNorm_GetNormalizedName_GetNormalizedFields", "[name_norm]") { NameNormalizer normer(NormalizationVersion::Initial); auto normalizedName = normer.NormalizeName("Name(X64)"); REQUIRE(normalizedName.GetNormalizedName(NormalizationField::None) == "Name"); REQUIRE(normalizedName.GetNormalizedName(NormalizationField::Architecture) == "Name(X64)"); REQUIRE(normalizedName.GetNormalizedFields() == NormalizationField::Architecture); auto normalizedName2 = normer.NormalizeName("Name"); REQUIRE(normalizedName2.GetNormalizedName(NormalizationField::None) == "Name"); REQUIRE(normalizedName2.GetNormalizedName(NormalizationField::Architecture) == "Name"); REQUIRE(normalizedName2.GetNormalizedFields() == NormalizationField::None); } ================================================ FILE: src/AppInstallerCLITests/PackageCollection.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include // Duplicating here because a change to these values in the product *REALLY* needs to be thought through. using namespace std::string_literals; using namespace std::string_view_literals; using namespace AppInstaller::CLI; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility; const std::string s_PackagesJson_Schema = "$schema"; const std::string s_PackagesJson_SchemaUri_v2_0 = "https://aka.ms/winget-packages.schema.2.0.json"; const std::string s_PackagesJson_WinGetVersion = "WinGetVersion"; const std::string s_PackagesJson_CreationDate = "CreationDate"; const std::string s_PackagesJson_Sources = "Sources"; const std::string s_PackagesJson_Source_Details = "SourceDetails"; const std::string s_PackagesJson_Source_Name = "Name"; const std::string s_PackagesJson_Source_Identifier = "Identifier"; const std::string s_PackagesJson_Source_Argument = "Argument"; const std::string s_PackagesJson_Source_Type = "Type"; const std::string s_PackagesJson_Packages = "Packages"; const std::string s_PackagesJson_Package_PackageIdentifier = "PackageIdentifier"; const std::string s_PackagesJson_Package_Version = "Version"; const std::string s_PackagesJson_Package_Channel = "Channel"; namespace { Json::Value ParseJsonString(const std::string& jsonString) { Json::Value root; std::stringstream{ jsonString } >> root; return root; } void ValidateJsonStringProperty(const Json::Value& node, const std::string& propertyName, std::string_view expectedValue, bool allowMissing = false) { if (allowMissing && expectedValue.empty() && !node.isMember(propertyName)) { return; } REQUIRE(node.isMember(propertyName)); REQUIRE(node[propertyName].isString()); if (!expectedValue.empty()) { REQUIRE(node[propertyName].asString() == expectedValue); } } const Json::Value& GetAndValidateJsonProperty(const Json::Value& node, const std::string& propertyName, Json::ValueType valueType) { REQUIRE(node.isMember(propertyName)); REQUIRE(node[propertyName].type() == valueType); return node[propertyName]; } void ValidateJsonWithCollection(const Json::Value& root, const PackageCollection& collection) { ValidateJsonStringProperty(root, s_PackagesJson_Schema, s_PackagesJson_SchemaUri_v2_0); ValidateJsonStringProperty(root, s_PackagesJson_WinGetVersion, collection.ClientVersion); // valijson now validates the date-time format, just check the string property exists. ValidateJsonStringProperty(root, s_PackagesJson_CreationDate, ""); const auto& jsonSources = GetAndValidateJsonProperty(root, s_PackagesJson_Sources, Json::ValueType::arrayValue); REQUIRE(jsonSources.size() == collection.Sources.size()); // Expect the order to be the same. Not really needed, but it makes things easier. auto jsonSourceItr = jsonSources.begin(); auto sourceItr = collection.Sources.begin(); for (; jsonSourceItr != jsonSources.end(); ++jsonSourceItr, ++sourceItr) { REQUIRE(jsonSourceItr->isObject()); const auto& jsonSourceDetails = GetAndValidateJsonProperty(*jsonSourceItr, s_PackagesJson_Source_Details, Json::ValueType::objectValue); ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Name, sourceItr->Details.Name); ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Argument, sourceItr->Details.Arg); ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Type, sourceItr->Details.Type); ValidateJsonStringProperty(jsonSourceDetails, s_PackagesJson_Source_Identifier, sourceItr->Details.Identifier); const auto& jsonPackages = GetAndValidateJsonProperty(*jsonSourceItr, s_PackagesJson_Packages, Json::ValueType::arrayValue); REQUIRE(jsonPackages.size() == sourceItr->Packages.size()); auto jsonPackageItr = jsonPackages.begin(); auto packageItr = sourceItr->Packages.begin(); for (; jsonPackageItr != jsonPackages.end(); ++jsonPackageItr, ++packageItr) { REQUIRE(jsonPackageItr->isObject()); ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_PackageIdentifier, packageItr->Id); ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_Version, packageItr->VersionAndChannel.GetVersion().ToString(), true); ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_Channel, packageItr->VersionAndChannel.GetChannel().ToString(), true); } } } void ValidateEqualCollections(const PackageCollection& first, const PackageCollection& second) { REQUIRE(first.ClientVersion == second.ClientVersion); REQUIRE(first.Sources.size() == second.Sources.size()); auto firstSourceItr = first.Sources.begin(); auto secondSourceItr = second.Sources.begin(); for (; firstSourceItr != first.Sources.end(); ++firstSourceItr, ++secondSourceItr) { REQUIRE(firstSourceItr->Details.Name == secondSourceItr->Details.Name); REQUIRE(firstSourceItr->Details.Arg == secondSourceItr->Details.Arg); REQUIRE(firstSourceItr->Details.Type == secondSourceItr->Details.Type); REQUIRE(firstSourceItr->Details.Identifier == secondSourceItr->Details.Identifier); REQUIRE(firstSourceItr->Packages.size() == secondSourceItr->Packages.size()); auto firstPackageItr = firstSourceItr->Packages.begin(); auto secondPackageItr = secondSourceItr->Packages.begin(); for (; firstPackageItr != firstSourceItr->Packages.end(); ++firstPackageItr, ++secondPackageItr) { REQUIRE(firstPackageItr->Id == secondPackageItr->Id); REQUIRE(firstPackageItr->VersionAndChannel.ToString() == secondPackageItr->VersionAndChannel.ToString()); } } } } TEST_CASE("PackageCollection_Write_SingleSource", "[PackageCollection]") { PackageCollection::Source source; source.Details.Name = "TestSource"; source.Details.Arg = "https://aka.ms/winget"; source.Details.Type = "Microsoft.PreIndexed.Package"; source.Details.Identifier = "TestSourceId"; source.Packages.emplace_back(LocIndString{ "test.package1"sv }, Version{ "1.0.1" }, Channel{ "" }); source.Packages.emplace_back(LocIndString{ "test.package2"sv }, Version{ "2" }, Channel{ "Public" }); PackageCollection pc { "0.1.2.3", std::vector{ source } }; ValidateJsonWithCollection(PackagesJson::CreateJson(pc), pc); } TEST_CASE("PackageCollection_Write_MultipleSources", "[PackageCollection]") { PackageCollection::Source source1; source1.Details.Name = "TestSource"; source1.Details.Arg = "https://aka.ms/winget"; source1.Details.Type = "Microsoft.PreIndexed.Package"; source1.Details.Identifier = "TestSourceId"; source1.Packages.emplace_back(LocIndString{ "test.package1"sv }, Version{ "1.0.1" }, Channel{ "" }); PackageCollection::Source source2; source2.Details.Name = "TestSource2"; source2.Details.Arg = "https://aka.ms/winget"; source2.Details.Type = "*Test"; source2.Details.Identifier = "SecondId"; source2.Packages.emplace_back(LocIndString{ "test.package2"sv }, Version{ "2.1.0" }, Channel{ "Beta" }); PackageCollection pc { "1.0.0.0", std::vector{ source1, source2 } }; ValidateJsonWithCollection(PackagesJson::CreateJson(pc), pc); } TEST_CASE("PackageCollection_Read_SingleSource_1_0", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "test.WithVersion", "Version": "0.1", "Channel": "Preview" }, { "Id": "test.NoVersion" } ], "SourceDetails": { "Argument": "https://aka.ms/winget", "Identifier": "TestSourceId", "Name": "TestSource", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" })"); auto parseResult = PackagesJson::TryParseJson(json); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::Success); REQUIRE(parseResult.Errors.empty()); PackageCollection::Source source; source.Details.Name = "TestSource"; source.Details.Arg = "https://aka.ms/winget"; source.Details.Type = "Microsoft.PreIndexed.Package"; source.Details.Identifier = "TestSourceId"; source.Packages.emplace_back(LocIndString{ "test.WithVersion"sv }, Version{ "0.1" }, Channel{ "Preview" }); source.Packages.emplace_back(LocIndString{ "test.NoVersion"sv }, Version{ "" }, Channel{ "" }); PackageCollection expected { "1.0.0", std::vector{ source } }; ValidateEqualCollections(parseResult.Packages, expected); } TEST_CASE("PackageCollection_Read_SingleSource_2_0", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-packages.schema.2.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "PackageIdentifier": "test.WithVersion", "Version": "0.1", "Channel": "Preview" }, { "PackageIdentifier": "test.NoVersion" } ], "SourceDetails": { "Argument": "https://aka.ms/winget", "Identifier": "TestSourceId", "Name": "TestSource", "Type": "Microsoft.PreIndexed.Package" } } ] })"); auto parseResult = PackagesJson::TryParseJson(json); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::Success); REQUIRE(parseResult.Errors.empty()); PackageCollection::Source source; source.Details.Name = "TestSource"; source.Details.Arg = "https://aka.ms/winget"; source.Details.Type = "Microsoft.PreIndexed.Package"; source.Details.Identifier = "TestSourceId"; source.Packages.emplace_back(LocIndString{ "test.WithVersion"sv }, Version{ "0.1" }, Channel{ "Preview" }); source.Packages.emplace_back(LocIndString{ "test.NoVersion"sv }, Version{ "" }, Channel{ "" }); PackageCollection expected { "", std::vector{ source } }; ValidateEqualCollections(parseResult.Packages, expected); } TEST_CASE("PackageCollection_Read_MultipleSources_1_0", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "WinGetVersion": "1.0.0", "Sources": [ { "SourceDetails": { "Argument": "//firstSource", "Identifier": "Id1", "Name": "First", "Type": "Microsoft.PreIndexed.Package" }, "Packages": [ { "Id": "test" } ] }, { "SourceDetails": { "Argument": "//secondSource", "Identifier": "Id2", "Name": "Second", "Type": "*TestSource" }, "Packages": [ { "Id": "test2", "Version": "1.0" } ] } ] })"); auto parseResult = PackagesJson::TryParseJson(json); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::Success); REQUIRE(parseResult.Errors.empty()); PackageCollection::Source source1; source1.Details.Name = "First"; source1.Details.Arg = "//firstSource"; source1.Details.Type = "Microsoft.PreIndexed.Package"; source1.Details.Identifier = "Id1"; source1.Packages.emplace_back(LocIndString{ "test"sv }, Version{ "" }, Channel{ "" }); PackageCollection::Source source2; source2.Details.Name = "Second"; source2.Details.Arg = "//secondSource"; source2.Details.Type = "*TestSource"; source2.Details.Identifier = "Id2"; source2.Packages.emplace_back(LocIndString{ "test2"sv }, Version{ "1.0" }, Channel{ "" }); PackageCollection expected { "1.0.0", std::vector{ source1, source2 } }; ValidateEqualCollections(parseResult.Packages, expected); } TEST_CASE("PackageCollection_Read_MultipleSources_2_0", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-packages.schema.2.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "WinGetVersion": "1.0.0", "Sources": [ { "SourceDetails": { "Argument": "//firstSource", "Identifier": "Id1", "Name": "First", "Type": "Microsoft.PreIndexed.Package" }, "Packages": [ { "PackageIdentifier": "test" } ] }, { "SourceDetails": { "Argument": "//secondSource", "Identifier": "Id2", "Name": "Second", "Type": "*TestSource" }, "Packages": [ { "PackageIdentifier": "test2", "Version": "1.0" } ] } ] })"); auto parseResult = PackagesJson::TryParseJson(json); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::Success); REQUIRE(parseResult.Errors.empty()); PackageCollection::Source source1; source1.Details.Name = "First"; source1.Details.Arg = "//firstSource"; source1.Details.Type = "Microsoft.PreIndexed.Package"; source1.Details.Identifier = "Id1"; source1.Packages.emplace_back(LocIndString{ "test"sv }, Version{ "" }, Channel{ "" }); PackageCollection::Source source2; source2.Details.Name = "Second"; source2.Details.Arg = "//secondSource"; source2.Details.Type = "*TestSource"; source2.Details.Identifier = "Id2"; source2.Packages.emplace_back(LocIndString{ "test2"sv }, Version{ "1.0" }, Channel{ "" }); PackageCollection expected { "1.0.0", std::vector{ source1, source2 } }; ValidateEqualCollections(parseResult.Packages, expected); } TEST_CASE("PackageCollection_Read_RepeatedSource", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "WinGetVersion": "1.0.0", "Sources": [ { "SourceDetails": { "Argument": "//firstSource", "Identifier": "Id1", "Name": "First", "Type": "Microsoft.PreIndexed.Package" }, "Packages": [ { "Id": "test" } ] }, { "SourceDetails": { "Argument": "//secondSource", "Identifier": "Id2", "Name": "Second", "Type": "*TestSource" }, "Packages": [ { "Id": "test2", "Version": "1.0" } ] }, { "SourceDetails": { "Argument": "//secondSource", "Identifier": "Id2", "Name": "Second", "Type": "*TestSource" }, "Packages": [ { "Id": "test3", "Version": "1.2" } ] } ] })"); auto parseResult = PackagesJson::TryParseJson(json); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::Success); REQUIRE(parseResult.Errors.empty()); PackageCollection::Source source1; source1.Details.Name = "First"; source1.Details.Arg = "//firstSource"; source1.Details.Type = "Microsoft.PreIndexed.Package"; source1.Details.Identifier = "Id1"; source1.Packages.emplace_back(LocIndString{ "test"sv }, Version{ "" }, Channel{ "" }); PackageCollection::Source source2; source2.Details.Name = "Second"; source2.Details.Arg = "//secondSource"; source2.Details.Type = "*TestSource"; source2.Details.Identifier = "Id2"; source2.Packages.emplace_back(LocIndString{ "test2"sv }, Version{ "1.0" }, Channel{ "" }); source2.Packages.emplace_back(LocIndString{ "test3"sv }, Version{ "1.2" }, Channel{ "" }); PackageCollection expected { "1.0.0", std::vector{ source1, source2 } }; ValidateEqualCollections(parseResult.Packages, expected); } TEST_CASE("PackageCollection_Read_MissingSchema", "[PackageCollection]") { auto json = ParseJsonString(R"( { "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "test.test" } ], "SourceDetails": { "Argument": "https://aka.ms/winget", "Identifier": "TestSourceId", "Name": "TestSource", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" })"); auto parseResult = PackagesJson::TryParseJson(json); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::MissingSchema); json = ParseJsonString("\"Not even a JSON object\""); parseResult = PackagesJson::TryParseJson(json); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::MissingSchema); } TEST_CASE("PackageCollection_Read_WrongSchema", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-settings.schema.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "test.test" } ], "SourceDetails": { "Argument": "https://aka.ms/winget", "Identifier": "TestSourceId", "Name": "TestSource", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" })"); auto parseResult = PackagesJson::TryParseJson(json); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::UnrecognizedSchema); } TEST_CASE("PackageCollection_Read_SchemaValidationFail", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "NotSources": [ { "Packages": [ { "Id": "test.test" } ], "SourceDetails": { "Argument": "https://aka.ms/winget", "Identifier": "TestSourceId", "Name": "TestSource", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" })"); auto parseResult = PackagesJson::TryParseJson(json); INFO(parseResult.Errors); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::SchemaValidationFailed); REQUIRE(parseResult.Errors.find("Missing required property 'Sources'.") != std::string::npos); } TEST_CASE("PackageCollection_Read_SchemaValidationFail_Id", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "NotId": "test.test" } ], "SourceDetails": { "Argument": "https://aka.ms/winget", "Identifier": "TestSourceId", "Name": "TestSource", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" })"); auto parseResult = PackagesJson::TryParseJson(json); INFO(parseResult.Errors); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::SchemaValidationFailed); REQUIRE(parseResult.Errors.find("Missing required property 'Id'.") != std::string::npos); } TEST_CASE("PackageCollection_Read_BadTimeStamp", "[PackageCollection]") { auto json = ParseJsonString(R"( { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021- 1- 1 12:00:00.000", "Sources": [ { "Packages": [ { "Id": "test" } ], "SourceDetails": { "Argument": "https://aka.ms/winget", "Identifier": "TestSourceId", "Name": "TestSource", "Type": "Microsoft.PreIndexed.Package" } } ], "WinGetVersion": "1.0.0" })"); auto parseResult = PackagesJson::TryParseJson(json); INFO(parseResult.Errors); REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::SchemaValidationFailed); REQUIRE_FALSE(parseResult.Errors.empty()); } ================================================ FILE: src/AppInstallerCLITests/PackageDependenciesValidationUtil.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSource.h" #include using namespace AppInstaller::Manifest; TEST_CASE("GetValidationResultFromException", "[PackageDependenciesValidationUtil][dependencies]") { std::map dependenciesErrorMessageMap = { { WinGetManifestDependenciesErrorResult::SingleManifestPackageHasDependencies, ManifestException { { ManifestError::SingleManifestPackageHasDependencies }, APPINSTALLER_CLI_ERROR_MANIFEST_FAILED } }, { WinGetManifestDependenciesErrorResult::MultiManifestPackageHasDependencies, ManifestException { { ManifestError::MultiManifestPackageHasDependencies }, APPINSTALLER_CLI_ERROR_MANIFEST_FAILED } }, { WinGetManifestDependenciesErrorResult::MissingManifestDependenciesNode, ManifestException { { ManifestError::MissingManifestDependenciesNode }, APPINSTALLER_CLI_ERROR_MANIFEST_FAILED } }, { WinGetManifestDependenciesErrorResult::NoSuitableMinVersionDependency, ManifestException { { ManifestError::NoSuitableMinVersionDependency }, APPINSTALLER_CLI_ERROR_MANIFEST_FAILED } }, { WinGetManifestDependenciesErrorResult::FoundDependencyLoop, ManifestException { { ManifestError::FoundDependencyLoop }, APPINSTALLER_CLI_ERROR_MANIFEST_FAILED } }, }; for (auto current : dependenciesErrorMessageMap) { REQUIRE(GetDependenciesValidationResultFromException(current.second) == current.first); } } ================================================ FILE: src/AppInstallerCLITests/PackageTrackingCatalog.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; using namespace AppInstaller::Utility; namespace { static Source SimpleTestSetup(const std::string& filePath, SourceDetails& details, Manifest& manifest, std::string& relativePath) { SQLiteIndex index = SQLiteIndex::CreateNew(filePath, AppInstaller::SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); TestDataFile testManifest("Manifest-Good.yaml"); manifest = YamlParser::CreateFromPath(testManifest); relativePath = testManifest.GetPath().filename().u8string(); index.AddManifest(manifest, relativePath); details.Identifier = "*SimpleTestSetup"; details.Name = "TestName"; details.Type = "TestType"; details.Arg = testManifest.GetPath().parent_path().u8string(); details.Data = ""; auto result = std::make_shared(details, std::move(index)); PackageTrackingCatalog::RemoveForSource(result->GetIdentifier()); return { result }; } struct TestCatalog : public PackageTrackingCatalog { using PackageTrackingCatalog::CreateForSource; }; PackageTrackingCatalog CreatePackageTrackingCatalogForSource(const Source& source) { return TestCatalog::CreateForSource(source); } } TEST_CASE("TrackingCatalog_Create", "[tracking_catalog]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; auto source = SimpleTestSetup(tempFile, details, manifest, relativePath); PackageTrackingCatalog catalog = CreatePackageTrackingCatalogForSource(source); } TEST_CASE("TrackingCatalog_Install", "[tracking_catalog]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; auto source = SimpleTestSetup(tempFile, details, manifest, relativePath); PackageTrackingCatalog catalog = CreatePackageTrackingCatalogForSource(source); SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, manifest.Id); SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 0); catalog.RecordInstall(manifest, manifest.Installers[0], false); SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); REQUIRE(resultAfter.Matches[0].Package->GetAvailable().size() == 1); auto trackingVersion = resultAfter.Matches[0].Package->GetAvailable()[0]->GetLatestVersion(); REQUIRE(trackingVersion); auto metadata = trackingVersion->GetMetadata(); REQUIRE(metadata.find(PackageVersionMetadata::TrackingWriteTime) != metadata.end()); } TEST_CASE("TrackingCatalog_Reinstall", "[tracking_catalog]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; auto source = SimpleTestSetup(tempFile, details, manifest, relativePath); PackageTrackingCatalog catalog = CreatePackageTrackingCatalogForSource(source); SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, manifest.Id); catalog.RecordInstall(manifest, manifest.Installers[0], false); SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 1); REQUIRE(resultBefore.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(resultBefore.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Name) == manifest.DefaultLocalization.Get()); // Change name std::string newName = "New Package Name"; manifest.DefaultLocalization.Add(newName); catalog.RecordInstall(manifest, manifest.Installers[0], false); SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); REQUIRE(resultAfter.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(resultAfter.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Name) == newName); } TEST_CASE("TrackingCatalog_Upgrade", "[tracking_catalog]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; auto source = SimpleTestSetup(tempFile, details, manifest, relativePath); PackageTrackingCatalog catalog = CreatePackageTrackingCatalogForSource(source); SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, manifest.Id); catalog.RecordInstall(manifest, manifest.Installers[0], false); SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 1); REQUIRE(resultBefore.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(resultBefore.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Version) == manifest.Version); // Change version manifest.Version = "99.1.2.3"; catalog.RecordInstall(manifest, manifest.Installers[0], true); SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); REQUIRE(resultAfter.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(resultAfter.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Version) == manifest.Version); } TEST_CASE("TrackingCatalog_Uninstall", "[tracking_catalog]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; auto source = SimpleTestSetup(tempFile, details, manifest, relativePath); PackageTrackingCatalog catalog = CreatePackageTrackingCatalogForSource(source); SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, manifest.Id); catalog.RecordInstall(manifest, manifest.Installers[0], false); SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 1); catalog.RecordUninstall(LocIndString{ manifest.Id }); SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 0); } TEST_CASE("TrackingCatalog_Overlapping_ARP_Range", "[tracking_catalog]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; auto source = SimpleTestSetup(tempFile, details, manifest, relativePath); REQUIRE(manifest.Installers.size() >= 2); AppsAndFeaturesEntry appEntry{}; appEntry.DisplayVersion = "1.23"; manifest.Installers[0].AppsAndFeaturesEntries.emplace_back(appEntry); appEntry.DisplayVersion = "1.24"; manifest.Installers[1].AppsAndFeaturesEntries.emplace_back(appEntry); PackageTrackingCatalog catalog = CreatePackageTrackingCatalogForSource(source); SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, manifest.Id); catalog.RecordInstall(manifest, manifest.Installers[0], false); SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 1); REQUIRE(resultBefore.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(resultBefore.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Version) == manifest.Version); // Change version manifest.Version = "99.1.2.3"; catalog.RecordInstall(manifest, manifest.Installers[0], true); SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); REQUIRE(resultAfter.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(resultAfter.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Version) == manifest.Version); } TEST_CASE("TrackingCatalog_Corrupt", "[tracking_catalog]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; auto source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, manifest.Id); std::filesystem::path catalogFile; { // Add data to initial database PackageTrackingCatalog catalog = CreatePackageTrackingCatalogForSource(source); SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 0); catalog.RecordInstall(manifest, manifest.Installers[0], false); SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); REQUIRE(resultAfter.Matches[0].Package->GetAvailable().size() == 1); catalogFile = catalog.GetFilePath(); } { std::ofstream file{ catalogFile, std::ios_base::trunc }; file << "Corrupted!"; } { // Open database again after "corruption" PackageTrackingCatalog catalog = CreatePackageTrackingCatalogForSource(source); // Should not find anything in new database SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 0); } } ================================================ FILE: src/AppInstallerCLITests/PackageVersionDataManifest.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Manifest; using namespace AppInstaller::Utility; void RequireVersionDataEqual(const PackageVersionDataManifest::VersionData& first, const PackageVersionDataManifest::VersionData& second) { REQUIRE(first.Version == second.Version); REQUIRE(first.ArpMinVersion == second.ArpMinVersion); REQUIRE(first.ArpMaxVersion == second.ArpMaxVersion); REQUIRE(first.ManifestRelativePath == second.ManifestRelativePath); REQUIRE(first.ManifestHash == second.ManifestHash); } TEST_CASE("PackageVersionDataManifest_Empty", "[PackageVersionDataManifest]") { PackageVersionDataManifest original; PackageVersionDataManifest copy; copy.Deserialize(original.Serialize()); REQUIRE(original.Versions().empty()); REQUIRE(copy.Versions().empty()); } TEST_CASE("PackageVersionDataManifest_Single_Simple", "[PackageVersionDataManifest]") { PackageVersionDataManifest original; original.AddVersion({ VersionAndChannel{ Version{ "1.0" }, Channel{} }, {}, {}, "path", "hash" }); PackageVersionDataManifest copy; copy.Deserialize(original.Serialize()); REQUIRE(original.Versions().size() == 1); REQUIRE(copy.Versions().size() == 1); RequireVersionDataEqual(copy.Versions()[0], original.Versions()[0]); } TEST_CASE("PackageVersionDataManifest_Single_Complete", "[PackageVersionDataManifest]") { PackageVersionDataManifest original; original.AddVersion({ VersionAndChannel{ Version{ "1.0" }, Channel{} }, ".99", "1.01", "path", "hash"}); PackageVersionDataManifest copy; copy.Deserialize(original.Serialize()); REQUIRE(original.Versions().size() == 1); REQUIRE(copy.Versions().size() == 1); RequireVersionDataEqual(copy.Versions()[0], original.Versions()[0]); } TEST_CASE("PackageVersionDataManifest_Multiple", "[PackageVersionDataManifest]") { PackageVersionDataManifest original; original.AddVersion({ VersionAndChannel{ Version{ "1.0" }, Channel{} }, ".99", "1.01", "path", "hash" }); original.AddVersion({ VersionAndChannel{ Version{ "1.1" }, Channel{} }, "1.99", "2.01", "path2", "hash2" }); original.AddVersion({ VersionAndChannel{ Version{ "1.2" }, Channel{} }, {}, {}, "path2", "hash2" }); original.AddVersion({ VersionAndChannel{ Version{ "2.0" }, Channel{} }, "3.99", "15.01", "path4", "hash4" }); PackageVersionDataManifest copy; copy.Deserialize(original.Serialize()); REQUIRE(original.Versions().size() == copy.Versions().size()); for (size_t i = 0; i < original.Versions().size(); ++i) { INFO(i); RequireVersionDataEqual(copy.Versions()[i], original.Versions()[i]); } } TEST_CASE("PackageVersionDataManifest_CompressionRoundTrip", "[PackageVersionDataManifest]") { PackageVersionDataManifest original; original.AddVersion({ VersionAndChannel{ Version{ "1.0" }, Channel{} }, ".99", "1.01", "path", "hash" }); original.AddVersion({ VersionAndChannel{ Version{ "1.1" }, Channel{} }, "1.99", "2.01", "path2", "hash2" }); original.AddVersion({ VersionAndChannel{ Version{ "1.2" }, Channel{} }, {}, {}, "path2", "hash2" }); original.AddVersion({ VersionAndChannel{ Version{ "2.0" }, Channel{} }, "3.99", "15.01", "path4", "hash4" }); std::string serialized = original.Serialize(); auto compressed = PackageVersionDataManifest::CreateCompressor().Compress(serialized); auto decompressed = PackageVersionDataManifest::CreateDecompressor().Decompress(compressed); PackageVersionDataManifest copy; copy.Deserialize(decompressed); REQUIRE(original.Versions().size() == copy.Versions().size()); for (size_t i = 0; i < original.Versions().size(); ++i) { INFO(i); RequireVersionDataEqual(copy.Versions()[i], original.Versions()[i]); } } ================================================ FILE: src/AppInstallerCLITests/PathVariable.cpp ================================================ #include "pch.h" #include "TestCommon.h" #include #include #include #include using namespace AppInstaller::Manifest; using namespace AppInstaller::Registry::Environment; TEST_CASE("PathVariable_EnforceReadOnly", "[pathVariable]") { auto pathVariable = PathVariable(ScopeEnum::User, true); REQUIRE_THROWS_HR(pathVariable.Append("testString"), E_ACCESSDENIED); REQUIRE_THROWS_HR(pathVariable.Remove("testString"), E_ACCESSDENIED); } TEST_CASE("PathVariable_Append_NoSemiColon", "[pathVariable]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } auto pathVariable = PathVariable(ScopeEnum::User); std::filesystem::path testPath{ "testString" }; REQUIRE_FALSE(pathVariable.Contains(testPath)); REQUIRE(pathVariable.Append(testPath)); REQUIRE(pathVariable.Contains(testPath)); // Verify that the path value ends with a ';' and not include ";;" std::string pathValue = pathVariable.GetPathValue(); REQUIRE(pathValue.back() == ';'); REQUIRE(pathValue.find(";;") == std::string::npos); REQUIRE(pathVariable.Remove(testPath)); REQUIRE_FALSE(pathVariable.Contains(testPath)); } TEST_CASE("PathVariable_Append_WithSemicolon", "[pathVariable]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } auto pathVariable = PathVariable(ScopeEnum::User); std::filesystem::path testPath{ "testString;" }; REQUIRE_FALSE(pathVariable.Contains(testPath)); REQUIRE(pathVariable.Append(testPath)); REQUIRE(pathVariable.Contains(testPath)); // Verify that the path value ends with a ';' and does not include ";;" std::string pathValue = pathVariable.GetPathValue(); REQUIRE(pathValue.back() == ';'); REQUIRE(pathValue.find(";;") == std::string::npos); REQUIRE(pathVariable.Remove(testPath)); REQUIRE_FALSE(pathVariable.Contains(testPath)); } std::wstring GetCurrentProcessPathVariable() { size_t requiredSize; _wgetenv_s(&requiredSize, nullptr, 0, L"PATH"); if (requiredSize > 0) { auto buffer = std::make_unique(requiredSize); errno_t errorResult = _wgetenv_s(&requiredSize, buffer.get(), requiredSize, L"PATH"); if (errorResult == 0) { return std::wstring(buffer.get()); } } return {}; } TEST_CASE("RefreshEnvironmentVariable_User", "[pathVariable]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } std::wstring testPathEntry = L"testUserPathEntry"; auto pathVariable = AppInstaller::Registry::Environment::PathVariable(ScopeEnum::User); pathVariable.Append(testPathEntry); std::wstring initialPathValue = GetCurrentProcessPathVariable(); bool firstCheck = initialPathValue.find(testPathEntry) != std::string::npos; AppInstaller::Registry::Environment::RefreshPathVariableForCurrentProcess(); std::wstring updatedPathValue = GetCurrentProcessPathVariable(); bool secondCheck = updatedPathValue.find(testPathEntry) != std::string::npos; pathVariable.Remove(testPathEntry); REQUIRE_FALSE(firstCheck); REQUIRE(secondCheck); } TEST_CASE("RefreshEnvironmentVariable_System", "[pathVariable]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } std::wstring testPathEntry = L"testSystemPathEntry"; auto pathVariable = AppInstaller::Registry::Environment::PathVariable(ScopeEnum::Machine); pathVariable.Append(testPathEntry); std::wstring initialPathValue = GetCurrentProcessPathVariable(); bool firstCheck = initialPathValue.find(testPathEntry) != std::string::npos; AppInstaller::Registry::Environment::RefreshPathVariableForCurrentProcess(); std::wstring updatedPathValue = GetCurrentProcessPathVariable(); bool secondCheck = updatedPathValue.find(testPathEntry) != std::string::npos; pathVariable.Remove(testPathEntry); REQUIRE_FALSE(firstCheck); REQUIRE(secondCheck); } TEST_CASE("VerifyPathRefreshExpandsValues", "[pathVariable]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } std::filesystem::path testEntry{ "%USERPROFILE%\\testPath" }; auto pathVariable = AppInstaller::Registry::Environment::PathVariable(ScopeEnum::User); pathVariable.Append(testEntry); std::wstring initialPathValue = GetCurrentProcessPathVariable(); bool firstCheck = initialPathValue.find(testEntry) != std::string::npos; AppInstaller::Registry::Environment::RefreshPathVariableForCurrentProcess(); // %USERPROFILE% should be replaced with the actual path. std::wstring updatedPathValue = GetCurrentProcessPathVariable(); std::wstring expandedTestPath = AppInstaller::Filesystem::GetExpandedPath(testEntry.u8string()); bool secondCheck = updatedPathValue.find(expandedTestPath) != std::string::npos; pathVariable.Remove(testEntry); REQUIRE_FALSE(firstCheck); REQUIRE(secondCheck); } ================================================ FILE: src/AppInstallerCLITests/PinFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include "TestHooks.h" #include #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Utility; using namespace AppInstaller::Pinning; using namespace AppInstaller::SQLite; TEST_CASE("PinFlow_Add", "[PinFlow][workflow]") { TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); std::ostringstream pinAddOutput; TestContext addContext{ pinAddOutput, std::cin }; OverrideForCompositeInstalledSource(addContext, CreateTestSource({ TSR::TestInstaller_Exe })); addContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); addContext.Args.AddArg(Execution::Args::Type::BlockingPin); PinAddCommand pinAdd({}); pinAdd.Execute(addContext); INFO(pinAddOutput.str()); SECTION("Pin is saved") { auto index = PinningIndex::Open(indexFile.GetPath().u8string(), SQLiteStorageBase::OpenDisposition::Read); auto pins = index.GetAllPins(); REQUIRE(pins.size() == 1); REQUIRE(pins[0].GetType() == PinType::Blocking); REQUIRE(pins[0].GetGatedVersion().ToString() == ""); REQUIRE(pins[0].GetKey().PackageId == "AppInstallerCliTest.TestExeInstaller"); REQUIRE(pins[0].GetKey().SourceId == "*TestSource"); std::ostringstream pinListOutput; TestContext listContext{ pinListOutput, std::cin }; OverrideForCompositeInstalledSource(listContext, CreateTestSource({ TSR::TestInstaller_Exe })); listContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); PinListCommand pinList({}); pinList.Execute(listContext); INFO(pinListOutput.str()); REQUIRE(pinListOutput.str().find("AppInstallerCliTest.TestExeInstaller")); REQUIRE(pinListOutput.str().find("Blocking")); } SECTION("Remove pin") { std::ostringstream pinRemoveOutput; TestContext removeContext{ pinRemoveOutput, std::cin }; OverrideForCompositeInstalledSource(removeContext, CreateTestSource({ TSR::TestInstaller_Exe })); removeContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); PinRemoveCommand pinRemove({}); pinRemove.Execute(removeContext); INFO(pinRemoveOutput.str()); auto index = PinningIndex::Open(indexFile.GetPath().u8string(), SQLiteStorageBase::OpenDisposition::Read); auto pins = index.GetAllPins(); REQUIRE(pins.empty()); } SECTION("Reset pins") { std::ostringstream pinResetOutput; TestContext resetContext{ pinResetOutput, std::cin }; SECTION("Without --force") { OverrideForCompositeInstalledSource(resetContext, CreateTestSource({ TSR::TestInstaller_Exe })); PinResetCommand pinReset({}); pinReset.Execute(resetContext); INFO(pinResetOutput.str()); auto index = PinningIndex::Open(indexFile.GetPath().u8string(), SQLiteStorageBase::OpenDisposition::Read); auto pins = index.GetAllPins(); REQUIRE(pins.size() == 1); } SECTION("With --force") { resetContext.Args.AddArg(Execution::Args::Type::Force); PinResetCommand pinReset({}); pinReset.Execute(resetContext); INFO(pinResetOutput.str()); auto index = PinningIndex::Open(indexFile.GetPath().u8string(), SQLiteStorageBase::OpenDisposition::Read); auto pins = index.GetAllPins(); REQUIRE(pins.empty()); } } SECTION("Update pin") { std::ostringstream pinUpdateOutput; TestContext updateContext{ pinUpdateOutput, std::cin }; OverrideForCompositeInstalledSource(updateContext, CreateTestSource({ TSR::TestInstaller_Exe })); updateContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); SECTION("Without --force") { PinAddCommand pinUpdate({}); pinUpdate.Execute(updateContext); INFO(pinUpdateOutput.str()); REQUIRE_TERMINATED_WITH(updateContext, APPINSTALLER_CLI_ERROR_PIN_ALREADY_EXISTS); auto index = PinningIndex::Open(indexFile.GetPath().u8string(), SQLiteStorageBase::OpenDisposition::Read); auto pins = index.GetAllPins(); REQUIRE(pins.size() == 1); REQUIRE(pins[0].GetType() == PinType::Blocking); } SECTION("With --force") { updateContext.Args.AddArg(Execution::Args::Type::Force); PinAddCommand pinUpdate({}); pinUpdate.Execute(updateContext); INFO(pinUpdateOutput.str()); auto index = PinningIndex::Open(indexFile.GetPath().u8string(), SQLiteStorageBase::OpenDisposition::Read); auto pins = index.GetAllPins(); REQUIRE(pins.size() == 1); REQUIRE(pins[0].GetType() == PinType::Pinning); } } } TEST_CASE("PinFlow_Add_NotFound", "[PinFlow][workflow]") { TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); std::ostringstream pinAddOutput; TestContext addContext{ pinAddOutput, std::cin }; OverrideForCompositeInstalledSource(addContext, CreateTestSource({})); addContext.Args.AddArg(Execution::Args::Type::Query, "This package doesn't exist"sv); PinAddCommand pinAdd({}); pinAdd.Execute(addContext); INFO(pinAddOutput.str()); REQUIRE_TERMINATED_WITH(addContext, APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); } TEST_CASE("PinFlow_ListEmpty", "[PinFlow][workflow]") { TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); std::ostringstream pinListOutput; TestContext listContext{ pinListOutput, std::cin }; OverrideForCompositeInstalledSource(listContext, CreateTestSource({})); PinListCommand pinList({}); pinList.Execute(listContext); INFO(pinListOutput.str()); REQUIRE(pinListOutput.str().find(Resource::LocString(Resource::String::PinNoPinsExist)) != std::string::npos); } TEST_CASE("PinFlow_RemoveNonExisting", "[PinFlow][workflow]") { TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); std::ostringstream pinRemoveOutput; TestContext removeContext{ pinRemoveOutput, std::cin }; OverrideForCompositeInstalledSource(removeContext, CreateTestSource({ TSR::TestInstaller_Exe })); removeContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); PinRemoveCommand pinRemove({}); pinRemove.Execute(removeContext); INFO(pinRemoveOutput.str()); REQUIRE_TERMINATED_WITH(removeContext, APPINSTALLER_CLI_ERROR_PIN_DOES_NOT_EXIST); } TEST_CASE("PinFlow_ResetEmpty", "[PinFlow][workflow]") { TempFile indexFile("pinningIndex", ".db"); TestHook::SetPinningIndex_Override pinningIndexOverride(indexFile.GetPath()); std::ostringstream pinResetOutput; TestContext resetContext{ pinResetOutput, std::cin }; resetContext.Args.AddArg(Execution::Args::Type::Force); PinResetCommand pinReset({}); pinReset.Execute(resetContext); INFO(pinResetOutput.str()); REQUIRE(pinResetOutput.str().find(Resource::LocString(Resource::String::PinNoPinsExist)) != std::string::npos); } ================================================ FILE: src/AppInstallerCLITests/PinningIndex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Pinning; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; TEST_CASE("PinningIndexCreateLatestAndReopen", "[pinningIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Version versionCreated; // Create the index { PinningIndex index = PinningIndex::CreateNew(tempFile, Version::Latest()); versionCreated = index.GetVersion(); } // Reopen the index for read only { INFO("Trying with Read"); PinningIndex index = PinningIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } // Reopen the index for read/write { INFO("Trying with ReadWrite"); PinningIndex index = PinningIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } // Reopen the index for immutable read { INFO("Trying with Immutable"); PinningIndex index = PinningIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Immutable); Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } } TEST_CASE("PinningIndexAddEntryToTable", "[pinningIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Pin pin = Pin::CreateBlockingPin({ "pkgId", "sourceId" }); { PinningIndex index = PinningIndex::CreateNew(tempFile, { 1, 0 }); index.AddPin(pin); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); auto pins = Pinning_V1_0::PinTable::GetAllPins(connection); REQUIRE(pins.size() == 1); REQUIRE(pins[0] == pin); auto pinFromIndex = Pinning_V1_0::PinTable::GetPinById(connection, 1); REQUIRE(pinFromIndex.has_value()); REQUIRE(pinFromIndex.value() == pin); REQUIRE(pinFromIndex->GetType() == pin.GetType()); REQUIRE(pinFromIndex->GetKey() == pin.GetKey()); } { PinningIndex index = PinningIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.RemovePin(pin.GetKey()); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Pinning_V1_0::PinTable::GetAllPins(connection).empty()); REQUIRE(!Pinning_V1_0::PinTable::GetPinById(connection, 1)); } } TEST_CASE("PinningIndex_AddUpdateRemove", "[pinningIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Pin pin = Pin::CreateGatingPin({ "pkgId", "srcId" }, { "1.0.*"sv }); Pin updatedPin = Pin::CreatePinningPin({ "pkgId", "srcId" }); { PinningIndex index = PinningIndex::CreateNew(tempFile, { 1, 0 }); index.AddPin(pin); REQUIRE(index.UpdatePin(updatedPin)); } { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); auto pinFromIndex = Pinning_V1_0::PinTable::GetPinById(connection, 1); REQUIRE(pinFromIndex.has_value()); REQUIRE(pinFromIndex.value() == updatedPin); } { PinningIndex index = PinningIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.RemovePin(updatedPin.GetKey()); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Pinning_V1_0::PinTable::GetAllPins(connection).empty()); REQUIRE(!Pinning_V1_0::PinTable::GetPinById(connection, 1)); } } TEST_CASE("PinningIndex_ResetAll", "[pinningIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Pin pin1 = Pin::CreateBlockingPin({ "pkg1", "src1" }); Pin pin2 = Pin::CreatePinningPin({ "pkg2", "src2" }); // Add two pins to the index, then check that they show up when queried PinningIndex index = PinningIndex::CreateNew(tempFile, { 1, 0 }); index.AddPin(pin1); index.AddPin(pin2); REQUIRE(index.GetAllPins().size() == 2); REQUIRE(index.GetPin(pin1.GetKey()).has_value()); REQUIRE(index.GetPin(pin2.GetKey()).has_value()); REQUIRE(!index.GetPin({ "pkg", "src" }).has_value()); // Reset the index, then check that there are no pins index.ResetAllPins(); REQUIRE(index.GetAllPins().empty()); REQUIRE(!index.GetPin(pin1.GetKey()).has_value()); REQUIRE(!index.GetPin(pin2.GetKey()).has_value()); } TEST_CASE("PinningIndex_AddDuplicatePin", "[pinningIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Pin pin = Pin::CreateGatingPin({ "pkg", "src" }, { "1.*"sv }); PinningIndex index = PinningIndex::CreateNew(tempFile, { 1, 0 }); index.AddPin(pin); REQUIRE_THROWS(index.AddPin(pin), ERROR_ALREADY_EXISTS); } ================================================ FILE: src/AppInstallerCLITests/PortableIndex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include #include #include using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Portable; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; void CreateFakePortableFile(PortableFileEntry& file) { file.SetFilePath("testPortableFile.exe"); file.FileType = PortableFileType::File; file.SHA256 = "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"; file.SymlinkTarget = "testSymlinkTarget.exe"; } TEST_CASE("PortableIndexCreateLatestAndReopen", "[portableIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Version versionCreated; // Create the index { PortableIndex index = PortableIndex::CreateNew(tempFile, Version::Latest()); versionCreated = index.GetVersion(); } // Reopen the index for read only { INFO("Trying with Read"); PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } // Reopen the index for read/write { INFO("Trying with ReadWrite"); PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } // Reopen the index for immutable read { INFO("Trying with Immutable"); PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Immutable); Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } } TEST_CASE("PortableIndexAddEntryToTable", "[portableIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); PortableFileEntry portableFile; CreateFakePortableFile(portableFile); { PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); index.AddPortableFile(portableFile); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(!Schema::Portable_V1_0::PortableTable::IsEmpty(connection)); } { PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.RemovePortableFile(portableFile); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::Portable_V1_0::PortableTable::IsEmpty(connection)); } } TEST_CASE("PortableIndex_AddUpdateRemove", "[portableIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); PortableFileEntry portableFile; CreateFakePortableFile(portableFile); PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); index.AddPortableFile(portableFile); // Apply changes to portable file std::string updatedHash = "2db8ae7657c6622b04700137740002c51c36588e566651c9f67b4b096c8ad18b"; portableFile.FileType = PortableFileType::Symlink; portableFile.SHA256 = updatedHash; portableFile.SymlinkTarget = "fakeSymlinkTarget.exe"; REQUIRE(index.UpdatePortableFile(portableFile)); { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); auto fileFromIndex = Schema::Portable_V1_0::PortableTable::GetPortableFileById(connection, 1); REQUIRE(fileFromIndex.has_value()); REQUIRE(fileFromIndex->GetFilePath() == portableFile.GetFilePath()); REQUIRE(fileFromIndex->FileType == PortableFileType::Symlink); REQUIRE(fileFromIndex->SHA256 == updatedHash); REQUIRE(fileFromIndex->SymlinkTarget == "fakeSymlinkTarget.exe"); } { PortableIndex index2 = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index2.RemovePortableFile(portableFile); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::Portable_V1_0::PortableTable::IsEmpty(connection)); } } TEST_CASE("PortableIndex_UpdateFile_CaseInsensitive", "[portableIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); PortableFileEntry portableFile; CreateFakePortableFile(portableFile); PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); index.AddPortableFile(portableFile); // By default, portable file path is set to "testPortableFile.exe" // Change file path to all upper case should still successfully update. portableFile.SetFilePath("TESTPORTABLEFILE.exe"); std::string updatedHash = "2db8ae7657c6622b04700137740002c51c36588e566651c9f67b4b096c8ad18b"; portableFile.FileType = PortableFileType::Symlink; portableFile.SHA256 = updatedHash; portableFile.SymlinkTarget = "fakeSymlinkTarget.exe"; REQUIRE(index.UpdatePortableFile(portableFile)); { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); auto fileFromIndex = Schema::Portable_V1_0::PortableTable::GetPortableFileById(connection, 1); REQUIRE(fileFromIndex.has_value()); REQUIRE(fileFromIndex->GetFilePath() == portableFile.GetFilePath()); REQUIRE(fileFromIndex->FileType == PortableFileType::Symlink); REQUIRE(fileFromIndex->SHA256 == updatedHash); REQUIRE(fileFromIndex->SymlinkTarget == "fakeSymlinkTarget.exe"); } } TEST_CASE("PortableIndex_AddDuplicateFile", "[portableIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); PortableFileEntry portableFile; CreateFakePortableFile(portableFile); PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); index.AddPortableFile(portableFile); // Change file path to all upper case. Adding duplicate file should fail. portableFile.SetFilePath("TESTPORTABLEFILE.exe"); REQUIRE_THROWS(index.AddPortableFile(portableFile), ERROR_ALREADY_EXISTS); } TEST_CASE("PortableIndex_RemoveWithId", "[portableIndex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); PortableFileEntry portableFile; CreateFakePortableFile(portableFile); PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); index.AddPortableFile(portableFile); { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Portable_V1_0::PortableTable::ExistsById(connection, 1)); Portable_V1_0::PortableTable::DeleteById(connection, 1); REQUIRE_FALSE(Portable_V1_0::PortableTable::ExistsById(connection, 1)); } } ================================================ FILE: src/AppInstallerCLITests/PortableInstaller.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include #include #include #include #include #include using namespace std::string_literals; using namespace AppInstaller::CLI::Portable; using namespace AppInstaller::Filesystem; using namespace AppInstaller::Manifest; using namespace AppInstaller::Registry::Environment; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; using namespace AppInstaller::Utility; using namespace AppInstaller::CLI::Workflow; using namespace TestCommon; TEST_CASE("PortableInstaller_InstallToRegistry", "[PortableInstaller]") { TempDirectory tempDirectory = TestCommon::TempDirectory("TempDirectory", false); std::vector desiredTestState; TestCommon::TempFile testPortable("testPortable.txt"); std::ofstream file(testPortable, std::ofstream::out); file.close(); std::filesystem::path targetPath = tempDirectory.GetPath() / "testPortable.txt"; std::filesystem::path symlinkPath = tempDirectory.GetPath() / "testSymlink.exe"; desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable.GetPath(), targetPath, {}))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath, targetPath))); PortableInstaller portableInstaller = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); portableInstaller.TargetInstallLocation = tempDirectory.GetPath(); portableInstaller.SetDesiredState(desiredTestState); REQUIRE(portableInstaller.VerifyExpectedState()); portableInstaller.Install(AppInstaller::CLI::Workflow::OperationType::Install); auto entry = portableInstaller.GetAppsAndFeaturesEntry(); REQUIRE(entry.ProductCode == portableInstaller.GetProductCode()); PortableInstaller portableInstaller2 = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); REQUIRE(portableInstaller2.ARPEntryExists()); REQUIRE(std::filesystem::exists(portableInstaller2.PortableTargetFullPath)); REQUIRE(AppInstaller::Filesystem::SymlinkExists(portableInstaller2.PortableSymlinkFullPath)); portableInstaller2.Uninstall(); REQUIRE_FALSE(std::filesystem::exists(portableInstaller2.PortableTargetFullPath)); REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(portableInstaller2.PortableSymlinkFullPath)); REQUIRE_FALSE(std::filesystem::exists(portableInstaller2.InstallLocation)); } TEST_CASE("PortableInstaller_InstallToIndex_CreateInstallRoot", "[PortableInstaller]") { TempDirectory installRootDirectory = TestCommon::TempDirectory("PortableInstallRoot", false); std::vector desiredTestState; TestCommon::TempFile testPortable("testPortable.txt"); std::ofstream file1(testPortable, std::ofstream::out); file1.close(); TestCommon::TempFile testPortable2("testPortable2.txt"); std::ofstream file2(testPortable2, std::ofstream::out); file2.close(); TestCommon::TempDirectory testDirectoryFolder("testDirectory", true); std::filesystem::path installRootPath = installRootDirectory.GetPath(); std::filesystem::path targetPath = installRootPath / "testPortable.txt"; std::filesystem::path targetPath2 = installRootPath / "testPortable2.txt"; std::filesystem::path symlinkPath = installRootPath / "testSymlink.exe"; std::filesystem::path symlinkPath2 = installRootPath / "testSymlink2.exe"; std::filesystem::path directoryPath = installRootPath / "testDirectory"; desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable.GetPath(), targetPath, {}))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable2.GetPath(), targetPath2, {}))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath, targetPath))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath2, targetPath2))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateDirectoryEntry(testDirectoryFolder.GetPath(), directoryPath))); PortableInstaller portableInstaller = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); portableInstaller.TargetInstallLocation = installRootDirectory.GetPath(); portableInstaller.RecordToIndex = true; portableInstaller.SetDesiredState(desiredTestState); REQUIRE(portableInstaller.VerifyExpectedState()); portableInstaller.Install(AppInstaller::CLI::Workflow::OperationType::Install); REQUIRE(std::filesystem::exists(installRootPath / portableInstaller.GetPortableIndexFileName())); REQUIRE(std::filesystem::exists(targetPath)); REQUIRE(std::filesystem::exists(targetPath2)); REQUIRE(AppInstaller::Filesystem::SymlinkExists(symlinkPath)); REQUIRE(AppInstaller::Filesystem::SymlinkExists(symlinkPath2)); REQUIRE(std::filesystem::exists(directoryPath)); PortableInstaller portableInstaller2 = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); REQUIRE(portableInstaller2.ARPEntryExists()); portableInstaller2.Uninstall(); // Install root directory should be removed since it was created. REQUIRE_FALSE(std::filesystem::exists(installRootPath)); REQUIRE_FALSE(std::filesystem::exists(targetPath)); REQUIRE_FALSE(std::filesystem::exists(targetPath2)); REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(symlinkPath)); REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(symlinkPath2)); REQUIRE_FALSE(std::filesystem::exists(directoryPath)); } TEST_CASE("PortableInstaller_InstallToIndex_ExistingInstallRoot", "[PortableInstaller]") { TempDirectory installRootDirectory = TestCommon::TempDirectory("PortableInstallRoot", true); std::vector desiredTestState; TestCommon::TempFile testPortable("testPortable.txt"); std::ofstream file1(testPortable, std::ofstream::out); file1.close(); TestCommon::TempFile testPortable2("testPortable2.txt"); std::ofstream file2(testPortable2, std::ofstream::out); file2.close(); TestCommon::TempDirectory testDirectoryFolder("testDirectory", true); std::filesystem::path installRootPath = installRootDirectory.GetPath(); std::filesystem::path targetPath = installRootPath / "testPortable.txt"; std::filesystem::path targetPath2 = installRootPath / "testPortable2.txt"; std::filesystem::path symlinkPath = installRootPath / "testSymlink.exe"; std::filesystem::path symlinkPath2 = installRootPath / "testSymlink2.exe"; std::filesystem::path directoryPath = installRootPath / "testDirectory"; desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable.GetPath(), targetPath, {}))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable2.GetPath(), targetPath2, {}))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath, targetPath))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath2, targetPath2))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateDirectoryEntry(testDirectoryFolder.GetPath(), directoryPath))); PortableInstaller portableInstaller = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); portableInstaller.TargetInstallLocation = installRootDirectory.GetPath(); portableInstaller.RecordToIndex = true; portableInstaller.SetDesiredState(desiredTestState); REQUIRE(portableInstaller.VerifyExpectedState()); portableInstaller.Install(AppInstaller::CLI::Workflow::OperationType::Install); REQUIRE(std::filesystem::exists(installRootPath / portableInstaller.GetPortableIndexFileName())); REQUIRE(std::filesystem::exists(targetPath)); REQUIRE(std::filesystem::exists(targetPath2)); REQUIRE(AppInstaller::Filesystem::SymlinkExists(symlinkPath)); REQUIRE(AppInstaller::Filesystem::SymlinkExists(symlinkPath2)); REQUIRE(std::filesystem::exists(directoryPath)); PortableInstaller portableInstaller2 = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); REQUIRE(portableInstaller2.ARPEntryExists()); portableInstaller2.Uninstall(); // Install root directory should still exist since it was created previously. REQUIRE(std::filesystem::exists(installRootPath)); REQUIRE_FALSE(std::filesystem::exists(targetPath)); REQUIRE_FALSE(std::filesystem::exists(targetPath2)); REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(symlinkPath)); REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(symlinkPath2)); REQUIRE_FALSE(std::filesystem::exists(directoryPath)); } TEST_CASE("PortableInstaller_UnicodeSymlinkPath", "[PortableInstaller]") { TempDirectory tempDirectory = TestCommon::TempDirectory("TempDirectory", false); // Modify install location path to include unicode characters. std::filesystem::path testInstallLocation = tempDirectory.GetPath() / std::filesystem::path{ ConvertToUTF16("романтический") }; std::vector desiredTestState; TestCommon::TempFile testPortable("testPortable.txt"); std::ofstream file(testPortable, std::ofstream::out); file.close(); std::filesystem::path targetPath = testInstallLocation / "testPortable.txt"; std::filesystem::path symlinkPath = tempDirectory.GetPath() / "testSymlink.exe"; desiredTestState.emplace_back(std::move(PortableFileEntry::CreateFileEntry(testPortable.GetPath(), targetPath, {}))); desiredTestState.emplace_back(std::move(PortableFileEntry::CreateSymlinkEntry(symlinkPath, targetPath))); PortableInstaller portableInstaller = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); portableInstaller.TargetInstallLocation = testInstallLocation; portableInstaller.SetDesiredState(desiredTestState); REQUIRE(portableInstaller.VerifyExpectedState()); portableInstaller.Install(AppInstaller::CLI::Workflow::OperationType::Install); PortableInstaller portableInstaller2 = PortableInstaller(ScopeEnum::User, Architecture::X64, "testProductCode"); REQUIRE(portableInstaller2.ARPEntryExists()); REQUIRE(std::filesystem::exists(portableInstaller2.PortableTargetFullPath)); REQUIRE(AppInstaller::Filesystem::SymlinkExists(portableInstaller2.PortableSymlinkFullPath)); portableInstaller2.Uninstall(); REQUIRE_FALSE(std::filesystem::exists(portableInstaller2.PortableTargetFullPath)); REQUIRE_FALSE(AppInstaller::Filesystem::SymlinkExists(portableInstaller2.PortableSymlinkFullPath)); REQUIRE_FALSE(std::filesystem::exists(portableInstaller2.InstallLocation)); } ================================================ FILE: src/AppInstallerCLITests/PreIndexedPackageSource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestSource.h" #include "TestCommon.h" #include "TestSettings.h" #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Repository; using namespace AppInstaller::Runtime; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; namespace fs = std::filesystem; constexpr std::string_view s_RepositorySettings_UserSources = "usersources"sv; constexpr std::string_view s_MsixFile_1 = "index.1.0.0.0.signed.msix"; constexpr std::string_view s_MsixFile_2 = "index.2.0.0.0.signed.msix"; constexpr std::string_view s_Msix_FamilyName = "AppInstallerCLITestsFakeIndex_8wekyb3d8bbwe"; constexpr std::string_view s_IndexMsixName = "source.msix"sv; void CopyIndexFileToDirectory(const fs::path& from, const fs::path& to) { fs::path toFile = to; toFile /= s_IndexMsixName; if (fs::exists(toFile)) { fs::remove(toFile); } fs::copy_file(from, toFile); } fs::path GetPathToFileDir() { fs::path result = GetPathTo(Runtime::PathName::LocalState); result /= AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); result /= s_Msix_FamilyName; return result; } std::string GetContents(const fs::path& file) { REQUIRE(fs::exists(file)); std::ifstream stream(file); return ReadEntireStream(stream); } void CleanSources() { RemoveSetting(Stream::UserSources); RemoveSetting(Stream::SourcesMetadata); fs::remove_all(GetPathToFileDir()); } TEST_CASE("PIPS_Add", "[pips]") { if (!Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } CleanSources(); TempDirectory dir("pipssource"); TestDataFile index(s_MsixFile_1); CopyIndexFileToDirectory(index, dir); bool shouldCleanCert = InstallCertFromSignedPackage(index); SourceDetails details; details.Name = "TestName"; details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); details.Arg = dir; ProgressCallback callback; AddSource(details, callback); fs::path state = GetPathToFileDir(); REQUIRE(fs::exists(state)); fs::path indexMsix = state; indexMsix /= s_IndexMsixName; REQUIRE(fs::exists(indexMsix)); REQUIRE(fs::file_size(indexMsix) > 0); if (shouldCleanCert) { UninstallCertFromSignedPackage(index); } } TEST_CASE("PIPS_UpdateSameVersion", "[pips]") { if (!Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } CleanSources(); TempDirectory dir("pipssource"); TestDataFile index(s_MsixFile_1); CopyIndexFileToDirectory(index, dir); bool shouldCleanCert = InstallCertFromSignedPackage(index); SourceDetails details; details.Name = "TestName"; details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); details.Arg = dir; TestProgress callback; AddSource(details, callback); fs::path state = GetPathToFileDir(); REQUIRE(fs::exists(state)); bool progressCalled = false; callback.m_OnProgress = [&](uint64_t, uint64_t, ProgressType) { progressCalled = true; }; UpdateSource(details.Name, callback); REQUIRE(!progressCalled); if (shouldCleanCert) { UninstallCertFromSignedPackage(index); } } TEST_CASE("PIPS_UpdateNewVersion", "[pips]") { if (!Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } CleanSources(); TempDirectory dir("pipssource"); TestDataFile indexMsix1(s_MsixFile_1); CopyIndexFileToDirectory(indexMsix1, dir); bool shouldCleanCert = InstallCertFromSignedPackage(indexMsix1); SourceDetails details; details.Name = "TestName"; details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); details.Arg = dir; TestProgress callback; AddSource(details, callback); fs::path state = GetPathToFileDir(); REQUIRE(fs::exists(state)); fs::path indexMsix = state; indexMsix /= s_IndexMsixName; std::string indexContents1 = GetContents(indexMsix); TestDataFile indexMsix2(s_MsixFile_2); CopyIndexFileToDirectory(indexMsix2, dir); bool progressCalled = false; callback.m_OnProgress = [&](uint64_t, uint64_t, ProgressType) { progressCalled = true; }; UpdateSource(details.Name, callback); REQUIRE(progressCalled); std::string indexContents2 = GetContents(indexMsix); REQUIRE(indexContents1 != indexContents2); if (shouldCleanCert) { UninstallCertFromSignedPackage(indexMsix1); } } TEST_CASE("PIPS_Remove", "[pips]") { if (!Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } CleanSources(); TempDirectory dir("pipssource"); TestDataFile index(s_MsixFile_1); CopyIndexFileToDirectory(index, dir); bool shouldCleanCert = InstallCertFromSignedPackage(index); SourceDetails details; details.Name = "TestName"; details.Type = AppInstaller::Repository::Microsoft::PreIndexedPackageSourceFactory::Type(); details.Arg = dir; ProgressCallback callback; AddSource(details, callback); fs::path state = GetPathToFileDir(); REQUIRE(fs::exists(state)); fs::path indexMsix = state; indexMsix /= s_IndexMsixName; REQUIRE(fs::exists(indexMsix)); RemoveSource(details.Name, callback); REQUIRE(!fs::exists(state)); if (shouldCleanCert) { UninstallCertFromSignedPackage(index); } } ================================================ FILE: src/AppInstallerCLITests/PredefinedInstalledSource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Runtime; using namespace AppInstaller::Utility; using SQLiteIndex = AppInstaller::Repository::Microsoft::SQLiteIndex; using SQLiteIndexSource = AppInstaller::Repository::Microsoft::SQLiteIndexSource; using Factory = AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory; using ARPHelper = AppInstaller::Repository::Microsoft::ARPHelper; constexpr std::string_view s_TestScope = "TestScope"sv; struct ARPEntry { ARPEntry(std::string entryName) : EntryName(std::move(entryName)) {} ARPEntry(std::string entryName, std::optional displayName, std::optional displayVersion, bool systemComponent = false) : EntryName(std::move(entryName)), DisplayName(std::move(displayName)), DisplayVersion(std::move(displayVersion)), SystemComponent(systemComponent) {} std::string EntryName; std::optional DisplayName; std::optional DisplayVersion; std::optional Publisher; std::optional InstallLocation; std::optional UninstallString; std::optional QuietUninstallString; std::optional WindowsInstaller; std::optional SystemComponent; }; void AddARPValueToKey(HKEY key, const std::wstring& name, const std::optional& value) { if (value) { SetRegistryValue(key, name, ConvertToUTF16(value.value())); } } void AddARPValueToKey(HKEY key, const std::wstring& name, const std::optional& value) { if (value) { SetRegistryValue(key, name, (value.value() ? 1 : 0)); } } void AddARPEntryToKey(HKEY key, const ARPHelper& helper, const ARPEntry& entry) { auto subkey = RegCreateVolatileSubKey(key, ConvertToUTF16(entry.EntryName)); #define ADD_ARP_VALUE(_name_) AddARPValueToKey(subkey.get(), helper._name_, entry._name_) ADD_ARP_VALUE(DisplayName); ADD_ARP_VALUE(DisplayVersion); ADD_ARP_VALUE(Publisher); ADD_ARP_VALUE(InstallLocation); ADD_ARP_VALUE(UninstallString); ADD_ARP_VALUE(QuietUninstallString); ADD_ARP_VALUE(WindowsInstaller); ADD_ARP_VALUE(SystemComponent); #undef ADD_ARP_VALUE } void AddARPEntriesToKey(HKEY key, const ARPHelper& helper, const std::vector& entries) { for (const auto& entry : entries) { AddARPEntryToKey(key, helper, entry); } } SQLiteIndex::MetadataResult::const_iterator Find(const SQLiteIndex::MetadataResult& metadata, PackageVersionMetadata value) { return std::find_if(metadata.begin(), metadata.end(), [value](const auto& m) { return m.first == value; }); } void VerifyInstalledType(const SQLiteIndex::MetadataResult& metadata, InstallerTypeEnum type) { auto itr = Find(metadata, PackageVersionMetadata::InstalledType); REQUIRE(itr != metadata.end()); REQUIRE(ConvertToInstallerTypeEnum(itr->second) == type); } void VerifyTestScope(const SQLiteIndex::MetadataResult& metadata) { auto itr = Find(metadata, PackageVersionMetadata::InstalledScope); REQUIRE(itr != metadata.end()); REQUIRE(itr->second == s_TestScope); } void VerifyMetadataString(const SQLiteIndex::MetadataResult& metadata, PackageVersionMetadata pvm, const std::optional& value) { auto itr = Find(metadata, pvm); if (value) { REQUIRE(itr != metadata.end()); REQUIRE(itr->second == value.value()); } else { REQUIRE(itr == metadata.end()); } } void VerifyEntryAgainstIndex(const SQLiteIndex& index, SQLiteIndex::IdType manifestId, const ARPEntry& entry) { REQUIRE(index.GetPropertyByPrimaryId(manifestId, PackageVersionProperty::Name) == entry.DisplayName); REQUIRE(index.GetPropertyByPrimaryId(manifestId, PackageVersionProperty::Version) == entry.DisplayVersion); REQUIRE(index.GetMultiPropertyByPrimaryId(manifestId, PackageVersionMultiProperty::PackageFamilyName).empty()); auto productCodes = index.GetMultiPropertyByPrimaryId(manifestId, PackageVersionMultiProperty::ProductCode); REQUIRE(productCodes.size() == 1); REQUIRE(productCodes[0] == FoldCase(static_cast(entry.EntryName))); auto metadata = index.GetMetadataByManifestId(manifestId); VerifyInstalledType(metadata, entry.WindowsInstaller.value_or(false) ? InstallerTypeEnum::Msi : InstallerTypeEnum::Exe); VerifyTestScope(metadata); VerifyMetadataString(metadata, PackageVersionMetadata::Publisher, entry.Publisher); VerifyMetadataString(metadata, PackageVersionMetadata::InstalledLocation, entry.InstallLocation); VerifyMetadataString(metadata, PackageVersionMetadata::StandardUninstallCommand, entry.UninstallString); VerifyMetadataString(metadata, PackageVersionMetadata::SilentUninstallCommand, entry.QuietUninstallString); } std::shared_ptr CreatePredefinedInstalledSource(Factory::Filter filter = Factory::Filter::None) { SourceDetails details; details.Type = Factory::Type(); details.Arg = Factory::FilterToString(filter); TestProgress progress; auto factory = Factory::Create(); return factory->Create(details)->Open(progress); } SQLiteIndex CreateMemoryIndex() { return SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless); } TEST_CASE("ARPHelper_GetARPForArchitecture", "[arphelper][list]") { auto systemArch = GetSystemArchitecture(); ARPHelper helper; auto nativeMachineKey = helper.GetARPKey(ScopeEnum::Machine, systemArch); REQUIRE(nativeMachineKey); } TEST_CASE("ARPHelper_GetBoolValue_DoesNotExist", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); std::wstring valueName = L"TestValueName"; ARPHelper helper; REQUIRE_FALSE(helper.GetBoolValue(key, valueName)); } TEST_CASE("ARPHelper_GetBoolValue_NotDword", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); std::wstring valueName = L"TestValueName"; SetRegistryValue(root.get(), valueName, L"True"); ARPHelper helper; REQUIRE_FALSE(helper.GetBoolValue(key, valueName)); } TEST_CASE("ARPHelper_GetBoolValue_Zero", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); std::wstring valueName = L"TestValueName"; SetRegistryValue(root.get(), valueName, 0); ARPHelper helper; REQUIRE_FALSE(helper.GetBoolValue(key, valueName)); } TEST_CASE("ARPHelper_GetBoolValue_One", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); std::wstring valueName = L"TestValueName"; SetRegistryValue(root.get(), valueName, 1); ARPHelper helper; REQUIRE(helper.GetBoolValue(key, valueName)); } TEST_CASE("ARPHelper_GetBoolValue_FortyTwo", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); std::wstring valueName = L"TestValueName"; SetRegistryValue(root.get(), valueName, 42); ARPHelper helper; REQUIRE(helper.GetBoolValue(key, valueName)); } TEST_CASE("ARPHelper_DetermineVersion_DisplayVersion", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); ARPHelper helper; SetRegistryValue(root.get(), helper.DisplayVersion, L"1.0"); SetRegistryValue(root.get(), helper.Version, 0x0207002A); SetRegistryValue(root.get(), helper.VersionMajor, 3); SetRegistryValue(root.get(), helper.VersionMinor, 14); auto result = helper.DetermineVersion(key); REQUIRE(result == "1.0"); } TEST_CASE("ARPHelper_DetermineVersion_Version", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); ARPHelper helper; SetRegistryValue(root.get(), helper.Version, 0x0207002A); SetRegistryValue(root.get(), helper.VersionMajor, 3); SetRegistryValue(root.get(), helper.VersionMinor, 14); auto result = helper.DetermineVersion(key); REQUIRE(result == "3.14"); } TEST_CASE("ARPHelper_DetermineVersion_VersionMajorMinor", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); ARPHelper helper; SetRegistryValue(root.get(), helper.VersionMajor, 3); SetRegistryValue(root.get(), helper.VersionMinor, 14); auto result = helper.DetermineVersion(key); REQUIRE(result == "3.14"); } TEST_CASE("ARPHelper_DetermineVersion_Unknown", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); ARPHelper helper; auto result = helper.DetermineVersion(key); REQUIRE(result == Version::CreateUnknown().ToString()); } TEST_CASE("ARPHelper_PopulateIndexFromKey_Single", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); ARPHelper helper; // Create a single ARP entry under the root ARPEntry entry("SingleEntry"); entry.DisplayName = "Test Name"; entry.DisplayVersion = "1.2"; entry.Publisher = "Test Publisher"; entry.InstallLocation = "TestLocation"; entry.UninstallString = "Test Uninstall"; entry.QuietUninstallString = "Test Quiet Uninstall"; entry.WindowsInstaller = true; AddARPEntryToKey(root.get(), helper, entry); auto index = CreateMemoryIndex(); helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture"); auto result = index.Search({}); REQUIRE(result.Matches.size() == 1); VerifyEntryAgainstIndex(index, result.Matches[0].first, entry); } TEST_CASE("ARPHelper_PopulateIndexFromKey_SingleValid", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); ARPHelper helper; // Create a single ARP entry under the root ARPEntry entry("SingleEntry"); entry.DisplayName = "Test Name"; entry.DisplayVersion = "1.2"; entry.Publisher = "Test Publisher"; entry.InstallLocation = "TestLocation"; entry.UninstallString = "Test Uninstall"; entry.QuietUninstallString = "Test Quiet Uninstall"; entry.WindowsInstaller = false; AddARPEntryToKey(root.get(), helper, entry); // Name and version must exist, as well as not being a system component. AddARPEntriesToKey(root.get(), helper, { { "ValidButIsSystemComponent", "A", "0.1", true }, { "NoName", {}, "0.2" }, { "Nothing" }, }); auto index = CreateMemoryIndex(); helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture"); auto result = index.Search({}); REQUIRE(result.Matches.size() == 1); VerifyEntryAgainstIndex(index, result.Matches[0].first, entry); } TEST_CASE("ARPHelper_PopulateIndexFromKey_Two", "[arphelper][list]") { auto root = RegCreateVolatileTestRoot(); Registry::Key key(root.get()); ARPHelper helper; ARPEntry entry1("FirstEntry"); entry1.DisplayName = "Test Name"; entry1.DisplayVersion = "1.2"; entry1.Publisher = "Test Publisher"; entry1.InstallLocation = "TestLocation"; entry1.UninstallString = "Test Uninstall"; entry1.QuietUninstallString = "Test Quiet Uninstall"; entry1.WindowsInstaller = true; ARPEntry entry2("SecondEntry"); entry2.DisplayName = "Different Test Name"; entry2.DisplayVersion = "31.4"; entry2.Publisher = "Different Test Publisher"; entry2.InstallLocation = "DifferentTestLocation"; entry2.UninstallString = "Different Test Uninstall"; entry2.QuietUninstallString = "Different Test Quiet Uninstall"; AddARPEntryToKey(root.get(), helper, entry1); AddARPEntryToKey(root.get(), helper, entry2); auto index = CreateMemoryIndex(); helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture"); REQUIRE(index.Search({}).Matches.size() == 2); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, entry1.EntryName); auto result = index.Search(request); REQUIRE(result.Matches.size() == 1); VerifyEntryAgainstIndex(index, result.Matches[0].first, entry1); request.Query = RequestMatch(MatchType::Exact, entry2.EntryName); result = index.Search(request); REQUIRE(result.Matches.size() == 1); VerifyEntryAgainstIndex(index, result.Matches[0].first, entry2); } TEST_CASE("PredefinedInstalledSource_Create", "[installed][list]") { auto source = CreatePredefinedInstalledSource(); } TEST_CASE("PredefinedInstalledSource_Search", "[installed][list]") { auto source = CreatePredefinedInstalledSource(); SearchRequest request; auto results = source->Search(request); REQUIRE_FALSE(results.Matches.empty()); } std::string GetDatabaseIdentifier(const std::shared_ptr& source) { return reinterpret_cast(source->CastTo(ISourceType::SQLiteIndexSource))->GetIndex().GetDatabaseIdentifier(); } void RequirePackagesHaveSameNames(std::shared_ptr& source1, std::shared_ptr& source2) { auto result1 = source1->Search({}); REQUIRE(!result1.Matches.empty()); // Ensure that all packages have the same name values for (const auto& match : result1.Matches) { std::string packageId = match.Package->GetProperty(PackageProperty::Id).get(); INFO(packageId); SearchRequest id2; id2.Inclusions.emplace_back(PackageMatchFilter{ PackageMatchField::Id, MatchType::CaseInsensitive, packageId }); auto result2 = source2->Search(id2); REQUIRE(result2.Matches.size() == 1); REQUIRE(match.Package->GetProperty(PackageProperty::Name) == result2.Matches[0].Package->GetProperty(PackageProperty::Name)); } } TEST_CASE("PredefinedInstalledSource_Create_Cached", "[installed][list][installed-cache]") { auto source1 = CreatePredefinedInstalledSource(); auto source2 = CreatePredefinedInstalledSource(); // Ensure the same identifier (which should mean the cache was not updated) REQUIRE( GetDatabaseIdentifier(source1) == GetDatabaseIdentifier(source2) ); RequirePackagesHaveSameNames(source1, source2); RequirePackagesHaveSameNames(source2, source1); } TEST_CASE("PredefinedInstalledSource_Create_ForceCacheUpdate", "[installed][list][installed-cache]") { auto source1 = CreatePredefinedInstalledSource(); auto source2 = CreatePredefinedInstalledSource(Factory::Filter::NoneWithForcedCacheUpdate); // Ensure different identifier (which should mean the cache was updated) REQUIRE( GetDatabaseIdentifier(source1) != GetDatabaseIdentifier(source2) ); RequirePackagesHaveSameNames(source1, source2); RequirePackagesHaveSameNames(source2, source1); } TEST_CASE("PredefinedInstalledSource_Create_ForceCacheUpdate_StillCached", "[installed][list][installed-cache]") { auto source1 = CreatePredefinedInstalledSource(); auto source2 = CreatePredefinedInstalledSource(Factory::Filter::NoneWithForcedCacheUpdate); auto source3 = CreatePredefinedInstalledSource(); CAPTURE(GetDatabaseIdentifier(source1), GetDatabaseIdentifier(source2), GetDatabaseIdentifier(source3)); // Ensure different identifier (which should mean the cache was updated) REQUIRE( GetDatabaseIdentifier(source1) != GetDatabaseIdentifier(source2) ); // Ensure the same identifier (which should mean the cache was not updated) REQUIRE( GetDatabaseIdentifier(source2) == GetDatabaseIdentifier(source3) ); } ================================================ FILE: src/AppInstallerCLITests/PromptFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::Settings; TEST_CASE("PromptFlow_InteractivityDisabled", "[PromptFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TestUserSettings testSettings; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_LicenseAgreement.yaml").GetPath().u8string()); SECTION("Disabled by setting") { testSettings.Set(true); } SECTION("Disabled by arg") { context.Args.AddArg(Execution::Args::Type::DisableInteractivity); } InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify prompt is not shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageAgreementsPrompt).get()) == std::string::npos); // Verify installation failed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED); REQUIRE_FALSE(std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageAgreementsNotAgreedTo).get()) != std::string::npos); } TEST_CASE("PromptFlow_InstallerAbortsTerminal_Proceed", "[PromptFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); // Accept that the installer may abort the terminal by saying "Yes" at the prompt std::istringstream installInput{ "y" }; std::ostringstream installOutput; TestContext context{ installOutput, installInput }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_AbortsTerminal.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify prompt is shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallerAbortsTerminal).get()) != std::string::npos); // Verify Installer is called. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); } TEST_CASE("PromptFlow_InstallerAbortsTerminal_Cancel", "[PromptFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); // Cancel the installation by saying "No" at the prompt that the installer may abort the terminal std::istringstream installInput{ "n" }; std::ostringstream installOutput; TestContext context{ installOutput, installInput }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_AbortsTerminal.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify prompt is shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallerAbortsTerminal).get()) != std::string::npos); // Verify installation failed REQUIRE_TERMINATED_WITH(context, E_ABORT); REQUIRE_FALSE(std::filesystem::exists(installResultPath.GetPath())); } TEST_CASE("PromptFlow_InstallLocationRequired", "[PromptFlow][workflow]") { TestCommon::TempDirectory installLocation("TempDirectory"); TestCommon::TestUserSettings testSettings; std::istringstream installInput; std::ostringstream installOutput; TestContext context{ installOutput, installInput }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_InstallLocationRequired.yaml").GetPath().u8string()); bool shouldShowPrompt = false; std::filesystem::path installResultPath = installLocation.GetPath() / "TestExeInstalled.txt"; SECTION("From argument") { context.Args.AddArg(Execution::Args::Type::InstallLocation, installLocation.GetPath().string()); } SECTION("From settings") { testSettings.Set(installLocation.GetPath().string()); // When using the default location from settings, the Package ID is appended to the root auto installLocationWithPackageId = installLocation.GetPath() / "AppInstallerCliTest.TestInstaller"; std::filesystem::create_directory(installLocationWithPackageId); installResultPath = installLocationWithPackageId / "TestExeInstalled.txt"; } SECTION("From prompt") { installInput.str(installLocation.GetPath().string()); shouldShowPrompt = true; } InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); bool promptShown = installOutput.str().find(Resource::LocString(Resource::String::InstallerRequiresInstallLocation).get()) != std::string::npos; REQUIRE(shouldShowPrompt == promptShown); // Verify Installer is called with the right parameters REQUIRE(std::filesystem::exists(installResultPath)); std::ifstream installResultFile(installResultPath); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); const auto installDirArgument = "/InstallDir " + installLocation.GetPath().string(); REQUIRE(installResultStr.find(installDirArgument) != std::string::npos); } TEST_CASE("PromptFlow_InstallLocationRequired_Missing", "[PromptFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_InstallLocationRequired.yaml").GetPath().u8string()); // Disable interactivity so that there is not prompt and we cannot get the required location context.Args.AddArg(Execution::Args::Type::DisableInteractivity); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify prompt is shown REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallerRequiresInstallLocation).get()) != std::string::npos); // Verify installation failed REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INSTALL_LOCATION_REQUIRED); REQUIRE_FALSE(std::filesystem::exists(installResultPath.GetPath())); } ================================================ FILE: src/AppInstallerCLITests/PropertySheet.props ================================================ ================================================ FILE: src/AppInstallerCLITests/Regex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace std::string_view_literals; using namespace AppInstaller::Regex; TEST_CASE("Regex_Construction", "[regex]") { Expression empty; REQUIRE(!empty); Expression lowerVowels("(a|e|i|o|u)"); REQUIRE(lowerVowels); // Ensure functionality to later verify against copy REQUIRE(lowerVowels.IsMatch(L"a")); REQUIRE(!lowerVowels.IsMatch(L"b")); Expression copy = lowerVowels; REQUIRE(copy); // Ensure that the copy can also work REQUIRE(lowerVowels.IsMatch(L"a")); REQUIRE(copy.IsMatch(L"a")); REQUIRE(!lowerVowels.IsMatch(L"b")); REQUIRE(!copy.IsMatch(L"b")); Expression moved = std::move(copy); REQUIRE(moved); REQUIRE(moved.IsMatch(L"a")); REQUIRE(!moved.IsMatch(L"b")); } TEST_CASE("Regex_IsMatch", "[regex]") { Expression ArchitectureX32{ R"((X32|X86)(?=\P{Nd}|$)(?:\sEDITION)?)", Options::CaseInsensitive }; REQUIRE(ArchitectureX32.IsMatch(L"X32")); REQUIRE(ArchitectureX32.IsMatch(L"X86 edition")); REQUIRE(!ArchitectureX32.IsMatch(L"Not a match")); REQUIRE(!ArchitectureX32.IsMatch(L"X86 editions")); } TEST_CASE("Regex_Replace", "[regex]") { Expression test{ R"((b|d\s|vy))" }; REQUIRE(test.Replace(L"The bright and swervy", {}) == std::wstring{ L"The right answer" }); Expression vowels{ "(a|e|i|o|u)", Options::CaseInsensitive }; REQUIRE(vowels.Replace(L"The QUICK brown fox jumped over the lazy dog.", L"[$0]") == std::wstring{ L"Th[e] Q[U][I]CK br[o]wn f[o]x j[u]mp[e]d [o]v[e]r th[e] l[a]zy d[o]g." }); } TEST_CASE("Regex_ForEach", "[regex]") { std::wstring input = L"The words in the Sentence but no more"; std::vector expected = { L"The", L"words", L"in", L"the", L"Sentence" }; Expression test{ R"(\S+)" }; size_t i = 0; test.ForEach(input, [&](bool isMatch, std::wstring_view text) { if (!isMatch) { REQUIRE(text == L" "); return true; } else { REQUIRE(i < expected.size()); REQUIRE(text == expected[i]); ++i; return i < expected.size(); } }); } ================================================ FILE: src/AppInstallerCLITests/Registry.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace AppInstaller::Registry; using namespace AppInstaller::Utility; using namespace TestCommon; TEST_CASE("EmptyKey", "[registry]") { Key key; REQUIRE(!key); } TEST_CASE("Constructor_NotFound", "[registry]") { Key key; REQUIRE_THROWS_HR(key = Key(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Foo\\Bar\\Does\\Not\\Exist"), HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } TEST_CASE("OpenIfExists_NotFound", "[registry]") { Key key = Key::OpenIfExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Foo\\Bar\\Does\\Not\\Exist"); REQUIRE(!key); } TEST_CASE("CreateKeyAndDelete", "[registry]") { std::wstring subkey = L"Foo\\Bar"; wil::unique_hkey root = RegCreateVolatileTestRoot(); Key key = Key::Create(root.get(), subkey, REG_OPTION_VOLATILE); REQUIRE(key); Key::Delete(root.get(), subkey, KEY_WOW64_64KEY); Key secondKey = Key::OpenIfExists(root.get(), subkey); REQUIRE(!secondKey); } TEST_CASE("SetKeyValue", "[registry]") { std::wstring valueName = L"TestValueName"; std::wstring valueValue = L"TestValueValue"; std::wstring subkey = L"FooBar"; wil::unique_hkey root = RegCreateVolatileTestRoot(); Key key = Key::Create(root.get(), subkey, REG_OPTION_VOLATILE); key.SetValue(valueName, valueValue, REG_SZ); auto value = key[valueName]; REQUIRE(value); REQUIRE(value->GetType() == Value::Type::String); REQUIRE(value->GetValue() == ConvertToUTF8(valueValue)); } TEST_CASE("DeleteKeyValue", "[registry]") { std::wstring valueName = L"TestValueName"; std::wstring valueValue = L"TestValueValue"; std::wstring subkey = L"FooBar"; wil::unique_hkey root = RegCreateVolatileTestRoot(); Key key = Key::Create(root.get(), subkey, REG_OPTION_VOLATILE); key.SetValue(valueName, valueValue, REG_SZ); auto value = key[valueName]; REQUIRE(value); key.DeleteValue(valueName); value = key[valueName]; REQUIRE(!value); } TEST_CASE("EnumerateKeys", "[registry]") { wil::unique_hkey root = RegCreateVolatileTestRoot(); std::vector subKeyNames = { L"A", L"BEE", L"SEE", L"deigh" }; for (const auto& name : subKeyNames) { RegCreateVolatileSubKey(root.get(), name); } Key key{ root.get(), L"" }; for (const auto& subkey : key) { INFO(subkey.Name()); std::wstring nameUtf16 = ConvertToUTF16(subkey.Name()); auto itr = std::find(subKeyNames.begin(), subKeyNames.end(), nameUtf16); if (itr == subKeyNames.end()) { FAIL(); } else { subKeyNames.erase(itr); } Key sk = subkey.Open(); REQUIRE(sk); } REQUIRE(subKeyNames.empty()); } TEST_CASE("Values_String", "[registry]") { std::wstring valueName = L"TestValueName"; std::wstring valueValue = L"TestValueValue"; wil::unique_hkey root = RegCreateVolatileTestRoot(); SetRegistryValue(root.get(), valueName, valueValue); Key key{ root.get(), L"" }; auto value = key[valueName]; REQUIRE(value); REQUIRE(value->GetType() == Value::Type::String); REQUIRE(value->GetValue() == ConvertToUTF8(valueValue)); } TEST_CASE("Values_WideStringWithNarrowNull", "[registry]") { std::wstring valueName = L"TestValueName"; std::wstring valueValue = L"TestValueValue"; wil::unique_hkey root = RegCreateVolatileTestRoot(); // Copy the bytes from the string value into a byte vector std::vector valueBytes; valueBytes.resize((valueValue.length() + 1) * sizeof(wchar_t)); memcpy_s(valueBytes.data(), valueBytes.size(), valueValue.c_str(), (valueValue.length() + 1) * sizeof(wchar_t)); // Remove the last byte to make a narrow null valueBytes.resize(valueBytes.size() - 1); SetRegistryValue(root.get(), valueName, valueBytes, REG_SZ); Key key{ root.get(), L"" }; auto value = key[valueName]; REQUIRE(value); REQUIRE(value->GetType() == Value::Type::String); REQUIRE(value->GetValue() == ConvertToUTF8(valueValue)); } TEST_CASE("Values_ExpandString", "[registry]") { std::wstring valueName = L"TestValueName"; std::wstring valueValue = L"%TEMP%"; wil::unique_hkey root = RegCreateVolatileTestRoot(); SetRegistryValue(root.get(), valueName, valueValue, REG_EXPAND_SZ); Key key{ root.get(), L"" }; auto value = key[valueName]; REQUIRE(value); REQUIRE(value->GetType() == Value::Type::ExpandString); REQUIRE(value->GetValue() == ConvertToUTF8(valueValue)); wchar_t buffer[MAX_PATH]; GetTempPathW(ARRAYSIZE(buffer), buffer); std::string tempPath = ConvertToUTF8(buffer); if (!tempPath.empty() && tempPath.back() == '\\') { tempPath.resize(tempPath.size() - 1); } REQUIRE(value->GetValue() == tempPath); } TEST_CASE("Values_Binary", "[registry]") { std::wstring valueName = L"TestValueName"; std::vector valueValue = { 2, 7, 3, 14, 42 }; wil::unique_hkey root = RegCreateVolatileTestRoot(); SetRegistryValue(root.get(), valueName, valueValue); Key key{ root.get(), L"" }; auto value = key[valueName]; REQUIRE(value); REQUIRE(value->GetType() == Value::Type::Binary); auto result = value->GetValue(); REQUIRE(result.size() == valueValue.size()); for (size_t i = 0; i < result.size(); ++i) { INFO(i); REQUIRE(result[i] == valueValue[i]); } } TEST_CASE("Values_DWORD", "[registry]") { std::wstring valueName = L"TestValueName"; DWORD valueValue = 42; wil::unique_hkey root = RegCreateVolatileTestRoot(); SetRegistryValue(root.get(), valueName, valueValue); Key key{ root.get(), L"" }; auto value = key[valueName]; REQUIRE(value); REQUIRE(value->GetType() == Value::Type::DWord); REQUIRE(value->GetValue() == valueValue); } ================================================ FILE: src/AppInstallerCLITests/Resources.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include using namespace std::string_view_literals; using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; using namespace AppInstaller::CLI; #define WINGET_TEST_OUTPUT_STREAM(_expected_, _input_) \ do { \ std::istringstream iInput; \ std::ostringstream oInput; \ std::istringstream iExpected; \ std::ostringstream oExpected; \ Execution::Reporter(oInput, iInput).Info() << _input_; \ Execution::Reporter(oExpected, iExpected).Info() << _expected_; \ REQUIRE(oExpected.str()== oInput.str()); \ } while(0); TEST_CASE("Resources_StringId", "[resources]") { WINGET_TEST_OUTPUT_STREAM( "Filter results by command"_liv, Resource::String::CommandArgumentDescription ); } TEST_CASE("Resources_StringIdWithPlaceholders_LocIndString", "[resources]") { WINGET_TEST_OUTPUT_STREAM( "The value provided for the `First` argument is invalid; valid values are: Second"_liv , Resource::String::InvalidArgumentValueError("First"_liv, "Second"_liv) ); } TEST_CASE("Resources_StringIdWithPlaceholders_StringId", "[resources]") { WINGET_TEST_OUTPUT_STREAM( "This operation is disabled by Group Policy: Enable Additional Windows App Installer Sources"_liv , Resource::String::DisabledByGroupPolicy(AppInstaller::StringResource::String::PolicyAdditionalSources) ); } TEST_CASE("Resources_StringIdWithPlaceholders_Arithmetic", "[resources]") { WINGET_TEST_OUTPUT_STREAM( "42 upgrades available."_liv , Resource::String::AvailableUpgrades(42) ); } ================================================ FILE: src/AppInstallerCLITests/Rest.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "cpprest/json.h" #include using namespace AppInstaller::Rest; TEST_CASE("ValidateAndGetRestAPIBaseUri", "[RestSource]") { REQUIRE(GetRestAPIBaseUri("https://restsource.azurewebsites.net/api/ ") == L"https://restsource.azurewebsites.net/api"); REQUIRE(GetRestAPIBaseUri("http://rest_sourc e.azurewebsites.net/api") == L"http://rest_sourc%20e.azurewebsites.net/api"); REQUIRE(GetRestAPIBaseUri("http://restsource.azurewebsites.net/v1.0/%v1") == L"http://restsource.azurewebsites.net/v1.0/%25v1"); } TEST_CASE("IsValidUri", "[RestSource]") { REQUIRE(IsValidUri(L"http://rest%20source.azurewebsites.net/api")); REQUIRE(!IsValidUri(L"http://rest source.azurewebsites.net/api")); } TEST_CASE("AppendPathToUri", "[RestSource]") { REQUIRE(AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/path") == L"http://restsource.azurewebsites.net/api/path"); REQUIRE(AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/pat h") == L"http://restsource.azurewebsites.net/api/pat%20%20h"); REQUIRE(AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/path+version") == L"http://restsource.azurewebsites.net/api/path%2Bversion"); } TEST_CASE("AppendQueryParamsToUri", "[RestSource]") { utility::string_t url = L"http://restsource.azurewebsites.net/api"; std::map queryParams; queryParams.emplace("Version", "1.0 .0"); queryParams.emplace("Channel", "beta+"); REQUIRE(AppendQueryParamsToUri(url, queryParams) == L"http://restsource.azurewebsites.net/api?Channel=beta%2B&Version=1.0%20.0"); } TEST_CASE("GetUniqueItems", "[RestSource]") { std::vector list; REQUIRE(GetUniqueItems(list).size() == 0); std::vector listWithDuplicates; listWithDuplicates.emplace_back("object1"); listWithDuplicates.emplace_back("object1"); listWithDuplicates.emplace_back("object2"); listWithDuplicates.emplace_back("object2"); listWithDuplicates.emplace_back("object3"); std::vector result = GetUniqueItems(listWithDuplicates); REQUIRE(result.size() == 3); REQUIRE(result.at(0) == "object1"); REQUIRE(result.at(1) == "object2"); REQUIRE(result.at(2) == "object3"); } ================================================ FILE: src/AppInstallerCLITests/RestClient.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include #include #include #include using namespace AppInstaller; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; const std::string TestRestUri = "http://restsource.net"; RestClient CreateRestClient( const std::string& restApi, const std::optional& customHeader, std::string_view caller, const Http::HttpClientHelper& helper, const Authentication::AuthenticationArguments& authArgs = {}) { return RestClient::Create(restApi, customHeader, caller, helper, RestClient::GetInformation(restApi, customHeader, caller, helper), authArgs); } TEST_CASE("GetLatestCommonVersion", "[RestSource]") { std::set wingetSupportedContracts = { Version {"1.0.0"}, Version {"1.2.0"} }; std::vector versions{ "1.0.0", "2.0.0", "1.2.0" }; std::optional actual = RestClient::GetLatestCommonVersion(versions, wingetSupportedContracts); REQUIRE(actual); REQUIRE(actual.value().ToString() == "1.2.0"); } TEST_CASE("GetLatestCommonVersion_OnlyMajorMinorVersionMatched", "[RestSource]") { std::set wingetSupportedContracts = { Version {"1.0.0"}, Version {"1.2.0"} }; std::vector versions{ "1.0.0", "2.0.0", "1.2.1" }; std::optional actual = RestClient::GetLatestCommonVersion(versions, wingetSupportedContracts); REQUIRE(actual); REQUIRE(actual.value().ToString() == "1.2.0"); } TEST_CASE("GetLatestCommonVersion_UnsupportedVersion", "[RestSource]") { std::set wingetSupportedContracts = { Version {"3.0.0"}, Version {"4.2.0"} }; std::vector versions{ "1.0.0", "2.0.0" }; std::optional actual = RestClient::GetLatestCommonVersion(versions, wingetSupportedContracts); REQUIRE(!actual); } TEST_CASE("GetSupportedInterface", "[RestSource]") { IRestClient::Information info{ "TestId", { "1.0.0" } }; Version version{ "1.0.0" }; REQUIRE(RestClient::GetSupportedInterface(TestRestUri, {}, info, {}, version, {})->GetVersion() == version); // Update this test to next version so that we don't forget to add to supported versions before rest e2e tests are available. Version invalid{ "1.13.0" }; REQUIRE_THROWS_HR(RestClient::GetSupportedInterface(TestRestUri, {}, info, {}, invalid, {}), APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION); Authentication::AuthenticationArguments authArgs; authArgs.Mode = Authentication::AuthenticationMode::Silent; Version version_1_7{ "1.7.0" }; // GetSupportedInterface throws on unknown authentication type. IRestClient::Information infoWithUnknownAuthenticationType{ "TestId", { "1.7.0" } }; infoWithUnknownAuthenticationType.Authentication.Type = Authentication::AuthenticationType::Unknown; REQUIRE_THROWS_HR(RestClient::GetSupportedInterface(TestRestUri, {}, infoWithUnknownAuthenticationType, authArgs, version_1_7, {}), APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED); // GetSupportedInterface throws on invalid authentication info. IRestClient::Information infoWithInvalidAuthenticationInfo{ "TestId", { "1.7.0" } }; infoWithInvalidAuthenticationInfo.Authentication.Type = Authentication::AuthenticationType::MicrosoftEntraId; REQUIRE_THROWS_HR(RestClient::GetSupportedInterface(TestRestUri, {}, infoWithInvalidAuthenticationInfo, authArgs, version_1_7, {}), APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO); } TEST_CASE("GetInformation_Success", "[RestSource]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.0.0", "1.1.0" ], "SourceAgreements": { "AgreementsIdentifier": "agreementV1", "Agreements": [{ "AgreementLabel": "EULA", "Agreement": "this is store agreement", "AgreementUrl": "https://store.agreement" } ] }, "RequiredQueryParameters": [ "Market" ], "RequiredPackageMatchFields": [ "Market" ], "UnsupportedQueryParameters": [ "Moniker" ], "UnsupportedPackageMatchFields": [ "Moniker" ] }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; IRestClient::Information information = RestClient::GetInformation(TestRestUri, {}, {}, helper); REQUIRE(information.SourceIdentifier == "Source123"); REQUIRE(information.ServerSupportedVersions.size() == 2); REQUIRE(information.ServerSupportedVersions.at(0) == "1.0.0"); REQUIRE(information.ServerSupportedVersions.at(1) == "1.1.0"); REQUIRE(information.SourceAgreementsIdentifier == "agreementV1"); REQUIRE(information.SourceAgreements.size() == 1); REQUIRE(information.SourceAgreements.at(0).Label == "EULA"); REQUIRE(information.SourceAgreements.at(0).Text == "this is store agreement"); REQUIRE(information.SourceAgreements.at(0).Url == "https://store.agreement"); REQUIRE(information.RequiredQueryParameters.size() == 1); REQUIRE(information.RequiredQueryParameters.at(0) == "Market"); REQUIRE(information.RequiredPackageMatchFields.size() == 1); REQUIRE(information.RequiredPackageMatchFields.at(0) == "Market"); REQUIRE(information.UnsupportedQueryParameters.size() == 1); REQUIRE(information.UnsupportedQueryParameters.at(0) == "Moniker"); REQUIRE(information.UnsupportedPackageMatchFields.size() == 1); REQUIRE(information.UnsupportedPackageMatchFields.at(0) == "Moniker"); REQUIRE(information.Authentication.Type == Authentication::AuthenticationType::None); REQUIRE_FALSE(information.Authentication.MicrosoftEntraIdInfo.has_value()); } TEST_CASE("GetInformation_WithAuthenticationInfo_Success", "[RestSource]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.7.0" ], "SourceAgreements": { "AgreementsIdentifier": "agreementV1", "Agreements": [{ "AgreementLabel": "EULA", "Agreement": "this is store agreement", "AgreementUrl": "https://store.agreement" } ] }, "RequiredQueryParameters": [ "Market" ], "RequiredPackageMatchFields": [ "Market" ], "UnsupportedQueryParameters": [ "Moniker" ], "UnsupportedPackageMatchFields": [ "Moniker" ], "Authentication": { "AuthenticationType": "microsoftEntraId", "MicrosoftEntraIdAuthenticationInfo" : { "Resource": "GUID", "Scope" : "test" } } }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; IRestClient::Information information = RestClient::GetInformation(TestRestUri, {}, {}, helper); REQUIRE(information.SourceIdentifier == "Source123"); REQUIRE(information.ServerSupportedVersions.size() == 1); REQUIRE(information.ServerSupportedVersions.at(0) == "1.7.0"); REQUIRE(information.SourceAgreementsIdentifier == "agreementV1"); REQUIRE(information.SourceAgreements.size() == 1); REQUIRE(information.SourceAgreements.at(0).Label == "EULA"); REQUIRE(information.SourceAgreements.at(0).Text == "this is store agreement"); REQUIRE(information.SourceAgreements.at(0).Url == "https://store.agreement"); REQUIRE(information.RequiredQueryParameters.size() == 1); REQUIRE(information.RequiredQueryParameters.at(0) == "Market"); REQUIRE(information.RequiredPackageMatchFields.size() == 1); REQUIRE(information.RequiredPackageMatchFields.at(0) == "Market"); REQUIRE(information.UnsupportedQueryParameters.size() == 1); REQUIRE(information.UnsupportedQueryParameters.at(0) == "Moniker"); REQUIRE(information.UnsupportedPackageMatchFields.size() == 1); REQUIRE(information.UnsupportedPackageMatchFields.at(0) == "Moniker"); REQUIRE(information.Authentication.Type == Authentication::AuthenticationType::MicrosoftEntraId); REQUIRE(information.Authentication.MicrosoftEntraIdInfo.has_value()); REQUIRE(information.Authentication.MicrosoftEntraIdInfo->Resource == "GUID"); REQUIRE(information.Authentication.MicrosoftEntraIdInfo->Scope == "test"); } TEST_CASE("GetInformation_Fail_AgreementsWithoutIdentifier", "[RestSource]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.0.0", "1.1.0"], "SourceAgreements": { "Agreements": [{ "AgreementLabel": "EULA", "Agreement": "this is store agreement", "AgreementUrl": "https://store.agreement" } ] } }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, helper), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE); } TEST_CASE("GetInformation_Fail_InvalidMicrosoftEntraIdInfo", "[RestSource]") { utility::string_t sample1 = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.7.0" ], "Authentication": { "AuthenticationType": "microsoftEntraId" } }})delimiter"); HttpClientHelper helper1{ GetTestRestRequestHandler(web::http::status_codes::OK, sample1) }; REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, helper1), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE); utility::string_t sample2 = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.7.0" ], "Authentication": { "AuthenticationType": "microsoftEntraId", "MicrosoftEntraIdAuthenticationInfo" : { "Resource": "", "Scope" : "test" } } }})delimiter"); HttpClientHelper helper2{ GetTestRestRequestHandler(web::http::status_codes::OK, sample2) }; REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, helper2), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE); utility::string_t sample3 = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.7.0" ], "Authentication": { "AuthenticationType": "microsoftEntraId", "MicrosoftEntraIdAuthenticationInfo" : { "Scope" : "test" } } }})delimiter"); HttpClientHelper helper3{ GetTestRestRequestHandler(web::http::status_codes::OK, sample3) }; REQUIRE_THROWS_HR(RestClient::GetInformation(TestRestUri, {}, {}, helper3), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE); utility::string_t sample4 = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.7.0" ], "Authentication": { "AuthenticationType": "microsoftEntraIdForAzureBlobStorage" } }})delimiter"); HttpClientHelper helper4{ GetTestRestRequestHandler(web::http::status_codes::OK, sample4) }; Authentication::AuthenticationArguments authArgs; authArgs.Mode = Authentication::AuthenticationMode::Silent; Version version_1_7{ "1.7.0" }; REQUIRE_THROWS_HR(RestClient::GetSupportedInterface(TestRestUri, {}, RestClient::GetInformation(TestRestUri, {}, {}, helper4), authArgs, version_1_7, {}), APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED); } TEST_CASE("RestClientCreate_UnsupportedVersion", "[RestSource]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.2.0", "2.0.0"] }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; REQUIRE_THROWS_HR(CreateRestClient("https://restsource.com/api", {}, {}, helper), APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE); } TEST_CASE("RestClientCreate_UnsupportedAuthenticationMethod", "[RestSource]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.7.0" ], "Authentication": { "AuthenticationType": "unknown" } }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; Authentication::AuthenticationArguments authArgs; authArgs.Mode = Authentication::AuthenticationMode::Silent; REQUIRE_THROWS_HR(CreateRestClient("https://restsource.com/api", {}, {}, helper, authArgs), APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED); } TEST_CASE("RestClientCreate_InvalidAuthenticationArguments", "[RestSource]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.7.0" ], "Authentication": { "AuthenticationType": "microsoftEntraId", "MicrosoftEntraIdAuthenticationInfo" : { "Resource" : "test" } } }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; Authentication::AuthenticationArguments authArgs; authArgs.Mode = Authentication::AuthenticationMode::Unknown; REQUIRE_THROWS_HR(CreateRestClient("https://restsource.com/api", {}, {}, helper, authArgs), E_UNEXPECTED); } TEST_CASE("RestClientCreate_1.0_Success", "[RestSource]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.0.0", "2.0.0"] }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; RestClient client = CreateRestClient(TestRestUri, {}, {}, helper); REQUIRE(client.GetSourceIdentifier() == "Source123"); } TEST_CASE("RestClientCreate_1.1_Success", "[RestSource]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.0.0", "1.1.0"], "SourceAgreements": { "AgreementsIdentifier": "agreementV1", "Agreements": [{ "AgreementLabel": "EULA", "Agreement": "this is store agreement", "AgreementUrl": "https://store.agreement" } ] }, "RequiredQueryParameters": [ "Market" ], "RequiredPackageMatchFields": [ "Market" ], "UnsupportedQueryParameters": [ "Moniker" ], "UnsupportedPackageMatchFields": [ "Moniker" ] }})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; RestClient client = CreateRestClient(TestRestUri, {}, {}, helper); REQUIRE(client.GetSourceIdentifier() == "Source123"); auto information = client.GetSourceInformation(); REQUIRE(information.SourceAgreementsIdentifier == "agreementV1"); REQUIRE(information.SourceAgreements.size() == 1); REQUIRE(information.SourceAgreements.at(0).Label == "EULA"); REQUIRE(information.SourceAgreements.at(0).Text == "this is store agreement"); REQUIRE(information.SourceAgreements.at(0).Url == "https://store.agreement"); REQUIRE(information.RequiredQueryParameters.size() == 1); REQUIRE(information.RequiredQueryParameters.at(0) == "Market"); REQUIRE(information.RequiredPackageMatchFields.size() == 1); REQUIRE(information.RequiredPackageMatchFields.at(0) == "Market"); REQUIRE(information.UnsupportedQueryParameters.size() == 1); REQUIRE(information.UnsupportedQueryParameters.at(0) == "Moniker"); REQUIRE(information.UnsupportedPackageMatchFields.size() == 1); REQUIRE(information.UnsupportedPackageMatchFields.at(0) == "Moniker"); } TEST_CASE("RestClientCreate_1.7_Success", "[RestSource]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : { "SourceIdentifier": "Source123", "ServerSupportedVersions": [ "1.7.0" ], "SourceAgreements": { "AgreementsIdentifier": "agreementV1", "Agreements": [{ "AgreementLabel": "EULA", "Agreement": "this is store agreement", "AgreementUrl": "https://store.agreement" } ] }, "RequiredQueryParameters": [ "Market" ], "RequiredPackageMatchFields": [ "Market" ], "UnsupportedQueryParameters": [ "Moniker" ], "UnsupportedPackageMatchFields": [ "Moniker" ], "Authentication": { "AuthenticationType": "microsoftEntraId", "MicrosoftEntraIdAuthenticationInfo" : { "Resource": "GUID", "Scope" : "test" } } }})delimiter"); Authentication::AuthenticationArguments authArgs; authArgs.Mode = Authentication::AuthenticationMode::Silent; HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, sample) }; RestClient client = CreateRestClient(TestRestUri, {}, {}, helper, authArgs); REQUIRE(client.GetSourceIdentifier() == "Source123"); auto information = client.GetSourceInformation(); REQUIRE(information.SourceAgreementsIdentifier == "agreementV1"); REQUIRE(information.SourceAgreements.size() == 1); REQUIRE(information.SourceAgreements.at(0).Label == "EULA"); REQUIRE(information.SourceAgreements.at(0).Text == "this is store agreement"); REQUIRE(information.SourceAgreements.at(0).Url == "https://store.agreement"); REQUIRE(information.RequiredQueryParameters.size() == 1); REQUIRE(information.RequiredQueryParameters.at(0) == "Market"); REQUIRE(information.RequiredPackageMatchFields.size() == 1); REQUIRE(information.RequiredPackageMatchFields.at(0) == "Market"); REQUIRE(information.UnsupportedQueryParameters.size() == 1); REQUIRE(information.UnsupportedQueryParameters.at(0) == "Moniker"); REQUIRE(information.UnsupportedPackageMatchFields.size() == 1); REQUIRE(information.UnsupportedPackageMatchFields.at(0) == "Moniker"); REQUIRE(information.Authentication.Type == Authentication::AuthenticationType::MicrosoftEntraId); REQUIRE(information.Authentication.MicrosoftEntraIdInfo.has_value()); REQUIRE(information.Authentication.MicrosoftEntraIdInfo->Resource == "GUID"); REQUIRE(information.Authentication.MicrosoftEntraIdInfo->Scope == "test"); } // Simulate the msstore cache round trip using real world data. TEST_CASE("RestInformationCache_RoundTrip", "[RestInformationCache]") { Settings::Stream{ Settings::Stream::RestInformationCache }.Remove(); std::wstring endpoint = L"https://test-url-com/information"; CacheControlPolicy cacheControl{ L"public, max-age=77287" }; auto response = web::json::value::parse( R"delimiter({ "$type": "Microsoft.Marketplace.Storefront.StoreEdgeFD.BusinessLogic.Response.PackageMetadata.PackageMetadataResponse, StoreEdgeFD", "Data": { "$type": "Microsoft.Marketplace.Storefront.StoreEdgeFD.BusinessLogic.Response.PackageMetadata.PackageMetadataData, StoreEdgeFD", "SourceIdentifier": "StoreEdgeFD", "SourceAgreements": { "$type": "Microsoft.Marketplace.Storefront.StoreEdgeFD.BusinessLogic.Response.PackageMetadata.SourceAgreements, StoreEdgeFD", "AgreementsIdentifier": "StoreEdgeFD", "Agreements": [ { "$type": "Microsoft.Marketplace.Storefront.StoreEdgeFD.BusinessLogic.Response.PackageManifest.AgreementDetail, StoreEdgeFD", "AgreementLabel": "Terms of Transaction", "AgreementUrl": "https://aka.ms/microsoft-store-terms-of-transaction" } ] }, "ServerSupportedVersions": [ "1.0.0", "1.1.0", "1.6.0" ], "RequiredQueryParameters": [ "market" ], "RequiredPackageMatchFields": [ "market" ] } })delimiter"); RestInformationCache cache; cache.Cache(endpoint, {}, {}, cacheControl, response); auto cachedValue = cache.Get(endpoint, {}, {}); REQUIRE(cachedValue.has_value()); InformationResponseDeserializer deserializer; const auto expected = deserializer.Deserialize(response); const auto& actual = cachedValue.value(); REQUIRE(expected.SourceIdentifier == actual.SourceIdentifier); REQUIRE(expected.SourceAgreementsIdentifier == actual.SourceAgreementsIdentifier); REQUIRE(expected.SourceAgreements.size() == actual.SourceAgreements.size()); REQUIRE(1 == actual.SourceAgreements.size()); REQUIRE(expected.SourceAgreements[0].Label == actual.SourceAgreements[0].Label); REQUIRE(expected.SourceAgreements[0].Text == actual.SourceAgreements[0].Text); REQUIRE(expected.SourceAgreements[0].Url == actual.SourceAgreements[0].Url); REQUIRE(expected.ServerSupportedVersions.size() == actual.ServerSupportedVersions.size()); for (const auto& expectedVersion : expected.ServerSupportedVersions) { REQUIRE(std::find(actual.ServerSupportedVersions.begin(), actual.ServerSupportedVersions.end(), expectedVersion) != actual.ServerSupportedVersions.end()); } REQUIRE(expected.RequiredQueryParameters.size() == actual.RequiredQueryParameters.size()); REQUIRE(1 == actual.RequiredQueryParameters.size()); REQUIRE(expected.RequiredQueryParameters[0] == actual.RequiredQueryParameters[0]); REQUIRE(expected.RequiredPackageMatchFields.size() == actual.RequiredPackageMatchFields.size()); REQUIRE(1 == actual.RequiredPackageMatchFields.size()); REQUIRE(expected.RequiredPackageMatchFields[0] == actual.RequiredPackageMatchFields[0]); } web::json::value CreateInformationResponse(std::string_view identifier) { std::ostringstream stream; stream << R"({ "Data": { "SourceIdentifier": ")" << identifier << R"(", "ServerSupportedVersions": [ "1.0.0" ] } })"; return web::json::value::parse(stream.str()); } TEST_CASE("RestInformationCache_Get", "[RestInformationCache]") { Settings::Stream{ Settings::Stream::RestInformationCache }.Remove(); std::wstring endpoint1 = L"https://test-url1-com/information"; std::wstring endpoint2 = L"https://test-url2-com/information"; std::wstring endpointNotPresent = L"https://test-url-not-present-com/information"; std::string header = "Header"; std::string caller = "Caller"; std::string publicEndpoint1Identifier = "Identifier1"; std::string privateEndpoint1Identifier = "Identifier2"; std::string privateEndpoint2Identifier = "Identifier3"; auto publicEndpoint1Response = CreateInformationResponse(publicEndpoint1Identifier); auto privateEndpoint1Response = CreateInformationResponse(privateEndpoint1Identifier); auto privateEndpoint2Response = CreateInformationResponse(privateEndpoint2Identifier); RestInformationCache cache; // Cache: // 1. public and private for same endpoint cache.Cache(endpoint1, header, caller, { L"public" }, publicEndpoint1Response); cache.Cache(endpoint1, header, caller, {}, privateEndpoint1Response); // 2. another endpoint with private data (same headers) cache.Cache(endpoint2, header, caller, {}, privateEndpoint2Response); SECTION("Same headers prefers private") { auto cachedValue = cache.Get(endpoint1, header, caller); REQUIRE(cachedValue.has_value()); REQUIRE(privateEndpoint1Identifier == cachedValue->SourceIdentifier); } SECTION("Different headers falls back to public") { auto cachedValue = cache.Get(endpoint1, "Different", "Different"); REQUIRE(cachedValue.has_value()); REQUIRE(publicEndpoint1Identifier == cachedValue->SourceIdentifier); } SECTION("Second endpoint") { auto cachedValue = cache.Get(endpoint2, header, caller); REQUIRE(cachedValue.has_value()); REQUIRE(privateEndpoint2Identifier == cachedValue->SourceIdentifier); } SECTION("Second endpoint different headers") { auto cachedValue = cache.Get(endpoint2, {}, {}); REQUIRE(!cachedValue.has_value()); } SECTION("Missing endpoint") { auto cachedValue = cache.Get(endpointNotPresent, header, caller); REQUIRE(!cachedValue.has_value()); } } TEST_CASE("RestInformationCache_Cache_NoStore", "[RestInformationCache]") { Settings::Stream{ Settings::Stream::RestInformationCache }.Remove(); std::wstring endpoint = L"https://test-url-com/information"; RestInformationCache cache; cache.Cache(endpoint, {}, {}, { L"no-store" }, CreateInformationResponse("Identifier")); auto cachedValue = cache.Get(endpoint, {}, {}); REQUIRE(!cachedValue.has_value()); } TEST_CASE("RestInformationCache_Cache_Expiration", "[RestInformationCache]") { Settings::Stream{ Settings::Stream::RestInformationCache }.Remove(); std::wstring endpoint = L"https://test-url-com/information"; RestInformationCache cache; cache.Cache(endpoint, {}, {}, { L"max-age=2" }, CreateInformationResponse("Identifier")); auto cachedValue = cache.Get(endpoint, {}, {}); REQUIRE(cachedValue.has_value()); std::this_thread::sleep_for(5s); cachedValue = cache.Get(endpoint, {}, {}); REQUIRE(!cachedValue.has_value()); } TEST_CASE("RestInformationCache_Cache_Overwrite", "[RestInformationCache]") { Settings::Stream{ Settings::Stream::RestInformationCache }.Remove(); std::wstring endpoint = L"https://test-url-com/information"; std::string identifier1 = "Identifier1"; std::string identifier2 = "Identifier2"; RestInformationCache cache; cache.Cache(endpoint, {}, {}, {}, CreateInformationResponse(identifier1)); auto cachedValue = cache.Get(endpoint, {}, {}); REQUIRE(cachedValue.has_value()); REQUIRE(identifier1 == cachedValue->SourceIdentifier); cache.Cache(endpoint, {}, {}, {}, CreateInformationResponse(identifier2)); cachedValue = cache.Get(endpoint, {}, {}); REQUIRE(cachedValue.has_value()); REQUIRE(identifier2 == cachedValue->SourceIdentifier); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_0; namespace { const std::string TestRestUriString = "http://restsource.com/api"; utility::string_t GetGoodManifest_RequiredFields() { return _XPLATSTR( R"delimiter({ "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "5.0.0", "DefaultLocale": { "PackageLocale": "en-us", "Publisher": "Foo", "PackageName": "Bar", "License": "Foo bar license", "ShortDescription": "Foo bar description" }, "Installers": [ { "Architecture": "x64", "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerType": "exe", "InstallerUrl": "https://installer.example.com/foobar.exe" } ] } ] } })delimiter"); } utility::string_t GetManifestsResponse_MultipleVersions() { return _XPLATSTR( R"delimiter({ "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "5.0.0", "DefaultLocale": { "PackageLocale": "en-us", "Publisher": "Foo", "PackageName": "Bar", "License": "Foo bar license", "ShortDescription": "Foo bar description" }, "Installers": [ { "Architecture": "x64", "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerType": "exe", "InstallerUrl": "https://installer.example.com/foobar.exe" } ] }, { "PackageVersion": "6.0.0", "DefaultLocale": { "PackageLocale": "en-us", "Publisher": "Foo", "PackageName": "Bar", "License": "Foo bar license", "ShortDescription": "Foo bar description" }, "Installers": [ { "Architecture": "x64", "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerType": "exe", "InstallerUrl": "https://installer.example.com/foobar.exe" } ] } ] } })delimiter"); } struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker" }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ] } ], "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.exe", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "msix", "Scope": "user", "SignatureSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "install", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "PackageFamilyName": "FooBar.PackageFamilyName", "ProductCode": "", "Capabilities": [ "Bluetooth" ], "RestrictedCapabilities": [ "restrictedCapability" ] } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); } void VerifyInstallers_AllFields(const Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.exe"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Msix); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.SignatureSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 7); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Install); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == "FooBar.PackageFamilyName"); REQUIRE(actualInstaller.ProductCode == ""); REQUIRE(actualInstaller.Capabilities.at(0) == "Bluetooth"); REQUIRE(actualInstaller.RestrictedCapabilities.at(0) == "restrictedCapability"); } }; } TEST_CASE("Search_GoodResponse", "[RestSource][Interface_1_0]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [ { "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": [ { "PackageVersion": "1.0.0" }, { "PackageVersion": "2.0.0" }] }] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1{ TestRestUriString, std::move(helper) }; Schema::IRestClient::SearchResult searchResponse = v1.Search({}); REQUIRE(searchResponse.Matches.size() == 1); Schema::IRestClient::Package package = searchResponse.Matches.at(0); REQUIRE(package.PackageInformation.PackageIdentifier.compare("git.package") == 0); REQUIRE(package.PackageInformation.Publisher.compare("git") == 0); REQUIRE(package.PackageInformation.PackageName.compare("package") == 0); REQUIRE(package.Versions.size() == 2); REQUIRE(package.Versions.at(0).VersionAndChannel.GetVersion().ToString().compare("1.0.0") == 0); REQUIRE(package.Versions.at(1).VersionAndChannel.GetVersion().ToString().compare("2.0.0") == 0); } TEST_CASE("Search_GoodResponse_AllFields", "[RestSource][Interface_1_0]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [ { "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": [ { "PackageVersion": "1.0.0", "PackageFamilyNames" : [ "pfn1", "pfn2", "pfn2" ], "ProductCodes" : [ "pc1", "pc2" ] }] }] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1{ TestRestUriString, std::move(helper) }; Schema::IRestClient::SearchResult searchResponse = v1.Search({}); REQUIRE(searchResponse.Matches.size() == 1); Schema::IRestClient::Package package = searchResponse.Matches.at(0); REQUIRE(package.PackageInformation.PackageIdentifier.compare("git.package") == 0); REQUIRE(package.PackageInformation.Publisher.compare("git") == 0); REQUIRE(package.PackageInformation.PackageName.compare("package") == 0); REQUIRE(package.Versions.size() == 1); REQUIRE(package.Versions.at(0).VersionAndChannel.GetVersion().ToString().compare("1.0.0") == 0); REQUIRE(package.Versions.at(0).PackageFamilyNames.size() == 2); REQUIRE(package.Versions.at(0).PackageFamilyNames.at(0) == "pfn1"); REQUIRE(package.Versions.at(0).PackageFamilyNames.at(1) == "pfn2"); REQUIRE(package.Versions.at(0).ProductCodes.at(0) == "pc1"); REQUIRE(package.Versions.at(0).ProductCodes.at(1) == "pc2"); } TEST_CASE("Search_GoodResponse_404AsEmpty", "[RestSource][Interface_1_0]") { utility::string_t notFoundResponse = _XPLATSTR( R"delimiter({"code":"DataNotFound","data":[],"details":[],"innererror":{"code":"DataNotFound","data":[],"details":[],"message":"Product is not present","source":"StoreEdgeFD"},"message":"Product is not present","source":"StoreEdgeFD"})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound, std::move(notFoundResponse)) }; Interface v1{ TestRestUriString, std::move(helper) }; Schema::IRestClient::SearchResult searchResponse = v1.Search({}); REQUIRE(searchResponse.Matches.size() == 0); } TEST_CASE("Search_ContinuationToken", "[RestSource][Interface_1_0]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [ { "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": [ { "PackageVersion": "1.0.0" }] }, { "PackageIdentifier": "foo.package", "PackageName": "package", "Publisher": "foo", "Versions": [ { "PackageVersion": "1.0.0" }] }], "ContinuationToken" : "abcd-ct=" })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1{ TestRestUriString, std::move(helper) }; SearchRequest request{}; request.MaximumResults = 9; Schema::IRestClient::SearchResult results = v1.Search(request); REQUIRE(results.Matches.size() == request.MaximumResults); SearchRequest requestWithSize1{}; requestWithSize1.MaximumResults = 1; Schema::IRestClient::SearchResult resultsWithSize1 = v1.Search(requestWithSize1); REQUIRE(resultsWithSize1.Matches.size() == requestWithSize1.MaximumResults); } TEST_CASE("Search_BadResponse_NoVersions", "[RestSource][Interface_1_0]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [ { "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": null }] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1{ TestRestUriString, std::move(helper) }; REQUIRE_THROWS_HR(v1.Search({}), APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } TEST_CASE("Search_BadResponse_NotFoundCode", "[RestSource][Interface_1_0]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound) }; Interface v1{ TestRestUriString, std::move(helper) }; REQUIRE_THROWS_HR(v1.Search({}), APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND); } TEST_CASE("Search_Optimized_ManifestResponse", "[RestSource][Interface_1_0]") { utility::string_t sample = GetGoodManifest_RequiredFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; AppInstaller::Repository::SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Id, MatchType::Exact, "Foo" }; request.Filters.emplace_back(std::move(filter)); Interface v1{ TestRestUriString, std::move(helper) }; Schema::IRestClient::SearchResult result = v1.Search(request); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Versions.size() == 1); REQUIRE(result.Matches[0].Versions[0].VersionAndChannel.GetVersion().ToString() == "5.0.0"); REQUIRE(result.Matches[0].Versions[0].VersionAndChannel.GetChannel().ToString() == ""); REQUIRE(result.Matches[0].Versions[0].Manifest); // Verify manifest is populated Manifest manifest = result.Matches[0].Versions[0].Manifest.value(); REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "5.0.0"); REQUIRE(manifest.DefaultLocalization.Locale == "en-us"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar license"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar description"); REQUIRE(manifest.Installers.size() == 1); REQUIRE(manifest.Installers[0].Arch == Architecture::X64); REQUIRE(manifest.Installers[0].Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(manifest.Installers[0].BaseInstallerType == InstallerTypeEnum::Exe); REQUIRE(manifest.Installers[0].Url == "https://installer.example.com/foobar.exe"); } TEST_CASE("Search_Optimized_ManifestResponse_MultipleVersions", "[RestSource][Interface_1_0]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, GetManifestsResponse_MultipleVersions()) }; AppInstaller::Repository::SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Id, MatchType::Exact, "Foo.Bar" }; request.Filters.emplace_back(std::move(filter)); Interface v1{ TestRestUriString, std::move(helper) }; Schema::IRestClient::SearchResult result = v1.Search(request); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Versions.size() == 2); REQUIRE(result.Matches[0].Versions[0].VersionAndChannel.GetVersion().ToString() == "5.0.0"); REQUIRE(result.Matches[0].Versions[0].Manifest); REQUIRE(result.Matches[0].Versions[1].VersionAndChannel.GetVersion().ToString() == "6.0.0"); REQUIRE(result.Matches[0].Versions[1].Manifest); } TEST_CASE("Search_Optimized_NoResponse_NotFoundCode", "[RestSource][Interface_1_0]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound) }; AppInstaller::Repository::SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Id, MatchType::Exact, "Foo" }; request.Filters.emplace_back(std::move(filter)); Interface v1{ TestRestUriString, std::move(helper) }; REQUIRE_THROWS_HR(v1.Search(request), APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND); } TEST_CASE("GetManifests_GoodResponse", "[RestSource][Interface_1_0]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1{ TestRestUriString, std::move(helper) }; std::vector manifests = v1.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated Manifest manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.0.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } TEST_CASE("GetManifests_GoodResponse_404AsEmpty", "[RestSource][Interface_1_0]") { utility::string_t notFoundResponse = _XPLATSTR( R"delimiter({"code":"DataNotFound","data":[],"details":[],"innererror":{"code":"DataNotFound","data":[],"details":[],"message":"Product is not present","source":"StoreEdgeFD"},"message":"Product is not present","source":"StoreEdgeFD"})delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound, std::move(notFoundResponse)) }; Interface v1{ TestRestUriString, std::move(helper) }; std::vector manifests = v1.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 0); } TEST_CASE("GetManifests_GoodResponse_MultipleVersions", "[RestSource][Interface_1_0]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, GetManifestsResponse_MultipleVersions()) }; Interface v1{ TestRestUriString, std::move(helper) }; // GetManifests std::vector manifests = v1.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 2); REQUIRE(manifests[0].Version == "5.0.0"); REQUIRE(manifests[1].Version == "6.0.0"); } TEST_CASE("GetManifests_BadResponse_SuccessCode", "[RestSource][Interface_1_0]") { utility::string_t badManifest = _XPLATSTR( R"delimiter({ "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "5.0.0" } ] } })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(badManifest)) }; Interface v1{ TestRestUriString, std::move(helper) }; REQUIRE_THROWS_HR(v1.GetManifests("Foo.Bar"), APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } TEST_CASE("GetManifests_NotFoundCode", "[RestSource][Interface_1_0]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound) }; Interface v1{ TestRestUriString, std::move(helper) }; REQUIRE_THROWS_HR(v1.GetManifests("Foo.Bar"), APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND); } TEST_CASE("GetManifests_GoodResponse_UnknownInstaller", "[RestSource][Interface_1_0]") { utility::string_t msstoreInstallerResponse = _XPLATSTR( R"delimiter({ "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "5.0.0", "DefaultLocale": { "PackageLocale": "en-us", "Publisher": "Foo", "PackageName": "Bar", "License": "Foo bar license", "ShortDescription": "Foo bar description" }, "Installers": [ { "Architecture": "x64", "InstallerType": "msstore", "MSStoreProductIdentifier": "9nblggh4nns1" } ] } ] } })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(msstoreInstallerResponse)) }; Interface v1{ TestRestUriString, std::move(helper) }; std::vector manifests = v1.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated and manifest validation passed Manifest& manifest = manifests[0]; REQUIRE(manifest.Installers.size() == 1); REQUIRE(manifest.Installers.at(0).BaseInstallerType == InstallerTypeEnum::Unknown); REQUIRE(manifest.Installers.at(0).ProductId.empty()); } TEST_CASE("GetManifestByVersion_GoodResponse_MultipleVersions_VersionFound", "[RestSource][Interface_1_0]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, GetManifestsResponse_MultipleVersions()) }; Interface v1{ TestRestUriString, std::move(helper) }; // GetManifests std::optional manifest = v1.GetManifestByVersion("Foo.Bar", "5.0.0", ""); REQUIRE(manifest.has_value()); REQUIRE(manifest->Version == "5.0.0"); } TEST_CASE("GetManifestByVersion_GoodResponse_MultipleVersions_VersionNotFound", "[RestSource][Interface_1_0]") { HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, GetManifestsResponse_MultipleVersions()) }; Interface v1{ TestRestUriString, std::move(helper) }; // GetManifests std::optional manifest = v1.GetManifestByVersion("Foo.Bar", "7.0.0", ""); REQUIRE_FALSE(manifest.has_value()); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_1; namespace { const std::string TestRestUriString = "http://restsource.com/api"; IRestClient::Information GetTestSourceInformation() { IRestClient::Information result; result.RequiredPackageMatchFields.emplace_back("Market"); result.RequiredQueryParameters.emplace_back("Market"); result.UnsupportedPackageMatchFields.emplace_back("Moniker"); result.UnsupportedQueryParameters.emplace_back("Channel"); return result; } struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker", "ReleaseNotes": "Default release notes", "ReleaseNotesUrl": "https://DefaultReleaseNotes.net", "Agreements": [{ "AgreementLabel": "DefaultLabel", "Agreement": "DefaultText", "AgreementUrl": "https://DefaultAgreementUrl.net" }] }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ], "ReleaseNotes": "Release notes", "ReleaseNotesUrl": "https://ReleaseNotes.net", "Agreements": [{ "AgreementLabel": "Label", "Agreement": "Text", "AgreementUrl": "https://AgreementUrl.net" }] } ], "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.exe", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "msi", "Scope": "user", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "install", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "ProductCode": "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d", "ReleaseDate": "2021-01-01", "InstallerAbortsTerminal": true, "InstallLocationRequired": true, "RequireExplicitUpgrade": true, "UnsupportedOSArchitectures": [ "arm" ], "ElevationRequirement": "elevatesSelf", "AppsAndFeaturesEntries": [{ "DisplayName": "DisplayName", "DisplayVersion": "DisplayVersion", "Publisher": "Publisher", "ProductCode": "ProductCode", "UpgradeCode": "UpgradeCode", "InstallerType": "exe" }], "Markets" : { "AllowedMarkets": [ "US" ] }, "ExpectedReturnCodes": [{ "InstallerReturnCode": 3, "ReturnResponse": "InstallInProgress" }] } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); REQUIRE(frenchLocalization.Get() == "Release notes"); REQUIRE(frenchLocalization.Get() == "https://ReleaseNotes.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Label == "Label"); REQUIRE(frenchLocalization.Get().at(0).AgreementText == "Text"); REQUIRE(frenchLocalization.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); } void VerifyInstallers_AllFields(const Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.exe"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Msi); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 7); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Install); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == ""); REQUIRE(actualInstaller.ProductCode == "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d"); REQUIRE(actualInstaller.ReleaseDate == "2021-01-01"); REQUIRE(actualInstaller.InstallerAbortsTerminal); REQUIRE(actualInstaller.InstallLocationRequired); REQUIRE(actualInstaller.RequireExplicitUpgrade); REQUIRE(actualInstaller.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(actualInstaller.UnsupportedOSArchitectures.size() == 1); REQUIRE(actualInstaller.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(actualInstaller.AppsAndFeaturesEntries.size() == 1); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::InstallInProgress); } }; } TEST_CASE("Search_BadResponse_UnsupportedPackageMatchFields", "[RestSource][Interface_1_1]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [], "UnsupportedPackageMatchFields" : [ "Moniker" ] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {} }; AppInstaller::Repository::SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "Foo" }; request.Filters.emplace_back(std::move(filter)); REQUIRE_THROWS_HR(v1_1.Search(request), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST); } TEST_CASE("Search_BadResponse_RequiredPackageMatchFields", "[RestSource][Interface_1_1]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [], "RequiredPackageMatchFields" : [ "Moniker" ] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {} }; AppInstaller::Repository::SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "Foo" }; request.Filters.emplace_back(std::move(filter)); REQUIRE_THROWS_HR(v1_1.Search(request), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST); } TEST_CASE("GetManifests_BadResponse_UnsupportedQueryParameters", "[RestSource][Interface_1_1]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : null, "UnsupportedQueryParameters" : [ "Channel" ] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {} }; REQUIRE_THROWS_HR(v1_1.GetManifests("Foo"), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST); } TEST_CASE("GetManifests_BadResponse_RequiredQueryParameters", "[RestSource][Interface_1_1]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : null, "RequiredQueryParameters" : [ "Version" ] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {} }; REQUIRE_THROWS_HR(v1_1.GetManifests("Foo"), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST); } TEST_CASE("Search_BadRequest_UnsupportedPackageMatchFields", "[RestSource][Interface_1_1]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [ { "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": [ { "PackageVersion": "1.0.0" }, { "PackageVersion": "2.0.0" }] }] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {} }; AppInstaller::Repository::SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Moniker, MatchType::Exact, "Foo" }; request.Filters.emplace_back(std::move(filter)); REQUIRE_THROWS_HR(v1_1.Search(request), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST); } TEST_CASE("Search_GoodRequest_OnlyMarketRequired", "[RestSource][Interface_1_1]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [ { "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": [ { "PackageVersion": "1.0.0" }, { "PackageVersion": "2.0.0" }] }] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {} }; AppInstaller::Repository::SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "Foo" }; request.Filters.emplace_back(std::move(filter)); Schema::IRestClient::SearchResult searchResponse = v1_1.Search(request); REQUIRE(searchResponse.Matches.size() == 1); Schema::IRestClient::Package package = searchResponse.Matches.at(0); REQUIRE(package.PackageInformation.PackageIdentifier.compare("git.package") == 0); REQUIRE(package.PackageInformation.Publisher.compare("git") == 0); REQUIRE(package.PackageInformation.PackageName.compare("package") == 0); REQUIRE(package.Versions.size() == 2); REQUIRE(package.Versions.at(0).VersionAndChannel.GetVersion().ToString().compare("1.0.0") == 0); REQUIRE(package.Versions.at(1).VersionAndChannel.GetVersion().ToString().compare("2.0.0") == 0); } TEST_CASE("GetManifests_BadRequest_UnsupportedQueryParameters", "[RestSource][Interface_1_1]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "5.0.0", "DefaultLocale": { "PackageLocale": "en-us", "Publisher": "Foo", "PackageName": "Bar", "License": "Foo bar license", "ShortDescription": "Foo bar description" }, "Installers": [ { "Architecture": "x64", "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerType": "exe", "InstallerUrl": "https://installer.example.com/foobar.exe" } ] } ] } })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {} }; REQUIRE_THROWS_HR(v1_1.GetManifestByVersion("Foo", "1.0", "beta"), APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST); } TEST_CASE("GetManifests_GoodRequest_OnlyMarketRequired", "[RestSource][Interface_1_1]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "5.0.0", "DefaultLocale": { "PackageLocale": "en-us", "Publisher": "Foo", "PackageName": "Bar", "License": "Foo bar license", "ShortDescription": "Foo bar description" }, "Installers": [ { "Architecture": "x64", "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerType": "exe", "InstallerUrl": "https://installer.example.com/foobar.exe" } ] } ] } })delimiter"); IRestClient::Information info = GetTestSourceInformation(); info.UnsupportedQueryParameters.clear(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), info, {} }; auto manifestResult = v1_1.GetManifestByVersion("Foo", "5.0.0", ""); REQUIRE(manifestResult.has_value()); const Manifest& manifest = manifestResult.value(); REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "5.0.0"); REQUIRE(manifest.DefaultLocalization.Locale == "en-us"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar license"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar description"); REQUIRE(manifest.Installers.size() == 1); REQUIRE(manifest.Installers[0].Arch == Architecture::X64); REQUIRE(manifest.Installers[0].Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(manifest.Installers[0].BaseInstallerType == InstallerTypeEnum::Exe); REQUIRE(manifest.Installers[0].Url == "https://installer.example.com/foobar.exe"); } TEST_CASE("GetManifests_GoodResponse_MSStoreType", "[RestSource][Interface_1_1]") { utility::string_t msstoreInstallerResponse = _XPLATSTR( R"delimiter({ "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "5.0.0", "DefaultLocale": { "PackageLocale": "en-us", "Publisher": "Foo", "PackageName": "Bar", "License": "Foo bar license", "ShortDescription": "Foo bar description" }, "Installers": [ { "Architecture": "x64", "InstallerType": "msstore", "MSStoreProductIdentifier": "9nblggh4nns1" } ] } ] } })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(msstoreInstallerResponse)) }; Interface v1_1{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {} }; std::vector manifests = v1_1.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated and manifest validation passed Manifest manifest = manifests[0]; REQUIRE(manifest.Installers.size() == 1); REQUIRE(manifest.Installers.at(0).BaseInstallerType == InstallerTypeEnum::MSStore); REQUIRE(manifest.Installers.at(0).ProductId == "9nblggh4nns1"); } TEST_CASE("GetManifests_GoodResponse_V1_1", "[RestSource][Interface_1_1]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_1{ TestRestUriString, std::move(helper), {} }; std::vector manifests = v1_1.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated Manifest& manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.1.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_10.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_10; namespace { const std::string TestRestUriString = "http://restsource.com/api"; struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker", "ReleaseNotes": "Default release notes", "ReleaseNotesUrl": "https://DefaultReleaseNotes.net", "Agreements": [{ "AgreementLabel": "DefaultLabel", "Agreement": "DefaultText", "AgreementUrl": "https://DefaultAgreementUrl.net" }], "PurchaseUrl": "http://DefaultPurchaseUrl.net", "InstallationNotes": "Default Installation Notes", "Documentations": [{ "DocumentLabel": "Default Document Label", "DocumentUrl": "http://DefaultDocumentUrl.net" }], "Icons": [{ "IconUrl": "https://DefaultTestIcon", "IconFileType": "ico", "IconResolution": "custom", "IconTheme": "default", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123" }] }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ], "ReleaseNotes": "Release notes", "ReleaseNotesUrl": "https://ReleaseNotes.net", "Agreements": [{ "AgreementLabel": "Label", "Agreement": "Text", "AgreementUrl": "https://AgreementUrl.net" }], "PurchaseUrl": "http://purchaseUrl.net", "InstallationNotes": "Installation Notes", "Documentations": [{ "DocumentLabel": "Document Label", "DocumentUrl": "http://documentUrl.net" }], "Icons": [{ "IconUrl": "https://testIcon", "IconFileType": "png", "IconResolution": "32x32", "IconTheme": "light", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321" }] } ],)delimiter") _XPLATSTR(R"delimiter( "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.zip", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "zip", "Scope": "user", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom", "Repair": "/repair" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "deny", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "ProductCode": "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d", "ReleaseDate": "2021-01-01", "InstallerAbortsTerminal": true, "InstallLocationRequired": true, "RequireExplicitUpgrade": true, "UnsupportedOSArchitectures": [ "arm" ], "ElevationRequirement": "elevatesSelf", "AppsAndFeaturesEntries": [{ "DisplayName": "DisplayName", "DisplayVersion": "DisplayVersion", "Publisher": "Publisher", "ProductCode": "ProductCode", "UpgradeCode": "UpgradeCode", "InstallerType": "exe" }], "Markets" : { "AllowedMarkets": [ "US" ] }, "ExpectedReturnCodes": [{ "InstallerReturnCode": 3, "ReturnResponse": "custom", "ReturnResponseUrl": "http://returnResponseUrl.net" }], "NestedInstallerType": "portable", "DisplayInstallWarnings": true, "UnsupportedArguments": [ "log" ], "NestedInstallerFiles": [{ "RelativeFilePath": "test\\app.exe", "PortableCommandAlias": "test.exe" }], "InstallationMetadata": { "DefaultInstallLocation": "%TEMP%\\DefaultInstallLocation", "Files": [{ "RelativeFilePath": "test\\app.exe", "FileSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "FileType": "launch", "InvocationParameter": "/parameter", "DisplayName": "test" }] }, "DownloadCommandProhibited": true, "RepairBehavior": "uninstaller", "ArchiveBinariesDependOnPath": true, "Authentication": { "AuthenticationType": "microsoftEntraId", "MicrosoftEntraIdAuthenticationInfo" : { "Resource": "TestResource", "Scope" : "TestScope" } } } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const AppInstaller::Manifest::Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://DefaultPurchaseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Default Installation Notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default Document Label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "http://DefaultDocumentUrl.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://DefaultTestIcon"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); REQUIRE(frenchLocalization.Get() == "Release notes"); REQUIRE(frenchLocalization.Get() == "https://ReleaseNotes.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Label == "Label"); REQUIRE(frenchLocalization.Get().at(0).AgreementText == "Text"); REQUIRE(frenchLocalization.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); REQUIRE(frenchLocalization.Get() == "http://purchaseUrl.net"); REQUIRE(frenchLocalization.Get() == "Installation Notes"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).DocumentLabel == "Document Label"); REQUIRE(frenchLocalization.Get().at(0).DocumentUrl == "http://documentUrl.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Url == "https://testIcon"); REQUIRE(frenchLocalization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(frenchLocalization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(frenchLocalization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(frenchLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); } void VerifyInstallers_AllFields(const AppInstaller::Manifest::Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.zip"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 8); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Repair) == "/repair"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Deny); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == ""); REQUIRE(actualInstaller.ProductCode == "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d"); REQUIRE(actualInstaller.ReleaseDate == "2021-01-01"); REQUIRE(actualInstaller.InstallerAbortsTerminal); REQUIRE(actualInstaller.InstallLocationRequired); REQUIRE(actualInstaller.RequireExplicitUpgrade); REQUIRE(actualInstaller.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(actualInstaller.UnsupportedOSArchitectures.size() == 1); REQUIRE(actualInstaller.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(actualInstaller.AppsAndFeaturesEntries.size() == 1); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseUrl == "http://returnResponseUrl.net"); REQUIRE(actualInstaller.NestedInstallerType == InstallerTypeEnum::Portable); REQUIRE(actualInstaller.DisplayInstallWarnings); REQUIRE(actualInstaller.UnsupportedArguments.size() == 1); REQUIRE(actualInstaller.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); REQUIRE(actualInstaller.NestedInstallerFiles.size() == 1); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).PortableCommandAlias == "test.exe"); REQUIRE(actualInstaller.InstallationMetadata.DefaultInstallLocation == "%TEMP%\\DefaultInstallLocation"); REQUIRE(actualInstaller.InstallationMetadata.Files.size() == 1); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).InvocationParameter == "/parameter"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).DisplayName == "test"); REQUIRE(actualInstaller.DownloadCommandProhibited); REQUIRE(actualInstaller.RepairBehavior == RepairBehaviorEnum::Uninstaller); REQUIRE(actualInstaller.ArchiveBinariesDependOnPath); REQUIRE(actualInstaller.AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraId); REQUIRE(actualInstaller.AuthInfo.MicrosoftEntraIdInfo.has_value()); REQUIRE(actualInstaller.AuthInfo.MicrosoftEntraIdInfo->Resource == "TestResource"); REQUIRE(actualInstaller.AuthInfo.MicrosoftEntraIdInfo->Scope == "TestScope"); } }; } TEST_CASE("GetManifests_GoodResponse_V1_10", "[RestSource][Interface_1_10]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_10{ TestRestUriString, std::move(helper), {} }; std::vector manifests = v1_10.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated Manifest& manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.10.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_12.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_12; namespace { const std::string TestRestUriString = "http://restsource.com/api"; struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker", "ReleaseNotes": "Default release notes", "ReleaseNotesUrl": "https://DefaultReleaseNotes.net", "Agreements": [{ "AgreementLabel": "DefaultLabel", "Agreement": "DefaultText", "AgreementUrl": "https://DefaultAgreementUrl.net" }], "PurchaseUrl": "http://DefaultPurchaseUrl.net", "InstallationNotes": "Default Installation Notes", "Documentations": [{ "DocumentLabel": "Default Document Label", "DocumentUrl": "http://DefaultDocumentUrl.net" }], "Icons": [{ "IconUrl": "https://DefaultTestIcon", "IconFileType": "ico", "IconResolution": "custom", "IconTheme": "default", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123" }] }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ], "ReleaseNotes": "Release notes", "ReleaseNotesUrl": "https://ReleaseNotes.net", "Agreements": [{ "AgreementLabel": "Label", "Agreement": "Text", "AgreementUrl": "https://AgreementUrl.net" }], "PurchaseUrl": "http://purchaseUrl.net", "InstallationNotes": "Installation Notes", "Documentations": [{ "DocumentLabel": "Document Label", "DocumentUrl": "http://documentUrl.net" }], "Icons": [{ "IconUrl": "https://testIcon", "IconFileType": "png", "IconResolution": "32x32", "IconTheme": "light", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321" }] } ],)delimiter") _XPLATSTR(R"delimiter( "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.zip", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "zip", "Scope": "user", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom", "Repair": "/repair" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "deny", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "ProductCode": "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d", "ReleaseDate": "2021-01-01", "InstallerAbortsTerminal": true, "InstallLocationRequired": true, "RequireExplicitUpgrade": true, "UnsupportedOSArchitectures": [ "arm" ], "ElevationRequirement": "elevatesSelf", "AppsAndFeaturesEntries": [{ "DisplayName": "DisplayName", "DisplayVersion": "DisplayVersion", "Publisher": "Publisher", "ProductCode": "ProductCode", "UpgradeCode": "UpgradeCode", "InstallerType": "exe" }], "Markets" : { "AllowedMarkets": [ "US" ] }, "ExpectedReturnCodes": [{ "InstallerReturnCode": 3, "ReturnResponse": "custom", "ReturnResponseUrl": "http://returnResponseUrl.net" }], "NestedInstallerType": "portable", "DisplayInstallWarnings": true, "UnsupportedArguments": [ "log" ], "NestedInstallerFiles": [{ "RelativeFilePath": "test\\app.exe", "PortableCommandAlias": "test.exe" }], "InstallationMetadata": { "DefaultInstallLocation": "%TEMP%\\DefaultInstallLocation", "Files": [{ "RelativeFilePath": "test\\app.exe", "FileSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "FileType": "launch", "InvocationParameter": "/parameter", "DisplayName": "test" }] }, "DownloadCommandProhibited": true, "RepairBehavior": "uninstaller", "ArchiveBinariesDependOnPath": true, "Authentication": { "AuthenticationType": "microsoftEntraId", "MicrosoftEntraIdAuthenticationInfo" : { "Resource": "TestResource", "Scope" : "TestScope" } } } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const AppInstaller::Manifest::Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://DefaultPurchaseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Default Installation Notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default Document Label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "http://DefaultDocumentUrl.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://DefaultTestIcon"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); REQUIRE(frenchLocalization.Get() == "Release notes"); REQUIRE(frenchLocalization.Get() == "https://ReleaseNotes.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Label == "Label"); REQUIRE(frenchLocalization.Get().at(0).AgreementText == "Text"); REQUIRE(frenchLocalization.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); REQUIRE(frenchLocalization.Get() == "http://purchaseUrl.net"); REQUIRE(frenchLocalization.Get() == "Installation Notes"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).DocumentLabel == "Document Label"); REQUIRE(frenchLocalization.Get().at(0).DocumentUrl == "http://documentUrl.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Url == "https://testIcon"); REQUIRE(frenchLocalization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(frenchLocalization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(frenchLocalization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(frenchLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); } void VerifyInstallers_AllFields(const AppInstaller::Manifest::Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.zip"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 8); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Repair) == "/repair"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Deny); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == ""); REQUIRE(actualInstaller.ProductCode == "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d"); REQUIRE(actualInstaller.ReleaseDate == "2021-01-01"); REQUIRE(actualInstaller.InstallerAbortsTerminal); REQUIRE(actualInstaller.InstallLocationRequired); REQUIRE(actualInstaller.RequireExplicitUpgrade); REQUIRE(actualInstaller.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(actualInstaller.UnsupportedOSArchitectures.size() == 1); REQUIRE(actualInstaller.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(actualInstaller.AppsAndFeaturesEntries.size() == 1); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseUrl == "http://returnResponseUrl.net"); REQUIRE(actualInstaller.NestedInstallerType == InstallerTypeEnum::Portable); REQUIRE(actualInstaller.DisplayInstallWarnings); REQUIRE(actualInstaller.UnsupportedArguments.size() == 1); REQUIRE(actualInstaller.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); REQUIRE(actualInstaller.NestedInstallerFiles.size() == 1); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).PortableCommandAlias == "test.exe"); REQUIRE(actualInstaller.InstallationMetadata.DefaultInstallLocation == "%TEMP%\\DefaultInstallLocation"); REQUIRE(actualInstaller.InstallationMetadata.Files.size() == 1); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).InvocationParameter == "/parameter"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).DisplayName == "test"); REQUIRE(actualInstaller.DownloadCommandProhibited); REQUIRE(actualInstaller.RepairBehavior == RepairBehaviorEnum::Uninstaller); REQUIRE(actualInstaller.ArchiveBinariesDependOnPath); REQUIRE(actualInstaller.AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraId); REQUIRE(actualInstaller.AuthInfo.MicrosoftEntraIdInfo.has_value()); REQUIRE(actualInstaller.AuthInfo.MicrosoftEntraIdInfo->Resource == "TestResource"); REQUIRE(actualInstaller.AuthInfo.MicrosoftEntraIdInfo->Scope == "TestScope"); } }; } TEST_CASE("GetManifests_GoodResponse_V1_12", "[RestSource][Interface_1_12]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_12{ TestRestUriString, std::move(helper), {} }; std::vector manifests = v1_12.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated Manifest& manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.12.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_4.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_4; namespace { const std::string TestRestUriString = "http://restsource.com/api"; IRestClient::Information GetTestSourceInformation() { IRestClient::Information result; result.RequiredPackageMatchFields.emplace_back("Market"); result.RequiredQueryParameters.emplace_back("Market"); result.UnsupportedPackageMatchFields.emplace_back("Moniker"); result.UnsupportedQueryParameters.emplace_back("Channel"); return result; } struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker", "ReleaseNotes": "Default release notes", "ReleaseNotesUrl": "https://DefaultReleaseNotes.net", "Agreements": [{ "AgreementLabel": "DefaultLabel", "Agreement": "DefaultText", "AgreementUrl": "https://DefaultAgreementUrl.net" }], "PurchaseUrl": "http://DefaultPurchaseUrl.net", "InstallationNotes": "Default Installation Notes", "Documentations": [{ "DocumentLabel": "Default Document Label", "DocumentUrl": "http://DefaultDocumentUrl.net" }] }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ], "ReleaseNotes": "Release notes", "ReleaseNotesUrl": "https://ReleaseNotes.net", "Agreements": [{ "AgreementLabel": "Label", "Agreement": "Text", "AgreementUrl": "https://AgreementUrl.net" }], "PurchaseUrl": "http://purchaseUrl.net", "InstallationNotes": "Installation Notes", "Documentations": [{ "DocumentLabel": "Document Label", "DocumentUrl": "http://documentUrl.net" }] } ], "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.zip", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "zip", "Scope": "user", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "install", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "ProductCode": "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d", "ReleaseDate": "2021-01-01", "InstallerAbortsTerminal": true, "InstallLocationRequired": true, "RequireExplicitUpgrade": true, "UnsupportedOSArchitectures": [ "arm" ], "ElevationRequirement": "elevatesSelf", "AppsAndFeaturesEntries": [{ "DisplayName": "DisplayName", "DisplayVersion": "DisplayVersion", "Publisher": "Publisher", "ProductCode": "ProductCode", "UpgradeCode": "UpgradeCode", "InstallerType": "exe" }], "Markets" : { "AllowedMarkets": [ "US" ] }, "ExpectedReturnCodes": [{ "InstallerReturnCode": 3, "ReturnResponse": "custom", "ReturnResponseUrl": "http://returnResponseUrl.net" }], "NestedInstallerType": "portable", "DisplayInstallWarnings": true, "UnsupportedArguments": [ "log" ], "NestedInstallerFiles": [{ "RelativeFilePath": "test\\app.exe", "PortableCommandAlias": "test.exe" }], "InstallationMetadata": { "DefaultInstallLocation": "%TEMP%\\DefaultInstallLocation", "Files": [{ "RelativeFilePath": "test\\app.exe", "FileSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "FileType": "launch", "InvocationParameter": "/parameter", "DisplayName": "test" }] } } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://DefaultPurchaseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Default Installation Notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default Document Label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "http://DefaultDocumentUrl.net"); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); REQUIRE(frenchLocalization.Get() == "Release notes"); REQUIRE(frenchLocalization.Get() == "https://ReleaseNotes.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Label == "Label"); REQUIRE(frenchLocalization.Get().at(0).AgreementText == "Text"); REQUIRE(frenchLocalization.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); REQUIRE(frenchLocalization.Get() == "http://purchaseUrl.net"); REQUIRE(frenchLocalization.Get() == "Installation Notes"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).DocumentLabel == "Document Label"); REQUIRE(frenchLocalization.Get().at(0).DocumentUrl == "http://documentUrl.net"); } void VerifyInstallers_AllFields(const Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.zip"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 7); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Install); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == ""); REQUIRE(actualInstaller.ProductCode == "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d"); REQUIRE(actualInstaller.ReleaseDate == "2021-01-01"); REQUIRE(actualInstaller.InstallerAbortsTerminal); REQUIRE(actualInstaller.InstallLocationRequired); REQUIRE(actualInstaller.RequireExplicitUpgrade); REQUIRE(actualInstaller.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(actualInstaller.UnsupportedOSArchitectures.size() == 1); REQUIRE(actualInstaller.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(actualInstaller.AppsAndFeaturesEntries.size() == 1); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseUrl == "http://returnResponseUrl.net"); REQUIRE(actualInstaller.NestedInstallerType == InstallerTypeEnum::Portable); REQUIRE(actualInstaller.DisplayInstallWarnings); REQUIRE(actualInstaller.UnsupportedArguments.size() == 1); REQUIRE(actualInstaller.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); REQUIRE(actualInstaller.NestedInstallerFiles.size() == 1); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).PortableCommandAlias == "test.exe"); REQUIRE(actualInstaller.InstallationMetadata.DefaultInstallLocation == "%TEMP%\\DefaultInstallLocation"); REQUIRE(actualInstaller.InstallationMetadata.Files.size() == 1); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).InvocationParameter == "/parameter"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).DisplayName == "test"); } }; } TEST_CASE("GetManifests_GoodResponse_V1_4", "[RestSource][Interface_1_4]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_4{ TestRestUriString, std::move(helper), {} }; std::vector manifests = v1_4.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated Manifest& manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.4.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } TEST_CASE("Search_GoodResponse_V1_4", "[RestSource][Interface_1_4]") { utility::string_t sample = _XPLATSTR( R"delimiter({ "Data" : [{ "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": [{ "PackageVersion": "1.0.0", "PackageFamilyNames": [ "pfn1" ], "ProductCodes": [ "pc1" ], "UpgradeCodes": [ "upgradeCode" ], "AppsAndFeaturesEntryVersions": [ "2.0", "1.0" ] }] }] })delimiter"); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_4{ TestRestUriString, std::move(helper), {} }; Schema::IRestClient::SearchResult searchResponse = v1_4.Search({}); REQUIRE(searchResponse.Matches.size() == 1); Schema::IRestClient::Package package = searchResponse.Matches.at(0); REQUIRE(package.PackageInformation.PackageIdentifier.compare("git.package") == 0); REQUIRE(package.PackageInformation.Publisher.compare("git") == 0); REQUIRE(package.PackageInformation.PackageName.compare("package") == 0); REQUIRE(package.Versions.size() == 1); REQUIRE(package.Versions.at(0).VersionAndChannel.GetVersion().ToString().compare("1.0.0") == 0); REQUIRE(package.Versions.at(0).PackageFamilyNames.size() == 1); REQUIRE(package.Versions.at(0).PackageFamilyNames.at(0) == "pfn1"); REQUIRE(package.Versions.at(0).ProductCodes.size() == 1); REQUIRE(package.Versions.at(0).ProductCodes.at(0) == "pc1"); REQUIRE(package.Versions.at(0).UpgradeCodes.size() == 1); REQUIRE(package.Versions.at(0).UpgradeCodes.at(0) == "upgradeCode"); REQUIRE(package.Versions.at(0).ArpVersions.size() == 2); REQUIRE(package.Versions.at(0).ArpVersions.at(0).ToString() == "1.0"); REQUIRE(package.Versions.at(0).ArpVersions.at(1).ToString() == "2.0"); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_5.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_5; namespace { const std::string TestRestUriString = "http://restsource.com/api"; IRestClient::Information GetTestSourceInformation() { IRestClient::Information result; result.RequiredPackageMatchFields.emplace_back("Market"); result.RequiredQueryParameters.emplace_back("Market"); result.UnsupportedPackageMatchFields.emplace_back("Moniker"); result.UnsupportedQueryParameters.emplace_back("Channel"); return result; } struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker", "ReleaseNotes": "Default release notes", "ReleaseNotesUrl": "https://DefaultReleaseNotes.net", "Agreements": [{ "AgreementLabel": "DefaultLabel", "Agreement": "DefaultText", "AgreementUrl": "https://DefaultAgreementUrl.net" }], "PurchaseUrl": "http://DefaultPurchaseUrl.net", "InstallationNotes": "Default Installation Notes", "Documentations": [{ "DocumentLabel": "Default Document Label", "DocumentUrl": "http://DefaultDocumentUrl.net" }], "Icons": [{ "IconUrl": "https://DefaultTestIcon", "IconFileType": "ico", "IconResolution": "custom", "IconTheme": "default", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123" }] }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ], "ReleaseNotes": "Release notes", "ReleaseNotesUrl": "https://ReleaseNotes.net", "Agreements": [{ "AgreementLabel": "Label", "Agreement": "Text", "AgreementUrl": "https://AgreementUrl.net" }], "PurchaseUrl": "http://purchaseUrl.net", "InstallationNotes": "Installation Notes", "Documentations": [{ "DocumentLabel": "Document Label", "DocumentUrl": "http://documentUrl.net" }], "Icons": [{ "IconUrl": "https://testIcon", "IconFileType": "png", "IconResolution": "32x32", "IconTheme": "light", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321" }] } ],)delimiter") _XPLATSTR(R"delimiter( "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.zip", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "zip", "Scope": "user", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "install", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "ProductCode": "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d", "ReleaseDate": "2021-01-01", "InstallerAbortsTerminal": true, "InstallLocationRequired": true, "RequireExplicitUpgrade": true, "UnsupportedOSArchitectures": [ "arm" ], "ElevationRequirement": "elevatesSelf", "AppsAndFeaturesEntries": [{ "DisplayName": "DisplayName", "DisplayVersion": "DisplayVersion", "Publisher": "Publisher", "ProductCode": "ProductCode", "UpgradeCode": "UpgradeCode", "InstallerType": "exe" }], "Markets" : { "AllowedMarkets": [ "US" ] }, "ExpectedReturnCodes": [{ "InstallerReturnCode": 3, "ReturnResponse": "custom", "ReturnResponseUrl": "http://returnResponseUrl.net" }], "NestedInstallerType": "portable", "DisplayInstallWarnings": true, "UnsupportedArguments": [ "log" ], "NestedInstallerFiles": [{ "RelativeFilePath": "test\\app.exe", "PortableCommandAlias": "test.exe" }], "InstallationMetadata": { "DefaultInstallLocation": "%TEMP%\\DefaultInstallLocation", "Files": [{ "RelativeFilePath": "test\\app.exe", "FileSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "FileType": "launch", "InvocationParameter": "/parameter", "DisplayName": "test" }] } } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://DefaultPurchaseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Default Installation Notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default Document Label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "http://DefaultDocumentUrl.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://DefaultTestIcon"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); REQUIRE(frenchLocalization.Get() == "Release notes"); REQUIRE(frenchLocalization.Get() == "https://ReleaseNotes.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Label == "Label"); REQUIRE(frenchLocalization.Get().at(0).AgreementText == "Text"); REQUIRE(frenchLocalization.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); REQUIRE(frenchLocalization.Get() == "http://purchaseUrl.net"); REQUIRE(frenchLocalization.Get() == "Installation Notes"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).DocumentLabel == "Document Label"); REQUIRE(frenchLocalization.Get().at(0).DocumentUrl == "http://documentUrl.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Url == "https://testIcon"); REQUIRE(frenchLocalization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(frenchLocalization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(frenchLocalization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(frenchLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); } void VerifyInstallers_AllFields(const Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.zip"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 7); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Install); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == ""); REQUIRE(actualInstaller.ProductCode == "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d"); REQUIRE(actualInstaller.ReleaseDate == "2021-01-01"); REQUIRE(actualInstaller.InstallerAbortsTerminal); REQUIRE(actualInstaller.InstallLocationRequired); REQUIRE(actualInstaller.RequireExplicitUpgrade); REQUIRE(actualInstaller.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(actualInstaller.UnsupportedOSArchitectures.size() == 1); REQUIRE(actualInstaller.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(actualInstaller.AppsAndFeaturesEntries.size() == 1); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseUrl == "http://returnResponseUrl.net"); REQUIRE(actualInstaller.NestedInstallerType == InstallerTypeEnum::Portable); REQUIRE(actualInstaller.DisplayInstallWarnings); REQUIRE(actualInstaller.UnsupportedArguments.size() == 1); REQUIRE(actualInstaller.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); REQUIRE(actualInstaller.NestedInstallerFiles.size() == 1); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).PortableCommandAlias == "test.exe"); REQUIRE(actualInstaller.InstallationMetadata.DefaultInstallLocation == "%TEMP%\\DefaultInstallLocation"); REQUIRE(actualInstaller.InstallationMetadata.Files.size() == 1); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).InvocationParameter == "/parameter"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).DisplayName == "test"); } }; } TEST_CASE("GetManifests_GoodResponse_V1_5", "[RestSource][Interface_1_5]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_5{ TestRestUriString, std::move(helper), {} }; std::vector manifests = v1_5.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated Manifest& manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.5.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_6.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_6; namespace { const std::string TestRestUriString = "http://restsource.com/api"; IRestClient::Information GetTestSourceInformation() { IRestClient::Information result; result.RequiredPackageMatchFields.emplace_back("Market"); result.RequiredQueryParameters.emplace_back("Market"); result.UnsupportedPackageMatchFields.emplace_back("Moniker"); result.UnsupportedQueryParameters.emplace_back("Channel"); return result; } struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker", "ReleaseNotes": "Default release notes", "ReleaseNotesUrl": "https://DefaultReleaseNotes.net", "Agreements": [{ "AgreementLabel": "DefaultLabel", "Agreement": "DefaultText", "AgreementUrl": "https://DefaultAgreementUrl.net" }], "PurchaseUrl": "http://DefaultPurchaseUrl.net", "InstallationNotes": "Default Installation Notes", "Documentations": [{ "DocumentLabel": "Default Document Label", "DocumentUrl": "http://DefaultDocumentUrl.net" }], "Icons": [{ "IconUrl": "https://DefaultTestIcon", "IconFileType": "ico", "IconResolution": "custom", "IconTheme": "default", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123" }] }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ], "ReleaseNotes": "Release notes", "ReleaseNotesUrl": "https://ReleaseNotes.net", "Agreements": [{ "AgreementLabel": "Label", "Agreement": "Text", "AgreementUrl": "https://AgreementUrl.net" }], "PurchaseUrl": "http://purchaseUrl.net", "InstallationNotes": "Installation Notes", "Documentations": [{ "DocumentLabel": "Document Label", "DocumentUrl": "http://documentUrl.net" }], "Icons": [{ "IconUrl": "https://testIcon", "IconFileType": "png", "IconResolution": "32x32", "IconTheme": "light", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321" }] } ],)delimiter") _XPLATSTR(R"delimiter( "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.zip", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "zip", "Scope": "user", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "deny", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "ProductCode": "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d", "ReleaseDate": "2021-01-01", "InstallerAbortsTerminal": true, "InstallLocationRequired": true, "RequireExplicitUpgrade": true, "UnsupportedOSArchitectures": [ "arm" ], "ElevationRequirement": "elevatesSelf", "AppsAndFeaturesEntries": [{ "DisplayName": "DisplayName", "DisplayVersion": "DisplayVersion", "Publisher": "Publisher", "ProductCode": "ProductCode", "UpgradeCode": "UpgradeCode", "InstallerType": "exe" }], "Markets" : { "AllowedMarkets": [ "US" ] }, "ExpectedReturnCodes": [{ "InstallerReturnCode": 3, "ReturnResponse": "custom", "ReturnResponseUrl": "http://returnResponseUrl.net" }], "NestedInstallerType": "portable", "DisplayInstallWarnings": true, "UnsupportedArguments": [ "log" ], "NestedInstallerFiles": [{ "RelativeFilePath": "test\\app.exe", "PortableCommandAlias": "test.exe" }], "InstallationMetadata": { "DefaultInstallLocation": "%TEMP%\\DefaultInstallLocation", "Files": [{ "RelativeFilePath": "test\\app.exe", "FileSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "FileType": "launch", "InvocationParameter": "/parameter", "DisplayName": "test" }] }, "DownloadCommandProhibited": true } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://DefaultPurchaseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Default Installation Notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default Document Label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "http://DefaultDocumentUrl.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://DefaultTestIcon"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); REQUIRE(frenchLocalization.Get() == "Release notes"); REQUIRE(frenchLocalization.Get() == "https://ReleaseNotes.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Label == "Label"); REQUIRE(frenchLocalization.Get().at(0).AgreementText == "Text"); REQUIRE(frenchLocalization.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); REQUIRE(frenchLocalization.Get() == "http://purchaseUrl.net"); REQUIRE(frenchLocalization.Get() == "Installation Notes"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).DocumentLabel == "Document Label"); REQUIRE(frenchLocalization.Get().at(0).DocumentUrl == "http://documentUrl.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Url == "https://testIcon"); REQUIRE(frenchLocalization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(frenchLocalization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(frenchLocalization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(frenchLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); } void VerifyInstallers_AllFields(const Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.zip"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 7); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Deny); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == ""); REQUIRE(actualInstaller.ProductCode == "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d"); REQUIRE(actualInstaller.ReleaseDate == "2021-01-01"); REQUIRE(actualInstaller.InstallerAbortsTerminal); REQUIRE(actualInstaller.InstallLocationRequired); REQUIRE(actualInstaller.RequireExplicitUpgrade); REQUIRE(actualInstaller.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(actualInstaller.UnsupportedOSArchitectures.size() == 1); REQUIRE(actualInstaller.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(actualInstaller.AppsAndFeaturesEntries.size() == 1); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseUrl == "http://returnResponseUrl.net"); REQUIRE(actualInstaller.NestedInstallerType == InstallerTypeEnum::Portable); REQUIRE(actualInstaller.DisplayInstallWarnings); REQUIRE(actualInstaller.UnsupportedArguments.size() == 1); REQUIRE(actualInstaller.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); REQUIRE(actualInstaller.NestedInstallerFiles.size() == 1); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).PortableCommandAlias == "test.exe"); REQUIRE(actualInstaller.InstallationMetadata.DefaultInstallLocation == "%TEMP%\\DefaultInstallLocation"); REQUIRE(actualInstaller.InstallationMetadata.Files.size() == 1); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).InvocationParameter == "/parameter"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).DisplayName == "test"); REQUIRE(actualInstaller.DownloadCommandProhibited); } }; } TEST_CASE("GetManifests_GoodResponse_V1_6", "[RestSource][Interface_1_6]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_6{ TestRestUriString, std::move(helper), {} }; std::vector manifests = v1_6.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated Manifest& manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.6.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_7.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestHooks.h" #include "TestRestRequestHandler.h" #include #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Authentication; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_7; namespace { const std::string TestRestUriString = "http://restsource.com/api"; IRestClient::Information GetTestSourceInformation() { IRestClient::Information result; result.Authentication.Type = AuthenticationType::MicrosoftEntraId; MicrosoftEntraIdAuthenticationInfo microsoftEntraIdInfo; microsoftEntraIdInfo.Resource = "GUID"; result.Authentication.MicrosoftEntraIdInfo = std::move(microsoftEntraIdInfo); return result; } AuthenticationArguments GetTestAuthenticationArguments() { AuthenticationArguments result; result.Mode = AuthenticationMode::Silent; return result; } utility::string_t SampleSearchResponse = _XPLATSTR( R"delimiter({ "Data" : [ { "PackageIdentifier": "git.package", "PackageName": "package", "Publisher": "git", "Versions": [ { "PackageVersion": "1.0.0" }] }] })delimiter"); utility::string_t SampleGetManifestResponse = _XPLATSTR( R"delimiter({ "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "5.0.0", "DefaultLocale": { "PackageLocale": "en-us", "Publisher": "Foo", "PackageName": "Bar", "License": "Foo bar license", "ShortDescription": "Foo bar description" }, "Installers": [ { "Architecture": "x64", "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerType": "exe", "InstallerUrl": "https://installer.example.com/foobar.exe" } ] } ] } })delimiter"); struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker", "ReleaseNotes": "Default release notes", "ReleaseNotesUrl": "https://DefaultReleaseNotes.net", "Agreements": [{ "AgreementLabel": "DefaultLabel", "Agreement": "DefaultText", "AgreementUrl": "https://DefaultAgreementUrl.net" }], "PurchaseUrl": "http://DefaultPurchaseUrl.net", "InstallationNotes": "Default Installation Notes", "Documentations": [{ "DocumentLabel": "Default Document Label", "DocumentUrl": "http://DefaultDocumentUrl.net" }], "Icons": [{ "IconUrl": "https://DefaultTestIcon", "IconFileType": "ico", "IconResolution": "custom", "IconTheme": "default", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123" }] }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ], "ReleaseNotes": "Release notes", "ReleaseNotesUrl": "https://ReleaseNotes.net", "Agreements": [{ "AgreementLabel": "Label", "Agreement": "Text", "AgreementUrl": "https://AgreementUrl.net" }], "PurchaseUrl": "http://purchaseUrl.net", "InstallationNotes": "Installation Notes", "Documentations": [{ "DocumentLabel": "Document Label", "DocumentUrl": "http://documentUrl.net" }], "Icons": [{ "IconUrl": "https://testIcon", "IconFileType": "png", "IconResolution": "32x32", "IconTheme": "light", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321" }] } ],)delimiter") _XPLATSTR(R"delimiter( "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.zip", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "zip", "Scope": "user", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom", "Repair": "/repair" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "deny", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "ProductCode": "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d", "ReleaseDate": "2021-01-01", "InstallerAbortsTerminal": true, "InstallLocationRequired": true, "RequireExplicitUpgrade": true, "UnsupportedOSArchitectures": [ "arm" ], "ElevationRequirement": "elevatesSelf", "AppsAndFeaturesEntries": [{ "DisplayName": "DisplayName", "DisplayVersion": "DisplayVersion", "Publisher": "Publisher", "ProductCode": "ProductCode", "UpgradeCode": "UpgradeCode", "InstallerType": "exe" }], "Markets" : { "AllowedMarkets": [ "US" ] }, "ExpectedReturnCodes": [{ "InstallerReturnCode": 3, "ReturnResponse": "custom", "ReturnResponseUrl": "http://returnResponseUrl.net" }], "NestedInstallerType": "portable", "DisplayInstallWarnings": true, "UnsupportedArguments": [ "log" ], "NestedInstallerFiles": [{ "RelativeFilePath": "test\\app.exe", "PortableCommandAlias": "test.exe" }], "InstallationMetadata": { "DefaultInstallLocation": "%TEMP%\\DefaultInstallLocation", "Files": [{ "RelativeFilePath": "test\\app.exe", "FileSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "FileType": "launch", "InvocationParameter": "/parameter", "DisplayName": "test" }] }, "DownloadCommandProhibited": true, "RepairBehavior": "uninstaller" } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const AppInstaller::Manifest::Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://DefaultPurchaseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Default Installation Notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default Document Label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "http://DefaultDocumentUrl.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://DefaultTestIcon"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); REQUIRE(frenchLocalization.Get() == "Release notes"); REQUIRE(frenchLocalization.Get() == "https://ReleaseNotes.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Label == "Label"); REQUIRE(frenchLocalization.Get().at(0).AgreementText == "Text"); REQUIRE(frenchLocalization.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); REQUIRE(frenchLocalization.Get() == "http://purchaseUrl.net"); REQUIRE(frenchLocalization.Get() == "Installation Notes"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).DocumentLabel == "Document Label"); REQUIRE(frenchLocalization.Get().at(0).DocumentUrl == "http://documentUrl.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Url == "https://testIcon"); REQUIRE(frenchLocalization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(frenchLocalization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(frenchLocalization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(frenchLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); } void VerifyInstallers_AllFields(const AppInstaller::Manifest::Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.zip"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 8); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Repair) == "/repair"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Deny); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == ""); REQUIRE(actualInstaller.ProductCode == "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d"); REQUIRE(actualInstaller.ReleaseDate == "2021-01-01"); REQUIRE(actualInstaller.InstallerAbortsTerminal); REQUIRE(actualInstaller.InstallLocationRequired); REQUIRE(actualInstaller.RequireExplicitUpgrade); REQUIRE(actualInstaller.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(actualInstaller.UnsupportedOSArchitectures.size() == 1); REQUIRE(actualInstaller.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(actualInstaller.AppsAndFeaturesEntries.size() == 1); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseUrl == "http://returnResponseUrl.net"); REQUIRE(actualInstaller.NestedInstallerType == InstallerTypeEnum::Portable); REQUIRE(actualInstaller.DisplayInstallWarnings); REQUIRE(actualInstaller.UnsupportedArguments.size() == 1); REQUIRE(actualInstaller.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); REQUIRE(actualInstaller.NestedInstallerFiles.size() == 1); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).PortableCommandAlias == "test.exe"); REQUIRE(actualInstaller.InstallationMetadata.DefaultInstallLocation == "%TEMP%\\DefaultInstallLocation"); REQUIRE(actualInstaller.InstallationMetadata.Files.size() == 1); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).InvocationParameter == "/parameter"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).DisplayName == "test"); REQUIRE(actualInstaller.DownloadCommandProhibited); REQUIRE(actualInstaller.RepairBehavior == RepairBehaviorEnum::Uninstaller); } }; } TEST_CASE("GetManifests_GoodRequest_Authentication", "[RestSource][Interface_1_7]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } std::string expectedToken = "TestToken"; // Set good authentication result AuthenticationResult authResultOverride; authResultOverride.Status = S_OK; authResultOverride.Token = expectedToken; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); // GetManifest should succeed with expected value. HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, SampleGetManifestResponse, { web::http::header_names::authorization, JSON::GetUtilityString(CreateBearerToken(expectedToken)) }, web::http::status_codes::Unauthorized) }; Interface v1_7{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {}, GetTestAuthenticationArguments() }; auto manifestResult = v1_7.GetManifestByVersion("Foo.Bar", "5.0.0", ""); REQUIRE(manifestResult.has_value()); const auto& manifest = manifestResult.value(); REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "5.0.0"); } TEST_CASE("GetManifests_BadRequest_AuthenticationFailed", "[RestSource][Interface_1_7]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } std::string expectedToken = "TestToken"; // Set authentication failed result AuthenticationResult authResultOverride; authResultOverride.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); // GetManifest should fail with authentication failure HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, SampleGetManifestResponse, { web::http::header_names::authorization, JSON::GetUtilityString(CreateBearerToken(expectedToken)) }, web::http::status_codes::Unauthorized) }; Interface v1_7{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {}, GetTestAuthenticationArguments() }; REQUIRE_THROWS_HR(v1_7.GetManifestByVersion("Foo.Bar", "5.0.0", ""), APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED); } TEST_CASE("GetManifests_BadRequest_InvalidAuthenticationToken", "[RestSource][Interface_1_7]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } std::string expectedToken = "TestToken"; // Set authentication result with incorrect token AuthenticationResult authResultOverride; authResultOverride.Status = S_OK; authResultOverride.Token = "OtherToken"; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); // GetManifest should fail with access denied HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, SampleGetManifestResponse, { web::http::header_names::authorization, JSON::GetUtilityString(CreateBearerToken(expectedToken)) }, web::http::status_codes::Unauthorized) }; Interface v1_7{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {}, GetTestAuthenticationArguments() }; REQUIRE_THROWS_HR(v1_7.GetManifestByVersion("Foo.Bar", "5.0.0", ""), HTTP_E_STATUS_DENIED); } TEST_CASE("Search_GoodRequest_Authentication", "[RestSource][Interface_1_7]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } std::string expectedToken = "TestToken"; // Set good authentication result AuthenticationResult authResultOverride; authResultOverride.Status = S_OK; authResultOverride.Token = expectedToken; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); // Search should succeed with expected value. HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, SampleSearchResponse, { web::http::header_names::authorization, JSON::GetUtilityString(CreateBearerToken(expectedToken)) }, web::http::status_codes::Unauthorized) }; Interface v1_7{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {}, GetTestAuthenticationArguments() }; SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "package" }; request.Filters.emplace_back(std::move(filter)); IRestClient::SearchResult searchResponse = v1_7.Search(request); REQUIRE(searchResponse.Matches.size() == 1); IRestClient::Package package = searchResponse.Matches.at(0); REQUIRE(package.PackageInformation.PackageIdentifier.compare("git.package") == 0); } TEST_CASE("Search_BadRequest_AuthenticationFailed", "[RestSource][Interface_1_7]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } std::string expectedToken = "TestToken"; // Set authentication failed result AuthenticationResult authResultOverride; authResultOverride.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); // Search should fail with authentication failure HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, SampleSearchResponse, { web::http::header_names::authorization, JSON::GetUtilityString(CreateBearerToken(expectedToken)) }, web::http::status_codes::Unauthorized) }; Interface v1_7{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {}, GetTestAuthenticationArguments() }; SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "package" }; request.Filters.emplace_back(std::move(filter)); REQUIRE_THROWS_HR(v1_7.Search(request), APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED); } TEST_CASE("Search_BadRequest_InvalidAuthenticationToken", "[RestSource][Interface_1_7]") { if (Runtime::IsRunningAsSystem()) { WARN("Test does not support running as system. Skipped."); return; } std::string expectedToken = "TestToken"; // Set authentication result with incorrect token AuthenticationResult authResultOverride; authResultOverride.Status = S_OK; authResultOverride.Token = "OtherToken"; TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride); // Search should fail with access denied HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, SampleSearchResponse, { web::http::header_names::authorization, JSON::GetUtilityString(CreateBearerToken(expectedToken)) }, web::http::status_codes::Unauthorized) }; Interface v1_7{ TestRestUriString, std::move(helper), GetTestSourceInformation(), {}, GetTestAuthenticationArguments() }; SearchRequest request; PackageMatchFilter filter{ PackageMatchField::Name, MatchType::Exact, "package" }; request.Filters.emplace_back(std::move(filter)); REQUIRE_THROWS_HR(v1_7.Search(request), HTTP_E_STATUS_DENIED); } TEST_CASE("GetManifests_GoodResponse_V1_7", "[RestSource][Interface_1_7]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_7{ TestRestUriString, std::move(helper), {} }; std::vector manifests = v1_7.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated AppInstaller::Manifest::Manifest& manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.7.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } ================================================ FILE: src/AppInstallerCLITests/RestInterface_1_9.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_9; namespace { const std::string TestRestUriString = "http://restsource.com/api"; struct GoodManifest_AllFields { utility::string_t GetSampleManifest_AllFields() { return _XPLATSTR( R"delimiter( { "Data": { "PackageIdentifier": "Foo.Bar", "Versions": [ { "PackageVersion": "3.0.0abc", "DefaultLocale": { "PackageLocale": "en-US", "Publisher": "Foo", "PublisherUrl": "http://publisher.net", "PublisherSupportUrl": "http://publisherSupport.net", "PrivacyUrl": "http://packagePrivacyUrl.net", "Author": "FooBar", "PackageName": "Bar", "PackageUrl": "http://packageUrl.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl.net", "ShortDescription": "Foo bar is a foo bar.", "Description": "Foo bar is a placeholder.", "Tags": [ "FooBar", "Foo", "Bar" ], "Moniker": "FooBarMoniker", "ReleaseNotes": "Default release notes", "ReleaseNotesUrl": "https://DefaultReleaseNotes.net", "Agreements": [{ "AgreementLabel": "DefaultLabel", "Agreement": "DefaultText", "AgreementUrl": "https://DefaultAgreementUrl.net" }], "PurchaseUrl": "http://DefaultPurchaseUrl.net", "InstallationNotes": "Default Installation Notes", "Documentations": [{ "DocumentLabel": "Default Document Label", "DocumentUrl": "http://DefaultDocumentUrl.net" }], "Icons": [{ "IconUrl": "https://DefaultTestIcon", "IconFileType": "ico", "IconResolution": "custom", "IconTheme": "default", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123" }] }, "Channel": "", "Locales": [ { "PackageLocale": "fr-Fr", "Publisher": "Foo French", "PublisherUrl": "http://publisher-fr.net", "PublisherSupportUrl": "http://publisherSupport-fr.net", "PrivacyUrl": "http://packagePrivacyUrl-fr.net", "Author": "FooBar French", "PackageName": "Bar", "PackageUrl": "http://packageUrl-fr.net", "License": "Foo Bar License", "LicenseUrl": "http://licenseUrl-fr.net", "Copyright": "Foo Bar Copyright", "CopyrightUrl": "http://copyrightUrl-fr.net", "ShortDescription": "Foo bar is a foo bar French.", "Description": "Foo bar is a placeholder French.", "Tags": [ "FooBarFr", "FooFr", "BarFr" ], "ReleaseNotes": "Release notes", "ReleaseNotesUrl": "https://ReleaseNotes.net", "Agreements": [{ "AgreementLabel": "Label", "Agreement": "Text", "AgreementUrl": "https://AgreementUrl.net" }], "PurchaseUrl": "http://purchaseUrl.net", "InstallationNotes": "Installation Notes", "Documentations": [{ "DocumentLabel": "Document Label", "DocumentUrl": "http://documentUrl.net" }], "Icons": [{ "IconUrl": "https://testIcon", "IconFileType": "png", "IconResolution": "32x32", "IconTheme": "light", "IconSha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321" }] } ],)delimiter") _XPLATSTR(R"delimiter( "Installers": [ { "InstallerSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "InstallerUrl": "http://foobar.zip", "Architecture": "x86", "InstallerLocale": "en-US", "Platform": [ "Windows.Desktop" ], "MinimumOSVersion": "1078", "InstallerType": "zip", "Scope": "user", "InstallModes": [ "interactive" ], "InstallerSwitches": { "Silent": "/s", "SilentWithProgress": "/s", "Interactive": "/i", "InstallLocation": "C:\\Users\\User1", "Log": "/l", "Upgrade": "/u", "Custom": "/custom", "Repair": "/repair" }, "InstallerSuccessCodes": [ 0 ], "UpgradeBehavior": "deny", "Commands": [ "command1" ], "Protocols": [ "protocol1" ], "FileExtensions": [ ".file-extension" ], "Dependencies": { "WindowsFeatures": [ "feature1" ], "WindowsLibraries": [ "library1" ], "PackageDependencies": [ { "PackageIdentifier": "Foo.Baz", "MinimumVersion": "2.0.0" } ], "ExternalDependencies": [ "FooBarBaz" ] }, "ProductCode": "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d", "ReleaseDate": "2021-01-01", "InstallerAbortsTerminal": true, "InstallLocationRequired": true, "RequireExplicitUpgrade": true, "UnsupportedOSArchitectures": [ "arm" ], "ElevationRequirement": "elevatesSelf", "AppsAndFeaturesEntries": [{ "DisplayName": "DisplayName", "DisplayVersion": "DisplayVersion", "Publisher": "Publisher", "ProductCode": "ProductCode", "UpgradeCode": "UpgradeCode", "InstallerType": "exe" }], "Markets" : { "AllowedMarkets": [ "US" ] }, "ExpectedReturnCodes": [{ "InstallerReturnCode": 3, "ReturnResponse": "custom", "ReturnResponseUrl": "http://returnResponseUrl.net" }], "NestedInstallerType": "portable", "DisplayInstallWarnings": true, "UnsupportedArguments": [ "log" ], "NestedInstallerFiles": [{ "RelativeFilePath": "test\\app.exe", "PortableCommandAlias": "test.exe" }], "InstallationMetadata": { "DefaultInstallLocation": "%TEMP%\\DefaultInstallLocation", "Files": [{ "RelativeFilePath": "test\\app.exe", "FileSha256": "011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6", "FileType": "launch", "InvocationParameter": "/parameter", "DisplayName": "test" }] }, "DownloadCommandProhibited": true, "RepairBehavior": "uninstaller", "ArchiveBinariesDependOnPath": true } ] } ] }, "ContinuationToken": "abcd" })delimiter"); } void VerifyLocalizations_AllFields(const AppInstaller::Manifest::Manifest& manifest) { REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisher.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://publisherSupport.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packagePrivacyUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get() == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "http://packageUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar License"); REQUIRE(manifest.DefaultLocalization.Get() == "http://licenseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo Bar Copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "http://copyrightUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a foo bar."); REQUIRE(manifest.DefaultLocalization.Get() == "Foo bar is a placeholder."); REQUIRE(manifest.DefaultLocalization.Get().size() == 3); REQUIRE(manifest.DefaultLocalization.Get().at(0) == "FooBar"); REQUIRE(manifest.DefaultLocalization.Get().at(1) == "Foo"); REQUIRE(manifest.DefaultLocalization.Get().at(2) == "Bar"); REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "http://DefaultPurchaseUrl.net"); REQUIRE(manifest.DefaultLocalization.Get() == "Default Installation Notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default Document Label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "http://DefaultDocumentUrl.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://DefaultTestIcon"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization frenchLocalization = manifest.Localizations.at(0); REQUIRE(frenchLocalization.Locale == "fr-Fr"); REQUIRE(frenchLocalization.Get() == "Foo French"); REQUIRE(frenchLocalization.Get() == "http://publisher-fr.net"); REQUIRE(frenchLocalization.Get() == "http://publisherSupport-fr.net"); REQUIRE(frenchLocalization.Get() == "http://packagePrivacyUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "FooBar French"); REQUIRE(frenchLocalization.Get() == "Bar"); REQUIRE(frenchLocalization.Get() == "http://packageUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar License"); REQUIRE(frenchLocalization.Get() == "http://licenseUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo Bar Copyright"); REQUIRE(frenchLocalization.Get() == "http://copyrightUrl-fr.net"); REQUIRE(frenchLocalization.Get() == "Foo bar is a foo bar French."); REQUIRE(frenchLocalization.Get() == "Foo bar is a placeholder French."); REQUIRE(frenchLocalization.Get().size() == 3); REQUIRE(frenchLocalization.Get().at(0) == "FooBarFr"); REQUIRE(frenchLocalization.Get().at(1) == "FooFr"); REQUIRE(frenchLocalization.Get().at(2) == "BarFr"); REQUIRE(frenchLocalization.Get() == "Release notes"); REQUIRE(frenchLocalization.Get() == "https://ReleaseNotes.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Label == "Label"); REQUIRE(frenchLocalization.Get().at(0).AgreementText == "Text"); REQUIRE(frenchLocalization.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); REQUIRE(frenchLocalization.Get() == "http://purchaseUrl.net"); REQUIRE(frenchLocalization.Get() == "Installation Notes"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).DocumentLabel == "Document Label"); REQUIRE(frenchLocalization.Get().at(0).DocumentUrl == "http://documentUrl.net"); REQUIRE(frenchLocalization.Get().size() == 1); REQUIRE(frenchLocalization.Get().at(0).Url == "https://testIcon"); REQUIRE(frenchLocalization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(frenchLocalization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(frenchLocalization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(frenchLocalization.Get().at(0).Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); } void VerifyInstallers_AllFields(const AppInstaller::Manifest::Manifest& manifest) { REQUIRE(manifest.Installers.size() == 1); ManifestInstaller actualInstaller = manifest.Installers.at(0); REQUIRE(actualInstaller.Sha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.Url == "http://foobar.zip"); REQUIRE(actualInstaller.Arch == Architecture::X86); REQUIRE(actualInstaller.Locale == "en-US"); REQUIRE(actualInstaller.Platform.size() == 1); REQUIRE(actualInstaller.Platform[0] == PlatformEnum::Desktop); REQUIRE(actualInstaller.MinOSVersion == "1078"); REQUIRE(actualInstaller.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(actualInstaller.Scope == ScopeEnum::User); REQUIRE(actualInstaller.InstallModes.size() == 1); REQUIRE(actualInstaller.InstallModes.at(0) == InstallModeEnum::Interactive); REQUIRE(actualInstaller.Switches.size() == 8); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::SilentWithProgress) == "/s"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::InstallLocation) == "C:\\Users\\User1"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Log) == "/l"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(actualInstaller.Switches.at(InstallerSwitchType::Repair) == "/repair"); REQUIRE(actualInstaller.InstallerSuccessCodes.size() == 1); REQUIRE(actualInstaller.InstallerSuccessCodes.at(0) == 0); REQUIRE(actualInstaller.UpdateBehavior == UpdateBehaviorEnum::Deny); REQUIRE(actualInstaller.Commands.at(0) == "command1"); REQUIRE(actualInstaller.Protocols.at(0) == "protocol1"); REQUIRE(actualInstaller.FileExtensions.at(0) == ".file-extension"); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsFeature, "feature1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "library1")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::Package, "Foo.Baz", "2.0.0")); REQUIRE(actualInstaller.Dependencies.HasExactDependency(DependencyType::External, "FooBarBaz")); REQUIRE(actualInstaller.PackageFamilyName == ""); REQUIRE(actualInstaller.ProductCode == "5b6e0f8a-3bbf-4a17-aefd-024c2b3e075d"); REQUIRE(actualInstaller.ReleaseDate == "2021-01-01"); REQUIRE(actualInstaller.InstallerAbortsTerminal); REQUIRE(actualInstaller.InstallLocationRequired); REQUIRE(actualInstaller.RequireExplicitUpgrade); REQUIRE(actualInstaller.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(actualInstaller.UnsupportedOSArchitectures.size() == 1); REQUIRE(actualInstaller.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(actualInstaller.AppsAndFeaturesEntries.size() == 1); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseUrl == "http://returnResponseUrl.net"); REQUIRE(actualInstaller.NestedInstallerType == InstallerTypeEnum::Portable); REQUIRE(actualInstaller.DisplayInstallWarnings); REQUIRE(actualInstaller.UnsupportedArguments.size() == 1); REQUIRE(actualInstaller.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); REQUIRE(actualInstaller.NestedInstallerFiles.size() == 1); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.NestedInstallerFiles.at(0).PortableCommandAlias == "test.exe"); REQUIRE(actualInstaller.InstallationMetadata.DefaultInstallLocation == "%TEMP%\\DefaultInstallLocation"); REQUIRE(actualInstaller.InstallationMetadata.Files.size() == 1); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).RelativeFilePath == "test\\app.exe"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).FileSha256 == AppInstaller::Utility::SHA256::ConvertToBytes("011048877dfaef109801b3f3ab2b60afc74f3fc4f7b3430e0c897f5da1df84b6")); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).InvocationParameter == "/parameter"); REQUIRE(actualInstaller.InstallationMetadata.Files.at(0).DisplayName == "test"); REQUIRE(actualInstaller.DownloadCommandProhibited); REQUIRE(actualInstaller.RepairBehavior == RepairBehaviorEnum::Uninstaller); REQUIRE(actualInstaller.ArchiveBinariesDependOnPath); } }; } TEST_CASE("GetManifests_GoodResponse_V1_9", "[RestSource][Interface_1_9]") { GoodManifest_AllFields sampleManifest; utility::string_t sample = sampleManifest.GetSampleManifest_AllFields(); HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::OK, std::move(sample)) }; Interface v1_9{ TestRestUriString, std::move(helper), {} }; std::vector manifests = v1_9.GetManifests("Foo.Bar"); REQUIRE(manifests.size() == 1); // Verify manifest is populated Manifest& manifest = manifests[0]; REQUIRE(manifest.Id == "Foo.Bar"); REQUIRE(manifest.Version == "3.0.0abc"); REQUIRE(manifest.Moniker == "FooBarMoniker"); REQUIRE(manifest.Channel == ""); REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.9.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); } ================================================ FILE: src/AppInstallerCLITests/ResumeFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include "TestHooks.h" #include #include #include #include #include #include #include using namespace std::string_literals; using namespace AppInstaller::CLI; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Settings; using namespace AppInstaller::Runtime; using namespace TestCommon; using namespace AppInstaller::Checkpoints; constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv; TEST_CASE("ResumeFlow_IndexNotFound", "[Resume]") { std::string tempGuidString = "{ec3a098c-a815-4d52-8866-946c03093a37}"; std::ostringstream resumeOutput; TestContext context{ resumeOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ResumeId, tempGuidString); ResumeCommand resume({}); context.SetExecutingCommand(&resume); resume.Execute(context); INFO(resumeOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND); auto expectedMessage = Resource::String::ResumeIdNotFoundError(AppInstaller::Utility::LocIndString(tempGuidString)); REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } TEST_CASE("ResumeFlow_InvalidClientVersion", "[Resume]") { TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", true); const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); // Create temp guid and populate with invalid client version. std::string tempGuidString = "{615339e9-3ac5-4e86-a5ab-c246657aca25}"; auto tempRecordPath = tempCheckpointRecordDirectoryPath / tempGuidString / s_CheckpointsFileName; std::string_view invalidClientVersion = "1.2.3.4"sv; INFO("Using temporary file named: " << tempRecordPath); { // Manually set invalid client version std::filesystem::create_directories(tempRecordPath.parent_path()); std::shared_ptr checkpointRecord = CheckpointDatabase::CreateNew(tempRecordPath.u8string()); CheckpointDatabase::IdType checkpointId = checkpointRecord->AddCheckpoint(s_AutomaticCheckpoint); checkpointRecord->SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { "1.2.3.4" }); } std::ostringstream resumeOutput; TestContext context{ resumeOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ResumeId, tempGuidString); ResumeCommand resume({}); context.SetExecutingCommand(&resume); resume.Execute(context); INFO(resumeOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH); auto expectedMessage = Resource::String::ClientVersionMismatchError(AppInstaller::Utility::LocIndString(invalidClientVersion)); REQUIRE(resumeOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } TEST_CASE("ResumeFlow_EmptyIndex", "[Resume]") { TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", true); const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); std::string tempGuidString = "{43ca664c-3eae-4f73-99ee-18cf83912c02}"; auto tempRecordPath = tempCheckpointRecordDirectoryPath / tempGuidString / s_CheckpointsFileName; INFO("Using temporary file named: " << tempRecordPath); { std::filesystem::create_directories(tempRecordPath.parent_path()); CheckpointDatabase::CreateNew(tempRecordPath.u8string()); } std::ostringstream resumeOutput; TestContext context{ resumeOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::ResumeId, tempGuidString); ResumeCommand resume({}); context.SetExecutingCommand(&resume); resume.Execute(context); INFO(resumeOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE); REQUIRE(resumeOutput.str().find(Resource::LocString(Resource::String::ResumeStateDataNotFoundError).get()) != std::string::npos); } TEST_CASE("ResumeFlow_InstallSuccess", "[Resume]") { TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", false); const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); TestCommon::TempFile installResultPath("TestExeInstalled.txt"); { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); const auto& testManifestPath = TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string(); context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); InstallCommand install({}); context.SetExecutingCommand(&install); install.Execute(context); INFO(installOutput.str()); } // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); // The checkpoint index should not exist if the context succeeded. std::vector checkpointFiles; for (const auto& entry : std::filesystem::directory_iterator(tempCheckpointRecordDirectoryPath)) { checkpointFiles.emplace_back(entry.path()); } REQUIRE(checkpointFiles.size() == 0); } TEST_CASE("ResumeFlow_InstallFailure", "[Resume]") { TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", false); const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); { std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); const auto& testManifestPath = TestDataFile("InstallFlowTest_UnsupportedArguments.yaml").GetPath().u8string(); context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); context.Args.AddArg(Execution::Args::Type::InstallLocation, "installLocation"sv); InstallCommand install({}); context.SetExecutingCommand(&install); install.Execute(context); INFO(installOutput.str()); // Verify unsupported arguments error message is shown REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT); } // Checkpoint file should be cleaned up if the hr is not reboot related. REQUIRE(std::filesystem::is_empty(tempCheckpointRecordDirectoryPath)); } TEST_CASE("ResumeFlow_WriteToRunOnceRegistry", "[Reboot][Resume][windowsFeature]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestCommon::TempDirectory tempCheckpointRecordDirectory("TempCheckpointRecordDirectory", false); const auto& tempCheckpointRecordDirectoryPath = tempCheckpointRecordDirectory.GetPath(); TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideOpenDependencySource(context); OverrideRegisterStartupAfterReboot(context); // Override with reboot required HRESULT. auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(ERROR_SUCCESS_REBOOT_REQUIRED); TestHook::SetRegisterForRestartResult_Override registerForRestartResultOverride(false); TestHook::SetInitiateRebootResult_Override initiateRebootResultOverride(false); const auto& testManifestPath = TestDataFile("InstallFlowTest_WindowsFeatures.yaml").GetPath().u8string(); context.Args.AddArg(Execution::Args::Type::Manifest, testManifestPath); context.Args.AddArg(Execution::Args::Type::AllowReboot); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL); } TEST_CASE("ResumeFlow_ResumeLimitExceeded", "[Resume]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestCommon::TempDirectory tempCheckpointsLocationDir("TempCheckpointsLocationDir", true); const auto& tempCheckpointRecordDirectoryPath = tempCheckpointsLocationDir.GetPath(); TestHook_SetPathOverride(PathName::CheckpointsLocation, tempCheckpointRecordDirectoryPath); TestCommon::TestUserSettings testSettings; testSettings.Set(true); testSettings.Set(1); std::filesystem::path testResumeId = L"testResumeId"; std::filesystem::path tempResumeDir = tempCheckpointRecordDirectoryPath / testResumeId; std::filesystem::path tempCheckpointDatabasePath = tempResumeDir / L"checkpoints.db"; std::filesystem::create_directory(tempResumeDir); { std::shared_ptr database = CheckpointDatabase::CreateNew(tempCheckpointDatabasePath.u8string(), {1, 0}); std::string_view testCheckpointName = "testCheckpoint"sv; CheckpointDatabase::IdType checkpointId = database->AddCheckpoint(testCheckpointName); database->SetDataValue(checkpointId, AutomaticCheckpointData::Command, {}, { "install" }); database->SetDataValue(checkpointId, AutomaticCheckpointData::ClientVersion, {}, { GetClientVersion()}); database->SetDataValue(checkpointId, AutomaticCheckpointData::ResumeCount, {}, {"1"}); } { std::ostringstream resumeOutput; TestContext resumeContext{ resumeOutput, std::cin }; auto previousThreadGlobals = resumeContext.SetForCurrentThread(); resumeContext.Args.AddArg(Execution::Args::Type::ResumeId, testResumeId.u8string()); ResumeCommand resume({}); resume.Execute(resumeContext); INFO(resumeOutput.str()); REQUIRE(resumeContext.IsTerminated()); REQUIRE(resumeContext.GetTerminationHR() == APPINSTALLER_CLI_ERROR_RESUME_LIMIT_EXCEEDED); REQUIRE(resumeOutput.str().find(Resource::LocString(Resource::String::ResumeLimitExceeded('1')).get()) != std::string::npos); REQUIRE(resumeOutput.str().find("winget resume -g " + testResumeId.u8string() + " --ignore-resume-limit") != std::string::npos); } } ================================================ FILE: src/AppInstallerCLITests/Run-TestsInPackage.ps1 ================================================ <# .SYNOPSIS Runs the AppInstallerCLI tests within the packaged context. .DESCRIPTION Registers the loose files generated by the AppInstallerCLIPackage project, then runs the existing tests from "within" this context. .PARAMETER BuildRoot The root of the build output directory. If not provided, assumed to be the local default location relative to this script. .PARAMETER PackageRoot The root of the package build output directory. If not provided, assumed to be the local default location relative to this script. .PARAMETER LogTarget The file path to log to. .PARAMETER MdmpTarget The path to write a minidump to if the tests crash. .PARAMETER TestResultsTarget The file path to place the test result file in. .PARAMETER Args Additional args to pass to the tests. .PARAMETER Wait Have the test process wait for user input before exiting. .PARAMETER ScriptWait Have the script wait for the output files to be freed before exiting. #> param( [Parameter(Mandatory=$false)] [string]$BuildRoot, [Parameter(Mandatory=$false)] [string]$PackageRoot, [Parameter(Mandatory=$false)] [string]$LogTarget, [Parameter(Mandatory=$false)] [string]$MdmpTarget, [Parameter(Mandatory=$false)] [string]$TestResultsTarget, [Parameter(Mandatory=$false)] [string]$Args, [switch]$Wait, [switch]$ScriptWait ) function Wait-ForFileClose([string]$Path) { $Local:FileInfo = [System.IO.FileInfo]::new($Path) $Local:SleepCount = 0 $Local:SleepCountMax = 600 while ($Local:SleepCount -lt $Local:SleepCountMax) { try { [System.IO.FileStream] $Local:Stream = $Local:FileInfo.OpenWrite() $Local:Stream.Dispose() break } catch { Start-Sleep 1 $Local:SleepCount = $Local:SleepCount + 1 } } if ($Local:SleepCount -ge $Local:SleepCountMax) { throw "Timed out waiting for file close: $Path" } } if ([String]::IsNullOrEmpty($BuildRoot)) { $BuildRoot = Split-Path -Parent $PSCommandPath; $BuildRoot = Join-Path $BuildRoot "..\x64\Debug"; } $BuildRoot = Resolve-Path $BuildRoot Write-Host "Using BuildRoot = $BuildRoot" if ([String]::IsNullOrEmpty($PackageRoot)) { $PackageRoot = Split-Path -Parent $PSCommandPath; $PackageRoot = Join-Path $PackageRoot "..\AppInstallerCLIPackage\bin\x64\Debug"; } $PackageRoot = Resolve-Path $PackageRoot Write-Host "Using PackageRoot = $PackageRoot" if (![String]::IsNullOrEmpty($LogTarget)) { $Local:temp = Split-Path -Parent $LogTarget $Local:temp = Resolve-Path $Local:temp $LogTarget = Join-Path $Local:temp (Split-Path -Leaf $LogTarget) Write-Host "Using LogTarget = $LogTarget" } if (![String]::IsNullOrEmpty($MdmpTarget)) { $Local:temp = Split-Path -Parent $MdmpTarget $Local:temp = Resolve-Path $Local:temp $MdmpTarget = Join-Path $Local:temp (Split-Path -Leaf $MdmpTarget) Write-Host "Using MdmpTarget = $MdmpTarget" } if (![String]::IsNullOrEmpty($TestResultsTarget)) { $Local:temp = Split-Path -Parent $TestResultsTarget $Local:temp = Resolve-Path $Local:temp $TestResultsTarget = Join-Path $Local:temp (Split-Path -Leaf $TestResultsTarget) Write-Host "Using TestResultsTarget = $TestResultsTarget" } # Register the package; this requires the local package to have been deployed at least once or it won't be built. $Local:ManifestPath = Join-Path $PackageRoot "AppxManifest.xml" if (-not (Test-Path $Local:ManifestPath)) { $Local:ManifestPath = Join-Path $PackageRoot "AppX\AppxManifest.xml" } Write-Host "Registering manifest at path: $Local:ManifestPath" Add-AppxPackage -Register $Local:ManifestPath # Execute the tests from within the package's runtime. $Local:TestExePath = Join-Path $BuildRoot "AppInstallerCLITests\AppInstallerCLITests.exe" $Local:TestArgs = $Args if ([String]::IsNullOrEmpty($LogTarget)) { $Local:TestArgs = $Local:TestArgs + " -log" } else { $Local:TestArgs = $Local:TestArgs + " -logto ""$LogTarget""" } if ([String]::IsNullOrEmpty($MdmpTarget)) { $Local:TestArgs = $Local:TestArgs + " -mdmp" } else { $Local:TestArgs = $Local:TestArgs + " -mdmpto ""$MdmpTarget""" } if (![String]::IsNullOrEmpty($TestResultsTarget)) { $Local:TestArgs = $Local:TestArgs + " -s -r junit -o ""$TestResultsTarget""" } if ($Wait) { $Local:TestArgs = $Local:TestArgs + " -wait" } Write-Host "Executing tests at path: $Local:TestExePath" Write-Host "Executing tests with args: $Local:TestArgs" Invoke-CommandInDesktopPackage -PackageFamilyName WinGetDevCLI_8wekyb3d8bbwe -AppId WinGetDev -Command $Local:TestExePath -Args $Local:TestArgs if ($ScriptWait) { try { Write-Host "Waiting for output files to be closed..." Start-Sleep 5 if (![String]::IsNullOrEmpty($LogTarget)) { Wait-ForFileClose $LogTarget } if (![String]::IsNullOrEmpty($TestResultsTarget)) { Wait-ForFileClose $TestResultsTarget } Write-Host "Done" } finally { Write-Host "Remove registered package" Get-AppxPackage WinGetDevCLI | Remove-AppxPackage } } ================================================ FILE: src/AppInstallerCLITests/Runtime.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestHooks.h" #include #include using namespace AppInstaller; using namespace AppInstaller::Filesystem; using namespace AppInstaller::Runtime; using namespace TestCommon; bool CanWriteToPath(const std::filesystem::path& directory, const std::filesystem::path& file = "test.txt") { std::ofstream out{ directory / file }; out << "Test"; return out.good(); } void RequireAdminOwner(const std::filesystem::path& directory) { wil::unique_hlocal_security_descriptor securityDescriptor; PSID ownerSID = nullptr; THROW_IF_WIN32_ERROR(GetNamedSecurityInfoW(directory.c_str(), SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &ownerSID, nullptr, nullptr, nullptr, &securityDescriptor)); auto adminSID = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); REQUIRE(EqualSid(adminSID.get(), ownerSID)); } TEST_CASE("ApplyACL_CurrentUserOwner", "[runtime]") { TempDirectory directory("CurrentUserOwner"); PathDetails details; details.Path = directory; details.SetOwner(ACEPrincipal::CurrentUser); details.ApplyACL(); REQUIRE(CanWriteToPath(directory)); } TEST_CASE("ApplyACL_RemoveWriteForUser", "[runtime]") { TempDirectory directory("CurrentUserCantWrite"); PathDetails details; details.Path = directory; details.ACL[ACEPrincipal::CurrentUser] = ACEPermissions::ReadExecute; details.ApplyACL(); REQUIRE(!CanWriteToPath(directory)); } TEST_CASE("ApplyACL_AdminOwner", "[runtime]") { TempDirectory directory("AdminOwner"); PathDetails details; details.Path = directory; details.SetOwner(ACEPrincipal::Admins); if (IsRunningAsAdmin()) { details.ApplyACL(); RequireAdminOwner(directory); REQUIRE(CanWriteToPath(directory)); } else { // A non-admin token cannot set the owner to be the Admins group REQUIRE_THROWS_HR(details.ApplyACL(), HRESULT_FROM_WIN32(ERROR_INVALID_OWNER)); } } TEST_CASE("ApplyACL_BothOwners", "[runtime]") { TempDirectory directory("AdminOwner"); PathDetails details; details.Path = directory; details.ACL[ACEPrincipal::CurrentUser] = ACEPermissions::ReadExecute; details.ACL[ACEPrincipal::System] = ACEPermissions::All; if (IsRunningAsSystem()) { // Both cannot be owners REQUIRE_THROWS_HR(details.ApplyACL(), HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } else { REQUIRE_NOTHROW(details.ApplyACL()); } } TEST_CASE("ApplyACL_CurrentUserOwner_SystemAll", "[runtime]") { TempDirectory directory("UserAndSystem"); PathDetails details; details.Path = directory; details.SetOwner(ACEPrincipal::CurrentUser); details.ACL[ACEPrincipal::System] = ACEPermissions::All; details.ApplyACL(); REQUIRE(CanWriteToPath(directory)); } TEST_CASE("VerifyDevModeEnabledCheck", "[runtime]") { if (!Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } bool initialState = IsDevModeEnabled(); EnableDevMode(!initialState); bool modifiedState = IsDevModeEnabled(); // Revert to original state. EnableDevMode(initialState); bool revertedState = IsDevModeEnabled(); REQUIRE(modifiedState != initialState); REQUIRE(revertedState == initialState); } TEST_CASE("EnsureUserProfileNotPresentInDisplayPaths", "[runtime]") { // Clear the overrides that we use when testing as they don't consider display purposes Runtime::TestHook_ClearPathOverrides(); auto restorePaths = wil::scope_exit([]() { TestCommon::SetTestPathOverrides(); }); std::filesystem::path userProfilePath = Filesystem::GetKnownFolderPath(FOLDERID_Profile); std::string userProfileString = userProfilePath.u8string(); for (auto i = ToIntegral(ToEnum(0)); i < ToIntegral(Runtime::PathName::Max); ++i) { std::filesystem::path displayPath = GetPathTo(ToEnum(i), true); std::string displayPathString = displayPath.u8string(); INFO(i << " = " << displayPathString); REQUIRE(displayPathString.find(userProfileString) == std::string::npos); } } ================================================ FILE: src/AppInstallerCLITests/SQLiteDynamicStorage.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include #include using namespace AppInstaller::SQLite; using namespace std::string_literals; TEST_CASE("SQLiteDynamicStorage_UpgradeDetection", "[sqlite_dynamic]") { TestCommon::TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); // Create a database with version 1.0 SQLiteDynamicStorage storage{ tempFile.GetPath(), Version{ 1, 0 } }; { auto transactionLock = storage.TryBeginTransaction("test", false); REQUIRE(transactionLock); } // Update the database to version 2.0 { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::Create); Version version{ 2, 0 }; version.SetSchemaVersion(connection); } REQUIRE(storage.GetVersion() == Version{ 1, 0 }); auto transactionLock = storage.TryBeginTransaction("test", false); REQUIRE(!transactionLock); REQUIRE(storage.GetVersion() == Version{ 2, 0 }); transactionLock = storage.TryBeginTransaction("test", false); REQUIRE(transactionLock); } ================================================ FILE: src/AppInstallerCLITests/SQLiteIndex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace TestCommon; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; using namespace AppInstaller::Utility; using UtilityVersion = AppInstaller::Utility::Version; using SQLiteVersion = AppInstaller::SQLite::Version; SQLiteIndex CreateTestIndex(const std::string& filePath, std::optional version = {}) { // If no specific version requested, then use generator to run against the last 3 versions. if (!version) { SQLiteVersion latestVersion{ 2, 0 }; SQLiteVersion versionMinus1 = SQLiteVersion{ 1, 7 }; SQLiteVersion versionMinus2 = SQLiteVersion{ 1, 6 }; version = GENERATE_COPY(SQLiteVersion{ versionMinus2 }, SQLiteVersion{ versionMinus1 }, SQLiteVersion{ latestVersion }); } return SQLiteIndex::CreateNew(filePath, version.value()); } SQLiteVersion TestPrepareForRead(SQLiteIndex& index) { SQLiteVersion latestVersion{ 2, 0 }; SQLiteVersion versionMinus1 = SQLiteVersion{ 1, 7 }; SQLiteVersion versionMinus2 = SQLiteVersion{ 1, 6 }; index.PrepareForPackaging(); if (index.GetVersion() == versionMinus2) { // Degenerate case where we don't need to do anything } else if (index.GetVersion() == versionMinus1) { SQLiteVersion version = GENERATE_COPY(SQLiteVersion{ versionMinus2 }, SQLiteVersion{ versionMinus1 }); if (version != versionMinus1) { index.ForceVersion(version); return version; } } else if (index.GetVersion() == latestVersion) { // This crosses major versions, so leave it at 2.0 always } return index.GetVersion(); } std::string GetPathFromManifest(Manifest& manifest) { auto publisher = manifest.Id; AppInstaller::Utility::FindAndReplace(publisher, ".", "/"); return AppInstaller::Utility::ToLower(publisher).append("/").append(manifest.Version); } void CreateFakeManifest(Manifest& manifest, string_t publisher, string_t version = "1.0.0") { manifest.Installers.push_back({}); manifest.Id = publisher.append(".").append("Id"); manifest.DefaultLocalization.Add(publisher.append(" Name")); manifest.Moniker = "testmoniker"; manifest.Version = version; manifest.Channel = "test"; manifest.DefaultLocalization.Add({ "t1", "t2" }); manifest.Installers[0].Commands = { "test1", "test2" }; } SQLiteIndex SimpleTestSetup(const std::string& filePath, Manifest& manifest, std::optional version = {}) { SQLiteIndex index = CreateTestIndex(filePath, version); string_t publisher = "Test"; CreateFakeManifest(manifest, publisher); auto relativePath = GetPathFromManifest(manifest); index.AddManifest(manifest, relativePath); return index; } struct IndexFields { IndexFields( std::string id, std::string name, std::string moniker, std::string version, std::string channel, std::vector tags, std::vector commands, std::string path ) : Id(std::move(id)), Name(std::move(name)), Moniker(std::move(moniker)), Version(std::move(version)), Channel(std::move(channel)), Tags(std::move(tags)), Commands(std::move(commands)), Path(std::move(path)) {} IndexFields( std::string id, std::string name, std::string moniker, std::string version, std::string channel, std::vector tags, std::vector commands, std::string path, std::vector packageFamilyNames, std::vector productCodes ) : Id(std::move(id)), Name(std::move(name)), Moniker(std::move(moniker)), Version(std::move(version)), Channel(std::move(channel)), Tags(std::move(tags)), Commands(std::move(commands)), Path(std::move(path)), PackageFamilyNames(std::move(packageFamilyNames)), ProductCodes(std::move(productCodes)) {} IndexFields( std::string id, std::string name, std::string publisher, std::string moniker, std::string version, std::string channel, std::vector tags, std::vector commands, std::string path, std::vector packageFamilyNames, std::vector productCodes ) : Id(std::move(id)), Name(std::move(name)), Publisher(std::move(publisher)), Moniker(std::move(moniker)), Version(std::move(version)), Channel(std::move(channel)), Tags(std::move(tags)), Commands(std::move(commands)), Path(std::move(path)), PackageFamilyNames(std::move(packageFamilyNames)), ProductCodes(std::move(productCodes)) {} IndexFields( std::string id, std::string name, std::string publisher, std::string moniker, std::string version, std::string channel, std::vector tags, std::vector commands, std::string path, std::vector packageFamilyNames, std::vector productCodes, std::string arpName, std::string arpPublisher ) : Id(std::move(id)), Name(std::move(name)), Publisher(std::move(publisher)), Moniker(std::move(moniker)), Version(std::move(version)), Channel(std::move(channel)), Tags(std::move(tags)), Commands(std::move(commands)), Path(std::move(path)), PackageFamilyNames(std::move(packageFamilyNames)), ProductCodes(std::move(productCodes)), ArpName(std::move(arpName)), ArpPublisher(std::move(arpPublisher)) {} std::string Id; std::string Name; std::string Publisher; std::string Moniker; std::string Version; std::string Channel; std::vector Tags; std::vector Commands; std::string Path; std::vector PackageFamilyNames; std::vector ProductCodes; std::string ArpName; std::string ArpPublisher; }; SQLiteIndex SearchTestSetup(const std::string& filePath, std::initializer_list data = {}, std::optional version = {}) { SQLiteIndex index = CreateTestIndex(filePath, version); Manifest manifest; auto addFunc = [&](const IndexFields& d) { manifest.Id = d.Id; manifest.DefaultLocalization.Add(d.Name); manifest.DefaultLocalization.Add(d.Publisher); manifest.Moniker = d.Moniker; manifest.Version = d.Version; manifest.DefaultLocalization.Add(d.Tags); manifest.Installers.resize(std::max(d.PackageFamilyNames.size(), d.ProductCodes.size())); if (manifest.Installers.size() == 0) { manifest.Installers.push_back({}); } manifest.Channel = d.Channel; manifest.Installers[0].Commands = d.Commands; for (size_t i = 0; i < d.PackageFamilyNames.size(); ++i) { manifest.Installers[i].PackageFamilyName = d.PackageFamilyNames[i]; } for (size_t i = 0; i < d.ProductCodes.size(); ++i) { manifest.Installers[i].ProductCode = d.ProductCodes[i]; } if (!d.ArpName.empty() || !d.ArpPublisher.empty()) { manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayName = d.ArpName; manifest.Installers[0].AppsAndFeaturesEntries[0].Publisher = d.ArpPublisher; } index.AddManifest(manifest, d.Path); }; for (const auto& d : data) { addFunc(d); } return index; } bool ArePackageFamilyNameAndProductCodeSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); return (index.GetVersion() >= SQLiteVersion{ 1, 1 } && testVersion >= SQLiteVersion{ 1, 1 }); } bool AreNormalizedNameAndPublisherSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); return (index.GetVersion() >= SQLiteVersion{ 1, 2 } && testVersion >= SQLiteVersion{ 1, 2 }); } bool IsManifestMetadataSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); return (index.GetVersion() >= SQLiteVersion{ 1, 1 } && testVersion >= SQLiteVersion{ 1, 1 }); } bool AreManifestHashesSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); return (index.GetVersion() >= SQLiteVersion{ 1, 3 } && testVersion >= SQLiteVersion{ 1, 3 } && index.GetVersion() < SQLiteVersion{ 2, 0 }); } bool AreArpVersionsSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); return (index.GetVersion() >= SQLiteVersion{ 1, 5 } && testVersion >= SQLiteVersion{ 1, 5 }); } bool AreArpVersionsNullable(const SQLiteIndex& index) { UNSCOPED_INFO("Index " << index.GetVersion()); return (index.GetVersion() >= SQLiteVersion{ 2, 0 }); } bool IsMapDataFoldingSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); return (index.GetVersion() >= SQLiteVersion{ 1, 7 } && testVersion >= SQLiteVersion{ 1, 7 }); } bool IsMapDataFolded(const SQLiteIndex& index) { UNSCOPED_INFO("Index " << index.GetVersion()); return (index.GetVersion() >= SQLiteVersion{ 1, 7 }); } bool AreVersionKeysSupported(const SQLiteIndex& index) { UNSCOPED_INFO("Index " << index.GetVersion()); return (index.GetVersion() < SQLiteVersion{ 2, 0 }); } bool AreChannelsSupported(const SQLiteIndex& index) { UNSCOPED_INFO("Index " << index.GetVersion()); return (index.GetVersion() < SQLiteVersion{ 2, 0 }); } bool AreManifestPathsSupported(const SQLiteIndex& index) { UNSCOPED_INFO("Index " << index.GetVersion()); return (index.GetVersion() < SQLiteVersion{ 2, 0 }); } std::string GetPropertyStringByKey(const SQLiteIndex& index, rowid_t primaryId, PackageVersionProperty property) { auto result = index.GetPropertyByPrimaryId(primaryId, property); REQUIRE(result); return result.value(); } std::string GetPropertyStringByKey(const SQLiteIndex& index, rowid_t id, PackageVersionProperty property, std::string_view version, std::string_view channel) { if (AreVersionKeysSupported(index)) { auto manifestId = index.GetManifestIdByKey(id, version, channel); REQUIRE(manifestId); auto result = index.GetPropertyByPrimaryId(manifestId.value(), property); REQUIRE(result); return result.value(); } else { return GetPropertyStringByKey(index, id, property); } } std::string GetPropertyStringById(const SQLiteIndex& index, rowid_t id, PackageVersionProperty property) { if (AreVersionKeysSupported(index)) { auto versions = index.GetVersionKeysById(id); REQUIRE(!versions.empty()); return GetPropertyStringByKey(index, versions[0].ManifestId, property); } else { return GetPropertyStringByKey(index, id, property); } } std::string GetIdStringById(const SQLiteIndex& index, rowid_t id) { return GetPropertyStringById(index, id, PackageVersionProperty::Id); } std::string GetNameStringById(const SQLiteIndex& index, rowid_t id) { return GetPropertyStringById(index, id, PackageVersionProperty::Name); } std::string GetPathStringByKey(const SQLiteIndex& index, rowid_t id, std::string_view version, std::string_view channel) { return GetPropertyStringByKey(index, id, PackageVersionProperty::RelativePath, version, channel); } TEST_CASE("SQLiteIndexCreateLatestAndReopen", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteVersion versionCreated; // Create the index { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, SQLiteVersion::Latest()); versionCreated = index.GetVersion(); } // Reopen the index for read only { INFO("Trying with Read"); SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); SQLiteVersion versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } // Reopen the index for read/write { INFO("Trying with ReadWrite"); SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); SQLiteVersion versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } // Reopen the index for immutable read { INFO("Trying with Immutable"); SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Immutable); SQLiteVersion versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } } TEST_CASE("SQLiteIndexCreateAndAddManifest", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; std::string relativePath = "test/id/1.0.0.yaml"; SQLiteIndex index = SimpleTestSetup(tempFile, manifest, SQLiteVersion::Latest()); } TEST_CASE("SQLiteIndexCreateAndAddManifestFile", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile); TestDataFile manifestFile{ "Manifest-Good.yaml" }; std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; index.AddManifest(manifestFile, manifestPath); } TEST_CASE("SQLiteIndexCreateAndAddManifestDuplicate", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; SQLiteIndex index = SimpleTestSetup(tempFile, manifest); auto relativePath = GetPathFromManifest(manifest); // Attempting to add the same manifest at a different path should fail. REQUIRE_THROWS_HR(index.AddManifest(manifest, "differentpath.yaml"), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); // Attempting to add the same manifest with a differently cased Id at a different path should fail. manifest.Id = ToLower(manifest.Id); REQUIRE_THROWS_HR(index.AddManifest(manifest, "differentpath.yaml"), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); // Attempting to add a different manifest at the same path should fail. manifest.Id += "-new"; REQUIRE_THROWS_HR(index.AddManifest(manifest, relativePath), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); } TEST_CASE("SQLiteIndex_VersionReferencedByDependenciesClearsUnusedVersionAndKeepUsedVersion", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest, isolatedManifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); std::string dependencyOnlyVersion = "0.0.5"; manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, dependencyOnlyVersion)); index.AddManifest(manifest, GetPathFromManifest(manifest)); // Create a new manifest that depends on v0.0.5 auto& publisher4 = "Test4"; CreateFakeManifest(isolatedManifest, publisher4); isolatedManifest.Version = dependencyOnlyVersion; index.AddManifest(isolatedManifest); index.RemoveManifest(isolatedManifest); // After deletion that the version(v0.0.5) must be present because it still referenced via dependencies table. { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, dependencyOnlyVersion).has_value()); } index.RemoveManifest(manifest); // Now, that we've deleted the manifest depending on version(v0.0.5), it should be absent. { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); REQUIRE(!Schema::V1_0::VersionTable::SelectIdByValue(connection, dependencyOnlyVersion).has_value()); } index.RemoveManifest(dependencyManifest1); index.RemoveManifest(dependencyManifest2); // Final sanity check, nothing should be in the version table. { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); } } TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); index.AddManifest(manifest, GetPathFromManifest(manifest)); index.UpdateManifest(manifest, GetPathFromManifest(manifest)); index.RemoveManifest(manifest); } TEST_CASE("SQLiteIndex_AddManifestWithDependencies_MissingPackage", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); // Publisher2 is not present auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); REQUIRE_THROWS_HR(index.AddManifest(manifest, GetPathFromManifest(manifest)), APPINSTALLER_CLI_ERROR_MISSING_PACKAGE); } TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies_MissingVersion", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "0.0.1")); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "0.0.2")); index.AddManifest(manifest, GetPathFromManifest(manifest)); index.UpdateManifest(manifest, GetPathFromManifest(manifest)); index.RemoveManifest(manifest); } TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies_EmptyManifestVersion", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id)); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id)); index.AddManifest(manifest, GetPathFromManifest(manifest)); index.UpdateManifest(manifest, GetPathFromManifest(manifest)); index.RemoveManifest(manifest); } TEST_CASE("SQLiteIndex_DependenciesTable_CheckConsistency", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); { Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); } { // Open it directly to modify the table Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); rowid_t nonExistentRowId = 40; rowid_t nonExistentManifest = 41; rowid_t nonExistentVersion = 42; rowid_t nonExistentPackageId = 43; Builder::StatementBuilder builder; builder.InsertInto(Schema::V1_4::DependenciesTable::TableName()) .Values(nonExistentRowId, nonExistentManifest, nonExistentVersion, nonExistentPackageId); builder.Execute(connection); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); REQUIRE(!index.CheckConsistency(true)); } TempFile tempFile2{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile2.GetPath()); { SQLiteIndex index = CreateTestIndex(tempFile2, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "10.0"; index.AddManifest(manifest, "path"); REQUIRE(index.CheckConsistency(true)); // Add dependency that does not require min version Manifest manifestWithDependency1; manifestWithDependency1.Id = "Bar1"; manifestWithDependency1.Version = "10.0"; manifestWithDependency1.Installers.push_back({}); manifestWithDependency1.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, manifest.Id)); index.AddManifest(manifestWithDependency1, "path1"); REQUIRE(index.CheckConsistency(true)); // Add dependency with min version satisfied Manifest manifestWithDependency2; manifestWithDependency2.Id = "Bar2"; manifestWithDependency2.Version = "10.0"; manifestWithDependency2.Installers.push_back({}); manifestWithDependency2.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, manifest.Id, "1.0")); index.AddManifest(manifestWithDependency2, "path2"); REQUIRE(index.CheckConsistency(true)); // Add dependency with min version not satisfied Manifest manifestWithDependency3; manifestWithDependency3.Id = "Bar3"; manifestWithDependency3.Version = "10.0"; manifestWithDependency3.Installers.push_back({}); manifestWithDependency3.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, manifest.Id, "11.0")); index.AddManifest(manifestWithDependency3, "path3"); REQUIRE_FALSE(index.CheckConsistency(true)); } } TEST_CASE("SQLiteIndex_RemoveManifestFile_NotPresent", "[sqliteindex]") { SQLiteIndex index = CreateTestIndex(SQLITE_MEMORY_DB_CONNECTION_TARGET); TestDataFile manifestFile{ "Manifest-Good.yaml" }; std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; REQUIRE_THROWS_HR(index.RemoveManifest(manifestFile, manifestPath), E_NOT_SET); } TEST_CASE("SQLiteIndex_RemoveManifest", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; Manifest manifest1; manifest1.Installers.push_back({}); manifest1.Id = "test.id"; manifest1.DefaultLocalization.Add("Test Name"); manifest1.Moniker = "testmoniker"; manifest1.Version = "1.0.0"; manifest1.Channel = "test"; manifest1.DefaultLocalization.Add({ "t1", "t2" }); manifest1.Installers[0].Commands = { "test1", "test2" }; std::string manifest2Path = "test/woah/test.id-1.0.0.yaml"; Manifest manifest2; manifest2.Installers.push_back({}); manifest2.Id = "test.woah"; manifest2.DefaultLocalization.Add("Test Name WOAH"); manifest2.Moniker = "testmoniker"; manifest2.Version = "1.0.0"; manifest2.Channel = "test"; manifest2.DefaultLocalization.Add({}); manifest2.Installers[0].Commands = { "test1", "test2", "test3" }; { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); index.AddManifest(manifest1, manifest1Path); index.AddManifest(manifest2, manifest2Path); // Now remove manifest1 index.RemoveManifest(manifest1, manifest1Path); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); // Because manifest2 had no tags REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); // Now remove manifest2 index.RemoveManifest(manifest2, manifest2Path); } // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } TEST_CASE("SQLiteIndex_RemoveManifestWithDependencies", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); index.AddManifest(manifest, GetPathFromManifest(manifest)); index.RemoveManifest(manifest, GetPathFromManifest(manifest)); } TEST_CASE("SQLiteIndex_ValidateManifestWithDependencies", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); REQUIRE(PackageDependenciesValidation::ValidateManifestDependencies(&index, topLevelManifest)); } TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesHasLoops", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); index.AddManifest(topLevelManifest, GetPathFromManifest(topLevelManifest)); levelThreeManifest.Installers.push_back(ManifestInstaller{}); levelThreeManifest.Installers[1].Dependencies.Add(Dependency(DependencyType::Package, topLevelManifest.Id, "1.0.0")); REQUIRE_THROWS_HR( PackageDependenciesValidation::ValidateManifestDependencies(&index, levelThreeManifest), APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); } TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesMissingNode", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); // This node is missing, because it's not in the index. constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); REQUIRE_THROWS_HR( PackageDependenciesValidation::ValidateManifestDependencies(&index, topLevelManifest), APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); } TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesNoSuitableMinVersion", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "2.0.0")); REQUIRE_THROWS_HR( PackageDependenciesValidation::ValidateManifestDependencies(&index, topLevelManifest), APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); } TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureBroken", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); index.AddManifest(topLevelManifest, GetPathFromManifest(topLevelManifest)); REQUIRE_THROWS_HR( PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(&index, levelThreeManifest), APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); } TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureNotBroken", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest, levelThreeManifestV2; SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); index.AddManifest(topLevelManifest, GetPathFromManifest(topLevelManifest)); constexpr std::string_view levelThreeManifestV2Publisher = "Test"; CreateFakeManifest(levelThreeManifestV2, levelThreeManifestV2Publisher, "2.0.0"); index.AddManifest(levelThreeManifestV2, GetPathFromManifest(levelThreeManifestV2)); REQUIRE(PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(&index, levelThreeManifest)); } TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureBroken_NoSuitableOldManifest", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest, levelThreeManifestV2; SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelThreeManifestV2Publisher = "Test"; CreateFakeManifest(levelThreeManifestV2, levelThreeManifestV2Publisher, "2.0.0"); index.AddManifest(levelThreeManifestV2, GetPathFromManifest(levelThreeManifestV2)); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "2.0.0")); index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); index.AddManifest(topLevelManifest, GetPathFromManifest(topLevelManifest)); REQUIRE_THROWS( PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(&index, levelThreeManifestV2), APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); } TEST_CASE("SQLiteIndex_RemoveManifest_EnsureConsistentRowId", "[sqliteindex][V1_7]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; Manifest manifest1; manifest1.Installers.push_back({}); manifest1.Id = "test.id"; manifest1.DefaultLocalization.Add("Test Name"); manifest1.Moniker = "testmoniker"; manifest1.Version = "1.0.0"; manifest1.Channel = "test"; manifest1.DefaultLocalization.Add({ "t1", "t2" }); manifest1.Installers[0].Commands = { "test1", "test2" }; std::string manifest2Path = "test/woah/test.id-1.0.0.yaml"; Manifest manifest2; manifest2.Installers.push_back({}); manifest2.Id = "test.woah"; manifest2.DefaultLocalization.Add("Test Name WOAH"); manifest2.Moniker = "testmoniker"; manifest2.Version = "1.0.0"; manifest2.Channel = "test"; manifest2.DefaultLocalization.Add({}); manifest2.Installers[0].Commands = { "test1", "test2", "test3" }; SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion{ 1, 7 }); index.AddManifest(manifest1, manifest1Path); index.AddManifest(manifest2, manifest2Path); // Get the second manifest's id for validating consistency SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, manifest2.Id)); auto result = index.Search(request); REQUIRE(result.Matches.size() == 1); auto manifest2IdRowId = result.Matches[0].first; auto rowId = index.GetManifestIdByKey(manifest2IdRowId, {}, {}); REQUIRE(rowId); auto manifest2RowId = rowId.value(); // Now remove manifest1 and prepare index.RemoveManifest(manifest1, manifest1Path); index.PrepareForPackaging(); // Checking consistency will also uncover issues, but not potentially the same ones as below. REQUIRE(index.CheckConsistency(true)); // Repeat search to ensure consistent ids result = index.Search(request); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].first == manifest2IdRowId); rowId = index.GetManifestIdByKey(manifest2IdRowId, {}, {}); REQUIRE(rowId); REQUIRE(rowId.value() == manifest2RowId); REQUIRE(manifest2.Id == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Id)); REQUIRE(manifest2.DefaultLocalization.Get() == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Name)); REQUIRE(manifest2.Moniker == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Moniker)); REQUIRE(manifest2.Version == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Version)); REQUIRE(manifest2.Channel == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Channel)); REQUIRE(manifest2Path == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::RelativePath)); } TEST_CASE("SQLiteIndex_RemoveManifestFile", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); TestDataFile manifestFile{ "Manifest-Good.yaml" }; std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; index.AddManifest(manifestFile, manifestPath); // Now remove that manifest index.RemoveManifest(manifestFile, manifestPath); } // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } TEST_CASE("SQLiteIndex_UpdateManifest", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string manifestPath = "test/id/test.id-1.0.0.yaml"; Manifest manifest; manifest.Installers.push_back({}); manifest.Id = "test.id"; manifest.DefaultLocalization.Add < Localization::PackageName>("Test Name"); manifest.Moniker = "testmoniker"; manifest.Version = "1.0.0"; manifest.Channel = "test"; manifest.DefaultLocalization.Add({ "t1", "t2" }); manifest.Installers[0].Commands = { "test1", "test2" }; { auto version = GENERATE(SQLiteVersion{ 1, 0 }, SQLiteVersion::Latest()); SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, version); index.AddManifest(manifest, manifestPath); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); // Update with no updates should return false REQUIRE(!index.UpdateManifest(manifest, manifestPath)); manifest.DefaultLocalization.Add("description2"); // Update with no indexed updates should return false REQUIRE(!index.UpdateManifest(manifest, manifestPath)); // Update with indexed changes manifest.DefaultLocalization.Add("Test Name2"); manifest.Moniker = "testmoniker2"; manifest.DefaultLocalization.Add({ "t1", "t2", "t3" }); manifest.Installers[0].Commands = {}; REQUIRE(index.UpdateManifest(manifest, manifestPath)); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); // The update removed all commands REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); // Now remove manifest2 index.RemoveManifest(manifest, manifestPath); } // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } TEST_CASE("SQLiteIndex_UpdateManifestWithDependencies", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest, updateManifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); const std::string dependencyPath3 = GetPathFromManifest(manifest); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); index.AddManifest(manifest, dependencyPath3); auto& publisher4 = "Test4"; CreateFakeManifest(updateManifest, publisher4); index.AddManifest(updateManifest, GetPathFromManifest(updateManifest)); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, updateManifest.Id, "1.0.0")); REQUIRE(index.UpdateManifest(manifest, dependencyPath3)); } TEST_CASE("SQLiteIndex_UpdateManifestWithDependenciesDeleteAndAdd", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest, updateManifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); const std::string dependencyPath3 = GetPathFromManifest(manifest); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); index.AddManifest(manifest, dependencyPath3); manifest.Installers[0].Dependencies.Clear(); auto& publisher4 = "Test4"; CreateFakeManifest(updateManifest, publisher4); index.AddManifest(updateManifest, GetPathFromManifest(updateManifest)); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, updateManifest.Id, "1.0.0")); REQUIRE(index.UpdateManifest(manifest, dependencyPath3)); } TEST_CASE("SQLiteIndex_UpdateManifestChangePath", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string manifestPath = "test/id/test.id-1.0.0.yaml"; Manifest manifest; manifest.Installers.push_back({}); manifest.Id = "test.id"; manifest.DefaultLocalization.Add("Test Name"); manifest.Moniker = "testmoniker"; manifest.Version = "1.0.0"; manifest.Channel = "test"; manifest.DefaultLocalization.Add({ "t1", "t2" }); manifest.Installers[0].Commands = { "test1", "test2" }; { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); index.AddManifest(manifest, manifestPath); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); manifestPath = "test/newid/test.newid-1.0.0.yaml"; // Update with path update should indicate change REQUIRE(index.UpdateManifest(manifest, manifestPath)); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); // Now remove manifest, with unknown path index.RemoveManifest(manifest, ""); } // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } TEST_CASE("SQLiteIndex_UpdateManifest_Pathless", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; manifest.Installers.push_back({}); manifest.Id = "test.id"; manifest.DefaultLocalization.Add < Localization::PackageName>("Test Name"); manifest.Moniker = "testmoniker"; manifest.Version = "1.0.0"; manifest.Channel = "test"; manifest.DefaultLocalization.Add({ "t1", "t2" }); manifest.Installers[0].Commands = { "test1", "test2" }; { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); index.AddManifest(manifest); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); // Update with no updates should return false REQUIRE(!index.UpdateManifest(manifest)); manifest.DefaultLocalization.Add("description2"); // Update with no indexed updates should return false REQUIRE(!index.UpdateManifest(manifest)); // Update with indexed changes manifest.DefaultLocalization.Add("Test Name2"); manifest.Moniker = "testmoniker2"; manifest.DefaultLocalization.Add({ "t1", "t2", "t3" }); manifest.Installers[0].Commands = {}; REQUIRE(index.UpdateManifest(manifest)); } { // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); // The update removed all commands REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); // Now remove manifest2 index.RemoveManifest(manifest); } // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } TEST_CASE("SQLiteIndex_UpdateManifestChangeCase", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string manifestPath = "test/id/test.id-1.0.0.yaml"; Manifest manifest; manifest.Installers.push_back({}); manifest.Id = "test.id"; manifest.DefaultLocalization.Add("Test Name"); manifest.Moniker = "testmoniker"; manifest.Version = "1.0.0-test"; manifest.Channel = "test"; manifest.DefaultLocalization.Add({ "t1", "t2" }); manifest.Installers[0].Commands = { "test1", "test2" }; { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); index.AddManifest(manifest, manifestPath); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); manifest.Id = "Test.Id"; // Update with path update should indicate change REQUIRE(index.UpdateManifest(manifest, manifestPath)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); manifest.Version = "1.0.0-Test"; // Update with path update should indicate change REQUIRE(index.UpdateManifest(manifest, manifestPath)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); manifest.Channel = "Test"; // Update with path update should indicate change REQUIRE(index.UpdateManifest(manifest, manifestPath)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); manifest.DefaultLocalization.Add("test name"); // Update with path update should indicate change REQUIRE(index.UpdateManifest(manifest, manifestPath)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); // Now remove manifest, with unknown path index.RemoveManifest(manifest, ""); } // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } TEST_CASE("SQLiteIndex_IdCaseInsensitivity", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; Manifest manifest1; manifest1.Installers.push_back({}); manifest1.Id = "test.id"; manifest1.DefaultLocalization.Add("Test Name"); manifest1.Moniker = "testmoniker"; manifest1.Version = "1.0.0"; manifest1.DefaultLocalization.Add({ "t1", "t2" }); manifest1.Installers[0].Commands = { "test1", "test2" }; std::string manifest2Path = "test/id/test.id-2.0.0.yaml"; Manifest manifest2 = manifest1; manifest2.Id = "Test.Id"; manifest1.Version = "2.0.0"; { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); index.AddManifest(manifest1, manifest1Path); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.AddManifest(manifest2, manifest2Path); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); REQUIRE(manifest2.Id == GetIdStringById(index, results.Matches[0].first)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); manifest1.Id = "TEST.ID"; REQUIRE(index.UpdateManifest(manifest1, manifest1Path)); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.RemoveManifest(manifest1, manifest1Path); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.RemoveManifest(manifest2, manifest2Path); auto results = index.Search({}); REQUIRE(results.Matches.empty()); } // Open it directly to directly test table state Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } TEST_CASE("PathPartTable_EnsurePathExists_Negative_Paths", "[sqliteindex][V1_0]") { // Open it directly to directly test pathpart table Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"()", false), E_INVALIDARG); REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(\)", false), E_INVALIDARG); REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(/)", false), E_INVALIDARG); REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(C:)", false), E_INVALIDARG); REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(C:\\)", false), E_INVALIDARG); REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(C:\temp\path\file.txt)", false), E_INVALIDARG); REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(\temp\path\file.txt)", false), E_INVALIDARG); } TEST_CASE("PathPartTable_EnsurePathExists", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); // Create the index { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); SQLiteVersion versionCreated = index.GetVersion(); REQUIRE(versionCreated == SQLiteVersion{ 1, 0 }); } // Open it directly to directly test pathpart table Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); // attempt to find path that doesn't exist auto result0 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\c.txt)", false); REQUIRE(!std::get<0>(result0)); // add path auto result1 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\c.txt)", true); REQUIRE(std::get<0>(result1)); // Second time trying to create should return false and same id auto result2 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\c.txt)", true); REQUIRE(!std::get<0>(result2)); REQUIRE(std::get<1>(result1) == std::get<1>(result2)); // Trying to find but not create should return true because it exists auto result3 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\c.txt)", false); REQUIRE(std::get<0>(result3)); REQUIRE(std::get<1>(result1) == std::get<1>(result3)); // attempt to find a different file auto result4 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\d.txt)", false); REQUIRE(!std::get<0>(result4)); // add a different file auto result5 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\d.txt)", true); REQUIRE(std::get<0>(result5)); REQUIRE(std::get<1>(result1) != std::get<1>(result5)); // add the same file but deeper auto result6 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\d\c.txt)", true); REQUIRE(std::get<0>(result6)); REQUIRE(std::get<1>(result1) != std::get<1>(result6)); // get the deeper file with extra separators auto result7 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\\b\d\\c.txt)", true); REQUIRE(!std::get<0>(result7)); REQUIRE(std::get<1>(result6) == std::get<1>(result7)); } TEST_CASE("SQLiteIndex_PrepareForPackaging", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile); TestDataFile manifestFile{ "Manifest-Good.yaml" }; std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; index.AddManifest(manifestFile, manifestPath); index.PrepareForPackaging(); } TEST_CASE("SQLiteIndex_Search_IdExactMatch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; std::string relativePath = "test/id/1.0.0.yaml"; SQLiteIndex index = SimpleTestSetup(tempFile, manifest); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].second.Field == PackageMatchField::Id); REQUIRE(results.Matches[0].second.Type == MatchType::Exact); REQUIRE(results.Matches[0].second.Value == manifest.Id); } TEST_CASE("SQLiteIndex_Search_MultipleMatch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; std::string relativePath = "test/id/1.0.0.yaml"; SQLiteIndex index = SimpleTestSetup(tempFile, manifest); manifest.Version = "2.0.0"; index.AddManifest(manifest, relativePath + "2"); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); if (AreVersionKeysSupported(index)) { auto result = index.GetVersionKeysById(results.Matches[0].first); REQUIRE(result.size() == 2); } else { REQUIRE_THROWS_HR(index.GetVersionKeysById(results.Matches[0].first), E_NOT_VALID_STATE); } } TEST_CASE("SQLiteIndex_Search_NoMatch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; SQLiteIndex index = SimpleTestSetup(tempFile, manifest); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, "THIS DOES NOT MATCH ANYTHING!"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 0); } TEST_CASE("SQLiteIndex_IdString", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; SQLiteIndex index = SimpleTestSetup(tempFile, manifest); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = GetIdStringById(index, results.Matches[0].first); REQUIRE(result == manifest.Id); } TEST_CASE("SQLiteIndex_NameString", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; SQLiteIndex index = SimpleTestSetup(tempFile, manifest); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = GetNameStringById(index, results.Matches[0].first); REQUIRE(result == manifest.DefaultLocalization.Get()); } TEST_CASE("SQLiteIndex_PathString", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; SQLiteIndex index = SimpleTestSetup(tempFile, manifest); auto relativePath = GetPathFromManifest(manifest); TestPrepareForRead(index); if (!AreManifestPathsSupported(index)) { return; } SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto specificResult = GetPathStringByKey(index, results.Matches[0].first, manifest.Version, manifest.Channel); REQUIRE(specificResult == relativePath); auto latestResult = GetPathStringByKey(index, results.Matches[0].first, "", manifest.Channel); REQUIRE(latestResult == relativePath); } TEST_CASE("SQLiteIndex_PathlessString", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; std::string relativePath; SQLiteIndex index = CreateTestIndex(tempFile); CreateFakeManifest(manifest, "Test"); index.AddManifest(manifest); TestPrepareForRead(index); if (!AreManifestPathsSupported(index)) { return; } SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto specificResult = GetPathStringByKey(index, results.Matches[0].first, manifest.Version, manifest.Channel); REQUIRE(specificResult == relativePath); auto latestResult = GetPathStringByKey(index, results.Matches[0].first, "", manifest.Channel); REQUIRE(latestResult == relativePath); } TEST_CASE("SQLiteIndex_Versions", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; std::string relativePath = "test/id/1.0.0.yaml"; SQLiteIndex index = SimpleTestSetup(tempFile, manifest); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); if (AreVersionKeysSupported(index)) { auto result = index.GetVersionKeysById(results.Matches[0].first); REQUIRE(result.size() == 1); REQUIRE(result[0].VersionAndChannel.GetVersion().ToString() == manifest.Version); REQUIRE(result[0].VersionAndChannel.GetChannel().ToString() == manifest.Channel); } else { REQUIRE_THROWS_HR(index.GetVersionKeysById(results.Matches[0].first), E_NOT_VALID_STATE); } } TEST_CASE("SQLiteIndex_Search_VersionSorting", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::vector sortedList = { { UtilityVersion("15.0.0"), Channel("") }, { UtilityVersion("14.0.0"), Channel("") }, { UtilityVersion("13.2.0"), Channel("") }, { UtilityVersion("13.2.0-bugfix"), Channel("") }, { UtilityVersion("13.0.0"), Channel("") }, { UtilityVersion("16.0.0"), Channel("alpha") }, { UtilityVersion("15.8.0"), Channel("alpha") }, { UtilityVersion("15.1.0"), Channel("beta") }, }; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Moniker", "14.0.0", "", { "foot" }, { "com34" }, "Path1" }, { "Id", "Name", "Moniker", "16.0.0", "alpha", { "floor" }, { "com3" }, "Path2" }, { "Id", "Name", "Moniker", "15.0.0", "", {}, { "Command" }, "Path3" }, { "Id", "Name", "Moniker", "13.2.0", "", {}, { "Command" }, "Path4" }, { "Id", "Name", "Moniker", "15.1.0", "beta", { "foo" }, { "com3" }, "Path5" }, { "Id", "Name", "Moniker", "15.8.0", "alpha", { "foo" }, { "com3" }, "Path6" }, { "Id", "Name", "Moniker", "13.2.0-bugfix", "", { "foo" }, { "com3" }, "Path7" }, { "Id", "Name", "Moniker", "13.0.0", "", { "foo" }, { "com3" }, "Path8" }, }); TestPrepareForRead(index); if (!AreChannelsSupported(index)) { return; } SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = index.GetVersionKeysById(results.Matches[0].first); REQUIRE(result.size() == sortedList.size()); for (size_t i = 0; i < result.size(); ++i) { const VersionAndChannel& sortedVAC = sortedList[i]; const VersionAndChannel& resultVAC = result[i].VersionAndChannel; INFO(i); REQUIRE(sortedVAC.GetVersion().ToString() == resultVAC.GetVersion().ToString()); REQUIRE(sortedVAC.GetChannel().ToString() == resultVAC.GetChannel().ToString()); } } TEST_CASE("SQLiteIndex_PathString_VersionSorting", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::vector sortedList = { { UtilityVersion("15.0.0"), Channel("") }, { UtilityVersion("14.0.0"), Channel("") }, { UtilityVersion("13.2.0"), Channel("") }, { UtilityVersion("13.2.0-bugfix"), Channel("") }, { UtilityVersion("13.0.0"), Channel("") }, { UtilityVersion("16.0.0"), Channel("alpha") }, { UtilityVersion("15.8.0"), Channel("alpha") }, { UtilityVersion("15.1.0"), Channel("beta") }, }; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Moniker", "14.0.0", "", { "foot" }, { "com34" }, "Path1" }, { "Id", "Name", "Moniker", "16.0.0", "alpha", { "floor" }, { "com3" }, "Path2" }, { "Id", "Name", "Moniker", "15.0.0", "", {}, { "Command" }, "Path3" }, { "Id", "Name", "Moniker", "13.2.0", "", {}, { "Command" }, "Path4" }, { "Id", "Name", "Moniker", "15.1.0", "beta", { "foo" }, { "com3" }, "Path5" }, { "Id", "Name", "Moniker", "15.8.0", "alpha", { "foo" }, { "com3" }, "Path6" }, { "Id", "Name", "Moniker", "13.2.0-bugfix", "", { "foo" }, { "com3" }, "Path7" }, { "Id", "Name", "Moniker", "13.0.0", "", { "foo" }, { "com3" }, "Path8" }, }); TestPrepareForRead(index); if (!AreChannelsSupported(index)) { return; } SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = GetPathStringByKey(index, results.Matches[0].first, "", ""); REQUIRE(result == "Path3"); result = GetPathStringByKey(index, results.Matches[0].first, "", "alpha"); REQUIRE(result == "Path2"); result = GetPathStringByKey(index, results.Matches[0].first, "", "beta"); REQUIRE(result == "Path5"); auto nonResult = index.GetManifestIdByKey(results.Matches[0].first, "", "gamma"); REQUIRE(!nonResult.has_value()); } TEST_CASE("SQLiteIndex_PathString_CaseInsensitive", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Moniker", "14.0.0", "", { "foot" }, { "com34" }, "Path1" }, { "Id", "Name", "Moniker", "16.0.0", "alpha", { "floor" }, { "com3" }, "Path2" }, { "Id", "Name", "Moniker", "15.0.0", "", {}, { "Command" }, "Path3" }, { "Id", "Name", "Moniker", "13.2.0-BUGFIX", "", {}, { "Command" }, "Path4" }, { "Id", "Name", "Moniker", "15.1.0", "beta", { "foo" }, { "com3" }, "Path5" }, { "Id", "Name", "Moniker", "15.8.0", "alpha", { "foo" }, { "com3" }, "Path6" }, { "Id", "Name", "Moniker", "13.2.0-bugfix", "beta", { "foo" }, { "com3" }, "Path7" }, { "Id", "Name", "Moniker", "13.0.0", "", { "foo" }, { "com3" }, "Path8" }, }); TestPrepareForRead(index); if (!AreChannelsSupported(index)) { return; } SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = index.GetManifestIdByKey(results.Matches[0].first, "", "Alpha"); REQUIRE(result.has_value()); result = index.GetManifestIdByKey(results.Matches[0].first, "13.2.0-BugFix", ""); REQUIRE(result.has_value()); result = index.GetManifestIdByKey(results.Matches[0].first, "13.2.0-BugFix", "BETA"); REQUIRE(result.has_value()); } TEST_CASE("SQLiteIndex_SearchResultsTableSearches", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; { (void)SimpleTestSetup(tempFile, manifest, SQLiteVersion{ 1, 0 }); } Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); Schema::V1_0::SearchResultsTable search(connection); std::string value = "test"; // Perform every type of field and match search PackageMatchFilter filter(PackageMatchField::Id, MatchType::Exact, value); for (auto field : { PackageMatchField::Id, PackageMatchField::Name, PackageMatchField::Moniker, PackageMatchField::Tag, PackageMatchField::Command }) { filter.Field = field; for (auto match : { MatchType::Exact, MatchType::Fuzzy, MatchType::FuzzySubstring, MatchType::Substring, MatchType::Wildcard }) { filter.Type = match; search.SearchOnField(filter); } } } TEST_CASE("SQLiteIndex_Search_EmptySearch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile,{ { "Id1", "Name", "Moniker", "Version1", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id1", "Name", "Moniker", "Version2", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, { "Id3", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path4" }, }); TestPrepareForRead(index); SearchRequest request; auto results = index.Search(request); REQUIRE(results.Matches.size() == 3); } TEST_CASE("SQLiteIndex_Search_Exact", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, }); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); } TEST_CASE("SQLiteIndex_Search_Substring", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, }); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); } TEST_CASE("SQLiteIndex_Search_ExactBeforeSubstring", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, }); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "Id"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); REQUIRE(GetIdStringById(index, results.Matches[0].first) == "Id"); REQUIRE(GetIdStringById(index, results.Matches[1].first) == "Id2"); } TEST_CASE("SQLiteIndex_Search_SingleFilter", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.Filters.emplace_back(PackageMatchField::Name, MatchType::Substring, "a"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); request.Filters[0].Value = "e"; results = index.Search(request); REQUIRE(results.Matches.size() == 1); } TEST_CASE("SQLiteIndex_Search_Multimatch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id1", "Name1", "Moniker", "Version1", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id2", "Name", "Moniker", "Version", "", { "Tag" }, { "Command" }, "Path3" }, { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path4" }, { "Id3", "Name", "Moniker", "Version1", "", { "Tag" }, { "Command" }, "Path5" }, { "Id3", "Name", "Moniker", "Version2", "", { "Tag" }, { "Command" }, "Path6" }, { "Id3", "Name", "Moniker", "Version3", "", { "Tag" }, { "Command" }, "Path7" }, }); TestPrepareForRead(index); SearchRequest request; // An empty string should match all substrings request.Query = RequestMatch(MatchType::Substring, ""); auto results = index.Search(request); REQUIRE(results.Matches.size() == 3); } TEST_CASE("SQLiteIndex_Search_QueryAndFilter", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "Id"); request.Filters.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = GetIdStringById(index, results.Matches[0].first); REQUIRE(result == "Id2"); } TEST_CASE("SQLiteIndex_Search_QueryAndMultipleFilters", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name", "Moniker", "Version", "Channel", { "foot" }, { "com34" }, "Path1" }, { "Id1", "Name1", "Moniker", "Version1", "Channel", { "floor" }, { "com3" }, "Path2" }, { "Id2", "Name", "Moniker", "Version", "", {}, { "Command" }, "Path3" }, { "Id2", "Name", "Moniker", "Version", "Channel", {}, { "Command" }, "Path4" }, { "Id3", "Tagit", "Moniker", "Version1", "", { "foo" }, { "com3" }, "Path5" }, { "Id3", "Tagit", "Moniker", "Version2", "", { "foo" }, { "com3" }, "Path6" }, { "Id3", "Tagit", "new", "Version3", "", { "foo" }, { "com3" }, "Path7" }, }); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "tag"); request.Filters.emplace_back(PackageMatchField::Command, MatchType::Exact, "com3"); request.Filters.emplace_back(PackageMatchField::Tag, MatchType::Substring, "foo"); request.Filters.emplace_back(PackageMatchField::Moniker, MatchType::Substring, "new"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = GetIdStringById(index, results.Matches[0].first); REQUIRE(result == "Id3"); } TEST_CASE("SQLiteIndex_Search_SimpleICULike", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); // Insert decomposed character: [upper] A + umlaut SQLiteIndex index = SearchTestSetup(tempFile, { { u8"\x41\x308wesomeApp", "HasUmlaut", "Moniker", "Version", "Channel", { "foot" }, { "com34" }, "Path1" }, { u8"AwesomeApp", "Nope", "Moniker", "Version", "Channel", { "foot" }, { "com34" }, "Path2" }, }); TestPrepareForRead(index); SearchRequest request; // Search for anything containing: [lower] a + umlaut request.Filters.emplace_back(PackageMatchField::Id, MatchType::Substring, u8"\xE4"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = GetNameStringById(index, results.Matches[0].first); REQUIRE(result == "HasUmlaut"); } TEST_CASE("SQLiteIndex_Search_MaximumResults_Equal", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.MaximumResults = 3; auto results = index.Search(request); REQUIRE(results.Matches.size() == 3); REQUIRE(!results.Truncated); } TEST_CASE("SQLiteIndex_Search_MaximumResults_Less", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.MaximumResults = 2; auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); REQUIRE(results.Truncated); } TEST_CASE("SQLiteIndex_Search_MaximumResults_Greater", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.MaximumResults = 4; auto results = index.Search(request); REQUIRE(results.Matches.size() == 3); REQUIRE(!results.Truncated); } TEST_CASE("SQLiteIndex_Search_QueryAndInclusion", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::CaseInsensitive, "id3"); request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 3); } TEST_CASE("SQLiteIndex_Search_InclusionOnly", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); } TEST_CASE("SQLiteIndex_Search_InclusionAndFilter", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); request.Filters.emplace_back(PackageMatchField::Name, MatchType::CaseInsensitive, "name"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto result = GetIdStringById(index, results.Matches[0].first); REQUIRE(result == "Nope"); } TEST_CASE("SQLiteIndex_Search_QueryInclusionAndFilter", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "monicka", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, { "Id3", "No", "moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "id3"); request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "na"); request.Filters.emplace_back(PackageMatchField::Moniker, MatchType::CaseInsensitive, "MONIKER"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); } TEST_CASE("SQLiteIndex_Search_CaseInsensitive", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Nope", "id3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::CaseInsensitive, "id3"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 3); } TEST_CASE("SQLiteIndex_Search_StartsWith", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "NopeId", "id3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, { "Id2", "Na", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2" }, { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, }); TestPrepareForRead(index); SearchRequest request; request.Inclusions.push_back(PackageMatchFilter(PackageMatchField::Id, MatchType::StartsWith, "id")); auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); } TEST_CASE("SQLiteIndex_Search_Query_PackageFamilyNameSubstring", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "PFN"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 0); } TEST_CASE("SQLiteIndex_Search_Query_ProductCodeSubstring", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "PC"); auto results = index.Search(request); REQUIRE(results.Matches.size() == 0); } TEST_CASE("SQLiteIndex_Search_Query_PackageFamilyNameMatch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "pfn1"); auto results = index.Search(request); if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 1); } else { REQUIRE(results.Matches.size() == 0); } } TEST_CASE("SQLiteIndex_Search_Query_ProductCodeMatch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "pc2"); auto results = index.Search(request); if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 1); } else { REQUIRE(results.Matches.size() == 0); } } TEST_CASE("SQLiteIndex_Search_PackageFamilyNameSubstring", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::PackageFamilyName, MatchType::Substring, "PFN"); auto results = index.Search(request); if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 3); } else { REQUIRE(results.Matches.size() == 0); } } TEST_CASE("SQLiteIndex_Search_ProductCodeSubstring", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::ProductCode, MatchType::Substring, "PC"); auto results = index.Search(request); if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 3); } else { REQUIRE(results.Matches.size() == 0); } } TEST_CASE("SQLiteIndex_Search_PackageFamilyNameMatch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::PackageFamilyName, MatchType::Exact, "pfn1"); auto results = index.Search(request); if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 1); } else { REQUIRE(results.Matches.size() == 0); } } TEST_CASE("SQLiteIndex_Search_ProductCodeMatch", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::ProductCode, MatchType::Exact, "pc2"); auto results = index.Search(request); if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 1); } else { REQUIRE(results.Matches.size() == 0); } } TEST_CASE("SQLiteIndex_CheckConsistency_Failure", "[sqliteindex][V1_1]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; Manifest manifest1; manifest1.Installers.push_back({}); manifest1.Id = "test.id"; manifest1.DefaultLocalization.Add("Test Name"); manifest1.Moniker = "testmoniker"; manifest1.Version = "1.0.0"; manifest1.Channel = "test"; manifest1.DefaultLocalization.Add({ "t1", "t2" }); manifest1.Installers[0].Commands = { "test1", "test2" }; std::string manifest2Path = "test/woah/test.id-1.0.0.yaml"; Manifest manifest2; manifest2.Installers.push_back({}); manifest2.Id = "test.woah"; manifest2.DefaultLocalization.Add("Test Name WOAH"); manifest2.Moniker = "testmoniker"; manifest2.Version = "1.0.0"; manifest2.Channel = "test"; manifest2.DefaultLocalization.Add({}); manifest2.Installers[0].Commands = { "test1", "test2", "test3" }; rowid_t manifestRowId = 0; { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 1 }); index.AddManifest(manifest1, manifest1Path); index.AddManifest(manifest2, manifest2Path); // Get the first manifest's id for removal SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, manifest1.Id)); auto result = index.Search(request); REQUIRE(result.Matches.size() == 1); manifestRowId = result.Matches[0].first; } { // Open it directly to modify the table Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); Builder::StatementBuilder builder; builder.DeleteFrom(Schema::V1_0::IdTable::TableName()).Where(RowIDName).Equals(manifestRowId); builder.Execute(connection); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); REQUIRE(!index.CheckConsistency(true)); } } TEST_CASE("SQLiteIndex_GetMultiProperty_PackageFamilyName", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1", "PFN2" }, {} }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto props = index.GetMultiPropertyByPrimaryId(results.Matches[0].first, PackageVersionMultiProperty::PackageFamilyName); if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) { REQUIRE(props.size() == 2); REQUIRE(std::find(props.begin(), props.end(), FoldCase("PFN1"sv)) != props.end()); REQUIRE(std::find(props.begin(), props.end(), FoldCase("PFN2"sv)) != props.end()); } else { REQUIRE(props.empty()); } } TEST_CASE("SQLiteIndex_GetMultiProperty_ProductCode", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto props = index.GetMultiPropertyByPrimaryId(results.Matches[0].first, PackageVersionMultiProperty::ProductCode); if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) { REQUIRE(props.size() == 2); REQUIRE(std::find(props.begin(), props.end(), FoldCase("PC1"sv)) != props.end()); REQUIRE(std::find(props.begin(), props.end(), FoldCase("PC2"sv)) != props.end()); } else { REQUIRE(props.empty()); } } TEST_CASE("SQLiteIndex_GetMultiProperty_Tag", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag1", "Tag2" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto props = index.GetMultiPropertyByPrimaryId(results.Matches[0].first, PackageVersionMultiProperty::Tag); REQUIRE(props.size() == 2); REQUIRE(std::find(props.begin(), props.end(), "Tag1") != props.end()); REQUIRE(std::find(props.begin(), props.end(), "Tag2") != props.end()); } TEST_CASE("SQLiteIndex_GetMultiProperty_Command", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag1", "Tag2" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; auto results = index.Search(request); REQUIRE(results.Matches.size() == 1); auto props = index.GetMultiPropertyByPrimaryId(results.Matches[0].first, PackageVersionMultiProperty::Command); REQUIRE(props.size() == 1); REQUIRE(props[0] == "Command"); } TEST_CASE("SQLiteIndex_ManifestMetadata", "[sqliteindex][V1_7]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, { "Id2", "Name2", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2", { "PFN1", "PFN2" }, {} }, }, SQLiteVersion{ 1, 7 }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; auto results = index.Search(request); REQUIRE(results.Matches.size() == 2); for (const auto [id, match] : results.Matches) { REQUIRE(index.GetMetadataByManifestId(id).empty()); } auto manifestId1 = results.Matches[0].first; auto manifestId2 = results.Matches[1].first; std::string metadataValue = "data about data"; index.SetMetadataByManifestId(manifestId1, PackageVersionMetadata::InstalledType, metadataValue); if (IsManifestMetadataSupported(index, testVersion)) { auto metadataResult = index.GetMetadataByManifestId(manifestId1); REQUIRE(metadataResult.size() == 1); REQUIRE(metadataResult[0].first == PackageVersionMetadata::InstalledType); REQUIRE(metadataResult[0].second == metadataValue); } else { REQUIRE(index.GetMetadataByManifestId(manifestId1).empty()); } REQUIRE(index.GetMetadataByManifestId(manifestId2).empty()); } TEST_CASE("SQLiteIndex_NormNameAndPublisher_Exact", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string testName = "Name"; std::string testPublisher = "Publisher"; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName, testPublisher)); auto results = index.Search(request); if (AreNormalizedNameAndPublisherSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 1); } else { REQUIRE(results.Matches.empty()); } } TEST_CASE("SQLiteIndex_NormNameAndPublisher_Simple", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string testName = "Name"; std::string testPublisher = "Publisher"; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName + " 1.0", testPublisher + " Corporation")); auto results = index.Search(request); if (AreNormalizedNameAndPublisherSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 1); } else { REQUIRE(results.Matches.empty()); } } TEST_CASE("SQLiteIndex_NormNameAndPublisher_Complex", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string testName = "Name"; std::string testPublisher = "Publisher"; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, { "Id2", testName, "Different Publisher", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2", {}, { "PC1", "PC2" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName + " 1.0", testPublisher)); auto results = index.Search(request); if (AreNormalizedNameAndPublisherSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 1); } else { REQUIRE(results.Matches.empty()); } } TEST_CASE("SQLiteIndex_NormNameAndPublisher_AppsAndFeatures", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string testName = "Name"; std::string testPublisher = "Publisher"; std::string arpTestName = "Other Thing"; std::string arpTestPublisher = "Big Company Name"; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" }, arpTestName, arpTestPublisher }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, arpTestName, arpTestPublisher)); auto results = index.Search(request); if (AreNormalizedNameAndPublisherSupported(index, testVersion)) { REQUIRE(results.Matches.size() == 1); } else { REQUIRE(results.Matches.empty()); } } TEST_CASE("SQLiteIndex_ManifestHash_Present", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); uint8_t data[4] = { 1, 2, 3, 4 }; SHA256::HashBuffer hash = SHA256::ComputeHash(data, sizeof(data)); SQLiteIndex index = CreateTestIndex(tempFile); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "Bar"; manifest.StreamSha256 = hash; index.AddManifest(manifest, "path"); SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); auto hashResult = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ManifestSHA256Hash); // Regardless of what hash, it should still be a SHA256 hash REQUIRE(hashResult); auto hashResultBytes = SHA256::ConvertToBytes(hashResult.value()); REQUIRE(hash.size() == hashResultBytes.size()); if (AreManifestHashesSupported(index, testVersion)) { REQUIRE(std::equal(hash.begin(), hash.end(), hashResultBytes.begin())); } } TEST_CASE("SQLiteIndex_ManifestHash_Missing", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "Bar"; index.AddManifest(manifest, "path"); SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); auto hashResult = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ManifestSHA256Hash); if (AreManifestHashesSupported(index, testVersion)) { REQUIRE(!hashResult); } } TEST_CASE("SQLiteIndex_ManifestArpVersion_Present_Add", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "Bar"; manifest.Installers.push_back({}); manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; index.AddManifest(manifest, "path"); SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); auto arpMin = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMinVersion); auto arpMax = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMaxVersion); if (AreArpVersionsSupported(index, testVersion)) { REQUIRE(arpMin); REQUIRE(UtilityVersion(arpMin.value()) == UtilityVersion(manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion)); REQUIRE(arpMax); REQUIRE(UtilityVersion(arpMax.value()) == UtilityVersion(manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion)); } else { REQUIRE_FALSE(arpMin); REQUIRE_FALSE(arpMax); } } TEST_CASE("SQLiteIndex_ManifestArpVersion_Present_AddThenUpdate", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "Bar"; manifest.Installers.push_back({}); manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; index.AddManifest(manifest, "path"); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.1"; index.UpdateManifest(manifest, "path"); SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); auto arpMin = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMinVersion); auto arpMax = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMaxVersion); if (AreArpVersionsSupported(index, testVersion)) { REQUIRE(arpMin); REQUIRE(arpMin.value() == "1.1"); REQUIRE(arpMax); REQUIRE(arpMax.value() == "1.1"); } else { REQUIRE_FALSE(arpMin); REQUIRE_FALSE(arpMax); } } TEST_CASE("SQLiteIndex_ManifestArpVersion_Empty", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "Bar"; index.AddManifest(manifest, "path"); SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); auto arpMin = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMinVersion); auto arpMax = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMaxVersion); if (AreArpVersionsSupported(index, testVersion) && !AreArpVersionsNullable(index)) { REQUIRE(arpMin); REQUIRE(arpMin.value() == ""); REQUIRE(arpMax); REQUIRE(arpMax.value() == ""); } else { REQUIRE_FALSE(arpMin); REQUIRE_FALSE(arpMax); } } TEST_CASE("SQLiteIndex_RemoveManifestArpVersionKeepUsedDeleteUnused", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "10.0"; manifest.Installers.push_back({}); manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; index.AddManifest(manifest, "path"); Manifest manifest2; manifest2.Id = "Foo2"; manifest2.Version = "1.0"; manifest2.Installers.push_back({}); manifest2.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifest2.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest2.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "10.0"; index.AddManifest(manifest2, "path2"); // Before removing, "10.0", "1.0" and "1.1" should all exist. { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "10.0").has_value()); REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "1.0").has_value()); REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "1.1").has_value()); } index.RemoveManifest(manifest); // After removing the first manifest, "10.0" and "1.0" should still stay, "1.1" should be removed. { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "10.0").has_value()); REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "1.0").has_value()); REQUIRE_FALSE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "1.1").has_value()); } } TEST_CASE("SQLiteIndex_ManifestArpVersionConflict_AddThrows", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "10.0"; manifest.DefaultLocalization.Add("ArpVersionCheckConsistencyTest"); manifest.Moniker = "testmoniker"; manifest.Installers.push_back({}); manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; index.AddManifest(manifest, "path"); REQUIRE(index.CheckConsistency(true)); // Add a conflicting one manifest.Version = "10.1"; REQUIRE_THROWS_HR(index.AddManifest(manifest, "path2"), APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); } TEST_CASE("SQLiteIndex_ManifestArpVersionConflict_UpdateThrows", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "10.0"; manifest.DefaultLocalization.Add("ArpVersionCheckConsistencyTest"); manifest.Moniker = "testmoniker"; manifest.Installers.push_back({}); manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; index.AddManifest(manifest, "path"); REQUIRE(index.CheckConsistency(true)); // Add another version manifest.Version = "10.1"; manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "2.0"; manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "2.1"; index.AddManifest(manifest, "path2"); REQUIRE(index.CheckConsistency(true)); // Update to a conflict manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "2.1"; REQUIRE_THROWS_HR(index.UpdateManifest(manifest, "path2"), APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); } TEST_CASE("SQLiteIndex_ManifestArpVersion_ValidateManifestAgainstIndex", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "10.0"; manifest.Installers.push_back({}); manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; index.AddManifest(manifest, "path"); // Updating same version should not result in failure. REQUIRE_NOTHROW(ValidateManifestArpVersion(&index, manifest)); // Add different version should result in failure. manifest.Version = "10.1"; REQUIRE_THROWS(ValidateManifestArpVersion(&index, manifest)); } TEST_CASE("SQLiteIndex_CheckConsistency_FindEmbeddedNull", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; manifest.Version = "10.0"; manifest.Installers.push_back({}); manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; index.AddManifest(manifest, "path"); // Inject a null character using SQL without binding since we block it Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); Statement update = Statement::Create(connection, "Update versions set version = '10.0'||char(0)||'After Null' where version = '10.0'"); update.Execute(); REQUIRE(!index.CheckConsistency(true)); } TEST_CASE("SQLiteIndex_MapDataFolding_Tags", "[sqliteindex][mapdatafolding]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string tag1 = "Tag1"; std::string tag2 = "Tag2"; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Publisher", "Moniker", "Version1", "", { tag1 }, { "Command" }, "Path1", {}, { "PC1" } }, { "Id", "Name", "Publisher", "Moniker", "Version2", "", { tag2 }, { "Command" }, "Path2", {}, { "PC2" } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request1; request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Tag, MatchType::Exact, tag1)); auto results1 = index.Search(request1); SearchRequest request2; request2.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Tag, MatchType::Exact, tag2)); auto results2 = index.Search(request2); REQUIRE(results1.Matches.size() == 1); REQUIRE(results2.Matches.size() == 1); REQUIRE(results1.Matches[0].first == results2.Matches[0].first); } TEST_CASE("SQLiteIndex_MapDataFolding_PFNs", "[sqliteindex][mapdatafolding]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string pfn1 = "PFN1"; std::string pfn2 = "PFN2"; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Publisher", "Moniker", "Version1", "", { }, { "Command" }, "Path1", { pfn1 }, { } }, { "Id", "Name", "Publisher", "Moniker", "Version2", "", { }, { "Command" }, "Path2", { pfn2 }, { } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); if (!AreChannelsSupported(index)) { return; } SearchRequest request1; request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, pfn1)); auto results1 = index.Search(request1); SearchRequest request2; request2.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, pfn2)); auto results2 = index.Search(request2); REQUIRE(results1.Matches.size() == 1); REQUIRE(results2.Matches.size() == 1); REQUIRE(results1.Matches[0].first == results2.Matches[0].first); auto versionKeys = index.GetVersionKeysById(results1.Matches[0].first); REQUIRE(versionKeys.size() == 2); auto manifestId1 = versionKeys[0].ManifestId; auto manifestId2 = versionKeys[1].ManifestId; auto pfnValues1 = index.GetMultiPropertyByPrimaryId(manifestId1, PackageVersionMultiProperty::PackageFamilyName); auto pfnValues2 = index.GetMultiPropertyByPrimaryId(manifestId2, PackageVersionMultiProperty::PackageFamilyName); if (IsMapDataFoldingSupported(index, testVersion)) { REQUIRE(pfnValues1.size() == 2); REQUIRE(pfnValues2.size() == 2); REQUIRE(pfnValues1[0] != pfnValues1[1]); } else if (IsMapDataFolded(index)) { if (manifestId1 > manifestId2) { REQUIRE(pfnValues1.size() == 2); REQUIRE(pfnValues2.size() == 0); REQUIRE(pfnValues1[0] != pfnValues1[1]); } else { REQUIRE(pfnValues1.size() == 0); REQUIRE(pfnValues2.size() == 2); REQUIRE(pfnValues2[0] != pfnValues2[1]); } } else { REQUIRE(pfnValues1.size() == 1); REQUIRE(pfnValues2.size() == 1); REQUIRE(pfnValues1[0] != pfnValues2[0]); } } TEST_CASE("SQLiteIndex_MapDataFolding_ProductCodes", "[sqliteindex][mapdatafolding]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string pc1 = "PC1"; std::string pc2 = "PC2"; SQLiteIndex index = SearchTestSetup(tempFile, { { "Id", "Name", "Publisher", "Moniker", "Version1", "", { }, { "Command" }, "Path1", { }, { pc1 } }, { "Id", "Name", "Publisher", "Moniker", "Version2", "", { }, { "Command" }, "Path2", { }, { pc2 } }, }); SQLiteVersion testVersion = TestPrepareForRead(index); if (!AreChannelsSupported(index)) { return; } SearchRequest request1; request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, pc1)); auto results1 = index.Search(request1); SearchRequest request2; request2.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, pc2)); auto results2 = index.Search(request2); REQUIRE(results1.Matches.size() == 1); REQUIRE(results2.Matches.size() == 1); REQUIRE(results1.Matches[0].first == results2.Matches[0].first); auto versionKeys = index.GetVersionKeysById(results1.Matches[0].first); REQUIRE(versionKeys.size() == 2); auto manifestId1 = versionKeys[0].ManifestId; auto manifestId2 = versionKeys[1].ManifestId; auto pcValues1 = index.GetMultiPropertyByPrimaryId(manifestId1, PackageVersionMultiProperty::ProductCode); auto pcValues2 = index.GetMultiPropertyByPrimaryId(manifestId2, PackageVersionMultiProperty::ProductCode); REQUIRE(pcValues1.size() == 1); REQUIRE(pcValues2.size() == 1); REQUIRE(pcValues1[0] != pcValues2[0]); } TEST_CASE("SQLiteIndex_FilePath_Memory", "[sqliteindex]") { SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET); auto contextData = index.GetContextData(); REQUIRE(!contextData.Contains(Schema::Property::DatabaseFilePath)); } TEST_CASE("SQLiteIndex_FilePath_Create", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; SQLiteIndex index = SQLiteIndex::CreateNew(tempFile); auto contextData = index.GetContextData(); REQUIRE(contextData.Contains(Schema::Property::DatabaseFilePath)); REQUIRE(contextData.Get() == tempFile.GetPath()); } TEST_CASE("SQLiteIndex_FilePath_Open", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile); } SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); auto contextData = index.GetContextData(); REQUIRE(contextData.Contains(Schema::Property::DatabaseFilePath)); REQUIRE(contextData.Get() == tempFile.GetPath()); } TEST_CASE("SQLiteIndex_MigrateTo_Unsupported", "[sqliteindex][V1_7]") { SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLiteVersion{ 1, 6 }); REQUIRE(!index.MigrateTo(SQLiteVersion{ 1, 7 })); } TEST_CASE("SQLiteIndex_MigrateTo_Empty", "[sqliteindex][V2_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, SQLiteVersion{ 1, 7 }); REQUIRE(index.MigrateTo(SQLiteVersion{ 2, 0 })); REQUIRE(index.GetVersion() == SQLiteVersion{ 2, 0 }); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); REQUIRE(index.GetVersion() == SQLiteVersion{ 2, 0 }); } } TEST_CASE("SQLiteIndex_MigrateTo_Data", "[sqliteindex][V2_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string packageId1 = "Id1"; std::string packageId2 = "Id2"; std::string packageId3 = "Id3"; SQLiteIndex index = SearchTestSetup(tempFile, { { packageId1, "Name1", "Moniker", "Version", "", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, { packageId2, "Name2", "Moniker", "Version", "", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, { packageId3, "Name3", "Moniker", "Version", "", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); auto preMigrationVersion = index.GetVersion(); if (preMigrationVersion == SQLiteVersion{ 1, 7 }) { REQUIRE(index.MigrateTo(SQLiteVersion{ 2, 0 })); REQUIRE(index.GetVersion() == SQLiteVersion{ 2, 0 }); Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); auto updateData = Schema::V2_0::PackageUpdateTrackingTable::GetUpdatesSince(connection, 0); REQUIRE(updateData.size() == 3); REQUIRE(std::count_if(updateData.begin(), updateData.end(), [&](const auto& x) { return x.PackageIdentifier == packageId1; }) == 1); REQUIRE(std::count_if(updateData.begin(), updateData.end(), [&](const auto& x) { return x.PackageIdentifier == packageId2; }) == 1); REQUIRE(std::count_if(updateData.begin(), updateData.end(), [&](const auto& x) { return x.PackageIdentifier == packageId3; }) == 1); } else { REQUIRE(!index.MigrateTo(SQLiteVersion{ 2, 0 })); REQUIRE(index.GetVersion() == preMigrationVersion); } } TEST_CASE("SQLiteIndex_Property_IntermediateFilePath", "[sqliteindex]") { SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET); std::filesystem::path intermediateFilePath = "A:\\Path"; index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, intermediateFilePath.u8string()); auto contextData = index.GetContextData(); REQUIRE(contextData.Contains(Schema::Property::IntermediateFileOutputPath)); REQUIRE(contextData.Get() == intermediateFilePath); } struct ManifestAndPath { Manifest Manifest; std::string Path; }; void CreateFakeManifestAndPath( ManifestAndPath& manifestAndPath, const string_t& publisher, std::string_view version = "1.0.0", std::optional arpMinVersion = {}, std::optional arpMaxVersion = {}) { CreateFakeManifest(manifestAndPath.Manifest, publisher, version); manifestAndPath.Path = ConvertToUTF8(CreateNewGuidNameWString()); manifestAndPath.Manifest.StreamSha256 = SHA256::ComputeHash(manifestAndPath.Path); if (arpMinVersion) { manifestAndPath.Manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifestAndPath.Manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifestAndPath.Manifest.Installers[0].AppsAndFeaturesEntries.back().DisplayVersion = arpMinVersion.value(); } if (arpMaxVersion) { manifestAndPath.Manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; manifestAndPath.Manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); manifestAndPath.Manifest.Installers[0].AppsAndFeaturesEntries.back().DisplayVersion = arpMaxVersion.value(); } } std::filesystem::path GetOnlyChild(const std::filesystem::path& parent) { auto parentDirectoryIterator = std::filesystem::directory_iterator{ parent }; std::filesystem::path result = parentDirectoryIterator->path(); REQUIRE(++parentDirectoryIterator == std::filesystem::directory_iterator{}); return result; } void CheckIntermediates(const std::filesystem::path& baseDirectory, const std::vector>& expectedIntermediatesData, std::chrono::seconds sleep = 1s) { std::filesystem::path intermediatesDirectory = baseDirectory / "packages"; size_t intermediatePackageCount = std::count_if(std::filesystem::directory_iterator{ intermediatesDirectory }, std::filesystem::directory_iterator{}, [](const auto&){ return true; }); REQUIRE(intermediatePackageCount == expectedIntermediatesData.size()); for (const auto& versions : expectedIntermediatesData) { REQUIRE(!versions.empty()); INFO(versions[0].Manifest.Id); std::filesystem::path packageDirectory = intermediatesDirectory / ConvertToUTF16(versions[0].Manifest.Id); REQUIRE(std::filesystem::exists(packageDirectory)); std::filesystem::path hashDirectory = GetOnlyChild(packageDirectory); std::filesystem::path versionDataFile = GetOnlyChild(hashDirectory); std::ifstream versionDataStream{ versionDataFile, std::ios_base::in | std::ios_base::binary }; auto versionDataBytes = ReadEntireStreamAsByteArray(versionDataStream); PackageVersionDataManifest versionDataManifest; versionDataManifest.Deserialize(PackageVersionDataManifest::CreateDecompressor().Decompress(versionDataBytes)); const auto& versionDataVersions = versionDataManifest.Versions(); REQUIRE(versionDataVersions.size() == versions.size()); for (const auto& manifestAndPath : versions) { const auto& versionDataItr = std::find_if(versionDataVersions.begin(), versionDataVersions.end(), [&](const auto& v) { return v.Version == manifestAndPath.Manifest.Version; }); REQUIRE(versionDataItr != versionDataVersions.end()); const auto& versionData = *versionDataItr; REQUIRE(manifestAndPath.Path == versionData.ManifestRelativePath); REQUIRE(SHA256::ConvertToString(manifestAndPath.Manifest.StreamSha256) == versionData.ManifestHash); auto versionRange = manifestAndPath.Manifest.GetArpVersionRange(); if (!versionRange.IsEmpty()) { REQUIRE(versionData.ArpMinVersion); REQUIRE(versionRange.GetMinVersion() == versionData.ArpMinVersion.value()); REQUIRE(versionData.ArpMaxVersion); REQUIRE(versionRange.GetMaxVersion() == versionData.ArpMaxVersion.value()); } } } // This is needed to force the timestamp to roll over to a new value for the next call to this function. // An alternate solution would be to hook the timestamp function and control the values it returns // so that we can advance/halt time arbitrarily. std::this_thread::sleep_for(sleep); } void PrepareAndCheckIntermediates(const std::filesystem::path& baseFile, const std::filesystem::path& preparedFile, const std::vector>& expectedIntermediatesData, std::chrono::seconds sleep = 1s) { TempDirectory intermediatesDirectory{ "v2_0_intermediates" }; INFO("Intermediates directory: " << intermediatesDirectory.GetPath()); std::filesystem::copy_file(baseFile, preparedFile, std::filesystem::copy_options::overwrite_existing); SQLiteIndex index = SQLiteIndex::Open(preparedFile.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, intermediatesDirectory); index.PrepareForPackaging(); CheckIntermediates(intermediatesDirectory, expectedIntermediatesData, sleep); } TEST_CASE("SQLiteIndex_V2_0_UsageFlow_Simple", "[sqliteindex][V2_0]") { TempFile baseFile{ "v2_0_index_tempdb"s, ".db"s }; TempFile preparedFile{ "v2_0_index_prepared_tempdb"s, ".db"s }; INFO("Using files named: [" << baseFile.GetPath() << "] and [" << preparedFile.GetPath() << "]"); // Create empty index std::ignore = SQLiteIndex::CreateNew(baseFile, SQLiteVersion{ 2, 0 }); std::string publisher = "Publisher"; ManifestAndPath manifest1; CreateFakeManifestAndPath(manifest1, publisher, "1.0"); { // Open existing file to add a manifest SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); index.AddManifest(manifest1.Manifest, manifest1.Path); } PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest1 } }, 0s); } TEST_CASE("SQLiteIndex_V2_0_UsageFlow_Complex", "[sqliteindex][V2_0]") { TempFile baseFile{ "v2_0_index_tempdb"s, ".db"s }; TempFile preparedFile{ "v2_0_index_prepared_tempdb"s, ".db"s }; INFO("Using files named: [" << baseFile.GetPath() << "] and [" << preparedFile.GetPath() << "]"); // Create empty index std::ignore = SQLiteIndex::CreateNew(baseFile, SQLiteVersion{ 2, 0 }); // Open existing file to add a new package std::string Publisher1 = "Publisher1"; ManifestAndPath manifest1; CreateFakeManifestAndPath(manifest1, Publisher1, "1.0"); { SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); index.AddManifest(manifest1.Manifest, manifest1.Path); } PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest1 } }); // Open existing file to add another new package ManifestAndPath manifest2; CreateFakeManifestAndPath(manifest2, "Publisher2", "1.0"); { SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); index.AddManifest(manifest2.Manifest, manifest2.Path); } PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 } }); // Open existing file to add a new version of existing package ManifestAndPath manifest3; CreateFakeManifestAndPath(manifest3, Publisher1, "2.0"); { SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); index.AddManifest(manifest3.Manifest, manifest3.Path); } PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest1, manifest3 } }); // Open existing file to add a new version of existing package and update an existing version manifest2.Manifest.StreamSha256 = SHA256::ComputeHash(manifest2.Manifest.Id); ManifestAndPath manifest4; CreateFakeManifestAndPath(manifest4, Publisher1, "3.0"); { SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); index.UpdateManifest(manifest2.Manifest, manifest2.Path); index.AddManifest(manifest4.Manifest, manifest4.Path); } PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1, manifest3, manifest4 } }, 0s); } void MigratePrepareAndCheckIntermediates(const std::filesystem::path& baseFile, const std::filesystem::path& preparedFile, const std::vector>& expectedIntermediatesData) { TempDirectory intermediatesDirectory{ "v2_0_intermediates" }; INFO("Intermediates directory: " << intermediatesDirectory.GetPath()); std::filesystem::copy_file(baseFile, preparedFile, std::filesystem::copy_options::overwrite_existing); SQLiteIndex index = SQLiteIndex::Open(preparedFile.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); index.MigrateTo({ 2, 0 }); index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, intermediatesDirectory); index.PrepareForPackaging(); CheckIntermediates(intermediatesDirectory, expectedIntermediatesData, 0s); } TEST_CASE("SQLiteIndex_V2_0_UsageFlow_ComplexMigration", "[sqliteindex][V2_0]") { TempFile baseFile{ "v1_7_index_tempdb"s, ".db"s }; TempFile preparedFile{ "v2_0_index_prepared_tempdb"s, ".db"s }; INFO("Using files named: [" << baseFile.GetPath() << "] and [" << preparedFile.GetPath() << "]"); // Create empty index std::ignore = SQLiteIndex::CreateNew(baseFile, SQLiteVersion{ 1, 7 }); // Open existing file to add a new package std::string Publisher1 = "Publisher1"; ManifestAndPath manifest1; CreateFakeManifestAndPath(manifest1, Publisher1, "1.0"); { SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.AddManifest(manifest1.Manifest, manifest1.Path); } MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest1 } }); // Open existing file to add another new package ManifestAndPath manifest2; CreateFakeManifestAndPath(manifest2, "Publisher2", "1.0"); { SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.AddManifest(manifest2.Manifest, manifest2.Path); } MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1 } }); // Open existing file to add a new version of existing package ManifestAndPath manifest3; CreateFakeManifestAndPath(manifest3, Publisher1, "2.0"); { SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.AddManifest(manifest3.Manifest, manifest3.Path); } MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1, manifest3 } }); // Open existing file to add a new version of existing package and update an existing version manifest2.Manifest.StreamSha256 = SHA256::ComputeHash(manifest2.Manifest.Id); ManifestAndPath manifest4; CreateFakeManifestAndPath(manifest4, Publisher1, "3.0"); { SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); index.UpdateManifest(manifest2.Manifest, manifest2.Path); index.AddManifest(manifest4.Manifest, manifest4.Path); } MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1, manifest3, manifest4 } }); } TEST_CASE("SQLiteIndex_DependencyWithCaseMismatch", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); // Must contain some upper case auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); auto& publisher3 = "Test3"; CreateFakeManifest(manifest, publisher3); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, ToLower(dependencyManifest2.Id), "1.0.0")); index.AddManifest(manifest, GetPathFromManifest(manifest)); } TEST_CASE("SQLiteIndex_AddOrUpdateManifest", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); std::string manifestPath = "test/id/test.id-1.0.0.yaml"; Manifest manifest; manifest.Installers.push_back({}); manifest.Id = "test.id"; manifest.DefaultLocalization.Add < Localization::PackageName>("Test Name"); manifest.Moniker = "testmoniker"; manifest.Version = "1.0.0"; manifest.Channel = "test"; manifest.DefaultLocalization.Add({ "t1", "t2" }); manifest.Installers[0].Commands = { "test1", "test2" }; { auto version = GENERATE(SQLiteVersion{ 1, 0 }, SQLiteVersion::Latest()); SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, version); REQUIRE(index.AddOrUpdateManifest(manifest, manifestPath)); } { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); // Update should return false REQUIRE(!index.AddOrUpdateManifest(manifest, manifestPath)); manifest.DefaultLocalization.Add("description2"); // Update should return false REQUIRE(!index.AddOrUpdateManifest(manifest, manifestPath)); // Update with indexed changes should still return false manifest.DefaultLocalization.Add("Test Name2"); manifest.Moniker = "testmoniker2"; manifest.DefaultLocalization.Add({ "t1", "t2", "t3" }); manifest.Installers[0].Commands = {}; REQUIRE(!index.AddOrUpdateManifest(manifest, manifestPath)); } } TEST_CASE("SQLiteIndex_VersionStringPreserved", "[sqliteindex]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Manifest manifest; SQLiteIndex index = CreateTestIndex(tempFile); string_t publisher = "Test"; std::string version = GENERATE("1.0", "1.10"); CreateFakeManifest(manifest, publisher, version); index.AddManifest(manifest, GetPathFromManifest(manifest)); TempDirectory intermediatesDirectory{ "v2_0_intermediates" }; INFO("Intermediates directory: " << intermediatesDirectory.GetPath()); index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, intermediatesDirectory); index.PrepareForPackaging(); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); std::string extractedVersion = GetPropertyStringById(index, results.Matches[0].first, PackageVersionProperty::Version); REQUIRE(extractedVersion == version); } ================================================ FILE: src/AppInstallerCLITests/SQLiteIndexSource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::SQLite; using SQLiteVersion = AppInstaller::SQLite::Version; static std::shared_ptr SimpleTestSetup(const std::string& filePath, SourceDetails& details, Manifest& manifest, std::string& relativePath, const std::filesystem::path& manifestFile = "Manifest-Good.yaml") { SQLiteVersion latest1 = Version::LatestForMajor(1); SQLiteVersion latest2 = Version::LatestForMajor(2); const SQLiteVersion versionToUse = GENERATE_COPY(SQLiteVersion{ latest1 }, SQLiteVersion{ latest2 }); SQLiteIndex index = SQLiteIndex::CreateNew(filePath, versionToUse); TestDataFile testManifest(manifestFile); manifest = YamlParser::CreateFromPath(testManifest); std::filesystem::path testManifestPath = testManifest.GetPath(); relativePath = testManifestPath.filename().u8string(); TempDirectory sourceFilesDirectory{ "SQLiteIndexSource" }; std::filesystem::path sourceFilesDirectoryPath = sourceFilesDirectory.GetPath(); std::filesystem::create_directories(sourceFilesDirectoryPath); std::filesystem::copy_file(testManifestPath, sourceFilesDirectoryPath / relativePath); sourceFilesDirectory.Release(); index.AddManifest(manifest, relativePath); details.Name = "TestName"; details.Type = "TestType"; details.Arg = sourceFilesDirectoryPath.u8string(); details.Data = ""; details.Identifier = "SimpleTestSetup"; if (versionToUse.MajorVersion == 2) { index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, sourceFilesDirectoryPath.u8string()); } index.PrepareForPackaging(); return std::make_shared(details, std::move(index)); } static bool SupportsChannel(const std::shared_ptr& source) { return source->GetIndex().GetVersion().MajorVersion == 1; } TEST_CASE("SQLiteIndexSource_Search_IdExactMatch", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); REQUIRE(results.Matches[0].MatchCriteria.Field == PackageMatchField::Id); REQUIRE(results.Matches[0].MatchCriteria.Type == MatchType::Exact); REQUIRE(results.Matches[0].MatchCriteria.Value == manifest.Id); } TEST_CASE("SQLiteIndexSource_Search_NoMatch", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, "THIS DOES NOT MATCH ANYTHING!"); auto results = source->Search(request); REQUIRE(results.Matches.size() == 0); } TEST_CASE("SQLiteIndexSource_Id", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); auto latestVersion = results.Matches[0].Package->GetAvailable()[0]->GetLatestVersion(); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Id).get() == manifest.Id); } TEST_CASE("SQLiteIndexSource_Name", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); auto latestVersion = results.Matches[0].Package->GetAvailable()[0]->GetLatestVersion(); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Name).get() == manifest.DefaultLocalization.Get()); } TEST_CASE("SQLiteIndexSource_Versions", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); REQUIRE(results.Matches[0].Package->GetAvailable().size() == 1); auto result = results.Matches[0].Package->GetAvailable()[0]->GetVersionKeys(); REQUIRE(result.size() == 1); REQUIRE(result[0].Version == manifest.Version); if (SupportsChannel(source)) { REQUIRE(result[0].Channel == manifest.Channel); } } TEST_CASE("SQLiteIndexSource_GetManifest", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); REQUIRE(results.Matches[0].Package->GetAvailable().size() == 1); auto package = results.Matches[0].Package->GetAvailable()[0]; auto specificResultVersion = package->GetVersion(PackageVersionKey("", manifest.Version, SupportsChannel(source) ? manifest.Channel : "")); REQUIRE(specificResultVersion); auto specificResult = specificResultVersion->GetManifest(); REQUIRE(specificResult.Id == manifest.Id); REQUIRE(specificResult.DefaultLocalization.Get() == manifest.DefaultLocalization.Get()); REQUIRE(specificResult.Version == manifest.Version); if (SupportsChannel(source)) { REQUIRE(specificResult.Channel == manifest.Channel); } if (SupportsChannel(source)) { auto latestResultVersion = package->GetVersion(PackageVersionKey("", "", manifest.Channel)); REQUIRE(latestResultVersion); auto latestResult = latestResultVersion->GetManifest(); REQUIRE(latestResult.Id == manifest.Id); REQUIRE(latestResult.DefaultLocalization.Get() == manifest.DefaultLocalization.Get()); REQUIRE(latestResult.Version == manifest.Version); REQUIRE(latestResult.Channel == manifest.Channel); } auto noResultVersion = package->GetVersion(PackageVersionKey("", "blargle", "flargle")); REQUIRE(!noResultVersion); } TEST_CASE("SQLiteIndexSource_IsSame", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto result1 = source->Search(request); REQUIRE(result1.Matches.size() == 1); REQUIRE(result1.Matches[0].Package->GetAvailable().size() == 1); auto result2 = source->Search(request); REQUIRE(result2.Matches.size() == 1); REQUIRE(result2.Matches[0].Package->GetAvailable().size() == 1); REQUIRE(result1.Matches[0].Package->GetAvailable()[0]->IsSame(result2.Matches[0].Package->GetAvailable()[0].get())); } TEST_CASE("SQLiteIndexSource_Package_ProductCodes", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); auto package = results.Matches[0].Package->GetAvailable()[0]; auto manifestPCs = manifest.GetProductCodes(); auto propertyPCs = package->GetMultiProperty(PackageMultiProperty::ProductCode); REQUIRE(manifestPCs.size() == 1); REQUIRE(propertyPCs.size() == 1); REQUIRE(manifestPCs[0] == propertyPCs[0].get()); } TEST_CASE("SQLiteIndexSource_VersionSelection", "[sqliteindexsource]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); SourceDetails details; Manifest manifest; std::string relativePath; std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath, "InstallFlowTest_Exe.yaml"); SearchRequest request; request.Query = RequestMatch(MatchType::Exact, manifest.Id); auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); auto package = results.Matches[0].Package->GetAvailable()[0]; PackageVersionKey key{ {}, "1", {} }; auto version = package->GetVersion(key); REQUIRE(version); } ================================================ FILE: src/AppInstallerCLITests/SQLiteWrapper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include using namespace AppInstaller::SQLite; using namespace std::string_literals; static const char* s_firstColumn = "first"; static const char* s_secondColumn = "second"; static const char* s_tableName = "simpletest"; static const char* s_savepoint = "simplesave"; static const char* s_CreateSimpleTestTableSQL = R"( CREATE TABLE [main].[simpletest]( [first] INT, [second] TEXT); )"; static const char* s_insertToSimpleTestTableSQL = R"( insert into simpletest (first, second) values (?, ?) )"; static const char* s_selectFromSimpleTestTableSQL = R"( select first, second from simpletest )"; void CreateSimpleTestTable(Connection& connection) { Builder::StatementBuilder builder; builder.CreateTable(s_tableName).Columns({ Builder::ColumnBuilder(s_firstColumn, Builder::Type::Int), Builder::ColumnBuilder(s_secondColumn, Builder::Type::Text), }); Statement createTable = builder.Prepare(connection); REQUIRE_FALSE(createTable.Step()); REQUIRE(createTable.GetState() == Statement::State::Completed); } void InsertIntoSimpleTestTable(Connection& connection, int firstVal, const std::string& secondVal) { Builder::StatementBuilder builder; builder.InsertInto(s_tableName).Columns({ s_firstColumn, s_secondColumn }).Values(firstVal, secondVal); Statement insert = builder.Prepare(connection); REQUIRE_FALSE(insert.Step()); REQUIRE(insert.GetState() == Statement::State::Completed); } void UpdateSimpleTestTable(Connection& connection, int firstVal, const std::string& secondVal) { Builder::StatementBuilder update; update.Update(s_tableName).Set().Column(s_firstColumn).Equals(firstVal).Column(s_secondColumn).Equals(secondVal); update.Execute(connection); } void InsertIntoSimpleTestTableWithNull(Connection& connection, int firstVal) { Builder::StatementBuilder builder; builder.InsertInto(s_tableName).Columns({ s_firstColumn, s_secondColumn }).Values(firstVal, nullptr); Statement insert = builder.Prepare(connection); REQUIRE_FALSE(insert.Step()); REQUIRE(insert.GetState() == Statement::State::Completed); } void SelectFromSimpleTestTableOnlyOneRow(Connection& connection, int firstVal, const std::string& secondVal) { Builder::StatementBuilder builder; builder.Select({ s_firstColumn, s_secondColumn }).From(s_tableName); Statement select = builder.Prepare(connection); REQUIRE(select.Step()); REQUIRE(select.GetState() == Statement::State::HasRow); int firstRead = select.GetColumn(0); std::string secondRead = select.GetColumn(1); REQUIRE(firstVal == firstRead); REQUIRE(secondVal == secondRead); auto tuple = select.GetRow(); REQUIRE(firstVal == std::get<0>(tuple)); REQUIRE(secondVal == std::get<1>(tuple)); REQUIRE_FALSE(select.Step()); REQUIRE(select.GetState() == Statement::State::Completed); select.Reset(); REQUIRE(select.GetState() == Statement::State::Prepared); REQUIRE(select.Step()); REQUIRE(select.GetState() == Statement::State::HasRow); } TEST_CASE("SQLiteWrapperMemoryCreate", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); int firstVal = 1; std::string secondVal = "test"; InsertIntoSimpleTestTable(connection, firstVal, secondVal); SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } TEST_CASE("SQLiteWrapperFileCreateAndReopen", "[sqlitewrapper]") { TestCommon::TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); int firstVal = 1; std::string secondVal = "test"; // Create the DB and some data { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); InsertIntoSimpleTestTable(connection, firstVal, secondVal); } // Reopen the DB and read data { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } } TEST_CASE("SQLiteWrapperSavepointRollback", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); int firstVal = 1; std::string secondVal = "test"; CreateSimpleTestTable(connection); Savepoint savepoint = Savepoint::Create(connection, "test_savepoint"); InsertIntoSimpleTestTable(connection, firstVal, secondVal); savepoint.Rollback(); Statement select = Statement::Create(connection, s_selectFromSimpleTestTableSQL); REQUIRE(!select.Step()); REQUIRE(select.GetState() == Statement::State::Completed); } TEST_CASE("SQLiteWrapperSavepointRollbackOnDestruct", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); int firstVal = 1; std::string secondVal = "test"; CreateSimpleTestTable(connection); { Savepoint savepoint = Savepoint::Create(connection, "test_savepoint"); InsertIntoSimpleTestTable(connection, firstVal, secondVal); } Statement select = Statement::Create(connection, s_selectFromSimpleTestTableSQL); REQUIRE(!select.Step()); REQUIRE(select.GetState() == Statement::State::Completed); } TEST_CASE("SQLiteWrapperSavepointCommit", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); int firstVal = 1; std::string secondVal = "test"; CreateSimpleTestTable(connection); { Savepoint savepoint = Savepoint::Create(connection, "test_savepoint"); InsertIntoSimpleTestTable(connection, firstVal, secondVal); savepoint.Commit(); } SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } TEST_CASE("SQLiteWrapperSavepointReuse", "[sqlitewrapper]") { TestCommon::TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); int firstVal = 1; std::string secondVal = "test"; // Create the DB and some data { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); InsertIntoSimpleTestTable(connection, firstVal, secondVal); } // Reopen the DB and update with a single savepoint { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); Savepoint savepoint = Savepoint::Create(connection, s_savepoint); firstVal = 2; secondVal = "test2"; UpdateSimpleTestTable(connection, firstVal, secondVal); savepoint.Commit(); } { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } // Reopen the DB and update with a multiple savepoint { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); { Savepoint savepoint = Savepoint::Create(connection, s_savepoint); firstVal = 3; secondVal = "test3"; UpdateSimpleTestTable(connection, firstVal, secondVal); } { Savepoint savepoint = Savepoint::Create(connection, s_savepoint); firstVal = 4; secondVal = "test4"; UpdateSimpleTestTable(connection, firstVal, secondVal); savepoint.Commit(); } } { Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } } TEST_CASE("SQLiteWrapper_EscapeStringForLike", "[sqlitewrapper]") { std::string escape(EscapeCharForLike); std::string input = "test"; std::string output = EscapeStringForLike(input); REQUIRE(input == output); input = EscapeCharForLike; output = EscapeStringForLike(input); REQUIRE((input + input) == output); input = "%"; output = EscapeStringForLike(input); REQUIRE((escape + input) == output); input = "_"; output = EscapeStringForLike(input); REQUIRE((escape + input) == output); input = "%_A_%"; std::string expected = escape + "%" + escape + "_A" + escape + "_" + escape + "%"; output = EscapeStringForLike(input); REQUIRE(expected == output); } TEST_CASE("SQLiteWrapper_BindWithEmbeddedNull", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); int firstVal = 1; std::string secondVal = "test"; secondVal[1] = '\0'; REQUIRE_THROWS_HR(InsertIntoSimpleTestTable(connection, firstVal, secondVal), APPINSTALLER_CLI_ERROR_BIND_WITH_EMBEDDED_NULL); } TEST_CASE("SQLiteWrapper_PrepareFailure", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); Builder::StatementBuilder builder; builder.Select({ s_firstColumn, s_secondColumn }).From(std::string{ s_tableName } + "2").Where(s_firstColumn).Equals(2); REQUIRE_THROWS_HR(builder.Prepare(connection), MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, SQLITE_ERROR)); } TEST_CASE("SQLiteWrapper_BusyTimeout_None", "[sqlitewrapper]") { TestCommon::TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); wil::unique_event busy, done; busy.create(); done.create(); std::thread busyThread([&]() { Connection threadConnection = Connection::Create(tempFile, Connection::OpenDisposition::Create); Statement threadStatement = Statement::Create(threadConnection, "BEGIN EXCLUSIVE TRANSACTION"); threadStatement.Execute(); busy.SetEvent(); done.wait(500); }); busyThread.detach(); busy.wait(500); Connection testConnection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); testConnection.SetBusyTimeout(0ms); Statement testStatement = Statement::Create(testConnection, "BEGIN EXCLUSIVE TRANSACTION"); REQUIRE_THROWS_HR(testStatement.Execute(), MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, SQLITE_BUSY)); done.SetEvent(); } TEST_CASE("SQLiteWrapper_BusyTimeout_Some", "[sqlitewrapper]") { TestCommon::TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); wil::unique_event busy, ready, done; busy.create(); ready.create(); done.create(); std::thread busyThread([&]() { Connection threadConnection = Connection::Create(tempFile, Connection::OpenDisposition::Create); Statement threadBeginStatement = Statement::Create(threadConnection, "BEGIN EXCLUSIVE TRANSACTION"); Statement threadCommitStatement = Statement::Create(threadConnection, "COMMIT"); threadBeginStatement.Execute(); busy.SetEvent(); ready.wait(500); done.wait(100); threadCommitStatement.Execute(); }); busyThread.detach(); busy.wait(500); Connection testConnection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); testConnection.SetBusyTimeout(500ms); Statement testStatement = Statement::Create(testConnection, "BEGIN EXCLUSIVE TRANSACTION"); ready.SetEvent(); testStatement.Execute(); done.SetEvent(); } TEST_CASE("SQLiteWrapper_CloseConnectionOnError", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); Builder::StatementBuilder builder; builder.CreateTable(s_tableName).Columns({ Builder::ColumnBuilder(s_firstColumn, Builder::Type::Int), Builder::ColumnBuilder(s_secondColumn, Builder::Type::Text), }); Statement createTable = builder.Prepare(connection); REQUIRE_FALSE(createTable.Step()); REQUIRE(createTable.GetState() == Statement::State::Completed); createTable.Reset(); REQUIRE_THROWS(createTable.Step(true)); // Do anything that needs the connection REQUIRE_THROWS_HR(connection.GetLastInsertRowID(), APPINSTALLER_CLI_ERROR_SQLITE_CONNECTION_TERMINATED); } TEST_CASE("SQLBuilder_SimpleSelectBind", "[sqlbuilder]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); InsertIntoSimpleTestTable(connection, 1, "1"); InsertIntoSimpleTestTable(connection, 2, "2"); InsertIntoSimpleTestTable(connection, 3, "3"); Builder::StatementBuilder builder; builder.Select({ s_firstColumn, s_secondColumn }).From(s_tableName).Where(s_firstColumn).Equals(2); auto statement = builder.Prepare(connection); REQUIRE(statement.Step()); REQUIRE(statement.GetColumn(0) == 2); REQUIRE(statement.GetColumn(0) == "2"); REQUIRE(!statement.Step()); Builder::StatementBuilder buildCount; buildCount.Select(Builder::RowCount).From(s_tableName); auto rows = buildCount.Prepare(connection); REQUIRE(rows.Step()); REQUIRE(rows.GetColumn(0) == 3); REQUIRE(!rows.Step()); } TEST_CASE("SQLBuilder_SimpleSelectUnbound", "[sqlbuilder]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); InsertIntoSimpleTestTable(connection, 1, "1"); InsertIntoSimpleTestTable(connection, 2, "2"); InsertIntoSimpleTestTable(connection, 3, "3"); Builder::StatementBuilder builder; builder.Select({ s_firstColumn, s_secondColumn }).From(s_tableName).Where(s_firstColumn).Equals(Builder::Unbound); auto statement = builder.Prepare(connection); statement.Bind(1, 2); REQUIRE(statement.Step()); REQUIRE(statement.GetColumn(0) == 2); REQUIRE(statement.GetColumn(0) == "2"); REQUIRE(!statement.Step()); } TEST_CASE("SQLBuilder_SimpleSelectNull", "[sqlbuilder]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); InsertIntoSimpleTestTable(connection, 1, "1"); InsertIntoSimpleTestTable(connection, 2, "2"); InsertIntoSimpleTestTableWithNull(connection, 3); Builder::StatementBuilder builder; builder.Select({ s_firstColumn, s_secondColumn }).From(s_tableName).Where(s_secondColumn).IsNull(); auto statement = builder.Prepare(connection); REQUIRE(statement.Step()); REQUIRE(statement.GetColumn(0) == 3); REQUIRE(statement.GetColumnIsNull(1)); REQUIRE(!statement.Step()); } TEST_CASE("SQLBuilder_SimpleSelectOptional", "[sqlbuilder]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); InsertIntoSimpleTestTable(connection, 1, "1"); InsertIntoSimpleTestTable(connection, 2, "2"); InsertIntoSimpleTestTableWithNull(connection, 3); std::optional secondValue; { Builder::StatementBuilder builder; builder.Select({ s_firstColumn, s_secondColumn }).From(s_tableName).Where(s_secondColumn).Equals(secondValue); auto statement = builder.Prepare(connection); REQUIRE(statement.Step()); REQUIRE(statement.GetColumn(0) == 3); REQUIRE(statement.GetColumnIsNull(1)); REQUIRE(!statement.Step()); } { secondValue = "2"; Builder::StatementBuilder builder; builder.Select({ s_firstColumn, s_secondColumn }).From(s_tableName).Where(s_secondColumn).Equals(secondValue); auto statement = builder.Prepare(connection); REQUIRE(statement.Step()); REQUIRE(statement.GetColumn(0) == 2); REQUIRE(statement.GetColumn(1) == "2"); REQUIRE(!statement.Step()); } } TEST_CASE("SQLBuilder_Update", "[sqlbuilder]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); CreateSimpleTestTable(connection); int firstVal = 1; std::string secondVal = "test"; InsertIntoSimpleTestTable(connection, firstVal, secondVal); SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); firstVal = 2; secondVal = "testing"; UpdateSimpleTestTable(connection, firstVal, secondVal); SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } TEST_CASE("SQLBuilder_CaseInsensitive", "[sqlbuilder]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); Builder::StatementBuilder createTable; createTable.CreateTable(s_tableName).Columns({ Builder::ColumnBuilder(s_firstColumn, Builder::Type::Text).CollateNoCase() }); createTable.Execute(connection); std::string upperCaseVal = "TEST"; std::string lowerCaseVal = "test"; { INFO("Insert initial value"); Builder::StatementBuilder builder; builder.InsertInto(s_tableName) .Columns({ s_firstColumn }) .Values(upperCaseVal); builder.Execute(connection); } { INFO("Retrieve using case-insensitive value"); Builder::StatementBuilder builder; builder.Select({ s_firstColumn }).From(s_tableName).Where(s_firstColumn).Equals(lowerCaseVal); auto statement = builder.Prepare(connection); REQUIRE(statement.Step()); } } TEST_CASE("SQLBuilder_CreateTable", "[sqlbuilder]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); int testRun = GENERATE(0, 1, 2, 3, 4, 5, 6, 7); bool notNull = ((testRun & 1) != 0); bool unique = ((testRun & 2) != 0); bool pk = ((testRun & 4) != 0); CAPTURE(notNull, unique, pk); Builder::StatementBuilder createTable; createTable.CreateTable(s_tableName).Columns({ Builder::ColumnBuilder(s_firstColumn, Builder::Type::Int).NotNull(notNull).Unique(unique).PrimaryKey(pk) }); createTable.Execute(connection); Builder::StatementBuilder insertBuilder; insertBuilder.InsertInto(s_tableName).Columns(s_firstColumn).Values(Builder::Unbound); Statement insertStatement = insertBuilder.Prepare(connection); { INFO("Insert NULL"); insertStatement.Bind(1, nullptr); if (notNull) { REQUIRE_THROWS_HR(insertStatement.Execute(), MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, SQLITE_CONSTRAINT_NOTNULL)); } else { insertStatement.Execute(); } } { INFO("Insert unique values"); insertStatement.Reset(); insertStatement.Bind(1, 1); insertStatement.Execute(); insertStatement.Reset(); insertStatement.Bind(1, 2); insertStatement.Execute(); } { INFO("Insert duplicate values"); insertStatement.Reset(); insertStatement.Bind(1, 1); if (unique || pk) { HRESULT expectedHR = S_OK; if (pk) { expectedHR = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, SQLITE_CONSTRAINT_PRIMARYKEY); } else { expectedHR = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, SQLITE_CONSTRAINT_UNIQUE); } REQUIRE_THROWS_HR(insertStatement.Execute(), expectedHR); } else { insertStatement.Execute(); } } } TEST_CASE("SQLBuilder_InsertValueBinding", "[sqlbuilder]") { char const* const columns[] = { "a", "b", "c", "d", "e", "f" }; TestCommon::TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::Create); { INFO("Create table"); Builder::StatementBuilder createTable; createTable.CreateTable(s_tableName).BeginColumns(); for (const auto c : columns) { createTable.Column(Builder::ColumnBuilder(c, Builder::Type::Int)); } createTable.EndColumns(); createTable.Execute(connection); } { INFO("Insert values"); Builder::StatementBuilder insertBuilder; insertBuilder.InsertInto(s_tableName).BeginColumns(); for (const auto c : columns) { insertBuilder.Column(c); } insertBuilder.EndColumns().Values(0, 1, 2, 3, 4, 5); insertBuilder.Execute(connection); } { INFO("Insert values"); Builder::StatementBuilder insertBuilder; insertBuilder.InsertInto(s_tableName).BeginColumns(); for (const auto c : columns) { insertBuilder.Column(c); } insertBuilder.EndColumns().BeginValues(); insertBuilder.Value(5); insertBuilder.Value(nullptr); insertBuilder.Value(3); insertBuilder.Value(std::optional{}); insertBuilder.Value(std::optional{ 1 }); insertBuilder.Value(Builder::Unbound); insertBuilder.EndValues(); insertBuilder.Execute(connection); } { INFO("Select values"); Builder::StatementBuilder selectBuilder; selectBuilder.Select(); for (const auto c : columns) { selectBuilder.Column(c); } selectBuilder.From(s_tableName); Statement select = selectBuilder.Prepare(connection); REQUIRE(select.Step()); for (int i = 0; i < ARRAYSIZE(columns); ++i) { REQUIRE(i == select.GetColumn(i)); } REQUIRE(select.Step()); for (int i = 0; i < ARRAYSIZE(columns); ++i) { if (i & 1) { REQUIRE(select.GetColumnIsNull(i)); } else { REQUIRE((5 - i) == select.GetColumn(i)); } } REQUIRE(!select.Step()); } } TEST_CASE("SQLiteWrapperTransactionRollback", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); int firstVal = 1; std::string secondVal = "test"; CreateSimpleTestTable(connection); Transaction transaction = Transaction::Create(connection, "test_transaction", false); InsertIntoSimpleTestTable(connection, firstVal, secondVal); transaction.Rollback(); Statement select = Statement::Create(connection, s_selectFromSimpleTestTableSQL); REQUIRE(!select.Step()); REQUIRE(select.GetState() == Statement::State::Completed); } TEST_CASE("SQLiteWrapperTransactionRollbackOnDestruct", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); int firstVal = 1; std::string secondVal = "test"; CreateSimpleTestTable(connection); { Transaction transaction = Transaction::Create(connection, "test_transaction", false); InsertIntoSimpleTestTable(connection, firstVal, secondVal); } Statement select = Statement::Create(connection, s_selectFromSimpleTestTableSQL); REQUIRE(!select.Step()); REQUIRE(select.GetState() == Statement::State::Completed); } TEST_CASE("SQLiteWrapperTransactionCommit", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); int firstVal = 1; std::string secondVal = "test"; CreateSimpleTestTable(connection); { Transaction transaction = Transaction::Create(connection, "test_transaction", false); InsertIntoSimpleTestTable(connection, firstVal, secondVal); transaction.Commit(); } SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } TEST_CASE("SQLiteWrapperTransactionImmediate", "[sqlitewrapper]") { Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); int firstVal = 1; std::string secondVal = "test"; CreateSimpleTestTable(connection); { Transaction transaction = Transaction::Create(connection, "test_transaction", true); InsertIntoSimpleTestTable(connection, firstVal, secondVal); transaction.Commit(); } SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } TEST_CASE("SQLiteWrapperTransactionWriteConflict", "[sqlitewrapper]") { TestCommon::TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::Create); connection.SetJournalMode("WAL"); int firstVal = 1; std::string secondVal = "test"; CreateSimpleTestTable(connection); Connection connection2 = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); std::chrono::milliseconds busyWait = 250ms; connection2.SetBusyTimeout(busyWait); { Transaction transaction = Transaction::Create(connection, "test_transaction", true); InsertIntoSimpleTestTable(connection, firstVal, secondVal); // Start second transaction std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); std::chrono::system_clock::time_point end = start; try { Transaction transaction2 = Transaction::Create(connection2, "test_transaction2", true); } catch (...) { end = std::chrono::system_clock::now(); } std::chrono::milliseconds duration = std::chrono::duration_cast(end - start); REQUIRE(duration >= busyWait); transaction.Commit(); Transaction transaction2 = Transaction::Create(connection2, "test_transaction2", true); InsertIntoSimpleTestTable(connection2, firstVal, secondVal); } SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); } ================================================ FILE: src/AppInstallerCLITests/SearchRequestSerializer.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include #include using namespace TestCommon; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest::Schema; TEST_CASE("SearchRequestSerializer_InclusionsFilters", "[RestSource]") { SearchRequest searchRequest; searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::Substring, "Foo.Bar")); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Name, MatchType::Substring, "Foo")); searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Moniker, MatchType::Exact, "FooBar")); searchRequest.MaximumResults = 10; V1_0::Json::SearchRequestSerializer serializer; web::json::value actual = serializer.Serialize(searchRequest); REQUIRE(!actual.is_null()); REQUIRE(!actual.has_field(L"FetchAllManifests")); REQUIRE(actual.at(L"MaximumResults").as_integer() == static_cast(searchRequest.MaximumResults)); // Inclusions web::json::array inclusions = actual.at(L"Inclusions").as_array(); REQUIRE(inclusions.size() == 2); REQUIRE(inclusions.at(0).at(L"PackageMatchField").as_string() == L"PackageIdentifier"); REQUIRE(inclusions.at(1).at(L"PackageMatchField").as_string() == L"PackageName"); web::json::value requestMatch = inclusions.at(0).at(L"RequestMatch"); REQUIRE(!requestMatch.is_null()); REQUIRE(requestMatch.at(L"KeyWord").as_string() == L"Foo.Bar"); REQUIRE(requestMatch.at(L"MatchType").as_string() == L"Substring"); // Filters web::json::array filters = actual.at(L"Filters").as_array(); REQUIRE(filters.size() == 1); REQUIRE(filters.at(0).at(L"PackageMatchField").as_string() == L"Moniker"); web::json::value requestMatchFilter = filters.at(0).at(L"RequestMatch"); REQUIRE(!requestMatchFilter.is_null()); REQUIRE(requestMatchFilter.at(L"KeyWord").as_string() == L"FooBar"); REQUIRE(requestMatchFilter.at(L"MatchType").as_string() == L"Exact"); } TEST_CASE("SearchRequestSerializer_Query", "[RestSource]") { SearchRequest searchRequest; searchRequest.Query = RequestMatch(MatchType::Substring, "Foo.Bar"); V1_0::Json::SearchRequestSerializer serializer; web::json::value actual = serializer.Serialize(std::move(searchRequest)); REQUIRE(!actual.is_null()); web::json::value query = actual.at(L"Query"); REQUIRE(query.at(L"KeyWord").as_string() == L"Foo.Bar"); REQUIRE(query.at(L"MatchType").as_string() == L"Substring"); } TEST_CASE("SearchRequestSerializer_FetchAllManifests", "[RestSource]") { V1_0::Json::SearchRequestSerializer serializer; web::json::value actual = serializer.Serialize({}); REQUIRE(!actual.is_null()); REQUIRE(actual.at(L"FetchAllManifests").as_bool()); } TEST_CASE("SearchRequestSerializer_NewFields", "[RestSource]") { SearchRequest searchRequest; searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::Substring, "Foo.Bar")); searchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Name, MatchType::Substring, "Foo")); searchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Market, MatchType::Exact, "FooBar")); V1_0::Json::SearchRequestSerializer serializerV1_0; web::json::value actual_1_0 = serializerV1_0.Serialize(searchRequest); REQUIRE(!actual_1_0.is_null()); REQUIRE(actual_1_0.at(L"Filters").as_array().size() == 0); V1_1::Json::SearchRequestSerializer serializerV1_1; web::json::value actual_1_1 = serializerV1_1.Serialize(searchRequest); REQUIRE(!actual_1_1.is_null()); REQUIRE(actual_1_1.at(L"Filters").as_array().size() == 1); } ================================================ FILE: src/AppInstallerCLITests/Settings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include using namespace AppInstaller::Runtime; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; TEST_CASE("ReadEmptySetting", "[settings]") { StreamDefinition name{ Type::Standard, "nonexistentsetting" }; auto result = Stream{ name }.Get(); REQUIRE(!result); } TEST_CASE("SetAndReadSetting", "[settings]") { StreamDefinition name{ Type::Standard, "testsettingname" }; std::string value = "This is the test setting value"; Stream stream{ name }; REQUIRE(stream.Set(value)); auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); } TEST_CASE("SetAndReadSettingInContainer", "[settings]") { StreamDefinition name{ Type::Standard, "testcontainer/testsettingname" }; std::string value = "This is the test setting value from inside a container"; Stream stream{ name }; REQUIRE(stream.Set(value)); auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); } TEST_CASE("RemoveSetting", "[settings]") { StreamDefinition name{ Type::Standard, "testsettingname" }; std::string value = "This is the test setting value to be removed"; Stream stream{ name }; REQUIRE(stream.Set(value)); { auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); } stream.Remove(); auto result = stream.Get(); REQUIRE(!static_cast(result)); } TEST_CASE("SetAndReadUserFileSetting", "[settings]") { StreamDefinition name{ Type::UserFile, "userfilesetting" }; std::string value = "This is the test setting value for a user file"; Stream stream{ name }; REQUIRE(stream.Set(value)); auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); } TEST_CASE("ReadEmptySecureSetting", "[settings]") { StreamDefinition name{ Type::Secure, "secure_nonexistentsetting" }; auto result = Stream{ name }.Get(); REQUIRE(!result); } TEST_CASE("SetAndReadSecureSetting", "[settings]") { StreamDefinition name{ Type::Secure, "secure_testsettingname" }; std::string value = "This is the test setting value"; Stream stream{ name }; REQUIRE(stream.Set(value)); auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); } TEST_CASE("SetAndReadSecureSettingInContainer", "[settings]") { StreamDefinition name{ Type::Secure, "testcontainer/secure_testsettingname" }; std::string value = "This is the test setting value from inside a container"; Stream stream{ name }; REQUIRE(stream.Set(value)); auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); } TEST_CASE("RemoveSecureSetting", "[settings]") { StreamDefinition name{ Type::Secure, "secure_testsettingname" }; std::string value = "This is the test setting value to be removed"; Stream stream{ name }; REQUIRE(stream.Set(value)); { auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); } stream.Remove(); auto result = stream.Get(); REQUIRE(!static_cast(result)); } TEST_CASE("SetAndReadSecureSetting_SecureDataRemoved", "[settings]") { StreamDefinition name{ Type::Secure, "secure_testsettingname" }; std::string value = "This is the test setting value"; Stream stream{ name }; REQUIRE(stream.Set(value)); auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); std::filesystem::remove(GetPathTo(PathName::SecureSettingsForRead) / name.Name); REQUIRE_THROWS_HR(stream.Get(), SPAPI_E_FILE_HASH_NOT_IN_CATALOG); } TEST_CASE("SetAndReadSecureSetting_DataTampered", "[settings]") { StreamDefinition name{ Type::Secure, "secure_testsettingname" }; std::string value = "This is the test setting value"; Stream stream{ name }; REQUIRE(stream.Set(value)); auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); StreamDefinition insecureName = name; insecureName.Type = Type::Standard; REQUIRE(Stream{ insecureName }.Set("Tampered data")); REQUIRE_THROWS_HR(stream.Get(), HRESULT_FROM_WIN32(ERROR_DATA_CHECKSUM_ERROR)); } TEST_CASE("SetChangeAndReadSetting", "[settings]") { StreamDefinition name{ Type::Standard, "testsettingname" }; std::string value1 = "This is the test setting value1"; std::string value2 = "This is the test setting value2, which is different"; std::string value3 = "This is the test setting value3; also different"; name.Type = GENERATE(Type::Standard, Type::Secure); INFO(ToString(name.Type)); // Set the value on stream 1 Stream stream1{ name }; REQUIRE(stream1.Set(value1)); // Read the value on stream 2 to verify { Stream stream2{ name }; auto result = stream2.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value1 == settingValue); // Set the value on stream 2 REQUIRE(stream2.Set(value2)); } // Attempt to set the value on stream 1 again REQUIRE(!stream1.Set(value3)); // Attempting to set again should still not work REQUIRE(!stream1.Set(value3)); // Ensure that the value remains value 2 auto result = stream1.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value2 == settingValue); // Now that we have read it, we can update it REQUIRE(stream1.Set(value3)); result = stream1.Get(); REQUIRE(static_cast(result)); settingValue = ReadEntireStream(*result); REQUIRE(value3 == settingValue); } TEST_CASE("AttemptSetOnNewValue", "[settings]") { StreamDefinition name{ Type::Standard, "testsettingname" }; std::string value1 = "This is the test setting value1"; std::string value2 = "This is the test setting value2, which is different"; name.Type = GENERATE(Type::Standard, Type::Secure); INFO(ToString(name.Type)); // Remove the stream Stream{ name }.Remove(); Stream stream1{ name }; REQUIRE(!stream1.Get()); // Set the value on stream 2 { Stream stream2{ name }; REQUIRE(stream2.Set(value1)); } // Attempt to set the value on stream 1 again REQUIRE(!stream1.Set(value2)); // Attempting to set again should still not work REQUIRE(!stream1.Set(value2)); // Ensure that the value remains value 2 auto result = stream1.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value1 == settingValue); // Now that we have read it, we can update it REQUIRE(stream1.Set(value2)); result = stream1.Get(); REQUIRE(static_cast(result)); settingValue = ReadEntireStream(*result); REQUIRE(value2 == settingValue); } TEST_CASE("SetAndReadSettingEncrypted", "[settings]") { StreamDefinition name{ Type::Encrypted, "encrypted_test_setting" }; std::string value = "This is the test setting value"; Stream stream{ name }; REQUIRE(stream.Set(value)); auto result = stream.Get(); REQUIRE(static_cast(result)); std::string settingValue = ReadEntireStream(*result); REQUIRE(value == settingValue); // Ensure that the data is encrypted name.Type = Type::StandardFile; Stream streamDirect{ name }; auto resultDirect = streamDirect.Get(); REQUIRE(static_cast(resultDirect)); std::string settingValueDirect = ReadEntireStream(*resultDirect); REQUIRE(value != settingValueDirect); } ================================================ FILE: src/AppInstallerCLITests/ShowFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include #include using namespace TestCommon; using namespace AppInstaller::CLI; TEST_CASE("ShowFlow_SearchAndShowAppInfo", "[ShowFlow][workflow]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context, CreateTestSource({ TSR::TestQuery_ReturnOne })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestQuery_ReturnOne.Query); ShowCommand show({}); show.Execute(context); INFO(showOutput.str()); // Verify AppInfo is printed REQUIRE(showOutput.str().find("AppInstallerCliTest.TestExeInstaller") != std::string::npos); REQUIRE(showOutput.str().find("AppInstaller Test Exe Installer") != std::string::npos); REQUIRE(showOutput.str().find("1.0.0.0") != std::string::npos); REQUIRE(showOutput.str().find("https://ThisIsNotUsed") != std::string::npos); } TEST_CASE("ShowFlow_SearchAndShowAppVersion", "[ShowFlow][workflow]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForOpenSource(context, CreateTestSource({ TSR::TestQuery_ReturnOne })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestQuery_ReturnOne.Query); context.Args.AddArg(Execution::Args::Type::ListVersions); ShowCommand show({}); show.Execute(context); INFO(showOutput.str()); // Verify App version is printed REQUIRE(showOutput.str().find("1.0.0.0") != std::string::npos); // No manifest info is printed REQUIRE(showOutput.str().find(" Download Url: https://ThisIsNotUsed") == std::string::npos); } TEST_CASE("ShowFlow_Dependencies", "[ShowFlow][workflow][dependencies]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); ShowCommand show({}); show.Execute(context); INFO(showOutput.str()); // Verify all types of dependencies are printed REQUIRE(showOutput.str().find("Dependencies") != std::string::npos); REQUIRE(showOutput.str().find("WindowsFeaturesDep") != std::string::npos); REQUIRE(showOutput.str().find("WindowsLibrariesDep") != std::string::npos); // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) REQUIRE(showOutput.str().find("Package.Dep1-x64 [>= 1.0]") != std::string::npos); REQUIRE(showOutput.str().find("Package.Dep2-x64") != std::string::npos); REQUIRE(showOutput.str().find("Package.Dep2-x64 [") == std::string::npos); REQUIRE(showOutput.str().find("ExternalDep") != std::string::npos); } TEST_CASE("ShowFlow_InstallerType", "[ShowFlow][workflow]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); ShowCommand show({}); show.Execute(context); INFO(showOutput.str()); // Verify that just the base installed type is shown; REQUIRE(showOutput.str().find(Resource::LocString(Resource::String::ShowLabelInstallerType)) != std::string::npos); REQUIRE(showOutput.str().find("exe") != std::string::npos); // If the base installer is incorrectly shown, an open parenthesis would appear after the effective installer type REQUIRE(showOutput.str().find("exe (") == std::string::npos); } TEST_CASE("ShowFlow_NestedInstallerType", "[ShowFlow][workflow]") { std::ostringstream showOutput; TestContext context{ showOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); ShowCommand show({}); show.Execute(context); INFO(showOutput.str()); // Verify that both the effective and base installer types are shown REQUIRE(showOutput.str().find(Resource::LocString(Resource::String::ShowLabelInstallerType)) != std::string::npos); REQUIRE(showOutput.str().find("exe (zip)") != std::string::npos); } ================================================ FILE: src/AppInstallerCLITests/Sixel.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace AppInstaller::CLI::VirtualTerminal::Sixel; void ValidateGetPixel(std::string_view info, UINT_PTR offset, UINT byteCount, UINT_PTR expected) { INFO(info); REQUIRE(offset < byteCount); REQUIRE(offset == expected); } TEST_CASE("ImageView_GetPixel", "[sixel]") { UINT width = 20; UINT height = 20; UINT stride = 32; UINT byteCount = height * stride; BYTE* byteBase = reinterpret_cast(100); ImageView view{ width, height, stride, byteCount, byteBase }; ValidateGetPixel("No translation", view.GetPixel(3, 17) - byteBase, byteCount, 17 * stride + 3); view.Translate(14, 8, true); ValidateGetPixel("Positive translation (tile)", view.GetPixel(3, 17) - byteBase, byteCount, 9 * stride + 9); view.Translate(-14, 8, true); ValidateGetPixel("Negative translation (tile)", view.GetPixel(3, 17) - byteBase, byteCount, 9 * stride + 17); view.Translate(14, -8, false); REQUIRE(view.GetPixel(3, 17) == nullptr); ValidateGetPixel("Negative translation (no tile)", view.GetPixel(15, 1) - byteBase, byteCount, 9 * stride + 1); } TEST_CASE("Image_Render", "[sixel]") { Image image{ TestCommon::TestDataFile("notepad.ico") }; REQUIRE(!image.Render().Get().empty()); image.AspectRatio(AspectRatio::ThreeToOne); image.ColorCount(64); image.RenderSizeInCells(2, 1); image.UseRepeatSequence(true); REQUIRE(!image.Render().Get().empty()); } ================================================ FILE: src/AppInstallerCLITests/SourceFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include "TestHooks.h" #include "TestSettings.h" #include #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; void OverrideForSourceAddWithAgreements(TestContext& context, bool isAddExpected = true) { context.Override({ EnsureRunningAsAdmin, [](TestContext&) { } }); if (isAddExpected) { context.Override({ AddSource, [](TestContext&) { } }); } context.Override({ CreateSourceForSourceAdd, [](TestContext& context) { auto testSource = std::make_shared(); testSource->Information.SourceAgreementsIdentifier = "AgreementsIdentifier"; testSource->Information.SourceAgreements.emplace_back("Agreement Label", "Agreement Text", "https://test"); testSource->Information.RequiredPackageMatchFields.emplace_back("Market"); testSource->Information.RequiredQueryParameters.emplace_back("Market"); context << Workflow::HandleSourceAgreements(Source{ testSource }); } }); } TEST_CASE("SourceAddFlow_Agreement", "[SourceAddFlow][workflow]") { std::ostringstream sourceAddOutput; TestContext context{ sourceAddOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); context.Args.AddArg(Execution::Args::Type::SourceArg, "TestArg"sv); context.Args.AddArg(Execution::Args::Type::AcceptSourceAgreements); SourceAddCommand sourceAdd({}); sourceAdd.Execute(context); INFO(sourceAddOutput.str()); // Verify agreements are shown REQUIRE(sourceAddOutput.str().find("Agreement Label") != std::string::npos); REQUIRE(sourceAddOutput.str().find("Agreement Text") != std::string::npos); REQUIRE(sourceAddOutput.str().find("https://test") != std::string::npos); REQUIRE(sourceAddOutput.str().find(Resource::LocString(Resource::String::SourceAgreementsMarketMessage).get()) != std::string::npos); // Verify Installer is called. REQUIRE(context.GetTerminationHR() == S_OK); } TEST_CASE("SourceAddFlow_Agreement_Prompt_Yes", "[SourceAddFlow][workflow]") { // Accept the agreements by saying "Yes" at the prompt std::istringstream sourceAddInput{ "y" }; std::ostringstream sourceAddOutput; TestContext context{ sourceAddOutput, sourceAddInput }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); context.Args.AddArg(Execution::Args::Type::SourceArg, "TestArg"sv); SourceAddCommand sourceAdd({}); sourceAdd.Execute(context); INFO(sourceAddOutput.str()); // Verify agreements are shown REQUIRE(sourceAddOutput.str().find("Agreement Label") != std::string::npos); REQUIRE(sourceAddOutput.str().find("Agreement Text") != std::string::npos); REQUIRE(sourceAddOutput.str().find("https://test") != std::string::npos); REQUIRE(sourceAddOutput.str().find(Resource::LocString(Resource::String::SourceAgreementsMarketMessage).get()) != std::string::npos); // Verify Installer is called. REQUIRE(context.GetTerminationHR() == S_OK); } TEST_CASE("SourceAddFlow_Agreement_Prompt_No", "[SourceAddFlow][workflow]") { // Accept the agreements by saying "No" at the prompt std::istringstream sourceAddInput{ "n" }; std::ostringstream sourceAddOutput; TestContext context{ sourceAddOutput, sourceAddInput }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForSourceAddWithAgreements(context, false); context.Args.AddArg(Execution::Args::Type::SourceName, "TestSource"sv); context.Args.AddArg(Execution::Args::Type::SourceType, "Microsoft.Test"sv); context.Args.AddArg(Execution::Args::Type::SourceArg, "TestArg"sv); SourceAddCommand sourceAdd({}); sourceAdd.Execute(context); INFO(sourceAddOutput.str()); // Verify agreements are shown REQUIRE(sourceAddOutput.str().find("Agreement Label") != std::string::npos); REQUIRE(sourceAddOutput.str().find("Agreement Text") != std::string::npos); REQUIRE(sourceAddOutput.str().find("https://test") != std::string::npos); REQUIRE(sourceAddOutput.str().find(Resource::LocString(Resource::String::SourceAgreementsMarketMessage).get()) != std::string::npos); // Verify Installer is called. REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED); } TEST_CASE("OpenSource_WithCustomHeader", "[OpenSource][CustomHeader]") { SetSetting(Stream::UserSources, R"(Sources:)"sv); TestHook_ClearSourceFactoryOverrides(); SourceDetails details; details.Name = "restsource"; details.Type = "Microsoft.Rest"; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; std::string customHeader = "Test custom header in Open source Flow"; bool receivedCustomHeader = false; TestSourceFactory factory{ [&](const SourceDetails& sd, std::optional header) { receivedCustomHeader = header.value() == customHeader; return std::shared_ptr(new TestSource(sd)); } }; TestHook_SetSourceFactoryOverride(details.Type, factory); TestProgress progress; AddSource(details, progress); std::ostringstream output; TestContext context{ output, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::Query, "TestQuery"sv); context.Args.AddArg(Execution::Args::Type::CustomHeader, customHeader); context.Args.AddArg(Execution::Args::Type::Source, details.Name); AppInstaller::CLI::Workflow::OpenSource()(context); REQUIRE(receivedCustomHeader); } ================================================ FILE: src/AppInstallerCLITests/Sources.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestHooks.h" #include "TestSettings.h" #include "TestSource.h" #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Runtime; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; // Duplicating here because a change to these values in the product *REALLY* needs to be thought through. using namespace std::string_literals; using namespace std::string_view_literals; constexpr size_t c_DefaultSourceCount = 3; constexpr std::string_view s_SourcesYaml_Sources = "Sources"sv; constexpr std::string_view s_SourcesYaml_Source_Name = "Name"sv; constexpr std::string_view s_SourcesYaml_Source_Type = "Type"sv; constexpr std::string_view s_SourcesYaml_Source_Arg = "Arg"sv; constexpr std::string_view s_SourcesYaml_Source_Data = "Data"sv; constexpr std::string_view s_SourcesYaml_Source_TrustLevel = "TrustLevel"sv; constexpr std::string_view s_SourcesYaml_Source_Explicit = "Explicit"sv; constexpr std::string_view s_SourcesYaml_Source_LastUpdate = "LastUpdate"sv; constexpr std::string_view s_EmptySources = R"( Sources: )"sv; constexpr std::string_view s_DefaultSourcesTombstoned = R"( Sources: - Name: winget Type: "" Arg: "" Data: "" IsTombstone: true - Name: msstore Type: "" Arg: "" Data: "" IsTombstone: true - Name: winget-font Type: "" Arg: "" Data: "" IsTombstone: true )"sv; constexpr std::string_view s_SingleSource = R"( Sources: - Name: testName Type: testType Arg: testArg Data: testData IsTombstone: false )"sv; constexpr std::string_view s_SingleSourceOverride = R"( Sources: - Name: winget-font Type: "" Arg: "" Data: "" IsTombstone: false IsOverride: true Explicit: false Priority: 12 )"sv; constexpr std::string_view s_SingleSourceMetadata = R"( Sources: - Name: testName LastUpdate: 100 )"sv; constexpr std::string_view s_SingleSourceMetadataUpdate = R"( Sources: - Name: testName LastUpdate: 101 )"sv; constexpr std::string_view s_DoubleSource = R"( Sources: - Name: testName Type: testType Arg: testArg Data: testData IsTombstone: false - Name: testName2 Type: testType Arg: testArg2 Data: testData2 IsTombstone: false )"sv; constexpr std::string_view s_DoubleSourceMetadata = R"( Sources: - Name: testName LastUpdate: 100 - Name: testName2 LastUpdate: 200 )"sv; constexpr std::string_view s_ThreeSources = R"( Sources: - Name: testName Type: testType Arg: testArg Data: testData IsTombstone: false Priority: 1 - Name: testName2 Type: testType2 Arg: testArg2 Data: testData2 IsTombstone: false Priority: 5 - Name: testName3 Type: testType3 Arg: testArg3 Data: testData3 IsTombstone: false Priority: 3 - Name: winget Type: "" Arg: "" Data: "" IsTombstone: true - Name: msstore Type: "" Arg: "" Data: "" IsTombstone: true - Name: winget-font Type: "" Arg: "" Data: "" IsTombstone: true )"sv; constexpr std::string_view s_ThreeSourcesMetadata = R"( Sources: - Name: testName LastUpdate: 0 - Name: testName2 LastUpdate: 1 - Name: testName3 LastUpdate: 2 )"sv; constexpr std::string_view s_SingleSource_MissingArg = R"( Sources: - Name: testName Type: testType Data: testData IsTombstone: false )"sv; constexpr std::string_view s_TwoSource_AggregateSourceTest = R"( Sources: - Name: winget Type: testType Arg: testArg Data: testData IsTombstone: false - Name: msstore Type: testType Arg: testArg Data: testData IsTombstone: false )"sv; constexpr std::string_view s_DefaultSourceAsUserSource = R"( Sources: - Name: not-winget Type: Microsoft.PreIndexed.Package Arg: https://cdn.winget.microsoft.com/cache Data: Microsoft.Winget.Source_8wekyb3d8bbwe IsTombstone: false )"sv; constexpr std::string_view s_UserSourceNamedLikeDefault = R"( Sources: - Name: winget Type: testType Arg: testArg Data: testData IsTombstone: false )"sv; constexpr std::string_view s_SingleSource_AllProperties= R"( Sources: - Name: testName Type: testType Arg: testArg Data: testData IsTombstone: false TrustLevel: 3 Explicit: true Priority: 1 )"sv; namespace { // Helper to create a simple source. struct SourcesTestSource : public TestSource { SourcesTestSource() = default; SourcesTestSource(const SourceDetails& details) { Details = details; } static std::shared_ptr Create(const SourceDetails& details) { // using return std::make_shared(details); will crash the x86 test during destruction. return std::shared_ptr(new SourcesTestSource(details)); } SearchResult Search(const SearchRequest&) const override { SearchResult result; PackageMatchFilter testMatchFilter1{ PackageMatchField::Id, MatchType::Exact, "test" }; PackageMatchFilter testMatchFilter2{ PackageMatchField::Name, MatchType::Exact, "test" }; PackageMatchFilter testMatchFilter3{ PackageMatchField::Id, MatchType::CaseInsensitive, "test" }; result.Matches.emplace_back(nullptr, testMatchFilter1); result.Matches.emplace_back(nullptr, testMatchFilter2); result.Matches.emplace_back(nullptr, testMatchFilter3); return result; } }; // Failing source for use with s_TwoSource_AggregateSourceTest struct FailingSourcesTestSource : public TestSource { static constexpr HRESULT FailingHR = 0xBADDAD0D; FailingSourcesTestSource() = default; FailingSourcesTestSource(const SourceDetails& details) { Details = details; } static std::shared_ptr CreateFailWinget(const SourceDetails& details) { if (details.Name == "winget") { THROW_HR(FailingHR); } return std::shared_ptr(new FailingSourcesTestSource(details)); } static std::shared_ptr CreateFailAll(const SourceDetails&) { THROW_HR(FailingHR); } }; void RequireDefaultSourcesAt(const std::vector& sources, size_t index) { REQUIRE(sources.size() >= index + c_DefaultSourceCount); for (size_t i = index; i < sources.size(); ++i) { INFO(i); REQUIRE(sources[i].Origin == SourceOrigin::Default); } } } TEST_CASE("RepoSources_UserSettingDoesNotExist", "[sources]") { RemoveSetting(Stream::UserSources); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); RequireDefaultSourcesAt(sources, 0); } TEST_CASE("RepoSources_EmptySourcesList", "[sources]") { SetSetting(Stream::UserSources, s_EmptySources); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); RequireDefaultSourcesAt(sources, 0); } TEST_CASE("RepoSources_DefaultSourcesTombstoned", "[sources]") { SetSetting(Stream::UserSources, s_DefaultSourcesTombstoned); std::vector sources = GetSources(); REQUIRE(sources.empty()); } TEST_CASE("RepoSources_DefaultSourceOverride", "[sources]") { SetSetting(Stream::UserSources, s_EmptySources); // Default font has explicit to true. // Font is at index 2 as it is the third one added. auto beforeOverride = GetSources(); REQUIRE(beforeOverride.size() == c_DefaultSourceCount); REQUIRE(beforeOverride[2].Name == "winget-font"); REQUIRE(beforeOverride[2].Arg == "https://cdn.winget.microsoft.com/fonts"); REQUIRE(beforeOverride[2].Data == "Microsoft.Winget.Fonts.Source_8wekyb3d8bbwe"); REQUIRE(beforeOverride[2].Type == "Microsoft.PreIndexed.Package"); REQUIRE(beforeOverride[2].Origin == SourceOrigin::Default); REQUIRE(beforeOverride[2].Explicit == true); REQUIRE(beforeOverride[2].Priority == 0); SetSetting(Stream::UserSources, s_SingleSourceOverride); auto afterOverride = GetSources(); // The override will change the index value as the Default will be replaced by the override. // User sources have higher priority so the override will be at index 0. // We expect the same count, and the Name, Arg, Data, and Type properties to all be identical. // Only the name is defined in the override setting so all others should be properly populated. REQUIRE(afterOverride.size() == c_DefaultSourceCount); REQUIRE(afterOverride[0].Name == beforeOverride[2].Name); REQUIRE(afterOverride[0].Arg == beforeOverride[2].Arg); REQUIRE(afterOverride[0].Data == beforeOverride[2].Data); REQUIRE(afterOverride[0].Type == beforeOverride[2].Type); // The only properties we expect to be different are the Origin, which is now User, and Explicit. REQUIRE(afterOverride[0].Origin == SourceOrigin::User); REQUIRE(afterOverride[0].Explicit == false); REQUIRE(afterOverride[0].Priority == 12); } TEST_CASE("RepoSources_SingleSource", "[sources]") { SetSetting(Stream::UserSources, s_SingleSource); RemoveSetting(Stream::SourcesMetadata); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == "testName"); REQUIRE(sources[0].Type == "testType"); REQUIRE(sources[0].Arg == "testArg"); REQUIRE(sources[0].Data == "testData"); REQUIRE(sources[0].Origin == SourceOrigin::User); REQUIRE(sources[0].LastUpdateTime == ConvertUnixEpochToSystemClock(0)); RequireDefaultSourcesAt(sources, 1); } TEST_CASE("RepoSources_SingleSource_AllProperties", "[sources]") { SetSetting(Stream::UserSources, s_SingleSource_AllProperties); RemoveSetting(Stream::SourcesMetadata); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == "testName"); REQUIRE(sources[0].Type == "testType"); REQUIRE(sources[0].Arg == "testArg"); REQUIRE(sources[0].Data == "testData"); REQUIRE(sources[0].Origin == SourceOrigin::User); REQUIRE(sources[0].Explicit == true); REQUIRE(sources[0].Priority == 1); REQUIRE(WI_IsFlagSet(sources[0].TrustLevel, SourceTrustLevel::Trusted)); REQUIRE(WI_IsFlagSet(sources[0].TrustLevel, SourceTrustLevel::StoreOrigin)); REQUIRE(sources[0].LastUpdateTime == ConvertUnixEpochToSystemClock(0)); RequireDefaultSourcesAt(sources, 1); } TEST_CASE("RepoSources_ThreeSources", "[sources]") { SetSetting(Stream::UserSources, s_ThreeSources); SetSetting(Stream::SourcesMetadata, s_ThreeSourcesMetadata); const char* suffixStrings[3] = { "", "2", "3" }; size_t suffixUnsorted[3] = { 0, 1, 2 }; size_t suffixPrioritySorted[3] = { 1, 2, 0 }; size_t* suffix = nullptr; std::unique_ptr override; SECTION("Unsorted") { suffix = suffixUnsorted; } SECTION("Priority Sorted") { override = std::make_unique(ExperimentalFeature::Feature::SourcePriority); suffix = suffixPrioritySorted; } std::vector sources = GetSources(); REQUIRE(sources.size() == 3); for (size_t index = 0; index < 3; ++index) { size_t i = suffix[index]; INFO("Source #" << index << " [" << i << "]"); REQUIRE(sources[index].Name == "testName"s + suffixStrings[i]); REQUIRE(sources[index].Type == "testType"s + suffixStrings[i]); REQUIRE(sources[index].Arg == "testArg"s + suffixStrings[i]); REQUIRE(sources[index].Data == "testData"s + suffixStrings[i]); REQUIRE(sources[index].LastUpdateTime == ConvertUnixEpochToSystemClock(i)); REQUIRE(sources[index].Origin == SourceOrigin::User); } } TEST_CASE("RepoSources_InvalidYAML", "[sources]") { SetSetting(Stream::UserSources, "Name: Value : BAD"); REQUIRE_NOTHROW(GetSources()); } TEST_CASE("RepoSources_MissingField", "[sources]") { SetSetting(Stream::UserSources, s_SingleSource_MissingArg); REQUIRE_NOTHROW(GetSources()); } TEST_CASE("RepoSources_AddSource", "[sources]") { SetSetting(Stream::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); SourceDetails details; details.Name = "thisIsTheName"; details.Type = "thisIsTheType"; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; details.TrustLevel = Repository::SourceTrustLevel::None; details.Explicit = false; details.Priority = 42; bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = details.Data; }; TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; AddSource(details, progress); REQUIRE(addCalledOnFactory); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == details.Name); REQUIRE(sources[0].Type == details.Type); REQUIRE(sources[0].Arg == details.Arg); REQUIRE(sources[0].Data == details.Data); REQUIRE(sources[0].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); REQUIRE(sources[0].Origin == SourceOrigin::User); REQUIRE(sources[0].TrustLevel == details.TrustLevel); REQUIRE(sources[0].Explicit == details.Explicit); REQUIRE(sources[0].Priority == details.Priority); RequireDefaultSourcesAt(sources, 1); } TEST_CASE("RepoSources_AddMultipleSources", "[sources]") { SetSetting(Stream::UserSources, s_EmptySources); SourceDetails details; details.Name = "thisIsTheName"; details.Type = "thisIsTheType"; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; const char* suffix[2] = { "", "2" }; TestSourceFactory factory1{ SourcesTestSource::Create }; factory1.OnAdd = [&](SourceDetails& sd) { sd.Data = details.Data; }; TestHook_SetSourceFactoryOverride(details.Type, factory1); ProgressCallback progress; AddSource(details, progress); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == details.Name); REQUIRE(sources[0].Type == details.Type); REQUIRE(sources[0].Arg == details.Arg); REQUIRE(sources[0].Data == details.Data); REQUIRE(sources[0].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); REQUIRE(sources[0].Origin == SourceOrigin::User); RequireDefaultSourcesAt(sources, 1); SourceDetails details2; details2.Name = details.Name + suffix[1]; details2.Type = details.Type + suffix[1]; details2.Arg = details.Arg + suffix[1]; details2.Data = details.Data + suffix[1]; TestSourceFactory factory2{ SourcesTestSource::Create }; factory2.OnAdd = [&](SourceDetails& sd) { sd.Data = details2.Data; }; TestHook_SetSourceFactoryOverride(details2.Type, factory2); AddSource(details2, progress); sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 2); for (size_t i = 0; i < 2; ++i) { INFO("Source #" << i); REQUIRE(sources[i].Name == details.Name + suffix[i]); REQUIRE(sources[i].Type == details.Type + suffix[i]); REQUIRE(sources[i].Arg == details.Arg + suffix[i]); REQUIRE(sources[i].Data == details.Data + suffix[i]); REQUIRE(sources[i].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); REQUIRE(sources[i].Origin == SourceOrigin::User); } RequireDefaultSourcesAt(sources, 2); } TEST_CASE("RepoSources_UpdateSource", "[sources]") { using namespace std::chrono_literals; SetSetting(Stream::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); SourceDetails details; details.Name = "thisIsTheName"; details.Type = "thisIsTheType"; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = details.Data; }; TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; AddSource(details, progress); REQUIRE(addCalledOnFactory); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == details.Name); REQUIRE(sources[0].Type == details.Type); REQUIRE(sources[0].Arg == details.Arg); REQUIRE(sources[0].Data == details.Data); REQUIRE(sources[0].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); REQUIRE(sources[0].Origin == SourceOrigin::User); RequireDefaultSourcesAt(sources, 1); // Reset for a call to update bool updateCalledOnFactory = false; auto now = std::chrono::system_clock::now(); factory.OnUpdate = [&](const SourceDetails&) { updateCalledOnFactory = true; }; UpdateSource(details.Name, progress); REQUIRE(updateCalledOnFactory); sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == details.Name); REQUIRE(sources[0].Type == details.Type); REQUIRE(sources[0].Arg == details.Arg); REQUIRE(sources[0].Data == details.Data); REQUIRE((now - sources[0].LastUpdateTime) < 1s); } TEST_CASE("RepoSources_UpdateSourceRetries", "[sources]") { using namespace std::chrono_literals; SetSetting(Stream::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); SourceDetails details; details.Name = "thisIsTheName"; details.Type = "thisIsTheType"; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnAdd = [&](SourceDetails& sd) { sd.Data = details.Data; }; TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; AddSource(details, progress); // Reset for a call to update bool updateShouldThrow = false; bool updateCalledOnFactoryAgain = false; factory.OnUpdate = [&](const SourceDetails&) { if (updateShouldThrow) { updateShouldThrow = false; THROW_HR(E_ACCESSDENIED); } updateCalledOnFactoryAgain = true; }; UpdateSource(details.Name, progress); REQUIRE(updateCalledOnFactoryAgain); } TEST_CASE("RepoSources_RemoveSource", "[sources]") { SetSetting(Stream::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); SourceDetails details; details.Name = "thisIsTheName"; details.Type = "thisIsTheType"; details.Arg = "thisIsTheArg"; details.Data = "thisIsTheData"; bool removeCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnRemove = [&](const SourceDetails&) { removeCalledOnFactory = true; }; TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; AddSource(details, progress); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); RemoveSource(details.Name, progress); REQUIRE(removeCalledOnFactory); sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); } TEST_CASE("RepoSources_RemoveDefaultSource", "[sources]") { SetSetting(Stream::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Origin == SourceOrigin::Default); bool removeCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnRemove = [&](const SourceDetails&) { removeCalledOnFactory = true; }; TestHook_SetSourceFactoryOverride(sources[0].Type, factory); ProgressCallback progress; RemoveSource(sources[0].Name, progress); REQUIRE(removeCalledOnFactory); sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount - 1); } TEST_CASE("RepoSources_UpdateOnOpen", "[sources]") { using namespace std::chrono_literals; TestHook_ClearSourceFactoryOverrides(); std::string name = "testName"; std::string type = "testType"; std::string arg = "testArg"; std::string data = "testData"; bool updateCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnUpdate = [&](const SourceDetails&) { updateCalledOnFactory = true; }; factory.ShouldUpdateBeforeOpenResult = true; TestHook_SetSourceFactoryOverride(type, factory); SetSetting(Stream::UserSources, s_SingleSource); ProgressCallback progress; auto source = OpenSource(name, progress); REQUIRE(updateCalledOnFactory); std::vector sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[0].Name == name); REQUIRE(sources[0].Type == type); REQUIRE(sources[0].Arg == arg); REQUIRE(sources[0].Data == data); REQUIRE(sources[0].LastUpdateTime != ConvertUnixEpochToSystemClock(0)); } TEST_CASE("RepoSources_DropSourceByName", "[sources]") { SetSetting(Stream::UserSources, s_ThreeSources); SetSetting(Stream::SourcesMetadata, s_ThreeSourcesMetadata); std::vector sources = GetSources(); REQUIRE(sources.size() == 3); DropSource("testName"); sources = GetSources(); REQUIRE(sources.size() == 2); const char* suffix[2] = { "2", "3" }; for (size_t i = 0; i < 2; ++i) { INFO("Source #" << i); REQUIRE(sources[i].Name == "testName"s + suffix[i]); REQUIRE(sources[i].Type == "testType"s + suffix[i]); REQUIRE(sources[i].Arg == "testArg"s + suffix[i]); REQUIRE(sources[i].Data == "testData"s + suffix[i]); REQUIRE(sources[i].LastUpdateTime == ConvertUnixEpochToSystemClock(i + 1)); REQUIRE(sources[i].Origin == SourceOrigin::User); } } TEST_CASE("RepoSources_DropAllSources", "[sources]") { SetSetting(Stream::UserSources, s_ThreeSources); std::vector sources = GetSources(); REQUIRE(sources.size() == 3); DropSource({}); sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Origin == SourceOrigin::Default); } TEST_CASE("RepoSources_SearchAcrossMultipleSources", "[sources]") { TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ SourcesTestSource::Create }; TestHook_SetSourceFactoryOverride("testType", factory); SetSetting(Stream::UserSources, s_TwoSource_AggregateSourceTest); ProgressCallback progress; auto source = OpenSource("", progress); SearchRequest request; auto result = source.Search(request); REQUIRE(result.Matches.size() == 6); REQUIRE_FALSE(result.Truncated); // matches are sorted in expected order REQUIRE((result.Matches[0].MatchCriteria.Type == MatchType::Exact && result.Matches[0].MatchCriteria.Field == PackageMatchField::Id)); REQUIRE((result.Matches[1].MatchCriteria.Type == MatchType::Exact && result.Matches[1].MatchCriteria.Field == PackageMatchField::Id)); REQUIRE((result.Matches[2].MatchCriteria.Type == MatchType::Exact && result.Matches[2].MatchCriteria.Field == PackageMatchField::Name)); REQUIRE((result.Matches[3].MatchCriteria.Type == MatchType::Exact && result.Matches[3].MatchCriteria.Field == PackageMatchField::Name)); REQUIRE((result.Matches[4].MatchCriteria.Type == MatchType::CaseInsensitive && result.Matches[4].MatchCriteria.Field == PackageMatchField::Id)); REQUIRE((result.Matches[5].MatchCriteria.Type == MatchType::CaseInsensitive && result.Matches[5].MatchCriteria.Field == PackageMatchField::Id)); // when truncate required request.MaximumResults = 3; result = source.Search(request); REQUIRE(result.Matches.size() == 3); REQUIRE(result.Truncated); // matches are sorted in expected order REQUIRE((result.Matches[0].MatchCriteria.Type == MatchType::Exact && result.Matches[0].MatchCriteria.Field == PackageMatchField::Id)); REQUIRE((result.Matches[1].MatchCriteria.Type == MatchType::Exact && result.Matches[1].MatchCriteria.Field == PackageMatchField::Id)); REQUIRE((result.Matches[2].MatchCriteria.Type == MatchType::Exact && result.Matches[2].MatchCriteria.Field == PackageMatchField::Name)); } TEST_CASE("RepoSources_GroupPolicy_DefaultSource", "[sources][groupPolicy]") { WHEN("Default source is disabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::DefaultSource, PolicyState::Disabled); SECTION("Get source") { // Listing the sources should not return the default. SetSetting(Stream::UserSources, s_EmptySources); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount - 1); } SECTION("Add default source") { // We should not be able to add the default source manually. SetSetting(Stream::UserSources, s_EmptySources); ProgressCallback progress; SourceDetails details; details.Name = "winget"; details.Type = "Microsoft.PreIndexed.Package"; details.Arg = "https://cdn.winget.microsoft.com/cache"; REQUIRE_POLICY_EXCEPTION( AddSource(details, progress), TogglePolicy::Policy::DefaultSource); } SECTION("Ignore default source from user") { // We should ignore any existing user source that is the same as the default. SetSetting(Stream::UserSources, s_DefaultSourceAsUserSource); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount - 1); } SECTION("Add same-name source from user") { // We should allow adding sources with the same name as the default but // pointing somewhere else. SetSetting(Stream::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); SourceDetails details; details.Name = "winget"; details.Type = "someType"; details.Arg = "notWingetRealArg"; details.Data = "someData"; bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = details.Data; }; TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; AddSource(details, progress); REQUIRE(addCalledOnFactory); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Name == details.Name); REQUIRE(sources[0].Type == details.Type); REQUIRE(sources[0].Arg == details.Arg); REQUIRE(sources[0].Data == details.Data); REQUIRE(sources[0].Origin == SourceOrigin::User); } SECTION("Allow same name source from user") { // We should respect existing user sources with the same name. // We should allow adding sources with the same name as the default but // pointing somewhere else. SetSetting(Stream::UserSources, s_UserSourceNamedLikeDefault); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Name == "winget"); REQUIRE(sources[0].Type == "testType"); REQUIRE(sources[0].Arg == "testArg"); REQUIRE(sources[0].Data == "testData"); REQUIRE(sources[0].Origin == SourceOrigin::User); } } WHEN("Default source is enabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::DefaultSource, PolicyState::Enabled); SECTION("Remove source is blocked") { // We should not be able to remove the default source. SetSetting(Stream::UserSources, s_EmptySources); ProgressCallback progress; REQUIRE_POLICY_EXCEPTION( RemoveSource("winget", progress), TogglePolicy::Policy::DefaultSource); } SECTION("Tombstone is overridden") { // We should ignore if the default source was already deleted. SetSetting(Stream::UserSources, s_DefaultSourcesTombstoned); auto sources = GetSources(); REQUIRE(sources.size() == 1); REQUIRE(sources[0].Name == "winget"); REQUIRE(sources[0].Origin == SourceOrigin::Default); } SECTION("Same name source is overridden") { // We should ignore existing user sources with the same name as the default. SetSetting(Stream::UserSources, s_UserSourceNamedLikeDefault); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[1].Name == "winget"); REQUIRE(sources[1].Arg == "https://cdn.winget.microsoft.com/cache"); REQUIRE(sources[1].Origin == SourceOrigin::Default); } } } TEST_CASE("RepoSources_GroupPolicy_AdditionalSources", "[sources][groupPolicy]") { WHEN("Additional sources are enabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::AdditionalSources, PolicyState::Enabled); SECTION("Additional sources are listed") { // Getting the current sources should list the additional sources. std::vector policySources; const std::string suffix[3] = { "", "2", "3" }; for (size_t i = 0; i < 3; ++i) { SourceFromPolicy source; source.Name = "name" + suffix[i]; source.Type = "type" + suffix[i]; source.Arg = "arg" + suffix[i]; source.Data = "data" + suffix[i]; source.Identifier = "id" + suffix[i]; policySources.emplace_back(std::move(source)); } policies.SetValue(policySources); SetSetting(Stream::UserSources, s_EmptySources); auto sources = GetSources(); // The source list includes the default source REQUIRE(sources.size() == policySources.size() + c_DefaultSourceCount); REQUIRE(sources.back().Origin == SourceOrigin::Default); for (size_t i = 0; i < policySources.size(); ++i) { REQUIRE(sources[i].Name == policySources[i].Name); REQUIRE(sources[i].Type == policySources[i].Type); REQUIRE(sources[i].Arg == policySources[i].Arg); REQUIRE(sources[i].Data == policySources[i].Data); REQUIRE(sources[i].Identifier == policySources[i].Identifier); REQUIRE(sources[i].Origin == SourceOrigin::GroupPolicy); } } SECTION("Same-name user source is overridden") { // User sources with the same name as an additional source are ignored. SourceFromPolicy policySource; policySource.Name = "testName"; policySource.Type = "notTestType"; policySource.Arg = "notTestArg"; policySource.Data = "notTestData"; policySource.Identifier = "notTestId"; policies.SetValue({ policySource }); SetSetting(Stream::UserSources, s_SingleSource); auto sources = GetSources(); // The source list includes the default source REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[1].Origin == SourceOrigin::Default); REQUIRE(sources[0].Name == policySource.Name); REQUIRE(sources[0].Type == policySource.Type); REQUIRE(sources[0].Arg == policySource.Arg); REQUIRE(sources[0].Data == policySource.Data); REQUIRE(sources[0].Identifier == policySource.Identifier); REQUIRE(sources[0].Origin == SourceOrigin::GroupPolicy); } SECTION("Cannot remove additional source") { // An additional source cannot be removed. SourceFromPolicy policySource; policySource.Name = "name"; policySource.Type = "type"; policySource.Arg = "arg"; policySource.Data = "data"; policySource.Identifier = "id"; bool removeCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnRemove = [&](const SourceDetails&) { removeCalledOnFactory = true; }; TestHook_SetSourceFactoryOverride(policySource.Type, factory); policies.SetValue({ policySource }); SetSetting(Stream::UserSources, s_EmptySources); ProgressCallback progress; REQUIRE_POLICY_EXCEPTION( RemoveSource(policySource.Name, progress), TogglePolicy::Policy::AdditionalSources); REQUIRE_FALSE(removeCalledOnFactory); } SECTION("Additional source overrides default") { // An additional source with the same name as a default overrides it. SourceFromPolicy policySource; policySource.Name = "winget"; policySource.Type = "notDefaultType"; policySource.Arg = "notDefaultArg"; policySource.Data = "notDefaultData"; policySource.Identifier = "notDefaultId"; policies.SetValue({ policySource }); SetSetting(Stream::UserSources, s_EmptySources); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Name == policySource.Name); REQUIRE(sources[0].Type == policySource.Type); REQUIRE(sources[0].Arg == policySource.Arg); REQUIRE(sources[0].Data == policySource.Data); REQUIRE(sources[0].Identifier == policySource.Identifier); REQUIRE(sources[0].Origin == SourceOrigin::GroupPolicy); } } } TEST_CASE("RepoSources_GroupPolicy_AllowedSources", "[sources][groupPolicy]") { WHEN("Allowed sources are enabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::AllowedSources, PolicyState::Enabled); SECTION("Add allowed source") { // We should be able to add sources in the allow list. SourceFromPolicy policySource; policySource.Name = "testName"; policySource.Type = "testType"; policySource.Arg = "testArg"; policySource.Data = "testData"; policySource.Identifier = "testId"; policies.SetValue({ policySource }); SetSetting(Stream::UserSources, s_EmptySources); TestHook_ClearSourceFactoryOverrides(); bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnAdd = [&](SourceDetails& sd) { addCalledOnFactory = true; sd.Data = policySource.Data; sd.Identifier = policySource.Identifier; }; TestHook_SetSourceFactoryOverride(policySource.Type, factory); ProgressCallback progress; SourceDetails details; details.Name = policySource.Name; details.Type = policySource.Type; details.Arg = policySource.Arg; AddSource(details, progress); REQUIRE(addCalledOnFactory); // The source list includes the default source auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount + 1); REQUIRE(sources[1].Origin == SourceOrigin::Default); REQUIRE(sources[0].Name == policySource.Name); REQUIRE(sources[0].Type == policySource.Type); REQUIRE(sources[0].Arg == policySource.Arg); REQUIRE(sources[0].Data == policySource.Data); REQUIRE(sources[0].Identifier == policySource.Identifier); REQUIRE(sources[0].Origin == SourceOrigin::User); } SECTION("Cannot add non-allowed source") { // We should not be allowed to add anything not matching the allow list. SourceFromPolicy policySource; policySource.Name = "testName"; policySource.Type = "testType"; policySource.Arg = "testArg"; policySource.Data = "testData"; policySource.Identifier = "testId"; policies.SetValue({ policySource }); SetSetting(Stream::UserSources, s_EmptySources); ProgressCallback progress; SourceDetails details; details.Name = "notAllowed"; details.Type = "type"; details.Arg = "arg"; bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnAdd = [&](SourceDetails&) { addCalledOnFactory = true; }; TestHook_SetSourceFactoryOverride(details.Type, factory); REQUIRE_POLICY_EXCEPTION( AddSource(details, progress), TogglePolicy::Policy::AllowedSources); REQUIRE_FALSE(addCalledOnFactory); } } WHEN("Allowed sources are disabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::AllowedSources, PolicyState::Disabled); SECTION("Cannot add any source") { SetSetting(Stream::UserSources, s_EmptySources); ProgressCallback progress; SourceDetails details; details.Name = "name"; details.Type = "type"; details.Arg = "arg"; bool addCalledOnFactory = false; TestSourceFactory factory{ SourcesTestSource::Create }; factory.OnAdd = [&](SourceDetails&) { addCalledOnFactory = true; }; TestHook_SetSourceFactoryOverride(details.Type, factory); REQUIRE_POLICY_EXCEPTION( AddSource(details, progress), TogglePolicy::Policy::AllowedSources); REQUIRE_FALSE(addCalledOnFactory); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Origin == SourceOrigin::Default); } SECTION("Existing sources are ignored") { SetSetting(Stream::UserSources, s_SingleSource); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); REQUIRE(sources[0].Origin == SourceOrigin::Default); } } } TEST_CASE("RepoSources_OpenMultipleWithSingleFailure", "[sources]") { TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ FailingSourcesTestSource::CreateFailWinget }; TestHook_SetSourceFactoryOverride("testType", factory); SetSetting(Stream::UserSources, s_TwoSource_AggregateSourceTest); ProgressCallback progress; auto result = OpenSource("", progress); REQUIRE(result); SearchResult searchResult = result.Search({}); REQUIRE(searchResult.Failures.size() == 1); HRESULT openFailure = S_OK; try { std::rethrow_exception(searchResult.Failures[0].Exception); } catch (const wil::ResultException& re) { openFailure = re.GetErrorCode(); } catch (...) {} REQUIRE(openFailure == FailingSourcesTestSource::FailingHR); } TEST_CASE("RepoSources_OpenMultipleWithTotalFailure", "[sources]") { TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ FailingSourcesTestSource::CreateFailAll }; TestHook_SetSourceFactoryOverride("testType", factory); SetSetting(Stream::UserSources, s_TwoSource_AggregateSourceTest); ProgressCallback progress; REQUIRE_THROWS_HR(OpenSource("", progress), APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES); } TEST_CASE("RepoSources_UpdateSettingsDuringAction_SourcesUpdate", "[sources]") { SetSetting(Stream::UserSources, s_SingleSource); SetSetting(Stream::SourcesMetadata, s_SingleSourceMetadata); std::string userSourcesUpdate{ s_DoubleSource }; std::string sourcesMetadataUpdate{ s_DoubleSourceMetadata }; std::string singleSourceName = "testName"; std::string doubleSourceName = "testName2"; std::string unusedSourceName = "unusedName"; std::string unusedSourceArg = "unusedArg"; std::string testSourceType = "testType"; TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ FailingSourcesTestSource::CreateFailAll }; auto settingsUpdate = [&](const AppInstaller::Repository::SourceDetails&) { SetSetting(Stream::UserSources, userSourcesUpdate); SetSetting(Stream::SourcesMetadata, sourcesMetadataUpdate); }; factory.OnAdd = settingsUpdate; factory.OnUpdate = settingsUpdate; factory.OnRemove = settingsUpdate; TestHook_SetSourceFactoryOverride(testSourceType, factory); ProgressCallback progress; SECTION("Add") { SourceDetails addedSource; addedSource.Name = unusedSourceName; addedSource.Type = testSourceType; addedSource.Arg = unusedSourceArg; AddSource(addedSource, progress); auto sources = GetSources(); REQUIRE(sources.size() == 3 + c_DefaultSourceCount); REQUIRE(sources[0].Name == singleSourceName); REQUIRE(sources[1].Name == doubleSourceName); REQUIRE(sources[2].Name == addedSource.Name); } SECTION("Add conflicting") { SourceDetails addedSource; addedSource.Name = doubleSourceName; addedSource.Type = testSourceType; addedSource.Arg = unusedSourceArg; REQUIRE_THROWS_HR(AddSource(addedSource, progress), APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS); } SECTION("Update") { UpdateSource(singleSourceName, progress); auto sources = GetSources(); REQUIRE(sources.size() == 2 + c_DefaultSourceCount); REQUIRE(sources[0].Name == singleSourceName); REQUIRE(sources[1].Name == doubleSourceName); } SECTION("Remove") { RemoveSource(singleSourceName, progress); auto sources = GetSources(); REQUIRE(sources.size() == 1 + c_DefaultSourceCount); REQUIRE(sources[0].Name == doubleSourceName); } SECTION("Remove already removed") { userSourcesUpdate = s_EmptySources; sourcesMetadataUpdate = s_EmptySources; RemoveSource(singleSourceName, progress); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); } } TEST_CASE("RepoSources_UpdateSettingsDuringAction_MetadataUpdate", "[sources]") { SetSetting(Stream::UserSources, s_SingleSource); SetSetting(Stream::SourcesMetadata, s_SingleSourceMetadata); std::string sourcesMetadataUpdate{ s_SingleSourceMetadataUpdate }; int64_t updateTime = 101; std::string singleSourceName = "testName"; std::string doubleSourceName = "testName2"; std::string unusedSourceName = "unusedName"; std::string unusedSourceArg = "unusedArg"; std::string testSourceType = "testType"; TestHook_ClearSourceFactoryOverrides(); TestSourceFactory factory{ FailingSourcesTestSource::CreateFailAll }; auto settingsUpdate = [&](const AppInstaller::Repository::SourceDetails&) { SetSetting(Stream::SourcesMetadata, sourcesMetadataUpdate); }; factory.OnAdd = settingsUpdate; factory.OnUpdate = settingsUpdate; factory.OnRemove = settingsUpdate; TestHook_SetSourceFactoryOverride(testSourceType, factory); ProgressCallback progress; SECTION("Add") { SourceDetails addedSource; addedSource.Name = unusedSourceName; addedSource.Type = testSourceType; addedSource.Arg = unusedSourceArg; AddSource(addedSource, progress); auto sources = GetSources(); REQUIRE(sources.size() == 2 + c_DefaultSourceCount); REQUIRE(sources[0].Name == singleSourceName); REQUIRE(ConvertSystemClockToUnixEpoch(sources[0].LastUpdateTime) == updateTime); REQUIRE(sources[1].Name == addedSource.Name); } SECTION("Update") { UpdateSource(singleSourceName, progress); auto sources = GetSources(); REQUIRE(sources.size() == 1 + c_DefaultSourceCount); REQUIRE(sources[0].Name == singleSourceName); REQUIRE(ConvertSystemClockToUnixEpoch(sources[0].LastUpdateTime) > updateTime); } SECTION("Remove") { RemoveSource(singleSourceName, progress); auto sources = GetSources(); REQUIRE(sources.size() == c_DefaultSourceCount); } } TEST_CASE("RepoSources_RestoringWellKnownSource", "[sources]") { TestHook_ClearSourceFactoryOverrides(); RemoveSetting(Stream::UserSources); Source storeSource{ WellKnownSource::MicrosoftStore }; SourceDetails details = storeSource.GetDetails(); REQUIRE(!details.CertificatePinningConfiguration.IsEmpty()); TestSourceFactory factory{ SourcesTestSource::Create }; TestHook_SetSourceFactoryOverride(details.Type, factory); ProgressCallback progress; REQUIRE(storeSource.Remove(progress)); Source storeAfterRemove{ details.Name }; REQUIRE(!storeAfterRemove); SECTION("with well known name") { Source addStoreBack{ details.Name, details.Arg, details.Type, Repository::SourceTrustLevel::None, {} }; REQUIRE(addStoreBack.Add(progress)); Source storeAfterAdd{ details.Name }; REQUIRE(storeAfterAdd); REQUIRE(!storeAfterAdd.GetDetails().CertificatePinningConfiguration.IsEmpty()); } SECTION("with different name") { std::string newName = details.Name + "_new"; Source addStoreBack{ newName, details.Arg, details.Type, Repository::SourceTrustLevel::None, {} }; REQUIRE(addStoreBack.Add(progress)); Source storeAfterAdd{ newName }; REQUIRE(storeAfterAdd); REQUIRE(storeAfterAdd.GetDetails().CertificatePinningConfiguration.IsEmpty()); } } TEST_CASE("RepoSources_GroupPolicy_BypassCertificatePinningForMicrosoftStore", "[sources][groupPolicy]") { TestHook_ClearSourceFactoryOverrides(); SECTION("Not configured") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore, PolicyState::NotConfigured); Source source(WellKnownSource::MicrosoftStore); REQUIRE_FALSE(source.GetDetails().CertificatePinningConfiguration.IsEmpty()); } SECTION("Enabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore, PolicyState::Enabled); Source source(WellKnownSource::MicrosoftStore); REQUIRE(source.GetDetails().CertificatePinningConfiguration.IsEmpty()); } SECTION("Disabled") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore, PolicyState::Disabled); Source source(WellKnownSource::MicrosoftStore); REQUIRE_FALSE(source.GetDetails().CertificatePinningConfiguration.IsEmpty()); } } TEST_CASE("RepoSources_BuiltInDesktopFrameworkSourceAlwaysCreatable", "[sources]") { Source source(WellKnownSource::DesktopFrameworks); REQUIRE(source); } TEST_CASE("RepoSources_MicrosoftStore_CertificatePinningLifetimeCheck", "[sources]") { TestHook_ClearSourceFactoryOverrides(); GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore, PolicyState::Disabled); Source source(WellKnownSource::MicrosoftStore); REQUIRE_FALSE(source.GetDetails().CertificatePinningConfiguration.IsEmpty()); // The configuration's remaining lifetime is the *maximum* of the remaining lifetimes of the individual chains. // A chain's remaining lifetime is the *minimum* of the remaining lifetimes of the individual certificates. // A certificate's remaining lifetime is a value between 0.0 and 1.0 that is the ratio of remaining valid time to total valid time. // The goal of this test is to warn when the pinning configuration may be in danger of expiration; either via certificate validity or // more likely by renewals causing the pinning to reject the new, correct certificates. It operates in percentage lifetime to normalize // the values across the chain. INFO("If this test has failed, the pinning certificates may be nearing expiration and should be investigated."); double lifetimePercentage = source.GetDetails().CertificatePinningConfiguration.GetRemainingLifetimePercentage(); REQUIRE(lifetimePercentage > 0.25); } ================================================ FILE: src/AppInstallerCLITests/Strings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; TEST_CASE("UTF8Length", "[strings]") { REQUIRE(UTF8Length("") == 0); REQUIRE(UTF8Length("a") == 1); REQUIRE(UTF8Length(" a b c ") == 7); REQUIRE(UTF8Length("K\xC3\xA4se") == 4); // "Käse" REQUIRE(UTF8Length("bye\xE2\x80\xA6") == 4); // "bye…" REQUIRE(UTF8Length("\xf0\x9f\xa6\x86") == 1); // [duck emoji] REQUIRE(UTF8Length("\xf0\x9d\x85\xa0\xf0\x9d\x85\xa0") == 2); // [8th note][8th note] } TEST_CASE("UTF8Substring", "[strings]") { REQUIRE(UTF8Substring("", 0, 0) == ""); REQUIRE(UTF8Substring("abcd", 0, 4) == "abcd"); REQUIRE(UTF8Substring("abcd", 0, 5) == "abcd"); REQUIRE(UTF8Substring("abcd", 0, 2) == "ab"); REQUIRE(UTF8Substring("abcd", 1, 0) == ""); REQUIRE(UTF8Substring("abcd", 1, 1) == "b"); REQUIRE(UTF8Substring("abcd", 1, 3) == "bcd"); REQUIRE(UTF8Substring("abcd", 4, 0) == ""); const char* s = "\xf0\x9f\xa6\x86s like \xf0\x9f\x8c\x8a"; // [duck emoji]s like [wave emoji] REQUIRE(UTF8Substring(s, 0, 9) == "\xf0\x9f\xa6\x86s like \xf0\x9f\x8c\x8a"); REQUIRE(UTF8Substring(s, 0, 1) == "\xf0\x9f\xa6\x86"); REQUIRE(UTF8Substring(s, 0, 2) == "\xf0\x9f\xa6\x86s"); REQUIRE(UTF8Substring(s, 1, 7) == "s like "); REQUIRE(UTF8Substring(s, 1, 8) == "s like \xf0\x9f\x8c\x8a"); } TEST_CASE("UTF8ColumnWidth", "[strings]") { REQUIRE(UTF8ColumnWidth("") == 0); REQUIRE(UTF8ColumnWidth("a") == 1); REQUIRE(UTF8ColumnWidth(" a b c ") == 7); REQUIRE(UTF8ColumnWidth("K\xC3\xA4se") == 4); // "Käse" REQUIRE(UTF8ColumnWidth("bye\xE2\x80\xA6") == 4); // "bye…" REQUIRE(UTF8ColumnWidth("fi\xEF\xAC\x81") == 3); // "fi[fi]" [fi] is not decoupled REQUIRE(UTF8ColumnWidth("\xf0\x9f\xa6\x86") == 2); // [duck emoji] REQUIRE(UTF8ColumnWidth("\xf0\x9d\x85\xa0\xf0\x9d\x85\xa0") == 2); // [8th note][8th note] REQUIRE(UTF8ColumnWidth("\xe6\xb5\x8b\xe8\xaf\x95") == 4); // 测试 REQUIRE(UTF8ColumnWidth("te\xe6\xb5\x8bs\xe8\xaf\x95t") == 8); // te测s试t } TEST_CASE("UTF8TrimRightToColumnWidth", "[strings]") { size_t actualWidth; REQUIRE((UTF8TrimRightToColumnWidth("", 0, actualWidth) == "" && actualWidth == 0)); REQUIRE((UTF8TrimRightToColumnWidth("abcd", 4, actualWidth) == "abcd" && actualWidth == 4)); REQUIRE((UTF8TrimRightToColumnWidth("abcd", 5, actualWidth) == "abcd" && actualWidth == 4)); REQUIRE((UTF8TrimRightToColumnWidth("abcd", 2, actualWidth) == "ab" && actualWidth == 2)); NormalizedString s{ "te\xe6\xb5\x8bs\xe8\xaf\x95t" }; // // te测s试t REQUIRE((UTF8TrimRightToColumnWidth(s, 0, actualWidth) == "" && actualWidth == 0)); REQUIRE((UTF8TrimRightToColumnWidth(s, 2, actualWidth) == "te" && actualWidth == 2)); REQUIRE((UTF8TrimRightToColumnWidth(s, 3, actualWidth) == "te" && actualWidth == 2)); REQUIRE((UTF8TrimRightToColumnWidth(s, 4, actualWidth) == "te\xe6\xb5\x8b" && actualWidth == 4)); REQUIRE((UTF8TrimRightToColumnWidth(s, 8, actualWidth) == "te\xe6\xb5\x8bs\xe8\xaf\x95t" && actualWidth == 8)); REQUIRE((UTF8TrimRightToColumnWidth(s, 10, actualWidth) == "te\xe6\xb5\x8bs\xe8\xaf\x95t" && actualWidth == 8)); } TEST_CASE("Normalize", "[strings]") { REQUIRE(Normalize("test") == "test"); // A + combining Dieresis => single A with umlaut char REQUIRE(Normalize(L"\x41\x308") == L"\xC4"); // This will stop working in C++20, sigh. REQUIRE(Normalize(u8"\x41\x308") == u8"\xC4"); // Ligature fi => f + i REQUIRE(Normalize(u8"\xFB01") == u8"fi"); } TEST_CASE("NormalizedString", "[strings]") { REQUIRE(NormalizedString("test") == "test"); std::string input = "test"; REQUIRE(NormalizedString(input) == input); // A + combining Dieresis => single A with umlaut char REQUIRE(NormalizedString(std::wstring_view(L"\x41\x308")) == u8"\xC4"); // This will stop working in C++20, sigh. input = u8"\x41\x308"; REQUIRE(NormalizedString(input) == u8"\xC4"); // Ligature fi => f + i std::string_view input2 = u8"\xFB01"; REQUIRE(NormalizedString(input2) == u8"fi"); // Embedded null std::string_view input3{ "Test\0Case", 9 }; REQUIRE(NormalizedString(input3) == "Test Case"); } TEST_CASE("Trim", "[strings]") { std::string str; REQUIRE(Trim(str.assign("")) == ""); REQUIRE(Trim(str.assign(" ")) == ""); REQUIRE(Trim(str.assign(" \t ")) == ""); REQUIRE(Trim(str.assign(" a")) == "a"); REQUIRE(Trim(str.assign("bght ")) == "bght"); REQUIRE(Trim(str.assign("\tStuff\f")) == "Stuff"); REQUIRE(Trim(str.assign("Multiple words")) == "Multiple words"); REQUIRE(Trim(str.assign(" Multiple words")) == "Multiple words"); REQUIRE(Trim(str.assign("Much after is taken \f\n\r\t\v\v\t\r\n\f ")) == "Much after is taken"); REQUIRE(Trim(L" Test"sv) == L"Test"); REQUIRE(Trim(L" "sv) == L""); } TEST_CASE("CaseInsensitiveStartsWith", "[strings]") { REQUIRE(CaseInsensitiveStartsWith("startswith", "starts")); REQUIRE(CaseInsensitiveStartsWith("startswith", "STAR")); REQUIRE(CaseInsensitiveStartsWith("startswith", "startSWITH")); REQUIRE(CaseInsensitiveStartsWith("startswith", "")); REQUIRE(!CaseInsensitiveStartsWith("starts", "startswith")); REQUIRE(!CaseInsensitiveStartsWith("", "nuffing")); REQUIRE(!CaseInsensitiveStartsWith("withstarts", "starts")); REQUIRE(!CaseInsensitiveStartsWith(" starts", "starts")); } TEST_CASE("FoldCase", "[strings]") { REQUIRE(FoldCase(""sv) == FoldCase(""sv)); REQUIRE(FoldCase("foldcase"sv) == FoldCase("FOLDCASE"sv)); REQUIRE(FoldCase(u8"f\xF6ldcase"sv) == FoldCase(u8"F\xD6LDCASE"sv)); REQUIRE(FoldCase(u8"foldc\x430se"sv) == FoldCase(u8"FOLDC\x410SE"sv)); } TEST_CASE("ExpandEnvironmentVariables", "[strings]") { wchar_t buffer[MAX_PATH]; GetTempPathW(ARRAYSIZE(buffer), buffer); std::wstring tempPath = buffer; if (!tempPath.empty() && tempPath.back() == '\\') { tempPath.resize(tempPath.size() - 1); } REQUIRE(ExpandEnvironmentVariables(L"%TEMP%") == tempPath); } TEST_CASE("PathOutput", "[strings]") { std::string original = "\xe6\xb5\x8b\xe8\xaf\x95"; std::filesystem::path path = ConvertToUTF16(original); AICLI_LOG(Test, Info, << path); std::istringstream in; std::ostringstream out; AppInstaller::CLI::Execution::Reporter reporter{ out, in }; reporter.Info() << path; std::string output = out.str(); REQUIRE(output.substr(output.size() - original.size()) == original); } TEST_CASE("ReplaceWhileCopying", "[strings]") { REQUIRE(ReplaceWhileCopying(L"A red apple", L"red", L"green") == L"A green apple"); REQUIRE(ReplaceWhileCopying(L"A red, red apple", L"red", L"green") == L"A green, green apple"); REQUIRE(ReplaceWhileCopying(L"A red, red apple", L"ed", L"ad") == L"A rad, rad apple"); REQUIRE(ReplaceWhileCopying(L"A red apple", L"p", L"f") == L"A red affle"); REQUIRE(ReplaceWhileCopying(L"A red apple", L"", L"green") == L"A red apple"); } TEST_CASE("MakeSuitablePathPart", "[strings]") { REQUIRE(MakeSuitablePathPart("A\\B") == "A_B"); REQUIRE(MakeSuitablePathPart("A\\B/") == "A_B_"); REQUIRE(MakeSuitablePathPart("*AB") == "_AB"); REQUIRE(MakeSuitablePathPart(u8"f*\xF6*ldcase") == u8"f_\xF6_ldcase"); REQUIRE(MakeSuitablePathPart(".") == "_"); REQUIRE(MakeSuitablePathPart("..") == "._"); REQUIRE(MakeSuitablePathPart(std::string(300, ' ')) == SHA256::ConvertToString(SHA256::ComputeHash(std::string(300, ' ')))); REQUIRE_THROWS_HR(MakeSuitablePathPart("COM1"), E_INVALIDARG); REQUIRE_THROWS_HR(MakeSuitablePathPart("NUL.txt"), E_INVALIDARG); } TEST_CASE("GetFileNameFromURI", "[strings]") { REQUIRE(GetFileNameFromURI("https://github.com/microsoft/winget-cli/pull/1722").u8string() == "1722"); REQUIRE(GetFileNameFromURI("https://github.com/microsoft/winget-cli/README.md").u8string() == "README.md"); REQUIRE(GetFileNameFromURI("https://microsoft.com/").u8string() == ""); } void ValidateSplitFileName(std::string_view uri, std::string_view base, std::string_view fileName) { auto split = SplitFileNameFromURI(uri); REQUIRE(split.first == base); REQUIRE(split.second.u8string() == fileName); } TEST_CASE("SplitFileNameFromURI", "[strings]") { ValidateSplitFileName("https://github.com/microsoft/winget-cli/pull/1722", "https://github.com/microsoft/winget-cli/pull/", "1722"); ValidateSplitFileName("https://github.com/microsoft/winget-cli/README.md", "https://github.com/microsoft/winget-cli/", "README.md"); ValidateSplitFileName("https://microsoft.com/", "https://microsoft.com/", ""); } TEST_CASE("SplitIntoWords", "[strings]") { REQUIRE(SplitIntoWords("A B") == std::vector{ "A", "B" }); REQUIRE(SplitIntoWords("Some-Thing") == std::vector{ "Some", "Thing" }); // 私のテスト = "My test" according to an online translator // Split as "私" "の" "テスト" REQUIRE(SplitIntoWords("\xe7\xa7\x81\xe3\x81\xae\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88") == std::vector{ "\xe7\xa7\x81", "\xe3\x81\xae", "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88" }); } TEST_CASE("ReplaceEmbeddedNullCharacters", "[strings]") { std::string test = "Test Parts"; test[4] = '\0'; ReplaceEmbeddedNullCharacters(test); REQUIRE(test == "Test Parts"); } TEST_CASE("HexStrings", "[strings]") { std::vector buffer{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; std::string value = "000102030405060708090a0b0c0d0e0f"; REQUIRE(value == ConvertToHexString(buffer)); REQUIRE(std::equal(buffer.begin(), buffer.end(), ParseFromHexString(value).begin())); } TEST_CASE("Join", "[strings]") { std::vector list_0{ }; std::vector list_1{ "A"_lis }; std::vector list_2{ "A"_lis, "B"_lis }; REQUIRE(""_lis == Join(", "_liv, list_0)); REQUIRE("A"_lis == Join(", "_liv, list_1)); REQUIRE("A, B"_lis == Join(", "_liv, list_2)); REQUIRE("AB"_lis == Join(""_liv, list_2)); } TEST_CASE("Format", "[strings]") { REQUIRE("First Second" == Format("{0} {1}", "First", "Second")); REQUIRE("First Second" == Format("{1} {0}", "Second", "First")); REQUIRE("First Second" == Format("{0} {1}", "First", "Second", "(Extra", "Input", "Ignored)")); REQUIRE("First Second First Second" == Format("{0} {1} {0} {1}", "First", "Second")); // Note: C++20 std::format will throw an exception for this test case REQUIRE("First {1}" == Format("{0} {1}", "First")); } TEST_CASE("SplitIntoLines", "[strings]") { REQUIRE(SplitIntoLines("Boring test") == std::vector{ "Boring test" }); REQUIRE(SplitIntoLines( "I'm Luffy! The Man Who Will Become the Pirate King!\r-Monkey D. Luffy") == std::vector{ "I'm Luffy! The Man Who Will Become the Pirate King!", "-Monkey D. Luffy" }); REQUIRE(SplitIntoLines( "I want live!\n-Nico Robin") == std::vector{ "I want live!", "-Nico Robin" }); REQUIRE(SplitIntoLines( "You want my treasure?\rYou can have it!\nI left everything I gathered in one place!\r\nYou just have to find it!") == std::vector{ "You want my treasure?", "You can have it!", "I left everything I gathered in one place!", "You just have to find it!" }); } TEST_CASE("SplitWithSeparator", "[strings]") { std::vector test1 = Split("first;second;third"s, ';'); REQUIRE(test1.size() == 3); REQUIRE(test1[0] == "first"); REQUIRE(test1[1] == "second"); REQUIRE(test1[2] == "third"); std::vector test2 = Split("two spaces"s, ' '); REQUIRE(test2.size() == 3); REQUIRE(test2[0] == "two"); REQUIRE(test2[1] == ""); REQUIRE(test2[2] == "spaces"); std::vector test3 = Split("test"s, '.'); REQUIRE(test3.size() == 1); REQUIRE(test3[0] == "test"); std::vector test4 = Split(" trim | spaces "s, '|', true); REQUIRE(test4.size() == 2); REQUIRE(test4[0] == "trim"); REQUIRE(test4[1] == "spaces"); std::vector test5 = Split(L" trim /spaces / "sv, '/', true); REQUIRE(test5.size() == 3); REQUIRE(test5[0] == L"trim"); REQUIRE(test5[1] == L"spaces"); REQUIRE(test5[2] == L""); std::vector test6 = Split(L" "sv, '/', true); REQUIRE(test6.size() == 1); REQUIRE(test6[0] == L""); std::vector test7 = Split(""s, ';'); REQUIRE(test7.size() == 1); REQUIRE(test7[0] == ""); } TEST_CASE("ConvertGuid", "[strings]") { std::string validGuidString = "{4d1e55b2-f16f-11cf-88cb-001111000030}"; GUID guid = { 0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }; REQUIRE(CaseInsensitiveEquals(ConvertGuidToString(guid), validGuidString)); } TEST_CASE("FindControlCodeToConvert", "[strings]") { REQUIRE(FindControlCodeToConvert("No codes") == std::string::npos); REQUIRE(FindControlCodeToConvert("Allowed codes: \t\r\n") == std::string::npos); REQUIRE(FindControlCodeToConvert("\x1bSkipped code", 1) == std::string::npos); REQUIRE(FindControlCodeToConvert("\x1bUnskipped code") == 0); REQUIRE(FindControlCodeToConvert("Escape code: \x1b") == 13); std::string_view allCodes{ "\x0\x1\x2\x3\x4\x5\x6\x7\x8\xb\xc\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"sv }; for (size_t i = 0; i < allCodes.length(); ++i) { REQUIRE(FindControlCodeToConvert(allCodes, i) == i); } } TEST_CASE("ConvertControlCodesToPictures", "[strings]") { REQUIRE(ConvertControlCodesToPictures("No codes") == "No codes"); REQUIRE(ConvertControlCodesToPictures("Allowed codes: \t\r\n") == "Allowed codes: \t\r\n"); REQUIRE(ConvertControlCodesToPictures("\x1b Code First") == ConvertToUTF8(L"\x241b Code First")); REQUIRE(ConvertControlCodesToPictures("Escape code: \x1b") == ConvertToUTF8(L"Escape code: \x241b")); std::string_view allCodes{ "\x0\x1\x2\x3\x4\x5\x6\x7\x8\xb\xc\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"sv }; std::wstring_view allPictures{ L"\x2400\x2401\x2402\x2403\x2404\x2405\x2406\x2407\x2408\x240b\x240c\x240e\x240f\x2410\x2411\x2412\x2413\x2414\x2415\x2416\x2417\x2418\x2419\x241a\x241b\x241c\x241d\x241e\x241f\x2421"sv }; REQUIRE(ConvertControlCodesToPictures(allCodes) == ConvertToUTF8(allPictures)); } ================================================ FILE: src/AppInstallerCLITests/Synchronization.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include using namespace AppInstaller::Synchronization; TEST_CASE("CPIL_BlocksOthers", "[CrossProcessInstallLock]") { wil::unique_event signal; signal.create(); AppInstaller::ProgressCallback progress; { CrossProcessInstallLock mainThreadLock; mainThreadLock.Acquire(progress); std::thread otherThread([&signal, &progress]() { CrossProcessInstallLock otherThreadLock; otherThreadLock.Acquire(progress); signal.SetEvent(); }); // In the event of bugs, we don't want to block the test waiting forever otherThread.detach(); REQUIRE(!signal.wait(500)); } // Upon release of the writer, the other thread should signal REQUIRE(signal.wait(500)); } TEST_CASE("CPIL_CancelEndsWait", "[CrossProcessInstallLock]") { wil::unique_event signal; signal.create(); AppInstaller::ProgressCallback progress; CrossProcessInstallLock mainThreadLock; mainThreadLock.Acquire(progress); std::optional otherThreadAcquireResult = std::nullopt; std::thread otherThread([&]() { CrossProcessInstallLock otherThreadLock; otherThreadAcquireResult = otherThreadLock.Acquire(progress); signal.SetEvent(); }); // In the event of bugs, we don't want to block the test waiting forever otherThread.detach(); REQUIRE(!signal.wait(500)); progress.Cancel(); // Upon release of the writer, the other thread should signal REQUIRE(signal.wait(500)); REQUIRE(otherThreadAcquireResult.has_value()); REQUIRE(!otherThreadAcquireResult.value()); } ================================================ FILE: src/AppInstallerCLITests/TestCommon.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestHooks.h" #include #include #include #include using namespace AppInstaller; namespace TestCommon { namespace { int initRand() { srand(static_cast(time(NULL))); return rand(); }; inline int getRand() { static int randStart = initRand(); return randStart++; } inline std::filesystem::path GetFilePath(std::filesystem::path path, const std::string& baseName, const std::string& baseExt) { path /= baseName + std::to_string(getRand()) + baseExt; return path; } inline std::filesystem::path GetTempFilePath(const std::string& baseName, const std::string& baseExt) { std::filesystem::path tempFilePath = std::filesystem::temp_directory_path(); return GetFilePath(tempFilePath, baseName, baseExt); } static TempFileDestructionBehavior s_TempFileDestructorBehavior = TempFileDestructionBehavior::Delete; static std::vector s_TempFilesOnFile; static std::filesystem::path s_TestDataFileBasePath{}; bool CleanVolatileTestRoot(HKEY root) { THROW_IF_WIN32_ERROR(RegDeleteTreeW(root, nullptr)); return true; } } TempFile::TempFile(const std::string& baseName, const std::string& baseExt, std::optional keepTempFile) { _filepath = GetTempFilePath(baseName, baseExt); if (!keepTempFile) { std::filesystem::remove(_filepath); } } TempFile::TempFile(const std::filesystem::path& parent, const std::string& baseName, const std::string& baseExt, std::optional keepTempFile) { _filepath = GetFilePath(parent, baseName, baseExt); if (!keepTempFile) { std::filesystem::remove(_filepath); } } TempFile::TempFile(const std::filesystem::path& filePath, std::optional keepTempFile) { if (filePath.is_relative()) { _filepath = std::filesystem::temp_directory_path(); _filepath /= filePath; } else { _filepath = filePath; } if (!keepTempFile) { std::filesystem::remove(_filepath); } } TempFile::~TempFile() try { if (m_destructionToken) { switch (s_TempFileDestructorBehavior) { case TempFileDestructionBehavior::Delete: std::filesystem::remove_all(_filepath); break; case TempFileDestructionBehavior::Keep: break; case TempFileDestructionBehavior::ShellExecuteOnFailure: s_TempFilesOnFile.emplace_back(std::move(_filepath)); break; } } } CATCH_LOG_RETURN() void TempFile::Rename(const std::filesystem::path& newFilePath) { std::filesystem::rename(GetPath(), newFilePath); _filepath = newFilePath; } void TempFile::Release() { m_destructionToken = false; } void TempFile::SetDestructorBehavior(TempFileDestructionBehavior behavior) { s_TempFileDestructorBehavior = behavior; } void TempFile::SetTestFailed(bool failed) { if (failed) { for (const auto& path : s_TempFilesOnFile) { SHELLEXECUTEINFOW seinfo{}; seinfo.cbSize = sizeof(seinfo); seinfo.lpVerb = L"open"; seinfo.lpFile = path.c_str(); ShellExecuteExW(&seinfo); } } else { s_TempFilesOnFile.clear(); } } TempDirectory::TempDirectory(const std::string& baseName, bool create) { _filepath = GetTempFilePath(baseName, ""); if (create) { if (std::filesystem::exists(_filepath)) { std::filesystem::remove_all(_filepath); } std::filesystem::create_directories(_filepath); } } TempFile TempDirectory::CreateTempFile(const std::string& baseName, const std::string& baseExt) { return { _filepath, baseName, baseExt }; } TempFile TempDirectory::CreateTempFile(const std::string& name) { return { _filepath / name }; } std::filesystem::path TestDataFile::GetPath() const { std::filesystem::path result = s_TestDataFileBasePath; result /= m_path; return result; } void TestDataFile::SetTestDataBasePath(const std::filesystem::path& path) { s_TestDataFileBasePath = path; } void TestProgress::OnProgress(uint64_t current, uint64_t maximum, AppInstaller::ProgressType type) { if (m_OnProgress) { m_OnProgress(current, maximum, type); } } void TestProgress::SetProgressMessage(std::string_view) { } void TestProgress::BeginProgress() { } void TestProgress::EndProgress(bool) { } bool TestProgress::IsCancelledBy(AppInstaller::CancelReason) { return false; } AppInstaller::IProgressCallback::CancelFunctionRemoval TestProgress::SetCancellationFunction(std::function&&) { return {}; } wil::unique_hkey RegCreateVolatileTestRoot() { // First create/open the real test root wil::unique_hkey root; THROW_IF_WIN32_ERROR(RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\WinGet\\TestRoot", 0, nullptr, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, &root, nullptr)); static bool s_ignored = CleanVolatileTestRoot(root.get()); // Create a random name GUID name{}; (void)CoCreateGuid(&name); wchar_t nameBuffer[256]; (void)StringFromGUID2(name, nameBuffer, ARRAYSIZE(nameBuffer)); return RegCreateVolatileSubKey(root.get(), nameBuffer); } wil::unique_hkey RegCreateVolatileSubKey(HKEY parent, const std::wstring& name) { wil::unique_hkey result; THROW_IF_WIN32_ERROR(RegCreateKeyExW(parent, name.c_str(), 0, nullptr, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, &result, nullptr)); return result; } void SetRegistryValue(HKEY key, const std::wstring& name, const std::wstring& value, DWORD type) { THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, type, reinterpret_cast(value.c_str()), static_cast(sizeof(wchar_t) * (value.size() + 1)))); } void SetRegistryValue(HKEY key, const std::wstring& name, const std::vector& value, DWORD type) { THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, type, reinterpret_cast(value.data()), static_cast(value.size()))); } void SetRegistryValue(HKEY key, const std::wstring& name, DWORD value) { THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, REG_DWORD, reinterpret_cast(&value), sizeof(DWORD))); } void EnableDevMode(bool enable) { wil::unique_hkey result; THROW_IF_WIN32_ERROR(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &result)); SetRegistryValue(result.get(), L"AllowDevelopmentWithoutDevLicense", (enable ? 1 : 0)); } TestUserSettings::TestUserSettings(bool keepFileSettings) { if (!keepFileSettings) { m_settings.clear(); } AppInstaller::Settings::SetUserSettingsOverride(this); } TestUserSettings::~TestUserSettings() { AppInstaller::Settings::SetUserSettingsOverride(nullptr); } std::unique_ptr TestUserSettings::EnableExperimentalFeature(Settings::ExperimentalFeature::Feature feature, bool keepFileSettings) { std::unique_ptr result = std::make_unique(keepFileSettings); // Due to the template usage, this needs to be updated for any features that want to use it. // Currently no feature is used. Uncomment below when a feature needs to be used. // switch (feature) // { // default: // THROW_HR(E_NOTIMPL); // } UNREFERENCED_PARAMETER(feature); return result; } bool InstallCertFromSignedPackage(const std::filesystem::path& package) { auto [certContext, certStore] = AppInstaller::Msix::GetCertContextFromMsix(package); wil::unique_hcertstore trustedPeopleStore; trustedPeopleStore.reset(CertOpenStore( CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"TrustedPeople")); THROW_LAST_ERROR_IF(!trustedPeopleStore.get()); wil::unique_cert_context existingCert; existingCert.reset(CertFindCertificateInStore( trustedPeopleStore.get(), PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_EXISTING, certContext.get(), nullptr)); // Add if it does not already exist in the store if (!existingCert.get()) { THROW_LAST_ERROR_IF(!CertAddCertificateContextToStore( trustedPeopleStore.get(), certContext.get(), CERT_STORE_ADD_NEW, nullptr)); return true; } return false; } bool UninstallCertFromSignedPackage(const std::filesystem::path& package) { auto [certContext, certStore] = AppInstaller::Msix::GetCertContextFromMsix(package); wil::unique_hcertstore trustedPeopleStore; trustedPeopleStore.reset(CertOpenStore( CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"TrustedPeople")); THROW_LAST_ERROR_IF(!trustedPeopleStore.get()); wil::unique_cert_context existingCert; existingCert.reset(CertFindCertificateInStore( trustedPeopleStore.get(), PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_EXISTING, certContext.get(), nullptr)); // Remove if it exists in the store if (existingCert.get()) { THROW_LAST_ERROR_IF(!CertDeleteCertificateFromStore(existingCert.get())); return true; } return false; } bool GetMsixPackageManifestReader(std::string_view testFileName, IAppxManifestReader** manifestReader) { // Locate test file TestDataFile testFile(testFileName); auto path = testFile.GetPath().u8string(); // Get the stream for the test file auto stream = AppInstaller::Utility::GetReadOnlyStreamFromURI(path); // Get manifest from package reader Microsoft::WRL::ComPtr packageReader; return AppInstaller::Msix::GetPackageReader(stream.Get(), &packageReader) && SUCCEEDED(packageReader->GetManifest(manifestReader)); } std::string RemoveConsoleFormat(const std::string& str) { // We are looking something that starts with "\x1b[0m" if (!str.empty() && str[0] == '\x1b') { // Find first m auto pos = str.find("m"); if (pos != std::string::npos) { return str.substr(pos + 1); } } return str; } Json::Value ConvertToJson(const std::string& content) { auto contentClean = RemoveConsoleFormat(content); Json::Value root; Json::CharReaderBuilder builder; const std::unique_ptr reader(builder.newCharReader()); std::string error; if (!reader->parse(contentClean.c_str(), contentClean.c_str() + contentClean.size(), &root, &error)) { throw error; } return root; } void SetTestPathOverrides() { // Force all tests to run against settings inside this container. // This prevents test runs from trashing the users actual settings. Runtime::TestHook_SetPathOverride(Runtime::PathName::LocalState, Runtime::GetPathTo(Runtime::PathName::LocalState) / "Tests"); Runtime::TestHook_SetPathOverride(Runtime::PathName::UserFileSettings, Runtime::GetPathTo(Runtime::PathName::UserFileSettings) / "Tests"); Runtime::TestHook_SetPathOverride(Runtime::PathName::StandardSettings, Runtime::GetPathTo(Runtime::PathName::StandardSettings) / "Tests"); Runtime::TestHook_SetPathOverride(Runtime::PathName::SecureSettingsForRead, Runtime::GetPathTo(Runtime::PathName::StandardSettings) / "WinGet_SecureSettings_Tests"); Runtime::TestHook_SetPathOverride(Runtime::PathName::SecureSettingsForWrite, Runtime::GetPathDetailsFor(Runtime::PathName::SecureSettingsForRead)); } } ================================================ FILE: src/AppInstallerCLITests/TestCommon.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #define REQUIRE_THROWS_HR(_expr_, _hr_) REQUIRE_THROWS_MATCHES(_expr_, wil::ResultException, ::TestCommon::ResultExceptionHRMatcher(_hr_)) namespace TestCommon { enum class TempFileDestructionBehavior { Delete, Keep, ShellExecuteOnFailure, }; struct KeepTempFile {}; // Use this to create a temporary file for testing. struct TempFile { TempFile(const std::string& baseName, const std::string& baseExt, std::optional keepTempFile = {}); TempFile(const std::filesystem::path& parent, const std::string& baseName, const std::string& baseExt, std::optional keepTempFile = {}); TempFile(const std::filesystem::path& filePath, std::optional keepTempFile = {}); TempFile(const TempFile&) = delete; TempFile& operator=(const TempFile&) = delete; TempFile(TempFile&&) = default; TempFile& operator=(TempFile&&) = default; ~TempFile(); const std::filesystem::path& GetPath() const { return _filepath; } operator const std::filesystem::path& () const { return _filepath; } operator const std::string() const { return _filepath.u8string(); } void Rename(const std::filesystem::path& newFilePath); void Release(); static void SetDestructorBehavior(TempFileDestructionBehavior behavior); static void SetTestFailed(bool failed); protected: TempFile() = default; std::filesystem::path _filepath; AppInstaller::DestructionToken m_destructionToken{ true }; }; // Use to create a temporary directory for testing. struct TempDirectory : public TempFile { TempDirectory(const std::string& baseName, bool create = true); TempFile CreateTempFile(const std::string& baseName, const std::string& baseExt); TempFile CreateTempFile(const std::string& name); }; // Use this to find a test data file when testing. struct TestDataFile { TestDataFile(const std::filesystem::path& path) : m_path(path) {} std::filesystem::path GetPath() const; operator std::filesystem::path () const { return GetPath(); } static void SetTestDataBasePath(const std::filesystem::path& path); private: std::filesystem::path m_path; }; // Matcher that lets us verify wil::ResultExceptions have a specific HR. struct ResultExceptionHRMatcher : public Catch::Matchers::MatcherBase { ResultExceptionHRMatcher(HRESULT hr) : m_expectedHR(hr) {} bool match(const wil::ResultException& re) const override { return re.GetErrorCode() == m_expectedHR; } std::string describe() const override { std::ostringstream result; result << "has HR == 0x" << AppInstaller::Logging::SetHRFormat << m_expectedHR; return result.str(); } private: HRESULT m_expectedHR = S_OK; }; // An IProgressCallback that is easily hooked. struct TestProgress : public AppInstaller::IProgressCallback { // Inherited via IProgressCallback void BeginProgress() override; void OnProgress(uint64_t current, uint64_t maximum, AppInstaller::ProgressType type) override; void SetProgressMessage(std::string_view message) override; void EndProgress(bool) override; bool IsCancelledBy(AppInstaller::CancelReason) override; CancelFunctionRemoval SetCancellationFunction(std::function&& f) override; std::function m_OnProgress; }; // Creates a volatile key for testing. wil::unique_hkey RegCreateVolatileTestRoot(); // Creates a volatile subkey for testing. wil::unique_hkey RegCreateVolatileSubKey(HKEY parent, const std::wstring& name); // Set registry values. void SetRegistryValue(HKEY key, const std::wstring& name, const std::wstring& value, DWORD type = REG_SZ); void SetRegistryValue(HKEY key, const std::wstring& name, const std::vector& value, DWORD type = REG_BINARY); void SetRegistryValue(HKEY key, const std::wstring& name, DWORD value); // Enable or disable developer mode. void EnableDevMode(bool enable); // Override UserSettings using this class. // Automatically overrides the user settings for the lifetime of this object. // DOES NOT SUPPORT NESTED USE struct TestUserSettings : public AppInstaller::Settings::UserSettings { TestUserSettings(bool keepFileSettings = false); ~TestUserSettings(); template void Set(typename AppInstaller::Settings::details::SettingMapping::value_t&& value) { m_settings[S].emplace(std::move(value)); } static std::unique_ptr EnableExperimentalFeature(AppInstaller::Settings::ExperimentalFeature::Feature feature, bool keepFileSettings = false); }; // Below cert installation/uninstallation methods require admin privilege, // tests calling these functions should skip when not running with admin. bool InstallCertFromSignedPackage(const std::filesystem::path& package); bool UninstallCertFromSignedPackage(const std::filesystem::path& package); // Get manifest reader from a msix file path bool GetMsixPackageManifestReader(std::string_view testFileName, IAppxManifestReader** manifestReader); // Removes console format std::string RemoveConsoleFormat(const std::string& str); // Convert to Json::Value Json::Value ConvertToJson(const std::string& content); // Sets up the test path overrides. void SetTestPathOverrides(); } ================================================ FILE: src/AppInstallerCLITests/TestConfiguration.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestConfiguration.h" using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Microsoft::Management::Configuration; namespace TestCommon { IConfigurationSetProcessor TestConfigurationSetProcessorFactory::CreateSetProcessor(const ConfigurationSet& configurationSet) { if (CreateSetProcessorFunc) { return CreateSetProcessorFunc(configurationSet); } else { return winrt::make(); } } winrt::event_token TestConfigurationSetProcessorFactory::Diagnostics(const EventHandler& handler) { return m_diagnostics.add(handler); } void TestConfigurationSetProcessorFactory::Diagnostics(const winrt::event_token& token) noexcept { m_diagnostics.remove(token); } DiagnosticLevel TestConfigurationSetProcessorFactory::MinimumLevel() { return DiagnosticLevel::Informational; } void TestConfigurationSetProcessorFactory::MinimumLevel(DiagnosticLevel) { } IConfigurationUnitProcessorDetails TestConfigurationSetProcessor::GetUnitProcessorDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags) { if (GetUnitProcessorDetailsFunc) { return GetUnitProcessorDetailsFunc(unit, detailFlags); } else { return winrt::make(unit); } } IConfigurationUnitProcessor TestConfigurationSetProcessor::CreateUnitProcessor(const ConfigurationUnit& unit) { if (CreateUnitProcessorFunc) { return CreateUnitProcessorFunc(unit); } else { return winrt::make(unit); } } TestConfigurationUnitProcessorDetails::TestConfigurationUnitProcessorDetails(const ConfigurationUnit& unit) : UnitTypeValue(unit.Type()) {} TestConfigurationUnitProcessor::TestConfigurationUnitProcessor(const ConfigurationUnit& unit) : UnitValue(unit) {} ITestSettingsResult TestConfigurationUnitProcessor::TestSettings() { if (TestSettingsFunc) { return TestSettingsFunc(); } else { return winrt::make(UnitValue); } } IGetSettingsResult TestConfigurationUnitProcessor::GetSettings() { if (GetSettingsFunc) { return GetSettingsFunc(); } else { return winrt::make(UnitValue); } } IApplySettingsResult TestConfigurationUnitProcessor::ApplySettings() { if (ApplySettingsFunc) { return ApplySettingsFunc(); } else { return winrt::make(UnitValue); } } } ================================================ FILE: src/AppInstallerCLITests/TestConfiguration.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace TestCommon { struct TestConfigurationSetProcessorFactory : winrt::implements { winrt::Microsoft::Management::Configuration::IConfigurationSetProcessor CreateSetProcessor(const winrt::Microsoft::Management::Configuration::ConfigurationSet& configurationSet); winrt::event_token Diagnostics(const winrt::Windows::Foundation::EventHandler& handler); void Diagnostics(const winrt::event_token& token) noexcept; winrt::Microsoft::Management::Configuration::DiagnosticLevel MinimumLevel(); void MinimumLevel(winrt::Microsoft::Management::Configuration::DiagnosticLevel value); std::function CreateSetProcessorFunc; private: winrt::event> m_diagnostics; }; struct TestConfigurationSetProcessor : winrt::implements { winrt::Microsoft::Management::Configuration::IConfigurationUnitProcessorDetails GetUnitProcessorDetails( const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit, winrt::Microsoft::Management::Configuration::ConfigurationUnitDetailFlags detailFlags); std::function GetUnitProcessorDetailsFunc; winrt::Microsoft::Management::Configuration::IConfigurationUnitProcessor CreateUnitProcessor( const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit); std::function CreateUnitProcessorFunc; }; struct TestConfigurationUnitProcessorDetails : winrt::implements { TestConfigurationUnitProcessorDetails(const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit); winrt::hstring UnitTypeValue; winrt::hstring UnitType() const { return UnitTypeValue; } winrt::hstring UnitDescriptionValue; winrt::hstring UnitDescription() const { return UnitDescriptionValue; } winrt::Windows::Foundation::Uri UnitDocumentationUriValue = nullptr; winrt::Windows::Foundation::Uri UnitDocumentationUri() const { return UnitDocumentationUriValue; } winrt::Windows::Foundation::Uri UnitIconUriValue = nullptr; winrt::Windows::Foundation::Uri UnitIconUri() const { return UnitIconUriValue; } winrt::hstring ModuleNameValue; winrt::hstring ModuleName() const { return ModuleNameValue; } winrt::hstring ModuleTypeValue; winrt::hstring ModuleType() const { return ModuleTypeValue; } winrt::hstring ModuleSourceValue; winrt::hstring ModuleSource() const { return ModuleSourceValue; } winrt::hstring ModuleDescriptionValue; winrt::hstring ModuleDescription() const { return ModuleDescriptionValue; } winrt::Windows::Foundation::Uri ModuleDocumentationUriValue = nullptr; winrt::Windows::Foundation::Uri ModuleDocumentationUri() const { return ModuleDocumentationUriValue; } winrt::Windows::Foundation::Uri PublishedModuleUriValue = nullptr; winrt::Windows::Foundation::Uri PublishedModuleUri() const { return PublishedModuleUriValue; } winrt::hstring VersionValue; winrt::hstring Version() const { return VersionValue; } winrt::Windows::Foundation::DateTime PublishedDateValue; winrt::Windows::Foundation::DateTime PublishedDate() const { return PublishedDateValue; } bool IsLocalValue = false; bool IsLocal() const { return IsLocalValue; } winrt::hstring AuthorValue; winrt::hstring Author() const { return AuthorValue; } winrt::hstring PublisherValue; winrt::hstring Publisher() const { return PublisherValue; } winrt::Windows::Foundation::Collections::IVector SigningInformationValue = nullptr; winrt::Windows::Foundation::Collections::IVectorView SigningInformation() const { return SigningInformationValue.GetView(); } winrt::Windows::Foundation::Collections::IVector SettingsValue; winrt::Windows::Foundation::Collections::IVectorView Settings() const { return (SettingsValue ? SettingsValue.GetView() : nullptr); } bool IsPublicValue = false; bool IsPublic() const { return IsPublicValue; } }; struct TestConfigurationUnitProcessor : winrt::implements { TestConfigurationUnitProcessor( const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit); winrt::Microsoft::Management::Configuration::ConfigurationUnit UnitValue; winrt::Microsoft::Management::Configuration::ConfigurationUnit Unit() { return UnitValue; } winrt::Microsoft::Management::Configuration::ITestSettingsResult TestSettings(); std::function TestSettingsFunc; winrt::Microsoft::Management::Configuration::IGetSettingsResult GetSettings(); std::function GetSettingsFunc; winrt::Microsoft::Management::Configuration::IApplySettingsResult ApplySettings(); std::function ApplySettingsFunc; }; struct TestSettingsResultInstance : winrt::implements { TestSettingsResultInstance(const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit) : m_unit(unit) {} winrt::Microsoft::Management::Configuration::ConfigurationUnit Unit() { return m_unit; } winrt::Microsoft::Management::Configuration::ConfigurationTestResult TestResult() { return m_testResult; } void TestResult(winrt::Microsoft::Management::Configuration::ConfigurationTestResult value) { m_testResult = value; } winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation ResultInformation() { return m_resultInformation; } void ResultInformation(winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation value) { m_resultInformation = value; } private: winrt::Microsoft::Management::Configuration::ConfigurationUnit m_unit; winrt::Microsoft::Management::Configuration::ConfigurationTestResult m_testResult = winrt::Microsoft::Management::Configuration::ConfigurationTestResult::Unknown; winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation m_resultInformation; }; struct ApplySettingsResultInstance : winrt::implements { ApplySettingsResultInstance(const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit) : m_unit(unit) {} winrt::Microsoft::Management::Configuration::ConfigurationUnit Unit() { return m_unit; } bool RebootRequired() { return m_rebootRequired; } void RebootRequired(bool value) { m_rebootRequired = value; } winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation ResultInformation() { return m_resultInformation; } void ResultInformation(winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation value) { m_resultInformation = value; } private: winrt::Microsoft::Management::Configuration::ConfigurationUnit m_unit; bool m_rebootRequired = false; winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation m_resultInformation; }; struct GetSettingsResultInstance : winrt::implements { GetSettingsResultInstance(const winrt::Microsoft::Management::Configuration::ConfigurationUnit& unit) : m_unit(unit) {} winrt::Microsoft::Management::Configuration::ConfigurationUnit Unit() { return m_unit; } winrt::Windows::Foundation::Collections::ValueSet Settings() { return m_settings; } void Settings(winrt::Windows::Foundation::Collections::ValueSet value) { m_settings = value; } winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation ResultInformation() { return m_resultInformation; } void ResultInformation(winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation value) { m_resultInformation = value; } private: winrt::Microsoft::Management::Configuration::ConfigurationUnit m_unit; winrt::Windows::Foundation::Collections::ValueSet m_settings; winrt::Microsoft::Management::Configuration::IConfigurationUnitResultInformation m_resultInformation; }; } ================================================ FILE: src/AppInstallerCLITests/TestData/ContainsEscapeControlCode.yaml ================================================ key: "This is the ESCAPE control code: \x1b" ================================================ FILE: src/AppInstallerCLITests/TestData/ContainsTooManyNestedLayers.yaml ================================================ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - value ================================================ FILE: src/AppInstallerCLITests/TestData/DownloadFlowTest_DownloadCommandProhibited.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.6.0.schema.json PackageIdentifier: AppInstallerCliTest.DownloadCommandProhibited PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test DownloadCommandProhibited Publisher: Microsoft Corporation AppMoniker: AICLITestDownloadCommandProhibited License: Test DownloadCommandProhibited: true Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLITests/TestData/DownloadFlowTest_MSStore.yaml ================================================ Id: AppInstallerCliTest.TestMSStoreDownload Version: Latest Name: AppInstaller Test MSStore Download Publisher: Microsoft Corporation AppMoniker: AICLITestMSStore License: Test Installers: - Arch: x64 Url: https://ThisIsNotUsed InstallerType: msstore ProductId: 9WZDNCRFJ364 Language: en-US - Arch: x64 Url: https://ThisIsNotUsed InstallerType: msstore ProductId: 9WZDNCRFJ364 Language: fr-FR - Arch: arm Url: https://ThisIsNotUsed InstallerType: msstore ProductId: 9WZDNCRFJ364 Language: en-US - Arch: arm Url: https://ThisIsNotUsed InstallerType: msstore ProductId: 9WZDNCRFJ364 Language: fr-FR - Arch: x64 Url: https://ThisIsNotUsed InstallerType: msstore ProductId: 9WZDNCRFJ364 Language: ja-JP - Arch: arm64 Url: https://ThisIsNotUsed InstallerType: msstore ProductId: 9WZDNCRFJ364 Language: en-US ManifestVersion: 0.2.0-msstore ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Bad-Invalid.json ================================================ "A valid JSON file that does not conform to the schema" ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Bad-Malformed.json ================================================ This is not a valid JSON file. ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackage.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "AppInstallerCliTest.TestExeInstaller", "Version": "2.0.0.0" }, { "Id": "MissingPackage", "Version": "1.0.0.0" } ], "SourceDetails": { "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", "Type": "Microsoft.TestSource" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownPackageVersion.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "AppInstallerCliTest.TestExeInstaller", "Version": "4.3.2.1" } ], "SourceDetails": { "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", "Type": "Microsoft.TestSource" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Bad-UnknownSource.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "AppInstallerCliTest.TestExeInstaller", "Version": "1.0.0.0" } ], "SourceDetails": { "Argument": "//bad", "Identifier": "*BadSource", "Name": "TestSource", "Type": "Microsoft.TestSource" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Good-AlreadyInstalled.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "AppInstallerCliTest.TestExeInstaller", "Version": "1.0.0.0" } ], "SourceDetails": { "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", "Type": "Microsoft.TestSource" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Good-Dependencies.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "AppInstallerCliTest.TestExeInstaller.Dependencies", "Version": "2.0.0.0" }, { "Id": "AppInstallerCliTest.TestMsixInstaller.WFDep", "Version": "1.0.0.0" } ], "SourceDetails": { "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", "Type": "Microsoft.TestSource" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Good-MachineScope.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "TestExeInstallerWithNothingInstalled", "Version": "1.0.0.0", "Scope": "machine" } ], "SourceDetails": { "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", "Type": "Microsoft.TestSource" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Good-WithLicenseAgreement.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "TestInstallerWithLicenseAgreement", "Version": "3.0.0.0" } ], "SourceDetails": { "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", "Type": "Microsoft.TestSource" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLITests/TestData/ImportFile-Good.json ================================================ { "$schema": "https://aka.ms/winget-packages.schema.1.0.json", "CreationDate": "2021-01-01T12:00:00.000-00:00", "Sources": [ { "Packages": [ { "Id": "AppInstallerCliTest.TestExeInstaller", "Version": "2.0.0.0" }, { "Id": "AppInstallerCliTest.TestMsixInstaller", "Version": "2.0.0.0" } ], "SourceDetails": { "Argument": "//arg", "Identifier": "*TestSource", "Name": "TestSource", "Type": "Microsoft.TestSource" } } ], "WinGetVersion": "1.0.0" } ================================================ FILE: src/AppInstallerCLITests/TestData/InputARPData.txt ================================================ XP890ZFCZZR294|Studio Fisioterapico Pro|Esposito Software di M. G. Caputo|Studio Fisioterapico Pro Demo||Copyright Esposito Software|Studio Fisioterapico Pro Demo_is1 XP89DCGQ3K6VLD|Microsoft PowerToys|Microsoft Corporation|PowerToys (Preview) x64|0.57.0|Microsoft Corporation|{582f7a19-045d-43d4-89bf-7f8e9479311c} XP89HZ8SVWTT0M|ElevenClock|Martí Climent|ElevenClock version 3.3.2|3.3.2|SomePythonThings|{D62480B8-71F1-48CE-BEEC-9D3E172C87B5}_is1 XP89HZKG342W76|POWER-KI GUI Client|XPLAB - Research in Automation|POWER-KI GUI|33.11|XPLAB - Research in Automation - Brescia - Italy|{0760E097-F794-4836-9941-8846EA43BE06} XP89J5462CMGJD|Apache OpenOffice|The Apache Software Foundation|OpenOffice 4.1.11|4.111.9808|Apache Software Foundation|{D2F124FC-5373-4A4A-8C5A-61052A3D34CA} XP8BTFNM0T53BJ|PolypopLive|Simmetri, Inc.|PolyPop 0.98.222.0|0.98.222.0|Simmetri, Inc.|{75454996-E72B-480E-BB8C-CD743A54C362}_is1 XP8BX12N1KK2QJ|MyLifeOrganized - To-Do List|Andriy Tkachuk|MyLifeOrganized v. 5.1.3|5.1.3|MyLifeOrganized.net|MyLife Organized XP8CD7JST163BL|BPM Counter|Abyssmedia.com|BPM Counter 3.8.0.0|3.8.0.0|AbyssMedia.com|BPM Counter_is1 XP8CDF4CV9XP5Q|Archivio Esami Clinici|Esposito Software di M. G. Caputo|Archivio Esami Clinici 3.0 Demo||Copyright Esposito Software|Archivio Esami Clinici 3.0 Demo_is1 XP8CF6SB8MX31V|Ashampoo Photo Optimizer 8|Ashampoo|Ashampoo Photo Optimizer 8|8.2.3|Ashampoo GmbH & Co. KG|{91B33C97-5FC6-8971-3444-C57BBE022215}_is1 XP8JJ8VX6VL0Q5|Cleaner One Pro - Free PC Cleaner|Trend Micro Inc.|Cleaner One Pro 6.6.0|6.6.0|Trend Micro, Inc.|99388cc2-2782-5495-bbd2-525df2487901 XP8JJRV6TV79LG|DiskZIP|ZIPmagic Software|DiskZIP|2022.3.1415.932|DiskZIP Computing, Inc.|DiskZIP XP8JJVZXG23JLN|WorldClock.Classic.ScreenSaver|Fulvio Castelli|WorldClock Screen Saver (Trial)|7.0.12.0|Fulvio Castelli|{EF3BC641-89A9-4703-9DED-19CEE72CEF07}_is1 XP8JK4HZBVF435|Auto Dark Mode|Armin Osaj|Auto Dark Mode|10.1.0.10|Armin Osaj & Samuel Schiegg|{470BC918-3740-4A97-9797-8570A7961130}_is1 XP8JMKMC3GVX23|Wondershare EdrawMax|WONDERSHARE GLOBAL LIMITED|Wondershare EdrawMax(Build 11.1.2.870)|11.1.2.870|EdrawSoft Co.,Ltd.|{037BAB81-3DF7-4381-A72C-A26B57C03548}_is1 XP8JNNTH0LT9F1|ApowerEdit|网旭科技|ApowerEdit V1.7.7.22|1.7.7.22|Apowersoft LIMITED|{3089CCCD-BC5F-4309-A3C1-45B5ACA7A5E7}_is1 XP8K17KD2T7W8V|Ashampoo WinOptimizer 19|Ashampoo|Ashampoo WinOptimizer 19|19.00.23|Ashampoo GmbH & Co. KG|{4209F371-A9E3-7DD2-C1E5-04BB2B081219}_is1 XP8K1F4KDP9DSJ|Autonoleggio N.S.C.|Esposito Software di M. G. Caputo|Autonoleggio NSC 3.0 Demo||Copyright Esposito Software|Autonoleggio NSC 3.0 Demo_is1 XP8K43JX54F7FL|Cute Cursors|Cute Cursors|CuteCursors|1.0.0|Apollo One|{6683BBFB-B899-4755-B260-DF0387D9F872} XP8K513CFB5K58|Archivio Dipendenti con Foto|Esposito Software di Maria Grazia Caputo|Archivio Dipendenti con Foto Demo||Copyright Esposito Software|Archivio Dipendenti con Foto Demo_is1 XP8LFCZM790F6B|Visual Studio Code - Insiders|Microsoft Corporation|Microsoft Visual Studio Code Insiders (User)|1.67.0|Microsoft Corporation|{217B4C08-948D-4276-BFBB-BEE930AE5A2C}_is1 XP8LFD92C0T8P0|Stampa Tessere Associazioni|Esposito Software di Maria Grazia Caputo|Stampa Tessere Associazioni 5.0 Demo||Copyright Esposito Software|Stampa Tessere Associazioni 5.0 Demo_is1 XP8LG1VTM0XW03|Gestione Protocollo e Pratiche|Esposito Software di Maria Grazia Caputo|Gestione Protocollo e Pratiche Demo||Copyright Esposito Software|Gestione Protocollo e Pratiche Demo_is1 XP8LG2X182JTJ9|Wondershare Dr.Fone - Mobile Device Management|WONDERSHARE GLOBAL LIMITED|Wondershare Dr.Fone (Version 10.9.6)|10.9.6.398|Wondershare Technology Co.,Ltd.|{E8F86DA8-B8E4-42C7-AFD4-EBB692AC43FD}_is1 XP8LG65GV4C7C8|GitMind Mind Map|网旭科技|GitMind 1.0.8|1.0.8|Apowersoft|a0e10d84-6512-552f-a0ec-5dd2e61ffe64 XP8LKPZT4X0Z0P|GOM Player|Gom and Company|GOM Player|2.3.67.5331|GOM & Company|GOM Player XP8LKWQ22DX3TF|JYL Visitor Windows|JYL Software|JYL Visitor 1.94|1.94|JYL Software|{02ADFF54-7D56-42F1-B517-FDA35F55D2CC} XP99J3KP4XZ4VV|ZOOM Cloud Meetings|Zoom Video Communications, Inc.|Zoom|5.10.0 (4306)|Zoom Video Communications, Inc.|ZoomUMX XP99J7FXZD0JDM|Emjysoft eSanté|Emjysoft|Suivi des soins et des remboursements de Santé|3.11|Emjysoft|{6CC28634-D98C-4DE1-9EE7-E121277996F6}_is1 XP99JXDBM4XKFP|Parallels Toolbox|Corel Corporation|Parallels Toolbox|5.1.0.3170|Parallels International GmbH|{5145E2CF-E9FC-48E6-A2B4-E409FC84D059} XP99K41V2P36RQ|MSIX Editor|InstallAware Software Corporation|InstallAware Msix Editor 1.0|1.0.0.2703|InstallAware Software|InstallAware Msix Editor 1.0 XP99VR1BPSBQJ2|Epic Games Store|Epic Games Inc.|Epic Games Launcher|1.3.23.0|Epic Games, Inc.|{FAC47927-1A6A-4C6E-AD7D-E9756794A4BC} XP99WSCKQSH7SW|Emjysoft Sauvegarde Facile|Emjysoft|Easy Backup|VersionApplication|Emjysoft|{37215B1A-1990-4F55-936E-C9BA1634EF75}}_is1 XP99WT9NMGB1PN|蜜蜂剪辑|网旭科技|BeeCut V1.7.7.22|1.7.7.22|Apowersoft LIMITED|{CA76BFA8-1862-49D7-B2C7-AE3D6CF40E53}_is1 XP9B0HTG55KTCH|Free Hex Editor Neo|HHD Software Ltd.|HHD Software Free Hex Editor Neo 6.54|6.54.02.6790|HHD Software, Ltd.|{8EB85C0E-DE7D-4A53-BD66-708B8F2C80B0} XP9B16C2TFN8P1|GOM Mix Pro|Gom and Company|GOM Mix Pro|2.0.4.8|GOM & Company|GOMMixPro XP9CFZ9PKV0DWS|Automation Workshop|Febooti, SIA|Febooti Automation Workshop|5.1.1.0|Febooti Software|{6114DD12-2516-4465-9275-FB9A8E1A583C} XP9CRZD7D219NK|FolderSizes|Key Metric Software|FolderSizes 9|9.3.362|Key Metric Software|{587D3069-EFE1-4FC2-B917-01496D5ABF8A} XP9CRZQDCJ0CC6|LetsView|网旭科技|LetsView V1.1.2.5|1.1.2.5|LetsView LIMITED|{6AA74BE4-9506-4D81-A07C-A40F883C2EA7}_is1 XP9CSP03RV8BX9|Audials One 2022|Audials AG|Audials 2022|22.0.177.0|Audials AG|{3F273072-3D14-479E-B4CD-AC8B1F436DA1} XP9K4SR87H227Q|VisualNEO Win|SinLios Soluciones Digitales|VisualNEO Win|21.9.9|SinLios|{57147D4D-2492-41EC-A552-FB37C1C7FF3E}_is1 XP9K5VRXFHVP75|Database Creator|Esposito Software di Maria Grazia Caputo|Database Creator Demo||Copyright Esposito Software|Database Creator Demo_is1 XP9K5XN9BRN466|Housecall Free Virus - Malware Scanner|Trend Micro Inc.|HouseCall|1.62|Trend Micro Inc.|{A114E34B-AA5C-4DD8-98A9-3130ACA19491} XP9KHKZS1M19ZP|x-studio|Simdsoft Limited|x-studio 2022|2022.1.4|Simdsoft Limited|{2F7387D3-EB5F-4CA5-8C42-04C59F922740} XP9KHM4BK9FZ7Q|Visual Studio Code|Microsoft Corporation|Microsoft Visual Studio Code (User)|1.66.0|Microsoft Corporation|{771FD6B0-FA20-440A-A002-3B3BAC16DC50}_is1 XP9KHPQ5C9MSN2|ZIPmagic|ZIPmagic Software|ZIPmagic|19.19.21|Simon King|ZIPmagic XP9KHPXMW6RQLL|Gestione Studio Tecnico|Esposito Software di M. G. Caputo|Gestione Studio Tecnico Demo||Copyright Esposito Software|Gestione Studio Tecnico Demo_is1 XP9KHQZV691PF9|PTZ Link|AVer Information|AVer PTZ Link|1.1.1013.0|AVer Information Inc|{AC08D179-14D5-4B93-9684-20DBE0848637} XP9KM2X7H10448|PCmover Reconfigurator|Laplink Software Inc|Laplink Reconfigurator|1.0.0.1|Laplink Software, Inc.|{BBB86720-65BA-452A-A14D-B152CB506DD8} XP9M20CZB2C5W8|Powder - Gaming Recorder|Unique Entertainment Experience SAS|Powder 2.5.0|2.5.0|powder-team|2b39bc52-9c37-5fcd-ab25-906727f7c690 XP9MFNDJM19N0G|Gestione Affitti Pro|Esposito Software di M. G. Caputo|Gestione Affitti Pro 4.0 Demo||Copyright Esposito Software|Gestione Affitti Pro 4.0 Demo_is1 XPDBZ0BW87BCTV|POWER-KI Executor|XPLAB - Research in Automation|POWER-KI Executor|33.11|XPLAB - Research in Automation - Brescia - Italy|{B2B40FB5-0B60-4B47-A1F1-F0254CD0BE04} XPDBZ4MPRKNN30|Opera GX|Opera Norway AS|Opera GX Stable 82.0.4227.44|82.0.4227.44|Opera Software|Opera GX 82.0.4227.44 XPDC1LX9VNW7Z7|VirtualDJ|Atomix International, S.A.|VirtualDJ 2021|8.5.6747.0|Atomix Productions|{97CFEA35-98EF-4EBC-8AF1-4F161CFCAE86} XPDC2KHD93HVJW|Stampa Ricevute Generiche|Esposito Software di Maria Grazia Caputo|Stampa Ricevute Generiche Demo||Copyright Esposito Software|Stampa Ricevute Generiche Demo_is1 XPDCFJD1GFFDXD|WorldClock.Classic|Fulvio Castelli|WorldClock (Trial)|7.0.12.0|Fulvio Castelli|{E32193B9-8870-40be-B88A-B302251B8AA7}_is1 XPDCJ80KGNRVSS|TeamSpeak|TeamSpeak Systems GmbH|TeamSpeak|5.0.0|TeamSpeak|{C9D97E1E-B188-4500-A87D-902530E0D1E0} XPDCK0XGHVWNBK|Trend Micro Antivirus Plus Security|Trend Micro Inc.|Trend Micro Antivirus+|17.7|Trend Micro Inc.|{ABBD4BA8-6703-40D2-AB1E-5BB1F7DB49A4} XPDDZ434WT2M5Z|SOLARWATT Pro experience|SOLARWATT GmbH|SOLARWATT Experience|2.1.0.4|SOLARWATT|{40CF234F-1D35-4ED8-AAFC-E07EA2FD8B3B} XPDF9J69VVFMX3|Apowersoft Background Eraser|网旭科技|Apowersoft background eraser V2.3.13|2.3.13|Apowersoft LIMITED|{98EC0F66-C563-40FA-A77A-F2FC558F5DAA}_is1 XPDFF6P40P0M5Q|星愿浏览器|Twinkstar|Twinkstar Browser|7.12.1000.2112|Twinkstar Limited|Twinkstar XPDLNG5248Q7NC|HttpMaster Express|Borvid, Informacijske storitve, Janez Čas s.p.|HttpMaster Express Edition 5.4.1|5.4.1|Borvid|{B61241AA-F5FC-42C9-A1F9-F6D72D654349} XPDM19SX6D8V40|JYL Orders Suppliers Windows|JYL Software|JYL Order Suppliers 1.70|1.70|JYL Software|{57DF6E60-F6E4-498F-9637-18D6C0FA08B9} XPDM4ZR5KJ9JN9|PowerDirector 365 Free - Video Editor, Movie Maker|CyberLink Corp.|CyberLink PowerDirector 365|20.1.2519.0|CyberLink Corp.|{278A8296-12A6-4CD0-8A8E-6947948477C5} XPDM5Q9J9SFCX9|Stampa Ricevute Pagamento|Esposito Software di M. G. Caputo|Stampa Ricevute Pagamento Demo||Copyright Esposito Software|Stampa Ricevute Pagamento Demo_is1 XPDNG54ZDC79K0|JYL Time Clock Windows|JYL Software|JYL Time Clock 2.22|2.22|JYL Software|{839FD23A-EFE9-4252-AF1A-B8B56ED925F4} XPDNH1FMW7NB40|火绒安全软件|Beijing Huorong Network Technology Co., Ltd.|Huorong Internet Security|5.0|Beijing Huorong Network Technology Co., Ltd.|HuorongSysdiag XPDNLQK867NNXF|Ashampoo ZIP Pro 4|Ashampoo|Ashampoo ZIP Pro 4|4.10.22|Ashampoo GmbH & Co. KG|{0A11EA01-1F01-7AF6-20A2-E6F8131AD29C}_is1 XPDNXDPXBRSVXT|WinZip 26|WinZip Computing|WinZip 26.0|26.0.15033|Corel Corporation|{CD95F661-A5C4-44F5-A6AA-ECDD91C2413F} XPDNXG5333CSVK|Hard Disk Sentinel Professional|Janos Mathe|Hard Disk Sentinel PRO|6.01|Janos Mathe|Hard Disk Sentinel_is1 XPDNZ9TPLKW6TB|Fy Slideshow|Guutara's Notebook|Fy Slideshow|5.6.0|Guutara|{5A4DEC47-8784-4591-983F-A3A6C3C89A46} XPDNZJFNCR1B07|Avast Free Antivirus|AVAST Software|Avast Free Antivirus|22.2.6003|Avast Software|Avast Antivirus XPDP1XPZR8NL28|Studio Medico Pro|Esposito Software di M. G. Caputo|Studio Medico Pro 3.0 Demo||Copyright Esposito Software|Studio Medico Pro 3.0 Demo_is1 XPDP255TRF9WP8|Logspire|Anfibia Software|Logspire 1.0.0.51|1.0.0.51|Anfibia|Logspire_is1 XPDP2X1MMZ4KR8|Ashampoo Burning Studio 23|Ashampoo|Ashampoo Burning Studio 23|23.0.5|Ashampoo GmbH & Co. KG|{91B33C97-2A56-F111-077E-E591CE9D7DE7}_is1 XPFCFBB4FB3D6D|Emjysoft Cleaner|Emjysoft|Emjysoft Cleaner 2022 v4.1|4.1|Emjysoft|{167B1302-A739-42DE-BBD2-4C2F13D1FF51}_is1 XPFCFKCNNTXGQD|Yandex Browser|Yandex|Yandex|21.9.1.686|ООО «ЯНДЕКС»|YandexBrowser XPFCFL5ZTNFGD7|Wondershare Anireel|WONDERSHARE GLOBAL LIMITED|Wondershare Anireel(Build 1.6.2)||Wondershare Software|Wondershare Anireel_is1 XPFCG86X2PGLDJ|Christmas Elf by Pothos|Pothos|Christmas Elf|||ChristmasElf XPFCGHHXNH4WBW|Biblioteca e Prestiti Librari|Esposito Software di M. G. Caputo|Gestione Biblioteca e Prestiti Librari 3.0 Demo||Copyright Esposito Software|Gestione Biblioteca e Prestiti Librari 3.0 Demo_is1 XPFCWP0SQWXM3V|CCleaner|Piriform Software Ltd|CCleaner|5.89|Piriform|CCleaner XPFCXFRDJ8VGPT|Домашняя Бухгалтерия|Keepsoft|Äîìàøíÿÿ áóõãàëòåðèÿ Lite|7.2|Keepsoft|Äîìàøíÿÿ áóõãàëòåðèÿ Lite XPFCXPF18WNKP6|Total Defense Essential Anti-Virus|Total Defense, Inc.|Total Defense|13.0.0.572|Total Defense, Inc.|TotalDefense XPFCXS0QVTHDC9|Active@ Disk Editor|LSoft Technologies Inc.|Active@ Disk Editor 7|7|LSoft Technologies Inc|{F40165C8-BD5B-4E42-A40D-396BB707E5B7}_is1 XPFD27PCFQJQ68|TextSeek|Xiamen Zesite Company|TextSeek|2.12.3060|Zesite Company|TextSeek XPFD28MTCS0GXJ|VisualNEO Web|SinLios Soluciones Digitales|VisualNeoWeb||SinLios|{EEF9B1C5-7E35-4972-A79A-44B2B2C72D3D}_is1 XPFFBRXVQ2L6JN|Coolnew PDF|CoolNewPDF|CoolNew PDF|3.0.0.1|CoolNew Software Corporation|coolnewpdf XPFFC9N4PVM9N8|Prenotazione Tavoli OK|Esposito Software di Maria Grazia Caputo|Prenotazione Tavoli OK Demo||Copyright Esposito Software|Prenotazione Tavoli OK Demo_is1 XPFFCCM235X204|Fy Memo|Guutara's Notebook|Fy Memo|6.5.0|Guutara|{4BDAE26E-3414-4516-89F9-B6C277029CA5} XPFFCM599XXT5P|傲软录屏|网旭科技|ApowerREC V1.5.5.18|1.5.5.18|Apowersoft LIMITED|{6F2998B2-21F7-4CEF-94B2-C3919D939CF9}_is1 XPFFH5S3C4Q1CB|傲软抠图|网旭科技|Apowersoft background eraser V2.3.13|2.3.13|Apowersoft LIMITED|{98EC0F66-C563-40FA-A77A-F2FC558F5DAA}_is1 XPFFSV0VCDKTM5|PolicyApplicator Conversion Kit|Hauke Hasselberg|PolicyApplicator Conversion Kit|1.0.11.0|Hauke Götze|{C918DB43-6B86-4364-BEAC-1184D3EE3C07} XPFFT29L5QQ7RL|SRPG Studio|SapphireSoft|SRPG Studio Trial version 1.251|1.251|SapphireSoft|{FBC98908-FD84-4C92-A539-5DA61EDD7F9F}_is1 XPFFT3RD5FMWX2|Emjysoft Comptabilité Personnelle|Emjysoft|Personal Finance|20.5|Emjysoft|{2369DC9E-11A7-4BAE-A43E-7A4CB477574F}_is1 XPFFTPNN0NNHVQ|Auto Print Order|NAMTUK|AutoPrintOrder 1.10.1215|1.10.1215|Namtuk|{B26EF0DD-2375-4E88-9991-4652AC89FE3F} XPFM2BJ3RPZ9XB|轻闪PDF编辑|网旭科技|LightPDF Editor V1.2.6.0|1.2.6.0|Apowersoft LIMITED|{161C8BF4-DB06-49A7-B6AC-7CAB7DAF136F}_is1 XPFM306TS4PHH5|Ashampoo Burning Studio FREE|Ashampoo|Ashampoo Burning Studio FREE|1.21.5|Ashampoo GmbH & Co. KG|{91B33C97-91F8-FFB3-581B-BC952C901685}_is1 XPFM5W1J84KQZX|ndCurveMaster|SigmaLab Tomasz Cepowski|ndCurveMaster Trial x64 version 8.2.0.1|8.2.0.1|SigmaLab|{5FB2948C-B95A-49CD-A2ED-62D0A38D7B1C}_is1 XPFMJGWHHCNL5P|傲软投屏—手机/电脑/电视高清投屏神器|网旭科技|ApowerMirror V1.6.5.1|1.6.5.1|APOWERSOFT LIMITED|{a9482532-9c34-478c-80c3-85bdccbb981f}_is1 XPFMKKKLHMMK6Q|Videoteca OK|Esposito Software di Maria Grazia Caputo|Videoteca OK 5.0 Demo||Copyright Esposito Software|Videoteca OK 5.0 Demo_is1 XPFNZJKG6100L4|ASM Visual|gri-software|ASM Visual version 1.1.7|1.1.7|gri-software|{7416EF27-89A5-4819-9996-36C16F49BAEC}_is1 XPFNZKDRP1SXM6|视频转换王|网旭科技|Apowersoft Video Converter Studio V4.8.6.7|4.8.6.7|APOWERSOFT LIMITED|{195E8D7F-292B-4B04-A6E7-E96CAF04C767}_is1 XPFP0G0V147H6D|Wondershare PDFelement|WONDERSHARE GLOBAL LIMITED|Wondershare PDFelement ( Version 8.3.0 )|8.3.0|Wondershare|{343A530C-4726-4091-87E0-F9CC41792CE2}_is1 XPFP2VCXM8D2DB|傲软PDF编辑——一键编辑&转化&压缩&签名PDF文件|网旭科技|ApowerPDF V5.4.2.3|5.4.2.3|Apowersoft LIMITED|{8691C793-7B2C-46C5-9AB2-AB80D129A5EC}_is1 XPFP30KL61D4SC|Wondershare UniConverter|WONDERSHARE GLOBAL LIMITED|Wondershare UniConverter 13(Build 13.5.1.116)|13.5.1.116|Wondershare Software|UniConverter 13_is1 XPFP42D8L456SK|X-VPN - Best VPN Proxy and Wifi Security|Free Connected Limited.|X-VPN|71.0|Free Connected Limited|X-VPN XPFP42J061BPC1|Documenti Lavori Cantiere|Esposito Software di M. G. Caputo|Documenti Lavori Cantiere Demo||Copyright Esposito Software|Documenti Lavori Cantiere Demo_is1 XPFPFN4LT21PZJ|Studio Dentistico Pro|Esposito Software di M. G. Caputo|Studio Dentistico Pro Demo||Copyright Esposito Software|Studio Dentistico Pro Demo_is1 XPFPFWMVTR0WHP|Ashampoo UnInstaller 11|Ashampoo|Ashampoo UnInstaller 11|11.00.12|Ashampoo GmbH & Co. KG|{4209F371-B84B-F321-6BD3-1D91E2505732}_is1 XPFPFWV5JD80K2|BeeCut|网旭科技|BeeCut V1.7.7.22|1.7.7.22|Apowersoft LIMITED|{CA76BFA8-1862-49D7-B2C7-AE3D6CF40E53}_is1 XPFPLCB36G8V8J|HttpMaster Professional|Borvid, Informacijske storitve, Janez Čas s.p.|HttpMaster Professional Edition 5.4.1|5.4.1|Borvid|{B61241AA-F5FC-42C9-A1F9-F6D72D654349} XP8CDJNZKFM06W|Visual Studio Community 2019|Microsoft Corporation|Microsoft Visual Studio Installer|3.1.2196.8931|Microsoft Corporation|{6F320B93-EE3C-4826-85E0-ADF79F8D4C61} ================================================ FILE: src/AppInstallerCLITests/TestData/InputNames.txt ================================================ 0 A.D. 010 Editor 11.0 (64-bit) 360安全卫士 360杀毒 4K Slideshow Maker 1.8 4K Stogram 4K Video Downloader 4K Video Downloader 4.12 4K Video to MP3 4K YouTube to MP3 3.12 7-Zip 16.04 (x64 edition) 7-Zip 19.00 (x64 edition) 7-Zip 20.02 alpha (x64) 7-Zip ZS 19.00 ZS v1.4.5 R2 (x64) AWS Command Line Interface AWS Command Line Interface v2 AWS SAM Command Line Interface AXIS Camera Station 5.33 AbaClient Accessibility Insights For Windows v1.1 Active Directory Authentication Library for SQL Server Adobe Acrobat Reader DC - Czech Adobe Acrobat Reader DC Adobe Acrobat Reader DC MUI AdoptOpenJDK JDK with Hotspot 11.0.7.10 (x64) AdoptOpenJDK JDK with Hotspot 11.0.8.10 (x64) AdoptOpenJDK JDK with Hotspot 14.0.1.7 (x64) AdoptOpenJDK JDK with Hotspot 14.0.2.12 (x64) AdoptOpenJDK JDK with Hotspot 15.0.0.36 (x64) AdoptOpenJDK JDK with Hotspot 8.0.252.09 (x64) AdoptOpenJDK JDK with Hotspot 8.0.265.01 (x64) Advanced IP Scanner 2.5 Advanced Log Viewer 8.1.0 Advanced Port Scanner 2.5 AdvancedRestClient 15.0.5 Aegisub 8975-master-8d77da3 Aegisub r8942 AeroZoom 4.0 beta 2 Alacritty Alchemy Beta x64 Algodoo v2.1.0 AltDrag Amazon Chime Amazon Corretto (x64) Amazon Corretto 8 (x64) Amazon WorkSpaces Anaconda3 2020.07 (Python 3.8.3 64-bit) Angry IP Scanner AntiMicro AppGet Appium 1.15.1 Appium 1.18.3 Arduino Armagetron Advanced 0.2.8.3.5 Artha 1.0.3.0 AssaultCube 1.2.0.2 Audacity 2.4.2 AuthPass version 1.7.9_1605 Auto Dark Mode AutoHotkey 1.1.32.00 AutoHotkey 1.1.33.02 Autopsy AviSynth 2.6 Avro Keyboard 5.6.0 Aya Azure Cosmos DB Emulator Azure Data Studio (User) Azure Functions Core Tools - 3.0.2534 (x64) Azure IoT Explorer (preview) Azure IoT explorer BCUninstaller BITS Manager BKChem-0.13.0 BOINC BPBible 0.5.3.1 Backup and Sync from Google Barrier 2.3.2-snapshot Barrier 2.3.3-release Beaker Browser 0.8.10 Beats winlogbeat 7.7.0 (x86_64) Beats winlogbeat 7.9.2 (x86_64) BeeBEEP 5.8.2 Beeftext Betaflight Configurator Beyond Compare 4 Beyond Compare 4.3.6 Beyond Compare 4.3.7 BiglyBT BitPay version 4.8.1 Bitwarden BleachBit 4.0.0.1628 (current user) Blender BlueJeans Bob the Hamster VGA 2008-01-23 Bonjour Borderless Gaming Bot Framework Composer 1.0.0 Bot Framework Composer 1.0.1 Bot Framework Composer 1.0.2 Bot Framework Composer 1.1.1 Bot Framework Emulator 4.10.0 Bot Framework Emulator 4.8.1 Bot Framework Emulator 4.9.0 Brackets Brave Brave Nightly Bulk Rename Utility 3.3.1.0 (64-bit) Buttercup 1.19.0 Buttercup 1.20.0 C-Dogs SDL CCEnhancer version 4.5.6 CCleaner CDBurnerXP (64 bit) CMake CORSAIR iCUE Software CPUID CPU-Z 1.92 CPUID CPU-Z 1.93 CPUID CPU-Z 1.94 CPUID HWMonitor 1.41 CPUID HWMonitor 1.42 Cacher CaesiumPH 0.9.5 Camtasia 2019 Camtasia 2020 Caprine 2.47.0 Caprine 2.48.0 Caption 2.0.1 (only current user) Captura v8.0.0 CemUI 2.3.3 (only current user) Cerebro 0.3.2 CertAid for Windows CertDump Certify The Web version 5.1.5 ChMac 2.0 ChefDK v4.11.0 ChemAxon ChemCurator ChemAxon Markush Editor ChemAxon Marvin Suite 20.13.0 ChemAxon Marvin Suite 20.19.0 Chromium Circuit Diagram version 3.0 Circuit Diagram version 3.1 Cisco Webex Meetings Citycraft Launcher 1.9.9 ClamWin Free Antivirus 0.99.4 Clash for Windows 0.11.1 Clash for Windows 0.11.6 Clash for Windows 0.11.8 Clash for Windows 0.9.11 ClassIn Clementine Clink v0.4.9 Cloudflare WARP CodeBlocks CodeLite Coffee Colobot: Gold Edition alpha-0.1.12 Color Cop 5.4.3 Colorpicker 2.0.3 ConEmu 201011.x64 Concept2 Utility ConfigMgr 2012 Toolkit R2 Contasimple Desktop 3.1.0 Core Temp 1.16 Couchbase Server 6.5.1-6299 Community Edition Couchbase Server 6.5.1-6299 Enterprise Edition Cozy Drive 3.20.0 Cppcheck x64 2.0 Crypter 4.0.0 Cryptomator CrystalDiskInfo 8.6.1 CrystalDiskInfo 8.6.2 CrystalDiskInfo 8.6.2 Kurei Kei Edition CrystalDiskInfo 8.6.2 Shizuku Edition CrystalDiskInfo 8.7.0 CrystalDiskInfo 8.8.1 CrystalDiskInfo 8.8.1 Kurei Kei Edition CrystalDiskInfo 8.8.1 Shizuku Edition CrystalDiskInfo 8.8.5 CrystalDiskInfo 8.8.9 CrystalDiskInfo 8.8.9 Kurei Kei Edition CrystalDiskInfo 8.8.9 Shizuku Edition CrystalDiskInfo 8_8_2 CrystalDiskInfo 8_8_2 Kurei Kei Edition CrystalDiskInfo 8_8_2 Shizuku Edition CrystalDiskMark 7.0.0h CrystalDiskMark 7.0.0h Shizuku Edition CubicSDR 0.2.5 Installer CutePDF Writer Cyberduck DB Browser for SQLite DBeaver 7.0.5 (current user) DBeaver 7.1.0 (current user) DBeaver 7.1.2 (current user) DBeaver 7.1.3 (current user) DBeaver 7.1.4 (current user) DBeaver 7.2.0 (current user) DBeaver 7.2.1 (current user) DBeaver 7.2.2 (current user) DBeaver 7.2.3 (current user) DJI Assistant 2 (DJI FPV series) version V2.0.2.11 DJI Assistant 2 For Aeroscope version V2.0.1.3 DJI Assistant 2 For Autopilot version V2.0.3.7 DJI Assistant 2 For Battery Station version V2.0.1.9 DJI Assistant 2 For MG version V2.0.18.1 DJI Assistant 2 For Matrice version V2.0.13.2 DJI Assistant 2 For Mavic version V2.0.14.1 DJI Assistant 2 For Phantom version V2.0.10.4 Dave Gnukem DeepVocalToolBox_beta_1.1.6 version beta_1.1.6 DeepVocal_beta_1.1.6 version beta_1.1.6 Deezer 4.19.20 DefaultAudio Defraggler Dell Command | Update Dell Update Dev-C++ Dimension 4 v5.31 Discord Media Loader version 1.3.0.0 Discord Media Loader version 1.4.0.0 Ditto Dixa DjVuLibre DjView 3.5.27+4.11 DockStation 1.5.1 Docker Desktop Dokan Library 1.3.0.1000 (x64) Dokan Library 1.4.0.1000 (x64) Dolphin Doomsday 2.2.2.3313 Dopamine Doxie 2.12.2 Dropbox EGR-SafenetActivation EGR-ShellExtension EagleGet version 2.1.5.10 EagleGet version 2.1.6.70 EasyConnect EditPlus (64 bit) EduMIPS64 Elasticsearch 7.9.2 Elgato Stream Deck Empoche 0.4.0 Empoche 0.4.3 Encrypto version 1.0.1 EnglishizeCmd 2.0 Enpass Eraser 6.2.0.2986 Eraser 6.2.0.2988 Eraser 6.2.0.2989 Eraser 6.2.0.2990 Esteem 2.2.7 Ethereum - Geth - Official Go implementation of the Ethereum protocol Evernote v. 6.21.2 Evernote v. 6.24.2 Everything 1.4.1.969 (x64) Everything 1.4.1.986 (x64) Everything 1.4.1.988 (x64) Everything 1.4.1.988 Lite (x64) Everything 1.4.1.992 (x64) ExpressVPN Expresso Extreme TuxRacer 0.8 (x64) Far Manager 3 x64 FastCopy FastStone Capture 9.3 FastStone Image Viewer 7.5 Fedora Media Writer Ferdi 5.5.0 Fiddler Everywhere 1.0.2 Fiddler Everywhere 1.1.0 Fiddler Everywhere 1.1.0-insiders Fiddler Everywhere 1.1.0-internal Fiddler Everywhere 1.1.1 Fiddler Everywhere 1.1.1-insiders Fiddler Everywhere 1.1.1-internal File Converter (64 bit) FileSeek 6.4 FileZilla Client 3.47.0 FileZilla Client 3.48.1 FileZilla Client 3.49.1 FileZilla Client 3.50.0 FileZilla Client 3.51.0 Firefox Developer Edition 77.0 (x64 en-US) Firefox Developer Edition 78.0 (x64 en-US) Firefox Developer Edition 80.0 (x64 en-US) Firefox Developer Edition 82.0 (x64 en-US) FlashFXP 5 FlightGear v2018.3.5 FontBase 2.11.3 FontForge version 14-03-2020 FormatFactory 5.3.0.1 FormatFactory 5.4.5.0 Foxit PhantomPDF Foxit Reader Franz 5.5.0 FreeCommander XE FreeMat GIMP 2.10.0 GIMP 2.10.10 GIMP 2.10.14 GIMP 2.10.16 GIMP 2.10.18 GIMP 2.10.20 GIMP 2.10.6 GIMP 2.10.8 GNU Arm Embedded Toolchain 9-2020-q2-update 9 2020 (remove only) GNU Midnight Commander version 4.8.24 (build: 20200521-217) GNU Privacy Guard GNURadio-3.7 GOG GALAXY GPL Ghostscript GSview 5.0 Garmin Express Gauge 1.0.6 Gauge 1.1.1 Geany 1.36 Geekbench 5 Gephi 0.9.2 GetDiz Git Extensions 3.3.1.7897 Git Extensions 3.4.1.9675 Git Extensions 3.4.3.9999 Git LFS version 2.11.0 Git version 2.24.1.2 Git version 2.25.1 Git version 2.26.2 Git version 2.27.0 Git version 2.28.0 Git version 2.29.0 GitHub CLI GitHub Desktop Machine-Wide Installer GitHubReleaseNotes Gitter Glimpse 0.1.2 Glimpse 0.2.0 (64-bit) Glimpse 0.2.0 GnuCash 3.9 GnuCash 4.1 GnuWin32: Grep-2.5.4 GnuWin32: Make-3.81 GnuWin32: Wget-1.11.4-1 GnuWin32: Zip-3.0 Go Programming Language amd64 go1.13.12 Go Programming Language amd64 go1.13.13 Go Programming Language amd64 go1.13.14 Go Programming Language amd64 go1.13.15 Go Programming Language amd64 go1.14.10 Go Programming Language amd64 go1.14.3 Go Programming Language amd64 go1.14.4 Go Programming Language amd64 go1.14.5 Go Programming Language amd64 go1.14.6 Go Programming Language amd64 go1.14.7 Go Programming Language amd64 go1.14.8 Go Programming Language amd64 go1.14.9 Go Programming Language amd64 go1.15 Go Programming Language amd64 go1.15.1 Go Programming Language amd64 go1.15.2 Go Programming Language amd64 go1.15.3 Go Programming Language amd64 go1.15beta1 GoldWave v6.52 Google Chrome Google Cloud SDK Google Earth Pro Gpg4win (3.1.11) Gpg4win (3.1.13) GrafanaEnterprise GrafanaOSS Grammarly for Microsoft® Office Suite GrampsAIO64 GraphQL Playground 1.8.10 GraphiQL 0.7.2 Graphviz Greenshot 1.2.10.6 Grid 1.6.2 Grindstone 4 HHD Software Free Hex Editor Neo 6.44 HM NIS Edit 2.0.3 HP Cloud Recovery Tool HUAWEI Cloud HWiNFO64 Version 6.26 HWiNFO64 Version 6.28 HWiNFO64 Version 6.30 HWiNFO64 Version 6.32 HandyWinGet Harmony 0.9.1 (only current user) HashTab 5.2.0.14 HashTab 6.0.0.34 HeavyLoad V3.6 (64 bit) Hedgewars HeidiSQL 11.0.0.5919 HeidiSQL 11.0.0.5995 HeidiSQL 11.0.0.5997 HeidiSQL 11.0.0.6000 HeidiSQL 11.0.0.6057 Helix Core Apps HelpNDoc 6.9.0.577 Personal Edition HexChat Hosts File Editor Hover HttpMaster Express Edition 4.7.0 HttpMaster Express Edition 4.7.1 HttpMaster Express Edition 4.7.2 HttpMaster Express Edition 4.7.3 HttpMaster Professional Edition 4.7.1 HttpMaster Professional Edition 4.7.2 HttpMaster Professional Edition 4.7.3 Huawei QuickApp IDE Hyne Timber Design 7.5.14.0 Hyperspace Desktop 1.1.3 IAP Desktop IDA Freeware 7.0 IO Ninja 3 IPFilter 3.0.2.9-beta IRCCloud 0.15.0 IZArc 4.4 ImageGlass Inkscape Inno Setup version 6.0.4 Inno Setup version 6.0.5 InternetOff 3.0, 32\64 bit edition Intuiter 0.5.0 IrfanView 4.54 (64-bit) IronPython 2.7.10 IronPython 2.7.9 IsWiX IsoBuster 4.6 JChem .NET API 20.19.0.482 JabRef Jackett Jami Java 8 Update 251 (64-bit) Java 8 Update 261 (64-bit) JetBrains Toolbox Jitsi Meet 2.3.1 Jitsi Meet 2.4.1 Joplin 1.0.201 Joplin 1.0.216 Joplin 1.0.233 Julia 1.4.1 Julia 1.4.2 Julia 1.5.1 K-Lite Codec Pack 15.7.0 Standard K-Lite Mega Codec Pack 15.7.0 KKBOX Kaku 2.0.2 KeePass Password Safe 2.44 KeePass Password Safe 2.45 KeePass Password Safe 2.46 KeePassXC KeeWeb Keybase KiCad 5.1.5_1 KiCad 5.1.6_1 KiCad 5.1.7_1 Krisp Krita (x64) 4.3.0 L'Math version r1.6 LBRY 0.45.1 LBRY 0.45.2 LBRY 0.46.2 LBRY 0.47.0 LBRY 0.47.1 LINE LINQPad 6 LLVM LMMS 1.2.1 LMMS 1.2.2 LOVE 11.3 Laragon 4.0.15 LastPass (uninstall only) Lazarus 2.0.8 League of Legends Lenovo Migration Assistant Lenovo System Update Lens 3.5.1 Leonflix 0.7.0 Liberica JDK 11 (64-bit) Liberica JDK 11 Full (64-bit) Liberica JDK 14 (64-bit) Liberica JDK 14 Full (64-bit) Liberica JDK 15 (64-bit) Liberica JDK 15 Full (64-bit) Liberica JDK 8 (64-bit) Liberica JDK 8 Full (64-bit) LibreCAD LibreOffice 7.0.1.2 LibreOffice 7.0.2.2 Lidarr version 0.7.1 LightBulb 2.0 LightBulb 2.2 Lightscreen version 2.4 Linrad-04.14a version 04.14a Lisk Hub 1.22.0 Listen1 2.13.0 Listen1 2.5.2 Local 5.5.3 LockHunter 3.3, 32/64 bit LogFusion 6.4 LogFusion 6.4.1 Logitech Gaming Software 9.02 Loom 0.37.2 LyX 2.3.4.4 LyX 2.3.5.2 MCX Studio version nightlybuild MCX Studio version v2020 MKVToolNix 46.0.0 (64-bit) MKVToolNix 47.0.0 (64-bit) MKVToolNix 48.0.0 (64-bit) MKVToolNix 49.0.0 (64-bit) MKVToolNix 50.0.0 (64-bit) MPC-HC 1.7.13 (64-bit) MPC-HC 1.9.5 (64-bit) MPC-HC 1.9.6 (64-bit) MQTT Explorer 0.3.5 MSIX Core MX5 MY.GAMES GameCenter MacType MailWasher MailWasherPro Majsoul Plus 2.0.0 MakeMKV v1.15.3 Malwarebytes version 4.2.1.89 Marble version 2.2.0 MariaDB 10.5 (x64) Mark Text 0.16.2 Markdown Monster 1.22.8.0 Markdown Monster 1.23.0.0 Markdown Monster 1.23.12.0 Markdown Monster 1.23.14.0 Markdown Monster 1.24.12.0 Markdown Outlook Master Packager Mattermost MediaInfo 20.09 MediaInfo-CLI 20.09 MediaMonkey 4.1 Meld Memurai Developer Microsoft .NET Core SDK 3.1.202 (x64) Microsoft .NET Core SDK 3.1.300 (x64) Microsoft .NET Core SDK 3.1.301 (x64) Microsoft .NET Core SDK 3.1.302 (x64) Microsoft .NET Core SDK 3.1.401 (x64) Microsoft .NET Core SDK 3.1.402 (x64) Microsoft .NET Framework 4.5 Multi-Targeting Pack Microsoft .NET Framework 4.5.1 Multi-Targeting Pack (ENU) Microsoft .NET Framework 4.5.1 Multi-Targeting Pack Microsoft .NET Framework 4.5.1 SDK Microsoft .NET Framework 4.5.2 Multi-Targeting Pack (ENU) Microsoft .NET Framework 4.5.2 Multi-Targeting Pack Microsoft .NET Framework 4.7.2 SDK Microsoft .NET Framework 4.7.2 Targeting Pack Microsoft .NET Framework 4.8 SDK Microsoft .NET Framework 4.8 Targeting Pack Microsoft .NET SDK 5.0.100-preview.5.20279.10 (x64) Microsoft .NET SDK 5.0.100-preview.8.20417.9 (x64) Microsoft .NET SDK 5.0.100-rc.1.20452.10 (x64) Microsoft .NET SDK 5.0.100-rc.2.20479.15 (x64) Microsoft Azure CLI Microsoft Azure Storage Emulator - v5.10 Microsoft Azure Storage Explorer version 1.14.0 Microsoft Azure Storage Explorer version 1.15.1 Microsoft Deployment Toolkit (6.3.8456.1000) Microsoft Edge Microsoft Edge Beta Microsoft Edge Dev Microsoft Garage Mouse without Borders Microsoft Help Viewer 2.2 Microsoft Help Viewer 2.3 Microsoft MPI (10.1.12498.16) Microsoft MPI (10.1.12498.18) Microsoft MPI SDK (10.1.12498.18) Microsoft ODBC Driver 13 for SQL Server Microsoft ODBC Driver 17 for SQL Server Microsoft OLE DB Driver for SQL Server Microsoft R Open 3.5.3 Microsoft R Open 4.0.2 Microsoft SQL Server 2012 Native Client Microsoft SQL Server 2014 Management Objects Microsoft SQL Server 2016 Microsoft SQL Server 2016 Policies Microsoft SQL Server 2016 T-SQL Language Service Microsoft SQL Server 2016 T-SQL ScriptDom Microsoft SQL Server 2017 Microsoft SQL Server 2017 Policies Microsoft SQL Server 2017 T-SQL Language Service Microsoft SQL Server Data-Tier Application Framework (x86) Microsoft SQL Server Management Studio - 16.5.3 Microsoft SQL Server Management Studio - 17.9.1 Microsoft SQL Server Management Studio - 18.5 Microsoft SQL Server Management Studio - 18.5.1 Microsoft SQL Server Management Studio - 18.6 Microsoft Small Basic v1.2 Microsoft System CLR Types for SQL Server 2014 Microsoft System CLR Types for SQL Server 2016 Microsoft System CLR Types for SQL Server 2017 Microsoft Visio Viewer 2016 Microsoft Visual Studio 2010 Tools for Office Runtime (x64) Microsoft Visual Studio 2015 Shell (Isolated) Microsoft Visual Studio Code (User) Microsoft Visual Studio Code Insiders (User) Microsoft Visual Studio Tools for Applications 2015 Microsoft Visual Studio Tools for Applications 2015 Language Support Microsoft Visual Studio Tools for Applications 2017 Microsoft Web Platform Installer 5.1 Miniconda3 4.7.12 (Python 3.7.4 64-bit) Miniconda3 py37_4.8.3 (Python 3.7.7 64-bit) MongoDB 4.2.8 2008R2Plus SSL (64 bit) Mono for Windows (x64) MonoGame SDK Moonlight Game Streaming Client Motrix 1.5.10 Motrix 1.5.15 Mozilla Firefox 68.8.0 ESR (x64 en-US) Mozilla Firefox 68.9.0 ESR (x64 en-US) Mozilla Firefox 76.0.1 (x86 en-US) Mozilla Firefox 77.0 (x64 en-US) Mozilla Firefox 77.0.1 (x64 en-US) Mozilla Firefox 78.0 ESR (x64 en-US) Mozilla Firefox 78.0.1 (x64 en-US) Mozilla Firefox 78.0.2 (x64 cs) Mozilla Firefox 78.0.2 (x64 en-US) Mozilla Firefox 78.1.0 ESR (x64 en-US) Mozilla Firefox 78.4.0 ESR (x64 en-US) Mozilla Firefox 79.0 (x64 en-US) Mozilla Firefox 80.0 (x64 en-US) Mozilla Firefox 80.0.1 (x64 en-US) Mozilla Firefox 81.0 (x64 en-US) Mozilla Firefox 81.0.1 (x64 en-US) Mozilla Firefox 81.0.2 (x64 en-US) Mozilla Firefox 82.0 (x64 en-US) Mozilla Firefox 82.0.1 (x64 en-US) Mozilla Firefox 82.0.1 (x64 es-MX) Mozilla Firefox 82.0.2 (x64 en-US) Mozilla Maintenance Service Mozilla Thunderbird 68.10.0 (x64 en-US) Mozilla Thunderbird 68.8.0 (x86 en-US) Mozilla Thunderbird 68.9.0 (x64 en-US) Mozilla Thunderbird 77.0 (x64 en-US) Mozilla Thunderbird 78.0 (x64 cs) Mozilla Thunderbird 78.0 (x64 en-US) Mozilla Thunderbird 78.0.1 (x64 en-US) Mozilla Thunderbird 78.1.0 (x64 en-US) Mozilla Thunderbird 78.1.1 (x64 en-US) Mozilla Thunderbird 78.3.2 (x64 en-US) Mu Mullvad VPN 2020.5.0 Mullvad VPN 2020.6.0 Multilingual App Toolkit 4.0 Multipass Mumble 1.3.1 Mumble 1.3.2 Mumble 1.3.3 MuseScore 3 Muta 2.1.02 MyHarmony MyPaint MySQL Installer - Community Mypal 28.14.2 (x64 en-US) NBTExplorer NSwagStudio NVDA NVIDIA NVIDIA RTX Voice Driver 1.0.0.2 NVIDIA RTX Voice Application NZXT CAM 4.10.1 NZXT CAM 4.11.0 NZXT CAM 4.12.0 NZXT CAM 4.13.0 NZXT CAM 4.8.0 NZXT CAM 4.9.2 Nelson-0.4.8.2662 (64 bits) NeoLoad 7.3.0 Netron 4.5.9 Nitro Pro Nmap 7.80 NoSQLBooster for MongoDB 4.7.5 (only current user) NoSQLBooster for MongoDB 5.2.12 NoSQLBooster for MongoDB 6.1.8 Node.js Nodist NordVPN NordVPN NordVPN network TAP NordVPN network TUN NoteHighlight2016 Notepad2-mod 4.2.25.998 Notion 2.0.8 Notion 2.0.9 Npcap 0.9982 NullpoMino version 7.5 Nullsoft Install System OBS Studio OHRRPGCE gorgonzola 20200502 ONLYOFFICE Desktop Editors 5.6 (x64) Octave 5.2.0 OneNote Tagging Kit Open Shop Channel Downloader version 1.2.9 Open-Shell OpenHashTab version 2.2.0 OpenHashTab version 2.3.0 OpenJDK 1.8.0_252-2-ojdkbuild OpenJDK 11.0.7-1-ojdkbuild OpenJDK 13.0.3-1-ojdkbuild OpenJDK 14.0.1-1-ojdkbuild OpenMPT 1.29 (64-Bit) OpenOffice 4.1.7 OpenRA OpenSCAD (remove only) OpenSSL (64-bit) OpenShot Video Editor version 2.5.1 OpenTTD 1.10.1 OpenTTD 1.10.3 OpenVPN 2.4.9-I601-Win10 OpenVPN Configuration Generator x64 OpenVPN Connect Opera GX Stable 68.0.3618.142 Opera GX Stable 68.0.3618.197 Opera Stable 68.0.3618.63 Opera Stable 69.0.3686.36 Opera Stable 69.0.3686.77 Opera Stable 70.0.3728.144 Opera Stable 70.0.3728.95 Opera Stable 71.0.3770.198 Oracle VM VirtualBox 6.1.10 Oracle VM VirtualBox 6.1.12 Oracle VM VirtualBox 6.1.14 Oracle VM VirtualBox 6.1.16 OutSystems Development Environment 11 PDF reDirect (remove only) PDFsam Basic PKU_Gateway 0.9.8 PSPad editor Packet Sender x64 Pale Moon 28.10.0 (x64 en-US) Pale Moon 28.9.3 (x64 en-US) Pandoc 2.10.1 Pandoc 2.11.0.2 Pandoc 2.9.2.1 Paradox Launcher Paragon Backup & Recoveryâ„¢ 17 CE Parsec PasteIntoFile version 2.0 PeaZip 7.2.1 (WIN64) PeaZip 7.3.1 (WIN64) Persepolis Download Manager version 3.2.0.0 PhonerLite 2.84 PhotoSync PicPick PicoTorrent Planet Blupi PlayStationâ„¢Now Playnite Plex Plex Media Player Plex Media Server Plexamp 3.0.3 Plexamp 3.1.0 Plexamp 3.1.1 PokerTH Postbox 7.0.18 (x86 en-US) PostgreSQL 12 PostgreSQL 13 PowerShell 7-preview-x64 PowerShell 7-x64 PowerShell Universal PowerToys (Preview) Primesieve version 7.5 Private Internet Access Progress Telerik Fiddler Project My Screen App ProtonVPN ProtonVPNTap PuTTY release 0.74 (64-bit) Puppet Puppet Agent (64-bit) Puppet Bolt Puppet Development Kit Pure Data (64-bit) 0.50-2 PyMODA version 1.1.0 Python 2.7.18 Python 3.7 PyAudio-0.2.11 Python 3.7.7 (64-bit) Python 3.8.1 (64-bit) Python 3.8.3 (64-bit) Python 3.8.4 (64-bit) Python 3.8.5 (64-bit) Python 3.8.6 (64-bit) Python 3.9.0 (64-bit) Python Launcher QGIS 3.10.6 'A Coru QGIS 3.12.3 'Bucuresti' QGIS 3.14.0 'Pi' QTextPad version 1.4 Qalculate! QtSpim Quick Picture Viewer QuickLook Quicken R for Windows 4.0.0 R for Windows 4.0.2 R for Windows 4.0.3 RStudio Rambox 0.7.5 Rapid Environment Editor version 9.2.0.937 Raspberry Pi Imager RawTherapee version 5.8 Reddit Wallpaper Changer Reko decompiler for x86-64 Remote Desktop Manager Remote Desktop Manager Free Remote Mouse version 3.015 RenderDoc Renode ResponsivelyApp 0.1.5 RetroShare Revo Uninstaller 2.1.7 Revo Uninstaller Pro 4.3.3 Robo 3T 1.3.1 Robo 3T 1.4.1 Robo 3T 1.4.2 Rocket.Chat 2.17.9 RocketDock 1.3.5 Rocks'n'Diamonds 4.1.4.1 Rosi Royal TS 5.02.60420.0 Royal TS 5.03.60925.0 Rtools 4.0 (4.0.0.28) Ruby 2.7.1-1-x64 Ruby 2.7.1-1-x64 with MSYS2 Ruby 2.7.2-1-x64 RunJS 1.10.1 RunJS 1.11.0 Rust 1.43 (MSVC 64-bit) Rust 1.44 (GNU 64-bit) Rust 1.44 (GNU) Rust 1.44 (MSVC 64-bit) Rust 1.44 (MSVC) Rust 1.45 (GNU 64-bit) Rust 1.45 (GNU) Rust 1.45 (MSVC 64-bit) Rust 1.45 (MSVC) SIW 2020 v10.6.0915a Trial SMPlayer 20.4.2 (x64) SSHFS-Win 2020 (x64) SVG Explorer Extension 0.1.1 Samsung DeX Satisfactory Mod Launcher 1.0.17 Scratch Desktop 3.11.1 ScreenToGif Scribus 1.4.8 (64bit) ScummVM 2.2.0 Search Deflector Sejda PDF Desktop Seq SharePoint Online Management Shell ShareX SharpKeys Shotcut Sigil 1.3.0 Signal 1.34.1 Signal 1.34.3 Signal 1.34.4 Signal 1.34.5 Signal 1.36.1 Signal 1.36.3 Signal 1.37.2 Simply Fortran 3 SitdownMW Skype version 8.60 Skype version 8.64 Skype version 8.65 Slack Machine-Wide Snagit 2020 SnakeTail 64-bit v1.9.6.0 Snoop SoapUI 5.5.0 Sonic Pi Sonos Sonos Controller SoundSwitch 5.0.4.31153 Sourcetree SpeedCrunch Spek Standard Notes 3.4.1 Steam Steel Bank Common Lisp 2.0.0 (X86-64) SteelSeries Engine 3.17.9 Stellarium 0.20.1 Stellarium 0.20.2 Stellarium 0.20.3 Strawberry Perl Streamlabs OBS Streamlabs OBS 0.23.2 Streamlabs OBS 0.24.0 Streamlink Streamlink Twitch GUI Stretchly 1.0.0 Stretchly 1.1.0 Stretchly 1.2.0 Stride Studio 2.0 version 2.0 Sublime Merge Sublime Text 3 SumatraPDF SuperCollider Version 3.11.0 SuperTuxKart 1.1.0 - 3D open-source arcade racer with a variety characters, tracks, and modes to play Surface Duo Emulator version 2020.806.2 SyncTrayzor (x64) version 1.1.24.0 System Explorer 7.0.0 TAP-Windows 9.24.2 Taiga Tailscale Tailscale IPN Taisei Project Taskade 3.1.1 Taskade 3.2.0 Td-agent v3.8.0 Td-agent v4.0.1 TeXstudio - TeXstudio is a fully featured LaTeX editor. TeXworks 0.6.5 TeamSpeak 3 Client TechPowerUp GPU-Z Telegram Desktop version 2.1.13 Telegram Desktop version 2.1.20 Telegram Desktop version 2.1.6 Telegram Desktop version 2.2 Telegram Desktop version 2.3 Telegram Desktop version 2.3.1 Telegram Desktop version 2.4.1 Telegram Desktop version 2.4.3 Telegram Desktop version 2.4.4 Tera Term 4.105 Terminus 1.0.117 Terminus 1.0.119 Terminus 1.0.120 Termite Tesseract-OCR - open source OCR engine Texmaker 5.0.4 (64-bit) Texnomic SecureDNS Terminal Textify v1.8.2 TickTick version 3.7.1.1 TightVNC TikzEdt 0.2.3 Tiled TmNationsForever Toggl Desktop Toggl Track TortoiseGit 2.10.0.2 (64 bit) TortoiseSVN 1.13.1.28686 (64 bit) TortoiseSVN 1.14.0.28885 (64 bit) TranslucentTB Transmission 3.00 (bb6b5a062e) (x64) Transmission Remote GUI 5.18 TrayStatus 4.3 TrayStatus 4.4 TreeSize Free V4.4.2 TreeSize V8.0.2 (64 bit) Trelby Trillian TunnelBear Tux Paint 0.9.23 Tweeten Twinkle Tray 1.12.2 Twitch TypeRefHasher USB Safely Remove 6.3 UXL Launcher Version 3.3.1.0 UXL Launcher Version 4.0.0.0 Ultimaker Cura Ultimaker Cura 4.5 Ultimaker Cura 4.6 UltraVnc Unchecky v1.2 Unified Remote Unity Hub 2.4.2 Update for (KB2504637) Update for Microsoft Visual Studio 2015 (KB3095681) Uplay Ut Video Codec Suite VCV Rack VLC media player 3.0.10 (64-bit) VLC media player 3.0.11 (64-bit) VLC media player 4.0.0 (64-bit) VMware Horizon Client VMware Player VMware Workstation VNC Server 6.0.0 VNC Viewer 6.0.0 VSCodium (User) VSCodium VcXsrv Vim 8.2 (x64) VirtViewer 9.0-256 (64-bit) Vivaldi Vortex Vrew 0.4.18 VulkanSDK 1.2.135.0 Warzone 2100-3.4.0 Waterfox Current 2020.05 (x64 en-US) Waterfox Current 2020.09 (x64 en-US) Waterfox Current 2020.10 (x64 en-US) Wayk Now WeakAuras Companion 3.0.1 WeakAuras Companion 3.0.2 WeakAuras Companion 3.0.3 WeakAuras Companion 3.0.6 Weka 3.8.4 Win32DiskImager version 1.0.0 WinCompose version 0.9.4 WinDynamicDesktop version 3.4.1.0 WinDynamicDesktop version 4.2.0.0 WinDynamicDesktop version 4.3.1.0 WinFsp 2020 WinHTTrack Website Copier 3.49-2 (x64) WinMerge 2.16.6.0 WinRAR 5.90 (64-bit) WinRAR 5.91 (64-bit) WinSCP 5.17.7 WinZip 24.0 Winamp Windows 10 Update Assistant Windows Admin Center Windows Assessment and Deployment Kit - Windows 10 Windows Assessment and Deployment Kit Windows Preinstallation Environment Add-ons - Windows 10 Windows Driver Kit - Windows 10.0.19041.1 Windows Driver Package - Dynastream Innovations, Inc. ANT LibUSB Drivers (04/11/2012 1.2.40.201) Windows Driver Package - Silicon Labs Software (DSI_SiUSBXp_3_1) USB (02/06/2007 3.1) Windows SDK AddOn Windows Software Development Kit - Windows 10.0.17763.132 Windows Software Development Kit - Windows 10.0.18362.1 Windows Software Development Kit - Windows 10.0.19041.1 WireGuard Wireshark 3.2.2 32-bit Wireshark 3.2.4 64-bit Wireshark 3.2.5 64-bit Wireshark 3.2.7 64-bit WizFile v2.06 WizKey v1.5.0.8 WizMouse v1.7.0.3 WizTree v3.33 WizTree v3.35 WordPress.com 5.2.0 WordPress.com 6.0.0 WordPress.com 6.0.1 WordPress.com 6.0.2 Workrave 1.10.44 Writage X2Go Client for Windows XAMPP XCA XMake build utility XMind 10.2.1 XnView 2.49.4 XnViewMP 0.97.1 Yarn Yinxiang Biji v. 6.20.16 Yinxiang Biji v. 6.21.10 Yinxiang Biji v. 6.21.3 Yinxiang Biji v. 6.21.4 Yinxiang Biji v. 6.21.9 YouTube Music Desktop App 1.11.0 YubiKey Manager Zentimo PRO 2.3 ZeroTier One Zettlr Zint Zoom Zoom Outlook Plugin Zotero Zulip Zulu 3.5.1 Zygor Client Uninstaller balenaEtcher 1.5.100 balenaEtcher 1.5.101 balenaEtcher 1.5.102 balenaEtcher 1.5.106 balenaEtcher 1.5.107 balenaEtcher 1.5.109 balenaEtcher 1.5.88 balenaEtcher 1.5.95 beatdrop 2.6.2 bottom butterflow-ui calibre 64bit copytranslator 9.1.0 darktable dnGREP 2.9.270 (x64) draw.io 13.0.3 dupeGuru 4.0.4 eM Client ebbflow f.lux ffftp ghostwriter version 1.7.1 gnuplot 5.2 patchlevel 8 grepWin x64 guinget version 0.1.0.1 guinget version 0.1.1 guinget version 0.1.2 hide.me VPN 3.4.0 hide.me VPN 3.4.1 i2pd iSEEK AnswerWorks English Runtime iTunes kdenlive kdiff3 mRemoteNG maxima-5.43.2 mpv.net version 5.4.8.0 ndm 1.2.0 (only current user) nexusfont 2.6 (ver 2.6.2.1870) ownCloud pCon.planner PRO pandoc-plot 0.7.1.0 pgAdmin 4 version 4.22 pgAdmin 4 version 4.23 pgAdmin 4 version 4.26 pgAdmin 4 version 4.27 qBittorrent 4.2.5 qBittorrent 4.3.0 qBittorrent 4.3.0.1 remoteit 2.5.32 remoteit 2.6.2 sbt 1.3.8 scilab-6.1.0 (64-bit) sqlectron 1.31.0 stretchly 0.21.1 ueli 8.7.0 ueli 8.8.1 ueli 8.9.0 xmoto 0.6.0 xmoto 0.6.1 微软设备健康助手 支付宝安全控件 5.3.0.3807 百度网盘 腾讯QQ ================================================ FILE: src/AppInstallerCLITests/TestData/InputPublishers.txt ================================================ Wildfire Games SweetScape Software 360安全中心 360安全中心 Open Media LLC Open Media LLC Open Media LLC Open Media LLC Open Media LLC Open Media LLC Igor Pavlov Igor Pavlov Igor Pavlov Igor Pavlov, Tino Reichardt Amazon Web Services Developer Relations Amazon Web Services AWS Serverless Applications Axis Communications AB Abacus Research AG Microsoft Microsoft Corporation Adobe Systems Incorporated Adobe Systems Incorporated Adobe Systems Incorporated AdoptOpenJDK AdoptOpenJDK AdoptOpenJDK AdoptOpenJDK AdoptOpenJDK AdoptOpenJDK AdoptOpenJDK Famatech Ondrej Salplachta Famatech Pawel Psztyc Aegisub Team Aegisub Team a wandersick Alacritty Alchemy Development Group Algoryx Stefan Sundin Amazon.com, Inc. Amazon Amazon Amazon Web Services, Inc Anaconda, Inc. Angry IP Scanner AntiMicro AppGet Appium Developers Appium Developers Arduino LLC Armagetron Advanced Team Sundaram Ramaswamy Rabid Viper Productions Audacity Team CodeUX.design e.U. Armin Osaj Lexikos Lexikos The Sleuth Kit GPL Public release. OmicronLab 7room Microsoft® Corporation Microsoft Corporation Microsoft Microsoft Microsoft Marcin Szeniak Contoso.com Beda Kosata Space Sciences Laboratory, U.C. Berkeley BPBible Development Team Google, Inc. Debauchee Open Source Group Debauchee Open Source Group Paul Frazee Elastic Elastic Marco Mastroddi Software beeftext.org The Betaflight open source project. Scooter Software, Inc. Scooter Software Scooter Software Bigly Software BitPay Bitwarden Inc. BleachBit Blender Foundation BlueJeans Network, Inc. Hamster Republic Productions Apple Inc. Andrew Sampson Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation brackets.io Brave Software Inc Brave Software Inc TGRMN Software Buttercup Buttercup C-Dogs SDL Team SingularLabs Piriform Canneverbe Limited Kitware Corsair CPUID, Inc. CPUID, Inc. CPUID, Inc. CPUID, Inc. CPUID, Inc. Penguin Labs, LLC SaeraSoft TechSmith Corporation TechSmith Corporation Sindre Sorhus Sindre Sorhus Giel Cobben Mathew Sachin RedDucks Alexandr Subbotin MIT IS&T secana Webprofusion Pty Ltd a wandersick Chef Software, Inc. ChemAxon ChemAxon ChemAxon ChemAxon The Chromium Authors Circuit Diagram Circuit Diagram Cisco Webex LLC Daniel Scalzi alch Fndroid Fndroid Fndroid Fndroid Beijing EEO Education Technology Co., Ltd. Clementine Martin Ridgers Cloudflare, Inc. The Code::Blocks Team Eran Ifrah Steven Cole TerranovaTeam Jay Prall Toinane ConEmu-Maximus5 Concept2 Inc. Microsoft Corporation Contasimple S.L. ALCPU Couchbase Inc. Couchbase Inc. Cozy Cloud The Cppcheck team Habib Rehman cryptomator.org Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World Crystal Dew World cubicsdr.com Acro Software Inc. iterate GmbH DB Browser for SQLite Team DBeaver Corp DBeaver Corp DBeaver Corp DBeaver Corp DBeaver Corp DBeaver Corp DBeaver Corp DBeaver Corp DBeaver Corp DJI DJI DJI DJI DJI DJI DJI DJI TshwaneDJe Boxstar Boxstar Deezer Ashley Stone Piriform Dell, Inc. Dell Inc. Bloodshed Software Thinking Man Software Serraniel Serraniel Scott Brogden Dixa DjVuZone DockStation Docker Inc. Dokany Project Dokany Project Dolphin Team dengine.net Digimezzo Doxie & Co. LLC Dropbox, Inc. EasternGraphics EasternGraphics EagleGet EagleGet Luke Stratman ES-Computing EduMIPS64 Development Team Elastic Elgato Systems GmbH Empoche.com Empoche.com MacPaw, Inc. a wandersick Sinew Software Systems Private Limited The Eraser Project The Eraser Project The Eraser Project The Eraser Project Esteem Ethereum Evernote Corp. Evernote Corp. David Carpenter David Carpenter David Carpenter David Carpenter voidtools ExpressVPN Ultrapico The ExtremeTuxRacer team Eugene Roshal & Far Group H.Shirouzu FastStone Soft FastStone Soft Fedora Project Amine Mouafik Progress Software Corporation Progress Software Corporation Progress Software Corporation Progress Software Corporation Progress Software Corporation Progress Software Corporation Progress Software Corporation Adrien Allard Binary Fortress Software Tim Kosse Tim Kosse Tim Kosse Tim Kosse Tim Kosse Mozilla Mozilla Mozilla Mozilla OpenSight Software LLC The FlightGear Team Dominik Levitsky Studio, LLC FontForgeBuilds Free Time Free Time Foxit Software Inc. Foxit Software Inc. Stefan Malzner Marek Jasinski - www.FreeCommander.com Humanity The GIMP Team The GIMP Team The GIMP Team The GIMP Team The GIMP Team The GIMP Team The GIMP Team The GIMP Team ARM Holdings The Free Software Foundation, Inc. The GnuPG Project GCN Development GOG.com Artifex Software Inc. Ghostgum Software Pty Ltd Garmin Ltd or its subsidiaries ThoughtWorks Inc. ThoughtWorks Inc. The Geany developer team Primate Labs Inc. Gephi Outertech Git Extensions Team Git Extensions Team Git Extensions Team GitHub, Inc. The Git Development Community The Git Development Community The Git Development Community The Git Development Community The Git Development Community The Git Development Community GitHub, Inc. GitHub, Inc. Stef Heyenrath Troupe Technology Limited Glimpse Project Glimpse Project Glimpse Project GnuCash Development Team GnuCash Development Team GnuWin32 GnuWin32 GnuWin32 GnuWin32 https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org https://golang.org GoldWave Inc. Google LLC Google Inc. Google The Gpg4win Project The Gpg4win Project Grafana Labs Grafana Labs Grammarly The Gramps project Graphcool Adam Miskiewicz AT&T Research Labs. Greenshot Grid Team Epiforge Software, LLC HHD Software, Ltd. Hector Maurcio Rodriguez Segura HP Inc. Huawei Software Technologies Co., Ltd. Martin Malik - REALiX Martin Malik - REALiX Martin Malik - REALiX Martin Malik - REALiX HandyOrg Vincent L Implbits Software Implbits Software JAM Software Hedgewars Project Ansgar Becker Ansgar Becker Ansgar Becker Ansgar Becker Ansgar Becker Perforce Software, Inc. IBE Software HexChat Scott Lerch Caphyon Borvid Borvid Borvid Borvid Borvid Borvid Borvid Huawei Corporation Hyne & Son Pty Ltd Marquis Kurt Google Inc Hex-Rays SA Tibbo Technology Inc David Moore IRCCloud Ltd. Ivan Zahariev Duong Dieu Phap Inkscape jrsoftware.org jrsoftware.org Crystal Rich, Ltd seonglae Irfan Skiljan IronPython Team IronPython Team ISWIX LLC Smart Projects ChemAxon JabRef Jackett Savoir-Faire Linux Oracle Corporation Oracle Corporation JetBrains Jitsi Team Jitsi Team Laurent Cozic Laurent Cozic Laurent Cozic Julia Language Julia Language Julia Language KLCP KLCP KKBOX Taiwan Co., Ltd. Chia-Lung, Chen Dominik Reichl Dominik Reichl Dominik Reichl KeePassXC Team KeeWeb Keybase, Inc. KiCad KiCad KiCad Krisp Technologies, Inc Krita Foundation Roni Lehto LBRY Inc. LBRY Inc. LBRY Inc. LBRY Inc. LBRY Inc. LINE Corporation Joseph Albahari LLVM LMMS Developers LMMS Developers love2d.org leokhoa LastPass Lazarus Team Riot Games, Inc Lenovo Lenovo Lakend Labs, Inc. Leonflix BellSoft BellSoft BellSoft BellSoft BellSoft BellSoft BellSoft BellSoft LibreCAD Team The Document Foundation The Document Foundation Team Lidarr Alexey 'Tyrrrz' Golub Alexey 'Tyrrrz' Golub Christian Kaiser Leif Asbrink SM5BSZ Lisk Foundation Listen 1 Listen 1 Flywheel Crystal Rich Ltd Binary Fortress Software Binary Fortress Software Logitech Inc. Loom, Inc. LyX Team LyX Team COTILab COTILab Moritz Bunkus Moritz Bunkus Moritz Bunkus Moritz Bunkus Moritz Bunkus MPC-HC Team MPC-HC Team MPC-HC Team Thomas Nordquist Microsoft Maxthon International Limited MY.COM B.V. FlyingSnow, Samantha Glocker Firetrust Firetrust Majsoul Plus Team GuinpinSoft inc Malwarebytes KDE MariaDB Corporation Ab Jocs West Wind Technologies West Wind Technologies West Wind Technologies West Wind Technologies West Wind Technologies Markdown Outlook Master Packager Ltd. Mattermost, Inc. MediaArea.net MediaArea.net Ventis Media Inc. The Meld project Janea Systems Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Garage Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Microsoft Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Anaconda, Inc. Anaconda, Inc. MongoDB Inc. Xamarin, Inc. The MonoGame Team Moonlight Game Streaming Project AGALWOOD AGALWOOD Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Mozilla Nicholas H.Tollervey Mullvad VPN Mullvad VPN Microsoft Corporation canonical The Mumble Developers The Mumble Developers The Mumble Developers Werner Schweer and Others Youta Tec Logitech Martin Renold and the MyPaint Development Team Oracle Corporation Feodor2 Justin Aquadro Rico Suter NV Access NVIDIA Corporation NVIDIA Corporation NZXT, Inc. NZXT, Inc. NZXT, Inc. NZXT, Inc. NZXT, Inc. NZXT, Inc. Allan CORNET Neotys Lutz Roeder Nitro Nmap Project qinghai qinghai qinghai Node.js Foundation Nodist NordVPN TEFINCOM S.A. NordVPN NordVPN CodingRoad XhmikosR Notion Labs, Incorporated Notion Labs, Incorporated Nmap Project NullNoname Nullsoft and Contributors OBS Project Hamster Republic Productions Ascensio System SIA GNU Octave WetHat Lab Open Shop Channel The Open-Shell Team namazso namazso ojdkbuild open-source project ojdkbuild open-source project ojdkbuild open-source project ojdkbuild open-source project OpenMPT Devs Apache Software Foundation OpenRA developers The OpenSCAD Developers Shining Light Productions OpenShot Studios, LLC OpenTTD OpenTTD OpenVPN Technologies, Inc. SparkLabs Pty Ltd OpenVPN Technologies Opera Software Opera Software Opera Software Opera Software Opera Software Opera Software Opera Software Opera Software Oracle Corporation Oracle Corporation Oracle Corporation Oracle Corporation OutSystems EXP Systems LLC Sober Lemur S.a.s. di Vacondio Andrea CCPKU Jan Fiala NagleCode, LLC Moonchild Productions Moonchild Productions John MacFarlane John MacFarlane John MacFarlane Paradox Interactive Paragon Software GmbH Parsec Cloud Inc. Francesco Sorge Giorgio Tani Giorgio Tani Persepolis Team Heiko Sommerfeldt touchbyte GmbH NGWIN PicoTorrent contributors. blupi.org Sony Interactive Entertainment Network America LLC Josef Nemec Plex, Inc. Plex Plex, Inc. Plex, Inc. Plex, Inc. Plex, Inc. www.pokerth.net Postbox, Inc. PostgreSQL Global Development Group PostgreSQL Global Development Group Microsoft Corporation Microsoft Corporation Ironman Software, LLC Microsoft Kim Walisch Private Internet Access, Inc. Progress Software Corporation Microsoft Corporation Proton Technologies AG Proton Technologies AG Simon Tatham Puppet Labs Puppet Inc Puppet, Inc. Puppet Inc Miller Puckette Lancaster University Physics Python Software Foundation Hubert Pham Python Software Foundation Python Software Foundation Python Software Foundation Python Software Foundation Python Software Foundation Python Software Foundation Python Software Foundation Python Software Foundation QGIS Development Team QGIS Development Team QGIS Development Team Michael Hansen Hanna Knutsson LarusStone Module Art Paddy Xu Quicken R Core Team R Core Team R Core Team RStudio Rambox Oleg Danilov Raspberry Pi rawtherapee.com Paul Rawnsley jklSoft Devolutions inc. Devolutions inc. Remote Mouse Baldur Karlsson Antmicro Responsively RetroShare Team VS Revo Group, Ltd. VS Revo Group, Ltd. 3T Software Labs Ltd 3T Software Labs Ltd 3T Software Labs Ltd Rocket.Chat Support Punk Software Artsoft Entertainment MarkoBL code4ward GmbH Royal Apps GmbH The R Foundation RubyInstaller Team RubyInstaller Team RubyInstaller Team Luke Haas Luke Haas The Rust Project Developers The Rust Project Developers The Rust Project Developers The Rust Project Developers The Rust Project Developers The Rust Project Developers The Rust Project Developers The Rust Project Developers The Rust Project Developers Topala Software Solutions Ricardo Villalba Navimatics LLC Dotz Softwares Samsung Electronics Co., Ltd. mircearoata Scratch Foundation Nicke Manarin The Scribus Team The ScummVM Team spikespaz Sejda BV Datalust Pty Ltd Microsoft Corporation ShareX Team RandyRants.com Meltytech, LLC Sigil-Ebook Open Whisper Systems Open Whisper Systems Open Whisper Systems Open Whisper Systems Open Whisper Systems Open Whisper Systems Open Whisper Systems Approximatrix, LLC Ashley Stone Skype Technologies S.A. Skype Technologies S.A. Skype Technologies S.A. Slack Technologies TechSmith Corporation SnakeNest.com Cory Plotts SmartBear Software Sonic Pi Sonos, Inc. Sonos, Inc. Antoine Aflalo Atlassian SpeedCrunch Spek Project Standard Notes Valve Corporation http://www.sbcl.org SteelSeries ApS Stellarium team Stellarium team Stellarium team strawberryperl.com project General Workings, Inc. General Workings, Inc. General Workings, Inc. Streamlink Sebastian Meyer Jan Hovancik Jan Hovancik Jan Hovancik Stride BrickLink Corporation Sublime HQ Pty Ltd Sublime HQ Pty Ltd Krzysztof Kowalczyk SuperCollider Community SuperTuxKart Microsoft SyncTrayzor Mister Group OpenVPN Technologies, Inc. erengy Tailscale Inc. Tailscale Inc. Taisei Project Taskcade Inc. Taskcade Inc. "Treasure Data, Inc" Treasure Data, Inc Benito van der Zander TeX Users Group TeamSpeak Systems GmbH TechPowerUp Telegram FZ-LLC Telegram FZ-LLC Telegram FZ-LLC Telegram FZ-LLC Telegram FZ-LLC Telegram FZ-LLC Telegram FZ-LLC Telegram FZ-LLC Telegram FZ-LLC TeraTerm Project Eugene Pankov Eugene Pankov Eugene Pankov CompuPhase Tesseract-OCR community Texmaker Texnomic RaMMicHaeL Appest.com GlavSoft LLC. TikzEdt mapeditor.org Nadeo Toggl Toggl TortoiseGit TortoiseSVN TortoiseSVN TranslucentTB Open Source Developers Transmission Project Yury Sidorov & Transmission Remote GUI working group Binary Fortress Software Binary Fortress Software JAM Software JAM Software Trelby.org Cerulean Studios, LLC TunnelBear New Breed Software Inspect Element Inc. Xander Frangos Twitch Interactive, Inc. G DATA CyberDefense AG SafelyRemove.com Drew Naylor Drew Naylor Ultimaker B.V. Ultimaker B.V. Ultimaker B.V. uvnc bvba Reason Software Company Inc. Unified Intents AB Unity Technologies Inc. Microsoft Corporation Microsoft Corporation Ubisoft UMEZAWA Takeshi VCV VideoLAN VideoLAN VideoLAN VMware, Inc. VMware, Inc. VMware, Inc. RealVNC Ltd RealVNC Ltd Microsoft Corporation Microsoft Corporation marha@users.sourceforge.net Bram Moolenaar et al. Virt Manager Project Vivaldi Technologies AS. Black Tree Gaming Ltd. VoyagerX, Inc. LunarG, Inc. Warzone 2100 Project Waterfox Waterfox Waterfox Devolutions Inc. Buds Buds Buds Buds Machine Learning Group, University of Waikato, Hamilton, NZ ImageWriter Developers Sam Hocevar Timothy Johnson Timothy Johnson Timothy Johnson Navimatics LLC HTTrack Thingamahoochie Software win.rar GmbH win.rar GmbH Martin Prikryl Corel Corporation Winamp SA Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation Dynastream Innovations, Inc. Silicon Labs Software Microsoft Corporation Microsoft Corporation Microsoft Corporation Microsoft Corporation WireGuard LLC The Wireshark developer community, https://www.wireshark.org The Wireshark developer community, https://www.wireshark.org The Wireshark developer community, https://www.wireshark.org The Wireshark developer community, https://www.wireshark.org Antibody Software Antibody Software Antibody Software Antibody Software Antibody Software Automattic Inc. Automattic Inc. Automattic Inc. Automattic Inc. Rob Caelers & Raymond Penners Writage X2Go Project Bitnami Christian Hohnstaedt The TBOOX Open Source Group XMind Ltd. Gougelet Pierre-e Gougelet Pierre-e Yarn Contributors Beijing Yinxiang Biji Technologies Co., Ltd. Beijing Yinxiang Biji Technologies Co., Ltd. Beijing Yinxiang Biji Technologies Co., Ltd. Beijing Yinxiang Biji Technologies Co., Ltd. Beijing Yinxiang Biji Technologies Co., Ltd. Adler Luiz Yubico AB Zentimo.com ZeroTier, Inc. Hendrik Erz Robin Stuart & BogDan Vatra Zoom Zoom Corporation for Digital Scholarship Kandra Labs, Inc. Sangoma Technologies Corp. Zygor Guides Balena Inc. Balena Inc. Balena Inc. Balena Inc. Balena Inc. Balena Inc. Balena Inc. Balena Inc. Nathaniel Johns Clement Tsang butterflow-ui @ github Kovid Goyal Elliott Zheng the darktable project dnGrep Community Contributors JGraph Hardcoded Software eM Client Inc. Ebbflow.io f.lux Software LLC Kurata Sayuri wereturtle gnuplot development team Stefans Tools Drew Naylor Drew Naylor Drew Naylor eVenture Limited eVenture Limited PurpleI2P Vantage Linguistics Apple Inc. KDE e.V. KDE e.V. Next Generation Software Maxima Team Frank Skare (stax76) 720kb xiles ownCloud GmbH EasternGraphics Laurent P. Ren© de Cotret The pgAdmin Development Team The pgAdmin Development Team The pgAdmin Development Team The pgAdmin Development Team The qBittorrent project The qBittorrent project The qBittorrent project remote.it remote.it Lightbend, Inc. Scilab Enterprises The Sqlectron Team Jan Hovancik Oliver Schwendener Oliver Schwendener Oliver Schwendener Humanity Humanity Microsoft Corporation Alipay.com Co., Ltd. 百度在线网络技术(北京)有限公司 腾讯科技(深圳)有限公司 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_AbortsTerminal.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: AppInstallerCliTest.TestInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer ShortDescription: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test InstallerAbortsTerminal: true Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_EncodedUrl.yaml ================================================ Id: AppInstallerCliTest.UrlEncodeTest Version: 1.0.0.0 Name: AppInstaller Test Exe Installer Publisher: Microsoft Corporation AppMoniker: AICLITestExe License: Test ProductCode: AppInstallerCliTest.TestExeInstaller Installers: - Arch: x64 Url: https://EncodedUrlTest/%E6%B5%8B%E8%AF%95.exe InstallerType: exe Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Switches: Custom: /encodedUrl SilentWithProgress: /silentwithprogress Silent: /silence Update: /update ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Exe.yaml ================================================ Id: AppInstallerCliTest.TestExeInstaller Version: 1.0.0.0 Name: AppInstaller Test Exe Installer Publisher: Microsoft Corporation AppMoniker: AICLITestExe License: Test ProductCode: AppInstallerCliTest.TestExeInstaller Installers: - Arch: x64 Url: https://ThisIsNotUsed InstallerType: exe Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Scope: user Switches: Custom: /custom /scope=user SilentWithProgress: /silentwithprogress Silent: /silence Update: /update - Arch: x64 Url: https://ThisIsNotUsed InstallerType: exe Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Scope: machine Switches: Custom: /custom /scope=machine SilentWithProgress: /silentwithprogress Silent: /silence Update: /update ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_ExpectedReturnCodes.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerCliTest.ExpectedReturnCodes PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: TestExeInstallerWithExpectedReturnCodes ShortDescription: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ExpectedReturnCodes: - InstallerReturnCode: 1 ReturnResponse: packageInUse - InstallerReturnCode: 2 ReturnResponse: installInProgress - InstallerReturnCode: 3 ReturnResponse: fileInUse - InstallerReturnCode: 4 ReturnResponse: missingDependency - InstallerReturnCode: 5 ReturnResponse: diskFull - InstallerReturnCode: 6 ReturnResponse: insufficientMemory - InstallerReturnCode: 7 ReturnResponse: noNetwork - InstallerReturnCode: 8 ReturnResponse: contactSupport ReturnResponseUrl: https://TestReturnResponseUrl - InstallerReturnCode: 9 ReturnResponse: rebootRequiredToFinish - InstallerReturnCode: 10 ReturnResponse: rebootRequiredForInstall - InstallerReturnCode: 11 ReturnResponse: rebootInitiated - InstallerReturnCode: 12 ReturnResponse: cancelledByUser - InstallerReturnCode: 13 ReturnResponse: alreadyInstalled - InstallerReturnCode: 14 ReturnResponse: downgrade - InstallerReturnCode: 15 ReturnResponse: blockedByPolicy ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_InstallLocationRequired.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: AppInstallerCliTest.TestInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer ShortDescription: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test InstallLocationRequired: true InstallerSwitches: InstallLocation: /InstallDir Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_InstallationNotes.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerCliTest.TestInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer ShortDescription: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test InstallationNotes: testInstallationNotes Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_InvalidFileCharacterUrl.yaml ================================================ Id: AppInstallerCliTest.InvalidFileCharacterUrlTest Version: 1.0.0.0 Name: AppInstaller Test Exe Installer Publisher: Microsoft Corporation AppMoniker: AICLITestExe License: Test ProductCode: AppInstallerCliTest.TestExeInstaller Installers: - Arch: x64 Url: https://EncodedUrlTest/te*st.exe InstallerType: exe Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Switches: Custom: /invalidFileCharacterUrl SilentWithProgress: /silentwithprogress Silent: /silence Update: /update ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_LicenseAgreement.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: TestInstaller.WithLicenseAgreement PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: TestInstallerWithLicenseAgreement Agreements: - AgreementLabel: Agreement with text Agreement: This is the text of the agreement. - AgreementLabel: Agreement with URL AgreementUrl: https://TestAgreementUrl InstallerSwitches: SilentWithProgress: /silentwithprogress Silent: /silence Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_MSStore.yaml ================================================ Id: AppInstallerCliTest.TestMSStoreInstaller Version: Latest Name: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITestMSStore License: Test Installers: - Arch: neutral Url: https://ThisIsNotUsed InstallerType: msstore ProductId: 9WZDNCRFJ364 PackageFamilyName: Microsoft.SkypeApp_kzf8qxf38zg5c ManifestVersion: 0.2.0-msstore ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml ================================================ Id: AppInstallerCliTest.TestMsixInstaller Version: 1.0.0.0 Name: AppInstaller Test MSIX Installer Publisher: Microsoft Corporation AppMoniker: AICLITestMsix License: Test Installers: - Arch: x64 Url: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml ================================================ Id: AppInstallerCliTest.TestMsixInstaller Version: 1.0.0.0 Name: AppInstaller Test MSIX Installer Publisher: Microsoft Corporation AppMoniker: AICLITestMsix License: Test Installers: - Arch: x64 Url: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea SignatureSha256: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07 PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_MultipleDependencies.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.3.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeInstaller.MultipleDependencies PackageVersion: 1.0.0.0 PackageName: AppInstaller Test Installer PackageLocale: en-US Publisher: Microsoft Corporation ShortDescription: Installs exe installer with multiple dependencies Moniker: AICLITestExe License: Test InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Upgrade: /upgrade Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Dependencies: PackageDependencies: - PackageIdentifier: Dependency1 - PackageIdentifier: Dependency2 ManifestType: singleton ManifestVersion: 1.3.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_NoApplicableArchitecture.yaml ================================================ Id: AppInstallerCliTest.TestInstaller Version: 1.0.0.0 Name: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITestExe License: Test Switches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Installers: - Arch: unknown Url: https://ThisIsNotUsed InstallerType: exe Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_NonZeroExitCode.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: AppInstallerCliTest.TestInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: AppInstaller Test Installer InstallerSwitches: Custom: /ExitCode 0x80070005 SilentWithProgress: /silentwithprogress Silent: /silence Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerSuccessCodes: - -2147024891 ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Portable.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerCliTest.TestPortableInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Portable Exe Publisher: Microsoft Corporation AppMoniker: AICLITestPortable License: Test ProductCode: AppInstallerCliTest.TestPortableInstaller__TestSource ShortDescription: AppInstaller Test Portable Exe Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Portable_WithCommand.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerCliTest.TestPortableInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Portable Exe Publisher: Microsoft Corporation AppMoniker: AICLITestPortable License: Test ShortDescription: AppInstaller Test Portable Exe Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Commands: - portableCommand ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_UnknownVersion.yaml ================================================ # Test manifest for upgrading a package with an unknown version. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.3.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeUnknownVersion PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Exe Unknown Version Publisher: Microsoft Corporation AppMoniker: AICLITestExe License: Test Switches: SilentWithProgress: /silentwithprogress Silent: /silence Update: /update Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.3.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_UnsupportedArguments.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerCliTest.TestInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer ShortDescription: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test UnsupportedArguments: - log - location Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_WindowsFeatures.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerCliTest.WindowsFeatures PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer ShortDescription: Installs exe installer with valid Windows Features dependencies Publisher: Microsoft Corporation License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerSwitches: Custom: /custom /scope=machine SilentWithProgress: /silentwithprogress Silent: /silence Update: /update Dependencies: WindowsFeatures: - testFeature1 - testFeature2 ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Zip_Exe.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerCliTest.TestZipInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Zip Installer ShortDescription: AppInstaller Test Zip Installer with exe Publisher: Microsoft Corporation Moniker: AICLITestZip License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: exe NestedInstallerFiles: - RelativeFilePath: relativeFilePath InstallerSwitches: Custom: /custom /scope=machine SilentWithProgress: /silentwithprogress Silent: /silence Update: /update ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Zip_MissingNestedInstaller.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerCliTest.TestZipInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Zip Installer ShortDescription: AppInstaller Test Zip Installer with exe Publisher: Microsoft Corporation Moniker: AICLITestZip License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: exe InstallerSwitches: Custom: /custom /scope=machine SilentWithProgress: /silentwithprogress Silent: /silence Update: /update ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Zip_MultipleNonPortableNestedInstallers.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerCliTest.TestZipInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Zip Installer ShortDescription: AppInstaller Test Zip Installer with exe Publisher: Microsoft Corporation Moniker: AICLITestZip License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: exe NestedInstallerFiles: - RelativeFilePath: installerOne.exe - RelativeFilePath: installerTwo.exe ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallFlowTest_Zip_UnsupportedNestedInstaller.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerCliTest.TestZipInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Zip Installer ShortDescription: AppInstaller Test Zip Installer with exe Publisher: Microsoft Corporation Moniker: AICLITestZip License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: msstore InstallerSwitches: Custom: /custom /scope=machine SilentWithProgress: /silentwithprogress Silent: /silence Update: /update ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_NoSwitches.yaml ================================================ Id: AppInstallerCliTest.TestInstaller Version: 1.0.0.0 Name: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITest License: Test Installers: - Arch: x64 Url: https://ThisIsNotUsed InstallerType: inno Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_WithSwitches.yaml ================================================ Id: AppInstallerCliTest.TestInstaller Version: 1.0.0.0 Name: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITest License: Test Installers: - Arch: x64 Url: https://ThisIsNotUsed InstallerType: inno Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Switches: Custom: /mycustom SilentWithProgress: /mysilentwithprogress Silent: /mysilent Log: /mylog="" InstallLocation: /myinstalldir="" ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml ================================================ Id: AppInstallerCliTest.TestInstaller Version: 1.0.0.0 Name: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITest License: Test Installers: - Arch: x64 Url: https://ThisIsNotUsed InstallerType: msi Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml ================================================ Id: AppInstallerCliTest.TestInstaller Version: 1.0.0.0 Name: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITest License: Test Installers: - Arch: x64 Url: https://ThisIsNotUsed InstallerType: msi Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' Switches: Custom: /mycustom SilentWithProgress: /mysilentwithprogress Silent: /mysilent Log: /mylog="" InstallLocation: /myinstalldir="" ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Installer_Exe_Dependencies.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies PackageVersion: '1.0' PackageLocale: en-US PackageName: AppInstaller Test Exe Installer With Package Dep Publisher: Microsoft Corporation Moniker: AICLITestExePackageDep License: Test ShortDescription: AppInstaller Test Exe Installer With Package Dep Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerSwitches: SilentWithProgress: /silentwithprogress Silent: /silence Dependencies: WindowsFeatures: - PreviewIIS ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesMultideclaration.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Exe Installer With Package Dep Publisher: Microsoft Corporation Moniker: AICLITestExePackageDep License: Test ShortDescription: AppInstaller Test Exe Installer With Package Dep Dependencies: WindowsFeatures: - PreviewIISOnRoot Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerSwitches: SilentWithProgress: /silentwithprogress Silent: /silence Dependencies: WindowsFeatures: - PreviewIIS ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Installer_Exe_DependenciesOnRoot.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Exe Installer With Package Dep Publisher: Microsoft Corporation Moniker: AICLITestExePackageDep License: Test ShortDescription: AppInstaller Test Exe Installer With Package Dep Dependencies: WindowsFeatures: - PreviewIISOnRoot Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerSwitches: SilentWithProgress: /silentwithprogress Silent: /silence ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Installer_Msix_WFDependency.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: AppInstallerCliTest.TestMsixInstaller.WFDep PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test MSIX Installer With Windows Feature Dep Publisher: Microsoft Corporation Moniker: AICLITestMsixWindowsFeatureDep License: Test ShortDescription: AppInstaller Test MSIX Installer With Windows Feature Dep Installers: - Architecture: x64 InstallerUrl: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix InstallerSha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe Dependencies: WindowsFeatures: - Hyper-V ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-ApproximateVersionInArpVersion.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft Author: Microsoft PackageName: MSIX SDK License: MIT License ShortDescription: This is MSIX SDK Installers: - Architecture: x86 InstallerType: inno InstallerUrl: https://www.microsoft.com/msixsdk/setup.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 AppsAndFeaturesEntries: - DisplayVersion: "< 12.0" ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-ApproximateVersionInPackageVersion.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: "> 1.7.32" PackageLocale: en-US Publisher: Microsoft Author: Microsoft PackageName: MSIX SDK License: MIT License ShortDescription: This is MSIX SDK Installers: - Architecture: x86 InstallerType: inno InstallerUrl: https://www.microsoft.com/msixsdk/setup.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-AppsAndFeaturesEntriesOnMSIX.yaml ================================================ # Bad manifest. Invalid Arch value # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageName: MSIX SDK PackageVersion: 1.7.32 Publisher: Microsoft License: Test ShortDescription: desc PackageLocale: en-US Installers: - Architecture: x86 InstallerType: msix InstallerUrl: https://ThisIsNotUsed InstallerSha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-ArchInvalid.yaml ================================================ # Bad manifest. Invalid Arch value Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: NotAnArch Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-ArchMissing.yaml ================================================ # Bad manifest. No Name Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-Channel-NotSupported.yaml ================================================ Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft Channel: release InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-UPPER.yaml ================================================ # Bad manifest. InstallerType is UPPER Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft INSTALLERTYPE: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-camelCase.yaml ================================================ # Bad manifest. InstallerType is camelCased Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft installerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-lower.yaml ================================================ # Bad manifest. InstallerType is lower case Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft installertype: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml ================================================ # Bad manifest. Duplicated key with different casing convention. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi installertype: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase.yaml ================================================ # Bad manifest. Duplicated key with different casing convention. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi installerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey.yaml ================================================ # Bad manifest. Duplicated key Id Id: microsoft.msixsdk Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml ================================================ # Bad manifest. Expected return codes repeat the same return code # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: AppInstallerCliTest.TestInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer ShortDescription: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ExpectedReturnCodes: - InstallerReturnCode: 1 ReturnResponse: packageInUse - InstallerReturnCode: 1 ReturnResponse: installInProgress ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml ================================================ # Bad manifest. Expected return codes repeat the same return code # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: AppInstallerCliTest.TestInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer ShortDescription: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerSuccessCodes: - 1 ExpectedReturnCodes: - InstallerReturnCode: 1 ReturnResponse: packageInUse ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateSha256.yaml ================================================ # Bad manifest. Different URLs point to two the same InstallerSha256 Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-US - Arch: x86 Url: https://ThisIsNotUsed2 Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: es-MX ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-IdInvalid.yaml ================================================ # Bad manifest. Invalid Id Id: m i c rosoft.msixsdk Name: MSIX SDK Version: 1.7.32 InstallerType: Msi Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-IdMissing.yaml ================================================ # Bad manifest. No Id Name: MSIX SDK Version: 1.7.32 InstallerType: Msi Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InconsistentMsixBundleInstallerFields.yaml ================================================ # Bad manifest: Inconsistent field values in unsigned MSIX bundle PackageIdentifier: AppInstallerCliTest.InconsistentMsixBundleInstallerFields PackageVersion: 1.2.3.4 # Mismatching value with msix installer PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_Bad # Mismatching value with msix installer MinimumOSVersion: 5.6.7.8 # Mismatching value with msix installer InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Good.msixbundle SignatureSha256: 0000000000000000000000000000000000000000000000000000000000000000 # Unexpected value for unsigned msix installer ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InconsistentMsixInstallerFields.yaml ================================================ # Bad manifest: Inconsistent field values in unsigned MSIX PackageIdentifier: AppInstallerCliTest.InconsistentMsixInstallerFields PackageVersion: 1.2.3.4 # Mismatching value with msix installer PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_Bad # Mismatching value with msix installer MinimumOSVersion: 5.6.7.8 # Mismatching value with msix installer InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Good.msix SignatureSha256: 0000000000000000000000000000000000000000000000000000000000000000 # Unexpected value for unsigned msix installer ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InconsistentSha256.yaml ================================================ # Bad manifest. Same URL points to two different InstallerSha256 # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.TestPortableInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Portable Exe Publisher: Microsoft Corporation AppMoniker: AICLITestPortable License: Test ProductCode: AppInstallerCliTest.TestPortableInstaller__TestSource ShortDescription: Url points to two different InstallerSha256 Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: portable InstallerSha256: A111111111111111111111111111111111111111111111111111111111111111 ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InconsistentSignedMsixBundleInstallerFields.yaml ================================================ # Bad manifest: Inconsistent field values in signed MSIX bundle PackageIdentifier: AppInstallerCliTest.InconsistentMsixBundleInstallerFields PackageVersion: 1.2.3.4 # Mismatching value with msix installer PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_Bad # Mismatching value with msix installer MinimumOSVersion: 5.6.7.8 # Mismatching value with msix installer InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Signed-Good.msixbundle SignatureSha256: 0000000000000000000000000000000000000000000000000000000000000000 # Inconsistent signature hash ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InconsistentSignedMsixInstallerFields.yaml ================================================ # Bad manifest: Inconsistent field values in signed MSIX PackageIdentifier: AppInstallerCliTest.InconsistentMsixInstallerFields PackageVersion: 1.2.3.4 # Mismatching value with msix installer PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_Bad # Mismatching value with msix installer MinimumOSVersion: 5.6.7.8 # Mismatching value with msix installer InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Signed-Good.msix SignatureSha256: 0000000000000000000000000000000000000000000000000000000000000000 # Inconsistent signature hash ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilent.yaml ================================================ # Bad manifest. Installer Type exe requires Silent switch. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: Exe Switches: Interactive: /i ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml ================================================ # Bad manifest. Installer Type exe requires Silent switch. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft License: Test Switches: Interactive: /i Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: Exe ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml ================================================ # Bad manifest. Installer Type exe requires Silent switch. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Exe License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Switches: Interactive: /i ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml ================================================ # Bad manifest. Installer Type exe requires Silent switch. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Exe License: Test Switches: Interactive: /i Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeInvalid.yaml ================================================ # Bad Manifest. Invalid value for InstallerType. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: NotAType License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeMissing.yaml ================================================ # Bad manifest. No InstallerType Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml ================================================ # Bad manifest. Installer Type portable can only have zero or one AppsAndFeatureEntry defined. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: TestInstaller.WithLicenseAgreement PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: TestInstallerWithLicenseAgreement AppsAndFeaturesEntries: - DisplayName: DisplayName1 DisplayVersion: DisplayVersion1 Publisher: Publisher1 ProductCode: ProductCode1 UpgradeCode: UpgradeCode1 - DisplayName: DisplayName2 DisplayVersion: DisplayVersion2 Publisher: Publisher2 ProductCode: ProductCode2 UpgradeCode: UpgradeCode2 Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B - Architecture: x86 InstallerUrl: https://ThisIsNotUsed2 InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B AppsAndFeaturesEntries: - DisplayName: DisplayName1 DisplayVersion: DisplayVersion1 Publisher: Publisher1 ProductCode: ProductCode1 UpgradeCode: UpgradeCode1 - DisplayName: DisplayName2 DisplayVersion: DisplayVersion2 Publisher: Publisher2 ProductCode: ProductCode2 UpgradeCode: UpgradeCode2 ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml ================================================ # Bad manifest. Installer Type portable can only have one or zero commands defined. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: TestInstaller.WithLicenseAgreement PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: TestInstallerWithLicenseAgreement Commands: - Command1 - Command2 Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B - Architecture: x86 InstallerUrl: https://ThisIsNotUsed2 InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Commands: - Command1 - Command2 ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml ================================================ # Bad manifest. Installer Type portable does not support scope and should show a warning. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: TestInstaller.WithLicenseAgreement PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: TestInstallerWithLicenseAgreement Scope: user Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Scope: Machine - Architecture: x86 InstallerUrl: https://ThisIsNotUsed2 InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-DuplicateCommandAlias.yaml ================================================ # Bad manifest. Installer type zip should not have any duplicate PortableCommandAlias values. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without nestedInstallers specified Scope: user Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: DUPLICATEALIAS - RelativeFilePath: relativeFilePath2 PortableCommandAlias: duplicateAlias ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-DuplicateRelativeFilePath.yaml ================================================ # Bad manifest. Installer type zip should not have any duplicate PortableCommandAlias values. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without nestedInstallers specified Scope: user Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerFiles: - RelativeFilePath: RELATIVEFILEPATH PortableCommandAlias: alias1 - RelativeFilePath: relativefilepath PortableCommandAlias: alias2 ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-InvalidRelativeFilePath.yaml ================================================ # Bad manifest. A nested installer file must have a RelativeFilePath specified. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without RelativeFilePath specified Scope: user Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: exe NestedInstallerFiles: - RelativeFilePath: ../../relativeFilePath ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-MissingRelativeFilePath.yaml ================================================ # Bad manifest. A nested installer file must have a RelativeFilePath specified. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without RelativeFilePath specified Scope: user Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: exe NestedInstallerFiles: - PortableCommandAlias: portableCommandAlias ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-MultipleNestedInstallers.yaml ================================================ # Bad manifest. Installer type zip should have exactly one NestedInstaller specified. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip with too many nestedInstallers specified. Scope: user Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableCommandAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableCommandAlias2 ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-NoNestedInstallerFile.yaml ================================================ # Bad manifest. Installer type zip should have exactly one NestedInstaller specified. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without nestedInstallers specified Scope: user Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: exe ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-NoNestedInstallerType.yaml ================================================ # Bad manifest. Installer type zip should have exactly one NestedInstaller specified. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without nestedInstallers specified Scope: user Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerFiles: - RelativeFilePath: relativeFilePath PortableCommandAlias: portableCommandAlias ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-PortableNotExe.yaml ================================================ # Bad manifest. Installer type zip with NestedInstallerType portable should only allow .exe files # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.9.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without nestedInstallers specified Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip NestedInstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerFiles: - RelativeFilePath: ScriptedApplication.bat ManifestType: singleton ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeZip-PortableNotExe_Root.yaml ================================================ # Bad manifest. Installer type zip with NestedInstallerType portable should only allow .exe files # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.9.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without nestedInstallers specified InstallerType: zip NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: ScriptedApplication.bat Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultScope.yaml ================================================ # Bad Manifest. Uniqueness installers same arch, same scope. Default scope is user Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Scope: user ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultValues.yaml ================================================ # Bad Manifest. Uniqueness installers same arch, same default values Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-SameLang.yaml ================================================ # Bad Manifest. Uniqueness installers same arch, language and scope Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-US - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-US ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness.yaml ================================================ # Bad Manifest. Uniqueness installers same arch, language and scope Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-US Scope: user - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-US Scope: user ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InstallersMissing.yaml ================================================ # Bad manifest. Installers missing Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 InstallerType: Msi Publisher: Microsoft License: Test ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InvalidLocale.yaml ================================================ # Bad manifest. Invalid locale. # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0 PackageName: MSIX SDK Publisher: Microsoft InstallerType: msi License: Test ShortDescription: Test invalid locale Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerSha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD PackageLocale: In-val-id-Lo-ca-le ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InvalidManifestVersionValue.yaml ================================================ # Bad manifest. Unsupported version Id: microsoft.msixsdk Name: MSIX SDK Version: 1.0.0.0 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0-invalid ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-InvalidUpdateBehavior.yaml ================================================ # Bad manifest. Invalid UpdateBehavior Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: NotAUrl Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD UpdateBehavior: InvalidUpdate ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-LicenseMissing.yaml ================================================ Id: microsoft.msixsdk Name: MSIX SDK Version: 1.07.32-beta Publisher: Microsoft InstallerType: Msi Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-MissingMsixInstallerFields.yaml ================================================ PackageIdentifier: AppInstallerCliTest.MissingMsixInstallerFields PackageVersion: 43690.48059.52428.56797 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher # PackageFamilyName: FakeInstallerForTesting_125rzkzqaqjwj # Missing field # MinimumOSVersion: 10.0.16299.0 # Missing field InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Good.msix ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-MsixInstaller-PackageVersion.yaml ================================================ PackageIdentifier: AppInstallerCliTest.BadMsixInstaller-PackageVersion PackageVersion: test.version # Version cannot be parsed to UINT64 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_125rzkzqaqjwj MinimumOSVersion: 10.0.0.0 InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Good.msix ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-NameMissing.yaml ================================================ # Bad manifest. No Name Id: microsoft.msixsdk Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-NoSupportedPlatforms.yaml ================================================ PackageIdentifier: AppInstallerCliTest.NoSupportedPlatforms PackageVersion: 43690.48059.52428.56797 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_125rzkzqaqjwj MinimumOSVersion: 10.0.16299.0 InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Bad-NoSupportedPlatforms.msix ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-PackageFamilyNameOnMSI.yaml ================================================ # Bad manifest. Invalid Arch value Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft License: Test Installers: - Arch: x86 InstallerType: MSI PackageFamilyName: Foo_123456789abcd Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-ProductCodeOnMSIX.yaml ================================================ # Bad manifest. Invalid Arch value Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft License: Test Installers: - Arch: x86 InstallerType: MSIX ProductCode: "{FOO}" Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-PublisherMissing.yaml ================================================ # Bad manifest. Publisher missing Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 InstallerType: Zip License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Invalid.yaml ================================================ # Bad manifest. Invalid Sha256 Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B6 ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Missing.yaml ================================================ # Bad manifest. No Sha256 Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-SwitchInvalid.yaml ================================================ # Bad manifest. Incorrect switch key. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Exe License: Test Switches: NotASwitch: /fakeswitch Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-UnknownProperty.yaml ================================================ # Bad manifest. Contains a key that is not in the manifest object. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Exe License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Fake: this is Fake ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-UnsupportedVersion.yaml ================================================ # Bad manifest. Unsupported version Id: microsoft.msixsdk Name: MSIX SDK Version: 1.0.0.0 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 1000.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-UrlInvalid.yaml ================================================ # Bad manifest. Invalid Url Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: NotAUrl Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-UrlMissing.yaml ================================================ # Bad manifest. No Url Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-VersionInvalid.yaml ================================================ # Bad manifest. Invalid version Id: microsoft.msixsdk Name: MSIX SDK Version: 1.0.9-/ Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Bad-VersionMissing.yaml ================================================ # Bad manifest. No Version Id: microsoft.msixsdk Name: MSIX SDK Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Encoding-ANSI.yaml ================================================ Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 InstallerType: Msi Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Encoding-UTF8-BOM.yaml ================================================ Id: microsoft.msixsdk Name: MSIX SDK© Version: 1.7.32 InstallerType: Msi Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Encoding-UTF8.yaml ================================================ Id: microsoft.msixsdk Name: MSIX SDK© Version: 1.7.32 InstallerType: Msi Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-AllDependencyTypes.yaml ================================================ # Installer with all types of dependencies # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: AppInstallerCliTest.TestMsixInstaller PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test MSIX Installer ShortDescription: AppInstaller Test MSIX Installer Publisher: Microsoft Corporation Moniker: AICLITestMsix License: Test Installers: - Architecture: x64 InstallerUrl: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix InstallerSha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe Dependencies: WindowsFeatures: - WindowsFeaturesDep1 - WindowsFeaturesDep2 WindowsLibraries: - WindowsLibrariesDep PackageDependencies: - PackageIdentifier: Package.Dep1-x64 MinimumVersion: '1.0' - PackageIdentifier: Package.Dep2-x64 ExternalDependencies: - ExternalDep ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-DefaultExpectedReturnCodeInInstallerSuccessCodes.yaml ================================================ # Default expected return codes exist in InstallerSuccessCodes # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageName: MSIX SDK PackageVersion: 1.07.32-beta Publisher: Microsoft ShortDescription: description License: Test PackageLocale: en-US InstallerSuccessCodes: - 1603 - 3010 Installers: - Architecture: x86 InstallerUrl: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.msi InstallerSha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: msi ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-Silent.yaml ================================================ # Good manifest. Installer Type exe requires Silent switch. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: Exe Switches: Silent: /s SilentWithProgress: /s ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-SilentRoot.yaml ================================================ # Good manifest. Installer Type exe requires Silent switch. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft License: Test Switches: Silent: /s SilentWithProgress: /s Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: Exe ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-Silent.yaml ================================================ # Good manifest. Installer Type exe requires Silent switch. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Exe License: Test Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Switches: Silent: /s SilentWithProgress: /s ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml ================================================ # Good manifest. Installer Type exe requires Silent switch. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Exe License: Test Switches: Silent: /s SilentWithProgress: /s Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeZip-PortableExe.yaml ================================================ # Bad manifest. Installer type zip with NestedInstallerType portable should only allow .exe files # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.9.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: Test installer for zip without nestedInstallers specified Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: zip NestedInstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerFiles: - RelativeFilePath: GoodApplication.exe ManifestType: singleton ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-InstallerUniqueness-DiffScope.yaml ================================================ # Good Manifest. Uniqueness installers different scope same arch Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Scope: user - Arch: x86 Url: https://ThisIsNotUsed2 Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCE Scope: machine ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DefaultLang.yaml ================================================ # Good Manifest. Uniqueness installers different languages same arch, with default language Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD - Arch: x86 Url: https://ThisIsNotUsed2 Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCE Language: en-MX ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DiffLangs.yaml ================================================ # Good Manifest. Uniqueness installers different languages same arch Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-US - Arch: x86 Url: https://ThisIsNotUsed2 Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCE Language: es-MX ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-Minimum-InstallerType.yaml ================================================ # Minimum required InstallerType in Installers Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: Msi ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-Minimum.yaml ================================================ # Minimum required Id: microsoft.msixsdk Name: MSIX SDK Version: 1.07.32-beta Publisher: Microsoft InstallerType: Msi License: Test Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-MsixBundleInstaller-WithStub.yaml ================================================ PackageIdentifier: AppInstallerCliTest.GoodMsixBundleInstaller PackageVersion: 43690.48059.52428.56797 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_125rzkzqaqjwj MinimumOSVersion: 10.0.16299.0 InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Good-WithStub.msixbundle ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-MsixBundleInstaller.yaml ================================================ PackageIdentifier: AppInstallerCliTest.GoodMsixBundleInstaller PackageVersion: 43690.48059.52428.56797 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_125rzkzqaqjwj MinimumOSVersion: 10.0.16299.0 InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Good.msixbundle ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-MsixInstaller.yaml ================================================ PackageIdentifier: AppInstallerCliTest.GoodMsixInstaller PackageVersion: 43690.48059.52428.56797 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_125rzkzqaqjwj MinimumOSVersion: 10.0.0.0 InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Good.msix ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-MultiLocale.yaml ================================================ PackageIdentifier: AppInstallerCliTest.MultiLocaleTest PackageVersion: 1.0.0.0 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher Moniker: AICLITestExe License: Test InstallerType: exe Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerSwitches: Custom: /unknownLocale SilentWithProgress: /silentwithprogress Silent: /silence - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerLocale: en-GB InstallerSwitches: Custom: /en-GB SilentWithProgress: /silentwithprogress Silent: /silence - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B InstallerLocale: fr-FR InstallerSwitches: Custom: /fr-FR SilentWithProgress: /silentwithprogress Silent: /silence Localization: - PackageLocale: en-GB PackageName: en-GB package name Publisher: en-GB publisher - PackageLocale: fr-FR PackageName: fr-FR package name ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-MultipleArpVersionDeclared.yaml ================================================ PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft Author: Microsoft PackageName: MSIX SDK License: MIT License ShortDescription: This is MSIX SDK Installers: - Architecture: x86 InstallerType: inno InstallerUrl: https://www.microsoft.com/msixsdk/setup.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 AppsAndFeaturesEntries: - DisplayVersion: "12.0" - Architecture: x64 InstallerType: inno InstallerUrl: https://www.microsoft.com/msixsdk/setup.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 AppsAndFeaturesEntries: - DisplayVersion: "13.0" ManifestType: merged ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-NoArpVersionDeclared.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft Author: Microsoft PackageName: MSIX SDK License: MIT License ShortDescription: This is MSIX SDK Installers: - Architecture: x86 InstallerType: inno InstallerUrl: https://www.microsoft.com/msixsdk/setup.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-SignedMsixBundleInstaller.yaml ================================================ PackageIdentifier: AppInstallerCliTest.GoodSignedMsixBundleInstaller PackageVersion: 43690.48059.52428.56797 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_125rzkzqaqjwj MinimumOSVersion: 10.0.16299.0 InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Signed-Good.msixbundle SignatureSha256: d70bd623f87b6ce4ddba4506c6000cf43ef3af4ab1207f5579ec43400de1623f ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-SignedMsixInstaller.yaml ================================================ PackageIdentifier: AppInstallerCliTest.GoodSignedMsixInstaller PackageVersion: 43690.48059.52428.56797 PackageLocale: es-MX PackageName: es-MX package name Publisher: es-MX publisher PackageFamilyName: FakeInstallerForTesting_125rzkzqaqjwj MinimumOSVersion: 10.0.0.0 InstallerType: msix Installers: - Architecture: x64 InstallerUrl: Installer-Signed-Good.msix SignatureSha256: 50562001202c8dad456474d3f20903138d0a15c44ee497c3d4f82e85edbf2f97 ManifestType: merged ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-SingleArpVersionDeclared.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft Author: Microsoft PackageName: MSIX SDK License: MIT License ShortDescription: This is MSIX SDK Installers: - Architecture: x86 InstallerType: inno InstallerUrl: https://www.microsoft.com/msixsdk/setup.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 AppsAndFeaturesEntries: - DisplayVersion: "11.0" ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-Spaces.yaml ================================================ Id: microsoft.msixsdk Name: " MSIX SDK" AppMoniker: "msixsdk" Version: 1.7.32 Publisher: Microsoft Channel: release Author: Microsoft License: MIT License LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE MinOSVersion: 0.0.0.0 Description: The MSIX SDK project is an effort to enable developers Homepage: https://github.com/microsoft/msix-packaging Tags: "msix, appx" Commands: "makemsix ,makeappx" Protocols: "protocol1,protocol2" FileExtensions: "appx,appxbundle,msix,msixbundle" # InstallerType and Switches CAN have a "default" value # on the root. An installer can override them. InstallerType: Zip Switches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Language: /en-us Log: /log= InstallLocation: /dir= Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Language: en-US InstallerType: Zip Scope: user Switches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Language: /en Log: /l= InstallLocation: /d= - Arch: x64 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx64.zip Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF0000 Language: en-US InstallerType: Zip Scope: user Localization: - Language: es-MX Description: El proyecto MSIX SDK es habilita desarrolladores de diferentes Homepage: https://github.com/microsoft/msix-packaging/es-MX LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-Switches.yaml ================================================ # Good manifest with Switches. Id: microsoft.msixsdk Name: MSIX SDK Version: 1.7.32 Publisher: Microsoft InstallerType: Msi License: Test Switches: SilentWithProgress: /sp Installers: - Arch: x86 Url: https://ThisIsNotUsed Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Switches: Silent: /s ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good-SystemReferenceComplex.yaml ================================================ Id: microsoft.sysrefcomp Name: Complex System Reference Version: 1.7.32 Publisher: Microsoft License: MIT License Description: A complex system reference test file. InstallerType: exe PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" Installers: - InstallerType: msix Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 - InstallerType: msi Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 - InstallerType: msix Arch: x64 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 PackageFamilyName: Override_8wekyb3d8bbwe - InstallerType: msi Arch: x64 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "Override" ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-Good.yaml ================================================ Id: microsoft.msixsdk Name: MSIX SDK AppMoniker: msixsdk Version: 1.7.32 Publisher: Microsoft Channel: release Author: Microsoft License: MIT License LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE MinOSVersion: 0.0.0.0 Description: The MSIX SDK project is an effort to enable developers Homepage: https://github.com/microsoft/msix-packaging Tags: "msix,appx" Commands: "makemsix,makeappx" Protocols: "protocol1,protocol2" FileExtensions: "appx,appxbundle,msix,msixbundle" # InstallerType and Switches CAN have a "default" value # on the root. An installer can override them. InstallerType: exe PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" UpdateBehavior: UninstallPrevious Switches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Language: /en-us Log: /log= InstallLocation: /dir= Update: /update Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Language: en-US InstallerType: exe Scope: user UpdateBehavior: Install Switches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Language: /en Log: /l= InstallLocation: /d= Update: /u - Arch: x64 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx64.zip Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF0000 Language: en-US InstallerType: exe Scope: user Localization: - Language: es-MX Description: El proyecto MSIX SDK es habilita desarrolladores de diferentes Homepage: https://github.com/microsoft/msix-packaging/es-MX LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-MSIX-in-AppsAndFeatures.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.12.0.schema.json PackageIdentifier: Microsoft.ArchiveContainingMSIX PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: Archive with nested MSIX Description: A manifest containing an archive with a nested MSIX installer InstallerLocale: en-US InstallerType: exe PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe AppsAndFeaturesEntries: - DisplayName: DisplayName InstallerType: msix InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair Installers: - Architecture: x86 InstallerLocale: en-GB InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ManifestType: singleton ManifestVersion: 1.12.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Manifest-MSIX-in-Archive.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.12.0.schema.json PackageIdentifier: Microsoft.ArchiveContainingMSIX PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: Archive with nested MSIX Description: A manifest containing an archive with a nested MSIX installer InstallerLocale: en-US InstallerType: zip NestedInstallerType: msix PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias Installers: - Architecture: x86 InstallerLocale: en-GB InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ManifestType: singleton ManifestVersion: 1.12.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_1-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_10-Bad-SchemaHeaderInvalid.yaml ================================================ # yaml-language-server= $schema=https://aka.ms/winget-manifest.singleton.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeaderInvalid PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header Invalid Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has an invalid schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_10-Bad-SchemaHeaderManifestTypeMismatch.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeaderManifestTypeMismatch PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header ManifestType Mismatch Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has a mismatched ManisfestType in the schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_10-Bad-SchemaHeaderManifestVersionMismatch.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.9.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeaderVersionMismatch PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header ManifestVersion Mismatch Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has a mismatched ManisfestVersion in the schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_10-Bad-SchemaHeaderNotFound.yaml ================================================ PackageIdentifier: AppInstallerCliTest.SchemaHeaderNotFound PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header Not Found Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has a missing schema header Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_10-Bad-SchemaHeaderURLPatternMismatch.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest-invalid.singleton.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.SchemaHeaderURLPatternMismatch PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Schema Header URL Pattern Mismatch Publisher: Microsoft Corporation License: Test ShortDescription: This manifest has a mismatched schema header URL pattern Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_10-InstallerAuthentication.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.10.0.schema.json PackageIdentifier: AppInstallerCliTest.InstallerAuthentication PackageVersion: 1.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Authentication Publisher: Microsoft Corporation AppMoniker: AICLITestInstallerAuthentication License: Test Authentication: AuthenticationType: microsoftEntraId MicrosoftEntraIdAuthenticationInfo: Resource: TestResource Scope: TestScope ShortDescription: Test installer for authentication Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: msi InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Authentication: AuthenticationType: microsoftEntraIdForAzureBlobStorage ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_10-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.10.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false ManifestType: singleton ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_12-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.12.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false ManifestType: singleton ManifestVersion: 1.12.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_28-PowerShellDSC.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.28.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true DesiredStateConfiguration: PowerShell: - RepositoryUrl: https://www.powershellgallery.com/api/v2 ModuleName: Microsoft.WinGet.DSC Resources: - Name: WinGetUserSettings - Name: WinGetAdminSettings - Name: WinGetSource - Name: WinGetPackageManager - Name: WinGetPackage - RepositoryUrl: https://mcr.microsoft.com/ ModuleName: Microsoft.WinGet.DSC Resources: - Name: WinGetUserSettings - Name: WinGetAdminSettings - Name: WinGetSource - Name: WinGetPackageManager - Name: WinGetPackage DSCv3: Resources: - Type: Microsoft.WinGet/AdminSettings - Type: Microsoft.WinGet/Package - Type: Microsoft.WinGet/Source - Type: Microsoft.WinGet/UserSettingsFile Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false ManifestType: singleton ManifestVersion: 1.28.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_28-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.28.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true DesiredStateConfiguration: DSCv3: Resources: - Type: Microsoft.WinGet/AdminSettings - Type: Microsoft.WinGet/Package - Type: Microsoft.WinGet/Source - Type: Microsoft.WinGet/UserSettingsFile Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false ManifestType: singleton ManifestVersion: 1.28.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_4-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_5-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com ManifestType: singleton ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_6-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.6.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com DownloadCommandProhibited: false ManifestType: singleton ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_7-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.7.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com DownloadCommandProhibited: false ManifestType: singleton ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLITests/TestData/ManifestV1_9-Singleton.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.9.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - US ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedArguments: - location UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false ManifestType: singleton ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1/ManifestV1-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.0.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ManifestType: defaultLocale ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1/ManifestV1-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.0.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" ManifestType: installer ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1/ManifestV1-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.0.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ManifestType: locale ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1/ManifestV1-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.0.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_1/ManifestV1_1-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_1/ManifestV1_1-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" ManifestType: installer ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_1/ManifestV1_1-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net ManifestType: locale ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_1/ManifestV1_1-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.1.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_10/ManifestV1_10-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.10.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_10/ManifestV1_10-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.10.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" InstallerSwitches: Repair: /r UpgradeBehavior: deny RepairBehavior: uninstaller - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com - Architecture: x64 InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp2" Files: - RelativeFilePath: "main2.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: other InvocationParameter: "/arg2" DisplayName: "DisplayName2" ArchiveBinariesDependOnPath: true - Architecture: x64 InstallerType: burn InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" UpgradeBehavior: deny RepairBehavior: modify ManifestType: installer ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_10/ManifestV1_10-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.10.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://localeTestIcon-en-GB IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 ManifestType: locale ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_10/ManifestV1_10-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.10.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.10.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_12/ManifestV1_12-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.12.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.12.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_12/ManifestV1_12-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.12.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" InstallerSwitches: Repair: /r UpgradeBehavior: deny RepairBehavior: uninstaller - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com - Architecture: x64 InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp2" Files: - RelativeFilePath: "main2.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: other InvocationParameter: "/arg2" DisplayName: "DisplayName2" ArchiveBinariesDependOnPath: true - Architecture: x64 InstallerType: burn InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" UpgradeBehavior: deny RepairBehavior: modify - Architecture: neutral InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: font NestedInstallerFiles: - RelativeFilePath: relativeFilePath1.otf - RelativeFilePath: relativeFilePath2.ttf - RelativeFilePath: relativeFilePath3.fnt - RelativeFilePath: relativeFilePath4.ttc - RelativeFilePath: relativeFilePath5.otc - Architecture: neutral InstallerType: font InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ManifestType: installer ManifestVersion: 1.12.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_12/ManifestV1_12-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.12.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://localeTestIcon-en-GB IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 ManifestType: locale ManifestVersion: 1.12.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_12/ManifestV1_12-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.12.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.12.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.2.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.2.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com ManifestType: installer ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.2.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com ManifestType: locale ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.2.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.28.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.28.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.28.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true DesiredStateConfiguration: DSCv3: Resources: - Type: Microsoft.WinGet/AdminSettings - Type: Microsoft.WinGet/Package - Type: Microsoft.WinGet/Source - Type: Microsoft.WinGet/UserSettingsFile Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" InstallerSwitches: Repair: /r UpgradeBehavior: deny RepairBehavior: uninstaller DesiredStateConfiguration: DSCv3: Resources: - Type: None/None - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com DesiredStateConfiguration: DSCv3: - Architecture: x64 InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp2" Files: - RelativeFilePath: "main2.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: other InvocationParameter: "/arg2" DisplayName: "DisplayName2" ArchiveBinariesDependOnPath: true - Architecture: x64 InstallerType: burn InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" UpgradeBehavior: deny RepairBehavior: modify - Architecture: neutral InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: font NestedInstallerFiles: - RelativeFilePath: relativeFilePath1.otf - RelativeFilePath: relativeFilePath2.ttf - RelativeFilePath: relativeFilePath3.fnt - RelativeFilePath: relativeFilePath4.ttc - RelativeFilePath: relativeFilePath5.otc - Architecture: neutral InstallerType: font InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ManifestType: installer ManifestVersion: 1.28.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.28.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://localeTestIcon-en-GB IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 ManifestType: locale ManifestVersion: 1.28.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.28.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.28.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_4/ManifestV1_4-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_4/ManifestV1_4-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com - Architecture: x64 InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp2" Files: - RelativeFilePath: "main2.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: other InvocationParameter: "/arg2" DisplayName: "DisplayName2" ManifestType: installer ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_4/ManifestV1_4-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com ManifestType: locale ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_4/ManifestV1_4-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.4.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_5/ManifestV1_5-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_5/ManifestV1_5-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com - Architecture: x64 InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp2" Files: - RelativeFilePath: "main2.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: other InvocationParameter: "/arg2" DisplayName: "DisplayName2" ManifestType: installer ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_5/ManifestV1_5-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://localeTestIcon-en-GB IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 ManifestType: locale ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_5/ManifestV1_5-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_6/ManifestV1_6-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.6.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_6/ManifestV1_6-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.6.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location DownloadCommandProhibited: false - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" UpgradeBehavior: deny - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com - Architecture: x64 InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp2" Files: - RelativeFilePath: "main2.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: other InvocationParameter: "/arg2" DisplayName: "DisplayName2" ManifestType: installer ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_6/ManifestV1_6-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.6.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://localeTestIcon-en-GB IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 ManifestType: locale ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_6/ManifestV1_6-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.6.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.6.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_7/ManifestV1_7-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.7.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_7/ManifestV1_7-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.7.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location DownloadCommandProhibited: false - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" InstallerSwitches: Repair: /r UpgradeBehavior: deny RepairBehavior: uninstaller - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com - Architecture: x64 InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp2" Files: - RelativeFilePath: "main2.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: other InvocationParameter: "/arg2" DisplayName: "DisplayName2" - Architecture: x64 InstallerType: burn InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" UpgradeBehavior: deny RepairBehavior: modify ManifestType: installer ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_7/ManifestV1_7-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.7.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://localeTestIcon-en-GB IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 ManifestType: locale ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_7/ManifestV1_7-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.7.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.7.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_9/ManifestV1_9-MultiFile-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US Publisher: Microsoft PublisherUrl: https://www.microsoft.com PublisherSupportUrl: https://www.microsoft.com/support PrivacyUrl: https://www.microsoft.com/privacy Author: Microsoft PackageName: MSIX SDK PackageUrl: https://www.microsoft.com/msixsdk/home License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk Tags: - "appxsdk" - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://testIcon-en-US IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123 Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net ManifestType: defaultLocale ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_9/ManifestV1_9-MultiFile-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Platform: - Windows.Desktop - Windows.Universal MinimumOSVersion: 10.0.0.0 InstallerType: exe Scope: machine InstallModes: - interactive - silent - silentWithProgress InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade Repair: /repair InstallerSuccessCodes: - 1 - 0x80070005 UpgradeBehavior: uninstallPrevious RepairBehavior: modify Commands: - makemsix - makeappx Protocols: - protocol1 - protocol2 FileExtensions: - appx - msix - appxbundle - msixbundle Dependencies: WindowsFeatures: - IIS WindowsLibraries: - VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDep MinimumVersion: 1.0.0 ExternalDependencies: - Outside dependencies Capabilities: - internetClient RestrictedCapabilities: - runFullTrust PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe ProductCode: "{Foo}" ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm AppsAndFeaturesEntries: - DisplayName: DisplayName DisplayVersion: DisplayVersion Publisher: Publisher ProductCode: ProductCode UpgradeCode: UpgradeCode InstallerType: exe Markets: AllowedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse ReturnResponseUrl: https://DefaultReturnResponseUrl.com UnsupportedArguments: - log NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp" Files: - RelativeFilePath: "main.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: launch InvocationParameter: "/arg" DisplayName: "DisplayName" DownloadCommandProhibited: true ArchiveBinariesDependOnPath: true Installers: - Architecture: x86 InstallerLocale: en-GB Platform: - Windows.Desktop MinimumOSVersion: 10.0.1.0 InstallerType: msix InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Scope: user InstallModes: - interactive InstallerSwitches: Custom: /c SilentWithProgress: /sp Silent: /s Interactive: /i Log: /l= InstallLocation: /d= Upgrade: /u Repair: /r UpgradeBehavior: install Commands: - makemsixPreview - makeappxPreview Protocols: - protocol1preview - protocol2preview FileExtensions: - appxbundle - msixbundle - appx - msix Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime PackageDependencies: - PackageIdentifier: Microsoft.MsixSdkDepPreview MinimumVersion: 1.0.0 ExternalDependencies: - Preview Outside dependencies PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe Capabilities: - internetClientPreview RestrictedCapabilities: - runFullTrustPreview ReleaseDate: 2021-02-02 InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 Markets: ExcludedMarkets: - "US" ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport - InstallerReturnCode: 3 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com UnsupportedArguments: - location DownloadCommandProhibited: false ArchiveBinariesDependOnPath: false - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" InstallerSwitches: Repair: /r UpgradeBehavior: deny RepairBehavior: uninstaller - Architecture: x86 InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom ReturnResponseUrl: https://defaultReturnResponseUrl.com - Architecture: x64 InstallerType: zip InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 NestedInstallerType: portable NestedInstallerFiles: - RelativeFilePath: relativeFilePath1 PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 InstallationMetadata: DefaultInstallLocation: "%ProgramFiles%\\TestApp2" Files: - RelativeFilePath: "main2.exe" FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 FileType: other InvocationParameter: "/arg2" DisplayName: "DisplayName2" ArchiveBinariesDependOnPath: true - Architecture: x64 InstallerType: burn InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" UpgradeBehavior: deny RepairBehavior: modify ManifestType: installer ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_9/ManifestV1_9-MultiFile-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.9.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Publisher: Microsoft UK PublisherUrl: https://www.microsoft.com/UK PublisherSupportUrl: https://www.microsoft.com/support/UK PrivacyUrl: https://www.microsoft.com/privacy/UK Author: Microsoft UK PackageName: MSIX SDK UK PackageUrl: https://www.microsoft.com/msixsdk/home/UK License: MIT License UK LicenseUrl: https://www.microsoft.com/msixsdk/license/UK Copyright: Copyright Microsoft Corporation UK CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK ShortDescription: This is MSIX SDK UK Description: The MSIX SDK project is an effort to enable developers UK Tags: - "appxsdkUK" - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Icons: - IconUrl: https://localeTestIcon-en-GB IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321 ManifestType: locale ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLITests/TestData/MultiFileManifestV1_9/ManifestV1_9-MultiFile-Version.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 DefaultLocale: en-US ManifestType: version ManifestVersion: 1.9.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Node-Mapping.yaml ================================================ key: value repeatedkey: repeated value RepeatedKey: repeated value RepeatedKey: repeated value MergeNode: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence MergeNode2: silentWithProgress: /silentwithprogress Interactive: /interactive ================================================ FILE: src/AppInstallerCLITests/TestData/Node-Merge.yaml ================================================ StrawHats: - Name: Monkey D Luffy Bounty: 3,000,000,000 - Name: Roronoa Zoro Bounty: 1,111,000,000 - Name: Nami Bounty: 366,000,000 ================================================ FILE: src/AppInstallerCLITests/TestData/Node-Merge2.yaml ================================================ StrawHats: - name: Monkey d Luffy Bounty: 150,000,000 Fruit: Gomu Gomu no Mi - Name: Nico Robin Bounty: 930,000,000 Fruit: Hana Hana no Mi ================================================ FILE: src/AppInstallerCLITests/TestData/Node-Types.yaml ================================================ IntegerUnquoted: 12345 IntegerSingleQuoted: '12345' IntegerDoubleQuoted: "12345" BooleanTrue: true StringTrue: 'true' BooleanFalse: false StringFalse: 'false' LocalTag: !myTag value ================================================ FILE: src/AppInstallerCLITests/TestData/NormalizationInitialIds.txt ================================================ WildfireGames.0AD SweetScapeSoftware.010Editor 360安全中心.360安全卫士 360安全中心.360杀毒 OpenMedia.4KSlideshowMaker OpenMedia.4KStogram OpenMedia.4KVideoDownloader OpenMedia.4KVideoDownloader OpenMedia.4KVideotoMP3 OpenMedia.4KYouTubetoMP3 IgorPavlov.7Zip IgorPavlov.7Zip IgorPavlov.7Zipalpha IgorPavlovTinoReichardt.7ZipZSZS AmazonWebServicesDeveloperRelations.AWSCommandLineInterface AmazonWebServices.AWSCommandLineInterface AWSServerlessApplications.AWSSAMCommandLineInterface AxisCommunications.AXISCameraStation AbacusResearch.AbaClient Microsoft.AccessibilityInsightsForWindows Microsoft.ActiveDirectoryAuthenticationLibraryforSQLServer AdobeSystems.AdobeAcrobatReaderDCCzech AdobeSystems.AdobeAcrobatReaderDC AdobeSystems.AdobeAcrobatReaderDCMUI AdoptOpenJDK.AdoptOpenJDKJDKwithHotspot AdoptOpenJDK.AdoptOpenJDKJDKwithHotspot AdoptOpenJDK.AdoptOpenJDKJDKwithHotspot AdoptOpenJDK.AdoptOpenJDKJDKwithHotspot AdoptOpenJDK.AdoptOpenJDKJDKwithHotspot AdoptOpenJDK.AdoptOpenJDKJDKwithHotspot AdoptOpenJDK.AdoptOpenJDKJDKwithHotspot Famatech.AdvancedIPScanner OndrejSalplachta.AdvancedLogViewer Famatech.AdvancedPortScanner PawelPsztyc.AdvancedRestClient AegisubTeam.Aegisub8975master8d77da3 AegisubTeam.Aegisub awandersick.AeroZoombeta2 Alacritty.Alacritty AlchemyDevelopmentGroup.AlchemyBeta Algoryx.Algodoo StefanSundin.AltDrag Amazoncom.AmazonChime Amazon.AmazonCorretto Amazon.AmazonCorretto8 AmazonWebServices.AmazonWorkSpaces Anaconda.Anaconda3 AngryIPScanner.AngryIPScanner AntiMicro.AntiMicro AppGet.AppGet AppiumDevelopers.Appium AppiumDevelopers.Appium Arduino.Arduino ArmagetronAdvancedTeam.ArmagetronAdvanced SundaramRamaswamy.Artha RabidViperProductions.AssaultCube AudacityTeam.Audacity CodeUXdesigneU.AuthPass ArminOsaj.AutoDarkMode Lexikos.AutoHotkey Lexikos.AutoHotkey TheSleuthKit.Autopsy GPLPublicrelease.AviSynth OmicronLab.AvroKeyboard 7room.Aya Microsoft.AzureCosmosDBEmulator Microsoft.AzureDataStudio Microsoft.AzureFunctionsCoreTools Microsoft.AzureIoTExplorer Microsoft.AzureIoTexplorer MarcinSzeniak.BCUninstaller Contosocom.BITSManager BedaKosata.BKChem SpaceSciencesLaboratoryUCBerkeley.BOINC BPBibleDevelopmentTeam.BPBible Google.BackupandSyncfromGoogle DebaucheeOpenSourceGroup.Barrier DebaucheeOpenSourceGroup.Barrier PaulFrazee.BeakerBrowser Elastic.Beatswinlogbeat Elastic.Beatswinlogbeat MarcoMastroddiSoftware.BeeBEEP beeftextorg.Beeftext TheBetaflightopensourceproject.BetaflightConfigurator ScooterSoftware.BeyondCompare4 ScooterSoftware.BeyondCompare ScooterSoftware.BeyondCompare BiglySoftware.BiglyBT BitPay.BitPay Bitwarden.Bitwarden BleachBit.BleachBit BlenderFoundation.Blender BlueJeansNetwork.BlueJeans HamsterRepublicProductions.BobtheHamsterVGA Apple.Bonjour AndrewSampson.BorderlessGaming Microsoft.BotFrameworkComposer Microsoft.BotFrameworkComposer Microsoft.BotFrameworkComposer Microsoft.BotFrameworkComposer Microsoft.BotFrameworkEmulator Microsoft.BotFrameworkEmulator Microsoft.BotFrameworkEmulator bracketsio.Brackets BraveSoftware.Brave BraveSoftware.BraveNightly TGRMNSoftware.BulkRenameUtility Buttercup.Buttercup Buttercup.Buttercup CDogsSDLTeam.CDogsSDL SingularLabs.CCEnhancer Piriform.CCleaner Canneverbe.CDBurnerXP Kitware.CMake Corsair.CORSAIRiCUESoftware CPUID.CPUIDCPUZ CPUID.CPUIDCPUZ CPUID.CPUIDCPUZ CPUID.CPUIDHWMonitor CPUID.CPUIDHWMonitor PenguinLabs.Cacher SaeraSoft.CaesiumPH TechSmith.Camtasia2019 TechSmith.Camtasia2020 SindreSorhus.Caprine SindreSorhus.Caprine GielCobben.Caption MathewSachin.Captura RedDucks.CemUI AlexandrSubbotin.Cerebro MITIST.CertAidforWindows secana.CertDump Webprofusion.CertifyTheWeb awandersick.ChMac ChefSoftware.ChefDK ChemAxon.ChemAxonChemCurator ChemAxon.ChemAxonMarkushEditor ChemAxon.ChemAxonMarvinSuite ChemAxon.ChemAxonMarvinSuite TheChromiumAuthors.Chromium CircuitDiagram.CircuitDiagram CircuitDiagram.CircuitDiagram CiscoWebex.CiscoWebexMeetings DanielScalzi.CitycraftLauncher alch.ClamWinFreeAntivirus Fndroid.ClashforWindows Fndroid.ClashforWindows Fndroid.ClashforWindows Fndroid.ClashforWindows BeijingEEOEducationTechnology.ClassIn Clementine.Clementine MartinRidgers.Clink Cloudflare.CloudflareWARP TheCodeBlocksTeam.CodeBlocks EranIfrah.CodeLite StevenCole.Coffee TerranovaTeam.ColobotGoldEditionalpha JayPrall.ColorCop Toinane.Colorpicker ConEmuMaximus.ConEmu201011 Concept2.Concept2Utility Microsoft.ConfigMgr2012Toolkit Contasimple.ContasimpleDesktop ALCPU.CoreTemp Couchbase.CouchbaseServerCommunityEdition Couchbase.CouchbaseServerEnterpriseEdition CozyCloud.CozyDrive TheCppcheckteam.Cppcheck HabibRehman.Crypter cryptomatororg.Cryptomator CrystalDewWorld.CrystalDiskInfo CrystalDewWorld.CrystalDiskInfo CrystalDewWorld.CrystalDiskInfoKureiKeiEdition CrystalDewWorld.CrystalDiskInfoShizukuEdition CrystalDewWorld.CrystalDiskInfo CrystalDewWorld.CrystalDiskInfo CrystalDewWorld.CrystalDiskInfoKureiKeiEdition CrystalDewWorld.CrystalDiskInfoShizukuEdition CrystalDewWorld.CrystalDiskInfo CrystalDewWorld.CrystalDiskInfo CrystalDewWorld.CrystalDiskInfoKureiKeiEdition CrystalDewWorld.CrystalDiskInfoShizukuEdition CrystalDewWorld.CrystalDiskInfo CrystalDewWorld.CrystalDiskInfoKureiKeiEdition CrystalDewWorld.CrystalDiskInfoShizukuEdition CrystalDewWorld.CrystalDiskMark CrystalDewWorld.CrystalDiskMarkShizukuEdition cubicsdrcom.CubicSDRInstaller AcroSoftware.CutePDFWriter iterate.Cyberduck DBBrowserforSQLiteTeam.DBBrowserforSQLite DBeaver.DBeaver DBeaver.DBeaver DBeaver.DBeaver DBeaver.DBeaver DBeaver.DBeaver DBeaver.DBeaver DBeaver.DBeaver DBeaver.DBeaver DBeaver.DBeaver DJI.DJIAssistant2 DJI.DJIAssistant2ForAeroscope DJI.DJIAssistant2ForAutopilot DJI.DJIAssistant2ForBatteryStation DJI.DJIAssistant2ForMG DJI.DJIAssistant2ForMatrice DJI.DJIAssistant2ForMavic DJI.DJIAssistant2ForPhantom TshwaneDJe.DaveGnukem Boxstar.DeepVocalToolBoxbetaversionbeta Boxstar.DeepVocalbetaversionbeta Deezer.Deezer AshleyStone.DefaultAudio Piriform.Defraggler Dell.DellCommandUpdate Dell.DellUpdate BloodshedSoftware.DevC ThinkingManSoftware.Dimension4 Serraniel.DiscordMediaLoader Serraniel.DiscordMediaLoader ScottBrogden.Ditto Dixa.Dixa DjVuZone.DjVuLibreDjView DockStation.DockStation Docker.DockerDesktop DokanyProject.DokanLibrary DokanyProject.DokanLibrary DolphinTeam.Dolphin denginenet.Doomsday Digimezzo.Dopamine Doxie.Doxie Dropbox.Dropbox EasternGraphics.EGRSafenetActivation EasternGraphics.EGRShellExtension EagleGet.EagleGet EagleGet.EagleGet LukeStratman.EasyConnect ESComputing.EditPlus EduMIPS64DevelopmentTeam.EduMIPS64 Elastic.Elasticsearch ElgatoSystems.ElgatoStreamDeck Empochecom.Empoche Empochecom.Empoche MacPaw.Encrypto awandersick.EnglishizeCmd SinewSoftwareSystemsPrivate.Enpass TheEraserProject.Eraser TheEraserProject.Eraser TheEraserProject.Eraser TheEraserProject.Eraser Esteem.Esteem Ethereum.EthereumGethOfficialGoimplementationoftheEthereumprotocol Evernote.Evernotev Evernote.Evernotev DavidCarpenter.Everything DavidCarpenter.Everything DavidCarpenter.Everything DavidCarpenter.EverythingLite voidtools.Everything ExpressVPN.ExpressVPN Ultrapico.Expresso TheExtremeTuxRacerteam.ExtremeTuxRacer EugeneRoshalFarGroup.FarManager3 HShirouzu.FastCopy FastStoneSoft.FastStoneCapture FastStoneSoft.FastStoneImageViewer FedoraProject.FedoraMediaWriter AmineMouafik.Ferdi ProgressSoftware.FiddlerEverywhere ProgressSoftware.FiddlerEverywhere ProgressSoftware.FiddlerEverywhere ProgressSoftware.FiddlerEverywhere ProgressSoftware.FiddlerEverywhere ProgressSoftware.FiddlerEverywhere ProgressSoftware.FiddlerEverywhere AdrienAllard.FileConverter BinaryFortressSoftware.FileSeek TimKosse.FileZillaClient TimKosse.FileZillaClient TimKosse.FileZillaClient TimKosse.FileZillaClient TimKosse.FileZillaClient Mozilla.FirefoxDeveloperEdition Mozilla.FirefoxDeveloperEdition Mozilla.FirefoxDeveloperEdition Mozilla.FirefoxDeveloperEdition OpenSightSoftware.FlashFXP5 TheFlightGearTeam.FlightGear DominikLevitskyStudio.FontBase FontForgeBuilds.FontForge FreeTime.FormatFactory FreeTime.FormatFactory FoxitSoftware.FoxitPhantomPDF FoxitSoftware.FoxitReader StefanMalzner.Franz MarekJasinskiwwwFreeCommandercom.FreeCommanderXE Humanity.FreeMat TheGIMPTeam.GIMP TheGIMPTeam.GIMP TheGIMPTeam.GIMP TheGIMPTeam.GIMP TheGIMPTeam.GIMP TheGIMPTeam.GIMP TheGIMPTeam.GIMP TheGIMPTeam.GIMP ARM.GNUArmEmbeddedToolchain92020 TheFreeSoftwareFoundation.GNUMidnightCommander TheGnuPGProject.GNUPrivacyGuard GCNDevelopment.GNURadio GOGcom.GOGGALAXY ArtifexSoftware.GPLGhostscript GhostgumSoftware.GSview Garmin.GarminExpress ThoughtWorks.Gauge ThoughtWorks.Gauge TheGeanydeveloperteam.Geany PrimateLabs.Geekbench5 Gephi.Gephi Outertech.GetDiz GitExtensionsTeam.GitExtensions GitExtensionsTeam.GitExtensions GitExtensionsTeam.GitExtensions GitHub.GitLFS TheGitDevelopmentCommunity.Git TheGitDevelopmentCommunity.Git TheGitDevelopmentCommunity.Git TheGitDevelopmentCommunity.Git TheGitDevelopmentCommunity.Git TheGitDevelopmentCommunity.Git GitHub.GitHubCLI GitHub.GitHubDesktopMachineWideInstaller StefHeyenrath.GitHubReleaseNotes TroupeTechnology.Gitter GlimpseProject.Glimpse GlimpseProject.Glimpse GlimpseProject.Glimpse GnuCashDevelopmentTeam.GnuCash GnuCashDevelopmentTeam.GnuCash GnuWin.GnuWin32Grep GnuWin.GnuWin32Make GnuWin.GnuWin32Wget GnuWin.GnuWin32Zip golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego golangorg.GoProgrammingLanguagego GoldWave.GoldWave Google.GoogleChrome Google.GoogleCloudSDK Google.GoogleEarthPro TheGpg4winProject.Gpg4win TheGpg4winProject.Gpg4win GrafanaLabs.GrafanaEnterprise GrafanaLabs.GrafanaOSS Grammarly.GrammarlyforMicrosoftOfficeSuite TheGrampsproject.GrampsAIO64 Graphcool.GraphQLPlayground AdamMiskiewicz.GraphiQL ATTResearchLabs.Graphviz Greenshot.Greenshot GridTeam.Grid EpiforgeSoftware.Grindstone4 HHDSoftware.HHDSoftwareFreeHexEditorNeo HectorMaurcioRodriguezSegura.HMNISEdit HP.HPCloudRecoveryTool HuaweiSoftwareTechnologies.HUAWEICloud MartinMalikREALiX.HWiNFO64 MartinMalikREALiX.HWiNFO64 MartinMalikREALiX.HWiNFO64 MartinMalikREALiX.HWiNFO64 HandyOrg.HandyWinGet VincentL.Harmony ImplbitsSoftware.HashTab ImplbitsSoftware.HashTab JAMSoftware.HeavyLoad HedgewarsProject.Hedgewars AnsgarBecker.HeidiSQL AnsgarBecker.HeidiSQL AnsgarBecker.HeidiSQL AnsgarBecker.HeidiSQL AnsgarBecker.HeidiSQL PerforceSoftware.HelixCoreApps IBESoftware.HelpNDocPersonalEdition HexChat.HexChat ScottLerch.HostsFileEditor Caphyon.Hover Borvid.HttpMasterExpressEdition Borvid.HttpMasterExpressEdition Borvid.HttpMasterExpressEdition Borvid.HttpMasterExpressEdition Borvid.HttpMasterProfessionalEdition Borvid.HttpMasterProfessionalEdition Borvid.HttpMasterProfessionalEdition Huawei.HuaweiQuickAppIDE HyneSon.HyneTimberDesign MarquisKurt.HyperspaceDesktop Google.IAPDesktop HexRays.IDAFreeware TibboTechnology.IONinja3 DavidMoore.IPFilter IRCCloud.IRCCloud IvanZahariev.IZArc DuongDieuPhap.ImageGlass Inkscape.Inkscape jrsoftwareorg.InnoSetup jrsoftwareorg.InnoSetup CrystalRich.InternetOff seonglae.Intuiter IrfanSkiljan.IrfanView IronPythonTeam.IronPython IronPythonTeam.IronPython ISWIX.IsWiX SmartProjects.IsoBuster ChemAxon.JChemNETAPI JabRef.JabRef Jackett.Jackett SavoirFaireLinux.Jami Oracle.Java8Update251 Oracle.Java8Update261 JetBrains.JetBrainsToolbox JitsiTeam.JitsiMeet JitsiTeam.JitsiMeet LaurentCozic.Joplin LaurentCozic.Joplin LaurentCozic.Joplin JuliaLanguage.Julia JuliaLanguage.Julia JuliaLanguage.Julia KLCP.KLiteCodecPackStandard KLCP.KLiteMegaCodecPack KKBOXTaiwan.KKBOX ChiaLungChen.Kaku DominikReichl.KeePassPasswordSafe DominikReichl.KeePassPasswordSafe DominikReichl.KeePassPasswordSafe KeePassXCTeam.KeePassXC KeeWeb.KeeWeb Keybase.Keybase KiCad.KiCad KiCad.KiCad KiCad.KiCad KrispTechnologies.Krisp KritaFoundation.Krita RoniLehto.LMath LBRY.LBRY LBRY.LBRY LBRY.LBRY LBRY.LBRY LBRY.LBRY LINE.LINE JosephAlbahari.LINQPad6 LLVM.LLVM LMMSDevelopers.LMMS LMMSDevelopers.LMMS love2dorg.LOVE leokhoa.Laragon LastPass.LastPass LazarusTeam.Lazarus RiotGames.LeagueofLegends Lenovo.LenovoMigrationAssistant Lenovo.LenovoSystemUpdate LakendLabs.Lens Leonflix.Leonflix BellSoft.LibericaJDK11 BellSoft.LibericaJDK11Full BellSoft.LibericaJDK14 BellSoft.LibericaJDK14Full BellSoft.LibericaJDK15 BellSoft.LibericaJDK15Full BellSoft.LibericaJDK8 BellSoft.LibericaJDK8Full LibreCADTeam.LibreCAD TheDocumentFoundation.LibreOffice TheDocumentFoundation.LibreOffice TeamLidarr.Lidarr AlexeyTyrrrzGolub.LightBulb AlexeyTyrrrzGolub.LightBulb ChristianKaiser.Lightscreen LeifAsbrinkSM5BSZ.Linrad LiskFoundation.LiskHub Listen.Listen1 Listen.Listen1 Flywheel.Local CrystalRich.LockHunter BinaryFortressSoftware.LogFusion BinaryFortressSoftware.LogFusion Logitech.LogitechGamingSoftware Loom.Loom LyXTeam.LyX LyXTeam.LyX COTILab.MCXStudioversionnightlybuild COTILab.MCXStudio MoritzBunkus.MKVToolNix MoritzBunkus.MKVToolNix MoritzBunkus.MKVToolNix MoritzBunkus.MKVToolNix MoritzBunkus.MKVToolNix MPCHCTeam.MPCHC MPCHCTeam.MPCHC MPCHCTeam.MPCHC ThomasNordquist.MQTTExplorer Microsoft.MSIXCore MaxthonInternational.MX5 MYCOM.MYGAMESGameCenter FlyingSnowSamanthaGlocker.MacType Firetrust.MailWasher Firetrust.MailWasherPro MajsoulPlusTeam.MajsoulPlus GuinpinSoft.MakeMKV Malwarebytes.Malwarebytes KDE.Marble MariaDB.MariaDB Jocs.MarkText WestWindTechnologies.MarkdownMonster WestWindTechnologies.MarkdownMonster WestWindTechnologies.MarkdownMonster WestWindTechnologies.MarkdownMonster WestWindTechnologies.MarkdownMonster MarkdownOutlook.MarkdownOutlook MasterPackager.MasterPackager Mattermost.Mattermost MediaAreanet.MediaInfo MediaAreanet.MediaInfoCLI VentisMedia.MediaMonkey TheMeldproject.Meld JaneaSystems.MemuraiDeveloper Microsoft.MicrosoftNETCoreSDK Microsoft.MicrosoftNETCoreSDK Microsoft.MicrosoftNETCoreSDK Microsoft.MicrosoftNETCoreSDK Microsoft.MicrosoftNETCoreSDK Microsoft.MicrosoftNETCoreSDK Microsoft.MicrosoftNETFrameworkMultiTargetingPack Microsoft.MicrosoftNETFrameworkMultiTargetingPack Microsoft.MicrosoftNETFrameworkMultiTargetingPack Microsoft.MicrosoftNETFrameworkSDK Microsoft.MicrosoftNETFrameworkMultiTargetingPack Microsoft.MicrosoftNETFrameworkMultiTargetingPack Microsoft.MicrosoftNETFrameworkSDK Microsoft.MicrosoftNETFrameworkTargetingPack Microsoft.MicrosoftNETFrameworkSDK Microsoft.MicrosoftNETFrameworkTargetingPack Microsoft.MicrosoftNETSDK Microsoft.MicrosoftNETSDK Microsoft.MicrosoftNETSDK Microsoft.MicrosoftNETSDK Microsoft.MicrosoftAzureCLI Microsoft.MicrosoftAzureStorageEmulator Microsoft.MicrosoftAzureStorageExplorer Microsoft.MicrosoftAzureStorageExplorer Microsoft.MicrosoftDeploymentToolkit Microsoft.MicrosoftEdge Microsoft.MicrosoftEdgeBeta Microsoft.MicrosoftEdgeDev MicrosoftGarage.MicrosoftGarageMousewithoutBorders Microsoft.MicrosoftHelpViewer Microsoft.MicrosoftHelpViewer Microsoft.MicrosoftMPI Microsoft.MicrosoftMPI Microsoft.MicrosoftMPISDK Microsoft.MicrosoftODBCDriver13forSQLServer Microsoft.MicrosoftODBCDriver17forSQLServer Microsoft.MicrosoftOLEDBDriverforSQLServer Microsoft.MicrosoftROpen Microsoft.MicrosoftROpen Microsoft.MicrosoftSQLServer2012NativeClient Microsoft.MicrosoftSQLServer2014ManagementObjects Microsoft.MicrosoftSQLServer2016 Microsoft.MicrosoftSQLServer2016Policies Microsoft.MicrosoftSQLServer2016TSQLLanguageService Microsoft.MicrosoftSQLServer2016TSQLScriptDom Microsoft.MicrosoftSQLServer2017 Microsoft.MicrosoftSQLServer2017Policies Microsoft.MicrosoftSQLServer2017TSQLLanguageService Microsoft.MicrosoftSQLServerDataTierApplicationFramework Microsoft.MicrosoftSQLServerManagementStudio Microsoft.MicrosoftSQLServerManagementStudio Microsoft.MicrosoftSQLServerManagementStudio Microsoft.MicrosoftSQLServerManagementStudio Microsoft.MicrosoftSQLServerManagementStudio Microsoft.MicrosoftSmallBasic Microsoft.MicrosoftSystemCLRTypesforSQLServer2014 Microsoft.MicrosoftSystemCLRTypesforSQLServer2016 Microsoft.MicrosoftSystemCLRTypesforSQLServer2017 Microsoft.MicrosoftVisioViewer2016 Microsoft.MicrosoftVisualStudio2010ToolsforOfficeRuntime Microsoft.MicrosoftVisualStudio2015Shell Microsoft.MicrosoftVisualStudioCode Microsoft.MicrosoftVisualStudioCodeInsiders Microsoft.MicrosoftVisualStudioToolsforApplications2015 Microsoft.MicrosoftVisualStudioToolsforApplications2015LanguageSupport Microsoft.MicrosoftVisualStudioToolsforApplications2017 Microsoft.MicrosoftWebPlatformInstaller Anaconda.Miniconda3 Anaconda.Miniconda3py MongoDB.MongoDB2008PlusSSL Xamarin.MonoforWindows TheMonoGameTeam.MonoGameSDK MoonlightGameStreamingProject.MoonlightGameStreamingClient AGALWOOD.Motrix AGALWOOD.Motrix Mozilla.MozillaFirefoxESR Mozilla.MozillaFirefoxESR Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefoxESR Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefoxESR Mozilla.MozillaFirefoxESR Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaFirefox Mozilla.MozillaMaintenanceService Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird Mozilla.MozillaThunderbird NicholasHTollervey.Mu MullvadVPN.MullvadVPN MullvadVPN.MullvadVPN Microsoft.MultilingualAppToolkit canonical.Multipass TheMumbleDevelopers.Mumble TheMumbleDevelopers.Mumble TheMumbleDevelopers.Mumble WernerSchweerandOthers.MuseScore3 YoutaTec.Muta Logitech.MyHarmony MartinRenoldandtheMyPaintDevelopmentTeam.MyPaint Oracle.MySQLInstallerCommunity Feodor.Mypal JustinAquadro.NBTExplorer RicoSuter.NSwagStudio NVAccess.NVDA NVIDIA.NVIDIANVIDIARTXVoiceDriver NVIDIA.NVIDIARTXVoiceApplication NZXT.NZXTCAM NZXT.NZXTCAM NZXT.NZXTCAM NZXT.NZXTCAM NZXT.NZXTCAM NZXT.NZXTCAM AllanCORNET.Nelson Neotys.NeoLoad LutzRoeder.Netron Nitro.NitroPro NmapProject.Nmap qinghai.NoSQLBoosterforMongoDB qinghai.NoSQLBoosterforMongoDB qinghai.NoSQLBoosterforMongoDB NodejsFoundation.Nodejs Nodist.Nodist NordVPN.NordVPN TEFINCOM.NordVPN NordVPN.NordVPNnetworkTAP NordVPN.NordVPNnetworkTUN CodingRoad.NoteHighlight2016 XhmikosR.Notepad2mod NotionLabs.Notion NotionLabs.Notion NmapProject.Npcap NullNoname.NullpoMino NullsoftandContributors.NullsoftInstallSystem OBSProject.OBSStudio HamsterRepublicProductions.OHRRPGCEgorgonzola20200502 AscensioSystemSIA.ONLYOFFICEDesktopEditors GNUOctave.Octave WetHatLab.OneNoteTaggingKit OpenShopChannel.OpenShopChannelDownloader TheOpenShellTeam.OpenShell namazso.OpenHashTab namazso.OpenHashTab ojdkbuildopensourceproject.OpenJDK ojdkbuildopensourceproject.OpenJDK ojdkbuildopensourceproject.OpenJDK ojdkbuildopensourceproject.OpenJDK OpenMPTDevs.OpenMPT ApacheSoftwareFoundation.OpenOffice OpenRAdevelopers.OpenRA TheOpenSCADDevelopers.OpenSCAD ShiningLightProductions.OpenSSL OpenShotStudios.OpenShotVideoEditor OpenTTD.OpenTTD OpenTTD.OpenTTD OpenVPNTechnologies.OpenVPN SparkLabs.OpenVPNConfigurationGenerator OpenVPNTechnologies.OpenVPNConnect OperaSoftware.OperaGXStable OperaSoftware.OperaGXStable OperaSoftware.OperaStable OperaSoftware.OperaStable OperaSoftware.OperaStable OperaSoftware.OperaStable OperaSoftware.OperaStable OperaSoftware.OperaStable Oracle.OracleVMVirtualBox Oracle.OracleVMVirtualBox Oracle.OracleVMVirtualBox Oracle.OracleVMVirtualBox OutSystems.OutSystemsDevelopmentEnvironment11 EXPSystems.PDFreDirect SoberLemurSasdiVacondioAndrea.PDFsamBasic CCPKU.PKUGateway JanFiala.PSPadeditor NagleCode.PacketSender MoonchildProductions.PaleMoon MoonchildProductions.PaleMoon JohnMacFarlane.Pandoc JohnMacFarlane.Pandoc JohnMacFarlane.Pandoc ParadoxInteractive.ParadoxLauncher ParagonSoftware.ParagonBackupRecoveryÃâžÂ17CE ParsecCloud.Parsec FrancescoSorge.PasteIntoFile GiorgioTani.PeaZip GiorgioTani.PeaZip PersepolisTeam.PersepolisDownloadManager HeikoSommerfeldt.PhonerLite touchbyte.PhotoSync NGWIN.PicPick PicoTorrentcontributors.PicoTorrent blupiorg.PlanetBlupi SonyInteractiveEntertainmentNetworkAmerica.PlayStationÃâžÂNow JosefNemec.Playnite Plex.Plex Plex.PlexMediaPlayer Plex.PlexMediaServer Plex.Plexamp Plex.Plexamp Plex.Plexamp wwwpokerthnet.PokerTH Postbox.Postbox PostgreSQLGlobalDevelopmentGroup.PostgreSQL12 PostgreSQLGlobalDevelopmentGroup.PostgreSQL13 Microsoft.PowerShell7preview Microsoft.PowerShell7 IronmanSoftware.PowerShellUniversal Microsoft.PowerToys KimWalisch.Primesieve PrivateInternetAccess.PrivateInternetAccess ProgressSoftware.ProgressTelerikFiddler Microsoft.ProjectMyScreenApp ProtonTechnologies.ProtonVPN ProtonTechnologies.ProtonVPNTap SimonTatham.PuTTY PuppetLabs.Puppet Puppet.PuppetAgent Puppet.PuppetBolt Puppet.PuppetDevelopmentKit MillerPuckette.PureData LancasterUniversityPhysics.PyMODA PythonSoftwareFoundation.Python HubertPham.PythonPyAudio PythonSoftwareFoundation.Python PythonSoftwareFoundation.Python PythonSoftwareFoundation.Python PythonSoftwareFoundation.Python PythonSoftwareFoundation.Python PythonSoftwareFoundation.Python PythonSoftwareFoundation.Python PythonSoftwareFoundation.PythonLauncher QGISDevelopmentTeam.QGISACoru QGISDevelopmentTeam.QGISBucuresti QGISDevelopmentTeam.QGISPi MichaelHansen.QTextPad HannaKnutsson.Qalculate LarusStone.QtSpim ModuleArt.QuickPictureViewer PaddyXu.QuickLook Quicken.Quicken RCoreTeam.RforWindows RCoreTeam.RforWindows RCoreTeam.RforWindows RStudio.RStudio Rambox.Rambox OlegDanilov.RapidEnvironmentEditor RaspberryPi.RaspberryPiImager rawtherapeecom.RawTherapee PaulRawnsley.RedditWallpaperChanger jklSoft.Rekodecompilerfor Devolutions.RemoteDesktopManager Devolutions.RemoteDesktopManagerFree RemoteMouse.RemoteMouse BaldurKarlsson.RenderDoc Antmicro.Renode Responsively.ResponsivelyApp RetroShareTeam.RetroShare VSRevoGroup.RevoUninstaller VSRevoGroup.RevoUninstallerPro 3TSoftwareLabs.Robo3T 3TSoftwareLabs.Robo3T 3TSoftwareLabs.Robo3T RocketChatSupport.RocketChat PunkSoftware.RocketDock ArtsoftEntertainment.RocksnDiamonds MarkoBL.Rosi code4ward.RoyalTS RoyalApps.RoyalTS TheRFoundation.Rtools RubyInstallerTeam.Ruby RubyInstallerTeam.RubywithMSYS2 RubyInstallerTeam.Ruby LukeHaas.RunJS LukeHaas.RunJS TheRustProjectDevelopers.Rust TheRustProjectDevelopers.Rust TheRustProjectDevelopers.Rust TheRustProjectDevelopers.Rust TheRustProjectDevelopers.Rust TheRustProjectDevelopers.Rust TheRustProjectDevelopers.Rust TheRustProjectDevelopers.Rust TheRustProjectDevelopers.Rust TopalaSoftwareSolutions.SIW2020aTrial RicardoVillalba.SMPlayer Navimatics.SSHFSWin2020 DotzSoftwares.SVGExplorerExtension SamsungElectronics.SamsungDeX mircearoata.SatisfactoryModLauncher ScratchFoundation.ScratchDesktop NickeManarin.ScreenToGif TheScribusTeam.Scribus TheScummVMTeam.ScummVM spikespaz.SearchDeflector Sejda.SejdaPDFDesktop Datalust.Seq Microsoft.SharePointOnlineManagementShell ShareXTeam.ShareX RandyRantscom.SharpKeys Meltytech.Shotcut SigilEbook.Sigil OpenWhisperSystems.Signal OpenWhisperSystems.Signal OpenWhisperSystems.Signal OpenWhisperSystems.Signal OpenWhisperSystems.Signal OpenWhisperSystems.Signal OpenWhisperSystems.Signal Approximatrix.SimplyFortran3 AshleyStone.SitdownMW SkypeTechnologies.Skype SkypeTechnologies.Skype SkypeTechnologies.Skype SlackTechnologies.SlackMachineWide TechSmith.Snagit2020 SnakeNestcom.SnakeTail CoryPlotts.Snoop SmartBearSoftware.SoapUI SonicPi.SonicPi Sonos.Sonos Sonos.SonosController AntoineAflalo.SoundSwitch Atlassian.Sourcetree SpeedCrunch.SpeedCrunch SpekProject.Spek StandardNotes.StandardNotes Valve.Steam wwwsbclorg.SteelBankCommonLisp SteelSeries.SteelSeriesEngine Stellariumteam.Stellarium Stellariumteam.Stellarium Stellariumteam.Stellarium strawberryperlcomproject.StrawberryPerl GeneralWorkings.StreamlabsOBS GeneralWorkings.StreamlabsOBS GeneralWorkings.StreamlabsOBS Streamlink.Streamlink SebastianMeyer.StreamlinkTwitchGUI JanHovancik.Stretchly JanHovancik.Stretchly JanHovancik.Stretchly Stride.Stride BrickLink.Studio SublimeHQ.SublimeMerge SublimeHQ.SublimeText3 KrzysztofKowalczyk.SumatraPDF SuperColliderCommunity.SuperCollider SuperTuxKart.SuperTuxKart3Dopensourcearcaderacerwithavarietycharacterstracksandmodestoplay Microsoft.SurfaceDuoEmulator SyncTrayzor.SyncTrayzor MisterGroup.SystemExplorer OpenVPNTechnologies.TAPWindows erengy.Taiga Tailscale.Tailscale Tailscale.TailscaleIPN TaiseiProject.TaiseiProject Taskcade.Taskade Taskcade.Taskade TreasureData.Tdagent TreasureData.Tdagent BenitovanderZander.TeXstudioTeXstudioisafullyfeaturedLaTeXeditor TeXUsersGroup.TeXworks TeamSpeakSystems.TeamSpeak3Client TechPowerUp.TechPowerUpGPUZ TelegramFZ.TelegramDesktop TelegramFZ.TelegramDesktop TelegramFZ.TelegramDesktop TelegramFZ.TelegramDesktop TelegramFZ.TelegramDesktop TelegramFZ.TelegramDesktop TelegramFZ.TelegramDesktop TelegramFZ.TelegramDesktop TelegramFZ.TelegramDesktop TeraTermProject.TeraTerm EugenePankov.Terminus EugenePankov.Terminus EugenePankov.Terminus CompuPhase.Termite TesseractOCRcommunity.TesseractOCRopensourceOCRengine Texmaker.Texmaker Texnomic.TexnomicSecureDNSTerminal RaMMicHaeL.Textify Appestcom.TickTick GlavSoft.TightVNC TikzEdt.TikzEdt mapeditororg.Tiled Nadeo.TmNationsForever Toggl.TogglDesktop Toggl.TogglTrack TortoiseGit.TortoiseGit TortoiseSVN.TortoiseSVN TortoiseSVN.TortoiseSVN TranslucentTBOpenSourceDevelopers.TranslucentTB TransmissionProject.Transmission YurySidorovTransmissionRemoteGUIworkinggroup.TransmissionRemoteGUI BinaryFortressSoftware.TrayStatus BinaryFortressSoftware.TrayStatus JAMSoftware.TreeSizeFree JAMSoftware.TreeSize Trelbyorg.Trelby CeruleanStudios.Trillian TunnelBear.TunnelBear NewBreedSoftware.TuxPaint InspectElement.Tweeten XanderFrangos.TwinkleTray TwitchInteractive.Twitch GDATACyberDefense.TypeRefHasher SafelyRemovecom.USBSafelyRemove DrewNaylor.UXLLauncher DrewNaylor.UXLLauncher Ultimaker.UltimakerCura Ultimaker.UltimakerCura Ultimaker.UltimakerCura uvncbvba.UltraVnc ReasonSoftware.Unchecky UnifiedIntents.UnifiedRemote UnityTechnologies.UnityHub Microsoft.UpdateforKB2504637 Microsoft.UpdateforMicrosoftVisualStudio2015KB3095681 Ubisoft.Uplay UMEZAWATakeshi.UtVideoCodecSuite VCV.VCVRack VideoLAN.VLCmediaplayer VideoLAN.VLCmediaplayer VideoLAN.VLCmediaplayer VMware.VMwareHorizonClient VMware.VMwarePlayer VMware.VMwareWorkstation RealVNC.VNCServer RealVNC.VNCViewer Microsoft.VSCodium Microsoft.VSCodium marhauserssourceforgenet.VcXsrv BramMoolenaaretal.Vim VirtManagerProject.VirtViewer VivaldiTechnologies.Vivaldi BlackTreeGaming.Vortex VoyagerX.Vrew LunarG.VulkanSDK WarzoneProject.Warzone Waterfox.WaterfoxCurrent Waterfox.WaterfoxCurrent Waterfox.WaterfoxCurrent Devolutions.WaykNow Buds.WeakAurasCompanion Buds.WeakAurasCompanion Buds.WeakAurasCompanion Buds.WeakAurasCompanion MachineLearningGroupUniversityofWaikatoHamiltonNZ.Weka ImageWriterDevelopers.Win32DiskImager SamHocevar.WinCompose TimothyJohnson.WinDynamicDesktop TimothyJohnson.WinDynamicDesktop TimothyJohnson.WinDynamicDesktop Navimatics.WinFsp2020 HTTrack.WinHTTrackWebsiteCopier ThingamahoochieSoftware.WinMerge winrar.WinRAR winrar.WinRAR MartinPrikryl.WinSCP Corel.WinZip Winamp.Winamp Microsoft.Windows10UpdateAssistant Microsoft.WindowsAdminCenter Microsoft.WindowsAssessmentandDeploymentKitWindows10 Microsoft.WindowsAssessmentandDeploymentKitWindowsPreinstallationEnvironmentAddonsWindows10 Microsoft.WindowsDriverKitWindows DynastreamInnovations.WindowsDriverPackageDynastreamInnovationsANTLibUSBDrivers SiliconLabsSoftware.WindowsDriverPackageSiliconLabsSoftwareUSB Microsoft.WindowsSDKAddOn Microsoft.WindowsSoftwareDevelopmentKitWindows Microsoft.WindowsSoftwareDevelopmentKitWindows Microsoft.WindowsSoftwareDevelopmentKitWindows WireGuard.WireGuard TheWiresharkdevelopercommunitywwwwiresharkorg.Wireshark TheWiresharkdevelopercommunitywwwwiresharkorg.Wireshark TheWiresharkdevelopercommunitywwwwiresharkorg.Wireshark TheWiresharkdevelopercommunitywwwwiresharkorg.Wireshark AntibodySoftware.WizFile AntibodySoftware.WizKey AntibodySoftware.WizMouse AntibodySoftware.WizTree AntibodySoftware.WizTree Automattic.WordPresscom Automattic.WordPresscom Automattic.WordPresscom Automattic.WordPresscom RobCaelersRaymondPenners.Workrave Writage.Writage X2GoProject.X2GoClientforWindows Bitnami.XAMPP ChristianHohnstaedt.XCA TheTBOOXOpenSourceGroup.XMakebuildutility XMind.XMind GougeletPierree.XnView GougeletPierree.XnViewMP YarnContributors.Yarn BeijingYinxiangBijiTechnologies.YinxiangBijiv BeijingYinxiangBijiTechnologies.YinxiangBijiv BeijingYinxiangBijiTechnologies.YinxiangBijiv BeijingYinxiangBijiTechnologies.YinxiangBijiv BeijingYinxiangBijiTechnologies.YinxiangBijiv AdlerLuiz.YouTubeMusicDesktopApp Yubico.YubiKeyManager Zentimocom.ZentimoPRO ZeroTier.ZeroTierOne HendrikErz.Zettlr RobinStuartBogDanVatra.Zint Zoom.Zoom Zoom.ZoomOutlookPlugin CorporationforDigitalScholarship.Zotero KandraLabs.Zulip SangomaTechnologies.Zulu ZygorGuides.ZygorClientUninstaller Balena.balenaEtcher Balena.balenaEtcher Balena.balenaEtcher Balena.balenaEtcher Balena.balenaEtcher Balena.balenaEtcher Balena.balenaEtcher Balena.balenaEtcher NathanielJohns.beatdrop ClementTsang.bottom butterflowuigithub.butterflowui KovidGoyal.calibre ElliottZheng.copytranslator thedarktableproject.darktable dnGrepCommunityContributors.dnGREP JGraph.drawio HardcodedSoftware.dupeGuru eMClient.eMClient Ebbflowio.ebbflow fluxSoftware.flux KurataSayuri.ffftp wereturtle.ghostwriter gnuplotdevelopmentteam.gnuplotpatchlevel8 StefansTools.grepWin DrewNaylor.guinget DrewNaylor.guinget DrewNaylor.guinget eVenture.hidemeVPN eVenture.hidemeVPN PurpleI2P.i2pd VantageLinguistics.iSEEKAnswerWorksEnglishRuntime Apple.iTunes KDE.kdenlive KDE.kdiff3 NextGenerationSoftware.mRemoteNG MaximaTeam.maxima FrankSkare.mpvnet 720kb.ndm xiles.nexusfont ownCloud.ownCloud EasternGraphics.pConplannerPRO LaurentPRendeCotret.pandocplot ThepgAdminDevelopmentTeam.pgAdmin4 ThepgAdminDevelopmentTeam.pgAdmin4 ThepgAdminDevelopmentTeam.pgAdmin4 ThepgAdminDevelopmentTeam.pgAdmin4 TheqBittorrentproject.qBittorrent TheqBittorrentproject.qBittorrent TheqBittorrentproject.qBittorrent remoteit.remoteit remoteit.remoteit Lightbend.sbt ScilabEnterprises.scilab TheSqlectronTeam.sqlectron JanHovancik.stretchly OliverSchwendener.ueli OliverSchwendener.ueli OliverSchwendener.ueli Humanity.xmoto Humanity.xmoto Microsoft.微软设备健康助手 Alipaycom.支付宝安全控件 百度在线网络技术有限公司.百度网盘 腾讯科技有限公司.腾讯QQ ================================================ FILE: src/AppInstallerCLITests/TestData/Shadow/V1_5/ManifestV1_5-Shadow-DefaultLocale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-US PackageName: MSIX SDK License: MIT License ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Publisher: Microsoft ManifestType: defaultLocale ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Shadow/V1_5/ManifestV1_5-Shadow-Installer.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 InstallerLocale: en-US Installers: - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 ProductCode: "{Bar}" InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Interactive: /interactive Log: /log= InstallLocation: /dir= Upgrade: /upgrade ManifestType: installer ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Shadow/V1_5/ManifestV1_5-Shadow-Locale.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: en-GB Description: The MSIX SDK project is an effort to enable developers UK ManifestType: locale ManifestVersion: 1.5.0 ================================================ FILE: src/AppInstallerCLITests/TestData/Shadow/V1_5/ManifestV1_5-Shadow-Locale2.yaml ================================================ # yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.5.0.schema.json PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 PackageLocale: es-MX Description: The MSIX SDK project is an effort to enable developers MX ManifestType: locale ManifestVersion: 1.5.0 Icons: - IconUrl: https://localeTestIcon-es-MX IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: '4444444444444444444444444444444444444444444444444444444444444444' ================================================ FILE: src/AppInstallerCLITests/TestData/Shadow/V1_5/ManifestV1_5-Shadow-Shadow.yaml ================================================ PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 ManifestType: shadow ManifestVersion: 1.5.0 PackageLocale: en-US Icons: - IconUrl: https://shadowIcon-default IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 1111111111111111111111111111111111111111111111111111111111111111 Localization: - PackageLocale: en-gb Icons: - IconUrl: https://shadowIcon-en-GB IconFileType: png IconResolution: 32x32 IconTheme: light IconSha256: 2222222222222222222222222222222222222222222222222222222222222222 - PackageLocale: fr-FR Icons: - IconUrl: https://shadowIcon-fr-FR IconFileType: jpeg IconResolution: 20x20 IconTheme: dark IconSha256: 3333333333333333333333333333333333333333333333333333333333333333 ================================================ FILE: src/AppInstallerCLITests/TestData/Shadow/V1_5/ManifestV1_5-Shadow-Shadow2.yaml ================================================ PackageIdentifier: microsoft.msixsdk PackageVersion: 1.7.32 ManifestType: shadow ManifestVersion: 1.5.0 PackageLocale: en-US Icons: - IconUrl: https://shadowIcon-default2 IconFileType: ico IconResolution: custom IconTheme: default IconSha256: 1111111111111111111111111111111111111111111111111111111111111111 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe.yaml ================================================ # Same content with InstallFlowTest_Exe.yaml but with higher version Id: AppInstallerCliTest.TestExeInstaller Version: 2.0.0.0 Name: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITestExe License: Test Switches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Update: /update Installers: - Arch: x64 Url: https://ThisIsNotUsed InstallerType: exe Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_ExeDependencies.yaml ================================================ # Same content with Installer_Exe_Dependencies but with higher version # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.0.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeInstaller.Dependencies PackageVersion: 2.0.0.0 PackageName: AppInstaller Test Installer PackageLocale: en-US Publisher: Microsoft Corporation ShortDescription: Upgrade exe installer with dependencies Moniker: AICLITestExe License: Test InstallerSwitches: Custom: /custom SilentWithProgress: /silentwithprogress Silent: /silence Upgrade: /upgrade Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B Dependencies: WindowsFeatures: - PreviewIIS WindowsLibraries: - Preview VC Runtime ManifestType: singleton ManifestVersion: 1.0.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_2.yaml ================================================ # Same content with UpdateFlowTest_Exe.yaml but with higher version Id: AppInstallerCliTest.TestExeInstaller Version: 3.0.0.0 Name: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITestExe License: Test Switches: Custom: /custom /ver3.0.0.0 SilentWithProgress: /silentwithprogress Silent: /silence Update: /update Installers: - Arch: x64 Url: https://ThisIsNotUsed InstallerType: exe Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_2_LicenseAgreement.yaml ================================================ # Similar content to UpdateFlowTest_Exe_2.yaml but with Agreements # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeInstaller PackageVersion: 3.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: AppInstaller Test Installer Agreements: - AgreementLabel: Agreement for EXE Agreement: This is the agreement for the EXE installer. InstallerSwitches: Custom: /custom /ver3.0.0.0 SilentWithProgress: /silentwithprogress Silent: /silence Update: /update Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_ARPInstallerType.yaml ================================================ # Similar content to UpdateFlowTest_Exe.yaml, but with an AppsAndFeaturesEntry specifying installer type # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeInstaller PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test ShortDescription: AppInstaller Test Installer AppsAndFeaturesEntries: - InstallerType: msix InstallerSwitches: Custom: /custom /ver2.0.0.0 SilentWithProgress: /silentwithprogress Silent: /silence Update: /update Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Exe_UnsupportedArgs.yaml ================================================ # Same content with UpdateFlowTest_Exe.yaml but with higher version and UnsupportedArguments # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerCliTest.TestExeInstaller PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Installer Publisher: Microsoft Corporation AppMoniker: AICLITestExe License: Test Switches: Custom: /custom /ver3.0.0.0 SilentWithProgress: /silentwithprogress Silent: /silence Update: /update UnsupportedArguments: - log - location ShortDescription: AppInstaller Test Exe Installer Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_ExpectedReturnCodes.yaml ================================================ # Same content with InstallFlowTest_ExpectedReturnCodes.yaml but with higher version # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerCliTest.ExpectedReturnCodes PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: TestExeInstallerWithExpectedReturnCodes ShortDescription: AppInstaller Test Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ExpectedReturnCodes: - InstallerReturnCode: 1 ReturnResponse: packageInUse - InstallerReturnCode: 2 ReturnResponse: installInProgress - InstallerReturnCode: 3 ReturnResponse: fileInUse - InstallerReturnCode: 4 ReturnResponse: missingDependency - InstallerReturnCode: 5 ReturnResponse: diskFull - InstallerReturnCode: 6 ReturnResponse: insufficientMemory - InstallerReturnCode: 7 ReturnResponse: noNetwork - InstallerReturnCode: 8 ReturnResponse: contactSupport ReturnResponseUrl: https://TestReturnResponseUrl - InstallerReturnCode: 9 ReturnResponse: rebootRequiredToFinish - InstallerReturnCode: 10 ReturnResponse: rebootRequiredForInstall - InstallerReturnCode: 11 ReturnResponse: rebootInitiated - InstallerReturnCode: 12 ReturnResponse: cancelledByUser - InstallerReturnCode: 13 ReturnResponse: alreadyInstalled - InstallerReturnCode: 14 ReturnResponse: downgrade - InstallerReturnCode: 15 ReturnResponse: blockedByPolicy ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Msix.yaml ================================================ # Same content with InstallFlowTest_Msix_StreamingFlow.yaml but with higher version Id: AppInstallerCliTest.TestMsixInstaller Version: 2.0.0.0 Name: AppInstaller Test MSIX Installer Publisher: Microsoft Corporation AppMoniker: AICLITestMsix License: Test Installers: - Arch: x64 Url: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea SignatureSha256: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07 PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe ManifestVersion: 0.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Msix_LicenseAgreement.yaml ================================================ # Similar content to UpdateFlowTest_Msix.yaml but with Agreements # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.1.0.schema.json PackageIdentifier: AppInstallerCliTest.TestMsixInstaller PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test MSIX Installer Publisher: Microsoft Corporation Moniker: AICLITestExe License: Test Agreements: - AgreementLabel: Agreement for MSIX Agreement: This is the agreement for the MSIX installer. ShortDescription: AppInstaller Test MSIX Installer Installers: - Architecture: x64 InstallerUrl: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix InstallerSha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea SignatureSha256: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07 PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe ManifestType: singleton ManifestVersion: 1.1.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Portable.yaml ================================================ # Same content with InstallFlowTest_Portable.yaml but with higher version # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.2.0.schema.json PackageIdentifier: AppInstallerCliTest.TestPortableInstaller PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Portable Exe Publisher: Microsoft Corporation AppMoniker: AICLITestPortable ProductCode: AppInstallerCliTest.TestPortableInstaller__TestSource License: Test ShortDescription: AppInstaller Test Portable Exe Installers: - Architecture: x64 InstallerUrl: https://ThisIsNotUsed InstallerType: portable InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ManifestType: singleton ManifestVersion: 1.2.0 ================================================ FILE: src/AppInstallerCLITests/TestData/UpdateFlowTest_Zip_Exe.yaml ================================================ # Same content with InstallFlowTest_ZipWithExe.yaml but with higher version # yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.4.0.schema.json PackageIdentifier: AppInstallerCliTest.TestZipInstaller PackageVersion: 2.0.0.0 PackageLocale: en-US PackageName: AppInstaller Test Zip Installer ShortDescription: AppInstaller Test Zip Installer with exe Publisher: Microsoft Corporation Moniker: AICLITestZip License: Test Installers: - Architecture: x86 InstallerUrl: https://ThisIsNotUsed InstallerType: zip InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B NestedInstallerType: exe NestedInstallerFiles: - RelativeFilePath: relativeFilePath InstallerSwitches: Custom: /custom /ver2.0.0.0 SilentWithProgress: /silentwithprogress Silent: /silence Update: /update ManifestType: singleton ManifestVersion: 1.4.0 ================================================ FILE: src/AppInstallerCLITests/TestHooks.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "TestSettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef AICLI_DISABLE_TEST_HOOKS static_assert(false, "Test hooks have been disabled"); #endif namespace AppInstaller { // Don't forget to clear the overrides after use! // A good way is to create a helper struct that cleans when destroyed namespace Runtime { void TestHook_SetPathOverride(PathName target, const std::filesystem::path& path); void TestHook_SetPathOverride(PathName target, const Filesystem::PathDetails& details); void TestHook_ClearPathOverrides(); } namespace Repository { void TestHook_SetSourceFactoryOverride(const std::string& type, std::function()>&& factory); void TestHook_ClearSourceFactoryOverrides(); void TestHook_SetExtractIconFromArpEntryResult_Override(std::vector* result); } namespace Repository::Microsoft { void TestHook_SetPinningIndex_Override(std::optional&& indexPath); using GetARPKeyFunc = std::function; void SetGetARPKeyOverride(GetARPKeyFunc value); using GetFontRegistryRootFunc = std::function; void TestHook_SetGetFontRegistryRootFunc(GetFontRegistryRootFunc value); } namespace Logging { void TestHook_SetTelemetryOverride(std::shared_ptr ttl); } namespace Settings { void SetUserSettingsOverride(UserSettings* value); void SetExperimentalFeatureOverride(const std::map* override); } namespace Filesystem { void TestHook_SetCreateSymlinkResult_Override(bool* status); } namespace Archive { void TestHook_SetScanArchiveResult_Override(bool* status); } namespace CLI::Workflow { void TestHook_SetEnableWindowsFeatureResult_Override(std::optional&& result); void TestHook_SetDoesWindowsFeatureExistResult_Override(std::optional&& result); void TestHook_SetExtractArchiveWithTarResult_Override(std::optional&& result); } namespace Reboot { void TestHook_SetInitiateRebootResult_Override(bool* status); void TestHook_SetRegisterForRestartResult_Override(bool* status); } namespace Authentication { void TestHook_SetAuthenticationResult_Override(Authentication::AuthenticationResult* authResult); } namespace MSStore::TestHooks { void SetDisplayCatalogHttpPipelineStage_Override(std::shared_ptr value); void SetSfsClientAppContents_Override(std::function(std::string_view)>* value); void SetLicensingHttpPipelineStage_Override(std::shared_ptr value); } namespace Utility::TestHooks { void SetDownloadResult_Function_Override(std::function info)>* value); } } namespace TestHook { struct SetCreateSymlinkResult_Override { SetCreateSymlinkResult_Override(bool status) : m_status(status) { AppInstaller::Filesystem::TestHook_SetCreateSymlinkResult_Override(&m_status); } ~SetCreateSymlinkResult_Override() { AppInstaller::Filesystem::TestHook_SetCreateSymlinkResult_Override(nullptr); } private: bool m_status; }; struct SetScanArchiveResult_Override { SetScanArchiveResult_Override(bool status) : m_status(status) { AppInstaller::Archive::TestHook_SetScanArchiveResult_Override(&m_status); } ~SetScanArchiveResult_Override() { AppInstaller::Archive::TestHook_SetScanArchiveResult_Override(nullptr); } private: bool m_status; }; struct SetPinningIndex_Override { SetPinningIndex_Override(const std::filesystem::path& indexPath) { AppInstaller::Repository::Microsoft::TestHook_SetPinningIndex_Override(indexPath); } ~SetPinningIndex_Override() { AppInstaller::Repository::Microsoft::TestHook_SetPinningIndex_Override({}); } }; struct SetExtractIconFromArpEntryResult_Override { SetExtractIconFromArpEntryResult_Override(std::vector extractedIcons) : m_extractedIcons(std::move(extractedIcons)) { AppInstaller::Repository::TestHook_SetExtractIconFromArpEntryResult_Override(&m_extractedIcons); } ~SetExtractIconFromArpEntryResult_Override() { AppInstaller::Repository::TestHook_SetExtractIconFromArpEntryResult_Override(nullptr); } private: std::vector m_extractedIcons; }; struct SetEnableWindowsFeatureResult_Override { SetEnableWindowsFeatureResult_Override(DWORD result) { AppInstaller::CLI::Workflow::TestHook_SetEnableWindowsFeatureResult_Override(result); } ~SetEnableWindowsFeatureResult_Override() { AppInstaller::CLI::Workflow::TestHook_SetEnableWindowsFeatureResult_Override({}); } }; struct SetDoesWindowsFeatureExistResult_Override { SetDoesWindowsFeatureExistResult_Override(DWORD result) { AppInstaller::CLI::Workflow::TestHook_SetDoesWindowsFeatureExistResult_Override(result); } ~SetDoesWindowsFeatureExistResult_Override() { AppInstaller::CLI::Workflow::TestHook_SetDoesWindowsFeatureExistResult_Override({}); } }; struct SetExtractArchiveWithTarResult_Override { SetExtractArchiveWithTarResult_Override(DWORD result) { AppInstaller::CLI::Workflow::TestHook_SetExtractArchiveWithTarResult_Override(result); } ~SetExtractArchiveWithTarResult_Override() { AppInstaller::CLI::Workflow::TestHook_SetExtractArchiveWithTarResult_Override({}); } }; struct SetInitiateRebootResult_Override { SetInitiateRebootResult_Override(bool status) : m_status(status) { AppInstaller::Reboot::TestHook_SetInitiateRebootResult_Override(&m_status); } ~SetInitiateRebootResult_Override() { AppInstaller::Reboot::TestHook_SetInitiateRebootResult_Override(nullptr); } private: bool m_status; }; struct SetGetARPKey_Override { SetGetARPKey_Override(std::function function) { AppInstaller::Repository::Microsoft::SetGetARPKeyOverride(function); } ~SetGetARPKey_Override() { AppInstaller::Repository::Microsoft::SetGetARPKeyOverride({}); } private: }; struct SetRegisterForRestartResult_Override { SetRegisterForRestartResult_Override(bool status) : m_status(status) { AppInstaller::Reboot::TestHook_SetRegisterForRestartResult_Override(&m_status); } ~SetRegisterForRestartResult_Override() { AppInstaller::Reboot::TestHook_SetRegisterForRestartResult_Override(nullptr); } private: bool m_status; }; struct SetAuthenticationResult_Override { SetAuthenticationResult_Override(AppInstaller::Authentication::AuthenticationResult authResult) : m_authResult(authResult) { AppInstaller::Authentication::TestHook_SetAuthenticationResult_Override(&m_authResult); } ~SetAuthenticationResult_Override() { AppInstaller::Authentication::TestHook_SetAuthenticationResult_Override(nullptr); } private: AppInstaller::Authentication::AuthenticationResult m_authResult; }; struct SetDisplayCatalogHttpPipelineStage_Override { SetDisplayCatalogHttpPipelineStage_Override(std::shared_ptr value) { AppInstaller::MSStore::TestHooks::SetDisplayCatalogHttpPipelineStage_Override(value); } ~SetDisplayCatalogHttpPipelineStage_Override() { AppInstaller::MSStore::TestHooks::SetDisplayCatalogHttpPipelineStage_Override(nullptr); } }; struct SetSfsClientAppContents_Override { SetSfsClientAppContents_Override(std::function(std::string_view)> value) : m_appContentsFunction(std::move(value)) { AppInstaller::MSStore::TestHooks::SetSfsClientAppContents_Override(&m_appContentsFunction); } ~SetSfsClientAppContents_Override() { AppInstaller::MSStore::TestHooks::SetSfsClientAppContents_Override(nullptr); } private: std::function(std::string_view)> m_appContentsFunction; }; struct SetLicensingHttpPipelineStage_Override { SetLicensingHttpPipelineStage_Override(std::shared_ptr value) { AppInstaller::MSStore::TestHooks::SetLicensingHttpPipelineStage_Override(value); } ~SetLicensingHttpPipelineStage_Override() { AppInstaller::MSStore::TestHooks::SetLicensingHttpPipelineStage_Override(nullptr); } }; struct SetDownloadResult_Function_Override { SetDownloadResult_Function_Override(std::function info)> value) : m_downloadFunction(std::move(value)) { AppInstaller::Utility::TestHooks::SetDownloadResult_Function_Override(&m_downloadFunction); } ~SetDownloadResult_Function_Override() { AppInstaller::Utility::TestHooks::SetDownloadResult_Function_Override(nullptr); } private: std::function info)> m_downloadFunction; }; struct SetGetFontRegistryRoot_Override { SetGetFontRegistryRoot_Override(std::function function) { AppInstaller::Repository::Microsoft::TestHook_SetGetFontRegistryRootFunc(function); } ~SetGetFontRegistryRoot_Override() { AppInstaller::Repository::Microsoft::TestHook_SetGetFontRegistryRootFunc({}); } private: }; struct SetUserSettings_Override { SetUserSettings_Override(AppInstaller::Settings::UserSettings& settings) { AppInstaller::Settings::SetUserSettingsOverride(&settings); } ~SetUserSettings_Override() { AppInstaller::Settings::SetUserSettingsOverride(nullptr); } }; struct SetSingleExperimentalFeature_Override { SetSingleExperimentalFeature_Override(AppInstaller::Settings::ExperimentalFeature::Feature feature) { m_overrides[feature] = true; AppInstaller::Settings::SetExperimentalFeatureOverride(&m_overrides); } ~SetSingleExperimentalFeature_Override() { AppInstaller::Settings::SetExperimentalFeatureOverride(nullptr); } private: std::map m_overrides; }; } ================================================ FILE: src/AppInstallerCLITests/TestRestRequestHandler.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestRestRequestHandler.h" #include #include std::shared_ptr GetTestRestRequestHandler( const web::http::status_code statusCode, const utility::string_t& sampleResponseString, const utility::string_t& mimeType) { return std::make_shared([statusCode, sampleResponseString, mimeType](web::http::http_request) -> pplx::task { web::http::http_response response; if (sampleResponseString.empty()) { response.set_body(utf16string{}); } else { response.set_body(web::json::value::parse(sampleResponseString)); } response.headers().set_content_type(mimeType); response.headers().set_cache_control(L"no-store"); response.set_status_code(statusCode); return pplx::task_from_result(response); }); } std::shared_ptr GetTestRestRequestHandler( std::function handler) { return std::make_shared([handler = std::move(handler)](web::http::http_request request) -> pplx::task { web::http::http_response response; response.set_body(utf16string{}); response.headers().set_content_type(web::http::details::mime_types::application_json); response.headers().set_cache_control(L"no-store"); response.set_status_code(handler(request)); return pplx::task_from_result(response); }); } std::shared_ptr GetHeaderVerificationHandler( const web::http::status_code statusCode, const utility::string_t& sampleResponseString, const std::pair& header, web::http::status_code statusCodeOnFailure) { return std::make_shared([statusCode, sampleResponseString, header, statusCodeOnFailure](web::http::http_request request) -> pplx::task { web::http::http_response response; auto& headers = request.headers(); if (!headers.has(header.first) || (utility::conversions::to_utf8string(header.second).compare(utility::conversions::to_utf8string(headers[header.first]))) != 0) { response.set_body(utf16string{ L"Expected header not found" }); response.set_status_code(statusCodeOnFailure); return pplx::task_from_result(response); } if (!sampleResponseString.empty()) { response.set_body(web::json::value::parse(sampleResponseString)); } response.headers().set_content_type(web::http::details::mime_types::application_json); response.headers().set_cache_control(L"no-store"); response.set_status_code(statusCode); return pplx::task_from_result(response); }); } ================================================ FILE: src/AppInstallerCLITests/TestRestRequestHandler.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include class TestRestRequestHandler : public web::http::http_pipeline_stage { public: TestRestRequestHandler(const std::function(web::http::http_request request)>& handler) : m_handler(handler) {} virtual pplx::task propagate(web::http::http_request request) { return m_handler(request); } private: std::function(web::http::http_request request)> m_handler; }; std::shared_ptr GetTestRestRequestHandler( const web::http::status_code statusCode, const utility::string_t& sampleResponseString = {}, const utility::string_t& mimeType = web::http::details::mime_types::application_json); std::shared_ptr GetTestRestRequestHandler( std::function handler); std::shared_ptr GetHeaderVerificationHandler( const web::http::status_code statusCode, const utility::string_t& sampleResponseString, const std::pair& header, web::http::status_code statusCodeOnFailure = web::http::status_codes::BadRequest); ================================================ FILE: src/AppInstallerCLITests/TestSettings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSettings.h" #include using namespace AppInstaller::Settings; namespace TestCommon { namespace { void DeleteUserSettingsFilesInternal() { auto settingsPath = UserSettings::SettingsFilePath(); if (std::filesystem::exists(settingsPath)) { std::filesystem::remove(settingsPath); } auto settingsBackupPath = GetPathTo(Stream::BackupUserSettings); if (std::filesystem::exists(settingsBackupPath)) { std::filesystem::remove(settingsBackupPath); } } } void SetSetting(const AppInstaller::Settings::StreamDefinition& stream, std::string_view value) { REQUIRE(Stream{ stream }.Set(value)); } void RemoveSetting(const AppInstaller::Settings::StreamDefinition& stream) { Stream{ stream }.Remove(); } std::filesystem::path GetPathTo(const AppInstaller::Settings::StreamDefinition& stream) { return Stream{ stream }.GetPath(); } UserSettingsFileGuard::UserSettingsFileGuard() { DeleteUserSettingsFilesInternal(); } UserSettingsFileGuard::~UserSettingsFileGuard() { DeleteUserSettingsFilesInternal(); } [[nodiscard]] UserSettingsFileGuard DeleteUserSettingsFiles() { return {}; } GroupPolicyTestOverride::GroupPolicyTestOverride(const AppInstaller::Registry::Key& key) : GroupPolicy(key) { GroupPolicy::OverrideInstance(this); } GroupPolicyTestOverride::~GroupPolicyTestOverride() { GroupPolicy::ResetInstance(); } void GroupPolicyTestOverride::SetState(TogglePolicy::Policy policy, PolicyState state) { m_toggles[policy] = state; } } ================================================ FILE: src/AppInstallerCLITests/TestSettings.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "TestCommon.h" #include #include #include #include namespace TestCommon { // Repeat the policy values here so we can catch unintended changes in the source. const std::wstring WinGetPolicyValueName = L"EnableAppInstaller"; const std::wstring WinGetSettingsPolicyValueName = L"EnableSettings"; const std::wstring ExperimentalFeaturesPolicyValueName = L"EnableExperimentalFeatures"; const std::wstring LocalManifestsPolicyValueName = L"EnableLocalManifestFiles"; const std::wstring EnableHashOverridePolicyValueName = L"EnableHashOverride"; const std::wstring EnableLocalArchiveMalwareScanOverridePolicyValueName = L"EnableLocalArchiveMalwareScanOverride"; const std::wstring DefaultSourcePolicyValueName = L"EnableDefaultSource"; const std::wstring MSStoreSourcePolicyValueName = L"EnableMicrosoftStoreSource"; const std::wstring FontSourcePolicyValueName = L"EnableFontSource"; const std::wstring AdditionalSourcesPolicyValueName = L"EnableAdditionalSources"; const std::wstring AllowedSourcesPolicyValueName = L"EnableAllowedSources"; const std::wstring BypassCertificatePinningForMicrosoftStoreValueName = L"EnableBypassCertificatePinningForMicrosoftStore"; const std::wstring EnableWindowsPackageManagerCommandLineInterfaces = L"EnableWindowsPackageManagerCommandLineInterfaces"; const std::wstring ConfigurationPolicyValueName = L"EnableWindowsPackageManagerConfiguration"; const std::wstring ProxyCommandLineOptionsPolicyValueName = L"EnableWindowsPackageManagerProxyCommandLineOptions"; const std::wstring McpServerValueName = L"EnableWindowsPackageManagerMcpServer"; const std::wstring SourceUpdateIntervalPolicyValueName = L"SourceAutoUpdateInterval"; const std::wstring SourceUpdateIntervalPolicyOldValueName = L"SourceAutoUpdateIntervalInMinutes"; const std::wstring AdditionalSourcesPolicyKeyName = L"AdditionalSources"; const std::wstring AllowedSourcesPolicyKeyName = L"AllowedSources"; void SetSetting(const AppInstaller::Settings::StreamDefinition& stream, std::string_view value); void RemoveSetting(const AppInstaller::Settings::StreamDefinition& stream); std::filesystem::path GetPathTo(const AppInstaller::Settings::StreamDefinition& stream); // This type removes the settings file on creation and destruction to ensure that a test that modifies them can do so cleanly. struct UserSettingsFileGuard { UserSettingsFileGuard(); ~UserSettingsFileGuard(); }; [[nodiscard]] UserSettingsFileGuard DeleteUserSettingsFiles(); struct UserSettingsTest : AppInstaller::Settings::UserSettings { }; struct GroupPolicyTestOverride : AppInstaller::Settings::GroupPolicy { GroupPolicyTestOverride() : GroupPolicyTestOverride(RegCreateVolatileTestRoot().get()) {} GroupPolicyTestOverride(const AppInstaller::Registry::Key& key); ~GroupPolicyTestOverride(); template void SetValue(const ValueType

& value) { m_values.Add

(value); } template void SetValue(ValueType

&&value) { m_values.Add

(std::move(value)); } void SetState(AppInstaller::Settings::TogglePolicy::Policy policy, AppInstaller::Settings::PolicyState state); }; // Matcher that lets us verify GroupPolicyExceptions. struct GroupPolicyExceptionMatcher : public Catch::Matchers::MatcherBase { GroupPolicyExceptionMatcher(AppInstaller::Settings::TogglePolicy::Policy policy) : m_expectedPolicy(policy) {} bool match(const AppInstaller::Settings::GroupPolicyException& e) const override { return e.Policy() == m_expectedPolicy; } std::string describe() const override { std::ostringstream result; result << "has policy == " << m_expectedPolicy; return result.str(); } private: AppInstaller::Settings::TogglePolicy::Policy m_expectedPolicy; }; #define REQUIRE_POLICY_EXCEPTION(_expr_, _policy_) REQUIRE_THROWS_MATCHES(_expr_, AppInstaller::Settings::GroupPolicyException, TestCommon::GroupPolicyExceptionMatcher(_policy_)) } ================================================ FILE: src/AppInstallerCLITests/TestSource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSource.h" using namespace AppInstaller; using namespace AppInstaller::Repository; namespace TestCommon { namespace { size_t GetNextTestPackageId() { static std::atomic_size_t packageId(0); return ++packageId; } TestSource* GetTestSourceFromWeakPtr(const std::weak_ptr& weakSource) { if (auto source = weakSource.lock()) { if (auto testSource = const_cast(source.get())->CastTo(TestSource::SourceType)) { return reinterpret_cast(testSource); } } return nullptr; } } TestPackageVersion::TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source) : VersionManifest(manifest), Metadata(std::move(installationMetadata)), Source(source) {} TestPackageVersion::TestPackageVersion(const Manifest& manifest, std::weak_ptr source, bool hideSystemReferenceStrings) : VersionManifest(manifest), Source(source), HideSystemReferenceStrings(hideSystemReferenceStrings) {} TestPackageVersion::LocIndString TestPackageVersion::GetProperty(PackageVersionProperty property) const { switch (property) { case PackageVersionProperty::Id: return LocIndString{ VersionManifest.Id }; case PackageVersionProperty::Name: return LocIndString{ VersionManifest.DefaultLocalization.Get() }; case PackageVersionProperty::Version: return LocIndString{ VersionManifest.Version }; case PackageVersionProperty::Channel: return LocIndString{ VersionManifest.Channel }; case PackageVersionProperty::SourceIdentifier: return LocIndString{ Source.lock()->GetIdentifier() }; case PackageVersionProperty::Publisher: return LocIndString{ VersionManifest.DefaultLocalization.Get() }; case PackageVersionProperty::ArpMinVersion: return LocIndString{ VersionManifest.GetArpVersionRange().IsEmpty() ? "" : VersionManifest.GetArpVersionRange().GetMinVersion().ToString() }; case PackageVersionProperty::ArpMaxVersion: return LocIndString{ VersionManifest.GetArpVersionRange().IsEmpty() ? "" : VersionManifest.GetArpVersionRange().GetMaxVersion().ToString() }; case PackageVersionProperty::Moniker: return LocIndString{ VersionManifest.Moniker }; default: return {}; } } std::vector TestPackageVersion::GetMultiProperty(PackageVersionMultiProperty property) const { std::vector result; switch (property) { case PackageVersionMultiProperty::PackageFamilyName: if (!HideSystemReferenceStrings) { for (const auto& installer : VersionManifest.Installers) { AddIfHasValueAndNotPresent(installer.PackageFamilyName, result, true); } } break; case PackageVersionMultiProperty::ProductCode: if (!HideSystemReferenceStrings) { for (const auto& installer : VersionManifest.Installers) { bool shouldFoldCaseForNonPortable = installer.EffectiveInstallerType() != AppInstaller::Manifest::InstallerTypeEnum::Portable; AddIfHasValueAndNotPresent(installer.ProductCode, result, shouldFoldCaseForNonPortable); } } break; case PackageVersionMultiProperty::Name: for (auto name : VersionManifest.GetPackageNames()) { result.emplace_back(std::move(name)); } break; case PackageVersionMultiProperty::Publisher: for (auto publisher : VersionManifest.GetPublishers()) { result.emplace_back(std::move(publisher)); } break; case PackageVersionMultiProperty::Locale: result.emplace_back(VersionManifest.DefaultLocalization.Locale); for (const auto& loc : VersionManifest.Localizations) { result.emplace_back(loc.Locale); } break; case PackageVersionMultiProperty::Command: for (auto value : VersionManifest.GetAggregatedCommands()) { result.emplace_back(std::move(value)); } break; case PackageVersionMultiProperty::Tag: for (auto value : VersionManifest.GetAggregatedTags()) { result.emplace_back(std::move(value)); } break; case PackageVersionMultiProperty::UpgradeCode: if (!HideSystemReferenceStrings) { for (const auto& installer : VersionManifest.Installers) { bool shouldFoldCaseForNonPortable = installer.EffectiveInstallerType() != AppInstaller::Manifest::InstallerTypeEnum::Portable; for (const auto& entry : installer.AppsAndFeaturesEntries) { AddIfHasValueAndNotPresent(entry.UpgradeCode, result, shouldFoldCaseForNonPortable); } } } break; } return result; } TestPackageVersion::Manifest TestPackageVersion::GetManifest() { if (auto source = GetTestSource()) { source->IncrementCountOfCallsRequiringManifestData(); } return VersionManifest; } Repository::Source TestPackageVersion::GetSource() const { return std::const_pointer_cast(Source.lock()); } TestPackageVersion::MetadataMap TestPackageVersion::GetMetadata() const { return Metadata; } void TestPackageVersion::AddIfHasValueAndNotPresent(const Utility::NormalizedString& value, std::vector& target, bool folded) { if (!value.empty()) { std::string valueString = folded ? FoldCase(value) : value; auto itr = std::find(target.begin(), target.end(), valueString); if (itr == target.end()) { target.emplace_back(std::move(valueString)); } } } TestSource* TestPackageVersion::GetTestSource() const { return GetTestSourceFromWeakPtr(Source); } TestPackage::TestPackage(const std::vector& available, std::weak_ptr source, bool hideSystemReferenceStringsOnVersion) : Source(source) { DefaultIsSameIdentity = GetNextTestPackageId(); for (const auto& manifest : available) { Versions.emplace_back(TestPackageVersion::Make(manifest, source, hideSystemReferenceStringsOnVersion)); } } TestPackage::TestPackage(const Manifest& installed, MetadataMap installationMetadata, std::weak_ptr source) : Source(source) { DefaultIsSameIdentity = GetNextTestPackageId(); Versions.emplace_back(TestPackageVersion::Make(installed, std::move(installationMetadata), source)); } TestPackage::LocIndString TestPackage::GetProperty(PackageProperty property) const { std::shared_ptr truth; if (!Versions.empty()) { truth = Versions[0]; } if (!truth) { THROW_HR(E_NOT_VALID_STATE); } switch (property) { case PackageProperty::Id: return truth->GetProperty(PackageVersionProperty::Id); case PackageProperty::Name: return truth->GetProperty(PackageVersionProperty::Name); default: return {}; } } std::vector TestPackage::GetMultiProperty(PackageMultiProperty property) const { std::vector result; PackageVersionMultiProperty mappedProperty = PackageMultiPropertyToPackageVersionMultiProperty(property); for (const auto& version : Versions) { for (auto&& string : version->GetMultiProperty(mappedProperty)) { auto itr = std::lower_bound(result.begin(), result.end(), string); if (itr == result.end() || *itr != string) { result.emplace(itr, std::move(string)); } } } return result; } std::vector TestPackage::GetVersionKeys() const { if (auto source = GetTestSource()) { source->IncrementCountOfCallsRequiringVersionData(); } std::vector result; for (const auto& version : Versions) { result.emplace_back(PackageVersionKey(version->GetSource().GetIdentifier(), version->GetProperty(PackageVersionProperty::Version).get(), version->GetProperty(PackageVersionProperty::Channel).get())); } return result; } std::shared_ptr TestPackage::GetLatestVersion() const { if (Versions.empty()) { return {}; } return Versions[0]; } std::shared_ptr TestPackage::GetVersion(const PackageVersionKey& versionKey) const { if (!versionKey.IsDefaultLatest()) { if (auto source = GetTestSource()) { source->IncrementCountOfCallsRequiringVersionData(); } } for (const auto& version : Versions) { if ((versionKey.Version.empty() || versionKey.Version == version->GetProperty(PackageVersionProperty::Version).get()) && (versionKey.Channel.empty() || versionKey.Channel == version->GetProperty(PackageVersionProperty::Channel).get())) { return version; } } return {}; } Repository::Source TestPackage::GetSource() const { return std::const_pointer_cast(Source.lock()); } bool TestPackage::IsSame(const IPackage* other) const { if (IsSameOverride) { return IsSameOverride(this, other); } const TestPackage* otherPackage = PackageCast(other); if (otherPackage && DefaultIsSameIdentity == otherPackage->DefaultIsSameIdentity) { return true; } return false; } const void* TestPackage::CastTo(IPackageType type) const { if (type == PackageType) { return this; } return nullptr; } TestSource* TestPackage::GetTestSource() const { return GetTestSourceFromWeakPtr(Source); } TestCompositePackage::TestCompositePackage(const std::vector& available, std::weak_ptr source, bool hideSystemReferenceStringsOnVersion) { if (!available.empty()) { Available.emplace_back(TestPackage::Make(available, source, hideSystemReferenceStringsOnVersion)); } } TestCompositePackage::TestCompositePackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available, std::weak_ptr source) : Installed(TestPackage::Make(installed, std::move(installationMetadata), source)) { if (!available.empty()) { Available.emplace_back(TestPackage::Make(available, source)); } } TestCompositePackage::LocIndString TestCompositePackage::GetProperty(PackageProperty property) const { std::shared_ptr truth; if (!Available.empty()) { truth = Available[0]; } else { truth = Installed; } if (!truth) { THROW_HR(E_NOT_VALID_STATE); } switch (property) { case PackageProperty::Id: return truth->GetProperty(PackageProperty::Id); case PackageProperty::Name: return truth->GetProperty(PackageProperty::Name); default: return {}; } } std::shared_ptr TestCompositePackage::GetInstalled() { return Installed; } std::vector> TestCompositePackage::GetAvailable() { return { Available.begin(), Available.end() }; } const SourceDetails& TestSource::GetDetails() const { return Details; } const std::string& TestSource::GetIdentifier() const { return Details.Identifier; } SourceInformation TestSource::GetInformation() const { return Information; } bool TestSource::QueryFeatureFlag(SourceFeatureFlag flag) const { return (QueryFeatureFlagFunction ? QueryFeatureFlagFunction(flag) : false); } SearchResult TestSource::Search(const SearchRequest& request) const { if (SearchFunction) { return SearchFunction(request); } else { return {}; } } void* TestSource::CastTo(AppInstaller::Repository::ISourceType type) { if (type == SourceType) { return this; } return nullptr; } void TestSource::IncrementCountOfCallsRequiringVersionData() { ++CountOfCallsRequiringVersionData; } void TestSource::IncrementCountOfCallsRequiringManifestData() { ++CountOfCallsRequiringManifestData; } std::string_view TestSourceFactory::TypeName() const { return "*TestSource"sv; } std::shared_ptr TestSourceFactory::Create(const SourceDetails& details) { std::shared_ptr result; if (OnOpenWithCustomHeader) { result = std::make_shared(details, OnOpenWithCustomHeader); } else { result = std::make_shared(details, OnOpen); } result->ShouldUpdateBeforeOpenResult = ShouldUpdateBeforeOpenResult; return result; } bool TestSourceFactory::Add(SourceDetails& details, IProgressCallback&) { if (OnAdd) { OnAdd(details); } return true; } bool TestSourceFactory::Update(const SourceDetails& details, IProgressCallback&) { if (OnUpdate) { OnUpdate(details); } return true; } bool TestSourceFactory::Remove(const SourceDetails& details, IProgressCallback&) { if (OnRemove) { OnRemove(details); } return true; } // Make copies of self when requested. TestSourceFactory::operator std::function()>() { return [this]() { return std::make_unique(*this); }; } bool AddSource(const AppInstaller::Repository::SourceDetails& details, AppInstaller::IProgressCallback& progress) { Repository::SourceEdit additionalProperties; additionalProperties.Explicit = details.Explicit; additionalProperties.Priority = details.Priority; Repository::Source source{ details.Name, details.Arg, details.Type, Repository::SourceTrustLevel::None, additionalProperties }; return source.Add(progress); } bool UpdateSource(std::string_view name, AppInstaller::IProgressCallback& progress) { Repository::Source source{ name }; return source.Update(progress).empty(); } bool RemoveSource(std::string_view name, AppInstaller::IProgressCallback& progress) { Repository::Source source{ name }; return source.Remove(progress); } std::vector GetSources() { return Repository::Source::GetCurrentSources(); } AppInstaller::Repository::Source OpenSource(std::string_view name, AppInstaller::IProgressCallback& progress) { Repository::Source source{ name }; source.Open(progress); return source; } void DropSource(std::string_view name) { Source::DropSource(name); } } ================================================ FILE: src/AppInstallerCLITests/TestSource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace TestCommon { struct TestSource; // IPackageVersion for TestSource struct TestPackageVersion : public AppInstaller::Repository::IPackageVersion { using Manifest = AppInstaller::Manifest::Manifest; using ISource = AppInstaller::Repository::ISource; using LocIndString = AppInstaller::Utility::LocIndString; using MetadataMap = AppInstaller::Repository::IPackageVersion::Metadata; TestPackageVersion(const Manifest& manifest, std::weak_ptr source = {}, bool hideSystemReferenceStrings = false); TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source = {}); template static std::shared_ptr Make(Args&&... args) { return std::make_shared(std::forward(args)...); } LocIndString GetProperty(AppInstaller::Repository::PackageVersionProperty property) const override; std::vector GetMultiProperty(AppInstaller::Repository::PackageVersionMultiProperty property) const override; Manifest GetManifest() override; AppInstaller::Repository::Source GetSource() const override; MetadataMap GetMetadata() const override; Manifest VersionManifest; MetadataMap Metadata; std::weak_ptr Source; bool HideSystemReferenceStrings = false; protected: static void AddIfHasValueAndNotPresent(const AppInstaller::Utility::NormalizedString& value, std::vector& target, bool folded = false); TestSource* GetTestSource() const; }; // IPackage for TestSource struct TestPackage : public AppInstaller::Repository::IPackage { static constexpr AppInstaller::Repository::IPackageType PackageType = AppInstaller::Repository::IPackageType::TestPackage; using Manifest = AppInstaller::Manifest::Manifest; using ISource = AppInstaller::Repository::ISource; using LocIndString = AppInstaller::Utility::LocIndString; using MetadataMap = TestPackageVersion::MetadataMap; // Create a package with only available versions using these manifests. TestPackage(const std::vector& available, std::weak_ptr source = {}, bool hideSystemReferenceStringsOnVersion = false); // Create a package with an installed version, metadata, and optionally available versions. TestPackage(const Manifest& installed, MetadataMap installationMetadata, std::weak_ptr source = {}); template static std::shared_ptr Make(Args&&... args) { return std::make_shared(std::forward(args)...); } AppInstaller::Utility::LocIndString GetProperty(AppInstaller::Repository::PackageProperty property) const override; std::vector GetMultiProperty(AppInstaller::Repository::PackageMultiProperty property) const override; std::vector GetVersionKeys() const override; std::shared_ptr GetLatestVersion() const override; std::shared_ptr GetVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override; AppInstaller::Repository::Source GetSource() const override; bool IsSame(const IPackage* other) const override; const void* CastTo(AppInstaller::Repository::IPackageType type) const override; std::vector> Versions; std::weak_ptr Source; size_t DefaultIsSameIdentity = 0; std::function IsSameOverride; protected: TestSource* GetTestSource() const; }; // ICompositePackage for TestSource struct TestCompositePackage : public AppInstaller::Repository::ICompositePackage { using Manifest = AppInstaller::Manifest::Manifest; using ISource = AppInstaller::Repository::ISource; using LocIndString = AppInstaller::Utility::LocIndString; using MetadataMap = TestPackageVersion::MetadataMap; // Create a package with only available versions using these manifests. TestCompositePackage(const std::vector& available, std::weak_ptr source = {}, bool hideSystemReferenceStringsOnVersion = false); // Create a package with an installed version, metadata, and optionally available versions. TestCompositePackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available = {}, std::weak_ptr source = {}); template static std::shared_ptr Make(Args&&... args) { return std::make_shared(std::forward(args)...); } AppInstaller::Utility::LocIndString GetProperty(AppInstaller::Repository::PackageProperty property) const override; std::shared_ptr GetInstalled() override; std::vector> GetAvailable() override; std::shared_ptr Installed; std::vector> Available; }; // An ISource implementation for use across the test code. struct TestSource : public AppInstaller::Repository::ISource, public std::enable_shared_from_this { static constexpr AppInstaller::Repository::ISourceType SourceType = AppInstaller::Repository::ISourceType::TestSource; const AppInstaller::Repository::SourceDetails& GetDetails() const override; const std::string& GetIdentifier() const override; AppInstaller::Repository::SourceInformation GetInformation() const override; bool QueryFeatureFlag(AppInstaller::Repository::SourceFeatureFlag flag) const override; std::function QueryFeatureFlagFunction; AppInstaller::Repository::SearchResult Search(const AppInstaller::Repository::SearchRequest& request) const override; void* CastTo(AppInstaller::Repository::ISourceType type) override; AppInstaller::Repository::SourceDetails Details = { "TestSource", "Microsoft.TestSource", "//arg", "", "*TestSource" }; AppInstaller::Repository::SourceInformation Information; std::function SearchFunction; TestSource() = default; TestSource(const AppInstaller::Repository::SourceDetails& details) : Details(details) {} // Tracking for potential network impacts void IncrementCountOfCallsRequiringVersionData(); size_t CountOfCallsRequiringVersionData = 0; void IncrementCountOfCallsRequiringManifestData(); size_t CountOfCallsRequiringManifestData = 0; }; struct TestSourceReference : public AppInstaller::Repository::ISourceReference { using OpenFunctor = std::function(const AppInstaller::Repository::SourceDetails&)>; using OpenFunctorWithCustomHeader = std::function(const AppInstaller::Repository::SourceDetails&, std::optional)>; TestSourceReference(const AppInstaller::Repository::SourceDetails& details, OpenFunctor open) : m_details(details), m_onOpen(open) {} TestSourceReference(const AppInstaller::Repository::SourceDetails& details, OpenFunctorWithCustomHeader open) : m_details(details), m_onOpenWithCustomHeader(open) {} std::string GetIdentifier() override { return m_details.Identifier; } AppInstaller::Repository::SourceDetails& GetDetails() override { return m_details; }; bool SetCustomHeader(std::optional header) override { m_header = header; return true; } bool ShouldUpdateBeforeOpenResult = false; bool ShouldUpdateBeforeOpen(const std::optional&) override { return ShouldUpdateBeforeOpenResult; } std::shared_ptr Open(AppInstaller::IProgressCallback&) override { if (m_onOpenWithCustomHeader) { return m_onOpenWithCustomHeader(m_details, m_header); } else { return m_onOpen(m_details); } } private: AppInstaller::Repository::SourceDetails m_details; OpenFunctor m_onOpen; OpenFunctorWithCustomHeader m_onOpenWithCustomHeader; std::optional m_header; }; // An ISourceFactory implementation for use across the test code. struct TestSourceFactory : public AppInstaller::Repository::ISourceFactory { using OpenFunctor = std::function(const AppInstaller::Repository::SourceDetails&)>; using OpenFunctorWithCustomHeader = std::function(const AppInstaller::Repository::SourceDetails&, std::optional)>; using AddFunctor = std::function; using UpdateFunctor = std::function; using RemoveFunctor = std::function; TestSourceFactory(OpenFunctor open) : OnOpen(std::move(open)) {} TestSourceFactory(OpenFunctorWithCustomHeader open) : OnOpenWithCustomHeader(std::move(open)) {} // ISourceFactory std::string_view TypeName() const override; std::shared_ptr Create(const AppInstaller::Repository::SourceDetails& details) override; bool Add(AppInstaller::Repository::SourceDetails& details, AppInstaller::IProgressCallback&) override; bool Update(const AppInstaller::Repository::SourceDetails& details, AppInstaller::IProgressCallback&) override; bool Remove(const AppInstaller::Repository::SourceDetails& details, AppInstaller::IProgressCallback&) override; // Make copies of self when requested. operator std::function()>(); bool ShouldUpdateBeforeOpenResult = false; OpenFunctor OnOpen; OpenFunctorWithCustomHeader OnOpenWithCustomHeader; AddFunctor OnAdd; UpdateFunctor OnUpdate; RemoveFunctor OnRemove; }; bool AddSource(const AppInstaller::Repository::SourceDetails& details, AppInstaller::IProgressCallback& progress); bool UpdateSource(std::string_view name, AppInstaller::IProgressCallback& progress); bool RemoveSource(std::string_view name, AppInstaller::IProgressCallback& progress); AppInstaller::Repository::Source OpenSource(std::string_view name, AppInstaller::IProgressCallback& progress); void DropSource(std::string_view name); std::vector GetSources(); } ================================================ FILE: src/AppInstallerCLITests/UninstallFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include #include #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Workflow; void OverrideForPortableUninstall(TestContext& context) { context.Override({ GetUninstallInfo, [](TestContext&) { } }); context.Override({ PortableUninstallImpl, [](TestContext& context) { std::filesystem::path temp = std::filesystem::temp_directory_path(); temp /= "TestPortableUninstalled.txt"; std::ofstream file(temp, std::ofstream::out); file.close(); context.Add(0); } }); } void OverrideForExeUninstall(TestContext& context) { context.Override({ ShellExecuteUninstallImpl, [](TestContext& context) { // Write out the uninstall command std::filesystem::path temp = std::filesystem::temp_directory_path(); temp /= "TestExeUninstalled.txt"; std::ofstream file(temp, std::ofstream::out); file << context.Get(); file.close(); context.Add(0); } }); } void OverrideForMSIXUninstall(TestContext& context) { context.Override({ MsixUninstall, [](TestContext& context) { // Write out the package full name std::filesystem::path temp = std::filesystem::temp_directory_path(); temp /= "TestMsixUninstalled.txt"; std::ofstream file(temp, std::ofstream::out); for (const auto& packageFamilyName : context.Get()) { file << packageFamilyName << std::endl; } file.close(); } }); } TEST_CASE("UninstallFlow_UninstallPortable", "[UninstallFlow][workflow]") { TestCommon::TempFile uninstallResultPath("TestPortableUninstalled.txt"); std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Portable })); OverrideForPortableUninstall(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Portable.Query); UninstallCommand uninstall({}); uninstall.Execute(context); INFO(uninstallOutput.str()); REQUIRE(std::filesystem::exists(uninstallResultPath.GetPath())); } TEST_CASE("UninstallFlow_UninstallExe", "[UninstallFlow][workflow]") { TestCommon::TempFile uninstallResultPath("TestExeUninstalled.txt"); std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); OverrideForExeUninstall(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::Silent); UninstallCommand uninstall({}); uninstall.Execute(context); INFO(uninstallOutput.str()); // Verify Uninstaller is called and parameters are passed in. REQUIRE(std::filesystem::exists(uninstallResultPath.GetPath())); std::ifstream uninstallResultFile(uninstallResultPath.GetPath()); REQUIRE(uninstallResultFile.is_open()); std::string uninstallResultStr; std::getline(uninstallResultFile, uninstallResultStr); REQUIRE(uninstallResultStr.find("uninstall.exe") != std::string::npos); REQUIRE(uninstallResultStr.find("/silence") != std::string::npos); } TEST_CASE("UninstallFlow_UninstallMsix", "[UninstallFlow][workflow]") { TestCommon::TempFile uninstallResultPath("TestMsixUninstalled.txt"); std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Msix })); OverrideForMSIXUninstall(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Msix.Query); UninstallCommand uninstall({}); uninstall.Execute(context); INFO(uninstallOutput.str()); // Verify Uninstaller is called with the package full name. REQUIRE(std::filesystem::exists(uninstallResultPath.GetPath())); std::ifstream uninstallResultFile(uninstallResultPath.GetPath()); REQUIRE(uninstallResultFile.is_open()); std::string uninstallResultStr; std::getline(uninstallResultFile, uninstallResultStr); REQUIRE(uninstallResultStr.find("20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe") != std::string::npos); } TEST_CASE("UninstallFlow_UninstallMSStore", "[UninstallFlow][workflow]") { TestCommon::TempFile uninstallResultPath("TestMsixUninstalled.txt"); std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_MSStore })); OverrideForMSIXUninstall(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_MSStore.Query); UninstallCommand uninstall({}); uninstall.Execute(context); INFO(uninstallOutput.str()); // Verify Uninstaller is called with the package full name REQUIRE(std::filesystem::exists(uninstallResultPath.GetPath())); std::ifstream uninstallResultFile(uninstallResultPath.GetPath()); REQUIRE(uninstallResultFile.is_open()); std::string uninstallResultStr; std::getline(uninstallResultFile, uninstallResultStr); REQUIRE(uninstallResultStr.find("microsoft.skypeapp_kzf8qxf38zg5c") != std::string::npos); } TEST_CASE("UninstallFlow_UninstallExeNotFound", "[UninstallFlow][workflow]") { TestCommon::TempFile uninstallResultPath("TestExeUninstalled.txt"); std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({})); context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.MissingApp"sv); context.Args.AddArg(Execution::Args::Type::Silent); UninstallCommand uninstall({}); uninstall.Execute(context); INFO(uninstallOutput.str()); // Verify Uninstaller is not called. REQUIRE(!std::filesystem::exists(uninstallResultPath.GetPath())); REQUIRE(uninstallOutput.str().find(Resource::LocString(Resource::String::NoInstalledPackageFound).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); } TEST_CASE("UninstallFlow_UninstallMultiple", "[UninstallFlow][workflow][MultiQuery]") { TestCommon::TempFile exeUninstallResultPath("TestExeUninstalled.txt"); TestCommon::TempFile msixUninstallResultPath("TestMsixUninstalled.txt"); std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Msix })); OverrideForExeUninstall(context); OverrideForMSIXUninstall(context); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query); UninstallCommand uninstall({}); uninstall.Execute(context); INFO(uninstallOutput.str()); // Verify Uninstallers are called REQUIRE(std::filesystem::exists(exeUninstallResultPath.GetPath())); REQUIRE(std::filesystem::exists(msixUninstallResultPath.GetPath())); } TEST_CASE("UninstallFlow_UninstallMultiple_NotAllInstalled", "[UninstallFlow][workflow][MultiQuery]") { std::ostringstream uninstallOutput; TestContext context{ uninstallOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query); UninstallCommand uninstall({}); uninstall.Execute(context); INFO(uninstallOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE); } ================================================ FILE: src/AppInstallerCLITests/UpdateFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include "TestHooks.h" #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility::literals; TEST_CASE("UpdateFlow_UpdateWithManifest", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("UpdateFlowTest_Exe.yaml").GetPath().u8string()); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); std::ifstream updateResultFile(updateResultPath.GetPath()); REQUIRE(updateResultFile.is_open()); std::string updateResultStr; std::getline(updateResultFile, updateResultStr); REQUIRE(updateResultStr.find("/update") != std::string::npos); REQUIRE(updateResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("UpdateFlow_UpdateWithManifestMSStore", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestMSStoreUpdated.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_MSStore })); OverrideForMSStore(context, true); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_MSStore.yaml").GetPath().u8string()); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); std::ifstream updateResultFile(updateResultPath.GetPath()); REQUIRE(updateResultFile.is_open()); std::string updateResultStr; std::getline(updateResultFile, updateResultStr); REQUIRE(updateResultStr.find("9WZDNCRFJ364") != std::string::npos); } TEST_CASE("UpdateFlow_UpdateWithManifestAppNotInstalled", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({})); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml").GetPath().u8string()); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::NoInstalledPackageFound).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); } TEST_CASE("UpdateFlow_UpdateWithManifestVersionAlreadyInstalled", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } TEST_CASE("UpdateFlow_UpdateExe", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::Silent); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); std::ifstream updateResultFile(updateResultPath.GetPath()); REQUIRE(updateResultFile.is_open()); std::string updateResultStr; std::getline(updateResultFile, updateResultStr); REQUIRE(updateResultStr.find("/update") != std::string::npos); REQUIRE(updateResultStr.find("/silence") != std::string::npos); REQUIRE(updateResultStr.find("/ver3.0.0.0") != std::string::npos); } TEST_CASE("UpdateFlow_UpdateZip_Exe", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Zip })); OverrideForShellExecute(context); OverrideForExtractInstallerFromArchive(context); OverrideForVerifyAndSetNestedInstaller(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Zip.Query); context.Args.AddArg(Execution::Args::Type::Silent); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); std::ifstream updateResultFile(updateResultPath.GetPath()); REQUIRE(updateResultFile.is_open()); std::string updateResultStr; std::getline(updateResultFile, updateResultStr); REQUIRE(updateResultStr.find("/custom") != std::string::npos); REQUIRE(updateResultStr.find("/silence") != std::string::npos); REQUIRE(updateResultStr.find("/ver2.0.0.0") != std::string::npos); } TEST_CASE("UpdateFlow_UpdatePortable", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestPortableInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Portable })); OverrideForPortableInstallFlow(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Portable.Query); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); } TEST_CASE("UpdateFlow_Portable_SymlinkCreationFail", "[UpdateFlow][workflow]") { // Update portable with symlink creation failure verify that it succeeds. TestCommon::TempDirectory tempDirectory("TestPortableInstallRoot", false); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto PreviousThreadGlobals = context.SetForCurrentThread(); bool overrideCreateSymlinkStatus = false; AppInstaller::Filesystem::TestHook_SetCreateSymlinkResult_Override(&overrideCreateSymlinkStatus); OverridePortableInstaller(context); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Portable })); const auto& targetDirectory = tempDirectory.GetPath(); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Portable.Query); context.Args.AddArg(Execution::Args::Type::InstallLocation, targetDirectory.u8string()); context.Args.AddArg(Execution::Args::Type::InstallScope, "user"sv); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); const auto& portableTargetPath = targetDirectory / "AppInstallerTestExeInstaller.exe"; REQUIRE(std::filesystem::exists(portableTargetPath)); REQUIRE(AppInstaller::Registry::Environment::PathVariable(AppInstaller::Manifest::ScopeEnum::User).Contains(targetDirectory)); // Perform uninstall std::ostringstream uninstallOutput; TestContext uninstallContext{ uninstallOutput, std::cin }; auto previousThreadGlobals = uninstallContext.SetForCurrentThread(); OverrideForCompositeInstalledSource(uninstallContext, CreateTestSource({ TSR::TestInstaller_Portable })); uninstallContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Portable.Query); UninstallCommand uninstall({}); uninstall.Execute(uninstallContext); INFO(uninstallOutput.str()); REQUIRE_FALSE(std::filesystem::exists(portableTargetPath)); } TEST_CASE("UpdateFlow_UpdateExeWithUnsupportedArgs", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); TestCommon::TempDirectory tempDirectory("TempDirectory", false); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UnsupportedArguments })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_UnsupportedArguments.Query); context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); UpgradeCommand update({}); context.SetExecutingCommand(&update); update.Execute(context); INFO(updateOutput.str()); // Verify unsupported arguments error message is shown REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT); REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UnsupportedArgument).get()) != std::string::npos); REQUIRE(updateOutput.str().find("-l,--location") != std::string::npos); } TEST_CASE("UpdateFlow_UnknownVersion", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); TestCommon::TempDirectory tempDirectory("TempDirectory", false); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UnknownVersion })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_UnknownVersion.Query); context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); UpgradeCommand update({}); context.SetExecutingCommand(&update); update.Execute(context); INFO(updateOutput.str()); // Verify help message is shown the user to use --include-unknown REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeUnknownVersionExplanation).get()) != std::string::npos); } TEST_CASE("UpdateFlow_UnknownVersion_IncludeUnknownArg", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); TestCommon::TempDirectory tempDirectory("TempDirectory", false); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UnknownVersion })); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_UnknownVersion.Query); context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory); context.Args.AddArg(Execution::Args::Type::IncludeUnknown); UpgradeCommand update({}); context.SetExecutingCommand(&update); update.Execute(context); INFO(updateOutput.str()); REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); } TEST_CASE("UpdateFlow_NoArgs_UnknownVersion", "[UpdateFlow][workflow]") { std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); UpgradeCommand update({}); context.SetExecutingCommand(&update); update.Execute(context); INFO(updateOutput.str()); // Verify --include-unknown help text is displayed if update is executed with no args and an unknown version package is available for upgrade. REQUIRE(updateOutput.str().find(Resource::String::UpgradeUnknownVersionCount(1)) != std::string::npos); } TEST_CASE("UpdateFlow_IncludeUnknown", "[UpdateFlow][workflow]") { std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); context.Args.AddArg(Execution::Args::Type::IncludeUnknown); UpgradeCommand update({}); context.SetExecutingCommand(&update); update.Execute(context); INFO(updateOutput.str()); // Verify unknown version package is displayed available for upgrade. REQUIRE(updateOutput.str().find(Resource::String::UpgradeUnknownVersionCount(1)) == std::string::npos); REQUIRE(updateOutput.str().find("unknown") != std::string::npos); } TEST_CASE("UpdateFlow_UpdatePortableWithManifest", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestPortableInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Portable })); OverrideForPortableInstallFlow(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("UpdateFlowTest_Portable.yaml").GetPath().u8string()); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); } TEST_CASE("UpdateFlow_UpdateMsix", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestMsixInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Msix })); OverrideForMSIX(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Msix.Query); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is called. REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); } TEST_CASE("UpdateFlow_UpdateMSStore", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestMSStoreUpdated.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_MSStore })); OverrideForMSStore(context, true); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_MSStore.Query); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is called. REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); std::ifstream updateResultFile(updateResultPath.GetPath()); REQUIRE(updateResultFile.is_open()); std::string updateResultStr; std::getline(updateResultFile, updateResultStr); REQUIRE(updateResultStr.find("9WZDNCRFJ364") != std::string::npos); } TEST_CASE("UpdateFlow_UpdateExeLatestAlreadyInstalled", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_LatestInstalled })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_LatestInstalled.Query); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicable", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_IncompatibleInstallerType })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_IncompatibleInstallerType.Query); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnologyInNewerVersions).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicableSpecificVersion", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_IncompatibleInstallerType })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_IncompatibleInstallerType.Query); context.Args.AddArg(Execution::Args::Type::Version, "2.0.0.0"sv); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnology).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } TEST_CASE("UpdateFlow_UpdateExeWithDifferentInstalledType", "[UpdateFlow][workflow]") { // Tests installer applicability when installed type is different but listed in the manifest TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_DifferentInstallerType })); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_DifferentInstallerType.Query); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is called. REQUIRE(context.GetTerminationHR() == S_OK); REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); } TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotFound", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::Version, "1.2.3.4"sv); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::GetManifestResultVersionNotFound("1.2.3.4"_liv)).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND); } TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotApplicable", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_IncompatibleInstallerType })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_IncompatibleInstallerType.Query); // This must be 2.0.0.0 since the version would not be an upgrade otherwise context.Args.AddArg(Execution::Args::Type::Version, "2.0.0.0"sv); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnology).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]") { TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt"); TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt"); TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt"); TestCommon::TempFile updatePortableResultPath("TestPortableInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); OverrideForShellExecute(context); OverrideForMSIX(context); OverrideForMSStore(context, true); OverrideForPortableInstall(context); context.Args.AddArg(Execution::Args::Type::All); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify that --include-unknown help message is displayed. REQUIRE(updateOutput.str().find(Resource::String::UpgradeUnknownVersionCount(1)) != std::string::npos); REQUIRE(updateOutput.str().find("AppInstallerCliTest.TestExeUnknownVersion") == std::string::npos); // Verify installers are called. REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath())); } TEST_CASE("UpdateFlow_UpdateAll_IncludeUnknown", "[UpdateFlow][workflow]") { TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt"); TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt"); TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt"); TestCommon::TempFile updatePortableResultPath("TestPortableInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); OverrideForShellExecute(context); OverrideForMSIX(context); OverrideForMSStore(context, true); OverrideForPortableInstall(context); context.Args.AddArg(Execution::Args::Type::All); context.Args.AddArg(Execution::Args::Type::IncludeUnknown); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify that --include-unknown help message is NOT displayed and unknown version package is upgraded. REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeUnknownVersionCount).get()) == std::string::npos); REQUIRE(updateOutput.str().find("AppInstallerCliTest.TestExeUnknownVersion") != std::string::npos); // Verify installers are called. REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath())); } TEST_CASE("UpdateFlow_UpgradeWithDuplicateUpgradeItemsFound", "[UpdateFlow][workflow]") { TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UpgradeAllWithDuplicateUpgradeItems })); // Installer should only be run once since the 2 upgrade items are same. OverrideForShellExecute(context, 1); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_UpgradeAllWithDuplicateUpgradeItems.Query); context.Args.AddArg(Execution::Args::Type::All); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify installers are called. REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath())); } TEST_CASE("UpdateFlow_Dependencies", "[UpdateFlow][workflow][dependencies]") { std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_Dependencies })); OverrideForShellExecute(context); OverrideEnableWindowsFeaturesDependencies(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_Dependencies.Query);; UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); std::string updateResultStr = updateOutput.str(); // Verify dependencies are informed REQUIRE(updateResultStr.find(Resource::LocString(Resource::String::PackageRequiresDependencies).get()) != std::string::npos); REQUIRE(updateResultStr.find("PreviewIIS") != std::string::npos); REQUIRE(updateResultStr.find("Preview VC Runtime") != std::string::npos); } TEST_CASE("UpdateFlow_LicenseAgreement", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_LicenseAgreement })); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_LicenseAgreement.Query); context.Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify agreements are shown REQUIRE(updateOutput.str().find("Agreement for EXE") != std::string::npos); REQUIRE(updateOutput.str().find("This is the agreement for the EXE") != std::string::npos); // Verify Installer is called. REQUIRE(std::filesystem::exists(updateResultPath.GetPath())); } TEST_CASE("UpdateFlow_LicenseAgreement_NotAccepted", "[UpdateFlow][workflow]") { TestCommon::TempFile updateResultPath("TestExeInstalled.txt"); // Say "No" at the agreements prompt std::istringstream updateInput{ "n" }; std::ostringstream updateOutput; TestContext context{ updateOutput, updateInput }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_LicenseAgreement })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_LicenseAgreement.Query); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify agreements are shown REQUIRE(updateOutput.str().find("Agreement for EXE") != std::string::npos); REQUIRE(updateOutput.str().find("This is the agreement for the EXE") != std::string::npos); // Verify Installer is not called. REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED); REQUIRE_FALSE(std::filesystem::exists(updateResultPath.GetPath())); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::PackageAgreementsNotAgreedTo).get()) != std::string::npos); } TEST_CASE("UpdateFlow_All_LicenseAgreement", "[UpdateFlow][workflow]") { TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt"); TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt"); TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt"); TestCommon::TempFile updatePortableResultPath("TestPortableInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UpgradeUsesAgreements, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix_UpgradeUsesAgreements, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); OverrideForShellExecute(context); OverrideForMSIX(context); OverrideForMSStore(context, true); OverrideForPortableInstall(context); context.Args.AddArg(Execution::Args::Type::All); context.Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify agreements are shown REQUIRE(updateOutput.str().find("Agreement for EXE") != std::string::npos); REQUIRE(updateOutput.str().find("This is the agreement for the EXE") != std::string::npos); REQUIRE(updateOutput.str().find("Agreement for MSIX") != std::string::npos); REQUIRE(updateOutput.str().find("This is the agreement for the MSIX") != std::string::npos); // Verify installers are called. REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath())); } TEST_CASE("UpdateFlow_All_LicenseAgreement_NotAccepted", "[UpdateFlow][workflow]") { TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt"); TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt"); TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt"); // Say "No" at the agreements prompt std::istringstream updateInput{ "n" }; std::ostringstream updateOutput; TestContext context{ updateOutput, updateInput }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UpgradeUsesAgreements, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix_UpgradeUsesAgreements, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); context.Args.AddArg(Execution::Args::Type::All); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify agreements are shown REQUIRE(updateOutput.str().find("Agreement for EXE") != std::string::npos); REQUIRE(updateOutput.str().find("This is the agreement for the EXE") != std::string::npos); REQUIRE(updateOutput.str().find("Agreement for MSIX") != std::string::npos); REQUIRE(updateOutput.str().find("This is the agreement for the MSIX") != std::string::npos); // Verify installers are not called. REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED); REQUIRE_FALSE(std::filesystem::exists(updateExeResultPath.GetPath())); REQUIRE_FALSE(std::filesystem::exists(updateMsixResultPath.GetPath())); REQUIRE_FALSE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); } TEST_CASE("UpdateFlow_RequireExplicit", "[UpdateFlow][workflow]") { TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt"); TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); // Msix package has an update that requires explicit upgrade. // Exe, Portable, MSStore, Zip are also listed with an available upgrade. OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Exe_UnknownVersion, TSR::TestInstaller_Msix_UpgradeRequiresExplicit, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, TSR::TestInstaller_Zip, })); SECTION("List available upgrades") { UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // The package that requires explicit upgrade is listed below the header for pinned packages REQUIRE(updateOutput.str().find("AppInstallerCliTest.TestExeInstaller") != std::string::npos); auto pinnedPackagesHeaderPosition = updateOutput.str().find(Resource::LocString(Resource::String::UpgradeAvailableForPinned)); auto pinnedPackageLinePosition = updateOutput.str().find("AppInstallerCliTest.TestMsixInstaller"); REQUIRE(pinnedPackagesHeaderPosition != std::string::npos); REQUIRE(pinnedPackageLinePosition != std::string::npos); REQUIRE(pinnedPackagesHeaderPosition < pinnedPackageLinePosition); REQUIRE(updateOutput.str().find(Resource::String::UpgradeRequireExplicitCount(1)) == std::string::npos); } SECTION("Upgrade all except pinned") { context.Args.AddArg(Args::Type::All); OverrideForMSStore(context, true); OverrideForPortableInstall(context); OverrideForShellExecute(context); OverrideForExtractInstallerFromArchive(context); OverrideForVerifyAndSetNestedInstaller(context); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); auto s = updateOutput.str(); // Verify message is printed for skipped package REQUIRE(updateOutput.str().find(Resource::String::UpgradeRequireExplicitCount(1)) != std::string::npos); // Verify package is not installed, but all others are REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath())); REQUIRE(!std::filesystem::exists(updateMsixResultPath.GetPath())); } SECTION("Upgrade explicitly") { context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Msix.Query); OverrideForMSIX(context); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath())); } // Command should always succeed REQUIRE(context.GetTerminationHR() == S_OK); } TEST_CASE("InstallFlow_FoundInstalledAndUpgradeAvailable", "[UpdateFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::Silent); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/update") != std::string::npos); REQUIRE(installResultStr.find("/ver3.0.0.0") != std::string::npos); } TEST_CASE("InstallFlow_FoundInstalledAndUpgradeAvailable_WithNoUpgrade", "[UpdateFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::NoUpgrade); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageAlreadyInstalled).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_PACKAGE_ALREADY_INSTALLED); } TEST_CASE("InstallFlow_FoundInstalledAndUpgradeNotAvailable", "[UpdateFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_LatestInstalled })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_LatestInstalled.Query); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is not called. REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); } TEST_CASE("UpdateFlow_UpdateAll_ForwardArgs", "[UpdateFlow][workflow]") { TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt"); TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt"); TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt"); TestCommon::TempFile updatePortableResultPath("TestPortableInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Msix, TSR::TestInstaller_MSStore, TSR::TestInstaller_Portable, })); OverrideForShellExecute(context); OverrideForMSIX(context); OverrideForMSStore(context, true); OverrideForPortableInstall(context); context.Args.AddArg(Execution::Args::Type::All); context.Args.AddArg(Execution::Args::Type::Silent); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify installers are called with the silent flags REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath())); std::ifstream updateExeResultFile(updateExeResultPath.GetPath()); std::string updateExeResultStr; std::getline(updateExeResultFile, updateExeResultStr); REQUIRE(updateExeResultStr.find("/silence") != std::string::npos); REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath())); REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath())); } TEST_CASE("UpdateFlow_UpdateMultiple", "[UpdateFlow][workflow][MultiQuery]") { TestCommon::TempFile exeUpdateResultPath("TestExeInstalled.txt"); TestCommon::TempFile msixUpdateResultPath("TestMsixInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Msix })); OverrideForShellExecute(context); OverrideForMSIX(context); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); // Verify Installers are called called. REQUIRE(std::filesystem::exists(exeUpdateResultPath.GetPath())); REQUIRE(std::filesystem::exists(exeUpdateResultPath.GetPath())); } TEST_CASE("UpdateFlow_UpdateMultiple_NotAllFound", "[UpdateFlow][workflow][MultiQuery]") { TestCommon::TempFile exeUpdateResultPath("TestExeInstalled.txt"); std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe })); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query); context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query); SECTION("Ignore unavailable") { OverrideForShellExecute(context); context.Args.AddArg(Execution::Args::Type::IgnoreUnavailable); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); REQUIRE(!context.IsTerminated()); REQUIRE(std::filesystem::exists(exeUpdateResultPath.GetPath())); } SECTION("Don't ignore unavailable") { UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE); } } TEST_CASE("UpdateFlow_UpdateWithReboot", "[UpdateFlow][workflow][reboot]") { TestCommon::TestUserSettings testSettings; std::ostringstream updateOutput; TestContext context{ updateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_ExpectedReturnCodes })); context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_ExpectedReturnCodes.Query); context.Args.AddArg(Execution::Args::Type::AllowReboot); context.Override({ AppInstaller::CLI::Workflow::ShellExecuteInstallImpl, [&](TestContext& context) { // APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH (not treated as an installer error) context.Add(9); } }); SECTION("Reboot success") { TestHook::SetInitiateRebootResult_Override initiateRebootResultOverride(true); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); REQUIRE_FALSE(context.IsTerminated()); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::InitiatingReboot).get()) != std::string::npos); REQUIRE_FALSE(updateOutput.str().find(Resource::LocString(Resource::String::FailedToInitiateReboot).get()) != std::string::npos); } SECTION("Reboot failed") { TestHook::SetInitiateRebootResult_Override initiateRebootResultOverride(false); UpgradeCommand update({}); update.Execute(context); INFO(updateOutput.str()); REQUIRE_FALSE(context.IsTerminated()); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::InitiatingReboot).get()) != std::string::npos); REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::FailedToInitiateReboot).get()) != std::string::npos); } } ================================================ FILE: src/AppInstallerCLITests/UserSettings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSettings.h" #include #include #include "AppInstallerLogging.h" #include #include #include #include using namespace AppInstaller::Settings; using namespace AppInstaller::Logging; using namespace AppInstaller::Runtime; using namespace TestCommon; using namespace std::string_literals; using namespace std::string_view_literals; using namespace std::chrono_literals; namespace { static constexpr std::string_view s_goodJson = "{}"; static constexpr std::string_view s_badJson = "{"; static constexpr std::string_view s_settings = "settings.json"sv; static constexpr std::string_view s_settingsBackup = "settings.json.backup"sv; } TEST_CASE("UserSettingsFilePaths", "[settings]") { auto settingsPath = UserSettings::SettingsFilePath(); auto expectedPath = GetPathTo(PathName::UserFileSettings) / "settings.json"; REQUIRE(settingsPath == expectedPath); } TEST_CASE("UserSettingsType", "[settings]") { // These are all the possible combinations between (7 of them are impossible): // 1 - No settings.json file exists // 2 - Bad settings.json file // 3 - No settings.json.backup file exists // 4 - Bad settings.json.backup file exists. auto again = DeleteUserSettingsFiles(); SECTION("No setting.json No setting.json.backup") { UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Default); } SECTION("No setting.json Bad setting.json.backup") { SetSetting(Stream::BackupUserSettings, s_badJson); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Default); } SECTION("No setting.json Good setting.json.backup") { SetSetting(Stream::BackupUserSettings, s_goodJson); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Backup); } SECTION("Bad setting.json No setting.json.backup") { SetSetting(Stream::PrimaryUserSettings, s_badJson); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Default); } SECTION("Bad setting.json Bad setting.json.backup") { SetSetting(Stream::PrimaryUserSettings, s_badJson); SetSetting(Stream::BackupUserSettings, s_badJson); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Default); } SECTION("Bad setting.json Good setting.json.backup") { SetSetting(Stream::PrimaryUserSettings, s_badJson); SetSetting(Stream::BackupUserSettings, s_goodJson); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Backup); } SECTION("Good setting.json No setting.json.backup") { SetSetting(Stream::PrimaryUserSettings, s_goodJson); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Standard); } SECTION("Good setting.json Bad setting.json.backup") { SetSetting(Stream::PrimaryUserSettings, s_goodJson); SetSetting(Stream::BackupUserSettings, s_badJson); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Standard); } SECTION("Good setting.json Good setting.json.backup") { SetSetting(Stream::PrimaryUserSettings, s_goodJson); SetSetting(Stream::BackupUserSettings, s_goodJson); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Standard); } } TEST_CASE("UserSettingsCreateFiles", "[settings]") { auto again = DeleteUserSettingsFiles(); auto settingsPath = UserSettings::SettingsFilePath(); auto settingsBackupPath = GetPathTo(Stream::BackupUserSettings); SECTION("No settings.json create new") { REQUIRE(!std::filesystem::exists(settingsPath)); REQUIRE(!std::filesystem::exists(settingsBackupPath)); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Default); userSettingTest.PrepareToShellExecuteFile(); REQUIRE(std::filesystem::exists(settingsPath)); REQUIRE(!std::filesystem::exists(settingsBackupPath)); } SECTION("Good settings.json create new backup") { SetSetting(Stream::PrimaryUserSettings, s_goodJson); REQUIRE(std::filesystem::exists(settingsPath)); REQUIRE(!std::filesystem::exists(settingsBackupPath)); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.GetType() == UserSettingsType::Standard); userSettingTest.PrepareToShellExecuteFile(); REQUIRE(std::filesystem::exists(settingsPath)); REQUIRE(std::filesystem::exists(settingsBackupPath)); } } TEST_CASE("SettingProgressBar", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Default value") { UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == VisualStyle::Accent); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Accent") { std::string_view json = R"({ "visual": { "progressBar": "accent" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == VisualStyle::Accent); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Rainbow") { std::string_view json = R"({ "visual": { "progressBar": "rainbow" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == VisualStyle::Rainbow); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("retro") { std::string_view json = R"({ "visual": { "progressBar": "retro" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == VisualStyle::Retro); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Bad value") { std::string_view json = R"({ "visual": { "progressBar": "fake" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == VisualStyle::Accent); REQUIRE(userSettingTest.GetWarnings().size() == 1); } SECTION("Bad value type") { std::string_view json = R"({ "visual": { "progressBar": 5 } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == VisualStyle::Accent); REQUIRE(userSettingTest.GetWarnings().size() == 1); } } TEST_CASE("SettingsAnonymizePathForDisplay", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Default") { UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == true); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("True") { std::string_view json = R"({ "visual": { "anonymizeDisplayedPaths": true } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == true); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("False") { std::string_view json = R"({ "visual": { "anonymizeDisplayedPaths": false } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == false); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Invalid Value") { std::string_view json = R"({ "visual": { "anonymizeDisplayedPaths": "notBoolean" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == true); REQUIRE(userSettingTest.GetWarnings().size() == 1); } } TEST_CASE("SettingLoggingLevelPreference", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Default value") { UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Level::Info); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Info") { std::string_view json = R"({ "logging": { "level": "info" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Level::Info); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Verbose") { std::string_view json = R"({ "logging": { "level": "verbose" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Level::Verbose); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Warning") { std::string_view json = R"({ "logging": { "level": "warning" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Level::Warning); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Error") { std::string_view json = R"({ "logging": { "level": "error" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Level::Error); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Critical") { std::string_view json = R"({ "logging": { "level": "critical" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Level::Crit); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Bad value") { std::string_view json = R"({ "logging": { "level": "fake" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Level::Info); REQUIRE(userSettingTest.GetWarnings().size() == 1); } SECTION("Bad value type") { std::string_view json = R"({ "logging": { "level": 5 } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Level::Info); REQUIRE(userSettingTest.GetWarnings().size() == 1); } } TEST_CASE("SettingAutoUpdateIntervalInMinutes", "[settings]") { auto again = DeleteUserSettingsFiles(); constexpr static auto cinq = 5min; constexpr static auto cero = 0min; constexpr static auto threehundred = 300min; std::chrono::minutes defaultAutoUpdateTime{}; { SetSetting(Stream::PrimaryUserSettings, ""); UserSettingsTest userSettingTest; defaultAutoUpdateTime = userSettingTest.Get(); } SECTION("Valid value 0") { std::string_view json = R"({ "source": { "autoUpdateIntervalInMinutes": 0 } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == cero); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Valid value 300") { std::string_view json = R"({ "source": { "autoUpdateIntervalInMinutes": 300 } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == threehundred); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Invalid type negative integer") { std::string_view json = R"({ "source": { "autoUpdateIntervalInMinutes": -20 } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == defaultAutoUpdateTime); REQUIRE(userSettingTest.GetWarnings().size() == 1); } SECTION("Invalid type string") { std::string_view json = R"({ "source": { "autoUpdateIntervalInMinutes": "not a number" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == defaultAutoUpdateTime); REQUIRE(userSettingTest.GetWarnings().size() == 1); } SECTION("Overridden by Group Policy") { auto policiesKey = RegCreateVolatileTestRoot(); SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyValueName, (DWORD)threehundred.count()); GroupPolicyTestOverride policies{ policiesKey.get() }; std::string_view json = R"({ "source": { "autoUpdateIntervalInMinutes": 5 } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == threehundred); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Invalid Group Policy") { auto policiesKey = RegCreateVolatileTestRoot(); SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyValueName, L"Not a number"s); GroupPolicyTestOverride policies{ policiesKey.get() }; std::string_view json = R"({ "source": { "autoUpdateIntervalInMinutes": 5 } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == cinq); REQUIRE(userSettingTest.GetWarnings().size() == 0); } } TEST_CASE("SettingsExperimentalCmd", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Feature off default") { UserSettingsTest userSettingTest; REQUIRE(!userSettingTest.Get()); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Feature on") { std::string_view json = R"({ "experimentalFeatures": { "experimentalCmd": true } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get()); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Feature off") { std::string_view json = R"({ "experimentalFeatures": { "experimentalCmd": false } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(!userSettingTest.Get()); REQUIRE(userSettingTest.GetWarnings().size() == 0); } SECTION("Invalid value") { std::string_view json = R"({ "experimentalFeatures": { "experimentalCmd": "string" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(!userSettingTest.Get()); REQUIRE(userSettingTest.GetWarnings().size() == 1); } SECTION("Disabled by group policy") { auto policiesKey = RegCreateVolatileTestRoot(); SetRegistryValue(policiesKey.get(), SourceUpdateIntervalPolicyValueName, L"Not a number"s); GroupPolicyTestOverride policies{ policiesKey.get() }; std::string_view json = R"({ "experimentalFeatures": { "experimentalCmd": true } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; // Experimental features group policy is applied at the ExperimentalFeature level, // so it doesn't affect the settings. REQUIRE(userSettingTest.Get()); REQUIRE(userSettingTest.GetWarnings().size() == 0); } } TEST_CASE("SettingsPortablePackageUserRoot", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Relative path") { std::string_view json = R"({ "installBehavior": { "portablePackageUserRoot": "%LOCALAPPDATA%/Portable/Root" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get().empty()); auto warnings = userSettingTest.GetWarnings(); REQUIRE(warnings.size() == 1); REQUIRE(warnings[0].Message == AppInstaller::StringResource::String::SettingsWarningInvalidFieldValue); REQUIRE(warnings[0].Path == ".installBehavior.portablePackageUserRoot"); } SECTION("Valid path") { std::string_view json = R"({ "installBehavior": { "portablePackageUserRoot": "C:/Foo/Bar" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == "C:/Foo/Bar"); REQUIRE(userSettingTest.GetWarnings().size() == 0); } } TEST_CASE("SettingsPortablePackageMachineRoot", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Relative path") { std::string_view json = R"({ "installBehavior": { "portablePackageMachineRoot": "%LOCALAPPDATA%/Portable/Root" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get().empty()); auto warnings = userSettingTest.GetWarnings(); REQUIRE(warnings.size() == 1); REQUIRE(warnings[0].Message == AppInstaller::StringResource::String::SettingsWarningInvalidFieldValue); REQUIRE(warnings[0].Path == ".installBehavior.portablePackageMachineRoot"); } SECTION("Valid path") { std::string_view json = R"({ "installBehavior": { "portablePackageMachineRoot": "C:/Foo/Bar" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == "C:/Foo/Bar"); REQUIRE(userSettingTest.GetWarnings().size() == 0); } } TEST_CASE("SettingsDownloadDefaultDirectory", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Valid path") { std::string_view json = R"({ "downloadBehavior": { "defaultDownloadDirectory": "C:/Foo/Bar" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == "C:/Foo/Bar"); REQUIRE(userSettingTest.GetWarnings().size() == 0); } } TEST_CASE("SettingsConfigureDefaultModuleRoot", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Valid path") { std::string_view json = R"({ "configureBehavior": { "defaultModuleRoot": "C:/Foo/Bar" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == "C:/Foo/Bar"); REQUIRE(userSettingTest.GetWarnings().size() == 0); } } TEST_CASE("SettingsArchiveExtractionMethod", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Shell api") { std::string_view json = R"({ "installBehavior": { "archiveExtractionMethod": "shellApi" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == AppInstaller::Archive::ExtractionMethod::ShellApi); } SECTION("Shell api") { std::string_view json = R"({ "installBehavior": { "archiveExtractionMethod": "tar" } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == AppInstaller::Archive::ExtractionMethod::Tar); } } TEST_CASE("SettingsInstallScope", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("User scope preference") { std::string_view json = R"({ "installBehavior": { "preferences": { "scope": "user" } } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == AppInstaller::Manifest::ScopeEnum::User); } SECTION("Machine scope preference") { std::string_view json = R"({ "installBehavior": { "preferences": { "scope": "machine" } } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == AppInstaller::Manifest::ScopeEnum::Machine); } SECTION("User scope requirement") { std::string_view json = R"({ "installBehavior": { "requirements": { "scope": "user" } } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == AppInstaller::Manifest::ScopeEnum::User); } SECTION("Machine scope requirement") { std::string_view json = R"({ "installBehavior": { "requirements": { "scope": "machine" } } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == AppInstaller::Manifest::ScopeEnum::Machine); } } TEST_CASE("SettingsMaxResumes", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Modify max number of resumes") { std::string_view json = R"({ "installBehavior": { "maxResumes": 5 } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == 5); } } TEST_CASE("LoggingChannels", "[settings]") { auto again = DeleteUserSettingsFiles(); SECTION("Not provided") { std::string_view json = R"({ })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Channel::Defaults); } SECTION("No channels") { std::string_view json = R"({ "logging": { "channels": [] } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Channel::None); } SECTION("Default") { std::string_view json = R"({ "logging": { "channels": ["default"] } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == Channel::Defaults); } SECTION("Multiple") { std::string_view json = R"({ "logging": { "channels": ["core","Repo","YAML"] } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == (Channel::Core | Channel::Repo | Channel::YAML)); } SECTION("Some invalid") { std::string_view json = R"({ "logging": { "channels": ["cli","sql","INVALID"] } })"; SetSetting(Stream::PrimaryUserSettings, json); UserSettingsTest userSettingTest; REQUIRE(userSettingTest.Get() == (Channel::CLI | Channel::SQL)); } } ================================================ FILE: src/AppInstallerCLITests/Versions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace AppInstaller; using namespace AppInstaller::Utility; TEST_CASE("VersionParse", "[versions]") { Version version("1.2.3.4-alpha"); const auto& parts = version.GetParts(); REQUIRE(parts.size() == 4); for (size_t i = 0; i < parts.size(); ++i) { INFO(i); REQUIRE(parts[i].Integer == static_cast(i + 1)); if (i != 3) { REQUIRE(parts[i].Other == ""); } else { REQUIRE(parts[i].Other == "-alpha"); } } } TEST_CASE("VersionParsePlusDash", "[versions]") { Version version("1.2.3.4-alpha", ".-"); const auto& parts = version.GetParts(); REQUIRE(parts.size() == 5); for (size_t i = 0; i < 4; ++i) { INFO(i); REQUIRE(parts[i].Integer == static_cast(i + 1)); REQUIRE(parts[i].Other == ""); } REQUIRE(parts[4].Other == "alpha"); } TEST_CASE("VersionParseWithWhitespace", "[versions]") { Version version("1. 2.3 . 4 "); const auto& parts = version.GetParts(); REQUIRE(parts.size() == 4); for (size_t i = 0; i < parts.size(); ++i) { INFO(i); REQUIRE(parts[i].Integer == static_cast(i + 1)); REQUIRE(parts[i].Other == ""); } } TEST_CASE("VersionParseWithPreamble", "[versions]") { Version version("v1.2.3.4"); const auto& parts = version.GetParts(); REQUIRE(parts.size() == 4); for (size_t i = 0; i < parts.size(); ++i) { INFO(i); REQUIRE(parts[i].Integer == static_cast(i + 1)); REQUIRE(parts[i].Other == ""); } } TEST_CASE("VersionParseCorner", "[versions]") { Version version1(""); auto parts = version1.GetParts(); REQUIRE(parts.size() == 0); Version version2("."); parts = version2.GetParts(); REQUIRE(parts.size() == 0); Version version3(".0"); parts = version3.GetParts(); REQUIRE(parts.size() == 0); Version version4(".1"); parts = version4.GetParts(); REQUIRE(parts.size() == 2); REQUIRE(parts[0].Integer == 0); REQUIRE(parts[0].Other == ""); REQUIRE(parts[1].Integer == 1); REQUIRE(parts[1].Other == ""); Version version5("version"); parts = version5.GetParts(); REQUIRE(parts.size() == 1); REQUIRE(parts[0].Integer == 0); REQUIRE(parts[0].Other == "version"); Version version6(". 1 "); parts = version6.GetParts(); REQUIRE(parts.size() == 2); REQUIRE(parts[0].Integer == 0); REQUIRE(parts[0].Other == ""); REQUIRE(parts[1].Integer == 1); REQUIRE(parts[1].Other == ""); Version version7("v1.2a"); parts = version7.GetParts(); REQUIRE(parts.size() == 2); REQUIRE(parts[0].Integer == 1); REQUIRE(parts[0].Other == ""); REQUIRE(parts[1].Integer == 2); REQUIRE(parts[1].Other == "a"); } void RequireLessThan(std::string_view a, std::string_view b) { Version vA{ std::string(a) }; Version vB{ std::string(b) }; REQUIRE(vA < vB); REQUIRE_FALSE(vB < vA); REQUIRE(vA <= vB); REQUIRE_FALSE(vB <= vA); REQUIRE(vB > vA); REQUIRE_FALSE(vA > vB); REQUIRE(vB >= vA); REQUIRE_FALSE(vA >= vB); REQUIRE_FALSE(vA == vB); REQUIRE(vA != vB); } void RequireEqual(std::string_view a, std::string_view b) { Version vA{ std::string(a) }; Version vB{ std::string(b) }; REQUIRE(vA == vB); REQUIRE_FALSE(vA != vB); REQUIRE(vA <= vB); REQUIRE(vA >= vB); REQUIRE_FALSE(vA < vB); REQUIRE_FALSE(vA > vB); } TEST_CASE("VersionCompare", "[versions]") { RequireLessThan("1", "2"); RequireLessThan("1.0.0", "2.0.0"); RequireLessThan("0.0.1", "0.0.2"); RequireLessThan("0.0.1-alpha", "0.0.2-alpha"); RequireLessThan("0.0.1-beta", "0.0.2-alpha"); RequireLessThan("0.0.1-beta", "0.0.2-alpha"); RequireLessThan("13.9.8", "14.1"); // Ensure that versions with non-digit characters in their parts are sorted correctly RequireLessThan("1-rc", "1"); RequireLessThan("1.2-rc", "1.2"); RequireLessThan("1.0-rc", "1.0"); RequireLessThan("1.0.0-rc", "1"); RequireLessThan("22.0.0-rc.1", "22.0.0"); RequireLessThan("22.0.0-rc.1", "22.0.0.1"); RequireLessThan("22.0.0-rc.1", "22.0.0.1-rc"); // Ensure that Sub-RC versions are sorted correctly RequireLessThan("22.0.0-rc.1", "22.0.0-rc.1.1"); RequireLessThan("22.0.0-rc.1.1", "22.0.0-rc.1.2"); RequireLessThan("22.0.0-rc.1.2", "22.0.0-rc.2"); RequireEqual("1.0", "1.0.0"); // Ensure that integers are parsed correctly when there is a leading zero RequireEqual("1.2.00.3", "1.2.0.3"); RequireEqual("1.2.003.4", "1.2.3.4"); RequireEqual("01.02.03.04", "1.2.3.4"); RequireEqual("1.2.03-beta", "1.2.3-beta"); // Ensure whitespace doesn't affect equality RequireEqual("1.0", "1.0 "); RequireEqual("1.0", "1. 0"); RequireEqual("1.0", "1.0."); // Ensure versions with preambles are sorted correctly RequireEqual("1.0", "Version 1.0"); RequireEqual("foo1", "bar1"); RequireLessThan("v0.0.1", "0.0.2"); RequireLessThan("v0.0.1", "v0.0.2"); RequireLessThan("1.a2", "1.b1"); RequireLessThan("alpha", "beta"); } TEST_CASE("VersionAndChannelSort", "[versions]") { std::vector sortedList = { { Version("15.0.0"), Channel("") }, { Version("14.0.0"), Channel("") }, { Version("13.2.1-bugfix"), Channel("") }, { Version("13.2.0"), Channel("") }, { Version("13.2.0-rc"), Channel("") }, { Version("13.0.0"), Channel("") }, { Version("16.0.0"), Channel("alpha") }, { Version("15.8.0"), Channel("alpha") }, { Version("15.1.0"), Channel("beta") }, }; std::vector reorderList = { 4, 2, 1, 7, 6, 3, 8, 5, 0 }; REQUIRE(sortedList.size() == reorderList.size()); std::vector jumbledList; for (auto i : reorderList) { jumbledList.emplace_back(sortedList[i]); } std::sort(jumbledList.begin(), jumbledList.end()); for (size_t i = 0; i < jumbledList.size(); ++i) { const VersionAndChannel& sortedVAC = sortedList[i]; const VersionAndChannel& jumbledVAC = jumbledList[i]; INFO(i); REQUIRE(sortedVAC.GetVersion().ToString() == jumbledVAC.GetVersion().ToString()); REQUIRE(sortedVAC.GetChannel().ToString() == jumbledVAC.GetChannel().ToString()); } } TEST_CASE("MinOsVersion_Check", "[versions]") { // Just verify that we are greater than Win 7 and less than far future Win 10. // Unfortunately, an unmanifested process will also pass these validations, // but an unmanifested process also can't use Windows APIs to determine the actual version. REQUIRE(Runtime::IsCurrentOSVersionGreaterThanOrEqual(Version("6.1"))); REQUIRE(!Runtime::IsCurrentOSVersionGreaterThanOrEqual(Version("10.0.65535"))); } TEST_CASE("VersionLatest", "[versions]") { REQUIRE(Version::CreateLatest().IsLatest()); REQUIRE(Version("latest").IsLatest()); REQUIRE(Version("LATEST").IsLatest()); REQUIRE(!Version("1.0").IsLatest()); RequireLessThan("1.0", "latest"); RequireLessThan("100", "latest"); RequireLessThan("943849587389754876.1", "latest"); RequireEqual("latest", "LATEST"); } TEST_CASE("VersionUnknown", "[versions]") { REQUIRE(Version::CreateUnknown().IsUnknown()); REQUIRE(Version("unknown").IsUnknown()); REQUIRE(Version("UNKNOWN").IsUnknown()); REQUIRE(!Version("1.0").IsUnknown()); RequireLessThan("unknown", "1.0"); RequireLessThan("unknown", "1.fork"); RequireEqual("unknown", "UNKNOWN"); } TEST_CASE("VersionUnknownLessThanLatest", "[versions]") { REQUIRE(Version::CreateUnknown() < Version::CreateLatest()); } TEST_CASE("VersionIsEmpty", "[versions]") { REQUIRE(Version{}.IsEmpty()); REQUIRE(Version{""}.IsEmpty()); REQUIRE(!Version{"1"}.IsEmpty()); REQUIRE(!Version{"0"}.IsEmpty()); Version v{ "1" }; REQUIRE(!v.IsEmpty()); v.Assign(""); REQUIRE(v.IsEmpty()); } TEST_CASE("VersionPartAt", "[versions]") { REQUIRE(Version{}.PartAt(0).Integer == 0); REQUIRE(Version{"1"}.PartAt(0).Integer == 1); REQUIRE(Version{"1"}.PartAt(1).Integer == 0); REQUIRE(Version{"1"}.PartAt(9999).Integer == 0); } TEST_CASE("UInt64Version_Success_FourParts", "[versions]") { Version expectedVersion("1.2.3.4"); UInt64Version versionNumberFromNumber(0x0001000200030004); UInt64Version versionNumberFromString("1.2.3.4"); REQUIRE(expectedVersion == versionNumberFromNumber); REQUIRE(expectedVersion == versionNumberFromString); REQUIRE(expectedVersion.ToString() == versionNumberFromNumber.ToString()); REQUIRE(expectedVersion.ToString() == versionNumberFromString.ToString()); } TEST_CASE("UInt64Version_Success_LessThanFourParts", "[versions]") { UInt64Version versionNumberFromNumber(0x0001000200030000); UInt64Version versionNumberFromString("1.2.3"); REQUIRE(versionNumberFromNumber == versionNumberFromString); } TEST_CASE("UInt64Version_Success_NoOverflow", "[versions]") { REQUIRE_NOTHROW(UInt64Version("65535.65535.65535.65535")); // 65535 => 0xffff REQUIRE_NOTHROW(UInt64Version(0xffffffffffffffff)); } TEST_CASE("UInt64Version_Fail_Overflow", "[versions]") { REQUIRE_THROWS(UInt64Version("1.0.0.65536")); // 65536 => 0x10000 } TEST_CASE("UInt64Version_Fail_MoreThanFourParts", "[versions]") { REQUIRE_THROWS(UInt64Version("1.0.0.0.1")); } TEST_CASE("UInt64Version_Fail_NonNumeric", "[versions]") { REQUIRE_THROWS(UInt64Version("1.0.0.a")); } TEST_CASE("ApproximateVersionParse", "[versions]") { Version v1_0{ "1.0" }; Version v1_0_LessThan{ v1_0, Version::ApproximateComparator::LessThan }; Version v1_0_GreaterThan{ v1_0, Version::ApproximateComparator::GreaterThan }; Version v1_0_LessThanFromString = Version{ "< 1.0" }; Version v1_0_GreaterThanFromString = Version{ "> 1.0" }; REQUIRE_FALSE(v1_0.IsApproximate()); REQUIRE(v1_0_LessThanFromString.IsApproximate()); REQUIRE(v1_0_GreaterThanFromString.IsApproximate()); REQUIRE(v1_0_LessThan == v1_0_LessThanFromString); REQUIRE(v1_0_GreaterThan == v1_0_GreaterThanFromString); REQUIRE_THROWS(Version{ "< Unknown" }); REQUIRE_THROWS(Version{ v1_0_LessThan, Version::ApproximateComparator::LessThan }); REQUIRE_THROWS(Version{ Version::CreateUnknown(), Version::ApproximateComparator::LessThan }); } TEST_CASE("ApproximateVersionCompare", "[versions]") { RequireEqual("< 1.0", "< 1.0"); RequireEqual("< 1.0", "< 1.0.0"); RequireEqual("> 1.0", "> 1.0"); RequireEqual("> 1.0", "> 1.0.0"); RequireLessThan("< 1.0", "1.0"); RequireLessThan("< 1.0", "> 1.0"); RequireLessThan("1.0", "> 1.0"); RequireLessThan("0.9", "< 1.0"); RequireLessThan("> 1.0", "1.1"); // With latest RequireLessThan("< latest", "latest"); RequireLessThan("latest", "> latest"); RequireLessThan("9999", "< latest"); } TEST_CASE("VersionRange", "[versions]") { // Create REQUIRE_NOTHROW(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }); REQUIRE_NOTHROW(VersionRange{ Version{ "1.0" }, Version{ "1.0" } }); REQUIRE_NOTHROW(VersionRange{ Version{ "2.0" }, Version{ "1.0" } }); // Overlaps REQUIRE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{ Version{ "2.0" }, Version{ "3.0" } })); REQUIRE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{ Version{ "1.0" }, Version{ "1.0" } })); REQUIRE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{ Version{ "0.5" }, Version{ "1.5" } })); REQUIRE_FALSE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{ Version{ "2.1" }, Version{ "3.0" } })); REQUIRE_FALSE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{})); // Empty REQUIRE(VersionRange{}.IsEmpty()); REQUIRE_THROWS(VersionRange{}.GetMinVersion()); REQUIRE_THROWS(VersionRange{}.GetMaxVersion()); // Less than compare REQUIRE_THROWS(VersionRange{ Version{ "0.5" }, Version{ "1.0" } } < VersionRange{ Version{ "1.0" }, Version{ "2.0" } }); REQUIRE_THROWS(VersionRange{} < VersionRange{ Version{ "1.0" }, Version{ "2.0" } }); REQUIRE(VersionRange{ Version{ "0.5" }, Version{ "1.0" } } < VersionRange{ Version{ "1.5" }, Version{ "2.0" } }); REQUIRE_FALSE(VersionRange{ Version{ "1.5" }, Version{ "2.0" } } < VersionRange{ Version{ "0.5" }, Version{ "1.0" } }); } TEST_CASE("GatedVersion", "[versions]") { REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0.1" })); REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0" })); REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1" })); REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0.alpha" })); REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0.1.2.3" })); REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0.*" })); REQUIRE_FALSE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.1.1" })); REQUIRE(GatedVersion("1.*.*"sv).IsValidVersion({ "1.*.1" })); REQUIRE(GatedVersion("1.*.*"sv).IsValidVersion({ "1.*.*" })); REQUIRE_FALSE(GatedVersion("1.*.*"sv).IsValidVersion({ "1.1.1" })); REQUIRE(GatedVersion("1.0.1"sv).IsValidVersion({ "1.0.1" })); REQUIRE_FALSE(GatedVersion("1.0.1"sv).IsValidVersion({ "1.1.1" })); } TEST_CASE("SemanticVersion", "[versions]") { REQUIRE_THROWS_HR(SemanticVersion("1.2.3.4"), E_INVALIDARG); REQUIRE_THROWS_HR(SemanticVersion("1.2abc.3"), E_INVALIDARG); SemanticVersion version = SemanticVersion("1.2.3-alpha"); REQUIRE(version.IsPrerelease()); REQUIRE(version.PrereleaseVersion() == Version("alpha")); REQUIRE(!version.HasBuildMetadata()); REQUIRE(version.PartAt(2).Other == "-alpha"); version = SemanticVersion("1.2.3-4.5.6"); REQUIRE(version.IsPrerelease()); REQUIRE(version.PrereleaseVersion() == Version("4.5.6")); REQUIRE(!version.HasBuildMetadata()); REQUIRE(version.PartAt(2).Other == "-4.5.6"); // Really shouldn't be allowed, but we are loose here version = SemanticVersion("1.2+build"); REQUIRE(!version.IsPrerelease()); REQUIRE(version.HasBuildMetadata()); REQUIRE(version.BuildMetadata() == Version("build")); REQUIRE(version.PartAt(2).Other == "+build"); version = SemanticVersion("1.2.3-beta+4.5.6"); REQUIRE(version.IsPrerelease()); REQUIRE(version.PrereleaseVersion() == Version("beta")); REQUIRE(version.HasBuildMetadata()); REQUIRE(version.BuildMetadata() == Version("4.5.6")); REQUIRE(version.PartAt(2).Other == "-beta+4.5.6"); } TEST_CASE("OpenTypeFontVersion", "[versions]") { // Valid font version. OpenTypeFontVersion version = OpenTypeFontVersion("Version 1.234"); REQUIRE(version.ToString() == "1.234"); REQUIRE(version.GetParts().size() == 2); REQUIRE(version.PartAt(0).Integer == 1); REQUIRE(version.PartAt(1).Integer == 234); // Font version with additional metadata. version = OpenTypeFontVersion("Version 9.876.54 ;2024"); REQUIRE(version.ToString() == "9.876"); REQUIRE(version.GetParts().size() == 2); REQUIRE(version.PartAt(0).Integer == 9); REQUIRE(version.PartAt(1).Integer == 876); // Invalid version. Font version must have at least 2 parts. REQUIRE_NOTHROW(version = OpenTypeFontVersion("1234567")); REQUIRE(version.IsUnknown()); REQUIRE(version.ToString() == "Unknown"); // Major and minor parts must have digits. REQUIRE_NOTHROW(version = OpenTypeFontVersion(" abc.def ")); REQUIRE(version.IsUnknown()); REQUIRE(version.ToString() == "Unknown"); } ================================================ FILE: src/AppInstallerCLITests/WindowsFeature.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "WorkflowCommon.h" #include #include #include "TestHooks.h" using namespace AppInstaller::CLI; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; using namespace TestCommon; TEST_CASE("InstallFlow_WindowsFeatureDoesNotExist", "[windowsFeature]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TestUserSettings testSettings; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideOpenDependencySource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_WindowsFeatures.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::WindowsFeatureNotFound(LocIndView{ "testFeature1" })).get()) != std::string::npos); // "badFeature" should not be displayed as the flow should terminate after failing to find the first feature. REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::WindowsFeatureNotFound(LocIndView{ "testFeature2" })).get()) == std::string::npos); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeatureOverrideRequired).get()) != std::string::npos); } TEST_CASE("InstallFlow_FailedToEnableWindowsFeature", "[windowsFeature]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TestUserSettings testSettings; std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideOpenDependencySource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_WindowsFeatures.yaml").GetPath().u8string()); auto setDoesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(0xc0040001); // DISMAPI_E_DISMAPI_NOT_INITIALIZED InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeatureOverrideRequired).get()) != std::string::npos); } TEST_CASE("InstallFlow_FailedToEnableWindowsFeature_Force", "[windowsFeature]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TestUserSettings testSettings; auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); auto expectedErrorCode = 0xc0040001; // DISMAPI_E_DISMAPI_NOT_INITIALIZED auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(expectedErrorCode); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideOpenDependencySource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_WindowsFeatures.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Force); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(context.GetTerminationHR() == ERROR_SUCCESS); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeature(LocIndView{ "testFeature1" }, expectedErrorCode)).get()) != std::string::npos); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeature(LocIndView{ "testFeature2" }, expectedErrorCode)).get()) != std::string::npos); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::FailedToEnableWindowsFeatureOverridden).get()) != std::string::npos); REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } TEST_CASE("InstallFlow_RebootRequired", "[windowsFeature]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TestUserSettings testSettings; // Override with reboot required HRESULT. auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(ERROR_SUCCESS_REBOOT_REQUIRED); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideOpenDependencySource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_WindowsFeatures.yaml").GetPath().u8string()); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL); REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::RebootRequiredToEnableWindowsFeatureOverrideRequired).get()) != std::string::npos); } TEST_CASE("InstallFlow_RebootRequired_Force", "[windowsFeature]") { if (!AppInstaller::Runtime::IsRunningAsAdmin()) { WARN("Test requires admin privilege. Skipped."); return; } TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TestUserSettings testSettings; // Override with reboot required HRESULT. auto doesFeatureExistOverride = TestHook::SetDoesWindowsFeatureExistResult_Override(ERROR_SUCCESS); auto setEnableFeatureOverride = TestHook::SetEnableWindowsFeatureResult_Override(ERROR_SUCCESS_REBOOT_REQUIRED); std::ostringstream installOutput; TestContext context{ installOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); OverrideForShellExecute(context); OverrideOpenDependencySource(context); context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_WindowsFeatures.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::Force); InstallCommand install({}); install.Execute(context); INFO(installOutput.str()); // Verify Installer is called and parameters are passed in. REQUIRE(context.GetTerminationHR() == ERROR_SUCCESS); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::RebootRequiredToEnableWindowsFeatureOverridden).get()) != std::string::npos); REQUIRE(std::filesystem::exists(installResultPath.GetPath())); std::ifstream installResultFile(installResultPath.GetPath()); REQUIRE(installResultFile.is_open()); std::string installResultStr; std::getline(installResultFile, installResultStr); REQUIRE(installResultStr.find("/custom") != std::string::npos); REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } ================================================ FILE: src/AppInstallerCLITests/WorkFlow.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" #include "TestSettings.h" #include #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; void VerifyMotw(const std::filesystem::path& testFile, DWORD zone) { std::filesystem::path motwFile(testFile); motwFile += ":Zone.Identifier:$data"; std::ifstream motwStream(motwFile); std::stringstream motwContent; motwContent << motwStream.rdbuf(); std::string motwContentStr = motwContent.str(); motwStream.close(); REQUIRE(motwContentStr.find("ZoneId=" + std::to_string(zone)) != std::string::npos); } TEST_CASE("VerifyInstallerTrustLevelAndUpdateInstallerFileMotw", "[DownloadInstaller][workflow]") { TestCommon::TempFile testInstallerPath("TestInstaller.txt"); std::ofstream ofile(testInstallerPath, std::ofstream::out); ofile << "test"; ofile.close(); ApplyMotwIfApplicable(testInstallerPath, URLZONE_INTERNET); VerifyMotw(testInstallerPath, 3); std::ostringstream updateMotwOutput; TestContext context{ updateMotwOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Add({ {}, {} }); context.Add(testInstallerPath); auto packageVersion = std::make_shared(Manifest{}); auto testSource = std::make_shared(); testSource->Details.TrustLevel = SourceTrustLevel::Trusted; packageVersion->Source = testSource; context.Add(packageVersion); ManifestInstaller installer; installer.Url = "http://NotTrusted.com"; context.Add(std::move(installer)); context << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable; REQUIRE(WI_IsFlagSet(context.GetFlags(), ContextFlag::InstallerTrusted)); VerifyMotw(testInstallerPath, 2); testSource->Details.TrustLevel = SourceTrustLevel::None; context.ClearFlags(ContextFlag::InstallerTrusted); context << VerifyInstallerHash << UpdateInstallerFileMotwIfApplicable; REQUIRE_FALSE(WI_IsFlagSet(context.GetFlags(), ContextFlag::InstallerTrusted)); VerifyMotw(testInstallerPath, 3); INFO(updateMotwOutput.str()); } TEST_CASE("ValidateCommand_Dependencies", "[workflow][dependencies]") { std::ostringstream validateOutput; TestContext context{ validateOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Args::Type::ValidateManifest, TestDataFile("Manifest-Good-AllDependencyTypes.yaml").GetPath().u8string()); ValidateCommand validate({}); validate.Execute(context); INFO(validateOutput.str()); // Verify all types of dependencies are printed REQUIRE(validateOutput.str().find(Resource::LocString(Resource::String::ValidateCommandReportDependencies).get()) != std::string::npos); REQUIRE(validateOutput.str().find("WindowsFeaturesDep") != std::string::npos); REQUIRE(validateOutput.str().find("WindowsLibrariesDep") != std::string::npos); // PackageDep1 has minimum version (1.0), PackageDep2 doesn't (shouldn't show [>=...]) REQUIRE(validateOutput.str().find("Package.Dep1-x64 [>= 1.0]") != std::string::npos); REQUIRE(validateOutput.str().find("Package.Dep2-x64") != std::string::npos); REQUIRE(validateOutput.str().find("Package.Dep2-x64 [") == std::string::npos); REQUIRE(validateOutput.str().find("ExternalDep") != std::string::npos); } TEST_CASE("AdminSetting_LocalManifestFiles", "[LocalManifests][workflow]") { RemoveSetting(Stream::AdminSettings); { // If there's no admin setting, using local manifest should fail. Execution::Args args; args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); InstallCommand installCommand({}); REQUIRE_THROWS(installCommand.ValidateArguments(args)); } { // Using settings command to enable local manifests std::ostringstream settingsOutput; TestContext context{ settingsOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::AdminSettingEnable, "LocalManifestFiles"sv); context.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); SettingsCommand settings({}); settings.Execute(context); INFO(settingsOutput.str()); } { // Now using local manifests should succeed Execution::Args args2; args2.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); InstallCommand installCommand2({}); REQUIRE_NOTHROW(installCommand2.ValidateArguments(args2)); } { // Using settings command to disable local manifests std::ostringstream settingsOutput2; TestContext context2{ settingsOutput2, std::cin }; auto previousThreadGlobals = context2.SetForCurrentThread(); context2.Args.AddArg(Execution::Args::Type::AdminSettingDisable, "LocalManifestFiles"sv); context2.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); SettingsCommand settings2({}); settings2.Execute(context2); INFO(settingsOutput2.str()); } { // Now using local manifests should fail Execution::Args args3; args3.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); InstallCommand installCommand3({}); REQUIRE_THROWS(installCommand3.ValidateArguments(args3)); } } TEST_CASE("Export_Settings", "[Settings][workflow]") { RemoveSetting(Stream::AdminSettings); { // No admin settings, local manifest should be false. std::ostringstream exportOutput; TestContext context{ exportOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); SettingsExportCommand settingsExportCommand({}); settingsExportCommand.Execute(context); auto json = ConvertToJson(exportOutput.str()); REQUIRE(!json.isNull()); REQUIRE_FALSE(json["adminSettings"]["LocalManifestFiles"].asBool()); auto userSettingsFileValue = std::string(json["userSettingsFile"].asCString()); REQUIRE(userSettingsFileValue.find("settings.json") != std::string::npos); } { // Enable local manifest and verify export works. std::ostringstream settingsOutput; TestContext context{ settingsOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); context.Args.AddArg(Execution::Args::Type::AdminSettingEnable, "LocalManifestFiles"sv); context.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); SettingsCommand settings({}); settings.Execute(context); std::ostringstream exportOutput; TestContext context2{ exportOutput, std::cin }; auto previousThreadGlobals2 = context2.SetForCurrentThread(); SettingsExportCommand settingsExportCommand({}); settingsExportCommand.Execute(context2); auto json = ConvertToJson(exportOutput.str()); REQUIRE(!json.isNull()); REQUIRE(json["adminSettings"]["LocalManifestFiles"].asBool()); auto userSettingsFileValue = std::string(json["userSettingsFile"].asCString()); REQUIRE(userSettingsFileValue.find("settings.json") != std::string::npos); } } ================================================ FILE: src/AppInstallerCLITests/WorkflowCommon.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DependenciesTestSource.h" #include "WorkflowCommon.h" #include #include #include #include #include #include using namespace AppInstaller::CLI; using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility; namespace TestCommon { namespace TSR { const TestSourceResult TestQuery_ReturnOne( "TestQueryReturnOne"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make(std::vector{ manifest }, source), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnOne"))); }); const TestSourceResult TestQuery_ReturnTwo( "TestQueryReturnTwo"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make(std::vector{ manifest }, source), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("Manifest-Good.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make(std::vector{ manifest2 }, source), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); }); const TestSourceResult TestInstaller_Exe( "AppInstallerCliTest.TestExeInstaller"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); auto manifest3 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2.yaml")); auto testPackage = TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, }, std::vector{ manifest3, manifest2, manifest }, source ); for (auto& availablePackage : testPackage->Available) { availablePackage->IsSameOverride = [](const IPackage*, const IPackage*) { return true; }; } matches.emplace_back( ResultMatch( testPackage, PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); }); const TestSourceResult TestInstaller_Exe_UpgradeUsesAgreements( "AppInstallerCliTest.TestExeInstaller"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); auto manifest3 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2_LicenseAgreement.yaml")); auto testPackage = TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, }, std::vector{ manifest3, manifest2, manifest }, source ); for (auto& availablePackage : testPackage->Available) { availablePackage->IsSameOverride = [](const IPackage*, const IPackage*) { return true; }; } matches.emplace_back( ResultMatch( testPackage, PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); }); const TestSourceResult TestInstaller_Portable( "AppInstallerCliTest.TestPortableInstaller"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Portable.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Portable.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Portable" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestPortableInstaller"))); }); const TestSourceResult TestInstaller_Msix( "AppInstallerCliTest.TestMsixInstaller"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Msix_StreamingFlow.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Msix.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller"))); }); const TestSourceResult TestInstaller_Msix_UpgradeUsesAgreements( "AppInstallerCliTest.TestMsixInstaller"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Msix_StreamingFlow.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Msix_LicenseAgreement.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller"))); }); const TestSourceResult TestInstaller_Msix_UpgradeRequiresExplicit( "AppInstallerCliTest.TestMsixInstaller"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Msix_StreamingFlow.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Msix.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Msix" }, { PackageVersionMetadata::PinnedState, "PinnedByManifest" }, }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller"))); }); const TestSourceResult TestInstaller_Zip( "AppInstallerCliTest.TestZipInstaller"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Zip_Exe.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Zip_Exe.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestZipInstaller"))); }); const TestSourceResult TestInstaller_MSStore( "AppInstallerCliTest.TestMSStoreInstaller"sv, [](std::vector& matches, std::weak_ptr source) { auto installed = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_MSStore.yaml")); auto available = installed; // Override the installed version to not be Latest installed.Version = "1.0.0.0"; matches.emplace_back( ResultMatch( TestCompositePackage::Make( installed, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "MSStore" } }, std::vector{ available }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMSStoreInstaller"))); }); const TestSourceResult TestInstaller_Exe_ExpectedReturnCodes( "AppInstallerCliTest.ExpectedReturnCodes"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_ExpectedReturnCodes.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_ExpectedReturnCodes.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.ExpectedReturnCodes"))); }); const TestSourceResult TestInstaller_Exe_UnknownVersion( "TestExeInstallerWithUnknownVersion"sv, [](std::vector& matches, std::weak_ptr source) { auto installed = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_UnknownVersion.yaml")); auto available = installed; // Override the installed version to be unknown. installed.Version = "unknown"; matches.emplace_back( ResultMatch( TestCompositePackage::Make( installed, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ available }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeUnknownVersion"))); }); const TestSourceResult TestInstaller_Exe_LatestInstalled( "TestExeInstallerWithLatestInstalled"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest2, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); }); const TestSourceResult TestInstaller_Exe_IncompatibleInstallerType( "TestExeInstallerWithIncompatibleInstallerType"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); }); const TestSourceResult TestInstaller_Exe_DifferentInstallerType( "TestExeInstallerWithDifferentInstalledType"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_ARPInstallerType.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); }); const TestSourceResult TestInstaller_Exe_UnsupportedArguments( "TestExeInstallerWithUnsupportedArguments"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_UnsupportedArgs.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); }); const TestSourceResult TestInstaller_Exe_NothingInstalled( "TestExeInstallerWithNothingInstalled"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( std::vector{ manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); }); const TestSourceResult TestInstaller_Exe_Dependencies( "AppInstallerCliTest.TestExeInstaller.Dependencies"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Exe_Dependencies.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_ExeDependencies.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller.Dependencies"))); }); const TestSourceResult TestInstaller_Msix_WFDependency( "AppInstallerCliTest.TestMsixInstaller.WFDep"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Msix_WFDependency.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( std::vector{ manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestMsixInstaller.WFDep"))); }); const TestSourceResult TestInstaller_Exe_LicenseAgreement( "TestInstallerWithLicenseAgreement"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_LicenseAgreement.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2_LicenseAgreement.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestInstallerWithLicenseAgreement"))); }); const TestSourceResult TestInstaller_Exe_UpgradeAllWithDuplicateUpgradeItems( "TestUpgradeAllWithDuplicateUpgradeItems"sv, [](std::vector& matches, std::weak_ptr source) { auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); auto manifest3 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2.yaml")); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest, TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, }, std::vector{ manifest3, manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); matches.emplace_back( ResultMatch( TestCompositePackage::Make( manifest2, TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, }, std::vector{ manifest3, manifest2, manifest }, source ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); }); } SearchResult WorkflowTestSource::Search(const SearchRequest& request) const { std::string input; if (request.Query) { input = request.Query->Value; } else if (!request.Inclusions.empty()) { input = request.Inclusions[0].Value; } else if (!request.Filters.empty()) { input = request.Filters[0].Value; } SearchResult result; for (const auto& testSourceResult : m_testSourceResults) { if (input.empty() || CaseInsensitiveEquals(input, testSourceResult.Query)) { testSourceResult.AddResults(result.Matches, shared_from_this()); } } return result; } void WorkflowTestSource::AddResult(const TestSourceResult& testSourceResult) { m_testSourceResults.push_back(testSourceResult); } std::shared_ptr CreateTestSource(std::vector&& testSourceResults) { return std::make_shared(std::move(testSourceResults)); } TestContext::TestContext(std::ostream& out, std::istream& in) : TestContext(out, in, false, std::make_shared>()) { WorkflowTaskOverride wto { RemoveInstaller, [](TestContext&) { // Do nothing; we never want to remove the test files. } }; // Mark this one as used so that it doesn't anger the destructor. wto.UseCount++; Override(wto); } TestContext::TestContext(std::ostream& out, std::istream& in, bool isClone, std::shared_ptr> overrides) : m_out(out), m_in(in), m_overrides(overrides), m_isClone(isClone), Context(out, in) { m_shouldExecuteWorkflowTask = [this](const Workflow::WorkflowTask& task) { auto itr = std::find_if(m_overrides->begin(), m_overrides->end(), [&](const WorkflowTaskOverride& wto) { return wto.Target == task; }); if (itr == m_overrides->end()) { return true; } else { itr->UseCount++; itr->Override(*this); return false; } }; } TestContext::~TestContext() { if (!m_isClone) { for (const auto& wto : *m_overrides) { if (wto.UseCount == 0) { FAIL_CHECK("Unused override " + wto.Target.GetName()); } else if (wto.ExpectedUseCount != -1 && wto.ExpectedUseCount != wto.UseCount) { FAIL_CHECK("Used override count does not match expected " + wto.Target.GetName()); } } } } void TestContext::Override(const WorkflowTaskOverride& wto) { m_overrides->emplace_back(wto); } std::unique_ptr TestContext::CreateSubContext() { auto clone = std::make_unique(m_out, m_in, true, m_overrides); clone->SetFlags(this->GetFlags()); CopyArgsToSubContext(clone.get()); return clone; } void OverrideForOpenSource(TestContext& context, std::shared_ptr testSource, bool overrideOpenCompositeSource) { context.Override({ "OpenSource", [=](TestContext& context) { context.Add(Source{ testSource }); } }); if (overrideOpenCompositeSource) { context.Override({ "OpenCompositeSource", [](TestContext&) { } }); } } void OverrideForCompositeInstalledSource(TestContext& context, std::shared_ptr testSource) { context.Override({ "OpenSource", [](TestContext&) { } }); context.Override({ "OpenCompositeSource", [=](TestContext& context) { context.Add(Source{ testSource }); } }); } void OverrideForUpdateInstallerMotw(TestContext& context) { context.Override({ UpdateInstallerFileMotwIfApplicable, [](TestContext&) { } }); } void OverrideForCheckExistingInstaller(TestContext& context) { context.Override({ CheckForExistingInstaller, [](TestContext&) { } }); } void OverrideForShellExecute(TestContext& context, int expectedUseCount) { OverrideForCheckExistingInstaller(context); context.Override({ DownloadInstallerFile, [](TestContext& context) { context.Add({ {}, {} }); context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); }, expectedUseCount }); context.Override({ RenameDownloadedInstaller, [](TestContext&) { }, expectedUseCount }); OverrideForUpdateInstallerMotw(context); } void OverrideForShellExecute(TestContext& context, std::vector& installationLog) { context.Override({ DownloadInstallerFile, [&installationLog](TestContext& context) { context.Add({ {}, {} }); context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); auto dependency = Dependency(DependencyType::Package, context.Get().Id, context.Get().Version); installationLog.push_back(dependency); } }); context.Override({ RenameDownloadedInstaller, [](TestContext&) { } }); OverrideForUpdateInstallerMotw(context); } void OverrideForPortableInstall(TestContext& context) { context.Override({ Workflow::details::PortableInstall, [](TestContext&) { std::filesystem::path temp = std::filesystem::temp_directory_path(); temp /= "TestPortableInstalled.txt"; std::ofstream file(temp, std::ofstream::out); file.close(); } }); } void OverrideForPortableInstallFlow(TestContext& context) { context.Override({ DownloadInstallerFile, [](TestContext& context) { context.Add({ {}, {} }); context.Add(TestDataFile("AppInstallerTestExeInstaller.exe")); } }); context.Override({ RenameDownloadedInstaller, [](TestContext&) { } }); OverrideForUpdateInstallerMotw(context); OverrideForPortableInstall(context); } void OverridePortableInstaller(TestContext& context) { context.Override({ DownloadInstallerFile, [](TestContext& context) { std::filesystem::path tempDirectory = std::filesystem::temp_directory_path(); const auto& installerPath = TestDataFile("AppInstallerTestExeInstaller.exe").GetPath(); const auto& tempInstallerPath = tempDirectory / "AppInstallerTestExeInstaller.exe"; std::filesystem::copy(installerPath, tempInstallerPath, std::filesystem::copy_options::overwrite_existing); context.Add(tempInstallerPath); std::ifstream inStream{ tempInstallerPath, std::ifstream::binary }; SHA256::HashBuffer fileHash = SHA256::ComputeHash(inStream); context.Add({ fileHash, DownloadResult{ fileHash } }); } }); context.Override({ RenameDownloadedInstaller, [](TestContext&) { } }); OverrideForUpdateInstallerMotw(context); } void OverrideForExtractInstallerFromArchive(TestContext& context) { context.Override({ ExtractFilesFromArchive, [](TestContext&) { } }); } void OverrideForVerifyAndSetNestedInstaller(TestContext& context) { context.Override({ VerifyAndSetNestedInstaller, [](TestContext&) { } }); } void OverrideForMSIX(TestContext& context) { context.Override({ Workflow::details::MsixInstall, [](TestContext& context) { std::filesystem::path temp = std::filesystem::temp_directory_path(); temp /= "TestMsixInstalled.txt"; std::ofstream file(temp, std::ofstream::out); if (context.Contains(Execution::Data::InstallerPath)) { file << context.Get().u8string(); } else { file << context.Get()->Url; } file.close(); } }); } void OverrideForMSStore(TestContext& context, bool isUpdate) { if (isUpdate) { context.Override({ MSStoreUpdate, [](TestContext& context) { std::filesystem::path temp = std::filesystem::temp_directory_path(); temp /= "TestMSStoreUpdated.txt"; std::ofstream file(temp, std::ofstream::out); file << context.Get()->ProductId; file.close(); } }); } else { context.Override({ MSStoreInstall, [](TestContext& context) { std::filesystem::path temp = std::filesystem::temp_directory_path(); temp /= "TestMSStoreInstalled.txt"; std::ofstream file(temp, std::ofstream::out); file << context.Get()->ProductId; file.close(); } }); } context.Override({ Workflow::EnsureStorePolicySatisfied, [](TestContext&) { } }); } void OverrideOpenDependencySource(TestContext& context) { context.Override({ Workflow::OpenDependencySource, [](TestContext& context) { context.Add(Source{ std::make_shared() }); } }); } void OverrideEnableWindowsFeaturesDependencies(TestContext& context) { context.Override({ Workflow::EnableWindowsFeaturesDependencies, [](TestContext&) { } }); } void OverrideRegisterStartupAfterReboot(TestContext& context) { context.Override({ "RegisterStartupAfterReboot", [](TestContext&) { } }); } void OverrideDownloadInstallerFileForMSStoreDownload(TestContext& context) { context.Override({ DownloadInstallerFile, [](TestContext& context) { const auto& installer = context.Get().value(); const auto& installerPath = context.Get(); std::ofstream file(installerPath, std::ofstream::out | std::ofstream::trunc); file << installer.Url; file.close(); context.Add({ {}, {} }); } }); } } ================================================ FILE: src/AppInstallerCLITests/WorkflowCommon.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSource.h" #include #include #include #include #include #define REQUIRE_TERMINATED_WITH(_context_,_hr_) \ REQUIRE(_context_.IsTerminated()); \ REQUIRE(_hr_ == _context_.GetTerminationHR()) namespace TestCommon { using namespace std::string_view_literals; // Possible results returned when searching the WorkflowTestSource. // If the search query matches with this object or is empty, it adds to the search results. struct TestSourceResult { using AddResultsFunction = std::function&, std::weak_ptr)>; TestSourceResult(std::string_view query, AddResultsFunction addResults) : Query(query), AddResults(addResults) {} std::string Query; AddResultsFunction AddResults; }; namespace TSR { const extern TestSourceResult TestQuery_ReturnOne; const extern TestSourceResult TestQuery_ReturnTwo; const extern TestSourceResult TestInstaller_Exe; const extern TestSourceResult TestInstaller_Exe_Dependencies; const extern TestSourceResult TestInstaller_Exe_DifferentInstallerType; const extern TestSourceResult TestInstaller_Exe_ExpectedReturnCodes; const extern TestSourceResult TestInstaller_Exe_IncompatibleInstallerType; const extern TestSourceResult TestInstaller_Exe_LatestInstalled; const extern TestSourceResult TestInstaller_Exe_LicenseAgreement; const extern TestSourceResult TestInstaller_Exe_NothingInstalled; const extern TestSourceResult TestInstaller_Exe_UnknownVersion; const extern TestSourceResult TestInstaller_Exe_UnsupportedArguments; const extern TestSourceResult TestInstaller_Exe_UpgradeAllWithDuplicateUpgradeItems; const extern TestSourceResult TestInstaller_Exe_UpgradeUsesAgreements; const extern TestSourceResult TestInstaller_Msix; const extern TestSourceResult TestInstaller_Msix_UpgradeRequiresExplicit; const extern TestSourceResult TestInstaller_Msix_UpgradeUsesAgreements; const extern TestSourceResult TestInstaller_Msix_WFDependency; const extern TestSourceResult TestInstaller_MSStore; const extern TestSourceResult TestInstaller_Zip; const extern TestSourceResult TestInstaller_Portable; } struct WorkflowTestSource : public TestSource { WorkflowTestSource() {} WorkflowTestSource(std::vector&& testSourceResults) : m_testSourceResults(std::move(testSourceResults)) {} AppInstaller::Repository::SearchResult Search(const AppInstaller::Repository::SearchRequest& request) const override; void AddResult(const TestSourceResult& testSourceResult); private: std::vector m_testSourceResults; }; std::shared_ptr CreateTestSource(std::vector&& testSourceResults); struct TestContext; struct WorkflowTaskOverride { WorkflowTaskOverride(AppInstaller::CLI::Workflow::WorkflowTask::Func f, const std::function& o, int expectedUseCount = -1) : Target(f), Override(o), ExpectedUseCount(expectedUseCount) {} WorkflowTaskOverride(std::string_view n, const std::function& o, int expectedUseCount = -1) : Target(n), Override(o), ExpectedUseCount(expectedUseCount) {} WorkflowTaskOverride(const AppInstaller::CLI::Workflow::WorkflowTask& t, const std::function& o, int expectedUseCount = -1) : Target(t), Override(o), ExpectedUseCount(expectedUseCount) {} // -1 means no check on actual use count, as long as it's used. int ExpectedUseCount = -1; int UseCount = 0; AppInstaller::CLI::Workflow::WorkflowTask Target; std::function Override; }; // Enables overriding the behavior of specific workflow tasks. struct TestContext : public AppInstaller::CLI::Execution::Context { TestContext(std::ostream& out, std::istream& in); TestContext(std::ostream& out, std::istream& in, bool isClone, std::shared_ptr> overrides); ~TestContext(); void Override(const WorkflowTaskOverride& wto); std::unique_ptr CreateSubContext() override; private: std::shared_ptr> m_overrides; std::ostream& m_out; std::istream& m_in; bool m_isClone = false; }; void OverrideForOpenSource(TestContext& context, std::shared_ptr testSource, bool overrideOpenCompositeSource = false); void OverrideForCompositeInstalledSource(TestContext& context, std::shared_ptr testSource); void OverrideForUpdateInstallerMotw(TestContext& context); void OverrideForCheckExistingInstaller(TestContext& context); void OverrideForShellExecute(TestContext& context, int expectedUseCount = -1); void OverrideForShellExecute(TestContext& context, std::vector& installationLog); void OverrideForPortableInstall(TestContext& context); void OverrideForPortableInstallFlow(TestContext& context); void OverridePortableInstaller(TestContext& context); void OverrideForExtractInstallerFromArchive(TestContext& context); void OverrideForVerifyAndSetNestedInstaller(TestContext& context); void OverrideForMSIX(TestContext& context); void OverrideForMSStore(TestContext& context, bool isUpdate); void OverrideOpenDependencySource(TestContext& context); void OverrideEnableWindowsFeaturesDependencies(TestContext& context); void OverrideRegisterStartupAfterReboot(TestContext& context); void OverrideDownloadInstallerFileForMSStoreDownload(TestContext& context); } ================================================ FILE: src/AppInstallerCLITests/WorkflowGroupPolicy.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include "TestSettings.h" #include "AppInstallerErrors.h" #include "Commands/InstallCommand.h" #include "Commands/RootCommand.h" #include "Commands/ShowCommand.h" #include "Commands/UpgradeCommand.h" #include "Commands/ValidateCommand.h" using namespace TestCommon; using namespace AppInstaller::CLI; using namespace AppInstaller::Settings; using namespace std::string_view_literals; TEST_CASE("GroupPolicy_WinGet", "[groupPolicy]") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::WinGet, PolicyState::Disabled); SECTION("Install is blocked") { std::ostringstream output; Execution::Context context{ output, std::cin }; context.Args.AddArg(Execution::Args::Type::Query, "Fake.Package"sv); InstallCommand installCommand({}); REQUIRE_POLICY_EXCEPTION( installCommand.Execute(context), TogglePolicy::Policy::WinGet); } SECTION("Info is not blocked") { std::ostringstream output; Execution::Context context{ output, std::cin }; context.Args.AddArg(Execution::Args::Type::Info); RootCommand rootCommand({}); rootCommand.Execute(context); REQUIRE_FALSE(context.IsTerminated()); } } TEST_CASE("GroupPolicy_SettingsCommand", "[groupPolicy]") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::Settings, PolicyState::Disabled); Invocation inv{ std::vector{ "settings" } }; RootCommand rootCommand; REQUIRE_THROWS(rootCommand.FindSubCommand(inv)); } TEST_CASE("GroupPolicy_LocalManifests", "[groupPolicy]") { GroupPolicyTestOverride policies; policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::Disabled); SECTION("Blocked on install") { Execution::Args args; args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); InstallCommand installCommand({}); REQUIRE_THROWS(installCommand.ValidateArguments(args)); } SECTION("Blocked on upgrade") { Execution::Args args; args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); UpgradeCommand upgradeCommand({}); REQUIRE_THROWS(upgradeCommand.ValidateArguments(args)); } SECTION("Allowed on show") { Execution::Args args; args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); ShowCommand showCommand({}); REQUIRE_NOTHROW(showCommand.ValidateArguments(args)); } SECTION("Allowed on validate") { Execution::Args args; args.AddArg(Execution::Args::Type::ValidateManifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); ValidateCommand validateCommand({}); REQUIRE_NOTHROW(validateCommand.ValidateArguments(args)); } } TEST_CASE("GroupPolicy_Info", "[groupPolicy]") { GroupPolicyTestOverride policies; std::ostringstream output; Execution::Context context{ output, std::cin }; context.Args.AddArg(Execution::Args::Type::Info); RootCommand rootCommand({}); SECTION("Does not list not configured") { rootCommand.Execute(context); INFO(output.str()); REQUIRE_FALSE(context.IsTerminated()); REQUIRE(output.str().find("Group Policy") == std::string::npos); } SECTION("Shows enabled policies") { policies.SetState(TogglePolicy::Policy::HashOverride, PolicyState::Enabled); rootCommand.Execute(context); INFO(output.str()); REQUIRE_FALSE(context.IsTerminated()); REQUIRE(output.str().find("Group Policy") != std::string::npos); REQUIRE(output.str().find("Hash Override Enabled") != std::string::npos); } SECTION("Shows disabled policies") { policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::Disabled); rootCommand.Execute(context); INFO(output.str()); REQUIRE_FALSE(context.IsTerminated()); REQUIRE(output.str().find("Group Policy") != std::string::npos); REQUIRE(output.str().find("Local Manifest Files Disabled") != std::string::npos); } SECTION("Shows auto update interval") { policies.SetValue(60); rootCommand.Execute(context); INFO(output.str()); REQUIRE_FALSE(context.IsTerminated()); REQUIRE(output.str().find("Group Policy") != std::string::npos); REQUIRE(output.str().find("Source Auto Update Interval In Minutes 60") != std::string::npos); } SECTION("Shows additional sources list") { SourceFromPolicy source; source.Name = "policy-source"; source.Type = "Test.Type"; source.Arg = "test-arg"; policies.SetState(TogglePolicy::Policy::AdditionalSources, PolicyState::Enabled); policies.SetValue({ source }); rootCommand.Execute(context); INFO(output.str()); REQUIRE_FALSE(context.IsTerminated()); REQUIRE(output.str().find("Group Policy") != std::string::npos); REQUIRE(output.str().find("Sources Enabled") != std::string::npos); REQUIRE(output.str().find("Additional source") != std::string::npos); REQUIRE(output.str().find(source.Name) != std::string::npos); REQUIRE(output.str().find(source.Type) != std::string::npos); REQUIRE(output.str().find(source.Arg) != std::string::npos); } SECTION("Shows allowed sources list") { SourceFromPolicy source; source.Name = "allowed-source"; source.Type = "Test.Type"; source.Arg = "test-arg"; policies.SetState(TogglePolicy::Policy::AllowedSources, PolicyState::Enabled); policies.SetValue({ source }); rootCommand.Execute(context); INFO(output.str()); REQUIRE_FALSE(context.IsTerminated()); REQUIRE(output.str().find("Group Policy") != std::string::npos); REQUIRE(output.str().find("Allowed Sources Enabled") != std::string::npos); REQUIRE(output.str().find("Allowed source") != std::string::npos); REQUIRE(output.str().find(source.Name) != std::string::npos); REQUIRE(output.str().find(source.Type) != std::string::npos); REQUIRE(output.str().find(source.Arg) != std::string::npos); } } ================================================ FILE: src/AppInstallerCLITests/Yaml.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include using namespace TestCommon; using namespace AppInstaller::Utility; using namespace AppInstaller::YAML; TEST_CASE("YamlParserTypes", "[YAML]") { auto document = AppInstaller::YAML::Load(TestDataFile("Node-Types.yaml")); auto intUnquoted = document["IntegerUnquoted"]; CHECK(intUnquoted.GetTagType() == Node::TagType::Int); auto intSingleQuoted = document["IntegerSingleQuoted"]; CHECK(intSingleQuoted.GetTagType() == Node::TagType::Str); auto intDoubleQuoted = document["IntegerDoubleQuoted"]; CHECK(intDoubleQuoted.GetTagType() == Node::TagType::Str); auto boolTrue = document["BooleanTrue"]; CHECK(boolTrue.GetTagType() == Node::TagType::Bool); auto strTrue = document["StringTrue"]; CHECK(strTrue.GetTagType() == Node::TagType::Str); auto boolFalse = document["BooleanFalse"]; CHECK(boolFalse.GetTagType() == Node::TagType::Bool); auto strFalse = document["StringFalse"]; CHECK(strFalse.GetTagType() == Node::TagType::Str); auto localTag = document["LocalTag"]; CHECK(localTag.GetTagType() == Node::TagType::Unknown); } TEST_CASE("YamlMergeMappingNode", "[YAML]") { auto document = Load(TestDataFile("Node-Mapping.yaml")); auto mergeNode = document["MergeNode"]; auto mergeNode2 = document["MergeNode2"]; REQUIRE(3 == mergeNode.size()); REQUIRE(2 == mergeNode2.size()); mergeNode.MergeMappingNode(mergeNode2); REQUIRE(5 == mergeNode.size()); } TEST_CASE("YamlMergeMappingNode_CaseInsensitive", "[YAML]") { auto document = Load(TestDataFile("Node-Mapping.yaml")); auto mergeNode = document["MergeNode"]; auto mergeNode2 = document["MergeNode2"]; REQUIRE(3 == mergeNode.size()); REQUIRE(2 == mergeNode2.size()); mergeNode.MergeMappingNode(mergeNode2, true); REQUIRE(4 == mergeNode.size()); } TEST_CASE("YamlMergeSequenceNode", "[YAML]") { auto document = Load(TestDataFile("Node-Merge.yaml")); auto document2 = Load(TestDataFile("Node-Merge2.yaml")); REQUIRE(3 == document["StrawHats"].size()); REQUIRE(2 == document2["StrawHats"].size()); // Internally will call MergeMappingNode. document["StrawHats"].MergeSequenceNode(document2["StrawHats"], "Bounty"); REQUIRE(5 == document["StrawHats"].size()); } TEST_CASE("YamlMergeSequenceNode_CaseInsensitive", "[YAML]") { auto document = Load(TestDataFile("Node-Merge.yaml")); auto document2 = Load(TestDataFile("Node-Merge2.yaml")); REQUIRE(3 == document["StrawHats"].size()); REQUIRE(2 == document2["StrawHats"].size()); // Internally will call MergeMappingNode. document["StrawHats"].MergeSequenceNode(document2["StrawHats"], "Name", true); REQUIRE(4 == document["StrawHats"].size()); auto luffy = std::find_if( document["StrawHats"].Sequence().begin(), document["StrawHats"].Sequence().end(), [](Node const& n) { return n["Name"].as() == "Monkey D Luffy"; }); REQUIRE(luffy != document["StrawHats"].Sequence().end()); // From original node REQUIRE((*luffy)["Bounty"].as() == "3,000,000,000"); // From merged node REQUIRE((*luffy)["Fruit"].as() == "Gomu Gomu no Mi"); } TEST_CASE("YamlMergeNode_MergeSequenceNoKey", "[YAML]") { auto document = Load(TestDataFile("Node-Merge.yaml")); auto document2 = Load(TestDataFile("Node-Merge2.yaml")); REQUIRE_THROWS_HR(document["StrawHats"].MergeSequenceNode(document2["StrawHats"], "Power"), APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA); } TEST_CASE("YamlMappingNode", "[YAML]") { auto document = Load(TestDataFile("Node-Mapping.yaml")); auto node = document["key"]; REQUIRE(node.as() == "value"); auto node2 = document.GetChildNode("KEY"); REQUIRE(node2.as() == "value"); auto node3 = document.GetChildNode("key"); REQUIRE(node3.as() == "value"); auto node4 = document.GetChildNode("kEy"); REQUIRE(node4.as() == "value"); auto node5 = document.GetChildNode("fake"); REQUIRE(node5.IsNull()); auto node6 = document["repeatedkey"]; REQUIRE(node6.as() == "repeated value"); REQUIRE_THROWS_HR(document.GetChildNode("repeatedkey"), APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY); REQUIRE_THROWS_HR(document.GetChildNode("RepeatedKey"), APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY); REQUIRE_THROWS_HR(document["RepeatedKey"], APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY); } TEST_CASE("YamlMappingNode_const", "[YAML]") { const auto document = Load(TestDataFile("Node-Mapping.yaml")); auto node = document["key"]; REQUIRE(node.as() == "value"); auto node2 = document.GetChildNode("KEY"); REQUIRE(node2.as() == "value"); auto node3 = document.GetChildNode("key"); REQUIRE(node3.as() == "value"); auto node4 = document.GetChildNode("kEy"); REQUIRE(node4.as() == "value"); auto node5 = document.GetChildNode("fake"); REQUIRE(node5.IsNull()); auto node6 = document["repeatedkey"]; REQUIRE(node6.as() == "repeated value"); REQUIRE_THROWS_HR(document.GetChildNode("repeatedkey"), APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY); REQUIRE_THROWS_HR(document.GetChildNode("RepeatedKey"), APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY); REQUIRE_THROWS_HR(document["RepeatedKey"], APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY); } TEST_CASE("YamlContainsEscapeControlCode", "[YAML]") { REQUIRE_THROWS_HR(Load(TestDataFile("ContainsEscapeControlCode.yaml")), APPINSTALLER_CLI_ERROR_LIBYAML_ERROR); } TEST_CASE("YamlContainsTooManyNestedLayers", "[YAML]") { REQUIRE_THROWS_HR(Load(TestDataFile("ContainsTooManyNestedLayers.yaml")), APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED); } ================================================ FILE: src/AppInstallerCLITests/YamlManifest.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" #include #include #include #include #include using namespace TestCommon; using namespace AppInstaller::Manifest; using namespace AppInstaller::Manifest::YamlParser; using namespace AppInstaller::Manifest::YamlWriter; using namespace AppInstaller::Utility; using namespace AppInstaller::YAML; namespace { using MultiValue = std::vector; bool operator==(const MultiValue& a, const MultiValue& b) { if (a.size() != b.size()) { return false; } for (size_t i = 0; i < a.size(); ++i) { if (a[i] != b[i]) { return false; } } return true; } void ValidateError( const ValidationError& error, ValidationError::Level level, AppInstaller::StringResource::StringId message, std::string field, std::string value) { REQUIRE(level == error.ErrorLevel); REQUIRE(message == error.Message); REQUIRE(field == error.Context); REQUIRE(value == error.Value); } void ValidateError(const ValidationError& error, ValidationError::Level level, AppInstaller::StringResource::StringId message) { ValidateError(error, level, message, std::string(), std::string()); } struct ManifestExceptionMatcher : public Catch::Matchers::MatcherBase { ManifestExceptionMatcher(std::string expectedMessage, bool expectedWarningOnly = false) : m_expectedMessage(expectedMessage), m_expectedWarningOnly(expectedWarningOnly) {} // Performs the test for this matcher bool match(ManifestException const& e) const override { return e.GetManifestErrorMessage().find(m_expectedMessage) != std::string::npos && e.IsWarningOnly() == m_expectedWarningOnly; } virtual std::string describe() const override { std::ostringstream ss; ss << std::boolalpha << "Expected exception message: " << m_expectedMessage << " Expected IsWarningOnly: " << m_expectedWarningOnly; return ss.str(); } private: std::string m_expectedMessage; bool m_expectedWarningOnly; }; ManifestValidateOption GetTestManifestValidateOption( bool schemaValidationOnly = false, bool errorOnVerifiedPublisher = false) { ManifestValidateOption validateOption; validateOption.FullValidation = true; validateOption.ThrowOnWarning = true; validateOption.SchemaValidationOnly = schemaValidationOnly; validateOption.ErrorOnVerifiedPublisherFields = errorOnVerifiedPublisher; return validateOption; } void TestManifest( const std::filesystem::path& manifestPath, const std::string& expectedMessage = {}, bool expectedWarningOnly = false, ManifestValidateOption validateOption = GetTestManifestValidateOption()) { INFO(manifestPath.u8string()); if (expectedMessage.empty()) { CHECK_NOTHROW(YamlParser::CreateFromPath(TestDataFile(manifestPath), validateOption)); } else { CHECK_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile(manifestPath), validateOption), ManifestException, ManifestExceptionMatcher(expectedMessage, expectedWarningOnly)); } } struct ManifestTestCase { std::string TestFile; std::string ExpectedMessage = {}; bool IsWarningOnly = false; ManifestValidateOption ValidateOption = GetTestManifestValidateOption(); }; void CopyTestDataFilesToFolder(const std::vector& testDataFiles, const std::filesystem::path& dest) { for (const auto& fileName : testDataFiles) { std::filesystem::copy(TestDataFile(fileName), dest); } } void RequireContainerInfoPresent(const std::vector& containers, const DesiredStateConfigurationContainerInfo& info) { INFO("Looking for container info: " << AppInstaller::ToIntegral(info.Type) << " - " << info.RepositoryURL << " - " << info.ModuleName); for (const auto& container : containers) { if (container.Type == info.Type && container.RepositoryURL == info.RepositoryURL && container.ModuleName == info.ModuleName) { REQUIRE(container.Resources.size() == info.Resources.size()); for (const auto& resource : info.Resources) { INFO("Looking for resource: " << resource.Name); bool foundResource = std::any_of(container.Resources.begin(), container.Resources.end(), [&](const auto& a) { return a.Name == resource.Name; }); REQUIRE(foundResource); } return; } } FAIL("Did not find a matching container."); } void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, ManifestVer manifestVer = { s_ManifestVersionV1 }, bool isExported = false) { REQUIRE(manifest.Id == "microsoft.msixsdk"); REQUIRE(manifest.Version == "1.7.32"); REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Microsoft"); REQUIRE(manifest.DefaultLocalization.Get() == "https://www.microsoft.com"); REQUIRE(manifest.DefaultLocalization.Get() == "https://www.microsoft.com/support"); REQUIRE(manifest.DefaultLocalization.Get() == "https://www.microsoft.com/privacy"); REQUIRE(manifest.DefaultLocalization.Get() == "Microsoft"); REQUIRE(manifest.DefaultLocalization.Get() == "MSIX SDK"); REQUIRE(manifest.DefaultLocalization.Get() == "https://www.microsoft.com/msixsdk/home"); REQUIRE(manifest.DefaultLocalization.Get() == "MIT License"); REQUIRE(manifest.DefaultLocalization.Get() == "https://www.microsoft.com/msixsdk/license"); REQUIRE(manifest.DefaultLocalization.Get() == "Copyright Microsoft Corporation"); REQUIRE(manifest.DefaultLocalization.Get() == "https://www.microsoft.com/msixsdk/copyright"); REQUIRE(manifest.DefaultLocalization.Get() == "This is MSIX SDK"); REQUIRE(manifest.DefaultLocalization.Get() == "The MSIX SDK project is an effort to enable developers"); REQUIRE(manifest.Moniker == "msixsdk"); REQUIRE(manifest.DefaultLocalization.Get() == MultiValue{ "appxsdk", "msixsdk" }); if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) { REQUIRE(manifest.DefaultLocalization.Get() == "Default release notes"); REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultReleaseNotes.net"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Label == "DefaultLabel"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementText == "DefaultText"); REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) { REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultPurchaseUrl.com"); REQUIRE(manifest.DefaultLocalization.Get() == "Default installation notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default document label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com"); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_5 }) { REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://testIcon-en-US"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); } if (!isExported) { REQUIRE(manifest.DefaultInstallerInfo.Locale == "en-US"); REQUIRE(manifest.DefaultInstallerInfo.Platform == std::vector{ PlatformEnum::Desktop, PlatformEnum::Universal }); REQUIRE(manifest.DefaultInstallerInfo.MinOSVersion == "10.0.0.0"); REQUIRE(manifest.DefaultInstallerInfo.BaseInstallerType == InstallerTypeEnum::Exe); REQUIRE(manifest.DefaultInstallerInfo.Scope == ScopeEnum::Machine); REQUIRE(manifest.DefaultInstallerInfo.InstallModes == std::vector{ InstallModeEnum::Interactive, InstallModeEnum::Silent, InstallModeEnum::SilentWithProgress }); const auto& defaultSwitches = manifest.DefaultInstallerInfo.Switches; REQUIRE(defaultSwitches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(defaultSwitches.at(InstallerSwitchType::SilentWithProgress) == "/silentwithprogress"); REQUIRE(defaultSwitches.at(InstallerSwitchType::Silent) == "/silence"); REQUIRE(defaultSwitches.at(InstallerSwitchType::Interactive) == "/interactive"); REQUIRE(defaultSwitches.at(InstallerSwitchType::Log) == "/log="); REQUIRE(defaultSwitches.at(InstallerSwitchType::InstallLocation) == "/dir="); REQUIRE(defaultSwitches.at(InstallerSwitchType::Update) == "/upgrade"); REQUIRE(manifest.DefaultInstallerInfo.InstallerSuccessCodes == std::vector{ 1, static_cast(0x80070005) }); REQUIRE(manifest.DefaultInstallerInfo.UpdateBehavior == UpdateBehaviorEnum::UninstallPrevious); REQUIRE(manifest.DefaultInstallerInfo.Commands == MultiValue{ "makemsix", "makeappx" }); REQUIRE(manifest.DefaultInstallerInfo.Protocols == MultiValue{ "protocol1", "protocol2" }); REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "msix", "appxbundle", "msixbundle" }); const auto& dependencies = manifest.DefaultInstallerInfo.Dependencies; REQUIRE(dependencies.HasExactDependency(DependencyType::WindowsFeature, "IIS")); REQUIRE(dependencies.HasExactDependency(DependencyType::WindowsLibrary, "VC Runtime")); REQUIRE(dependencies.HasExactDependency(DependencyType::Package, "Microsoft.MsixSdkDep", "1.0.0")); REQUIRE(dependencies.HasExactDependency(DependencyType::External, "Outside dependencies")); REQUIRE(dependencies.Size() == 4); REQUIRE(manifest.DefaultInstallerInfo.Capabilities == MultiValue{ "internetClient" }); REQUIRE(manifest.DefaultInstallerInfo.RestrictedCapabilities == MultiValue{ "runFullTrust" }); REQUIRE(manifest.DefaultInstallerInfo.PackageFamilyName == "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"); REQUIRE(manifest.DefaultInstallerInfo.ProductCode == "{Foo}"); if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) { REQUIRE(manifest.DefaultInstallerInfo.ReleaseDate == "2021-01-01"); REQUIRE(manifest.DefaultInstallerInfo.InstallerAbortsTerminal); REQUIRE(manifest.DefaultInstallerInfo.InstallLocationRequired); REQUIRE(manifest.DefaultInstallerInfo.RequireExplicitUpgrade); REQUIRE(manifest.DefaultInstallerInfo.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(manifest.DefaultInstallerInfo.UnsupportedOSArchitectures.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(manifest.DefaultInstallerInfo.Markets.AllowedMarkets.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.at(10).ReturnResponseEnum == ExpectedReturnCodeEnum::PackageInUse); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) { REQUIRE(manifest.DefaultInstallerInfo.DisplayInstallWarnings); REQUIRE(manifest.DefaultInstallerInfo.UnsupportedArguments.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 }) { REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerType == InstallerTypeEnum::Msi); REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.at(0).RelativeFilePath == "RelativeFilePath"); REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.at(0).PortableCommandAlias == "PortableCommandAlias"); REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp"); REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).RelativeFilePath == "main.exe"); REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).InvocationParameter == "/arg"); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_6 }) { REQUIRE(manifest.DefaultInstallerInfo.DownloadCommandProhibited); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_7 }) { REQUIRE(defaultSwitches.at(InstallerSwitchType::Repair) == "/repair"); REQUIRE(manifest.DefaultInstallerInfo.RepairBehavior == RepairBehaviorEnum::Modify); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_9 }) { REQUIRE(manifest.DefaultInstallerInfo.ArchiveBinariesDependOnPath); } } if (isSingleton || isExported) { REQUIRE(manifest.Installers.size() == 1); } else { if (manifestVer >= ManifestVer{ s_ManifestVersionV1_12 }) { REQUIRE(manifest.Installers.size() == 7); } else if (manifestVer >= ManifestVer{ s_ManifestVersionV1_7 }) { REQUIRE(manifest.Installers.size() == 5); } else if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 }) { REQUIRE(manifest.Installers.size() == 4); } else if (manifestVer == ManifestVer{ s_ManifestVersionV1_2 }) { REQUIRE(manifest.Installers.size() == 3); } else { REQUIRE(manifest.Installers.size() == 2); } } const ManifestInstaller& installer1 = manifest.Installers.at(0); REQUIRE(installer1.Arch == Architecture::X86); REQUIRE(installer1.Locale == "en-GB"); REQUIRE(installer1.Platform == std::vector{ PlatformEnum::Desktop }); REQUIRE(installer1.MinOSVersion == "10.0.1.0"); REQUIRE(installer1.BaseInstallerType == InstallerTypeEnum::Msix); REQUIRE(installer1.Url == "https://www.microsoft.com/msixsdk/msixsdkx86.msix"); REQUIRE(installer1.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer1.SignatureSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer1.Scope == ScopeEnum::User); REQUIRE(installer1.InstallModes == std::vector{ InstallModeEnum::Interactive }); const auto& installer1Switches = installer1.Switches; REQUIRE(installer1Switches.at(InstallerSwitchType::Custom) == "/c"); REQUIRE(installer1Switches.at(InstallerSwitchType::SilentWithProgress) == "/sp"); REQUIRE(installer1Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(installer1Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(installer1Switches.at(InstallerSwitchType::Log) == "/l="); REQUIRE(installer1Switches.at(InstallerSwitchType::InstallLocation) == "/d="); REQUIRE(installer1Switches.at(InstallerSwitchType::Update) == "/u"); REQUIRE(installer1.UpdateBehavior == UpdateBehaviorEnum::Install); REQUIRE(installer1.Commands == MultiValue{ "makemsixPreview", "makeappxPreview" }); REQUIRE(installer1.Protocols == MultiValue{ "protocol1preview", "protocol2preview" }); REQUIRE(installer1.FileExtensions == MultiValue{ "appxbundle", "msixbundle", "appx", "msix" }); const auto& installer1Dependencies = installer1.Dependencies; REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::WindowsFeature, "PreviewIIS")); REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "Preview VC Runtime")); REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::Package, "Microsoft.MsixSdkDepPreview", "1.0.0")); REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::External, "Preview Outside dependencies")); REQUIRE(installer1Dependencies.Size() == 4); REQUIRE(installer1.Capabilities == MultiValue{ "internetClientPreview" }); REQUIRE(installer1.RestrictedCapabilities == MultiValue{ "runFullTrustPreview" }); REQUIRE(installer1.PackageFamilyName == "Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe"); if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) { REQUIRE(installer1.ReleaseDate == "2021-02-02"); REQUIRE_FALSE(installer1.InstallerAbortsTerminal); REQUIRE_FALSE(installer1.InstallLocationRequired); REQUIRE_FALSE(installer1.RequireExplicitUpgrade); REQUIRE(installer1.ElevationRequirement == ElevationRequirementEnum::ElevationRequired); REQUIRE(installer1.UnsupportedOSArchitectures.size() == 1); REQUIRE(installer1.UnsupportedOSArchitectures.at(0) == Architecture::Arm64); REQUIRE(installer1.AppsAndFeaturesEntries.size() == 0); REQUIRE(installer1.Markets.AllowedMarkets.size() == 0); REQUIRE(installer1.Markets.ExcludedMarkets.size() == 1); REQUIRE(installer1.Markets.ExcludedMarkets.at(0) == "US"); REQUIRE(installer1.ExpectedReturnCodes.at(2).ReturnResponseEnum == ExpectedReturnCodeEnum::ContactSupport); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) { REQUIRE_FALSE(installer1.DisplayInstallWarnings); REQUIRE(installer1.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(installer1.ExpectedReturnCodes.at(3).ReturnResponseUrl == "https://defaultReturnResponseUrl.com"); REQUIRE(installer1.UnsupportedArguments.size() == 1); REQUIRE(installer1.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Location); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 }) { // NestedInstaller metadata should not be populated unless the InstallerType is zip. REQUIRE(installer1.NestedInstallerType == InstallerTypeEnum::Unknown); REQUIRE(installer1.NestedInstallerFiles.size() == 0); REQUIRE(installer1.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp"); REQUIRE(installer1.InstallationMetadata.Files.size() == 1); REQUIRE(installer1.InstallationMetadata.Files.at(0).RelativeFilePath == "main.exe"); REQUIRE(installer1.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); REQUIRE(installer1.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer1.InstallationMetadata.Files.at(0).InvocationParameter == "/arg"); REQUIRE(installer1.InstallationMetadata.Files.at(0).DisplayName == "DisplayName"); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_6 }) { REQUIRE_FALSE(installer1.DownloadCommandProhibited); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_7 }) { REQUIRE(installer1.Switches.at(InstallerSwitchType::Repair) == "/r"); REQUIRE(installer1.RepairBehavior == RepairBehaviorEnum::Modify); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_9 }) { REQUIRE_FALSE(installer1.ArchiveBinariesDependOnPath); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_28 }) { auto containers = &manifest.DefaultInstallerInfo.DesiredStateConfiguration; if (isExported) { containers = &manifest.Installers[0].DesiredStateConfiguration; } RequireContainerInfoPresent(*containers, { { { "Microsoft.WinGet/AdminSettings" }, { "Microsoft.WinGet/Package" }, { "Microsoft.WinGet/Source" }, { "Microsoft.WinGet/UserSettingsFile" } } }); } if (!isSingleton) { if (!isExported) { const ManifestInstaller& installer2 = manifest.Installers.at(1); REQUIRE(installer2.BaseInstallerType == InstallerTypeEnum::Exe); REQUIRE(installer2.Arch == Architecture::X64); REQUIRE(installer2.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); REQUIRE(installer2.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer2.ProductCode == "{Bar}"); if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) { REQUIRE(installer2.ReleaseDate == "2021-01-01"); REQUIRE(installer2.InstallerAbortsTerminal); REQUIRE(installer2.InstallLocationRequired); REQUIRE(installer2.RequireExplicitUpgrade); REQUIRE(installer2.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); REQUIRE(installer2.UnsupportedOSArchitectures.size() == 1); REQUIRE(installer2.UnsupportedOSArchitectures.at(0) == Architecture::Arm); REQUIRE(installer2.AppsAndFeaturesEntries.size() == 1); REQUIRE(installer2.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); REQUIRE(installer2.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); REQUIRE(installer2.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); REQUIRE(installer2.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); REQUIRE(installer2.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); REQUIRE(installer2.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(installer2.Markets.AllowedMarkets.size() == 1); REQUIRE(installer2.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(installer2.ExpectedReturnCodes.size() == 1); REQUIRE(installer2.ExpectedReturnCodes.at(10).ReturnResponseEnum == ExpectedReturnCodeEnum::PackageInUse); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) { const ManifestInstaller& installer3 = manifest.Installers.at(2); REQUIRE(installer3.BaseInstallerType == InstallerTypeEnum::Portable); REQUIRE(installer3.Arch == Architecture::X86); REQUIRE(installer3.Url == "https://www.microsoft.com/msixsdk/msixsdkx86.exe"); REQUIRE(installer3.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer3.Commands == MultiValue{ "standalone" }); REQUIRE(installer3.ExpectedReturnCodes.size() == 1); REQUIRE(installer3.ExpectedReturnCodes.at(11).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); REQUIRE(installer3.ExpectedReturnCodes.at(11).ReturnResponseUrl == "https://defaultReturnResponseUrl.com"); REQUIRE_FALSE(installer3.DisplayInstallWarnings); REQUIRE(installer3.UnsupportedArguments.size() == 1); REQUIRE(installer3.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 }) { const ManifestInstaller& installer4 = manifest.Installers.at(3); REQUIRE(installer4.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(installer4.Arch == Architecture::X64); REQUIRE(installer4.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); REQUIRE(installer4.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer4.ProductCode == "{Foo}"); REQUIRE(installer4.NestedInstallerType == InstallerTypeEnum::Portable); REQUIRE(installer4.NestedInstallerFiles.size() == 2); REQUIRE(installer4.NestedInstallerFiles.at(0).RelativeFilePath == "relativeFilePath1"); REQUIRE(installer4.NestedInstallerFiles.at(0).PortableCommandAlias == "portableAlias1"); REQUIRE(installer4.NestedInstallerFiles.at(1).RelativeFilePath == "relativeFilePath2"); REQUIRE(installer4.NestedInstallerFiles.at(1).PortableCommandAlias == "portableAlias2"); REQUIRE(installer4.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp2"); REQUIRE(installer4.InstallationMetadata.Files.size() == 1); REQUIRE(installer4.InstallationMetadata.Files.at(0).RelativeFilePath == "main2.exe"); REQUIRE(installer4.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Other); REQUIRE(installer4.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer4.InstallationMetadata.Files.at(0).InvocationParameter == "/arg2"); REQUIRE(installer4.InstallationMetadata.Files.at(0).DisplayName == "DisplayName2"); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_6 }) { REQUIRE(installer2.DownloadCommandProhibited); REQUIRE(installer2.UpdateBehavior == UpdateBehaviorEnum::Deny); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_7 }) { REQUIRE(installer2.RepairBehavior == RepairBehaviorEnum::Uninstaller); REQUIRE(installer2.Switches.at(InstallerSwitchType::Repair) == "/r"); const ManifestInstaller& installer5 = manifest.Installers.at(4); REQUIRE(installer5.BaseInstallerType == InstallerTypeEnum::Burn); REQUIRE(installer5.Arch == Architecture::X64); REQUIRE(installer5.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); REQUIRE(installer5.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer5.ProductCode == "{Bar}"); REQUIRE(installer5.Switches.at(InstallerSwitchType::Repair) == "/repair"); REQUIRE(installer5.RepairBehavior == RepairBehaviorEnum::Modify); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_9 }) { const ManifestInstaller& installer4 = manifest.Installers.at(3); REQUIRE(installer4.ArchiveBinariesDependOnPath); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_12 }) { const ManifestInstaller& installer6 = manifest.Installers.at(5); REQUIRE(installer6.BaseInstallerType == InstallerTypeEnum::Zip); REQUIRE(installer6.Arch == Architecture::Neutral); REQUIRE(installer6.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); REQUIRE(installer6.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer6.NestedInstallerType == InstallerTypeEnum::Font); REQUIRE(installer6.NestedInstallerFiles.size() == 5); REQUIRE(installer6.NestedInstallerFiles.at(0).RelativeFilePath == "relativeFilePath1.otf"); REQUIRE(installer6.NestedInstallerFiles.at(1).RelativeFilePath == "relativeFilePath2.ttf"); REQUIRE(installer6.NestedInstallerFiles.at(2).RelativeFilePath == "relativeFilePath3.fnt"); REQUIRE(installer6.NestedInstallerFiles.at(3).RelativeFilePath == "relativeFilePath4.ttc"); REQUIRE(installer6.NestedInstallerFiles.at(4).RelativeFilePath == "relativeFilePath5.otc"); const ManifestInstaller& installer7 = manifest.Installers.at(6); REQUIRE(installer7.BaseInstallerType == InstallerTypeEnum::Font); REQUIRE(installer7.Arch == Architecture::Neutral); REQUIRE(installer7.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); REQUIRE(installer7.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_28 }) { RequireContainerInfoPresent(manifest.Installers[1].DesiredStateConfiguration, { { { "None/None" } } }); REQUIRE(manifest.Installers[2].DesiredStateConfiguration.size() == 0); } } // Localization REQUIRE(manifest.Localizations.size() == 1); const ManifestLocalization& localization1 = manifest.Localizations.at(0); REQUIRE(localization1.Locale == "en-GB"); REQUIRE(localization1.Get() == "Microsoft UK"); REQUIRE(localization1.Get() == "https://www.microsoft.com/UK"); REQUIRE(localization1.Get() == "https://www.microsoft.com/support/UK"); REQUIRE(localization1.Get() == "https://www.microsoft.com/privacy/UK"); REQUIRE(localization1.Get() == "Microsoft UK"); REQUIRE(localization1.Get() == "MSIX SDK UK"); REQUIRE(localization1.Get() == "https://www.microsoft.com/msixsdk/home/UK"); REQUIRE(localization1.Get() == "MIT License UK"); REQUIRE(localization1.Get() == "https://www.microsoft.com/msixsdk/license/UK"); REQUIRE(localization1.Get() == "Copyright Microsoft Corporation UK"); REQUIRE(localization1.Get() == "https://www.microsoft.com/msixsdk/copyright/UK"); REQUIRE(localization1.Get() == "This is MSIX SDK UK"); REQUIRE(localization1.Get() == "The MSIX SDK project is an effort to enable developers UK"); REQUIRE(localization1.Get() == MultiValue{ "appxsdkUK", "msixsdkUK" }); if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) { REQUIRE(localization1.Get() == "Release notes"); REQUIRE(localization1.Get() == "https://ReleaseNotes.net"); REQUIRE(localization1.Get().size() == 1); REQUIRE(localization1.Get().at(0).Label == "Label"); REQUIRE(localization1.Get().at(0).AgreementText == "Text"); REQUIRE(localization1.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) { REQUIRE(localization1.Get() == "https://DefaultPurchaseUrl.com"); REQUIRE(localization1.Get() == "Default installation notes"); REQUIRE(localization1.Get().size() == 1); REQUIRE(localization1.Get().at(0).DocumentLabel == "Default document label"); REQUIRE(localization1.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com"); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_5 }) { REQUIRE(localization1.Get().size() == 1); REQUIRE(localization1.Get().at(0).Url == "https://localeTestIcon-en-GB"); REQUIRE(localization1.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(localization1.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(localization1.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(localization1.Get().at(0).Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); } } } struct ManifestShadowTestInfo { bool shadowDefaultLocale; bool shadowEnGbLocale; }; void VerifyV1ManifestContentCreatedWithShadow(const Manifest& manifest, ManifestShadowTestInfo shadowInfo, ManifestVer manifestVer = { s_ManifestVersionV1_5 }) { REQUIRE(manifest.Id == "microsoft.msixsdk"); REQUIRE(manifest.Version == "1.7.32"); REQUIRE(manifest.Installers.size() == 1); // Default localization REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); REQUIRE(manifest.DefaultLocalization.Get() == "Microsoft"); REQUIRE(manifest.DefaultLocalization.Get() == "MSIX SDK"); REQUIRE(manifest.DefaultLocalization.Get() == "MIT License"); REQUIRE(manifest.DefaultLocalization.Get() == "The MSIX SDK project is an effort to enable developers"); REQUIRE(manifest.DefaultLocalization.Get() == "This is MSIX SDK"); if (manifestVer >= ManifestVer{ s_ManifestVersionV1_5 }) { REQUIRE(manifest.DefaultLocalization.Get().size() == 1); if (shadowInfo.shadowDefaultLocale) { REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://shadowIcon-default"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == SHA256::ConvertToBytes("1111111111111111111111111111111111111111111111111111111111111111")); } else { REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).Url == "https://testIcon-en-US"); REQUIRE(manifest.DefaultLocalization.Get().at(0).FileType == IconFileTypeEnum::Ico); REQUIRE(manifest.DefaultLocalization.Get().at(0).Resolution == IconResolutionEnum::Custom); REQUIRE(manifest.DefaultLocalization.Get().at(0).Theme == IconThemeEnum::Default); REQUIRE(manifest.DefaultLocalization.Get().at(0).Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); } } // Localization if (manifestVer >= ManifestVer{ s_ManifestVersionV1_5 }) { REQUIRE(manifest.Localizations.size() == 3); bool foundEnGbLocale = false; bool foundfrFrLocale = false; for (auto const& localization : manifest.Localizations) { if (localization.Locale == "en-GB") { REQUIRE(localization.Get() == "The MSIX SDK project is an effort to enable developers UK"); if (shadowInfo.shadowEnGbLocale) { REQUIRE(localization.Get().size() == 1); REQUIRE(localization.Get().at(0).Url == "https://shadowIcon-en-GB"); REQUIRE(localization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(localization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(localization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(localization.Get().at(0).Sha256 == SHA256::ConvertToBytes("2222222222222222222222222222222222222222222222222222222222222222")); } else { REQUIRE(localization.Get().size() == 1); REQUIRE(localization.Get().at(0).Url == "https://localeTestIcon-en-GB"); REQUIRE(localization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(localization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(localization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(localization.Get().at(0).Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); } foundEnGbLocale = true; } else if (localization.Locale == "fr-FR") { REQUIRE(localization.Get().size() == 1); REQUIRE(localization.Get().at(0).Url == "https://shadowIcon-fr-FR"); REQUIRE(localization.Get().at(0).FileType == IconFileTypeEnum::Jpeg); REQUIRE(localization.Get().at(0).Resolution == IconResolutionEnum::Square20); REQUIRE(localization.Get().at(0).Theme == IconThemeEnum::Dark); REQUIRE(localization.Get().at(0).Sha256 == SHA256::ConvertToBytes("3333333333333333333333333333333333333333333333333333333333333333")); foundfrFrLocale = true; } else { REQUIRE(localization.Locale == "es-MX"); REQUIRE(localization.Get() == "The MSIX SDK project is an effort to enable developers MX"); REQUIRE(localization.Get().size() == 1); REQUIRE(localization.Get().at(0).Url == "https://localeTestIcon-es-MX"); REQUIRE(localization.Get().at(0).FileType == IconFileTypeEnum::Png); REQUIRE(localization.Get().at(0).Resolution == IconResolutionEnum::Square32); REQUIRE(localization.Get().at(0).Theme == IconThemeEnum::Light); REQUIRE(localization.Get().at(0).Sha256 == SHA256::ConvertToBytes("4444444444444444444444444444444444444444444444444444444444444444")); } } REQUIRE(foundEnGbLocale); REQUIRE(foundfrFrLocale); } } } TEST_CASE("ReadPreviewGoodManifestAndVerifyContents", "[ManifestValidation]") { auto manifestFile = TestDataFile("Manifest-Good.yaml"); Manifest manifest = YamlParser::CreateFromPath(manifestFile); REQUIRE(manifest.Id == "microsoft.msixsdk"); REQUIRE(manifest.DefaultLocalization.Get() == "MSIX SDK"); REQUIRE(manifest.Moniker == "msixsdk"); REQUIRE(manifest.Version == "1.7.32"); REQUIRE(manifest.DefaultLocalization.Get() == "Microsoft"); REQUIRE(manifest.Channel == "release"); REQUIRE(manifest.DefaultLocalization.Get() == "Microsoft"); REQUIRE(manifest.DefaultLocalization.Get() == "MIT License"); REQUIRE(manifest.DefaultLocalization.Get() == "https://github.com/microsoft/msix-packaging/blob/master/LICENSE"); REQUIRE(manifest.DefaultInstallerInfo.MinOSVersion == "0.0.0.0"); REQUIRE(manifest.DefaultLocalization.Get() == "The MSIX SDK project is an effort to enable developers"); REQUIRE(manifest.DefaultLocalization.Get() == "https://github.com/microsoft/msix-packaging"); REQUIRE(manifest.DefaultLocalization.Get() == MultiValue{ "msix", "appx" }); REQUIRE(manifest.DefaultInstallerInfo.Commands == MultiValue{ "makemsix", "makeappx" }); REQUIRE(manifest.DefaultInstallerInfo.Protocols == MultiValue{ "protocol1", "protocol2" }); REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "appxbundle", "msix", "msixbundle" }); REQUIRE(manifest.DefaultInstallerInfo.BaseInstallerType == InstallerTypeEnum::Exe); REQUIRE(manifest.DefaultInstallerInfo.PackageFamilyName == "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"); REQUIRE(manifest.DefaultInstallerInfo.ProductCode == "{Foo}"); REQUIRE(manifest.DefaultInstallerInfo.UpdateBehavior == UpdateBehaviorEnum::UninstallPrevious); // default switches auto switches = manifest.DefaultInstallerInfo.Switches; REQUIRE(switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(switches.at(InstallerSwitchType::SilentWithProgress) == "/silentwithprogress"); REQUIRE(switches.at(InstallerSwitchType::Silent) == "/silence"); REQUIRE(switches.at(InstallerSwitchType::Interactive) == "/interactive"); REQUIRE(switches.at(InstallerSwitchType::Language) == "/en-us"); REQUIRE(switches.at(InstallerSwitchType::Log) == "/log="); REQUIRE(switches.at(InstallerSwitchType::InstallLocation) == "/dir="); REQUIRE(switches.at(InstallerSwitchType::Update) == "/update"); // installers REQUIRE(manifest.Installers.size() == 2); ManifestInstaller installer1 = manifest.Installers.at(0); REQUIRE(installer1.Arch == Architecture::X86); REQUIRE(installer1.Url == "https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip"); REQUIRE(installer1.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer1.Locale == "en-US"); REQUIRE(installer1.BaseInstallerType == InstallerTypeEnum::Exe); REQUIRE(installer1.Scope == ScopeEnum::User); REQUIRE(installer1.PackageFamilyName == ""); REQUIRE(installer1.ProductCode == "{Foo}"); REQUIRE(installer1.UpdateBehavior == UpdateBehaviorEnum::Install); auto installer1Switches = installer1.Switches; REQUIRE(installer1Switches.at(InstallerSwitchType::Custom) == "/c"); REQUIRE(installer1Switches.at(InstallerSwitchType::SilentWithProgress) == "/sp"); REQUIRE(installer1Switches.at(InstallerSwitchType::Silent) == "/s"); REQUIRE(installer1Switches.at(InstallerSwitchType::Interactive) == "/i"); REQUIRE(installer1Switches.at(InstallerSwitchType::Language) == "/en"); REQUIRE(installer1Switches.at(InstallerSwitchType::Log) == "/l="); REQUIRE(installer1Switches.at(InstallerSwitchType::InstallLocation) == "/d="); REQUIRE(installer1Switches.at(InstallerSwitchType::Update) == "/u"); ManifestInstaller installer2 = manifest.Installers.at(1); REQUIRE(installer2.Arch == Architecture::X64); REQUIRE(installer2.Url == "https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx64.zip"); REQUIRE(installer2.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF0000")); REQUIRE(installer2.Locale == "en-US"); REQUIRE(installer2.BaseInstallerType == InstallerTypeEnum::Exe); REQUIRE(installer2.Scope == ScopeEnum::User); REQUIRE(installer2.PackageFamilyName == ""); REQUIRE(installer2.ProductCode == "{Foo}"); REQUIRE(installer2.UpdateBehavior == UpdateBehaviorEnum::UninstallPrevious); // Installer2 does not declare switches, it inherits switches from package default. auto installer2Switches = installer2.Switches; REQUIRE(installer2Switches.at(InstallerSwitchType::Custom) == "/custom"); REQUIRE(installer2Switches.at(InstallerSwitchType::SilentWithProgress) == "/silentwithprogress"); REQUIRE(installer2Switches.at(InstallerSwitchType::Silent) == "/silence"); REQUIRE(installer2Switches.at(InstallerSwitchType::Interactive) == "/interactive"); REQUIRE(installer2Switches.at(InstallerSwitchType::Language) == "/en-us"); REQUIRE(installer2Switches.at(InstallerSwitchType::Log) == "/log="); REQUIRE(installer2Switches.at(InstallerSwitchType::InstallLocation) == "/dir="); REQUIRE(installer2Switches.at(InstallerSwitchType::Update) == "/update"); // Localization REQUIRE(manifest.Localizations.size() == 1); ManifestLocalization localization1 = manifest.Localizations.at(0); REQUIRE(localization1.Locale == "es-MX"); REQUIRE(localization1.Get() == "El proyecto MSIX SDK es habilita desarrolladores de diferentes"); REQUIRE(localization1.Get() == "https://github.com/microsoft/msix-packaging/es-MX"); REQUIRE(localization1.Get() == "https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX"); // Stream hash std::ifstream stream(manifestFile.GetPath(), std::ios_base::in | std::ios_base::binary); REQUIRE(!stream.fail()); auto manifestHash = SHA256::ComputeHash(stream); REQUIRE(manifestHash.size() == manifest.StreamSha256.size()); REQUIRE(std::equal(manifestHash.begin(), manifestHash.end(), manifest.StreamSha256.begin())); } TEST_CASE("ReadGoodManifestWithSpaces", "[ManifestValidation]") { Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-Spaces.yaml")); REQUIRE(manifest.Id == "microsoft.msixsdk"); REQUIRE(manifest.DefaultLocalization.Get() == "MSIX SDK"); REQUIRE(manifest.Moniker == "msixsdk"); REQUIRE(manifest.Version == "1.7.32"); REQUIRE(manifest.Channel == "release"); REQUIRE(manifest.DefaultInstallerInfo.MinOSVersion == "0.0.0.0"); REQUIRE(manifest.DefaultLocalization.Get() == MultiValue{ "msix", "appx" }); REQUIRE(manifest.DefaultInstallerInfo.Commands == MultiValue{ "makemsix", "makeappx" }); REQUIRE(manifest.DefaultInstallerInfo.Protocols == MultiValue{ "protocol1", "protocol2" }); REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "appxbundle", "msix", "msixbundle" }); } TEST_CASE("ReadGoodManifests", "[ManifestValidation]") { ManifestTestCase TestCases[] = { { "Manifest-Good-InstallerTypeExeRoot-Silent.yaml" }, { "Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml" }, { "Manifest-Good-InstallerTypeExe-Silent.yaml" }, { "Manifest-Good-InstallerTypeExe-SilentRoot.yaml" }, { "Manifest-Good-InstallerUniqueness-DefaultLang.yaml" }, { "Manifest-Good-InstallerUniqueness-DiffLangs.yaml" }, { "Manifest-Good-InstallerUniqueness-DiffScope.yaml" }, { "Manifest-Good-Minimum.yaml" }, { "Manifest-Good-Minimum-InstallerType.yaml" }, { "Manifest-Good-Switches.yaml" }, { "Manifest-Good-DefaultExpectedReturnCodeInInstallerSuccessCodes.yaml" }, { "Manifest-Good-InstallerTypeZip-PortableExe.yaml" }, }; for (auto const& testCase : TestCases) { TestManifest(testCase.TestFile); } } TEST_CASE("ReadBadManifests", "[ManifestValidation]") { ManifestTestCase TestCases[] = { { "Manifest-Bad-ArchInvalid.yaml", "Invalid field value. [Architecture]" }, { "Manifest-Bad-ArchMissing.yaml", "Missing required property 'Arch'" }, { "Manifest-Bad-Channel-NotSupported.yaml", "Field is not supported. [Channel]" }, { "Manifest-Bad-DifferentCase-camelCase.yaml", "All field names should be PascalCased. [installerType]" }, { "Manifest-Bad-DifferentCase-lower.yaml", "All field names should be PascalCased. [installertype]" }, { "Manifest-Bad-DifferentCase-UPPER.yaml", "All field names should be PascalCased. [INSTALLERTYPE]" }, { "Manifest-Bad-DuplicateKey.yaml", "Duplicate field found in the manifest." }, { "Manifest-Bad-DuplicateKey-DifferentCase.yaml", "Duplicate field found in the manifest." }, { "Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml", "Duplicate field found in the manifest." }, { "Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml", "Duplicate installer return code found." }, { "Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml", "Duplicate installer return code found." }, { "Manifest-Bad-DuplicateSha256.yaml", "Multiple Installer URLs found with the same InstallerSha256. Please ensure the accuracy of the URLs.", true }, { "Manifest-Bad-IdInvalid.yaml", "Failed to validate against schema associated with property name 'Id'" }, { "Manifest-Bad-IdMissing.yaml", "Missing required property 'Id'" }, { "Manifest-Bad-InconsistentSha256.yaml", "The values of InstallerSha256 do not match for all instances of the same InstallerUrl." }, { "Manifest-Bad-InstallersMissing.yaml", "Missing required property 'Installers'" }, { "Manifest-Bad-InstallerTypeExe-NoSilent.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, { "Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, { "Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, { "Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, { "Manifest-Bad-InstallerTypeInvalid.yaml", "Invalid field value. [InstallerType]" }, { "Manifest-Bad-InstallerTypeMissing.yaml", "Invalid field value. [InstallerType]" }, { "Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml", "Only zero or one entry for Apps and Features may be specified for InstallerType portable." }, { "Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml", "Only zero or one value for Commands may be specified for InstallerType portable." }, { "Manifest-Bad-InstallerTypePortable-InvalidScope.yaml", "Scope is not supported for InstallerType portable." }, { "Manifest-Bad-InstallerTypeZip-DuplicateCommandAlias.yaml", "Duplicate portable command alias found." }, { "Manifest-Bad-InstallerTypeZip-DuplicateRelativeFilePath.yaml", "Duplicate relative file path found." }, { "Manifest-Bad-InstallerTypeZip-InvalidRelativeFilePath.yaml", "Relative file path must not point to a location outside of archive directory" }, { "Manifest-Bad-InstallerTypeZip-MissingRelativeFilePath.yaml", "Required field missing. [RelativeFilePath]" }, { "Manifest-Bad-InstallerTypeZip-MultipleNestedInstallers.yaml", "Only one entry for NestedInstallerFiles can be specified for non-portable InstallerTypes." }, { "Manifest-Bad-InstallerTypeZip-NoNestedInstallerFile.yaml", "Required field missing. [NestedInstallerFiles]" }, { "Manifest-Bad-InstallerTypeZip-NoNestedInstallerType.yaml", "Required field missing. [NestedInstallerType]" }, { "Manifest-Bad-InstallerUniqueness.yaml", "Duplicate installer entry found." }, { "Manifest-Bad-InstallerUniqueness-DefaultScope.yaml", "Duplicate installer entry found." }, { "Manifest-Bad-InstallerUniqueness-DefaultValues.yaml", "Duplicate installer entry found." }, { "Manifest-Bad-InstallerUniqueness-SameLang.yaml", "Duplicate installer entry found." }, { "Manifest-Bad-LicenseMissing.yaml", "Missing required property 'License'" }, { "Manifest-Bad-NameMissing.yaml", "Missing required property 'Name'" }, { "Manifest-Bad-PublisherMissing.yaml", "Missing required property 'Publisher'" }, { "Manifest-Bad-Sha256Invalid.yaml", "Failed to validate against schema associated with property name 'Sha256'" }, { "Manifest-Bad-Sha256Missing.yaml", "Required field missing. [InstallerSha256]" }, { "Manifest-Bad-SwitchInvalid.yaml", "Unknown field. [NotASwitch]", true }, { "Manifest-Bad-UnknownProperty.yaml", "Unknown field. [Fake]", true }, { "Manifest-Bad-UnsupportedVersion.yaml", "Unsupported ManifestVersion" }, { "Manifest-Bad-UrlInvalid.yaml", "Invalid field value. [InstallerUrl]" }, { "Manifest-Bad-UrlMissing.yaml", "Required field missing. [InstallerUrl]" }, { "Manifest-Bad-VersionInvalid.yaml", "Failed to validate against schema associated with property name 'Version'" }, { "Manifest-Bad-VersionMissing.yaml", "Missing required property 'Version'" }, { "Manifest-Bad-InvalidManifestVersionValue.yaml", "Failed to validate against schema associated with property name 'ManifestVersion'" }, { "InstallFlowTest_MSStore.yaml", "Field value is not supported. [InstallerType] Value: msstore" }, { "Manifest-Bad-PackageFamilyNameOnMSI.yaml", "The specified installer type does not support PackageFamilyName. [InstallerType] Value: msi", true }, { "Manifest-Bad-ProductCodeOnMSIX.yaml", "The specified installer type does not support ProductCode. [InstallerType] Value: msix" }, { "Manifest-Bad-InvalidUpdateBehavior.yaml", "Invalid field value. [UpgradeBehavior]" }, { "Manifest-Bad-InvalidLocale.yaml", "The locale value is not a well formed bcp47 language tag." }, { "Manifest-Bad-AppsAndFeaturesEntriesOnMSIX.yaml", "The specified installer type does not write to Apps and Features entry." }, { "InstallFlowTest_LicenseAgreement.yaml", "Field usage requires verified publishers. [Agreement]", true }, { "InstallFlowTest_LicenseAgreement.yaml", "Field usage requires verified publishers. [Agreement]", false, GetTestManifestValidateOption(false, true) }, { "Manifest-Bad-ApproximateVersionInPackageVersion.yaml", "Approximate version not allowed. [PackageVersion]" }, { "Manifest-Bad-ApproximateVersionInArpVersion.yaml", "Approximate version not allowed. [DisplayVersion]" }, { "Manifest-Bad-InstallerTypeZip-PortableNotExe.yaml", "The file type of the referenced file is not allowed. [RelativeFilePath] Value: ScriptedApplication.bat" }, { "Manifest-Bad-InstallerTypeZip-PortableNotExe_Root.yaml", "The file type of the referenced file is not allowed. [RelativeFilePath] Value: ScriptedApplication.bat" }, }; for (auto const& testCase : TestCases) { TestManifest(testCase.TestFile, testCase.ExpectedMessage, testCase.IsWarningOnly, testCase.ValidateOption); } } TEST_CASE("ManifestEncoding", "[ManifestValidation]") { ManifestTestCase TestCases[] = { { "Manifest-Encoding-ANSI.yaml" }, { "Manifest-Encoding-UTF8.yaml" }, { "Manifest-Encoding-UTF8-BOM.yaml" }, { "Manifest-Encoding-UTF16BE.yaml" }, { "Manifest-Encoding-UTF16BE-BOM.yaml" }, { "Manifest-Encoding-UTF16LE.yaml" }, { "Manifest-Encoding-UTF16LE-BOM.yaml" }, }; for (auto const& testCase : TestCases) { INFO(testCase.TestFile); Manifest manifest = YamlParser::CreateFromPath(TestDataFile(testCase.TestFile), GetTestManifestValidateOption()); REQUIRE(manifest.DefaultLocalization.Get() == u8"MSIX SDK\xA9"); } } TEST_CASE("ComplexSystemReference", "[ManifestValidation]") { Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-SystemReferenceComplex.yaml")); REQUIRE(manifest.Installers.size() == 4); // MSIX installer does inherit auto& installer = manifest.Installers[0]; REQUIRE(installer.BaseInstallerType == InstallerTypeEnum::Msix); REQUIRE(installer.Arch == Architecture::X86); REQUIRE(installer.PackageFamilyName == "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"); REQUIRE(installer.ProductCode == ""); // MSI installer does inherit auto& installer1 = manifest.Installers[1]; REQUIRE(installer1.BaseInstallerType == InstallerTypeEnum::Msi); REQUIRE(installer1.Arch == Architecture::X86); REQUIRE(installer1.PackageFamilyName == ""); REQUIRE(installer1.ProductCode == "{Foo}"); // MSIX installer with override auto& installer2 = manifest.Installers[2]; REQUIRE(installer2.BaseInstallerType == InstallerTypeEnum::Msix); REQUIRE(installer2.Arch == Architecture::X64); REQUIRE(installer2.PackageFamilyName == "Override_8wekyb3d8bbwe"); REQUIRE(installer2.ProductCode == ""); // MSI installer with override auto& installer3 = manifest.Installers[3]; REQUIRE(installer3.BaseInstallerType == InstallerTypeEnum::Msi); REQUIRE(installer3.Arch == Architecture::X64); REQUIRE(installer3.PackageFamilyName == ""); REQUIRE(installer3.ProductCode == "Override"); } TEST_CASE("ManifestVersionExtensions", "[ManifestValidation]") { REQUIRE(!ManifestVer("1.0.0"sv).HasExtension("msstore")); REQUIRE(!ManifestVer("1.0.0-other"sv).HasExtension("msstore")); REQUIRE(!ManifestVer("1.0.0-other-other2"sv).HasExtension("msstore")); REQUIRE(ManifestVer("1.0.0-msstore"sv).HasExtension("msstore")); REQUIRE(ManifestVer("1.0.0-msstore.2"sv).HasExtension("msstore")); REQUIRE(ManifestVer("1.0.0-other-msstore.2"sv).HasExtension("msstore")); REQUIRE(ManifestVer("1.0.0-msstore.2-other"sv).HasExtension("msstore")); } void ValidateGoodManifestAndVerifyContents(const std::vector& singleton, const std::vector& multiFiles, std::string_view version) { ManifestValidateOption validateOption; validateOption.FullValidation = true; TempDirectory singletonDirectory{ "SingletonManifest" }; CopyTestDataFilesToFolder(singleton, singletonDirectory); Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ version }); TempDirectory multiFileDirectory{ "MultiFileManifest" }; CopyTestDataFilesToFolder(multiFiles, multiFileDirectory); TempFile mergedManifestFile{ "merged.yaml" }; Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ version }); // Read from merged manifest should have the same content as multi file manifest Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ version }); } #define WINGET_VALIDATE_GOOD_MANIFEST_VERSION(_version_) TEST_CASE("ValidateGoodManifestAndVerifyContents_" #_version_ , "[ManifestValidation][ManifestVersionValidation]") \ { \ ValidateGoodManifestAndVerifyContents( \ { "ManifestV" #_version_ "-Singleton.yaml" }, \ { \ "ManifestV" #_version_ "-MultiFile-Version.yaml", \ "ManifestV" #_version_ "-MultiFile-Installer.yaml", \ "ManifestV" #_version_ "-MultiFile-DefaultLocale.yaml", \ "ManifestV" #_version_ "-MultiFile-Locale.yaml" \ }, \ s_ManifestVersionV ## _version_); \ } WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_1) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_2) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_4) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_5) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_6) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_7) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_9) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_10) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_12) WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_28) void WriteSingletonManifestAndVerifyContents(const std::vector& singleton, const std::vector& multiFiles, std::string_view version) { TempDirectory singletonDirectory{ "SingletonManifest" }; CopyTestDataFilesToFolder(singleton, singletonDirectory); Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ version }, true); TempDirectory multiFileDirectory{ "MultiFileManifest" }; CopyTestDataFilesToFolder(multiFiles, multiFileDirectory); Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ version }, true); } #define WINGET_WRITE_VERIFY_MANIFEST_VERSION(_version_) TEST_CASE("WriteSingletonManifestAndVerifyContents_" #_version_ , "[ManifestCreation][ManifestVersionCreation]") \ { \ WriteSingletonManifestAndVerifyContents( \ { "ManifestV" #_version_ "-Singleton.yaml" }, \ { \ "ManifestV" #_version_ "-MultiFile-Version.yaml", \ "ManifestV" #_version_ "-MultiFile-Installer.yaml", \ "ManifestV" #_version_ "-MultiFile-DefaultLocale.yaml", \ "ManifestV" #_version_ "-MultiFile-Locale.yaml" \ }, \ s_ManifestVersionV ## _version_); \ } WINGET_WRITE_VERIFY_MANIFEST_VERSION(1) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_1) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_2) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_4) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_5) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_6) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_7) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_9) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_10) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_12) WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_28) // Since Authentication is not supported in community repo and will cause manifest validation failure, // we are not adding Authentication in v1_10 manifests. Instead a separate test is created for Authentication. TEST_CASE("ReadWriteValidateV1_10ManifestWithInstallerAuthentication", "[ManifestCreation][ManifestVersionCreation]") { // Read manifest TempDirectory testDirectory{ "TestManifest" }; CopyTestDataFilesToFolder({ "ManifestV1_10-InstallerAuthentication.yaml" }, testDirectory); Manifest testManifest = YamlParser::CreateFromPath(testDirectory); // Validate schema ManifestValidateOption validateOption; validateOption.SchemaValidationOnly = true; validateOption.ThrowOnWarning = true; YamlParser::CreateFromPath(testDirectory, validateOption); // Verify content REQUIRE(testManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_10 }); REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraId); REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.MicrosoftEntraIdInfo); REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.MicrosoftEntraIdInfo->Resource == "TestResource"); REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.MicrosoftEntraIdInfo->Scope == "TestScope"); REQUIRE(testManifest.Installers.size() == 1); REQUIRE(testManifest.Installers[0].AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage); REQUIRE(testManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo); REQUIRE(testManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Resource == "https://storage.azure.com/"); REQUIRE(testManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Scope.empty()); // Manifest Validation. Only error is "Authentication not supported". auto errors = ValidateManifest(testManifest, true); REQUIRE(errors.size() == 1); REQUIRE(errors[0].GetErrorMessage() == "Field is not supported."); REQUIRE(errors[0].Context == "Authentication"); // Write manifest TempDirectory exportedDirectory{ "ExportedManifest" }; std::filesystem::path exportedManifestPath = exportedDirectory.GetPath() / "ExportedManifest.yaml"; YamlWriter::OutputYamlFile(testManifest, testManifest.Installers[0], exportedManifestPath); // Read back and validate content REQUIRE(std::filesystem::exists(exportedManifestPath)); Manifest exportedManifest = YamlParser::CreateFromPath(exportedDirectory); REQUIRE(exportedManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_10 }); REQUIRE(exportedManifest.Installers.size() == 1); REQUIRE(exportedManifest.Installers[0].AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage); REQUIRE(exportedManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo); REQUIRE(exportedManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Resource == "https://storage.azure.com/"); REQUIRE(exportedManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Scope.empty()); } // PowerShell DSC is not supported in the community repo and will cause manifest validation failure. TEST_CASE("ReadWriteValidateV1_28ManifestWithPowerShellDSC", "[ManifestCreation][ManifestVersionCreation]") { // Read manifest TempDirectory testDirectory{ "TestManifest" }; CopyTestDataFilesToFolder({ "ManifestV1_28-PowerShellDSC.yaml" }, testDirectory); Manifest testManifest = YamlParser::CreateFromPath(testDirectory); // Validate schema ManifestValidateOption validateOption; validateOption.SchemaValidationOnly = true; validateOption.ThrowOnWarning = true; YamlParser::CreateFromPath(testDirectory, validateOption); // TODO: Update ValidateManifest // TODO: Update this test with similar validations // Verify content REQUIRE(testManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_28 }); REQUIRE(testManifest.Installers.size() == 1); REQUIRE(testManifest.Installers[0].DesiredStateConfiguration.size() == 3); RequireContainerInfoPresent(testManifest.Installers[0].DesiredStateConfiguration, { "https://www.powershellgallery.com/api/v2", "Microsoft.WinGet.DSC", { { "WinGetUserSettings" }, { "WinGetAdminSettings" }, { "WinGetSource" }, { "WinGetPackageManager" }, { "WinGetPackage" } } }); RequireContainerInfoPresent(testManifest.Installers[0].DesiredStateConfiguration, { "https://mcr.microsoft.com/", "Microsoft.WinGet.DSC", { { "WinGetUserSettings" }, { "WinGetAdminSettings" }, { "WinGetSource" }, { "WinGetPackageManager" }, { "WinGetPackage" } } }); RequireContainerInfoPresent(testManifest.Installers[0].DesiredStateConfiguration, { { { "Microsoft.WinGet/AdminSettings" }, { "Microsoft.WinGet/Package" }, { "Microsoft.WinGet/Source" }, { "Microsoft.WinGet/UserSettingsFile" } } }); // Manifest Validation. Only error is "PowerShell not supported". auto errors = ValidateManifest(testManifest, true); REQUIRE(errors.size() == 1); REQUIRE(errors[0].GetErrorMessage() == "Field is not supported."); REQUIRE(errors[0].Context == "DesiredStateConfiguration.PowerShell"); // Write manifest TempDirectory exportedDirectory{ "ExportedManifest" }; std::filesystem::path exportedManifestPath = exportedDirectory.GetPath() / "ExportedManifest.yaml"; YamlWriter::OutputYamlFile(testManifest, testManifest.Installers[0], exportedManifestPath); // Read back and validate content REQUIRE(std::filesystem::exists(exportedManifestPath)); Manifest exportedManifest = YamlParser::CreateFromPath(exportedDirectory); REQUIRE(exportedManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_28 }); REQUIRE(exportedManifest.Installers.size() == 1); REQUIRE(exportedManifest.Installers[0].DesiredStateConfiguration.size() == 3); RequireContainerInfoPresent(exportedManifest.Installers[0].DesiredStateConfiguration, { "https://www.powershellgallery.com/api/v2", "Microsoft.WinGet.DSC", { { "WinGetUserSettings" }, { "WinGetAdminSettings" }, { "WinGetSource" }, { "WinGetPackageManager" }, { "WinGetPackage" } } }); RequireContainerInfoPresent(exportedManifest.Installers[0].DesiredStateConfiguration, { "https://mcr.microsoft.com/", "Microsoft.WinGet.DSC", { { "WinGetUserSettings" }, { "WinGetAdminSettings" }, { "WinGetSource" }, { "WinGetPackageManager" }, { "WinGetPackage" } } }); RequireContainerInfoPresent(exportedManifest.Installers[0].DesiredStateConfiguration, { { { "Microsoft.WinGet/AdminSettings" }, { "Microsoft.WinGet/Package" }, { "Microsoft.WinGet/Source" }, { "Microsoft.WinGet/UserSettingsFile" } } }); } TEST_CASE("WriteManifestWithMultipleLocale", "[ManifestCreation]") { Manifest multiLocaleManifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultiLocale.yaml")); TempDirectory exportedDirectory{ "exported" }; std::filesystem::path generatedManifestPath = exportedDirectory.GetPath() / "testManifestWithMultipleLocale.yaml"; YamlWriter::OutputYamlFile(multiLocaleManifest, multiLocaleManifest.Installers[0], generatedManifestPath); REQUIRE(std::filesystem::exists(generatedManifestPath)); Manifest generatedManifest = YamlParser::CreateFromPath(generatedManifestPath); REQUIRE(generatedManifest.Localizations.size() == 2); } TEST_CASE("WriteManifestWithMSStoreInstaller", "[ManifestCreation]") { Manifest msstoreManifest = YamlParser::CreateFromPath(TestDataFile("DownloadFlowTest_MSStore.yaml")); TempDirectory exportedDirectory{ "exported" }; std::filesystem::path generatedManifestPath = exportedDirectory.GetPath() / "testManifestWithMultipleLocale.yaml"; msstoreManifest.ManifestVersion = ManifestVer{ "1.1.0" }; YamlWriter::OutputYamlFile(msstoreManifest, msstoreManifest.Installers[0], generatedManifestPath); REQUIRE(std::filesystem::exists(generatedManifestPath)); Manifest generatedManifest = YamlParser::CreateFromPath(generatedManifestPath); REQUIRE(generatedManifest.Installers[0].BaseInstallerType == InstallerTypeEnum::MSStore); REQUIRE(!generatedManifest.Installers[0].ProductId.empty()); } YamlManifestInfo CreateYamlManifestInfo(std::string testDataFile) { YamlManifestInfo result; result.Root = AppInstaller::YAML::Load(TestDataFile(testDataFile)); result.FileName = testDataFile; return result; } TEST_CASE("MultifileManifestInputValidation", "[ManifestValidation]") { auto previewManifest = CreateYamlManifestInfo("Manifest-Good.yaml"); auto v1SingletonManifest = CreateYamlManifestInfo("ManifestV1-Singleton.yaml"); auto v1VersionManifest = CreateYamlManifestInfo("ManifestV1-MultiFile-Version.yaml"); auto v1InstallerManifest = CreateYamlManifestInfo("ManifestV1-MultiFile-Installer.yaml"); auto v1DefaultLocaleManifest = CreateYamlManifestInfo("ManifestV1-MultiFile-DefaultLocale.yaml"); auto v1LocaleManifest = CreateYamlManifestInfo("ManifestV1-MultiFile-Locale.yaml"); { // Preview and multi file manifest together std::vector input = { previewManifest, v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("Preview manifest does not support multi file manifest format")); } { // Singleton and multi file manifest together std::vector input = { v1SingletonManifest, v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest should not contain file with the particular ManifestType. [ManifestType] Value: singleton")); } { // More than 1 version manifest std::vector input = { v1VersionManifest, v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest should contain only one file with the particular ManifestType. [ManifestType] Value: version")); } { // More than 1 installer manifest std::vector input = { v1VersionManifest, v1InstallerManifest, v1InstallerManifest, v1DefaultLocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest should contain only one file with the particular ManifestType. [ManifestType] Value: installer")); } { // More than 1 default locale manifest std::vector input = { v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest, v1DefaultLocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest should contain only one file with the particular ManifestType. [ManifestType] Value: defaultLocale")); } { // Duplicate locales std::vector input = { v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest, v1LocaleManifest, v1LocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest contains duplicate PackageLocale. [PackageLocale] Value: en-GB")); } { // default locale not match auto defaultLocaleManifestCopy = v1DefaultLocaleManifest; defaultLocaleManifestCopy.Root["PackageLocale"].SetScalar("fr-fr"); std::vector input = { v1VersionManifest, v1InstallerManifest, defaultLocaleManifestCopy, v1LocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("DefaultLocale value in version manifest does not match PackageLocale value in defaultLocale manifest")); } { // Package Id does not match auto installerManifestCopy = v1InstallerManifest; installerManifestCopy.Root["PackageIdentifier"].SetScalar("Another.Identifier"); std::vector input = { v1VersionManifest, installerManifestCopy, v1DefaultLocaleManifest, v1LocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest has inconsistent field values. [PackageIdentifier] Value: Another.Identifier")); } { // Package Version does not match auto installerManifestCopy = v1InstallerManifest; installerManifestCopy.Root["PackageVersion"].SetScalar("Another.Version"); std::vector input = { v1VersionManifest, installerManifestCopy, v1DefaultLocaleManifest, v1LocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest has inconsistent field values. [PackageVersion] Value: Another.Version")); } { // Incomplete multi file manifest, missing installer std::vector input = { v1VersionManifest, v1DefaultLocaleManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest is incomplete")); } { // Incomplete multi file manifest, missing default locale std::vector input = { v1VersionManifest, v1InstallerManifest }; REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest is incomplete")); } } TEST_CASE("ManifestApplyLocale", "[ManifestValidation]") { Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultiLocale.yaml")); // No better alternative locale, default is used. manifest.ApplyLocale("zh-CN"); REQUIRE(manifest.CurrentLocalization.Locale == "es-MX"); REQUIRE(manifest.CurrentLocalization.Get() == "es-MX package name"); REQUIRE(manifest.CurrentLocalization.Get() == "es-MX publisher"); // en-US results in en-GB, which is better than default. manifest.ApplyLocale("en-US"); REQUIRE(manifest.CurrentLocalization.Locale == "en-GB"); REQUIRE(manifest.CurrentLocalization.Get() == "en-GB package name"); REQUIRE(manifest.CurrentLocalization.Get() == "en-GB publisher"); // fr-FR results in fr-FR, but only package name is localized. manifest.ApplyLocale("fr-FR"); REQUIRE(manifest.CurrentLocalization.Locale == "fr-FR"); REQUIRE(manifest.CurrentLocalization.Get() == "fr-FR package name"); REQUIRE(manifest.CurrentLocalization.Get() == "es-MX publisher"); } TEST_CASE("ManifestLocalizationValidation", "[ManifestValidation]") { Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultiLocale.yaml")); // Set 1 locale to bad value manifest.Localizations.at(0).Locale = "Invalid"; // Full validation should detect as error auto errors = ValidateManifest(manifest, true); REQUIRE(errors.size() == 1); REQUIRE(errors.at(0).ErrorLevel == ValidationError::Level::Error); // Not full validation should detect as warning errors = ValidateManifest(manifest, false); REQUIRE(errors.size() == 1); REQUIRE(errors.at(0).ErrorLevel == ValidationError::Level::Warning); } TEST_CASE("PortableFileTypeValidation", "[ManifestValidation]") { Manifest installerManifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Bad-InstallerTypeZip-PortableNotExe.yaml")); Manifest rootManifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Bad-InstallerTypeZip-PortableNotExe_Root.yaml")); // Regular validation should detect as error auto errors = ValidateManifest(installerManifest, true); REQUIRE(errors.size() == 1); REQUIRE(errors.at(0).ErrorLevel == ValidationError::Level::Error); errors = ValidateManifest(rootManifest, true); REQUIRE(errors.size() == 1); REQUIRE(errors.at(0).ErrorLevel == ValidationError::Level::Error); // Should not error when full validation is set to false errors = ValidateManifest(installerManifest, false); REQUIRE(errors.size() == 0); errors = ValidateManifest(rootManifest, false); REQUIRE(errors.size() == 0); } TEST_CASE("ReadManifestAndValidateMsixInstallers_Success", "[ManifestValidation]") { TestDataFile testFile("Manifest-Good-MsixInstaller.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(0 == errors.size()); } TEST_CASE("ReadManifestAndValidateMsixInstallers_InconsistentFields", "[ManifestValidation]") { TestDataFile testFile("Manifest-Bad-InconsistentMsixInstallerFields.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(4 == errors.size()); ValidateError(errors[0], ValidationError::Level::Error, ManifestError::MsixSignatureHashFailed); ValidateError(errors[1], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); ValidateError(errors[2], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); ValidateError(errors[3], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.0.0"); } TEST_CASE("ReadManifestAndValidateMsixInstallers_NoSupportedPlatforms", "[ManifestValidation]") { auto testFileName = "Manifest-Bad-NoSupportedPlatforms.yaml"; TestDataFile testFile(testFileName); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(1 == errors.size()); ValidateError(errors[0], ValidationError::Level::Error, ManifestError::NoSupportedPlatforms, "InstallerUrl", manifest.Installers.front().Url); } TEST_CASE("ReadManifestAndValidateMsixInstallers_PackageVersionNotUINT64", "[ManifestValidation]") { Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Bad-MsixInstaller-PackageVersion.yaml")); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(1 == errors.size()); ValidateError(errors[0], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); } TEST_CASE("ReadManifestAndValidateMsixInstallers_MissingFields", "[ManifestValidation]") { TestDataFile testFile("Manifest-Bad-MissingMsixInstallerFields.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); for (bool treatErrorAsWarning : { false, true }) { auto errors = ValidateManifestInstallers(manifest, treatErrorAsWarning); auto expectedLevel = treatErrorAsWarning ? ValidationError::Level::Warning : ValidationError::Level::Error; REQUIRE(2 == errors.size()); ValidateError(errors[0], expectedLevel, ManifestError::OptionalFieldMissing, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); ValidateError(errors[1], expectedLevel, ManifestError::OptionalFieldMissing, "MinimumOSVersion", "10.0.0.0"); } } TEST_CASE("ReadManifestAndValidateMsixInstallers_Signed_Success", "[ManifestValidation]") { TestDataFile testFile("Manifest-Good-SignedMsixInstaller.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(0 == errors.size()); } TEST_CASE("ReadManifestAndValidateMsixInstallers_Signed_InconsistentFields", "[ManifestValidation]") { TestDataFile testFile("Manifest-Bad-InconsistentSignedMsixInstallerFields.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(4 == errors.size()); ValidateError(errors[0], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "SignatureSha256", "50562001202c8dad456474d3f20903138d0a15c44ee497c3d4f82e85edbf2f97"); ValidateError(errors[1], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); ValidateError(errors[2], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); ValidateError(errors[3], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.0.0"); } TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_Success", "[ManifestValidation]") { TestDataFile testFile("Manifest-Good-MsixBundleInstaller.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(0 == errors.size()); } TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_WithStub_Success", "[ManifestValidation]") { TestDataFile testFile("Manifest-Good-MsixBundleInstaller-WithStub.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(0 == errors.size()); } TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_InconsistentFields", "[ManifestValidation]") { TestDataFile testFile("Manifest-Bad-InconsistentMsixBundleInstallerFields.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(7 == errors.size()); ValidateError(errors[0], ValidationError::Level::Error, ManifestError::MsixSignatureHashFailed); // Validate errors for the first msix package in the msix bundle ValidateError(errors[1], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); ValidateError(errors[2], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); ValidateError(errors[3], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.16299.0"); // Validate errors for the second msix package in the msix bundle ValidateError(errors[4], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); ValidateError(errors[5], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); ValidateError(errors[6], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.16299.0"); } TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_Signed_Success", "[ManifestValidation]") { TestDataFile testFile("Manifest-Good-SignedMsixBundleInstaller.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(0 == errors.size()); } TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_Signed_InconsistentFields", "[ManifestValidation]") { TestDataFile testFile("Manifest-Bad-InconsistentSignedMsixBundleInstallerFields.yaml"); Manifest manifest = YamlParser::CreateFromPath(testFile); // Update the installer path for testing REQUIRE(1 == manifest.Installers.size()); TestDataFile msixFile(manifest.Installers[0].Url.c_str()); manifest.Installers[0].Url = msixFile.GetPath().u8string(); auto errors = ValidateManifestInstallers(manifest); REQUIRE(7 == errors.size()); ValidateError(errors[0], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "SignatureSha256", "d70bd623f87b6ce4ddba4506c6000cf43ef3af4ab1207f5579ec43400de1623f"); // Validate errors for the first msix package in the msix bundle ValidateError(errors[1], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); ValidateError(errors[2], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); ValidateError(errors[3], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.16299.0"); // Validate errors for the second msix package in the msix bundle ValidateError(errors[4], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); ValidateError(errors[5], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); ValidateError(errors[6], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.16299.0"); } TEST_CASE("ManifestArpVersionRange", "[ManifestValidation]") { Manifest manifestNoArp = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-NoArpVersionDeclared.yaml")); REQUIRE(manifestNoArp.GetArpVersionRange().IsEmpty()); Manifest manifestSingleArp = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-SingleArpVersionDeclared.yaml")); auto arpRangeSingleArp = manifestSingleArp.GetArpVersionRange(); REQUIRE(arpRangeSingleArp.GetMinVersion().ToString() == "11.0"); REQUIRE(arpRangeSingleArp.GetMaxVersion().ToString() == "11.0"); Manifest manifestMultiArp = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultipleArpVersionDeclared.yaml")); auto arpRangeMultiArp = manifestMultiArp.GetArpVersionRange(); REQUIRE(arpRangeMultiArp.GetMinVersion().ToString() == "12.0"); REQUIRE(arpRangeMultiArp.GetMaxVersion().ToString() == "13.0"); } TEST_CASE("ManifestV1_10_SchemaHeaderValidations", "[ManifestValidation]") { ManifestValidateOption validateOption; validateOption.FullValidation = true; // Schema header not found REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderNotFound.yaml"),validateOption), ManifestException, ManifestExceptionMatcher("Schema header not found")); // Schema header not valid REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderInvalid.yaml"), validateOption), ManifestException, ManifestExceptionMatcher("The schema header is invalid. Please verify that the schema header is present and formatted correctly.")); // Schema header URL does not match the expected schema URL REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderURLPatternMismatch.yaml"), validateOption), ManifestException, ManifestExceptionMatcher("The schema header URL does not match the expected pattern.")); // Schema header ManifestType does not match the expected value REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderManifestTypeMismatch.yaml"), validateOption), ManifestException, ManifestExceptionMatcher("The manifest type in the schema header does not match the ManifestType property value in the manifest.")); // Schema header version does not match the expected version REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderManifestVersionMismatch.yaml"), validateOption), ManifestException, ManifestExceptionMatcher("The manifest version in the schema header does not match the ManifestVersion property value in the manifest.")); } TEST_CASE("ShadowManifest", "[ShadowManifest]") { ManifestValidateOption validateOption; validateOption.FullValidation = true; validateOption.AllowShadowManifest = true; TempDirectory multiFileDirectory{ "MultiFileManifest" }; CopyTestDataFilesToFolder({ "ManifestV1_5-MultiFile-Version.yaml", "ManifestV1_5-Shadow-Installer.yaml", "ManifestV1_5-Shadow-DefaultLocale.yaml", "ManifestV1_5-Shadow-Locale.yaml", "ManifestV1_5-Shadow-Locale2.yaml", "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); auto shadowInfo = ManifestShadowTestInfo{}; shadowInfo.shadowDefaultLocale = true; shadowInfo.shadowEnGbLocale = true; TempFile mergedManifestFile{ "merged.yaml" }; Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); VerifyV1ManifestContentCreatedWithShadow(multiFileManifest, shadowInfo); // Read from merged manifest should have the same content as multi file manifest Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); VerifyV1ManifestContentCreatedWithShadow(mergedManifest, shadowInfo); } TEST_CASE("ShadowManifest_SkipShadowDefaultLocale", "[ShadowManifest]") { ManifestValidateOption validateOption; validateOption.FullValidation = true; validateOption.AllowShadowManifest = true; TempDirectory multiFileDirectory{ "MultiFileManifest" }; CopyTestDataFilesToFolder({ "ManifestV1_5-MultiFile-Version.yaml", "ManifestV1_5-Shadow-Installer.yaml", "ManifestV1_5-MultiFile-DefaultLocale.yaml", "ManifestV1_5-Shadow-Locale.yaml", "ManifestV1_5-Shadow-Locale2.yaml", "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); auto shadowInfo = ManifestShadowTestInfo{}; shadowInfo.shadowDefaultLocale = false; shadowInfo.shadowEnGbLocale = true; TempFile mergedManifestFile{ "merged.yaml" }; Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); VerifyV1ManifestContentCreatedWithShadow(multiFileManifest, shadowInfo); // Read from merged manifest should have the same content as multi file manifest Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); VerifyV1ManifestContentCreatedWithShadow(mergedManifest, shadowInfo); } TEST_CASE("ShadowManifest_SkipShadowLocalizationLocale", "[ShadowManifest]") { ManifestValidateOption validateOption; validateOption.FullValidation = true; validateOption.AllowShadowManifest = true; TempDirectory multiFileDirectory{ "MultiFileManifest" }; CopyTestDataFilesToFolder({ "ManifestV1_5-MultiFile-Version.yaml", "ManifestV1_5-Shadow-Installer.yaml", "ManifestV1_5-Shadow-DefaultLocale.yaml", "ManifestV1_5-MultiFile-Locale.yaml", "ManifestV1_5-Shadow-Locale2.yaml", "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); auto shadowInfo = ManifestShadowTestInfo{}; shadowInfo.shadowDefaultLocale = true; shadowInfo.shadowEnGbLocale = false; TempFile mergedManifestFile{ "merged.yaml" }; Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); VerifyV1ManifestContentCreatedWithShadow(multiFileManifest, shadowInfo); // Read from merged manifest should have the same content as multi file manifest Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); VerifyV1ManifestContentCreatedWithShadow(mergedManifest, shadowInfo); } TEST_CASE("ShadowManifest_ShadowNotAllowed", "[ShadowManifest]") { ManifestValidateOption validateOption; validateOption.FullValidation = true; validateOption.AllowShadowManifest = false; TempDirectory multiFileDirectory{ "MultiFileManifest" }; CopyTestDataFilesToFolder({ "ManifestV1_5-MultiFile-Version.yaml", "ManifestV1_5-Shadow-Installer.yaml", "ManifestV1_5-Shadow-DefaultLocale.yaml", "ManifestV1_5-Shadow-Locale.yaml", "ManifestV1_5-Shadow-Locale2.yaml", "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); TempFile mergedManifestFile{ "merged.yaml" }; REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile), ManifestException, ManifestExceptionMatcher("Shadow manifest is not allowed. [ManifestType] Value: shadow File: ManifestV1_5-Shadow-Shadow.yaml")); } TEST_CASE("ShadowManifest_TwoShadowFiles", "[ShadowManifest]") { ManifestValidateOption validateOption; validateOption.FullValidation = true; validateOption.AllowShadowManifest = true; TempDirectory multiFileDirectory{ "MultiFileManifest" }; CopyTestDataFilesToFolder({ "ManifestV1_5-MultiFile-Version.yaml", "ManifestV1_5-Shadow-Installer.yaml", "ManifestV1_5-Shadow-DefaultLocale.yaml", "ManifestV1_5-Shadow-Shadow.yaml", "ManifestV1_5-Shadow-Shadow2.yaml" }, multiFileDirectory); TempFile mergedManifestFile{ "merged.yaml" }; REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile), ManifestException, ManifestExceptionMatcher("The multi file manifest should contain only one file with the particular ManifestType. [ManifestType] Value: shadow File: ManifestV1_5-Shadow-Shadow2.yaml")); } TEST_CASE("ShadowManifest_NotVerifiedPublisher", "[ShadowManifest]") { ManifestValidateOption validateOption; validateOption.FullValidation = true; validateOption.AllowShadowManifest = true; validateOption.ErrorOnVerifiedPublisherFields = true; TempDirectory multiFileDirectory{ "MultiFileManifest" }; CopyTestDataFilesToFolder({ "ManifestV1_5-MultiFile-Version.yaml", "ManifestV1_5-Shadow-Installer.yaml", "ManifestV1_5-Shadow-DefaultLocale.yaml", "ManifestV1_5-Shadow-Locale.yaml", "ManifestV1_5-Shadow-Locale2.yaml", "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); TempFile mergedManifestFile{ "merged.yaml" }; REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile), ManifestException, ManifestExceptionMatcher("Field usage requires verified publishers. [Icons]")); } TEST_CASE("Manifest_PackageFamilyNameInheritance", "[ManifestValidation]") { std::filesystem::path testManifest; SECTION("MSIX inside Archive") { testManifest = "Manifest-MSIX-in-Archive.yaml"; } SECTION("MSIX in AppsAndFeatures") { testManifest = "Manifest-MSIX-in-AppsAndFeatures.yaml"; } auto manifest = YamlParser::CreateFromPath(TestDataFile(testManifest), GetTestManifestValidateOption()); REQUIRE(!manifest.Installers.empty()); REQUIRE(!manifest.Installers[0].PackageFamilyName.empty()); } ================================================ FILE: src/AppInstallerCLITests/main.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include #include #include "TestCommon.h" #include "TestSettings.h" using namespace winrt; using namespace winrt::Windows::Foundation; using namespace std::string_literals; using namespace AppInstaller; // Logs the AppInstaller log target to break up individual tests struct LoggingBreakListener : public Catch::EventListenerBase { using EventListenerBase::EventListenerBase; void testCaseStarting(const Catch::TestCaseInfo& info) override { Catch::EventListenerBase::testCaseStarting(info); AICLI_LOG(Test, Info, << "========== Test Case Begins :: " << info.name << " =========="); TestCommon::TempFile::SetTestFailed(false); } void testCaseEnded(const Catch::TestCaseStats& testCaseStats) override { AICLI_LOG(Test, Info, << "========== Test Case Ends =========="); if (!testCaseStats.totals.delta(lastTotals).testCases.allOk()) { TestCommon::TempFile::SetTestFailed(true); } lastTotals = testCaseStats.totals; Catch::EventListenerBase::testCaseEnded(testCaseStats); } Catch::Totals lastTotals{}; }; CATCH_REGISTER_LISTENER(LoggingBreakListener); // Map CATCH exceptions so that WIL doesn't fail fast the tests HRESULT __stdcall CatchResultFromCaughtException() WI_PFN_NOEXCEPT { try { throw; } catch (const Catch::TestFailureException&) { // REC_E_TEST_FAILURE :: Test failure. // Not sure what could generate this, but it is unlikely that we use it. // Since the message is aligned with the issue it should help diagnosis. return 0x8b051032; } catch (...) { // Means we couldn't map the exception return S_OK; } } int main(int argc, char** argv) { init_apartment(); bool hasSetTestDataBasePath = false; bool waitBeforeReturn = false; bool keepSQLLogging = false; std::vector args; for (int i = 0; i < argc; ++i) { if ("-ktf"s == argv[i]) { TestCommon::TempFile::SetDestructorBehavior(TestCommon::TempFileDestructionBehavior::Keep); } else if ("-seof"s == argv[i]) { TestCommon::TempFile::SetDestructorBehavior(TestCommon::TempFileDestructionBehavior::ShellExecuteOnFailure); } else if ("-log"s == argv[i]) { auto logger = std::make_unique(); logger->SetMaximumSize(0); Logging::Log().AddLogger(std::move(logger)); } else if ("-logto"s == argv[i]) { if (++i < argc) { auto logger = std::make_unique(std::filesystem::path{ argv[i] }); logger->SetMaximumSize(0); Logging::Log().AddLogger(std::move(logger)); } } else if ("-tdd"s == argv[i]) { if (++i < argc) { TestCommon::TestDataFile::SetTestDataBasePath(argv[i]); hasSetTestDataBasePath = true; } } else if ("-wait"s == argv[i]) { waitBeforeReturn = true; } else if ("-logsql"s == argv[i]) { keepSQLLogging = true; } else if ("-mdmp"s == argv[i]) { Debugging::EnableSelfInitiatedMinidump(); } else if ("-mdmpto"s == argv[i]) { if (++i < argc) { Debugging::EnableSelfInitiatedMinidump(std::filesystem::path{ argv[i] }); } } else { args.push_back(argv[i]); } } // If not set, use the current executables path if (!hasSetTestDataBasePath) { wchar_t fullFileName[1024]; DWORD chars = ARRAYSIZE(fullFileName); if (QueryFullProcessImageNameW(GetCurrentProcess(), 0, fullFileName, &chars)) { std::filesystem::path filepath{ fullFileName }; filepath.remove_filename(); TestCommon::TestDataFile::SetTestDataBasePath(filepath); } } // Enable logging, to force log string building to run. // Disable SQL by default, as it generates 10s of MBs of log file and // increases the full test run time by 60% or more. // By not creating a log target, it will all be thrown away. Logging::Log().SetEnabledChannels(Logging::Channel::All); if (!keepSQLLogging) { Logging::Log().DisableChannel(Logging::Channel::SQL); } Logging::Log().SetLevel(Logging::Level::Verbose); Logging::EnableWilFailureTelemetry(); wil::SetResultFromCaughtExceptionCallback(CatchResultFromCaughtException); // Forcibly enable event writing to catch bugs in that code g_IsTelemetryProviderEnabled = true; TestCommon::SetTestPathOverrides(); // Remove any existing settings files in the new tests path TestCommon::UserSettingsFileGuard settingsGuard; int result = Catch::Session().run(static_cast(args.size()), args.data()); if (waitBeforeReturn) { // Wait for some input before returning std::cin.get(); } return result; } ================================================ FILE: src/AppInstallerCLITests/packages.config ================================================  ================================================ FILE: src/AppInstallerCLITests/pch.cpp ================================================ #include "pch.h" ================================================ FILE: src/AppInstallerCLITests/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ================================================ FILE: src/AppInstallerCommonCore/AdminSettings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerLogging.h" #include "AppInstallerStrings.h" #include "winget/Settings.h" #include "winget/AdminSettings.h" #include "winget/GroupPolicy.h" #include "winget/Yaml.h" namespace AppInstaller::Settings { using namespace std::string_view_literals; using namespace Utility::literals; namespace { constexpr Utility::LocIndView s_AdminSettingsYaml_LocalManifestFiles = "LocalManifestFiles"_liv; constexpr Utility::LocIndView s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore = "BypassCertificatePinningForMicrosoftStore"_liv; constexpr Utility::LocIndView s_AdminSettingsYaml_InstallerHashOverride = "InstallerHashOverride"_liv; constexpr Utility::LocIndView s_AdminSettingsYaml_LocalArchiveMalwareScanOverride = "LocalArchiveMalwareScanOverride"_liv; constexpr Utility::LocIndView s_AdminSettingsYaml_ProxyCommandLineOptions = "ProxyCommandLineOptions"_liv; constexpr Utility::LocIndView s_AdminSettingsYaml_DefaultProxy = "DefaultProxy"_liv; // Attempts to read a single scalar value from the node. template bool TryReadScalar(const YAML::Node& rootNode, std::string_view name, Value& value) { YAML::Node valueNode = rootNode[std::string{ name }]; if (!valueNode || !valueNode.IsScalar()) { AICLI_LOG(Core, Verbose, << "Admin setting '" << name << "' was not found or did not contain the expected format"); return false; } value = valueNode.as(); return true; } struct AdminSettingValues { bool LocalManifestFiles = false; bool BypassCertificatePinningForMicrosoftStore = false; bool InstallerHashOverride = false; bool LocalArchiveMalwareScanOverride = false; bool ProxyCommandLineOptions = false; std::optional DefaultProxy; }; struct AdminSettingsInternal { AdminSettingsInternal(); void SetAdminSetting(BoolAdminSetting setting, bool enabled); void SetAdminSetting(StringAdminSetting setting, const std::optional& value); bool GetAdminSettingValue(BoolAdminSetting setting) const; std::optional GetAdminSettingValue(StringAdminSetting setting) const; void Reset(); private: void LoadAdminSettings(); [[nodiscard]] bool SaveAdminSettings(); // Sets the value of an admin setting using the given function and then saves the changes. // Encapsulates the retry and reload logic. // Stops if the value cannot be set, as indicated by the return value of setValue() void SetAdminSettingAndSave(std::function setValue); Stream m_settingStream; AdminSettingValues m_settingValues; }; AdminSettingsInternal::AdminSettingsInternal() : m_settingStream(Stream::AdminSettings) { LoadAdminSettings(); } void AdminSettingsInternal::SetAdminSettingAndSave(std::function setValue) { for (size_t i = 0; i < 10; ++i) { if (!setValue()) { return; } if (SaveAdminSettings()) { return; } // We need to reload the settings as they have changed LoadAdminSettings(); } THROW_HR_MSG(E_UNEXPECTED, "Too many attempts at SaveAdminSettings"); } void AdminSettingsInternal::SetAdminSetting(BoolAdminSetting setting, bool enabled) { SetAdminSettingAndSave([&]() { switch (setting) { case BoolAdminSetting::LocalManifestFiles: m_settingValues.LocalManifestFiles = enabled; return true; case BoolAdminSetting::BypassCertificatePinningForMicrosoftStore: m_settingValues.BypassCertificatePinningForMicrosoftStore = enabled; return true; case BoolAdminSetting::InstallerHashOverride: m_settingValues.InstallerHashOverride = enabled; return true; case BoolAdminSetting::LocalArchiveMalwareScanOverride: m_settingValues.LocalArchiveMalwareScanOverride = enabled; return true; case BoolAdminSetting::ProxyCommandLineOptions: m_settingValues.ProxyCommandLineOptions = enabled; return true; default: return false; } }); } void AdminSettingsInternal::SetAdminSetting(StringAdminSetting setting, const std::optional& value) { SetAdminSettingAndSave([&]() { switch (setting) { case StringAdminSetting::DefaultProxy: m_settingValues.DefaultProxy = value; return true; default: return false; } }); } bool AdminSettingsInternal::GetAdminSettingValue(BoolAdminSetting setting) const { switch (setting) { case BoolAdminSetting::LocalManifestFiles: return m_settingValues.LocalManifestFiles; case BoolAdminSetting::BypassCertificatePinningForMicrosoftStore: return m_settingValues.BypassCertificatePinningForMicrosoftStore; case BoolAdminSetting::InstallerHashOverride: return m_settingValues.InstallerHashOverride; case BoolAdminSetting::LocalArchiveMalwareScanOverride: return m_settingValues.LocalArchiveMalwareScanOverride; case BoolAdminSetting::ProxyCommandLineOptions: return m_settingValues.ProxyCommandLineOptions; default: return false; } } std::optional AdminSettingsInternal::GetAdminSettingValue(StringAdminSetting setting) const { switch (setting) { case StringAdminSetting::DefaultProxy: return m_settingValues.DefaultProxy; default: return std::nullopt; } } void AdminSettingsInternal::Reset() { m_settingStream.Remove(); } void AdminSettingsInternal::LoadAdminSettings() { auto stream = m_settingStream.Get(); if (!stream) { AICLI_LOG(Core, Verbose, << "Admin settings was not found"); return; } std::string adminSettingsYaml = Utility::ReadEntireStream(*stream); YAML::Node document; try { document = YAML::Load(adminSettingsYaml); } catch (const std::exception& e) { AICLI_LOG(YAML, Error, << "Admin settings contained invalid YAML (" << e.what() << ")"); return; } if (document.IsNull()) { AICLI_LOG(Core, Info, << "Admin settings is empty"); return; } if (!document.IsMap()) { AICLI_LOG(Core, Error, << "Admin settings did not contain the expected format"); return; } TryReadScalar(document, s_AdminSettingsYaml_LocalManifestFiles, m_settingValues.LocalManifestFiles); TryReadScalar(document, s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore, m_settingValues.BypassCertificatePinningForMicrosoftStore); TryReadScalar(document, s_AdminSettingsYaml_InstallerHashOverride, m_settingValues.InstallerHashOverride); TryReadScalar(document, s_AdminSettingsYaml_LocalArchiveMalwareScanOverride, m_settingValues.LocalArchiveMalwareScanOverride); TryReadScalar(document, s_AdminSettingsYaml_ProxyCommandLineOptions, m_settingValues.ProxyCommandLineOptions); std::string defaultProxy; if (TryReadScalar(document, s_AdminSettingsYaml_DefaultProxy, defaultProxy)) { m_settingValues.DefaultProxy.emplace(std::move(defaultProxy)); } } bool AdminSettingsInternal::SaveAdminSettings() { YAML::Emitter out; out << YAML::BeginMap; out << YAML::Key << s_AdminSettingsYaml_LocalManifestFiles << YAML::Value << m_settingValues.LocalManifestFiles; out << YAML::Key << s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore << YAML::Value << m_settingValues.BypassCertificatePinningForMicrosoftStore; out << YAML::Key << s_AdminSettingsYaml_InstallerHashOverride << YAML::Value << m_settingValues.InstallerHashOverride; out << YAML::Key << s_AdminSettingsYaml_LocalArchiveMalwareScanOverride << YAML::Value << m_settingValues.LocalArchiveMalwareScanOverride; out << YAML::Key << s_AdminSettingsYaml_ProxyCommandLineOptions << YAML::Value << m_settingValues.ProxyCommandLineOptions; if (m_settingValues.DefaultProxy) { out << YAML::Key << s_AdminSettingsYaml_DefaultProxy << YAML::Value << m_settingValues.DefaultProxy.value(); } out << YAML::EndMap; return m_settingStream.Set(out.str()); } auto GetPolicyStateForSetting(BoolAdminSetting setting) { auto policy = GetAdminSettingPolicy(setting); return GroupPolicies().GetState(policy); } std::optional> GetPolicyStateForSetting(StringAdminSetting setting) { switch (setting) { case AppInstaller::Settings::StringAdminSetting::DefaultProxy: return GroupPolicies().GetValueRef(); default: return std::nullopt; } } } BoolAdminSetting StringToBoolAdminSetting(std::string_view in) { BoolAdminSetting result = BoolAdminSetting::Unknown; if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_LocalManifestFiles, in)) { result = BoolAdminSetting::LocalManifestFiles; } else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore, in)) { result = BoolAdminSetting::BypassCertificatePinningForMicrosoftStore; } else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_InstallerHashOverride, in)) { result = BoolAdminSetting::InstallerHashOverride; } else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_LocalArchiveMalwareScanOverride, in)) { result = BoolAdminSetting::LocalArchiveMalwareScanOverride; } else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_ProxyCommandLineOptions, in)) { result = BoolAdminSetting::ProxyCommandLineOptions; } return result; } StringAdminSetting StringToStringAdminSetting(std::string_view in) { StringAdminSetting result = StringAdminSetting::Unknown; if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_DefaultProxy, in)) { result = StringAdminSetting::DefaultProxy; } return result; } Utility::LocIndView AdminSettingToString(BoolAdminSetting setting) { switch (setting) { case BoolAdminSetting::LocalManifestFiles: return s_AdminSettingsYaml_LocalManifestFiles; case BoolAdminSetting::BypassCertificatePinningForMicrosoftStore: return s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore; case BoolAdminSetting::InstallerHashOverride: return s_AdminSettingsYaml_InstallerHashOverride; case BoolAdminSetting::LocalArchiveMalwareScanOverride: return s_AdminSettingsYaml_LocalArchiveMalwareScanOverride; case BoolAdminSetting::ProxyCommandLineOptions: return s_AdminSettingsYaml_ProxyCommandLineOptions; default: return "Unknown"_liv; } } Utility::LocIndView AdminSettingToString(StringAdminSetting setting) { switch (setting) { case StringAdminSetting::DefaultProxy: return s_AdminSettingsYaml_DefaultProxy; default: return "Unknown"_liv; } } TogglePolicy::Policy GetAdminSettingPolicy(BoolAdminSetting setting) { switch (setting) { case BoolAdminSetting::LocalManifestFiles: return TogglePolicy::Policy::LocalManifestFiles; case BoolAdminSetting::BypassCertificatePinningForMicrosoftStore: return TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore; case BoolAdminSetting::InstallerHashOverride: return TogglePolicy::Policy::HashOverride; case BoolAdminSetting::LocalArchiveMalwareScanOverride: return TogglePolicy::Policy::LocalArchiveMalwareScanOverride; case BoolAdminSetting::ProxyCommandLineOptions: return TogglePolicy::Policy::ProxyCommandLineOptions; default: return TogglePolicy::Policy::None; } } bool EnableAdminSetting(BoolAdminSetting setting) { if (GetPolicyStateForSetting(setting) == PolicyState::Disabled) { return false; } AdminSettingsInternal adminSettingsInternal; adminSettingsInternal.SetAdminSetting(setting, true); return true; } bool DisableAdminSetting(BoolAdminSetting setting) { if (GetPolicyStateForSetting(setting) == PolicyState::Enabled) { return false; } AdminSettingsInternal adminSettingsInternal; adminSettingsInternal.SetAdminSetting(setting, false); return true; } bool IsAdminSettingEnabled(BoolAdminSetting setting) { // Check for a policy that overrides this setting. auto policyState = GetPolicyStateForSetting(setting); if (policyState != PolicyState::NotConfigured) { return policyState == PolicyState::Enabled; } AdminSettingsInternal adminSettingsInternal; return adminSettingsInternal.GetAdminSettingValue(setting); } bool SetAdminSetting(StringAdminSetting setting, std::string_view value) { if (GetPolicyStateForSetting(setting)) { return false; } AdminSettingsInternal adminSettingsInternal; adminSettingsInternal.SetAdminSetting(setting, std::string{ value }); return true; } bool ResetAdminSetting(StringAdminSetting setting) { if (GetPolicyStateForSetting(setting)) { return false; } AdminSettingsInternal adminSettingsInternal; adminSettingsInternal.SetAdminSetting(setting, std::nullopt); return true; } void ResetAllAdminSettings() { AdminSettingsInternal{}.Reset(); } std::optional GetAdminSetting(StringAdminSetting setting) { // Check for a policy that overrides this setting. auto policyState = GetPolicyStateForSetting(setting); if (policyState) { return policyState.value(); } AdminSettingsInternal adminSettingsInternal; return adminSettingsInternal.GetAdminSettingValue(setting); } std::vector GetAllBoolAdminSettings() { return GetAllSequentialEnumValues(BoolAdminSetting::Unknown); } std::vector GetAllStringAdminSettings() { return GetAllSequentialEnumValues(StringAdminSetting::Unknown); } } ================================================ FILE: src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj ================================================ true true false true 15.0 {5890d6ed-7c3b-40f3-b436-b54f640d9e65} Win32Proj AppInstallerLoggingCore 10.0.26100.0 10.0.17763.0 true Debug ARM64 Debug Win32 Fuzzing x64 Fuzzing Win32 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Debug x64 Release x64 StaticLibrary true true false true false false true false false false false true Spectre Spectre Spectre Spectre Spectre Spectre true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING Disabled _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;_DEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) true true true true true true false false false Windows Windows _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;WIN32;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) true true true false Windows MaxSpeed true true _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) true true true true true true false false false false false false true true false Windows Windows Windows MaxSpeed true true _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) true true true true true true false false false MultiThreaded MultiThreaded MultiThreaded false false false true true false Windows Windows Windows MaxSpeed true true _NO_ASYNCRTIMP;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD;WINGET_DISABLE_FOR_FUZZING;_DISABLE_VECTOR_ANNOTATION;_DISABLE_STRING_ANNOTATION $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true stdcpp17 MultiThreaded %(AdditionalOptions) /fsanitize=address /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div false true true false Windows true true true true true Create {f3f6e699-bc5d-4950-8a05-e49dd9eb0d51} This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {5cdf3fa3-e657-4d84-81bb-f740aa476143} {a9c14af9-ca74-4945-a19c-9e99df23a5ae} {41035fd6-dc74-4464-b9b1-4ffe95d6789c} {9b8e2682-3eb7-4530-bc9a-a57fafc44177} {552a58eb-8d07-41b2-87b5-3e71b9fb3cfd} {9dd9ab66-00f0-4b18-90ca-6c5da3dc01c4} Header Files Telemetry Public Public Public Public HttpStream HttpStream HttpStream Public Public Public Public Public Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Header Files Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Header Files Public\winget Public\winget Header Files Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Authentication Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Source Files Telemetry Source Files Source Files Source Files Source Files Source Files HttpStream HttpStream HttpStream Source Files Source Files Source Files Source Files Source Files Source Files Source Files Manifest Manifest Manifest Source Files Source Files Manifest Manifest Manifest Manifest Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Authentication Authentication Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Manifest Source Files ================================================ FILE: src/AppInstallerCommonCore/AppInstallerTelemetry.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerTelemetry.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerSHA256.h" #include "Public/AppInstallerStrings.h" #include "Public/winget/ThreadGlobals.h" #include "winget/Filesystem.h" #include "winget/UserSettings.h" #define AICLI_TraceLoggingStringView(_sv_,_name_) TraceLoggingCountedUtf8String(_sv_.data(), static_cast(_sv_.size()), _name_) #define AICLI_TraceLoggingWStringView(_sv_,_name_) TraceLoggingCountedWideString(_sv_.data(), static_cast(_sv_.size()), _name_) #define AICLI_TraceLoggingWriteActivity(_eventName_,...) TraceLoggingWriteActivity(\ g_hTraceProvider,\ _eventName_,\ s_useGlobalTelemetryActivityId ? &s_globalTelemetryLoggerActivityId : GetActivityId(),\ s_useGlobalTelemetryActivityId ? nullptr : GetParentActivityId(),\ TraceLoggingCountedUtf8String(m_caller.c_str(), static_cast(m_caller.size()), "Caller"),\ TraceLoggingPackedFieldEx(m_telemetryCorrelationJsonW.c_str(), static_cast((m_telemetryCorrelationJsonW.size() + 1) * sizeof(wchar_t)), TlgInUNICODESTRING, TlgOutJSON, "CvJson"),\ __VA_ARGS__) namespace AppInstaller::Logging { using namespace Utility; namespace { // TODO: This and all usages should be removed after transition to summary event in back end. static const uint32_t s_RootExecutionId = 0; static std::atomic_uint32_t s_subExecutionId{ s_RootExecutionId }; // Data that is needed by AnonymizeString constexpr std::wstring_view s_UserProfileReplacement = L"%USERPROFILE%"sv; // TODO: Temporary code to keep existing telemetry behavior static bool s_useGlobalTelemetryActivityId = false; static GUID s_globalTelemetryLoggerActivityId = GUID_NULL; void __stdcall wilResultLoggingCallback(const wil::FailureInfo& info) noexcept { Telemetry().LogFailure(info); } FailureTypeEnum ConvertWilFailureTypeToFailureType(wil::FailureType failureType) { switch (failureType) { case wil::FailureType::Exception: return FailureTypeEnum::ResultException; case wil::FailureType::Return: return FailureTypeEnum::ResultReturn; case wil::FailureType::Log: return FailureTypeEnum::ResultLog; case wil::FailureType::FailFast: return FailureTypeEnum::ResultFailFast; default: return FailureTypeEnum::Unknown; } } std::string_view LogExceptionTypeToString(FailureTypeEnum exceptionType) { switch (exceptionType) { case FailureTypeEnum::ResultException: return "wil::ResultException"sv; case FailureTypeEnum::WinrtHResultError: return "winrt::hresult_error"sv; case FailureTypeEnum::ResourceOpen: return "ResourceOpenException"sv; case FailureTypeEnum::StdException: return "std::exception"sv; case FailureTypeEnum::Unknown: default: return "unknown"sv; } } } TelemetrySummary::TelemetrySummary(const TelemetrySummary& other) { this->IsCOMCall = other.IsCOMCall; } TelemetryTraceLogger::TelemetryTraceLogger(bool useSummary) : m_useSummary(useSummary) { std::ignore = CoCreateGuid(&m_activityId); m_subExecutionId = s_RootExecutionId; } const GUID* TelemetryTraceLogger::GetActivityId() const { return &m_activityId; } const GUID* TelemetryTraceLogger::GetParentActivityId() const { return &m_parentActivityId; } bool TelemetryTraceLogger::DisableRuntime() { return m_isRuntimeEnabled.exchange(false); } void TelemetryTraceLogger::EnableRuntime() { m_isRuntimeEnabled = true; } void TelemetryTraceLogger::Initialize() { if (!m_isInitialized) { InitializeInternal(Settings::User()); } } bool TelemetryTraceLogger::TryInitialize() { if (!m_isInitialized) { // Only initialize if we already have the user settings, so that we can respect the telemetry setting. // We may not yet have the user settings if we are trying to report an error while reading them. auto userSettings = Settings::TryGetUser(); if (userSettings) { InitializeInternal(*userSettings); } } return m_isInitialized; } void TelemetryTraceLogger::SetTelemetryCorrelationJson(const std::wstring_view jsonStr_view) noexcept { // Check if passed in string is a valid Json formatted before returning the value // If invalid, return empty Json Json::CharReaderBuilder jsonBuilder; std::unique_ptr jsonReader(jsonBuilder.newCharReader()); std::unique_ptr pJsonValue = std::make_unique(); std::string errors; std::wstring jsonStrW{ jsonStr_view }; std::string jsonStr = ConvertToUTF8(jsonStrW.c_str()); bool result = jsonReader->parse(jsonStr.c_str(), jsonStr.c_str() + jsonStr.size(), pJsonValue.get(), &errors); if (result) { m_telemetryCorrelationJsonW = jsonStrW; AICLI_LOG(Core, Info, << "Passed in Correlation Vector Json is valid: " << jsonStr); } else { AICLI_LOG(Core, Error, << "Passed in Correlation Vector Json is invalid: " << jsonStr << "; Error: " << errors); } } void TelemetryTraceLogger::SetCaller(const std::string& caller) { auto callerUTF16 = Utility::ConvertToUTF16(caller); auto anonCaller = AnonymizeString(callerUTF16); m_caller = Utility::ConvertToUTF8(anonCaller); } void TelemetryTraceLogger::SetExecutionStage(uint32_t stage) noexcept { m_executionStage = stage; } std::unique_ptr TelemetryTraceLogger::CreateSubTraceLogger() const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !this->m_isInitialized); auto subTraceLogger = std::make_unique(*this); std::ignore = CoCreateGuid(&subTraceLogger->m_activityId); subTraceLogger->m_parentActivityId = this->m_activityId; subTraceLogger->m_subExecutionId = s_subExecutionId++; return subTraceLogger; } void TelemetryTraceLogger::LogFailure(const wil::FailureInfo& failure) const noexcept { if (IsTelemetryEnabled()) { auto anonMessage = AnonymizeString(failure.pszMessage); AICLI_TraceLoggingWriteActivity( "FailureInfo", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingHResult(failure.hr, "HResult"), AICLI_TraceLoggingWStringView(anonMessage, "Message"), TraceLoggingString(failure.pszModule, "Module"), TraceLoggingUInt32(failure.threadId, "ThreadId"), TraceLoggingUInt32(static_cast(failure.type), "Type"), TraceLoggingString(failure.pszFile, "File"), TraceLoggingUInt32(failure.uLineNumber, "Line"), TraceLoggingUInt32(m_executionStage, "ExecutionStage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.FailureHResult = failure.hr; m_summary.FailureMessage = anonMessage; m_summary.FailureModule = StringOrEmptyIfNull(failure.pszModule); m_summary.FailureThreadId = failure.threadId; m_summary.FailureType = ConvertWilFailureTypeToFailureType(failure.type); m_summary.FailureFile = StringOrEmptyIfNull(failure.pszFile); m_summary.FailureLine = failure.uLineNumber; } } // Also send failure to the log AICLI_LOG(Fail, Error, << [&]() { wchar_t message[2048]; GetFailureLogString(message, ARRAYSIZE(message), failure); return Utility::ConvertToUTF8(message); }()); } void TelemetryTraceLogger::LogStartup(bool isCOMCall) const noexcept { LocIndString version = Runtime::GetClientVersion(); LocIndString packageVersion; if (Runtime::IsRunningInPackagedContext()) { packageVersion = Runtime::GetPackageVersion(); } if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "ClientVersion", TraceLoggingBool(isCOMCall, "IsCOMCall"), TraceLoggingCountedString(version->c_str(), static_cast(version->size()), "Version"), TraceLoggingCountedString(packageVersion->c_str(), static_cast(packageVersion->size()), "PackageVersion"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.IsCOMCall = isCOMCall; } } AICLI_LOG(Core, Info, << "WinGet, version [" << version << "], activity [" << *GetActivityId() << ']'); AICLI_LOG(Core, Info, << "Process: " << Filesystem::GetExecutablePathForProcess(GetCurrentProcess()).filename() << "[" << GetCurrentProcessId() << "], Offset: " << &__ImageBase); AICLI_LOG(Core, Info, << "OS: " << Runtime::GetOSVersion()); AICLI_LOG(Core, Info, << "Command line Args: " << Utility::ConvertToUTF8(GetCommandLineW())); if (Runtime::IsRunningInPackagedContext()) { AICLI_LOG(Core, Info, << "Package: " << packageVersion); } AICLI_LOG(Core, Info, << "IsCOMCall:" << isCOMCall << "; Caller: " << m_caller); Log().SetTag(Tag::HeadersComplete); } void TelemetryTraceLogger::LogCommand(std::string_view commandName) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "CommandFound", AICLI_TraceLoggingStringView(commandName, "Command"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.Command = commandName; } } AICLI_LOG(CLI, Info, << "Leaf command to execute: " << commandName); } void TelemetryTraceLogger::LogCommandSuccess(std::string_view commandName) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "CommandSuccess", AICLI_TraceLoggingStringView(commandName, "Command"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.CommandSuccess = true; } } AICLI_LOG(CLI, Info, << "Leaf command succeeded: " << commandName); } void TelemetryTraceLogger::LogCommandTermination(HRESULT hr, std::string_view file, size_t line) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "CommandTermination", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingHResult(hr, "HResult"), AICLI_TraceLoggingStringView(file, "File"), TraceLoggingUInt64(static_cast(line), "Line"), TraceLoggingUInt32(m_executionStage, "ExecutionStage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.FailureHResult = hr; m_summary.FailureType = FailureTypeEnum::CommandTermination; m_summary.FailureFile = file; m_summary.FailureLine = static_cast(line); } } AICLI_LOG(CLI, Error, << "Terminating context: 0x" << SetHRFormat << hr << " at " << file << ":" << line); } void TelemetryTraceLogger::LogException(FailureTypeEnum type, std::string_view message) const noexcept { auto exceptionTypeString = LogExceptionTypeToString(type); if (IsTelemetryEnabled()) { auto anonMessage = AnonymizeString(Utility::ConvertToUTF16(message)); AICLI_TraceLoggingWriteActivity( "Exception", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(exceptionTypeString, "Type"), AICLI_TraceLoggingWStringView(anonMessage, "Message"), TraceLoggingUInt32(m_executionStage, "ExecutionStage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.FailureType = type; m_summary.FailureMessage = anonMessage; } } AICLI_LOG(CLI, Error, << "Caught " << exceptionTypeString << ": " << message); } void TelemetryTraceLogger::LogIsManifestLocal(bool isLocalManifest) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "GetManifest", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingBool(isLocalManifest, "IsManifestLocal"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.IsManifestLocal = isLocalManifest; } } } void TelemetryTraceLogger::LogManifestFields(std::string_view id, std::string_view name, std::string_view version) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "ManifestFields", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(name, "Name"), AICLI_TraceLoggingStringView(version, "Version"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.PackageIdentifier = id; m_summary.PackageName = name; m_summary.PackageVersion = version; } } AICLI_LOG(CLI, Info, << "Manifest fields: Name [" << name << "], Version [" << version << ']'); } void TelemetryTraceLogger::LogNoAppMatch() const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "NoAppMatch", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); } AICLI_LOG(CLI, Info, << "No app found matching input criteria"); } void TelemetryTraceLogger::LogMultiAppMatch() const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "MultiAppMatch", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); } AICLI_LOG(CLI, Info, << "Multiple apps found matching input criteria"); } void TelemetryTraceLogger::LogAppFound(std::string_view name, std::string_view id) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "AppFound", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(name, "Name"), AICLI_TraceLoggingStringView(id, "Id"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.PackageIdentifier = id; m_summary.PackageName = name; } } AICLI_LOG(CLI, Info, << "Found one app. App id: " << id << " App name: " << name); } void TelemetryTraceLogger::LogSelectedInstaller(int arch, std::string_view url, std::string_view installerType, std::string_view scope, std::string_view language) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "SelectedInstaller", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingInt32(arch, "Arch"), AICLI_TraceLoggingStringView(url, "Url"), AICLI_TraceLoggingStringView(installerType, "InstallerType"), AICLI_TraceLoggingStringView(scope, "Scope"), AICLI_TraceLoggingStringView(language, "Language"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.InstallerArchitecture = arch; m_summary.InstallerUrl = url; m_summary.InstallerType = installerType; m_summary.InstallerScope = scope; m_summary.InstallerLocale = language; } } AICLI_LOG(CLI, Verbose, << "Completed installer selection."); AICLI_LOG(CLI, Verbose, << "Selected installer Architecture: " << arch); AICLI_LOG(CLI, Verbose, << "Selected installer URL: " << url); AICLI_LOG(CLI, Verbose, << "Selected installer InstallerType: " << installerType); AICLI_LOG(CLI, Verbose, << "Selected installer Scope: " << scope); AICLI_LOG(CLI, Verbose, << "Selected installer Language: " << language); } void TelemetryTraceLogger::LogSearchRequest( std::string_view type, std::string_view query, std::string_view id, std::string_view name, std::string_view moniker, std::string_view tag, std::string_view command, size_t maximum, std::string_view request) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "SearchRequest", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(type, "Type"), AICLI_TraceLoggingStringView(query, "Query"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(name, "Name"), AICLI_TraceLoggingStringView(moniker, "Moniker"), AICLI_TraceLoggingStringView(tag, "Tag"), AICLI_TraceLoggingStringView(command, "Command"), TraceLoggingUInt64(static_cast(maximum), "Maximum"), AICLI_TraceLoggingStringView(request, "Request"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.SearchType = type; m_summary.SearchQuery = query; m_summary.SearchId = id; m_summary.SearchName = name; m_summary.SearchMoniker = moniker; m_summary.SearchTag = tag; m_summary.SearchCommand = command; m_summary.SearchMaximum = static_cast(maximum); m_summary.SearchRequest = request; } } } void TelemetryTraceLogger::LogSearchResultCount(uint64_t resultCount) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "SearchResultCount", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), TraceLoggingUInt64(resultCount, "ResultCount"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.SearchResultCount = resultCount; } } AICLI_LOG(CLI, Verbose, << "Search result size: " << resultCount); } void TelemetryTraceLogger::LogInstallerHashMismatch( std::string_view id, std::string_view version, std::string_view channel, const std::vector& expected, const std::vector& actual, bool overrideHashMismatch, uint64_t downloadSizeInBytes, const std::optional& contentType) const noexcept { std::string actualContentType = contentType.value_or(std::string{}); if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "HashMismatch", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), AICLI_TraceLoggingStringView(channel, "Channel"), TraceLoggingBinary(expected.data(), static_cast(expected.size()), "Expected"), TraceLoggingBinary(actual.data(), static_cast(actual.size()), "Actual"), TraceLoggingBool(overrideHashMismatch, "Override"), TraceLoggingUInt64(downloadSizeInBytes, "ActualSize"), AICLI_TraceLoggingStringView(actualContentType, "ContentType"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.PackageIdentifier = id; m_summary.PackageVersion = version; m_summary.Channel = channel; m_summary.HashMismatchExpected = expected; m_summary.HashMismatchActual = actual; m_summary.HashMismatchOverride = overrideHashMismatch; m_summary.HashMismatchActualSize = downloadSizeInBytes; m_summary.HashMismatchContentType = actualContentType; } } AICLI_LOG(CLI, Error, << "Package hash verification failed. SHA256 in manifest [" << Utility::SHA256::ConvertToString(expected) << "] does not match download [" << Utility::SHA256::ConvertToString(actual) << "] with file size [" << downloadSizeInBytes << "] and content type [" << actualContentType << "]"); } void TelemetryTraceLogger::LogInstallerFailure(std::string_view id, std::string_view version, std::string_view channel, std::string_view type, uint32_t errorCode) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "InstallerFailure", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), AICLI_TraceLoggingStringView(channel, "Channel"), AICLI_TraceLoggingStringView(type, "Type"), TraceLoggingUInt32(errorCode, "ErrorCode"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.PackageIdentifier = id; m_summary.PackageVersion = version; m_summary.Channel = channel; m_summary.InstallerExecutionType = type; m_summary.InstallerErrorCode = errorCode; } } AICLI_LOG(CLI, Error, << type << " installer failed: " << errorCode); } void TelemetryTraceLogger::LogUninstallerFailure(std::string_view id, std::string_view version, std::string_view type, uint32_t errorCode) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "UninstallerFailure", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), AICLI_TraceLoggingStringView(type, "Type"), TraceLoggingUInt32(errorCode, "ErrorCode"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.PackageIdentifier = id; m_summary.PackageVersion = version; m_summary.UninstallerExecutionType = type; m_summary.UninstallerErrorCode = errorCode; } } AICLI_LOG(CLI, Error, << type << " uninstaller failed: " << errorCode); } void TelemetryTraceLogger::LogSuccessfulInstallARPChange( std::string_view sourceIdentifier, std::string_view packageIdentifier, std::string_view packageVersion, std::string_view packageChannel, size_t changesToARP, size_t matchesInARP, size_t countOfIntersectionOfChangesAndMatches, std::string_view arpName, std::string_view arpVersion, std::string_view arpPublisher, std::string_view arpLanguage) const noexcept { if (IsTelemetryEnabled()) { size_t languageNumber = 0xFFFF; try { std::istringstream languageConversion{ std::string{ arpLanguage } }; languageConversion >> languageNumber; } catch (...) {} AICLI_TraceLoggingWriteActivity( "InstallARPChange", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(sourceIdentifier, "SourceIdentifier"), AICLI_TraceLoggingStringView(packageIdentifier, "PackageIdentifier"), AICLI_TraceLoggingStringView(packageVersion, "PackageVersion"), AICLI_TraceLoggingStringView(packageChannel, "PackageChannel"), TraceLoggingUInt64(static_cast(changesToARP), "ChangesToARP"), TraceLoggingUInt64(static_cast(matchesInARP), "MatchesInARP"), TraceLoggingUInt64(static_cast(countOfIntersectionOfChangesAndMatches), "ChangesThatMatch"), AICLI_TraceLoggingStringView(arpName, "ARPName"), AICLI_TraceLoggingStringView(arpVersion, "ARPVersion"), AICLI_TraceLoggingStringView(arpPublisher, "ARPPublisher"), TraceLoggingUInt64(static_cast(languageNumber), "ARPLanguage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage | PDT_SoftwareSetupAndInventory), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); if (m_useSummary) { m_summary.SourceIdentifier = sourceIdentifier; m_summary.PackageIdentifier = packageIdentifier; m_summary.PackageVersion = packageVersion; m_summary.Channel = packageChannel; m_summary.ChangesToARP = static_cast(changesToARP); m_summary.MatchesInARP = static_cast(matchesInARP); m_summary.ChangesThatMatch = static_cast(countOfIntersectionOfChangesAndMatches); m_summary.ARPName = arpName; m_summary.ARPVersion = arpVersion; m_summary.ARPPublisher = arpPublisher; m_summary.ARPLanguage = static_cast(languageNumber); } } AICLI_LOG(CLI, Info, << "During package install, " << changesToARP << " changes to ARP were observed, " << matchesInARP << " matches were found for the package, and " << countOfIntersectionOfChangesAndMatches << " packages were in both"); if (arpName.empty()) { AICLI_LOG(CLI, Info, << "No single entry was determined to be associated with the package"); } else { AICLI_LOG(CLI, Info, << "The entry determined to be associated with the package is '" << arpName << "', with publisher '" << arpPublisher << "'"); } } void TelemetryTraceLogger::LogNonFatalDOError(std::string_view url, HRESULT hr) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "NonFatalDOError", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(url, "Url"), TraceLoggingHResult(hr, "HResult"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)); if (m_useSummary) { m_summary.DOUrl = url; m_summary.DOHResult = hr; } } } void TelemetryTraceLogger::LogRepairFailure(std::string_view id, std::string_view version, std::string_view type, uint32_t errorCode) const noexcept { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "RepairFailure", TraceLoggingUInt32(m_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(id, "Id"), AICLI_TraceLoggingStringView(version, "Version"), AICLI_TraceLoggingStringView(type, "Type"), TraceLoggingUInt32(errorCode, "ErrorCode"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)); if (m_useSummary) { m_summary.PackageIdentifier = id; m_summary.PackageVersion = version; m_summary.RepairExecutionType = type; m_summary.RepairErrorCode = errorCode; } } AICLI_LOG(CLI, Error, << type << " repair failed: " << errorCode); } TelemetryTraceLogger::~TelemetryTraceLogger() { if (IsTelemetryEnabled()) { LocIndString version = Runtime::GetClientVersion(); LocIndString packageVersion; if (Runtime::IsRunningInPackagedContext()) { packageVersion = Runtime::GetPackageVersion(); } if (m_useSummary) { TraceLoggingWriteActivity( g_hTraceProvider, "SummaryV2", GetActivityId(), GetParentActivityId(), // From member fields or program info. AICLI_TraceLoggingStringView(m_caller, "Caller"), TraceLoggingPackedFieldEx(m_telemetryCorrelationJsonW.c_str(), static_cast((m_telemetryCorrelationJsonW.size() + 1) * sizeof(wchar_t)), TlgInUNICODESTRING, TlgOutJSON, "CvJson"), TraceLoggingCountedString(version->c_str(), static_cast(version->size()), "ClientVersion"), TraceLoggingCountedString(packageVersion->c_str(), static_cast(packageVersion->size()), "ClientPackageVersion"), TraceLoggingBool(Runtime::IsReleaseBuild(), "IsReleaseBuild"), TraceLoggingUInt32(m_executionStage, "ExecutionStage"), // From TelemetrySummary TraceLoggingHResult(m_summary.FailureHResult, "FailureHResult"), AICLI_TraceLoggingWStringView(m_summary.FailureMessage, "FailureMessage"), AICLI_TraceLoggingStringView(m_summary.FailureModule, "FailureModule"), TraceLoggingUInt32(m_summary.FailureThreadId, "FailureThreadId"), TraceLoggingUInt32(static_cast(m_summary.FailureType), "FailureType"), AICLI_TraceLoggingStringView(m_summary.FailureFile, "FailureFile"), TraceLoggingUInt32(m_summary.FailureLine, "FailureLine"), TraceLoggingBool(m_summary.IsCOMCall, "IsCOMCall"), AICLI_TraceLoggingStringView(m_summary.Command, "Command"), TraceLoggingBool(m_summary.CommandSuccess, "CommandSuccess"), TraceLoggingBool(m_summary.IsManifestLocal, "IsManifestLocal"), AICLI_TraceLoggingStringView(m_summary.PackageIdentifier, "PackageIdentifier"), AICLI_TraceLoggingStringView(m_summary.PackageName, "PackageName"), AICLI_TraceLoggingStringView(m_summary.PackageVersion, "PackageVersion"), AICLI_TraceLoggingStringView(m_summary.Channel, "Channel"), AICLI_TraceLoggingStringView(m_summary.SourceIdentifier, "SourceIdentifier"), TraceLoggingInt32(m_summary.InstallerArchitecture, "InstallerArchitecture"), AICLI_TraceLoggingStringView(m_summary.InstallerUrl, "InstallerUrl"), AICLI_TraceLoggingStringView(m_summary.InstallerType, "InstallerType"), AICLI_TraceLoggingStringView(m_summary.InstallerScope, "InstallerScope"), AICLI_TraceLoggingStringView(m_summary.InstallerLocale, "InstallerLocale"), AICLI_TraceLoggingStringView(m_summary.SearchType, "SearchType"), AICLI_TraceLoggingStringView(m_summary.SearchQuery, "SearchQuery"), AICLI_TraceLoggingStringView(m_summary.SearchId, "SearchId"), AICLI_TraceLoggingStringView(m_summary.SearchName, "SearchName"), AICLI_TraceLoggingStringView(m_summary.SearchMoniker, "SearchMoniker"), AICLI_TraceLoggingStringView(m_summary.SearchTag, "SearchTag"), AICLI_TraceLoggingStringView(m_summary.SearchCommand, "SearchCommand"), TraceLoggingUInt64(m_summary.SearchMaximum, "SearchMaximum"), AICLI_TraceLoggingStringView(m_summary.SearchRequest, "SearchRequest"), TraceLoggingUInt64(m_summary.SearchResultCount, "SearchResultCount"), TraceLoggingBinary(m_summary.HashMismatchExpected.data(), static_cast(m_summary.HashMismatchExpected.size()), "HashMismatchExpected"), TraceLoggingBinary(m_summary.HashMismatchActual.data(), static_cast(m_summary.HashMismatchActual.size()), "HashMismatchActual"), TraceLoggingBool(m_summary.HashMismatchOverride, "HashMismatchOverride"), TraceLoggingUInt64(m_summary.HashMismatchActualSize, "HashMismatchActualSize"), AICLI_TraceLoggingStringView(m_summary.HashMismatchContentType, "HashMismatchContentType"), AICLI_TraceLoggingStringView(m_summary.InstallerExecutionType, "InstallerExecutionType"), TraceLoggingUInt32(m_summary.InstallerErrorCode, "InstallerErrorCode"), AICLI_TraceLoggingStringView(m_summary.UninstallerExecutionType, "UninstallerExecutionType"), TraceLoggingUInt32(m_summary.UninstallerErrorCode, "UninstallerErrorCode"), TraceLoggingUInt64(m_summary.ChangesToARP, "ChangesToARP"), TraceLoggingUInt64(m_summary.MatchesInARP, "MatchesInARP"), TraceLoggingUInt64(m_summary.ChangesThatMatch, "ChangesThatMatch"), TraceLoggingUInt64(m_summary.ARPLanguage, "ARPLanguage"), AICLI_TraceLoggingStringView(m_summary.ARPName, "ARPName"), AICLI_TraceLoggingStringView(m_summary.ARPVersion, "ARPVersion"), AICLI_TraceLoggingStringView(m_summary.ARPPublisher, "ARPPublisher"), AICLI_TraceLoggingStringView(m_summary.DOUrl, "DOUrl"), TraceLoggingHResult(m_summary.DOHResult, "DOHResult"), AICLI_TraceLoggingStringView(m_summary.RepairExecutionType, "RepairExecutionType"), TraceLoggingUInt32(m_summary.RepairErrorCode, "RepairErrorCode"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage | PDT_SoftwareSetupAndInventory), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)); } } } bool TelemetryTraceLogger::IsTelemetryEnabled() const noexcept { return g_IsTelemetryProviderEnabled && m_isInitialized && m_isSettingEnabled && m_isRuntimeEnabled; } void TelemetryTraceLogger::InitializeInternal(const AppInstaller::Settings::UserSettings& userSettings) { m_isSettingEnabled = !userSettings.Get(); m_isInitialized = true; } std::wstring TelemetryTraceLogger::AnonymizeString(const wchar_t* input) const noexcept { return input ? AnonymizeString(std::wstring_view{ input }) : std::wstring{}; } std::wstring TelemetryTraceLogger::AnonymizeString(std::wstring_view input) const noexcept try { // GetPathTo() may need to read the settings, so this function should only be called after settings are initialized. // To ensure that, this function is only called when emitting an event, and we disable the telemetry until settings are ready. static const std::wstring s_UserProfile = Runtime::GetPathTo(Runtime::PathName::UserProfile).wstring(); return Utility::ReplaceWhileCopying(input, s_UserProfile, s_UserProfileReplacement); } catch (...) { return std::wstring{ input }; } #ifndef AICLI_DISABLE_TEST_HOOKS static std::shared_ptr s_TelemetryTraceLogger_TestOverride; #endif TelemetryTraceLogger& Telemetry() { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_TelemetryTraceLogger_TestOverride) { return *s_TelemetryTraceLogger_TestOverride.get(); } #endif ThreadLocalStorage::ThreadGlobals* pThreadGlobals = ThreadLocalStorage::ThreadGlobals::GetForCurrentThread(); if (pThreadGlobals) { return *reinterpret_cast(pThreadGlobals->GetTelemetryObject()); } else { // For the global telemetry object, we may not have yet read the settings file. // In that case, we will not be able to initialize it, so we need to try it // each time we get the object. static TelemetryTraceLogger processGlobalTelemetry(/* useSummary */ false); processGlobalTelemetry.TryInitialize(); return processGlobalTelemetry; } } void EnableWilFailureTelemetry() { wil::SetResultLoggingCallback(wilResultLoggingCallback); } void UseGlobalTelemetryLoggerActivityIdOnly() { s_useGlobalTelemetryActivityId = true; std::ignore = CoCreateGuid(&s_globalTelemetryLoggerActivityId); } DisableTelemetryScope::DisableTelemetryScope() { m_token = Telemetry().DisableRuntime(); } DisableTelemetryScope::~DisableTelemetryScope() { if (m_token) { Telemetry().EnableRuntime(); } } #ifndef AICLI_DISABLE_TEST_HOOKS // Replace this test hook with context telemetry when it gets moved over void TestHook_SetTelemetryOverride(std::shared_ptr ttl) { s_TelemetryTraceLogger_TestOverride = std::move(ttl); } #endif } ================================================ FILE: src/AppInstallerCommonCore/Architecture.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerArchitecture.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerStrings.h" namespace AppInstaller::Utility { using namespace literals; namespace { // IsWow64GuestMachineSupported() is available starting on Windows 10, version 1709 (RS3). // We generally target a later version (version 1809, RS5), but the WinGetUtil is used in // Azure Functions that run on version 1607 (RS1) where it is not available. So, we load and // call this function only if available. using IsWow64GuestMachineSupportedPtr = decltype(&IsWow64GuestMachineSupported); struct IsWow64GuestMachineSupportedHelper { IsWow64GuestMachineSupportedHelper() { m_module.reset(LoadLibraryEx(L"api-ms-win-core-wow64-l1-1-2.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)); if (!m_module) { AICLI_LOG(Core, Verbose, << "Could not load api-ms-win-core-wow64-l1-1-2.dll"); return; } m_isWow64GuestMachineSupported = reinterpret_cast(GetProcAddress(m_module.get(), "IsWow64GuestMachineSupported")); if (!m_isWow64GuestMachineSupported) { AICLI_LOG(Core, Verbose, << "Could not get proc address of IsWow64GuestMachineSupported"); return; } } void AddArchitectureIfGuestMachineSupported(std::vector& target, Architecture architecture, USHORT guestMachine) { if (m_isWow64GuestMachineSupported) { BOOL supported = FALSE; LOG_IF_FAILED(m_isWow64GuestMachineSupported(guestMachine, &supported)); if (supported) { target.push_back(architecture); } } } private: wil::unique_hmodule m_module; IsWow64GuestMachineSupportedPtr m_isWow64GuestMachineSupported = nullptr; }; void AddArchitectureIfGuestMachineSupported(std::vector& target, Architecture architecture, USHORT guestMachine) { IsWow64GuestMachineSupportedHelper helper; helper.AddArchitectureIfGuestMachineSupported(target, architecture, guestMachine); } // These types are defined in a future SDK and can be removed when we actually have them available. // The exception is that None was added (and this is an enum class). enum class MACHINE_ATTRIBUTES { None = 0, UserEnabled = 0x00000001, KernelEnabled = 0x00000002, Wow64Container = 0x00000004 }; DEFINE_ENUM_FLAG_OPERATORS(MACHINE_ATTRIBUTES); using GetMachineTypeAttributesPtr = HRESULT(WINAPI*)(USHORT Machine, MACHINE_ATTRIBUTES* MachineTypeAttributes); // GetMachineTypeAttributes can apparently replace IsWow64GuestMachineSupported, but no reason to do so right now. struct GetMachineTypeAttributesHelper { GetMachineTypeAttributesHelper() { m_module.reset(LoadLibraryEx(L"api-ms-win-core-processthreads-l1-1-7.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)); if (!m_module) { AICLI_LOG(Core, Verbose, << "Could not load api-ms-win-core-processthreads-l1-1-7.dll"); return; } m_getMachineTypeAttributes = reinterpret_cast(GetProcAddress(m_module.get(), "GetMachineTypeAttributes")); if (!m_getMachineTypeAttributes) { AICLI_LOG(Core, Verbose, << "Could not get proc address of GetMachineTypeAttributes"); return; } } void AddArchitectureIfMachineTypeAttributesUserEnabled(std::vector& target, Architecture architecture, USHORT guestMachine) { if (m_getMachineTypeAttributes) { MACHINE_ATTRIBUTES attributes = MACHINE_ATTRIBUTES::None; if (SUCCEEDED_LOG(m_getMachineTypeAttributes(guestMachine, &attributes))) { if (WI_IsFlagSet(attributes, MACHINE_ATTRIBUTES::UserEnabled)) { target.push_back(architecture); } } } } private: wil::unique_hmodule m_module; GetMachineTypeAttributesPtr m_getMachineTypeAttributes = nullptr; }; void AddArchitectureIfMachineTypeAttributesUserEnabled(std::vector& target, Architecture architecture, USHORT guestMachine) { GetMachineTypeAttributesHelper helper; helper.AddArchitectureIfMachineTypeAttributesUserEnabled(target, architecture, guestMachine); } // Gets the applicable architectures for the current machine. std::vector CreateApplicableArchitecturesVector() { std::vector applicableArchs; switch (GetSystemArchitecture()) { case Architecture::Arm64: { applicableArchs.push_back(Architecture::Arm64); AddArchitectureIfGuestMachineSupported(applicableArchs, Architecture::Arm, IMAGE_FILE_MACHINE_ARMNT); AddArchitectureIfMachineTypeAttributesUserEnabled(applicableArchs, Architecture::X64, IMAGE_FILE_MACHINE_AMD64); AddArchitectureIfGuestMachineSupported(applicableArchs, Architecture::X86, IMAGE_FILE_MACHINE_I386); applicableArchs.push_back(Architecture::Neutral); } break; case Architecture::Arm: applicableArchs.push_back(Architecture::Arm); applicableArchs.push_back(Architecture::Neutral); break; case Architecture::X86: applicableArchs.push_back(Architecture::X86); applicableArchs.push_back(Architecture::Neutral); break; case Architecture::X64: applicableArchs.push_back(Architecture::X64); AddArchitectureIfGuestMachineSupported(applicableArchs, Architecture::X86, IMAGE_FILE_MACHINE_I386); applicableArchs.push_back(Architecture::Neutral); break; default: applicableArchs.push_back(Architecture::Neutral); } return applicableArchs; } } Architecture ConvertToArchitectureEnum(std::string_view archStr) { std::string arch = ToLower(archStr); if (arch == "x86") { return Architecture::X86; } else if (arch == "x64") { return Architecture::X64; } else if (arch == "arm") { return Architecture::Arm; } else if (arch == "arm64") { return Architecture::Arm64; } else if (arch == "neutral") { return Architecture::Neutral; } AICLI_LOG(Core, Info, << "ConvertToArchitectureEnum: Unknown architecture: " << archStr); return Architecture::Unknown; } std::optional<::AppInstaller::Utility::Architecture> ConvertToArchitectureEnum(winrt::Windows::System::ProcessorArchitecture architecture) { switch (architecture) { case winrt::Windows::System::ProcessorArchitecture::X86: return ::AppInstaller::Utility::Architecture::X86; case winrt::Windows::System::ProcessorArchitecture::Arm: return ::AppInstaller::Utility::Architecture::Arm; case winrt::Windows::System::ProcessorArchitecture::X64: return ::AppInstaller::Utility::Architecture::X64; case winrt::Windows::System::ProcessorArchitecture::Neutral: return ::AppInstaller::Utility::Architecture::Neutral; case winrt::Windows::System::ProcessorArchitecture::Arm64: return ::AppInstaller::Utility::Architecture::Arm64; } return {}; } LocIndView ToString(Architecture architecture) { switch (architecture) { case Architecture::Neutral: return "Neutral"_liv; case Architecture::X86: return "X86"_liv; case Architecture::X64: return "X64"_liv; case Architecture::Arm: return "Arm"_liv; case Architecture::Arm64: return "Arm64"_liv; } return "Unknown"_liv; } Architecture GetSystemArchitecture() { Architecture systemArchitecture = Architecture::Unknown; USHORT processArchitecture = IMAGE_FILE_MACHINE_UNKNOWN; USHORT machineArchitecture = IMAGE_FILE_MACHINE_UNKNOWN; // Just log the error if failed and return architecture Unknown. LOG_IF_WIN32_BOOL_FALSE(IsWow64Process2(GetCurrentProcess(), &processArchitecture, &machineArchitecture)); switch (machineArchitecture) { case IMAGE_FILE_MACHINE_AMD64: systemArchitecture = Architecture::X64; break; case IMAGE_FILE_MACHINE_ARM: systemArchitecture = Architecture::Arm; break; case IMAGE_FILE_MACHINE_ARM64: systemArchitecture = Architecture::Arm64; break; case IMAGE_FILE_MACHINE_I386: systemArchitecture = Architecture::X86; break; } return systemArchitecture; } const std::vector& GetApplicableArchitectures() { static std::vector applicableArchs = CreateApplicableArchitecturesVector(); return applicableArchs; } const std::vector& GetAllArchitectures() { static std::vector allArchs = { Architecture::Neutral, Architecture::X86, Architecture::X64, Architecture::Arm, Architecture::Arm64 }; return allArchs; } int IsApplicableArchitecture(Architecture arch) { return IsApplicableArchitecture(arch, GetApplicableArchitectures()); } int IsApplicableArchitecture(Architecture arch, const std::vector& allowedArchitectures) { auto it = std::find(allowedArchitectures.begin(), allowedArchitectures.end(), arch); if (it != allowedArchitectures.end()) { return static_cast(std::distance(it, allowedArchitectures.end())); } else { return InapplicableArchitecture; } } } ================================================ FILE: src/AppInstallerCommonCore/Archive.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/Archive.h" // TODO: Move include statement to pch.h and resolve build errors #pragma warning( push ) #pragma warning ( disable : 4189 4244 26451 ) #include #pragma warning ( pop ) namespace AppInstaller::Archive { using unique_pidlist_absolute = wil::unique_any; using unique_lpitemidlist = wil::unique_any; HRESULT TryExtractArchive(const std::filesystem::path& archivePath, const std::filesystem::path& destPath) { wil::com_ptr pFileOperation; RETURN_IF_FAILED(CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pFileOperation))); RETURN_IF_FAILED(pFileOperation->SetOperationFlags(FOF_NO_UI)); wil::com_ptr pShellItemTo; RETURN_IF_FAILED(SHCreateItemFromParsingName(destPath.c_str(), NULL, IID_PPV_ARGS(&pShellItemTo))); unique_pidlist_absolute pidlFull; RETURN_IF_FAILED(SHParseDisplayName(archivePath.c_str(), NULL, &pidlFull, 0, NULL)); wil::com_ptr pArchiveShellFolder; RETURN_IF_FAILED(SHBindToObject(NULL, pidlFull.get(), NULL, IID_PPV_ARGS(&pArchiveShellFolder))); wil::com_ptr pEnumIdList; RETURN_IF_FAILED(pArchiveShellFolder->EnumObjects(nullptr, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIdList)); unique_lpitemidlist pidlChild; ULONG nFetched; while (pEnumIdList->Next(1, wil::out_param_ptr(pidlChild), &nFetched) == S_OK && nFetched == 1) { wil::com_ptr pShellItemFrom; STRRET strFolderName; WCHAR szFolderName[MAX_PATH]; RETURN_IF_FAILED(pArchiveShellFolder->GetDisplayNameOf(pidlChild.get(), SHGDN_INFOLDER | SHGDN_FORPARSING, &strFolderName)); RETURN_IF_FAILED(StrRetToBuf(&strFolderName, pidlChild.get(), szFolderName, MAX_PATH)); RETURN_IF_FAILED(SHCreateItemWithParent(pidlFull.get(), pArchiveShellFolder.get(), pidlChild.get(), IID_PPV_ARGS(&pShellItemFrom))); RETURN_IF_FAILED(pFileOperation->CopyItem(pShellItemFrom.get(), pShellItemTo.get(), NULL, NULL)); } RETURN_IF_FAILED(pFileOperation->PerformOperations()); return S_OK; } #ifndef AICLI_DISABLE_TEST_HOOKS static bool* s_ScanArchiveResult_TestHook_Override = nullptr; void TestHook_SetScanArchiveResult_Override(bool* status) { s_ScanArchiveResult_TestHook_Override = status; } #endif bool ScanZipFile(const std::filesystem::path& zipPath) { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_ScanArchiveResult_TestHook_Override) { return *s_ScanArchiveResult_TestHook_Override; } #endif std::ifstream instream{ zipPath, std::ios::in | std::ios::binary }; std::vector data{ { std::istreambuf_iterator{ instream } }, std::istreambuf_iterator{} }; uint8_t* buffer = &data[0]; uint64_t flag = 0; int scanResult = pure_zip(buffer, data.size(), flag); return scanResult == 0; } } ================================================ FILE: src/AppInstallerCommonCore/Authentication/Authentication.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/Authentication.h" #include "WebAccountManagerAuthenticator.h" #include #include using namespace std::string_view_literals; namespace AppInstaller::Authentication { namespace { constexpr std::string_view s_BearerTokenPrefix = "Bearer "sv; // Default Azure Blob Storage resource value. Used when manifest author did not provide specific blob resource. constexpr std::string_view s_DefaultAzureBlobStorageResource = "https://storage.azure.com/"sv; } Authenticator::Authenticator(AuthenticationInfo info, AuthenticationArguments args) { THROW_HR_IF(E_UNEXPECTED, args.Mode == AuthenticationMode::Unknown); THROW_HR_IF(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED, info.Type == AuthenticationType::Unknown); THROW_HR_IF(E_UNEXPECTED, info.Type == AuthenticationType::None); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO, !info.ValidateIntegrity()); AICLI_LOG(Core, Info, << "AuthenticationArguments values. Mode: " << AuthenticationModeToString(args.Mode) << ", Account: " << args.AuthenticationAccount); if (info.Type == AuthenticationType::MicrosoftEntraId || info.Type == AuthenticationType::MicrosoftEntraIdForAzureBlobStorage) { AICLI_LOG(Core, Info, << "Creating WebAccountManagerAuthenticator for " << AuthenticationTypeToString(info.Type)); m_authProvider = std::make_unique(std::move(info), std::move(args)); } } #ifndef AICLI_DISABLE_TEST_HOOKS static AuthenticationResult* s_AuthenticationResult_TestHook_Override = nullptr; void TestHook_SetAuthenticationResult_Override(Authentication::AuthenticationResult* authResult) { s_AuthenticationResult_TestHook_Override = authResult; } #endif // Each authentication provider uses its own mechanism for caching. // Here we directly call authentication provider to authenticate. AuthenticationResult Authenticator::AuthenticateForToken() { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_AuthenticationResult_TestHook_Override) { return *s_AuthenticationResult_TestHook_Override; } #endif THROW_HR_IF(E_UNEXPECTED, !m_authProvider); return m_authProvider->AuthenticateForToken(); } bool MicrosoftEntraIdAuthenticationInfo::operator<(const MicrosoftEntraIdAuthenticationInfo& other) const { // std::tie implements tuple comparison, wherein it checks the first item in the tuple, // iff the first elements are equal, then the second element is used for comparison, and so on return std::tie(Resource, Scope) < std::tie(other.Resource, other.Scope); } bool AuthenticationInfo::operator<(const AuthenticationInfo& other) const { // std::tie implements tuple comparison, wherein it checks the first item in the tuple, // iff the first elements are equal, then the second element is used for comparison, and so on return std::tie(Type, MicrosoftEntraIdInfo) < std::tie(other.Type, other.MicrosoftEntraIdInfo); } void AuthenticationInfo::UpdateRequiredFieldsIfNecessary() { // If MicrosoftEntraIdForAzureBlobStorage, populate default resource value if missing. if (Type == AuthenticationType::MicrosoftEntraIdForAzureBlobStorage) { if (MicrosoftEntraIdInfo.has_value()) { if (MicrosoftEntraIdInfo->Resource.empty()) { MicrosoftEntraIdInfo->Resource = s_DefaultAzureBlobStorageResource; MicrosoftEntraIdInfo->Scope = ""; } } else { MicrosoftEntraIdAuthenticationInfo authInfo; authInfo.Resource = s_DefaultAzureBlobStorageResource; MicrosoftEntraIdInfo = std::move(authInfo); } } } bool AuthenticationInfo::ValidateIntegrity() const { // For MicrosoftEntraId, Resource is required. if (Type == AuthenticationType::MicrosoftEntraId || Type == AuthenticationType::MicrosoftEntraIdForAzureBlobStorage) { return MicrosoftEntraIdInfo.has_value() && !MicrosoftEntraIdInfo->Resource.empty(); } return true; } AuthenticationWindowBase::AuthenticationWindowBase() { InitializeWindowThread(); } HWND AuthenticationWindowBase::GetHandle() { return m_windowHandle; } AuthenticationWindowBase::~AuthenticationWindowBase() { if (!PostMessageW(m_windowHandle, WM_CLOSE, 0, 0)) { m_terminateWindowThread = true; } if (m_windowThread.joinable()) { m_windowThread.join(); } } void AuthenticationWindowBase::InitializeWindowThread() { static std::once_flag s_registerWindowClassOnce; static LPCWSTR s_windowsClassName = L"WingetAuthenticationParentWindowClass"; static HMODULE hModule = GetModuleHandle(NULL); THROW_LAST_ERROR_IF_NULL_MSG(hModule, "Failed to get resource module for authentication window"); std::call_once(s_registerWindowClassOnce, [&]() { WNDCLASS wc = {}; wc.lpfnWndProc = AuthenticationWindowBase::WindowProcessFunction; wc.hInstance = hModule; wc.lpszClassName = s_windowsClassName; THROW_LAST_ERROR_IF_MSG(!RegisterClassW(&wc), "Failed to get resource module for authentication window"); }); wil::unique_event waitForWindowReady; waitForWindowReady.create(); m_windowThread = std::thread( [&]() { m_windowHandle = CreateWindowW( s_windowsClassName, L"WingetAuthenticationParentWindow", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, /* size and position */ NULL, /* hWndParent */ NULL, /* hMenu */ hModule, NULL); /* lpParam */ THROW_LAST_ERROR_IF_NULL_MSG(hModule, "Failed to create authentication parent window"); // Best effort only SetForegroundWindow(m_windowHandle); m_windowThreadId = GetCurrentThreadId(); // Set window ready event waitForWindowReady.SetEvent(); // Message loop MSG msg; BOOL getMsgResult; while ((getMsgResult = GetMessage(&msg, NULL, 0, 0)) != 0) { if (m_terminateWindowThread || getMsgResult == -1) { return; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } }); THROW_HR_IF_MSG(APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED, !waitForWindowReady.wait(10000), "Creating authentication parent window timed out"); } LRESULT __stdcall AuthenticationWindowBase::WindowProcessFunction(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_ENDSESSION: case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } std::string_view AuthenticationTypeToString(AuthenticationType in) { switch (in) { case AuthenticationType::None: return "none"sv; case AuthenticationType::MicrosoftEntraId: return "microsoftEntraId"sv; case AuthenticationType::MicrosoftEntraIdForAzureBlobStorage: return "microsoftEntraIdForAzureBlobStorage"sv; } return "unknown"sv; } AuthenticationType ConvertToAuthenticationType(std::string_view in) { std::string inStrLower = Utility::ToLower(in); AuthenticationType result = AuthenticationType::Unknown; if (inStrLower == "none") { result = AuthenticationType::None; } else if (inStrLower == "microsoftentraid") { result = AuthenticationType::MicrosoftEntraId; } else if (inStrLower == "microsoftentraidforazureblobstorage") { result = AuthenticationType::MicrosoftEntraIdForAzureBlobStorage; } return result; } std::string_view AuthenticationModeToString(AuthenticationMode in) { switch (in) { case AuthenticationMode::Silent: return "silent"sv; case AuthenticationMode::SilentPreferred: return "silentPreferred"sv; case AuthenticationMode::Interactive: return "interactive"sv; } return "unknown"sv; } AuthenticationMode ConvertToAuthenticationMode(std::string_view in) { std::string inStrLower = Utility::ToLower(in); AuthenticationMode result = AuthenticationMode::Unknown; if (inStrLower == "silent") { result = AuthenticationMode::Silent; } else if (inStrLower == "silentpreferred") { result = AuthenticationMode::SilentPreferred; } else if (inStrLower == "interactive") { result = AuthenticationMode::Interactive; } return result; } std::string AppInstaller::Authentication::CreateBearerToken(std::string rawToken) { return std::string{ s_BearerTokenPrefix } + rawToken; } } ================================================ FILE: src/AppInstallerCommonCore/Authentication/WebAccountManagerAuthenticator.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include #include "WebAccountManagerAuthenticator.h" using namespace std::string_view_literals; using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Security::Authentication::Web::Core; using namespace winrt::Windows::Security::Credentials; namespace AppInstaller::Authentication { namespace { constexpr std::wstring_view s_MicrosoftEntraIdProviderId = L"https://login.microsoft.com"sv; constexpr std::wstring_view s_MicrosoftEntraIdAuthority = L"organizations"sv; constexpr std::wstring_view s_MicrosoftEntraIdClientId = L"7b8ea11a-7f45-4b3a-ab51-794d5863af15"sv; constexpr std::wstring_view s_MicrosoftEntraIdResourceHeader = L"resource"sv; constexpr std::wstring_view s_MicrosoftEntraIdLoginHintHeader = L"LoginHint"sv; } WebAccountManagerAuthenticator::WebAccountManagerAuthenticator(AuthenticationInfo info, AuthenticationArguments args) : m_authInfo(std::move(info)), m_authArgs(std::move(args)) { // WebAccountManager manages accounts as user. When running as system, it can only retrieve domain joined device token. // This is very rare scenario for rest source to require a device token. And it needs approval to provision winget client registration. THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), Runtime::IsRunningAsSystem()); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO, !m_authInfo.ValidateIntegrity()); THROW_HR_IF(E_UNEXPECTED, m_authArgs.Mode == AuthenticationMode::Unknown); if (IsMicrosoftEntraIdAuthenticationType()) { m_webAccountProvider = WebAuthenticationCoreManager::FindAccountProviderAsync(s_MicrosoftEntraIdProviderId, s_MicrosoftEntraIdAuthority).get(); THROW_HR_IF_MSG(E_UNEXPECTED, !m_webAccountProvider, "Authentication Provider not found for Microsoft Entra Id"); AICLI_LOG(Core, Info, << "WebAccountManagerAuthenticator created for MicrosoftEntraId. Resource: " << m_authInfo.MicrosoftEntraIdInfo->Resource << ", Scope: " << m_authInfo.MicrosoftEntraIdInfo->Scope); } else if (m_authInfo.Type == AuthenticationType::None) { THROW_HR_MSG(E_UNEXPECTED, "WebAccountManagerAuthenticator initialized with authentication type none"); } else { THROW_HR(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED); } } // WebAccountManager manages token and cache at OS level. // So for each authentication request, we call WebAccountManager api to retrieve token. // We do not need to implement own cache logic. AuthenticationResult WebAccountManagerAuthenticator::AuthenticateForToken() { std::lock_guard lock{ m_authLock }; AICLI_LOG(Core, Info, << "Started WebAccountManagerAuthenticator::AuthenticateForToken."); AuthenticationResult result; if (!m_authenticatedAccount) { // This is the first time invocation or previous authentication failed // Find the account to use if user provided account name and the account is signed in before. Best effort only. WebAccount webAccount = nullptr; if (!m_authArgs.AuthenticationAccount.empty()) { webAccount = FindWebAccount(m_authArgs.AuthenticationAccount); } if (m_authArgs.Mode == AuthenticationMode::Interactive) { result = GetToken(webAccount, true); } else if (m_authArgs.Mode == AuthenticationMode::SilentPreferred) { result = GetTokenSilent(webAccount); if (FAILED(result.Status)) { result = GetToken(webAccount); } } else if (m_authArgs.Mode == AuthenticationMode::Silent) { result = GetTokenSilent(webAccount); } } else { // Previous authentication successful. Just retrieve the token with the authenticated account. // In rare cases silent flow fails, use interactive flow. result = GetTokenSilent(m_authenticatedAccount); if (FAILED(result.Status) && m_authArgs.Mode != AuthenticationMode::Silent) { result = GetToken(m_authenticatedAccount); } } AICLI_LOG(Core, Info, << "Finished WebAccountManagerAuthenticator::AuthenticateForToken. Result: " << result.Status); return result; } WebAccount WebAccountManagerAuthenticator::FindWebAccount(std::string_view accountName) { AICLI_LOG(Core, Info, << "FindWebAccount called. Desired Account: " << accountName); WebAccount result = nullptr; if (IsMicrosoftEntraIdAuthenticationType()) { auto findAccountsResult = WebAuthenticationCoreManager::FindAllAccountsAsync(m_webAccountProvider, s_MicrosoftEntraIdClientId).get(); if (findAccountsResult.Status() == FindAllWebAccountsStatus::Success) { for (auto const& account : findAccountsResult.Accounts()) { if (Utility::CaseInsensitiveEquals(accountName, Utility::ConvertToUTF8(account.UserName()))) { result = account; break; } } } else { AICLI_LOG(Core, Warning, << "FindAllAccountsAsync failed. Status: " << findAccountsResult.Status()); auto providerError = findAccountsResult.ProviderError(); if (providerError) { AICLI_LOG(Core, Warning, << "FindAllAccountsAsync Provider Error. ErrorCode: " << providerError.ErrorCode() << ", Message: " << Utility::ConvertToUTF8(providerError.ErrorMessage())); } } } AICLI_LOG(Core, Info, << "FindWebAccount result: " << ((result != nullptr) ? "found" : "not found")); return result; } WebTokenRequest WebAccountManagerAuthenticator::CreateTokenRequest(bool forceInteractive) { WebTokenRequest request = nullptr; if (IsMicrosoftEntraIdAuthenticationType()) { request = WebTokenRequest { m_webAccountProvider, Utility::ConvertToUTF16(m_authInfo.MicrosoftEntraIdInfo->Scope), s_MicrosoftEntraIdClientId, forceInteractive ? WebTokenRequestPromptType::ForceAuthentication : WebTokenRequestPromptType::Default }; request.Properties().Insert(s_MicrosoftEntraIdResourceHeader, Utility::ConvertToUTF16(m_authInfo.MicrosoftEntraIdInfo->Resource)); if (!m_authArgs.AuthenticationAccount.empty()) { request.Properties().Insert(s_MicrosoftEntraIdLoginHintHeader, Utility::ConvertToUTF16(m_authArgs.AuthenticationAccount)); } } return request; } AuthenticationResult WebAccountManagerAuthenticator::GetToken(WebAccount webAccount, bool forceInteractive) { AICLI_LOG(Core, Info, << "Started GetToken. ForceInteractive: " << forceInteractive); auto request = CreateTokenRequest(forceInteractive); if (!request) { AICLI_LOG(Core, Error, << "CreateTokenRequest returned empty request"); return {}; } IAsyncOperation requestOperation; constexpr winrt::guid iidAsyncRequestResult{ winrt::guid_of>() }; auto authManagerFactory = winrt::get_activation_factory(); winrt::com_ptr authManagerInterop{ authManagerFactory.as() }; HRESULT requestOperationResult = APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED; AuthenticationWindowBase parentWindow; if (webAccount) { requestOperationResult = authManagerInterop->RequestTokenWithWebAccountForWindowAsync( parentWindow.GetHandle(), request.as<::IInspectable>().get(), webAccount.as<::IInspectable>().get(), iidAsyncRequestResult, reinterpret_cast(&requestOperation)); } else { requestOperationResult = authManagerInterop->RequestTokenForWindowAsync( parentWindow.GetHandle(), request.as<::IInspectable>().get(), iidAsyncRequestResult, reinterpret_cast(&requestOperation)); } if (FAILED(requestOperationResult)) { AICLI_LOG(Core, Error, << "RequestTokenForWindowAsync failed. Result: " << requestOperationResult); return {}; } return HandleGetTokenResult(requestOperation.get()); } AuthenticationResult WebAccountManagerAuthenticator::GetTokenSilent(WebAccount webAccount) { AICLI_LOG(Core, Info, << "Started GetTokenSilent."); auto request = CreateTokenRequest(false); if (!request) { AICLI_LOG(Core, Error, << "CreateTokenRequest returned empty request"); return {}; } if (webAccount) { return HandleGetTokenResult(WebAuthenticationCoreManager::GetTokenSilentlyAsync(request, webAccount).get()); } else { return HandleGetTokenResult(WebAuthenticationCoreManager::GetTokenSilentlyAsync(request).get()); } } AuthenticationResult WebAccountManagerAuthenticator::HandleGetTokenResult(WebTokenRequestResult requestResult) { AuthenticationResult result; if (!requestResult) { AICLI_LOG(Core, Error, << "WebTokenRequestResult is null"); return result; } if (requestResult.ResponseStatus() == WebTokenRequestStatus::Success) { auto responseData = requestResult.ResponseData().GetAt(0); auto authenticatedAccount = responseData.WebAccount(); // Check token's corresponding account matches user input if applicable. if (m_authArgs.AuthenticationAccount.empty() || Utility::CaseInsensitiveEquals(m_authArgs.AuthenticationAccount, Utility::ConvertToUTF8(authenticatedAccount.UserName()))) { result.Status = S_OK; result.Token = Utility::ConvertToUTF8(responseData.Token()); // Assign authenticated account for future token retrieval. m_authenticatedAccount = authenticatedAccount; AICLI_LOG(Core, Info, << "Authentication success"); } else { AICLI_LOG(Core, Error, << "Authentication success. But the authenticated account is not the desired one."); result.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_INCORRECT_ACCOUNT; } } else if (requestResult.ResponseStatus() == WebTokenRequestStatus::AccountSwitch) { AICLI_LOG(Core, Error, << "Authentication failed. The authenticated account is not the desired one."); result.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_INCORRECT_ACCOUNT; } else if (requestResult.ResponseStatus() == WebTokenRequestStatus::ProviderError || requestResult.ResponseStatus() == WebTokenRequestStatus::AccountProviderNotAvailable) { AICLI_LOG(Core, Error, << "Authentication failed. Provider failed."); auto responseError = requestResult.ResponseError(); if (responseError) { AICLI_LOG(Core, Error, << "Provider Error. Code: " << responseError.ErrorCode() << ", Message: " << Utility::ConvertToUTF8(responseError.ErrorMessage())); } result.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED; } else if (requestResult.ResponseStatus() == WebTokenRequestStatus::UserCancel) { AICLI_LOG(Core, Error, << "Authentication failed. User cancelled."); result.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_CANCELLED_BY_USER; } else if (requestResult.ResponseStatus() == WebTokenRequestStatus::UserInteractionRequired) { AICLI_LOG(Core, Error, << "Authentication failed. Interactive authentication required."); result.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_INTERACTIVE_REQUIRED; } AICLI_LOG(Core, Info, << "HandleGetTokenResult Result: " << result.Status); return result; } bool WebAccountManagerAuthenticator::IsMicrosoftEntraIdAuthenticationType() { return m_authInfo.Type == AuthenticationType::MicrosoftEntraId || m_authInfo.Type == AuthenticationType::MicrosoftEntraIdForAzureBlobStorage; } } ================================================ FILE: src/AppInstallerCommonCore/Authentication/WebAccountManagerAuthenticator.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Public/winget/Authentication.h" #include #include namespace AppInstaller::Authentication { struct WebAccountManagerAuthenticator : public IAuthenticationProvider { WebAccountManagerAuthenticator(AuthenticationInfo info, AuthenticationArguments args); AuthenticationResult AuthenticateForToken(); private: AuthenticationInfo m_authInfo; AuthenticationArguments m_authArgs; winrt::Windows::Security::Credentials::WebAccountProvider m_webAccountProvider = nullptr; winrt::Windows::Security::Credentials::WebAccount m_authenticatedAccount = nullptr; std::mutex m_authLock; winrt::Windows::Security::Credentials::WebAccount FindWebAccount(std::string_view accountName); winrt::Windows::Security::Authentication::Web::Core::WebTokenRequest CreateTokenRequest(bool forceInteractive); AuthenticationResult GetToken(winrt::Windows::Security::Credentials::WebAccount webAccount, bool forceInteractive = false); AuthenticationResult GetTokenSilent(winrt::Windows::Security::Credentials::WebAccount webAccount); AuthenticationResult HandleGetTokenResult(winrt::Windows::Security::Authentication::Web::Core::WebTokenRequestResult requestResult); bool IsMicrosoftEntraIdAuthenticationType(); }; } ================================================ FILE: src/AppInstallerCommonCore/DODownloader.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DODownloader.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerSHA256.h" #include "Public/AppInstallerStrings.h" #include "winget/UserSettings.h" // TODO: Get this from the Windows SDK when available #define DODownloadProperty_HttpRedirectionTarget static_cast(DODownloadProperty_NonVolatile + 1) #define DODownloadProperty_HttpResponseHeaders static_cast(DODownloadProperty_HttpRedirectionTarget + 1) #define DODownloadProperty_HttpServerIPAddress static_cast(DODownloadProperty_HttpResponseHeaders + 1) #define DODownloadProperty_HttpStatusCode static_cast(DODownloadProperty_HttpServerIPAddress + 1) namespace AppInstaller::Utility { namespace { std::optional ExtractContentType(const std::optional& headers) { if (!headers) { return std::nullopt; } static constexpr std::string_view s_ContentType = "content-type:"sv; auto headerLines = Utility::SplitIntoLines(headers.value()); for (const auto& header : headerLines) { std::string_view headerView = header; if (header.length() >= s_ContentType.length()) { std::string lowerFragment = ToLower(headerView.substr(0, s_ContentType.length())); if (s_ContentType == lowerFragment) { return Trim(header.substr(s_ContentType.length())); } } } return std::nullopt; } } namespace DeliveryOptimization { // Represents a download work item for Delivery Optimization. struct Download { Download(IDOManager* manager) { THROW_IF_FAILED(manager->CreateDownload(&m_download)); // Cloaking - sets the authentication information that will be used to make calls on the DO interface proxy. // This will make sure DO server impersonates the correct client identity. THROW_IF_FAILED(CoSetProxyBlanket( m_download.get(), RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_DEFAULT)); } ~Download() { DO_DOWNLOAD_STATUS downloadStatus; if (SUCCEEDED_LOG(m_download->GetStatus(&downloadStatus))) { if (downloadStatus.State == DODownloadState_Transferred) { // Calling IDODownload::Finalize() to inform DO that the DO job can be cleaned up. // Otherwise, the resources associated with the job can be kept for a number of days // until expiration set by DO. (void)LOG_IF_FAILED(m_download->Finalize()); } else if (downloadStatus.State != DODownloadState_Finalized) { // For any other state, abort the download since it's no longer in use. // This will allow DO to clean up the cache for the associated content ID. (void)LOG_IF_FAILED(m_download->Abort()); } } } void SetProperty(DODownloadProperty prop, const std::wstring& value) { wil::unique_variant var; var.bstrVal = ::SysAllocString(value.c_str()); THROW_IF_NULL_ALLOC(var.bstrVal); var.vt = VT_BSTR; THROW_IF_FAILED(m_download->SetProperty(prop, &var)); } void SetProperty(DODownloadProperty prop, std::string_view value) { SetProperty(prop, Utility::ConvertToUTF16(value)); } void SetProperty(DODownloadProperty prop, uint32_t value) { wil::unique_variant var; var.ulVal = value; var.vt = VT_UI4; THROW_IF_FAILED(m_download->SetProperty(prop, &var)); } void SetProperty(DODownloadProperty prop, bool value) { wil::unique_variant var; var.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE; var.vt = VT_BOOL; THROW_IF_FAILED(m_download->SetProperty(prop, &var)); } template void SetUnknownProperty(DODownloadProperty prop, T&& value) { wil::unique_variant var; var.punkVal = nullptr; var.vt = VT_UNKNOWN; if (value) { THROW_IF_FAILED(value->QueryInterface(IID_PPV_ARGS(&var.punkVal))); } THROW_IF_FAILED(m_download->SetProperty(prop, &var)); } template std::optional TryGetProperty(DODownloadProperty prop) { std::optional result; wil::unique_variant var; HRESULT hr = m_download->GetProperty(prop, &var); if (SUCCEEDED(hr)) { T value; if (ExtractFromVariant(var, value)) { result = std::move(value); } } return result; } void Uri(std::string_view uri) { SetProperty(DODownloadProperty_Uri, uri); } void ContentId(std::string_view contentId) { SetProperty(DODownloadProperty_ContentId, contentId); } void DisplayName(std::string_view displayName) { SetProperty(DODownloadProperty_DisplayName, displayName); } void LocalPath(const std::filesystem::path& localPath) { SetProperty(DODownloadProperty_LocalPath, localPath.wstring()); } void CorrelationVector(std::string_view correlationVector) { SetProperty(DODownloadProperty_CorrelationVector, correlationVector); } void NoProgressTimeoutSeconds(uint32_t noProgressTimeoutSeconds) { SetProperty(DODownloadProperty_NoProgressTimeoutSeconds, noProgressTimeoutSeconds); } void ForegroundPriority(bool foregroundPriority) { SetProperty(DODownloadProperty_ForegroundPriority, foregroundPriority); } void BlockingMode(bool blockingMode) { SetProperty(DODownloadProperty_BlockingMode, blockingMode); } void CallbackInterface(IDODownloadStatusCallback* callbackInterface) { SetUnknownProperty(DODownloadProperty_CallbackInterface, callbackInterface); } void StreamInterface(IStream* streamInterface) { SetUnknownProperty(DODownloadProperty_StreamInterface, streamInterface); } void CustomHeaders(const std::vector& headers) { // DODownloadProperty_HttpCustomAuthHeaders is not used (does not work in our auth scenario). It is only used when challenged. std::string customHeaders; for (const auto& header : headers) { customHeaders += header.Name + ": " + header.Value + "\r\n"; } if (!customHeaders.empty()) { SetProperty(DODownloadProperty_HttpCustomHeaders, customHeaders); } } // Properties that may be interesting for future use: // https://docs.microsoft.com/en-us/windows/win32/delivery_optimization/deliveryoptimizationdownloadtypes/ne-deliveryoptimizationdownloadtypes-dodownloadproperty // - DODownloadProperty_CostPolicy :: Allow user to specify how to behave on metered networks void Start() { DO_DOWNLOAD_RANGES_INFO emptyRanges{}; emptyRanges.RangeCount = 0; THROW_IF_FAILED(m_download->Start(&emptyRanges)); } // Returns true if Abort was successful; false if not. bool Cancel() { return SUCCEEDED_LOG(m_download->Abort()); } void Finalize() { THROW_IF_FAILED(m_download->Finalize()); } DO_DOWNLOAD_STATUS Status() { DO_DOWNLOAD_STATUS result{}; THROW_IF_FAILED(m_download->GetStatus(&result)); return result; } private: bool ExtractFromVariant(const VARIANT& var, std::string& value) { if (var.vt == VT_BSTR && var.bstrVal != nullptr) { value = Utility::ConvertToUTF8(var.bstrVal); return true; } else if (var.vt == (VT_BSTR | VT_BYREF) && var.pbstrVal != nullptr && *var.pbstrVal != nullptr) { value = Utility::ConvertToUTF8(*var.pbstrVal); return true; } return false; } wil::com_ptr m_download; }; // The top level Delivery Optimization manager object. struct Manager { Manager() { THROW_IF_FAILED(CoCreateInstance( __uuidof(::DeliveryOptimization), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&m_manager))); } Download CreateDownload() { return { m_manager.get() }; } private: wil::com_ptr m_manager; }; // Status callback handler class DODownloadStatusCallback : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags, IDODownloadStatusCallback> { public: DODownloadStatusCallback(IProgressCallback& progress) : m_progress(progress) { } IFACEMETHOD(OnStatusChange)(IDODownload*, const DO_DOWNLOAD_STATUS* status) { { std::lock_guard guard(m_statusMutex); m_currentStatus = *status; } m_statusCV.notify_all(); return S_OK; } static HRESULT Create( IProgressCallback& progress, DODownloadStatusCallback** result) { Microsoft::WRL::ComPtr localResult = Microsoft::WRL::Make(progress); RETURN_IF_NULL_ALLOC(localResult); *result = localResult.Detach(); return S_OK; } // Simply breaks the wait in Wait; the progress object must already be cancelled to force it out. void Cancel() { m_statusCV.notify_all(); } // Returns true on successful completion, false on cancellation, and throws on an error. bool Wait() { std::unique_lock lock(m_statusMutex); // If there is no transfer status update for m_doNoProgressTimeout, we will fail. auto timeoutTime = std::chrono::steady_clock::now() + Settings::User().Get(); std::optional initialTransferAmount; bool transferChange = false; while (!m_progress.IsCancelledBy(CancelReason::Any)) { if (!transferChange) { if (m_statusCV.wait_until(lock, timeoutTime) == std::cv_status::timeout) { THROW_HR(DO_E_DOWNLOAD_NO_PROGRESS); } } else { m_statusCV.wait(lock); } // Since we just finished a wait, check for cancellation before handling anything else if (m_progress.IsCancelledBy(CancelReason::Any)) { return false; } AICLI_LOG(Core, Verbose, << "DO State " << m_currentStatus.State << ", " << m_currentStatus.BytesTransferred << " / " << m_currentStatus.BytesTotal << ", Error 0x" << Logging::SetHRFormat << m_currentStatus.Error << ", extended error 0x" << Logging::SetHRFormat << m_currentStatus.ExtendedError); // No matter the state, we are considering any error set to be a failure if (FAILED(m_currentStatus.Error)) { AICLI_LOG(Core, Error, << "DeliveryOptimization error: 0x" << Logging::SetHRFormat << m_currentStatus.Error << ", extended error: 0x" << Logging::SetHRFormat << m_currentStatus.ExtendedError); THROW_HR(m_currentStatus.Error); } switch (m_currentStatus.State) { // These states are ignored. case DODownloadState_Created: case DODownloadState_Paused: break; case DODownloadState_Transferring: if (m_currentStatus.BytesTransferred || m_currentStatus.BytesTotal) { m_progress.OnProgress(m_currentStatus.BytesTransferred, m_currentStatus.BytesTotal, ProgressType::Bytes); } if (!initialTransferAmount) { initialTransferAmount = m_currentStatus.BytesTransferred; } else if (m_currentStatus.BytesTransferred != initialTransferAmount.value()) { transferChange = true; } break; // These are considered to be 'done' case DODownloadState_Transferred: case DODownloadState_Finalized: if (m_currentStatus.BytesTransferred || m_currentStatus.BytesTotal) { m_progress.OnProgress(m_currentStatus.BytesTransferred, m_currentStatus.BytesTotal, ProgressType::Bytes); } return true; // This is the cancelled state case DODownloadState_Aborted: return false; } } return false; } private: IProgressCallback& m_progress; std::mutex m_statusMutex; std::condition_variable m_statusCV; DO_DOWNLOAD_STATUS m_currentStatus = {}; }; } // Debugging tip: // From an elevated PowerShell, run: // > Get-DeliveryOptimizationLog | Set-Content doLogs.txt DownloadResult DODownload( const std::string& url, const std::filesystem::path& dest, IProgressCallback& progress, std::optional info) { AICLI_LOG(Core, Info, << "DeliveryOptimization downloading from url: " << url); // Remove the target file since DO will not overwrite std::filesystem::remove(dest); DeliveryOptimization::Manager manager; DeliveryOptimization::Download download = manager.CreateDownload(); wil::com_ptr callback; THROW_IF_FAILED(DeliveryOptimization::DODownloadStatusCallback::Create(progress, &callback)); download.Uri(url); download.ForegroundPriority(true); download.LocalPath(dest); download.CallbackInterface(callback.get()); if (info) { if (!info->DisplayName.empty()) { download.DisplayName(info->DisplayName); } if (!info->ContentId.empty()) { download.ContentId(info->ContentId); } if (!info->RequestHeaders.empty()) { download.CustomHeaders(info->RequestHeaders); } } download.Start(); auto cancelLifetime = progress.SetCancellationFunction([&download, &callback]() { AICLI_LOG(Core, Info, << "Download cancelled."); download.Cancel(); callback->Cancel(); }); // Check to handle cancellation between Start and SetCancellationFunction if (progress.IsCancelledBy(CancelReason::Any)) { AICLI_LOG(Core, Info, << "Download cancelled."); download.Cancel(); return {}; } // Wait returns true for success, false for cancellation, and throws on error. if (callback->Wait()) { // Grab the headers so that we can use them later std::optional responseHeaders = download.TryGetProperty(DODownloadProperty_HttpResponseHeaders); // Finalize is required to flush the data and change the file name. download.Finalize(); AICLI_LOG(Core, Info, << "Download completed."); std::ifstream inStream{ dest, std::ifstream::binary }; auto hashDetails = SHA256::ComputeHashDetails(inStream); DownloadResult result; result.Sha256Hash = std::move(hashDetails.Hash); result.SizeInBytes = hashDetails.SizeInBytes; result.ContentType = ExtractContentType(responseHeaders); return result; } return {}; } bool IsDOErrorFatal(HRESULT error) { // If this gets to be large, store in a sorted array and binary search on it. // There will be more to update here, which we should be able to discover through telemetry. return error == DO_E_BLOCKED_BY_COST_TRANSFER_POLICY || error == DO_E_BLOCKED_BY_CELLULAR_POLICY || error == DO_E_BLOCKED_BY_POWER_STATE || error == DO_E_BLOCKED_BY_NO_NETWORK; } } ================================================ FILE: src/AppInstallerCommonCore/DODownloader.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Utility { // Downloads a file from the given URL and places it in the given location. // url: The url to be downloaded from. http->https redirection is allowed. // dest: The stream to be downloaded to. // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. DownloadResult DODownload( const std::string& url, const std::filesystem::path& dest, IProgressCallback& progress, std::optional info); // Returns true if the error from DODownload should be treated as fatal; // false if we should be able to fall back to other download methods. bool IsDOErrorFatal(HRESULT error); } ================================================ FILE: src/AppInstallerCommonCore/Debugging.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/Debugging.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerDateTime.h" namespace AppInstaller::Debugging { namespace { constexpr std::string_view c_minidumpPrefix = "Minidump"; constexpr std::string_view c_minidumpExtension = ".mdmp"; struct SelfInitiatedMinidumpHelper { SelfInitiatedMinidumpHelper() = default; ~SelfInitiatedMinidumpHelper() { if (!m_keepFile) { m_file.reset(); DeleteFile(m_filePath.wstring().c_str()); } } static SelfInitiatedMinidumpHelper& Instance() { static SelfInitiatedMinidumpHelper instance; return instance; } static LONG WINAPI UnhandledExceptionCallback(EXCEPTION_POINTERS* ExceptionInfo) { MINIDUMP_EXCEPTION_INFORMATION exceptionInformation{}; // The unhandled exception filter is executed in the context of the failing thread. exceptionInformation.ThreadId = GetCurrentThreadId(); exceptionInformation.ExceptionPointers = ExceptionInfo; exceptionInformation.ClientPointers = FALSE; std::thread([&]() { MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), Instance().m_file.get(), MiniDumpNormal, &exceptionInformation, nullptr, nullptr); Instance().m_keepFile = true; }).join(); return EXCEPTION_CONTINUE_SEARCH; } SelfInitiatedMinidumpHelper& Enable(const std::filesystem::path& filePath = {}) { std::call_once(m_enableFlag, [&]() { if (filePath.empty()) { m_filePath = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation); m_filePath /= c_minidumpPrefix.data() + ('-' + Utility::GetCurrentTimeForFilename() + c_minidumpExtension.data()); } else { m_filePath = filePath; } m_file.reset(CreateFile(m_filePath.wstring().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)); THROW_LAST_ERROR_IF(!m_file); SetUnhandledExceptionFilter(UnhandledExceptionCallback); }); return *this; } void WriteMinidump() { std::thread([&]() { MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), Instance().m_file.get(), MiniDumpNormal, nullptr, nullptr, nullptr); Instance().m_keepFile = true; }).join(); } private: std::once_flag m_enableFlag; std::filesystem::path m_filePath; wil::unique_handle m_file; std::atomic_bool m_keepFile{ false }; }; } void EnableSelfInitiatedMinidump() { SelfInitiatedMinidumpHelper::Instance().Enable(); } void EnableSelfInitiatedMinidump(const std::filesystem::path& filePath) { SelfInitiatedMinidumpHelper::Instance().Enable(filePath); } void WriteMinidump() { SelfInitiatedMinidumpHelper::Instance().Enable().WriteMinidump(); } } ================================================ FILE: src/AppInstallerCommonCore/DependenciesGraph.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget\DependenciesGraph.h" namespace AppInstaller::Manifest { // this constructor was intented for use during installation flow (we already have installer dependencies and there's no need to search the source again) DependencyGraph::DependencyGraph(const Dependency& root, const DependencyList& rootDependencies, std::function infoFunction) : m_root(root), getDependencies(infoFunction) { m_adjacents[m_root] = std::set(); m_toCheck = std::vector(); rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { m_toCheck.push_back(dependency); AddNode(dependency); AddAdjacent(root, dependency); }); m_rootDependencyEvaluated = true; } DependencyGraph::DependencyGraph(const Dependency& root, std::function infoFunction) : m_root(root), getDependencies(infoFunction) { m_adjacents[m_root] = std::set(); m_toCheck = std::vector(); } void DependencyGraph::BuildGraph() { if (!m_rootDependencyEvaluated) { const DependencyList& rootDependencies = getDependencies(m_root); rootDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { m_toCheck.push_back(dependency); AddNode(dependency); AddAdjacent(m_root, dependency); }); m_rootDependencyEvaluated = true; } if (m_toCheck.empty()) { return; } for (unsigned int i = 0; i < m_toCheck.size(); ++i) { auto node = m_toCheck.at(i); const auto& nodeDependencies = getDependencies(node); nodeDependencies.ApplyToType(DependencyType::Package, [&](Dependency dependency) { if (!HasNode(dependency)) { m_toCheck.push_back(dependency); AddNode(dependency); } AddAdjacent(node, dependency); }); } CheckForLoopsAndGetOrder(); } void DependencyGraph::AddNode(const Dependency& node) { m_adjacents[node] = std::set(); } void DependencyGraph::AddAdjacent(const Dependency& node, const Dependency& adjacent) { m_adjacents[node].emplace(adjacent); } bool DependencyGraph::HasNode(const Dependency& dependency) { auto search = m_adjacents.find(dependency); return search != m_adjacents.end(); } bool DependencyGraph::HasLoop() { return m_HasLoop; } void DependencyGraph::CheckForLoopsAndGetOrder() { m_installationOrder = std::vector(); std::set visited; m_HasLoop = HasLoopDFS(visited, m_root); } std::vector DependencyGraph::GetInstallationOrder() { return m_installationOrder; } // TODO make this function iterative bool DependencyGraph::HasLoopDFS(std::set visited, const Dependency& node) { bool loop = false; visited.insert(node); auto lAdjacents = m_adjacents.at(node); for (const auto& adjacent : m_adjacents.at(node)) { auto search = visited.find(adjacent); if (search == visited.end()) // if not found { if (HasLoopDFS(visited, adjacent)) { loop = true; // didn't break the loop to have a complete order at the end (even if a loop exists) } } else { loop = true; // didn't break the loop to have a complete order at the end (even if a loop exists) } } // Adding to have an order even if a loop is present if (std::find(m_installationOrder.begin(), m_installationOrder.end(), node) == m_installationOrder.end()) { m_installationOrder.push_back(node); } return loop; } } ================================================ FILE: src/AppInstallerCommonCore/Deployment.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerDeployment.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerMsixInfo.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerStrings.h" namespace AppInstaller::Deployment { using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Management::Deployment; namespace { size_t GetDeploymentOperationId() { static std::atomic_size_t s_deploymentId = 0; return s_deploymentId.fetch_add(1); } HRESULT WaitForDeployment( IAsyncOperationWithProgress& deployOperation, size_t id, IProgressCallback& callback, bool throwOnError = true) { AICLI_LOG(Core, Info, << "Begin waiting for operation #" << id); AsyncOperationProgressHandler progressCallback( [&callback](const IAsyncOperationWithProgress&, DeploymentProgress progress) { callback.OnProgress(progress.percentage, 100, ProgressType::Percent); } ); // Set progress callback. deployOperation.Progress(progressCallback); auto removeCancel = callback.SetCancellationFunction([&]() { deployOperation.Cancel(); }); AICLI_LOG(Core, Info, << "Begin blocking for operation #" << id); auto deployResult = deployOperation.get(); if (!SUCCEEDED(deployResult.ExtendedErrorCode())) { AICLI_LOG(Core, Error, << "Deployment operation #" << id << ": " << Utility::ConvertToUTF8(deployResult.ErrorText())); // Note that while the format string is char*, it gets converted to wchar before being used. if (throwOnError) { THROW_HR_MSG(deployResult.ExtendedErrorCode(), "Operation failed: %ws", deployResult.ErrorText().c_str()); } else { // Simple return because this path is generally used for recovery cases return deployResult.ExtendedErrorCode(); } } else { AICLI_LOG(Core, Info, << "Successfully completed #" << id); } return S_OK; } bool ShouldUseReputationCheck(const Options& options) { return options.ExpectedDigests.empty() && !options.SkipReputationCheck; } IAsyncOperationWithProgress StartAddPackage(PackageManager& packageManager, const winrt::Windows::Foundation::Uri& uri, const Options& options) { if (!options.ExpectedDigests.empty()) { // Must use API that supports digests THROW_WIN32_IF(ERROR_NOT_SUPPORTED, !IsExpectedDigestsSupported()); AddPackageOptions addPackageOptions; for (const auto& digest : options.ExpectedDigests) { addPackageOptions.ExpectedDigests().Insert(Uri{ Utility::ConvertToUTF16(digest.first) }, digest.second); } return packageManager.AddPackageByUriAsync(uri, addPackageOptions); } else if (options.SkipReputationCheck) { return packageManager.AddPackageAsync( uri, nullptr, /*dependencyPackageUris*/ DeploymentOptions::None, nullptr, /*targetVolume*/ nullptr, /*optionalAndRelatedPackageFamilyNames*/ nullptr, /*optionalPackageUris*/ nullptr /*relatedPackageUris*/); } else { return packageManager.RequestAddPackageAsync( uri, nullptr, /*dependencyPackageUris*/ DeploymentOptions::None, nullptr, /*targetVolume*/ nullptr, /*optionalAndRelatedPackageFamilyNames*/ nullptr /*relatedPackageUris*/); } } IAsyncOperationWithProgress StartStagePackage(PackageManager& packageManager, const winrt::Windows::Foundation::Uri& uri, const Options& options) { if (!options.ExpectedDigests.empty()) { // Must use API that supports digests THROW_WIN32_IF(ERROR_NOT_SUPPORTED, !IsExpectedDigestsSupported()); StagePackageOptions stagePackageOptions; for (const auto& digest : options.ExpectedDigests) { stagePackageOptions.ExpectedDigests().Insert(Uri{ Utility::ConvertToUTF16(digest.first) }, digest.second); } return packageManager.StagePackageByUriAsync(uri, stagePackageOptions); } else { return packageManager.StagePackageAsync( uri, nullptr /*dependencyPackageUris*/); } } } std::ostream& operator<<(std::ostream& out, const Options& options) { out << " { SkipReputationCheck = " << options.SkipReputationCheck << ", ExpectedDigests = {"; for (const auto& digest : options.ExpectedDigests) { out << " { URI = " << digest.first << ", Digest = " << Utility::ConvertToUTF8(digest.second) << " } "; } out << "} }"; return out; } void AddPackage( const winrt::Windows::Foundation::Uri& uri, const Options& options, IProgressCallback& callback) { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting AddPackage operation #" << id << ": " << Utility::ConvertToUTF8(uri.AbsoluteUri().c_str()) << " Options: " << options); PackageManager packageManager; IAsyncOperationWithProgress deployOperation = StartAddPackage(packageManager, uri, options); WaitForDeployment(deployOperation, id, callback); } bool AddPackageWithDeferredFallback( std::string_view uri, const Options& options, IProgressCallback& callback) { PackageManager packageManager; // In the event of a failure we want to ensure that the package is not left on the system. // No need for proxy as Deployment won't use it anyways. Msix::MsixInfo packageInfo{ uri }; std::wstring packageFullNameWide = packageInfo.GetPackageFullNameWide(); std::string packageFullName = Utility::ConvertToUTF8(packageFullNameWide); auto removePackage = wil::scope_exit([&]() { try { ProgressCallback cb; RemovePackage(packageFullName, RemovalOptions::None, cb); } CATCH_LOG(); }); Uri uriObject(Utility::ConvertToUTF16(uri)); if (ShouldUseReputationCheck(options)) { // The only way to get SmartScreen is to use RequestAddPackageAsync, so we will have to start with that. size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting RequestAddPackageAsync operation #" << id << ": " << uri); DeploymentOptions deploymentOptions = DeploymentOptions::None; // Optimization to keep files if the package is in use. Only available in a newer OS per: // https://docs.microsoft.com/en-us/uwp/api/Windows.Management.Deployment.DeploymentOptions if (Runtime::IsCurrentOSVersionGreaterThanOrEqual(Utility::Version{ "10.0.18362.0" })) { deploymentOptions = DeploymentOptions::RetainFilesOnFailure; } IAsyncOperationWithProgress deployOperation = packageManager.RequestAddPackageAsync( uriObject, nullptr, /*dependencyPackageUris*/ deploymentOptions, nullptr, /*targetVolume*/ nullptr, /*optionalAndRelatedPackageFamilyNames*/ nullptr /*relatedPackageUris*/); HRESULT hr = WaitForDeployment(deployOperation, id, callback, false); if (SUCCEEDED(hr)) { removePackage.release(); return false; } THROW_HR_IF(hr, FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_PACKAGES_IN_USE)); } // If we are skipping SmartScreen or the package was in use, stage then register the package. PartialPercentProgressCallback progress{ callback, 100 }; progress.SetRange(0, 95); { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting StagePackageAsync operation #" << id << ": " << uri << " Options: " << options); IAsyncOperationWithProgress stageOperation = StartStagePackage(packageManager, uriObject, options); WaitForDeployment(stageOperation, id, progress); } bool registrationDeferred = false; progress.SetRange(95, 100); { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting RegisterPackageByFullNameAsync operation #" << id << ": " << packageFullName); IAsyncOperationWithProgress registerOperation = packageManager.RegisterPackageByFullNameAsync(packageFullNameWide, nullptr, DeploymentOptions::None); HRESULT hr = WaitForDeployment(registerOperation, id, progress, false); if (hr == HRESULT_FROM_WIN32(ERROR_PACKAGES_IN_USE)) { registrationDeferred = true; } else { THROW_IF_FAILED(hr); } } removePackage.release(); return registrationDeferred; } void RemovePackage( std::string_view packageFullName, RemovalOptions options, IProgressCallback& callback) { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting RemovePackage operation #" << id << ": " << packageFullName); PackageManager packageManager; winrt::hstring fullName = Utility::ConvertToUTF16(packageFullName).c_str(); auto deployOperation = packageManager.RemovePackageAsync(fullName, options); WaitForDeployment(deployOperation, id, callback); } bool AddPackageMachineScope( std::string_view uri, const Options& options, IProgressCallback& callback) { PackageManager packageManager; // In the event of a failure we want to ensure that the package is not left on the system. // No need for proxy as Deployment won't use it anyways. Msix::MsixInfo packageInfo{ uri }; std::wstring packageFullNameWide = packageInfo.GetPackageFullNameWide(); std::string packageFullName = Utility::ConvertToUTF8(packageFullNameWide); std::string packageFamilyName = Msix::GetPackageFamilyNameFromFullName(packageFullName); auto removePackage = wil::scope_exit([&]() { try { ProgressCallback cb; RemovePackage(packageFullName, RemovalOptions::RemoveForAllUsers, cb); } CATCH_LOG(); }); Uri uriObject(Utility::ConvertToUTF16(uri)); PartialPercentProgressCallback progress{ callback, 100 }; // First stage package contents progress.SetRange(0, 90); { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting StagePackageAsync operation #" << id << ": " << uri << " Options: " << options); IAsyncOperationWithProgress stageOperation = StartStagePackage(packageManager, uriObject, options); WaitForDeployment(stageOperation, id, progress); } // Provision for all users progress.SetRange(90, 95); { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting ProvisionPackage operation #" << id << ": " << packageFamilyName); winrt::hstring familyName = Utility::ConvertToUTF16(packageFamilyName).c_str(); auto deployOperation = packageManager.ProvisionPackageForAllUsersAsync(familyName); WaitForDeployment(deployOperation, id, progress); } // Try registration as best effort, operation is considered successful as long as provisioning is successful. progress.SetRange(95, 100); bool registrationDeferred = false; if (Runtime::IsRunningAsSystem()) { // Packages cannot be registered under local system, just return registration deferred registrationDeferred = true; } else { try { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting RegisterPackageByFullNameAsync operation #" << id << ": " << packageFullName); IAsyncOperationWithProgress registerOperation = packageManager.RegisterPackageByFullNameAsync(packageFullNameWide, nullptr, DeploymentOptions::None); WaitForDeployment(registerOperation, id, progress); } catch (...) { registrationDeferred = true; } } progress.OnProgress(100, 100, ProgressType::Percent); removePackage.release(); return registrationDeferred; } void RemovePackageMachineScope( std::string_view packageFamilyName, std::string_view packageFullName, IProgressCallback& callback) { PartialPercentProgressCallback progress{ callback, 100 }; // Deprovision first progress.SetRange(0, 5); { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting DeprovisionPackage operation #" << id << ": " << packageFamilyName); PackageManager packageManager; winrt::hstring familyName = Utility::ConvertToUTF16(packageFamilyName).c_str(); auto deployOperation = packageManager.DeprovisionPackageForAllUsersAsync(familyName); WaitForDeployment(deployOperation, id, progress); } // Remove for all users progress.SetRange(5, 100); { RemovePackage(packageFullName, RemovalOptions::RemoveForAllUsers, progress); } } bool IsRegistered(std::string_view packageFamilyName) { std::wstring wideFamilyName = Utility::ConvertToUTF16(packageFamilyName); PackageManager packageManager; auto packages = packageManager.FindPackagesForUser({}, wideFamilyName); return packages.begin() != packages.end(); } void RegisterPackage( std::string_view packageFamilyName, IProgressCallback& callback) { size_t id = GetDeploymentOperationId(); AICLI_LOG(Core, Info, << "Starting RegisterPackageByFullNameAsync operation #" << id << ": " << packageFamilyName); PackageManager packageManager; winrt::hstring packageFamilyNameWide = Utility::ConvertToUTF16(packageFamilyName).c_str(); auto deployOperation = packageManager.RegisterPackageByFamilyNameAsync(packageFamilyNameWide, nullptr, DeploymentOptions::None, nullptr, nullptr); WaitForDeployment(deployOperation, id, callback); } bool IsExpectedDigestsSupported() { static bool s_IsExpectedDigestsSupported = Metadata::ApiInformation::IsPropertyPresent(winrt::name_of(), L"ExpectedDigests"); return s_IsExpectedDigestsSupported; } } ================================================ FILE: src/AppInstallerCommonCore/Downloader.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerDownloader.h" #include "Public/AppInstallerSHA256.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerTelemetry.h" #include "Public/winget/UserSettings.h" #include "Public/winget/NetworkSettings.h" #include "Public/winget/Filesystem.h" #include "DODownloader.h" #include "HttpStream/HttpRandomAccessStream.h" using namespace AppInstaller::Runtime; using namespace AppInstaller::Settings; using namespace AppInstaller::Filesystem; using namespace AppInstaller::Utility::HttpStream; using namespace winrt::Windows::Web::Http; using namespace winrt::Windows::Web::Http::Headers; using namespace winrt::Windows::Web::Http::Filters; namespace AppInstaller::Utility { namespace { std::wstring GetHttpQueryString(const wil::unique_hinternet& urlFile, DWORD queryProperty) { std::wstring result = {}; DWORD length = 0; if (!HttpQueryInfoW(urlFile.get(), queryProperty, &result[0], &length, nullptr)) { auto lastError = GetLastError(); if (lastError == ERROR_INSUFFICIENT_BUFFER) { // lpdwBufferLength contains the size, in bytes, of a buffer large enough to receive the requested information // without the nul char. not the exact buffer size. auto size = static_cast(length) / sizeof(wchar_t); result.resize(size + 1); if (HttpQueryInfoW(urlFile.get(), queryProperty, &result[0], &length, nullptr)) { // because the buffer can be bigger remove possible null chars result.erase(result.find(L'\0')); } else { AICLI_LOG(Core, Error, << "Error retrieving header value [" << queryProperty << "]: " << GetLastError()); result.clear(); } } else { AICLI_LOG(Core, Error, << "Error retrieving header [" << queryProperty << "]: " << GetLastError()); } } return result; } // Gets the retry after value in terms of a delay in seconds std::chrono::seconds GetRetryAfter(const HttpDateOrDeltaHeaderValue& retryAfter) { if (retryAfter) { auto delta = retryAfter.Delta(); if (delta) { return std::chrono::duration_cast(delta.GetTimeSpan()); } auto dateTimeRef = retryAfter.Date(); if (dateTimeRef) { auto dateTime = dateTimeRef.GetDateTime(); auto now = winrt::clock::now(); if (dateTime > now) { return std::chrono::duration_cast(dateTime - now); } } } return 0s; } std::chrono::seconds GetRetryAfter(const wil::unique_hinternet& urlFile) { std::wstring retryAfter = GetHttpQueryString(urlFile, HTTP_QUERY_RETRY_AFTER); return retryAfter.empty() ? 0s : AppInstaller::Utility::GetRetryAfter(retryAfter); } } #ifndef AICLI_DISABLE_TEST_HOOKS namespace TestHooks { static std::function info)>* s_Download_Function_Override = nullptr; void SetDownloadResult_Function_Override(std::function info)>* value) { s_Download_Function_Override = value; } } #endif DownloadResult WinINetDownloadToStream( const std::string& url, std::ostream& dest, IProgressCallback& progress, std::optional info) { // For AICLI_LOG usages with string literals. #pragma warning(push) #pragma warning(disable:26449) AICLI_LOG(Core, Info, << "WinINet downloading from url: " << url); auto agentWide = Utility::ConvertToUTF16(Runtime::GetDefaultUserAgent().get()); wil::unique_hinternet session; const auto& proxyUri = Network().GetProxyUri(); if (proxyUri) { AICLI_LOG(Core, Info, << "Using proxy " << proxyUri.value()); session.reset(InternetOpen( agentWide.c_str(), INTERNET_OPEN_TYPE_PROXY, Utility::ConvertToUTF16(proxyUri.value()).c_str(), NULL, 0)); } else { session.reset(InternetOpen( agentWide.c_str(), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0)); } THROW_LAST_ERROR_IF_NULL_MSG(session, "InternetOpen() failed."); std::string customHeaders; if (info && info->RequestHeaders.size() > 0) { for (const auto& header : info->RequestHeaders) { customHeaders += header.Name + ": " + header.Value + "\r\n"; } } std::wstring customHeadersWide = Utility::ConvertToUTF16(customHeaders); auto urlWide = Utility::ConvertToUTF16(url); wil::unique_hinternet urlFile(InternetOpenUrl( session.get(), urlWide.c_str(), customHeadersWide.empty() ? NULL : customHeadersWide.c_str(), customHeadersWide.empty() ? 0 : (DWORD)(customHeadersWide.size()), INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS, // This allows http->https redirection 0)); THROW_LAST_ERROR_IF_NULL_MSG(urlFile, "InternetOpenUrl() failed."); // Check http return status DWORD requestStatus = 0; DWORD cbRequestStatus = sizeof(requestStatus); THROW_LAST_ERROR_IF_MSG(!HttpQueryInfoW(urlFile.get(), HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &requestStatus, &cbRequestStatus, nullptr), "Query download request status failed."); constexpr DWORD TooManyRequest = 429; switch (requestStatus) { case HTTP_STATUS_OK: // All good break; case TooManyRequest: case HTTP_STATUS_SERVICE_UNAVAIL: { THROW_EXCEPTION(ServiceUnavailableException(GetRetryAfter(urlFile))); } default: AICLI_LOG(Core, Error, << "Download request failed. Returned status: " << requestStatus); THROW_HR_MSG(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, requestStatus), "Download request status is not success."); } AICLI_LOG(Core, Verbose, << "Download request status success."); // Get content length. Don't fail the download if failed. LONGLONG contentLength = 0; DWORD cbContentLength = sizeof(contentLength); HttpQueryInfoW( urlFile.get(), HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER64, &contentLength, &cbContentLength, nullptr); AICLI_LOG(Core, Verbose, << "Download size: " << contentLength); std::string contentType = Utility::ConvertToUTF8(GetHttpQueryString(urlFile, HTTP_QUERY_CONTENT_TYPE)); AICLI_LOG(Core, Verbose, << "Content Type: " << contentType); // Setup hash engine SHA256 hashEngine; const int bufferSize = 1024 * 1024; // 1MB auto buffer = std::make_unique(bufferSize); BOOL readSuccess = true; DWORD bytesRead = 0; LONGLONG bytesDownloaded = 0; do { if (progress.IsCancelledBy(CancelReason::Any)) { AICLI_LOG(Core, Info, << "Download cancelled."); return {}; } readSuccess = InternetReadFile(urlFile.get(), buffer.get(), bufferSize, &bytesRead); THROW_LAST_ERROR_IF_MSG(!readSuccess, "InternetReadFile() failed."); hashEngine.Add(buffer.get(), bytesRead); dest.write((char*)buffer.get(), bytesRead); bytesDownloaded += bytesRead; if (bytesRead != 0) { progress.OnProgress(bytesDownloaded, contentLength, ProgressType::Bytes); } } while (bytesRead != 0); dest.flush(); // Check download size matches if content length is provided in response header if (contentLength > 0) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH, bytesDownloaded != contentLength); } DownloadResult result; result.SizeInBytes = static_cast(bytesDownloaded); result.ContentType = std::move(contentType); result.Sha256Hash = hashEngine.Get(); AICLI_LOG(Core, Info, << "Download hash: " << SHA256::ConvertToString(result.Sha256Hash)); AICLI_LOG(Core, Info, << "Download completed."); #pragma warning(pop) return result; } std::map GetHeaders(std::string_view url) { // TODO: Use proxy info. HttpClient does not support using a custom proxy, only using the system-wide one. AICLI_LOG(Core, Verbose, << "Retrieving headers from url: " << url); HttpBaseProtocolFilter filter; filter.CacheControl().ReadBehavior(HttpCacheReadBehavior::MostRecent); HttpClient client(filter); client.DefaultRequestHeaders().Connection().Clear(); client.DefaultRequestHeaders().Append(L"Connection", L"close"); client.DefaultRequestHeaders().UserAgent().ParseAdd(Utility::ConvertToUTF16(Runtime::GetDefaultUserAgent().get())); winrt::Windows::Foundation::Uri uri{ Utility::ConvertToUTF16(url) }; HttpRequestMessage request(HttpMethod::Head(), uri); HttpResponseMessage response = client.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead).get(); switch (response.StatusCode()) { case HttpStatusCode::Ok: // All good break; case HttpStatusCode::TooManyRequests: case HttpStatusCode::ServiceUnavailable: { THROW_EXCEPTION(ServiceUnavailableException(GetRetryAfter(response.Headers().RetryAfter()))); } default: THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.StatusCode())); } std::map result; for (const auto& header : response.Headers()) { result.emplace(Utility::FoldCase(static_cast(Utility::ConvertToUTF8(header.Key()))), Utility::ConvertToUTF8(header.Value())); } return result; } DownloadResult DownloadToStream( const std::string& url, std::ostream& dest, DownloadType, IProgressCallback& progress, std::optional info) { THROW_HR_IF(E_INVALIDARG, url.empty()); return WinINetDownloadToStream(url, dest, progress, info); } DownloadResult Download( const std::string& url, const std::filesystem::path& dest, DownloadType type, IProgressCallback& progress, std::optional info) { #ifndef AICLI_DISABLE_TEST_HOOKS if (TestHooks::s_Download_Function_Override) { return (*TestHooks::s_Download_Function_Override)(url, dest, type, progress, info); } #endif THROW_HR_IF(E_INVALIDARG, url.empty()); THROW_HR_IF(E_INVALIDARG, dest.empty()); AICLI_LOG(Core, Info, << "Downloading to path: " << dest); std::filesystem::create_directories(dest.parent_path()); // Only Installers should be downloaded with DO currently, as: // - Index :: Constantly changing blob at same location is not what DO is for // - Manifest / InstallerMetadataCollectionInput :: DO overhead is not needed for small files // - WinGetUtil :: Intentionally not using DO at this time if (type == DownloadType::Installer) { if (Network().GetInstallerDownloader() == InstallerDownloader::DeliveryOptimization) { try { auto result = DODownload(url, dest, progress, info); // Since we cannot pre-apply to the file with DO, post-apply the MotW to the file. // Only do so if the file exists, because cancellation will not throw here. if (std::filesystem::exists(dest)) { ApplyMotwIfApplicable(dest, URLZONE_INTERNET); } return result; } catch (const wil::ResultException& re) { // Fall back to WinINet below unless the specific error is not one that should be ignored. // We need to be careful not to bypass metered networks or other reasons that might // intentionally cause the download to be blocked. HRESULT hr = re.GetErrorCode(); if (IsDOErrorFatal(hr)) { throw; } else { // Send telemetry so that we can understand the reasons for DO failing Logging::Telemetry().LogNonFatalDOError(url, hr); } } // If we reach this point, we are intending to fall through to WinINet. // Remove any file that may have been placed in the target location. if (std::filesystem::exists(dest)) { std::filesystem::remove(dest); } } } std::ofstream emptyDestFile(dest); emptyDestFile.close(); ApplyMotwIfApplicable(dest, URLZONE_INTERNET); // Use std::ofstream::app to append to previous empty file so that it will not // create a new file and clear motw. std::ofstream outfile(dest, std::ofstream::binary | std::ofstream::app); return WinINetDownloadToStream(url, outfile, progress, info); } using namespace std::string_view_literals; constexpr std::string_view s_http_start = "http://"sv; constexpr std::string_view s_https_start = "https://"sv; bool IsUrlRemote(std::string_view url) { // Very simple choice right now: "does it start with http:// or https://"? if (CaseInsensitiveStartsWith(url, s_http_start) || CaseInsensitiveStartsWith(url, s_https_start)) { return true; } return false; } bool IsUrlSecure(std::string_view url) { // Very simple choice right now: "does it start with https://"? if (CaseInsensitiveStartsWith(url, s_https_start)) { return true; } return false; } static inline bool FileSupportsMotw(const std::filesystem::path& path) { return SupportsNamedStreams(path); } void ApplyMotwIfApplicable(const std::filesystem::path& filePath, URLZONE zone) { AICLI_LOG(Core, Info, << "Started applying motw to " << filePath << " with zone: " << zone); if (!FileSupportsMotw(filePath)) { AICLI_LOG(Core, Info, << "File system does not support ADS. Skipped applying motw"); return; } Microsoft::WRL::ComPtr zoneIdentifier; THROW_IF_FAILED(CoCreateInstance(CLSID_PersistentZoneIdentifier, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&zoneIdentifier))); THROW_IF_FAILED(zoneIdentifier->SetId(zone)); Microsoft::WRL::ComPtr persistFile; THROW_IF_FAILED(zoneIdentifier.As(&persistFile)); THROW_IF_FAILED(persistFile->Save(filePath.c_str(), TRUE)); AICLI_LOG(Core, Info, << "Finished applying motw"); } void RemoveMotwIfApplicable(const std::filesystem::path& filePath) { AICLI_LOG(Core, Info, << "Started removing motw to " << filePath); if (!FileSupportsMotw(filePath)) { AICLI_LOG(Core, Info, << "File system does not support ADS. Skipped removing motw"); return; } Microsoft::WRL::ComPtr zoneIdentifier; THROW_IF_FAILED(CoCreateInstance(CLSID_PersistentZoneIdentifier, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&zoneIdentifier))); Microsoft::WRL::ComPtr persistFile; THROW_IF_FAILED(zoneIdentifier.As(&persistFile)); auto hr = persistFile->Load(filePath.c_str(), STGM_READ); if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { // IPersistFile::Load returns same error for "file not found" and "motw not found". // Check if the file exists to be sure we are on the "motw not found" case. THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), !std::filesystem::exists(filePath)); AICLI_LOG(Core, Info, << "File does not contain motw. Skipped removing motw"); return; } THROW_IF_FAILED(zoneIdentifier->Remove()); THROW_IF_FAILED(persistFile->Save(NULL, TRUE)); AICLI_LOG(Core, Info, << "Finished removing motw"); } HRESULT ApplyMotwUsingIAttachmentExecuteIfApplicable(const std::filesystem::path& filePath, const std::string& source, URLZONE zoneIfScanFailure) { AICLI_LOG(Core, Info, << "Started applying motw using IAttachmentExecute to " << filePath); if (!FileSupportsMotw(filePath)) { AICLI_LOG(Core, Info, << "File system does not support ADS. Skipped applying motw"); return S_OK; } // Attachment execution service needs STA to succeed, so we'll create a new thread and CoInitialize with STA. HRESULT aesSaveResult = S_OK; auto updateMotw = [&]() -> HRESULT { Microsoft::WRL::ComPtr attachmentExecute; RETURN_IF_FAILED(CoCreateInstance(CLSID_AttachmentServices, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&attachmentExecute))); RETURN_IF_FAILED(attachmentExecute->SetLocalPath(filePath.c_str())); RETURN_IF_FAILED(attachmentExecute->SetSource(Utility::ConvertToUTF16(source).c_str())); // IAttachmentExecute::Save() expects the local file to be clean(i.e. it won't clear existing motw if it thinks the source url is trusted) RemoveMotwIfApplicable(filePath); aesSaveResult = attachmentExecute->Save(); // Reapply desired zone upon scan failure. // Not using SUCCEEDED(hr) to check since there are cases file is missing after a successful scan if (aesSaveResult != S_OK && std::filesystem::exists(filePath)) { ApplyMotwIfApplicable(filePath, zoneIfScanFailure); } RETURN_IF_FAILED(aesSaveResult); return S_OK; }; HRESULT hr = S_OK; std::thread aesThread([&]() { hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { return; } hr = updateMotw(); CoUninitialize(); }); aesThread.join(); AICLI_LOG(Core, Info, << "Finished applying motw using IAttachmentExecute. Result: " << hr << " IAttachmentExecute::Save() result: " << aesSaveResult); return aesSaveResult; } Microsoft::WRL::ComPtr GetReadOnlyStreamFromURI(std::string_view uriStr) { Microsoft::WRL::ComPtr inputStream; if (Utility::IsUrlRemote(uriStr)) { // Get an IStream from the input uri and try to create package or bundler reader. winrt::Windows::Foundation::Uri uri(Utility::ConvertToUTF16(uriStr)); winrt::com_ptr httpRandomAccessStream = winrt::make_self(); try { auto randomAccessStream = httpRandomAccessStream->InitializeAsync(uri).get(); ::IUnknown* rasAsIUnknown = (::IUnknown*)winrt::get_abi(randomAccessStream); THROW_IF_FAILED(CreateStreamOverRandomAccessStream( rasAsIUnknown, IID_PPV_ARGS(inputStream.ReleaseAndGetAddressOf()))); } catch (const winrt::hresult_error& hre) { if (hre.code() == APPINSTALLER_CLI_ERROR_SERVICE_UNAVAILABLE) { THROW_EXCEPTION(AppInstaller::Utility::ServiceUnavailableException(httpRandomAccessStream->RetryAfter())); } throw; } } else { std::filesystem::path path(Utility::ConvertToUTF16(uriStr)); THROW_IF_FAILED(SHCreateStreamOnFileEx(path.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE | STGM_FAILIFTHERE, 0, FALSE, nullptr, &inputStream)); } return inputStream; } std::chrono::seconds GetRetryAfter(const std::wstring& retryAfter) { try { winrt::hstring hstringValue{ retryAfter }; HttpDateOrDeltaHeaderValue headerValue = nullptr; HttpDateOrDeltaHeaderValue::TryParse(hstringValue, headerValue); return GetRetryAfter(headerValue); } catch (...) { AICLI_LOG(Core, Error, << "Retry-After value not supported: " << Utility::ConvertToUTF8(retryAfter)); } return 0s; } std::chrono::seconds GetRetryAfter(const HttpResponseMessage& response) { return GetRetryAfter(response.Headers().RetryAfter()); } CacheControlPolicy::CacheControlPolicy(std::wstring_view header) { static constexpr std::wstring_view s_MaxAge = L"max-age"sv; if (header.empty()) { return; } std::vector directives = Utility::Split(header, L',', true); for (std::wstring_view directive : directives) { if (!directive.empty()) { // Even if we don't understand the directive, the value was not empty Present = true; } std::wstring lowerDirective = ToLower(directive); if (lowerDirective == L"public"sv) { Public = true; } else if (lowerDirective == L"no-cache"sv) { NoCache = true; } else if (lowerDirective == L"no-store"sv) { NoStore = true; } else if (StartsWith(lowerDirective, s_MaxAge)) { std::vector parts = Utility::SplitView(lowerDirective, L'=', true); if (parts.size() == 2) { try { MaxAge = std::min(std::stoull(std::wstring{ parts[1] }), MaximumMaxAge); } CATCH_LOG(); } } } } } ================================================ FILE: src/AppInstallerCommonCore/ExperimentalFeature.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerLogging.h" #include "winget/ExperimentalFeature.h" #include "winget/GroupPolicy.h" #include "winget/UserSettings.h" namespace AppInstaller::Settings { #ifndef AICLI_DISABLE_TEST_HOOKS const std::map* s_ExperimentalFeature_Override = nullptr; void SetExperimentalFeatureOverride(const std::map* override) { s_ExperimentalFeature_Override = override; } #endif namespace { bool IsEnabledInternal(ExperimentalFeature::Feature feature, const UserSettings& userSettings) { if (feature == ExperimentalFeature::Feature::None) { return true; } #ifdef WINGET_DISABLE_EXPERIMENTAL_FEATURES UNREFERENCED_PARAMETER(userSettings); return false; #else #ifndef AICLI_DISABLE_TEST_HOOKS if (s_ExperimentalFeature_Override) { auto itr = s_ExperimentalFeature_Override->find(feature); if (itr != s_ExperimentalFeature_Override->end()) { return itr->second; } } #endif if (!GroupPolicies().IsEnabled(TogglePolicy::Policy::ExperimentalFeatures)) { AICLI_LOG(Core, Info, << "Experimental feature '" << ExperimentalFeature::GetFeature(feature).Name() << "' is disabled due to group policy: " << TogglePolicy::GetPolicy(TogglePolicy::Policy::ExperimentalFeatures).RegValueName()); return false; } switch (feature) { case ExperimentalFeature::Feature::ExperimentalCmd: // ExperimentalArg depends on ExperimentalCmd, so instead of failing we could // assume that if ExperimentalArg is enabled then ExperimentalCmd is as well. return userSettings.Get() || userSettings.Get(); case ExperimentalFeature::Feature::ExperimentalArg: return userSettings.Get(); case ExperimentalFeature::Feature::DirectMSI: return userSettings.Get(); case ExperimentalFeature::Feature::Resume: return userSettings.Get(); case ExperimentalFeature::Feature::Font: return userSettings.Get(); case ExperimentalFeature::Feature::SourcePriority: return userSettings.Get(); default: THROW_HR(E_UNEXPECTED); } #endif } } bool ExperimentalFeature::IsEnabled(Feature feature) { return IsEnabledInternal(feature, User()); } #ifndef AICLI_DISABLE_TEST_HOOKS bool ExperimentalFeature::IsEnabled(Feature feature, const UserSettings& userSettings) { return IsEnabledInternal(feature, userSettings); } #endif ExperimentalFeature ExperimentalFeature::GetFeature(ExperimentalFeature::Feature feature) { switch (feature) { case Feature::ExperimentalCmd: return ExperimentalFeature{ "Command Sample", "experimentalCmd", "https://aka.ms/winget-settings", Feature::ExperimentalCmd }; case Feature::ExperimentalArg: return ExperimentalFeature{ "Argument Sample", "experimentalArg", "https://aka.ms/winget-settings", Feature::ExperimentalArg }; case Feature::DirectMSI: return ExperimentalFeature{ "Direct MSI Installation", "directMSI", "https://aka.ms/winget-settings", Feature::DirectMSI }; case Feature::Resume: return ExperimentalFeature{ "Resume", "resume", "https://aka.ms/winget-settings", Feature::Resume }; case Feature::Font: return ExperimentalFeature{ "Font", "fonts", "https://aka.ms/winget-settings", Feature::Font }; case Feature::SourcePriority: return ExperimentalFeature{ "Source Priority", "sourcePriority", "https://aka.ms/winget-settings", Feature::SourcePriority }; default: THROW_HR(E_UNEXPECTED); } } std::vector ExperimentalFeature::GetAllFeatures() { std::vector result; for (Feature_t i = 0x1; i < static_cast(Feature::Max); i = i << 1) { result.emplace_back(GetFeature(static_cast(i))); } return result; } } ================================================ FILE: src/AppInstallerCommonCore/ExtensionCatalog.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/ExtensionCatalog.h" #include "AppInstallerErrors.h" #include "AppInstallerLogging.h" #include "AppInstallerStrings.h" namespace AppInstaller::Deployment { namespace AppExt = winrt::Windows::ApplicationModel::AppExtensions; Extension::Extension(AppExt::AppExtension extension) : m_extension(extension) {} std::filesystem::path Extension::GetPackagePath() const { return m_extension.Package().InstalledLocation().Path().c_str(); } std::filesystem::path Extension::GetPublicFolderPath() const { auto folder = m_extension.GetPublicFolderAsync().get(); THROW_HR_IF(APPINSTALLER_CLI_ERROR_EXTENSION_PUBLIC_FAILED, !folder); return folder.Path().c_str(); } winrt::Windows::ApplicationModel::PackageVersion Extension::GetPackageVersion() const { return m_extension.Package().Id().Version(); } bool Extension::VerifyContentIntegrity(IProgressCallback& progress) { auto operation = m_extension.Package().VerifyContentIntegrityAsync(); auto removeCancel = progress.SetCancellationFunction([&]() { operation.Cancel(); }); return operation.get(); } ExtensionCatalog::ExtensionCatalog(std::wstring_view extensionName) { m_catalog = AppExt::AppExtensionCatalog::Open(winrt::hstring(extensionName)); } std::optional ExtensionCatalog::FindByPackageFamilyAndId(std::string_view packageFamilyName, std::wstring_view id) const { std::wstring wpfn = Utility::ConvertToUTF16(packageFamilyName); std::optional result; auto extensions = m_catalog.FindAllAsync().get(); for (const auto& extension : extensions) { auto info = extension.AppInfo(); AICLI_LOG(Core, Info, << "Examining extension: PFN = " << Utility::ConvertToUTF8(info.PackageFamilyName()) << ", ID = " << Utility::ConvertToUTF8(extension.Id())); if (info.PackageFamilyName() == wpfn && extension.Id() == id) { AICLI_LOG(Core, Info, << "Found matching extension."); result = Extension{ extension }; break; } } if (!result) { AICLI_LOG(Core, Info, << "Did not find extension: PFN = " << packageFamilyName << ", ID = " << Utility::ConvertToUTF8(id)); } return result; } } ================================================ FILE: src/AppInstallerCommonCore/FileCache.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/FileCache.h" #include #include #include namespace AppInstaller::Caching { namespace anon { std::string_view GetNameForType(FileCache::Type type) { switch (type) { case FileCache::Type::IndexV1_Manifest: return "V1_M"; case FileCache::Type::IndexV2_PackageVersionData: return "V2_PVD"; case FileCache::Type::IndexV2_Manifest: return "V2_M"; case FileCache::Type::Icon: return "Icon"; #ifndef AICLI_DISABLE_TEST_HOOKS case FileCache::Type::Tests: return "Tests"; #endif } THROW_HR(E_UNEXPECTED); } std::unique_ptr GetUpstreamFile(const std::string& basePath, const std::string& relativePath, const Utility::SHA256::HashBuffer& expectedHash) { // Until signed files are implemented, fail on an empty hash THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE, expectedHash.empty()); std::string fullPath = basePath; if (fullPath.back() != '/') { fullPath += '/'; } fullPath += relativePath; if (Utility::IsUrlRemote(fullPath)) { auto result = std::make_unique(); AICLI_LOG(Core, Verbose, << "Getting upstream file from remote: " << fullPath); ProgressCallback emptyCallback; constexpr int MaxRetryCount = 2; constexpr std::chrono::seconds maximumWaitTimeAllowed = 10s; for (int retryCount = 0; retryCount < MaxRetryCount; ++retryCount) { try { auto downloadResult = Utility::DownloadToStream(fullPath, *result, Utility::DownloadType::Manifest, emptyCallback); if (!expectedHash.empty() && !Utility::SHA256::AreEqual(expectedHash, downloadResult.Sha256Hash)) { AICLI_LOG(Core, Verbose, << "Invalid hash from [" << fullPath << "]: expected [" << Utility::SHA256::ConvertToString(expectedHash) << "], got [" << Utility::SHA256::ConvertToString(downloadResult.Sha256Hash) << "]"); THROW_HR(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE); } break; } catch (const Utility::ServiceUnavailableException& sue) { if (retryCount < MaxRetryCount - 1) { auto waitSecondsForRetry = sue.RetryAfter(); if (waitSecondsForRetry > maximumWaitTimeAllowed) { throw; } // TODO: Get real progress callback to allow cancelation. auto ms = std::chrono::duration_cast(waitSecondsForRetry); Sleep(static_cast(ms.count())); } else { throw; } } catch (...) { if (retryCount < MaxRetryCount - 1) { AICLI_LOG(Core, Verbose, << "Getting upstream file failed, waiting a bit and retrying: " << fullPath); Sleep(500); } else { throw; } } } return result; } else { AICLI_LOG(Core, Verbose, << "Getting upstream file from local: " << fullPath); std::ifstream fileStream{ fullPath, std::ios_base::in | std::ios_base::binary }; std::string fileContents = Utility::ReadEntireStream(fileStream); auto fileContentsHash = Utility::SHA256::ComputeHash(fileContents); if (expectedHash.empty() || Utility::SHA256::AreEqual(expectedHash, fileContentsHash)) { return std::make_unique(std::move(fileContents)); } else { THROW_HR(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE); } } } } FileCache::Details::Details(FileCache::Type type, std::string identifier) : Type(type), Identifier(std::move(identifier)) { switch (type) { case Type::IndexV1_Manifest: case Type::IndexV2_PackageVersionData: case Type::IndexV2_Manifest: case Type::Icon: #ifndef AICLI_DISABLE_TEST_HOOKS case Type::Tests: #endif BasePath = Runtime::PathName::Temp; break; default: THROW_HR(E_UNEXPECTED); } } std::filesystem::path FileCache::Details::GetCachePath() const { std::filesystem::path result = Runtime::GetPathTo(BasePath); result /= "cache"; result /= anon::GetNameForType(Type); result /= Utility::ConvertToUTF16(Identifier); return result; } FileCache::FileCache(Type type, std::string identifier, std::vector sources) : m_details(type, std::move(identifier)), m_sources(std::move(sources)) { m_cacheBase = m_details.GetCachePath(); } const FileCache::Details& FileCache::GetDetails() const { return m_details; } std::unique_ptr FileCache::GetFile(const std::filesystem::path& relativePath, const Utility::SHA256::HashBuffer& expectedHash) const { std::filesystem::path cachedFilePath = m_cacheBase / relativePath; // Check cache for matching file try { if (std::filesystem::is_regular_file(cachedFilePath)) { AICLI_LOG(Core, Verbose, << "Reading cached file [" << cachedFilePath << "]"); std::ifstream fileStream{ cachedFilePath, std::ios_base::in | std::ios_base::binary }; std::string fileContents = Utility::ReadEntireStream(fileStream); auto fileContentsHash = Utility::SHA256::ComputeHash(fileContents); if (Utility::SHA256::AreEqual(expectedHash, fileContentsHash)) { return std::make_unique(std::move(fileContents)); } else { AICLI_LOG(Core, Verbose, << "Removing cached file [" << cachedFilePath << "] due to hash mismatch; expected [" << Utility::SHA256::ConvertToString(expectedHash) << "] but was [" << Utility::SHA256::ConvertToString(fileContentsHash) << "]"); } } std::filesystem::remove_all(cachedFilePath); } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("Error while attempting to read cached file"); } // Making it here means that we do not have a cached file or it needed to be updated and was removed. auto result = GetUpstreamFile(relativePath.u8string(), expectedHash); // GetUpstreamFile only returns with a successfully verified hash, we just need to write the file out. // Only log failures as caching is an optimization. try { std::filesystem::create_directories(cachedFilePath.parent_path()); AICLI_LOG(Core, Verbose, << "Writing cached file [" << cachedFilePath << "]"); std::ofstream fileStream{ cachedFilePath, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc }; LOG_LAST_ERROR_IF(fileStream.fail()); fileStream << result->str() << std::flush; LOG_LAST_ERROR_IF(fileStream.fail()); } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("Error while attempting to write cached file"); } return result; } std::unique_ptr FileCache::GetUpstreamFile(std::string relativePath, const Utility::SHA256::HashBuffer& expectedHash) const { // Replace backslashes with forward slashes for HTTP requests (since local can handle them). Utility::FindAndReplace(relativePath, "\\", "/"); std::exception_ptr firstException; for (const auto& upstream : m_sources) { try { return anon::GetUpstreamFile(upstream, relativePath, expectedHash); } catch(...) { LOG_CAUGHT_EXCEPTION_MSG("GetUpstreamFile failed on source: %hs", upstream.c_str()); if (!firstException) { firstException = std::current_exception(); } } } if (firstException) { std::rethrow_exception(firstException); } // Somewhat arbitrary error that should only happen if no upstream sources provided. THROW_HR(E_NOT_SET); } } ================================================ FILE: src/AppInstallerCommonCore/FileLogger.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerFileLogger.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerDateTime.h" #include "Public/winget/UserSettings.h" #include #include namespace AppInstaller::Logging { using namespace std::string_view_literals; using namespace std::chrono_literals; namespace { static constexpr std::string_view s_fileLoggerDefaultFilePrefix = "WinGet"sv; static constexpr std::string_view s_fileLoggerDefaultFileExt = ".log"sv; // Send to a string first to create a single block to write to a file. std::string ToLogLine(Channel channel, std::string_view message) { std::stringstream strstr; strstr << std::chrono::system_clock::now() << " [" << std::setw(GetMaxChannelNameLength()) << std::left << std::setfill(' ') << GetChannelName(channel) << "] " << message; return std::move(strstr).str(); } // Determines the difference between the given position and the maximum as an offset. std::ofstream::off_type CalculateDiff(const std::ofstream::pos_type& position, std::ofstream::off_type maximum) { auto offsetPosition = static_cast(position); return maximum > offsetPosition ? maximum - offsetPosition : 0; } } FileLogger::FileLogger() : FileLogger(s_fileLoggerDefaultFilePrefix) {} FileLogger::FileLogger(const std::filesystem::path& filePath) { m_name = GetNameForPath(filePath); m_filePath = filePath; InitializeDefaultMaximumFileSize(); OpenFileLoggerStream(); } FileLogger::FileLogger(const std::string_view fileNamePrefix) { m_name = "file"; m_filePath = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation); m_filePath /= fileNamePrefix.data() + ('-' + Utility::GetCurrentTimeForFilename() + s_fileLoggerDefaultFileExt.data()); InitializeDefaultMaximumFileSize(); OpenFileLoggerStream(); } FileLogger::~FileLogger() { m_stream.flush(); // When std::ofstream is constructed from an existing File handle, it does not call fclose on destruction // Only calling close() explicitly will close the file handle. m_stream.close(); } FileLogger& FileLogger::SetMaximumSize(std::ofstream::off_type maximumSize) { THROW_HR_IF(E_INVALIDARG, maximumSize < 0); m_maximumSize = maximumSize; return *this; } std::string FileLogger::GetNameForPath(const std::filesystem::path& filePath) { using namespace std::string_literals; return "file :: "s + filePath.u8string(); } std::string_view FileLogger::DefaultPrefix() { return s_fileLoggerDefaultFilePrefix; } std::string_view FileLogger::DefaultExt() { return s_fileLoggerDefaultFileExt; } std::string FileLogger::GetName() const { return m_name; } void FileLogger::Write(Channel channel, Level level, std::string_view message) noexcept try { std::string log = ToLogLine(channel, message); WriteDirect(channel, level, log); } catch (...) {} void FileLogger::WriteDirect(Channel, Level, std::string_view message) noexcept try { HandleMaximumFileSize(message); m_stream << message << std::endl; } catch (...) {} void FileLogger::SetTag(Tag tag) noexcept try { if (tag == Tag::HeadersComplete) { auto currentPosition = m_stream.tellp(); if (currentPosition != std::ofstream::pos_type{ -1 }) { m_headersEnd = currentPosition; } } } catch (...) {} void FileLogger::Add() { Log().AddLogger(std::make_unique()); } void FileLogger::Add(const std::filesystem::path& filePath) { Log().AddLogger(std::make_unique(filePath)); } void FileLogger::Add(std::string_view fileNamePrefix) { Log().AddLogger(std::make_unique(fileNamePrefix)); } void FileLogger::BeginCleanup() { BeginCleanup(Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation)); } void FileLogger::BeginCleanup(const std::filesystem::path& filePath) { std::thread([filePath]() { try { const auto& settings = Settings::User(); Filesystem::FileLimits fileLimits; fileLimits.Age = settings.Get(); fileLimits.TotalSizeInMB = settings.Get(); fileLimits.Count = settings.Get(); auto filesInPath = Filesystem::GetFileInfoFor(filePath); Filesystem::FilterToFilesExceedingLimits(filesInPath, fileLimits); for (const auto& file : filesInPath) { std::filesystem::remove(file.Path); } } // Just throw out everything catch (...) {} }).detach(); } void FileLogger::OpenFileLoggerStream() { // Prevent other writers to our log file, but allow readers FILE* filePtr = _wfsopen(m_filePath.wstring().c_str(), L"w", _SH_DENYWR); if (filePtr) { auto closeFile = wil::scope_exit([&]() { fclose(filePtr); }); // Prevent inheritance to ensure log file handle is not opened by other processes THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(reinterpret_cast(_get_osfhandle(_fileno(filePtr))), HANDLE_FLAG_INHERIT, 0)); m_stream = std::ofstream{ filePtr }; closeFile.release(); } else { AICLI_LOG(Core, Error, << "Failed to open log file " << m_filePath.u8string()); throw std::system_error(errno, std::generic_category()); } } void FileLogger::InitializeDefaultMaximumFileSize() { m_maximumSize = static_cast(Settings::User().Get()) << 20; } void FileLogger::HandleMaximumFileSize(std::string_view& currentLog) { if (m_maximumSize == 0) { return; } auto maximumLogSize = static_cast(CalculateDiff(m_headersEnd, m_maximumSize)); // In the event that a single log is larger than the maximum if (currentLog.size() > maximumLogSize) { currentLog = currentLog.substr(0, maximumLogSize); WrapLogFile(); return; } auto currentPosition = m_stream.tellp(); if (currentPosition == std::ofstream::pos_type{ -1 }) { // The expectation is that if the stream is in an error state the write won't actually happen. return; } auto availableSpace = static_cast(CalculateDiff(currentPosition, m_maximumSize)); if (currentLog.size() > availableSpace) { WrapLogFile(); return; } } void FileLogger::WrapLogFile() { m_stream.seekp(m_headersEnd); // Yes, we may go over the size limit slightly due to this and the unaccounted for newlines m_stream << ToLogLine(Channel::Core, "--- log file has wrapped ---") << std::endl; } } ================================================ FILE: src/AppInstallerCommonCore/FolderFileWatcher.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/FolderFileWatcher.h" #include "AppInstallerStrings.h" namespace AppInstaller::Utility { FolderFileWatcher::FolderFileWatcher(const std::filesystem::path& path, const std::optional& ext) : m_path(path), m_ext(ext), m_changeReader{} { } void FolderFileWatcher::Start() { m_files.clear(); m_changeReader = wil::make_folder_change_reader(m_path.c_str(), true, wil::FolderChangeEvents::FileName, [this](wil::FolderChangeEvent changeEvent, PCWSTR filePath) { switch (changeEvent) { // The file was added to the directory. case wil::FolderChangeEvent::Added: // The file was renamed and this is the new name. case wil::FolderChangeEvent::RenameNewName: { std::filesystem::path path(filePath); if (!m_ext.has_value() || Utility::CaseInsensitiveEquals(path.extension().u8string(), *m_ext)) { m_files.emplace(path); } break; } // The file was removed from the directory. case wil::FolderChangeEvent::Removed: // The file was renamed and this is the old name. case wil::FolderChangeEvent::RenameOldName: { std::filesystem::path path(filePath); if (!m_ext.has_value() || Utility::CaseInsensitiveEquals(path.extension().u8string(), *m_ext)) { auto it = m_files.find(path); if (it != m_files.cend()) { m_files.erase(it); } } break; } // The file was modified. This can be a change in the time stamp or attributes. case wil::FolderChangeEvent::Modified: // A change happens but it got lost. The result of the IoCompletionCallback is ERROR_NOTIFY_ENUM_DIR. case wil::FolderChangeEvent::ChangesLost: default: break; } }); } void FolderFileWatcher::Stop() { m_changeReader.reset(); } } ================================================ FILE: src/AppInstallerCommonCore/Fonts.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace AppInstaller::Utility; namespace AppInstaller::Fonts { namespace { constexpr std::wstring_view s_FontsPathSubkey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; constexpr std::wstring_view s_FontsUserInstallFolder = L"Microsoft\\Windows\\Fonts"; constexpr std::wstring_view s_TrueType = L" (TrueType)"; constexpr std::wstring_view s_IdentifierPrefix = L"FONT"; constexpr std::wstring_view s_Separator = L"\\"; constexpr std::wstring_view s_FontsWinGetPrefix = L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"; const int s_RemoveFontResourceMaxTries = 1000; std::wstring GetFontFileTitle(const std::filesystem::path& fontFilePath) { // This code can fail in a number of ways, including if the file isn't actually // a font file or does not have a title. wil::com_ptr pPropertyStore; THROW_IF_FAILED(SHGetPropertyStoreFromParsingName(fontFilePath.c_str(), nullptr, GPS_DEFAULT, IID_PPV_ARGS(&pPropertyStore))); PROPVARIANT prop; PropVariantInit(&prop); THROW_IF_FAILED(pPropertyStore->GetValue(PKEY_Title, &prop)); std::wstring title; if (prop.pwszVal) { title = prop.pwszVal; } THROW_IF_FAILED(PropVariantClear(&prop)); return title; } void AssertPackageInformation(const FontContext& context) { if (!context.PackageId.empty() && !context.PackageVersion.empty()) { return; } // This is a programming error if we reach this point where the package identifer cannot be created or derived. THROW_HR_MSG(E_UNEXPECTED, "Package Id and Version must be provided and non-empty."); } std::wstring GetFontRegistryPath(const FontContext& context) { // Registry path for a font is well-defined based on installer source and package information. auto path = std::wstringstream(); path << s_FontsPathSubkey; switch (context.InstallerSource) { case InstallerSource::Unknown: // Unknown installer could be anywhere, assume it is in the default fonts location. break; case InstallerSource::WinGet: // WinGet path adds the WinGet prefix + package id + version. path << s_Separator << s_FontsWinGetPrefix << s_Separator << context.PackageId << s_Separator << context.PackageVersion; break; default: THROW_HR_MSG(E_UNEXPECTED, "Undefined InstallerSource."); } return path.str(); } std::filesystem::path GetRootFontPath(Manifest::ScopeEnum scope) { if (scope == Manifest::ScopeEnum::Machine) { return Runtime::GetPathTo(Runtime::PathName::FontsMachineInstallLocation); } else { return Runtime::GetPathTo(Runtime::PathName::FontsUserInstallLocation); } } std::filesystem::path GetFontFileInstallPath(const FontContext& context) { auto path = GetRootFontPath(context.Scope); switch (context.InstallerSource) { case InstallerSource::Unknown: // Unknown installer is assumed to be installed in the default location for machine. // For a user installed font it could be anywhere. break; case InstallerSource::WinGet: // WinGet installed packages have two formats depending on the scope. // For Per-user we use subfolders for better robustness and cleaner format. // For Per-machine we must use a single folder due to a system requirement, // so the root path is the machine install path. if (context.Scope == Manifest::ScopeEnum::User) { AssertPackageInformation(context); path /= s_FontsWinGetPrefix; path /= context.PackageId; path /= context.PackageVersion; } break; default: THROW_HR_MSG(E_UNEXPECTED, "Undefined InstallerSource."); } return path; } // For machine-installed fonts the files all reside in the same folder. We need a unique name that is reasonably human // readable. The way system fonts normally do this is by appending a number to the end of the file stem. We will follow // the same pattern by using the original file and adding a number until we arrive at a unique filename. std::filesystem::path GetUniquePathForDestination(const std::filesystem::path& source, const std::filesystem::path& destination) { const auto stem = source.stem(); const auto ext = source.extension(); auto uniqueName = source.filename(); auto candidatePath = destination / uniqueName; int index = 0; while (std::filesystem::exists(candidatePath)) { std::filesystem::path appendId = { std::to_string(index) }; uniqueName = stem; uniqueName += appendId; uniqueName += ext; candidatePath = destination / uniqueName; index++; } return candidatePath; } std::vector GetFontFilePaths(const wil::com_ptr& fontFace) { UINT32 fileCount = 0; THROW_IF_FAILED(fontFace->GetFiles(&fileCount, nullptr)); static_assert(sizeof(wil::com_ptr) == sizeof(IDWriteFontFile*)); std::vector> fontFiles; fontFiles.resize(fileCount); THROW_IF_FAILED(fontFace->GetFiles(&fileCount, fontFiles[0].addressof())); std::vector filePaths; for (UINT32 i = 0; i < fileCount; ++i) { wil::com_ptr loader; THROW_IF_FAILED(fontFiles[i]->GetLoader(loader.addressof())); const void* fontFileReferenceKey; UINT32 fontFileReferenceKeySize; THROW_IF_FAILED(fontFiles[i]->GetReferenceKey(&fontFileReferenceKey, &fontFileReferenceKeySize)); if (const auto localLoader = loader.try_query()) { UINT32 pathLength; THROW_IF_FAILED(localLoader->GetFilePathLengthFromKey(fontFileReferenceKey, fontFileReferenceKeySize, &pathLength)); pathLength += 1; // Account for the trailing null terminator during allocation. std::wstring path; path.resize(pathLength); THROW_IF_FAILED(localLoader->GetFilePathFromKey(fontFileReferenceKey, fontFileReferenceKeySize, &path[0], pathLength)); path.resize(pathLength - 1); // Remove the null char. filePaths.emplace_back(std::move(path)); } } return filePaths; } void RemoveAllFontResources(const std::filesystem::path& filePath) { // The recommended uninstall method of a font is to call RemoveFontResource until it fails, // This is not guaranteed to remove the file from use, but it is the best we have. // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-removefontresourcea int i = 0; while (::RemoveFontResource(filePath.c_str())) { // Let us not loop endlessly. if (++i >= s_RemoveFontResourceMaxTries) { break; } } } void NotifyFontChange() { // Send the WM_FONTCHANGE message so the system and apps know that there has been a font change. // Sometimes this does not have an error when it returns non-zero. To avoid random assert failures // we will not try to log failures for this call. If it fails it does not affect install state. auto sendResult = ::SendNotifyMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0); AICLI_LOG(Core, Info, << "Notified system of font change: " << sendResult); } // Checks the registry path for the file specified, and returns the key value if it finds one. std::optional CheckRegistryForFontFileReference(const std::wstring& registryPath, const std::filesystem::path& filePath, Manifest::ScopeEnum scope) { try { const auto& hive = scope == Manifest::ScopeEnum::Machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; const auto& key = Registry::Key::OpenIfExists(hive, registryPath, 0UL, KEY_READ); if (key) { // Check for the file being present in this key. for (const auto& valueRef : key.Values()) { const auto& value = valueRef.Value(); if (!value.has_value() || (value.value().GetType() != Registry::Value::Type::String)) { continue; } const auto& valueName = ConvertToUTF16(valueRef.Name()); const std::filesystem::path& valuePath = { value->GetValue() }; if (valuePath.is_relative()) { // Relative value is OK, just compare the filenames. // We will assume this is in the standard font paths. if (valuePath.filename() == filePath.filename()) { return valueName; } } else { if (valuePath == filePath) { return valueName; } } } } } CATCH_LOG(); return std::nullopt; } // The package registry for machine installed fonts stores the package filename as a registry value. // This is so we can map the original package filename to the installed filename since they may be different. std::optional GetRegisteredMachineFontInstallPath(const std::wstring& registryPath, const std::filesystem::path& filePath) { try { const auto& key = Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, registryPath, 0UL, KEY_READ); if (key) { for (const auto& valueRef : key.Values()) { const auto& value = valueRef.Value(); if (!value.has_value() || (value.value().GetType() != Registry::Value::Type::String)) { continue; } const auto& valueName = ConvertToUTF16(valueRef.Name()); if (valueName == filePath.filename()) { const std::filesystem::path& valuePath = { value->GetValue() }; return valuePath; } } } } CATCH_LOG(); return std::nullopt; } } FontResult FontOperationResult::Result() const { if (FAILED(HResult)) { return FontResult::Error; } else { return FontResult::Success; } } void FontContext::AddPackageFile(const std::filesystem::path& filePath) { PackageFiles.push_back(filePath); } Manifest::AppsAndFeaturesEntry FontContext::GetAppsAndFeaturesEntry() const { Manifest::AppsAndFeaturesEntry entry; entry.DisplayName = ConvertToUTF8(PackageId); entry.DisplayVersion = ConvertToUTF8(PackageVersion); entry.InstallerType = Manifest::InstallerTypeEnum::Font; // Product Code is the PackageId entry.ProductCode = ConvertToUTF8(PackageId); return entry; } std::wstring FontContext::GetPackageIdentifier() const { auto stream = std::wstringstream(); stream << s_IdentifierPrefix << s_Separator << ConvertToUTF16(Manifest::ScopeToString(Scope)) << s_Separator << PackageId; return stream.str(); } // Creates font file info used for install, validation, and enumeration. FontFileInfo CreateFontFileInfo(const FontContext& context, const std::filesystem::path& filePath, const std::wstring& title) { auto fileInfo = FontFileInfo(); fileInfo.FilePath = filePath; fileInfo.PackageIdentifier = context.GetPackageIdentifier(); fileInfo.PackageId = context.PackageId; fileInfo.PackageVersion = context.PackageVersion; fileInfo.InstallerSource = context.InstallerSource; fileInfo.Scope = context.Scope; fileInfo.Title = title; // For font file paths in the registry, we may have a relative filename (because the path is assumed). if (fileInfo.FilePath.is_relative()) { // Relative path is assumed to be relative to the default location for the scope. // We are assuming that a relative file path input refers to an already-installed file. auto fullPath = GetRootFontPath(fileInfo.Scope); fullPath /= fileInfo.FilePath; fileInfo.FilePath = std::move(fullPath); } // Get information about the font file itself. auto fontCatalog = FontCatalog(); fileInfo.WinGetSupported = fontCatalog.IsFontFileSupported(fileInfo.FilePath, fileInfo.FileType); if (fileInfo.WinGetSupported && fileInfo.Title.empty()) { try { fileInfo.Title = GetFontFileTitle(fileInfo.FilePath); } CATCH_LOG(); } if (fileInfo.Title.empty()) { // If the title is still empty (such as failure to set it from the File), use the Filename fileInfo.Title = fileInfo.FilePath.filename().wstring(); } fileInfo.RegistryPackagePath = GetFontRegistryPath(context); if (fileInfo.Scope == Manifest::ScopeEnum::Machine && fileInfo.InstallerSource == InstallerSource::WinGet) { // Machine fonts from WinGet still install into the Font folder but we track package source separately // due to limited information in the Font registry and all font files being in the same key and folder. fileInfo.RegistryInstallPath = s_FontsPathSubkey; } else { // User-installed and non-Winget fonts have same registry package path and install path. fileInfo.RegistryInstallPath = fileInfo.RegistryPackagePath; } // Check if the file is already in the install path, in which case this is a query. auto const& installPath = GetFontFileInstallPath(context); if (installPath == fileInfo.FilePath.parent_path()) { fileInfo.InstallPath = fileInfo.FilePath; } else { if (fileInfo.InstallerSource == InstallerSource::WinGet && fileInfo.Scope == Manifest::ScopeEnum::Machine) { // Machine installed WinGet fonts must have a non-conflicting filename in the root font folder. // To ensure a unique font name and allow different versions to exist side by side, the install file // name may be a different name from the original package install. To account for this, the Package // registry location uses the original filename as the registry value name and the value as the // resulting path. If we are installing a machine font that already exists, we must check to see // if this file already has a mapping defined. const auto& installedFilePath = GetRegisteredMachineFontInstallPath(fileInfo.RegistryPackagePath, fileInfo.FilePath.filename()); if (installedFilePath.has_value()) { fileInfo.InstallPath = installedFilePath.value(); } else { // File not already defined, we need to create a non-conflicting filename. // Machine-installed WinGet must provide a non-conflicting filename to the root install location. fileInfo.InstallPath = GetUniquePathForDestination(fileInfo.FilePath, installPath); } } else { fileInfo.InstallPath = installPath / fileInfo.FilePath.filename(); } } // If our font file is registered it will exist in the install path and have a registry value. const auto& registryTitle = CheckRegistryForFontFileReference(fileInfo.RegistryInstallPath, fileInfo.InstallPath, fileInfo.Scope); if (fileInfo.Title.empty()) { // Use the registry title or filename if we still dont' know. if (registryTitle.has_value()) { fileInfo.Title = registryTitle.value(); } else { fileInfo.Title = fileInfo.FilePath.filename().wstring(); } } fileInfo.IsFontFileInstalled = std::filesystem::exists(fileInfo.InstallPath) ? true : false; fileInfo.IsFontFileRegistered = registryTitle.has_value() ? true : false; if (fileInfo.IsFontFileInstalled && fileInfo.IsFontFileRegistered) { fileInfo.Status = FontStatus::OK; } else if (fileInfo.IsFontFileInstalled || fileInfo.IsFontFileRegistered) { fileInfo.Status = FontStatus::Corrupt; } else { fileInfo.Status = FontStatus::Absent; } return fileInfo; } FontValidationResult ValidateFontPackage(FontContext& context) { AssertPackageInformation(context); auto result = FontValidationResult(); AICLI_LOG(Core, Info, << "Validating font package: " << context.PackageId.c_str() << " " << context.PackageVersion.c_str()); try { // Create font file info for each package file for (const auto& file : context.PackageFiles) { result.FontFileInfos.push_back(CreateFontFileInfo(context, file)); } // We create an overall status for the validation with the following determination: // If ALL of the files are OK, the package is OK. // If ALL of the files are Absent, the package is Absent. // If there is any combination, the package is Corrupt. // We must check each file info to see if the package is supported by WinGet. // If any file is unsupported then there are unsupported fonts in the package. for (const auto& fontFileInfo : result.FontFileInfos) { if (!fontFileInfo.WinGetSupported) { result.HasUnsupportedFonts = true; } if (fontFileInfo.Status != result.Status) { if (result.Status == FontStatus::Unknown) { // This is the first status we've seen, make it the default. result.Status = fontFileInfo.Status; } else { // Previous status now differs from current status, package is corrupt. result.Status = FontStatus::Corrupt; } } } result.Result = FontResult::Success; } catch (...) { LOG_CAUGHT_EXCEPTION(); result.HResult = APPINSTALLER_CLI_ERROR_FONT_VALIDATION_FAILED; result.Result = FontResult::Error; } return result; } FontOperationResult InstallFontPackage(FontContext& context) { AssertPackageInformation(context); FontOperationResult result; if (context.InstallerSource != InstallerSource::WinGet) { throw std::logic_error("Only WinGet format of font install is supported."); } if (context.PackageFiles.size() == 0) { result.HResult = winrt::hresult(APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_FOUND); AICLI_LOG(Core, Error, << "Font package has no files: " << context.PackageId.c_str() << " - " << AppInstaller::Logging::SetHRFormat << result.HResult); return result; } // Validate the package data was all processed successfully. const auto& validationResult = ValidateFontPackage(context); if (validationResult.Result != FontResult::Success) { result.HResult = winrt::hresult(APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_SUPPORTED); AICLI_LOG(Core, Error, << "Font package validation failed: " << context.PackageId.c_str() << " - " << AppInstaller::Logging::SetHRFormat << validationResult.HResult); return result; } // Check for unsupported font files. if (validationResult.HasUnsupportedFonts) { result.HResult = winrt::hresult(APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_SUPPORTED); AICLI_LOG(Core, Error, << "Font package has unsupported fonts: " << context.PackageId.c_str() << " - " << result.HResult); return result; } // Check for package already installed and correct unless this is a force install. if (validationResult.Status == FontStatus::OK && !context.Force) { result.HResult = winrt::hresult(APPINSTALLER_CLI_ERROR_FONT_ALREADY_INSTALLED); AICLI_LOG(Core, Info, << "Font package is already installed and in a good state.: " << context.PackageId.c_str() << " - " << AppInstaller::Logging::SetHRFormat << result.HResult); return result; } // We will attempt a cleanup / force install if either this is a forced install or the package was not in a good state. if (validationResult.Status == FontStatus::Corrupt || context.Force) { // Force install of a font can have problems because if the font is already there then it // may be in use. The scenarios for using force is if a prior install attempt failed, so // we will try to clean up the existing font registration so it may be successful this time. AICLI_LOG(Core, Info, << "Package is corrupt or forced install, attempting to remove any existing registration."); auto uninstallResult = UninstallFontPackage(context); if (uninstallResult.Result() != FontResult::Success) { result.HResult = uninstallResult.HResult; AICLI_LOG(Core, Error, << "Font cleanup uninstall failed: " << context.PackageId.c_str() << " - " << AppInstaller::Logging::SetHRFormat << uninstallResult.HResult); return result; } } AICLI_LOG(Core, Info, << "Starting install of " << context.PackageId.c_str()); try { // Install each file from the file info. for (const auto& fontFileInfo : validationResult.FontFileInfos) { // Font install step 1: Copy file to winget package identifiable location. AICLI_LOG(Core, Info, << "Moving " << fontFileInfo.FilePath << " to " << fontFileInfo.InstallPath); if (!std::filesystem::exists(fontFileInfo.InstallPath.parent_path())) { std::filesystem::create_directories(fontFileInfo.InstallPath.parent_path()); } // The file should not be present, but if it is, do another attempt to remove it. if (std::filesystem::exists(fontFileInfo.InstallPath)) { // Try to remove font resource to avoid file-in-use. RemoveAllFontResources(fontFileInfo.InstallPath); if (!std::filesystem::remove(fontFileInfo.InstallPath)) { AICLI_LOG(Core, Error, << "Font file already exists and was unable to be removed."); THROW_HR(APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED); } } if (context.Scope == Manifest::ScopeEnum::Machine) { // Using std::filesystem::rename does not work when installing into the system Fonts // folder; the system does not recognize the font files. std::filesystem::copy_file does work. std::filesystem::copy_file(fontFileInfo.FilePath, fontFileInfo.InstallPath); std::filesystem::remove(fontFileInfo.FilePath); } else { // We prefer to use rename where it works. AppInstaller::Filesystem::RenameFile(fontFileInfo.FilePath, fontFileInfo.InstallPath); } AICLI_LOG(Core, Info, << "Adding " << fontFileInfo.Title.c_str() << " to " << fontFileInfo.InstallPath); if (context.Scope == Manifest::ScopeEnum::User) { auto key = Registry::Key::Create(HKEY_CURRENT_USER, fontFileInfo.RegistryInstallPath); key.SetValue(fontFileInfo.Title, fontFileInfo.InstallPath, REG_SZ); } else { // Machine install we set two keys, one for source information, and one for the actual install. // The value name is the source filename so we can map this later for validation. auto sourceKey = Registry::Key::Create(HKEY_LOCAL_MACHINE, fontFileInfo.RegistryPackagePath, 0UL, KEY_ALL_ACCESS); sourceKey.SetValue(fontFileInfo.FilePath.filename(), fontFileInfo.InstallPath, REG_SZ); // Machine font install uses relative filename. Use filename as value to avoid collisions in the key. auto installKey = Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, fontFileInfo.RegistryInstallPath, 0UL, KEY_ALL_ACCESS); installKey.SetValue(fontFileInfo.InstallPath.filename(), fontFileInfo.InstallPath.filename(), REG_SZ); } // Add Font Resource to the session. const auto& fontsAdded = ::AddFontResource(fontFileInfo.InstallPath.c_str()); if (fontsAdded == 0) { // AddFontResource does not add additional information for us, so we dont know why they were not added, // only that they were not added. At this point the "install" is successful, but we were not able to // add the font to the system, which means subsequent adds on session start may also fail, but is not // guaranteed to fail. We will note it in the log and carry on. AICLI_LOG(Core, Warning, << "Failed to add font resource: " << fontFileInfo.InstallPath); } else { AICLI_LOG(Core, Info, << "Added " << fontsAdded << " fonts to the session."); } } result.HResult = S_OK; } catch (...) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Core, Error, << "Install failed for " << context.PackageId.c_str() << ", attempting rollback."); try { // Rollback. In this case, rollback is uninstall, which should remove any partial installation. // This is best-effort. auto rollbackResult = UninstallFontPackage(context); if (rollbackResult.Result() == FontResult::Success) { AICLI_LOG(Core, Info, << "Rollback for " << context.PackageId.c_str() << " successful."); } else { AICLI_LOG(Core, Error, << "Rollback for " << context.PackageId.c_str() << " failed: " << AppInstaller::Logging::SetHRFormat << rollbackResult.HResult); } } CATCH_LOG(); result.HResult = APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED; } // Regardless of the result there's likely some changes that occurred. NotifyFontChange(); return result; } FontOperationResult UninstallFontPackage(FontContext& context) { FontOperationResult result; if (context.InstallerSource != InstallerSource::WinGet) { throw std::logic_error("Only WinGet format of font package uninstall is supported."); } AssertPackageInformation(context); const auto& installFolderPath = GetFontFileInstallPath(context); const auto& installPackagePath = GetFontRegistryPath(context); std::vector filesToRemove; try { auto hive = context.Scope == Manifest::ScopeEnum::Machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; auto key = Registry::Key::OpenIfExists(hive, installPackagePath, 0ul, KEY_ALL_ACCESS); if (key) { // Assume all values in this key are fonts related to this package. for (const auto& fontEntry : key.Values()) { auto value = fontEntry.Value(); if (!value.has_value() || (value.value().GetType() != Registry::Value::Type::String)) { // Not a valid entry, nothing to do here. continue; } std::filesystem::path filePath = { value->GetValue() }; if (!std::filesystem::exists(filePath)) { // File doesn't exist, nothing to do here. continue; } // The font may be in use by the system or other apps, it needs to be removed from // from use or at least attempted to be removed from use. RemoveAllFontResources(filePath); filesToRemove.push_back(filePath); } // Delete the key if (!Registry::Key::DeleteTree(hive, installPackagePath)) { AICLI_LOG(Core, Warning, << "Failed removing registry tree " << installPackagePath.c_str()); } if (context.Scope == Manifest::ScopeEnum::Machine) { // Machine installed fonts also have an entry in the System font registry. // We have to find the values that have the filename as data. auto machineFontRoot = Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, s_FontsPathSubkey.data(), 0ul, KEY_ALL_ACCESS); for (const auto& file : filesToRemove) { machineFontRoot.DeleteValue(file.filename()); } } } } catch (...) { LOG_CAUGHT_EXCEPTION(); // For uninstall we will log the error and continue trying to remove it, since we have partial remove and are in an unknown state. AICLI_LOG(Core, Error, << "Failed removing fonts in the registry."); } // Remove any font files. for (const auto& file : filesToRemove) { try { if (std::filesystem::exists(file)) { // TODO: Add robustness for files-in-use scenarios. std::filesystem::remove(file); } } catch (...) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Core, Error, << "Failed removing font files."); result.HResult = winrt::hresult(APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED); } } if (context.Scope == Manifest::ScopeEnum::User) { // User installed fonts also have an install folder, we will clean that up as well. if (std::filesystem::exists(installFolderPath)) { try { const auto& parent = installFolderPath.parent_path(); std::filesystem::remove_all(installFolderPath); // This may have been the only version installed, so remove parent if empty. if (std::filesystem::is_empty(parent)) { std::filesystem::remove(parent); } } catch (...) { // Font files have already been removed at this point, so failure to delete // this folder is not a failure of removing the fonts. LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Core, Error, << "Failed removing font folders."); } } } // Notify system of font changes. NotifyFontChange(); return result; } std::wstring GetFontRegistryRoot() { return s_FontsPathSubkey.data(); } FontCatalog::FontCatalog() { m_preferredLocales = AppInstaller::Locale::GetUserPreferredLanguagesUTF16(); THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(m_factory), m_factory.put_unknown())); } std::vector FontCatalog::GetInstalledFontFamilies(std::optional familyName) { wil::com_ptr collection; THROW_IF_FAILED(m_factory->GetSystemFontCollection(collection.addressof(), FALSE)); std::vector installedFontFamilies; if (familyName.has_value()) { UINT32 index; BOOL exists; THROW_IF_FAILED(collection->FindFamilyName(familyName.value().c_str(), &index, &exists)); if (exists) { installedFontFamilies.emplace_back(GetFontFamilyByIndex(collection, index)); } } else { UINT32 familyCount = collection->GetFontFamilyCount(); for (UINT32 index = 0; index < familyCount; index++) { installedFontFamilies.emplace_back(GetFontFamilyByIndex(collection, index)); } } return installedFontFamilies; } bool FontCatalog::IsFontFileSupported(const std::filesystem::path& filePath, DWRITE_FONT_FILE_TYPE& fileType) { if (!std::filesystem::exists(filePath)) { fileType = DWRITE_FONT_FILE_TYPE_UNKNOWN; return false; } wil::com_ptr fontFile; THROW_IF_FAILED(m_factory->CreateFontFileReference(filePath.c_str(), NULL, &fontFile)); BOOL isSupported; DWRITE_FONT_FACE_TYPE faceType; UINT32 numOfFaces; THROW_IF_FAILED(fontFile->Analyze(&isSupported, &fileType, &faceType, &numOfFaces)); return isSupported; } std::wstring FontCatalog::GetLocalizedStringFromFont(const wil::com_ptr& localizedStringCollection) { UINT32 index = 0; BOOL exists = false; for (const auto& locale : m_preferredLocales) { if (SUCCEEDED_LOG(localizedStringCollection->FindLocaleName(locale.c_str(), &index, &exists)) && exists) { break; } } // If the locale does not exist, resort to the default value at the 0 index. if (!exists) { index = 0; } UINT32 length = 0; THROW_IF_FAILED(localizedStringCollection->GetStringLength(index, &length)); length += 1; // Account for the trailing null terminator during allocation. std::wstring localizedString; localizedString.resize(length); THROW_IF_FAILED(localizedStringCollection->GetString(index, &localizedString[0], length)); localizedString.resize(length - 1); // Remove the null char. return localizedString; } std::wstring FontCatalog::GetFontFaceName(const wil::com_ptr& font) { wil::com_ptr faceNames; THROW_IF_FAILED(font->GetFaceNames(faceNames.addressof())); return GetLocalizedStringFromFont(faceNames); } std::wstring FontCatalog::GetFontFamilyName(const wil::com_ptr& fontFamily) { wil::com_ptr familyNames; THROW_IF_FAILED(fontFamily->GetFamilyNames(familyNames.addressof())); return GetLocalizedStringFromFont(familyNames); } Utility::OpenTypeFontVersion FontCatalog::GetFontFaceVersion(const wil::com_ptr& font) { wil::com_ptr fontVersion; BOOL exists; THROW_IF_FAILED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS, fontVersion.addressof(), &exists)); if (!exists) { return {}; } std::string value = ConvertToUTF8(GetLocalizedStringFromFont(fontVersion)); Utility::OpenTypeFontVersion openTypeFontVersion{ value }; return openTypeFontVersion; } FontFamily FontCatalog::GetFontFamilyByIndex(const wil::com_ptr& collection, UINT32 index) { wil::com_ptr family; THROW_IF_FAILED(collection->GetFontFamily(index, family.addressof())); std::wstring familyName = GetFontFamilyName(family); std::vector fontFaces; UINT32 fontCount = family->GetFontCount(); for (UINT32 fontIndex = 0; fontIndex < fontCount; fontIndex++) { wil::com_ptr font; THROW_IF_FAILED(family->GetFont(fontIndex, font.addressof())); wil::com_ptr fontFace; THROW_IF_FAILED(font->CreateFontFace(fontFace.addressof())); FontFace fontFaceEntry; fontFaceEntry.Name = GetFontFaceName(font); fontFaceEntry.Version = GetFontFaceVersion(font); fontFaceEntry.FilePaths = GetFontFilePaths(fontFace); fontFaces.emplace_back(std::move(fontFaceEntry)); } FontFamily fontFamily; fontFamily.Name = std::move(familyName); fontFamily.Faces = std::move(fontFaces); return fontFamily; } // This will create an inventory of all known permanently installed fonts to the user. // A font is "permanently installed" if it is present in the Font registry for the machine or the user. // This will not include fonts that are temporarily installed for the session. std::vector GetInstalledFontFiles() { auto fontFiles = std::vector(); try { auto wingetMachineFonts = std::unordered_map(); // Iterate through scopes for machine and user for (const auto& scope : { Manifest::ScopeEnum::Machine, Manifest::ScopeEnum::User }) { auto hive = scope == Manifest::ScopeEnum::Machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; auto root = Registry::Key::OpenIfExists(hive, std::wstring{ s_FontsPathSubkey }); // There are two supported scenarios for tracking sub-keys. // 1) We created the subkey, therefore it is a winget installed font. // 2) A package created the subkey for a package-deployed font // We assume that all sub-keys not the WinGet key are packaged keys. It is not guaranteed that // a package created the font, but we will assume that it is to be safe in how we handle them. for (const auto& subkey : root) { auto subkeyName = ConvertToUTF16(subkey.Name()); auto subkeyKey = subkey.Open(); if (subkeyName == s_FontsWinGetPrefix) { // Assume all sub-keys are WinGet Packages for (const auto& packageSubKey : subkeyKey) { // All sub-keys should be versions of the package. auto wingetPackageSubKey = packageSubKey.Open(); for (const auto& versionSubKey : wingetPackageSubKey) { auto packageId = ConvertToUTF16(packageSubKey.Name()); auto version = ConvertToUTF16(versionSubKey.Name()); auto packageVersionSubKey = versionSubKey.Open(); for (const auto& versionValue : packageVersionSubKey.Values()) { // Values are the files. auto value = versionValue.Value(); if (!value.has_value() || (value.value().GetType() != Registry::Value::Type::String)) { continue; } std::filesystem::path filePath = { value->GetValue() }; if (scope == Manifest::ScopeEnum::Machine) { // Capture the filename for winget machine fonts so we can avoid duplicates. wingetMachineFonts[filePath.filename()] = true; } auto context = FontContext(); context.Scope = scope; context.InstallerSource = InstallerSource::WinGet; context.PackageId = packageId; context.PackageVersion = version; auto fontFile = CreateFontFileInfo(context, filePath, ConvertToUTF16(versionValue.Name())); fontFiles.push_back(std::move(fontFile)); } } } } } // All remaining values in the root are externally installed fonts. for (const auto& rootValue : root.Values()) { auto value = rootValue.Value(); if (!value.has_value() || (value.value().GetType() != Registry::Value::Type::String)) { continue; } std::filesystem::path filePath = { value->GetValue() }; if (scope == Manifest::ScopeEnum::Machine) { // Skip it if this is already identified in the WinGet machine fonts. if (wingetMachineFonts[filePath.filename()]) { continue; } } auto context = FontContext(); context.Scope = scope; context.InstallerSource = InstallerSource::Unknown; context.PackageId = filePath.filename(); auto fontFile = CreateFontFileInfo(context, filePath, ConvertToUTF16(rootValue.Name())); fontFiles.push_back(std::move(fontFile)); } } } catch (...) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Core, Error, << "Failed getting font file information."); } return fontFiles; } std::vector GetInstalledFontPackages(Manifest::ScopeEnum scope) { auto fontPackages = std::vector(); try { auto hive = scope == Manifest::ScopeEnum::Machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; auto root = Registry::Key::OpenIfExists(hive, s_FontsPathSubkey.data(), 0UL, KEY_READ); for (const auto& subkey : root) { auto subkeyName = ConvertToUTF16(subkey.Name()); auto subkeyKey = subkey.Open(); if (subkeyName == s_FontsWinGetPrefix) { // Assume all sub-keys are WinGet Packages for (const auto& packageSubKey : subkeyKey) { try { // All sub-keys should be versions of the package. auto wingetPackageSubKey = packageSubKey.Open(); for (const auto& versionSubKey : wingetPackageSubKey) { auto packageId = ConvertToUTF16(packageSubKey.Name()); auto version = ConvertToUTF16(versionSubKey.Name()); auto context = FontContext(); context.Scope = scope; context.InstallerSource = InstallerSource::WinGet; context.PackageId = packageId; context.PackageVersion = version; auto packageVersionSubKey = versionSubKey.Open(); for (const auto& versionValue : packageVersionSubKey.Values()) { auto value = versionValue.Value(); if (!value.has_value() || (value.value().GetType() != Registry::Value::Type::String)) { continue; } std::filesystem::path filePath = { value->GetValue() }; context.AddPackageFile(filePath); } auto validationResult = ValidateFontPackage(context); if (validationResult.Status != FontStatus::Absent) { auto packageInfo = FontPackageInfo(); packageInfo.Scope = scope; packageInfo.Status = validationResult.Status; packageInfo.PackageId = packageId; packageInfo.PackageVersion = version; packageInfo.PackageIdentifier = context.GetPackageIdentifier(); fontPackages.push_back(std::move(packageInfo)); } } } catch (...) { // If we have bad data in the registry for this entry, log it and skip. LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Core, Error, << "Failed getting font package information for: " << packageSubKey.Name().c_str()); continue; } } } } } catch (...) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Core, Error, << "Failed getting font package information."); } return fontPackages; } } ================================================ FILE: src/AppInstallerCommonCore/HttpClientHelper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include #include namespace AppInstaller::Http { namespace { // If the caller does not pass in a user agent header, put the default one on the request. void EnsureDefaultUserAgent(web::http::http_request& request) { static utility::string_t c_defaultUserAgent = Utility::ConvertToUTF16(AppInstaller::Runtime::GetDefaultUserAgent()); if (!request.headers().has(web::http::header_names::user_agent)) { request.headers().add(web::http::header_names::user_agent, c_defaultUserAgent); } } void NativeHandleServerCertificateValidation(web::http::client::native_handle handle, const Certificates::PinningConfiguration& pinningConfiguration, ThreadLocalStorage::ThreadGlobals* threadGlobals) { decltype(threadGlobals->SetForCurrentThread()) previousThreadGlobals; if (threadGlobals) { previousThreadGlobals = threadGlobals->SetForCurrentThread(); } HINTERNET requestHandle = reinterpret_cast(handle); // Get certificate and pass along to pinning config wil::unique_cert_context certContext; DWORD bufferSize = sizeof(&certContext); THROW_IF_WIN32_BOOL_FALSE(WinHttpQueryOption(requestHandle, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &certContext, &bufferSize)); THROW_HR_IF(APPINSTALLER_CLI_ERROR_PINNED_CERTIFICATE_MISMATCH, !pinningConfiguration.Validate(certContext.get())); } std::chrono::seconds GetRetryAfter(const web::http::http_headers& headers) { auto retryAfterHeader = headers.find(web::http::header_names::retry_after); if (retryAfterHeader != headers.end()) { return AppInstaller::Utility::GetRetryAfter(retryAfterHeader->second.c_str()); } return 0s; } } HttpClientHelper::HttpClientHelper(std::shared_ptr stage) : m_defaultRequestHandlerStage(std::move(stage)) { const auto& proxyUri = Settings::Network().GetProxyUri(); if (proxyUri) { AICLI_LOG(Repo, Info, << "Setting proxy for REST HTTP Client helper to " << proxyUri.value()); m_clientConfig.set_proxy(web::web_proxy{ Utility::ConvertToUTF16(proxyUri.value()) }); } else { AICLI_LOG(Repo, Info, << "REST HTTP Client helper does not use proxy"); } } pplx::task HttpClientHelper::Post( const utility::string_t& uri, const web::json::value& body, const HttpClientHelper::HttpRequestHeaders& headers, const HttpClientHelper::HttpRequestHeaders& authHeaders) const { AICLI_LOG(Repo, Info, << "Sending http POST request to: " << utility::conversions::to_utf8string(uri)); web::http::client::http_client client = GetClient(uri); web::http::http_request request{ web::http::methods::POST }; request.headers().set_content_type(web::http::details::mime_types::application_json); request.set_body(body.serialize()); // Add headers for (auto& pair : headers) { request.headers().add(pair.first, pair.second); } EnsureDefaultUserAgent(request); AICLI_LOG(Repo, Verbose, << "Http POST request details:\n" << utility::conversions::to_utf8string(request.to_string())); // Add auth headers after logging for (auto& pair : authHeaders) { request.headers().add(pair.first, pair.second); } return client.request(request); } std::optional HttpClientHelper::HandlePost( const utility::string_t& uri, const web::json::value& body, const HttpClientHelper::HttpRequestHeaders& headers, const HttpClientHelper::HttpRequestHeaders& authHeaders, const HttpResponseHandler& customHandler) const try { web::http::http_response httpResponse; Post(uri, body, headers, authHeaders).then([&httpResponse](const web::http::http_response& response) { httpResponse = response; }).wait(); if (customHandler) { auto handlerResult = customHandler(httpResponse); if (!handlerResult.UseDefaultHandling) { return std::move(handlerResult.Result); } } return ValidateAndExtractResponse(httpResponse); } catch (web::http::http_exception& exception) { RethrowAsWilException(exception); } pplx::task HttpClientHelper::Get( const utility::string_t& uri, const HttpClientHelper::HttpRequestHeaders& headers, const HttpClientHelper::HttpRequestHeaders& authHeaders) const { AICLI_LOG(Repo, Info, << "Sending http GET request to: " << utility::conversions::to_utf8string(uri)); web::http::client::http_client client = GetClient(uri); web::http::http_request request{ web::http::methods::GET }; request.headers().set_content_type(web::http::details::mime_types::application_json); // Add headers for (auto& pair : headers) { request.headers().add(pair.first, pair.second); } EnsureDefaultUserAgent(request); AICLI_LOG(Repo, Verbose, << "Http GET request details:\n" << utility::conversions::to_utf8string(request.to_string())); // Add auth headers after logging for (auto& pair : authHeaders) { request.headers().add(pair.first, pair.second); } return client.request(request); } std::optional HttpClientHelper::HandleGet( const utility::string_t& uri, const HttpClientHelper::HttpRequestHeaders& headers, const HttpClientHelper::HttpRequestHeaders& authHeaders, const HttpResponseHandler& customHandler) const try { web::http::http_response httpResponse; Get(uri, headers, authHeaders).then([&httpResponse](const web::http::http_response& response) { httpResponse = response; }).wait(); if (customHandler) { auto handlerResult = customHandler(httpResponse); if (!handlerResult.UseDefaultHandling) { return std::move(handlerResult.Result); } } return ValidateAndExtractResponse(httpResponse); } catch (web::http::http_exception& exception) { RethrowAsWilException(exception); } void HttpClientHelper::SetPinningConfiguration(const Certificates::PinningConfiguration& configuration, std::shared_ptr threadGlobals) { m_clientConfig.set_nativehandle_servercertificate_validation([pinConfig = configuration, globals = std::move(threadGlobals)](web::http::client::native_handle handle) { NativeHandleServerCertificateValidation(handle, pinConfig, globals.get()); }); } web::http::client::http_client HttpClientHelper::GetClient(const utility::string_t& uri) const { web::http::client::http_client client{ uri, m_clientConfig }; // Add default custom handlers if any. if (m_defaultRequestHandlerStage) { client.add_handler(m_defaultRequestHandlerStage); } return client; } std::optional HttpClientHelper::ValidateAndExtractResponse(const web::http::http_response& response) const { AICLI_LOG(Repo, Info, << "Response status: " << response.status_code()); // Ensure that we wait for the content to be ready before we log it; otherwise it will be truncated. AICLI_LOG_LARGE_STRING(Repo, Verbose, << "Response details:", response.content_ready().then([&](const web::http::http_response&) { return utility::conversions::to_utf8string(response.to_string()); }).get()); std::optional result; switch (response.status_code()) { case web::http::status_codes::OK: result = ExtractJsonResponse(response); break; case web::http::status_codes::NotFound: THROW_HR(APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND); case web::http::status_codes::NoContent: result = {}; break; case web::http::status_codes::BadRequest: THROW_HR(APPINSTALLER_CLI_ERROR_RESTAPI_INTERNAL_ERROR); case web::http::status_codes::TooManyRequests: case web::http::status_codes::ServiceUnavailable: THROW_EXCEPTION(AppInstaller::Utility::ServiceUnavailableException(GetRetryAfter(response.headers()))); default: THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.status_code())); } return result; } std::optional HttpClientHelper::ExtractJsonResponse(const web::http::http_response& response) const { utility::string_t contentType = response.headers().content_type(); THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTAPI_UNSUPPORTED_MIME_TYPE, !contentType._Starts_with(web::http::details::mime_types::application_json)); return response.extract_json().get(); } [[noreturn]] void HttpClientHelper::RethrowAsWilException(const web::http::http_exception& exception) { // Some http_exceptions have no error code; default to REST internal error. HRESULT toThrow = APPINSTALLER_CLI_ERROR_RESTAPI_INTERNAL_ERROR; // 99% of the time this code comes from GetLastError. // In a few cases it will be 400; as in the HTTP status code. // Since that is the one case that http_client_winhttp.cpp uses, we map it specifically. // In the event that this makes no sense, ERROR_THREAD_MODE_ALREADY_BACKGROUND is Win32 error 400. int errorValue = exception.error_code().value(); if (errorValue == web::http::status_codes::BadRequest) { toThrow = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, web::http::status_codes::BadRequest); } else if (errorValue) { toThrow = HRESULT_FROM_WIN32(errorValue); } THROW_HR_MSG(toThrow, "%hs", exception.what()); } } ================================================ FILE: src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerStrings.h" #include "HttpClientWrapper.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerDownloader.h" using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Security::Cryptography; using namespace winrt::Windows::Storage; using namespace winrt::Windows::Storage::Streams; using namespace winrt::Windows::Web::Http; using namespace winrt::Windows::Web::Http::Headers; using namespace winrt::Windows::Web::Http::Filters; // Note: this class is used by the HttpRandomAccessStream which is passed to the AppxPackaging COM API // All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. // The HRESULTs will be mapped to UI error code by the appropriate component namespace AppInstaller::Utility::HttpStream { std::future> HttpClientWrapper::CreateAsync(const Uri& uri) { // TODO: Use proxy info. HttpClient does not support using a custom proxy, only using the system-wide one. std::shared_ptr instance = std::make_shared(); // Use an HTTP filter to disable the default caching behavior and use the Most Recent caching behavior instead // so we don't use a stale cached resource. Note: this wrapper object is used in the custom HTTP stream implementation // so this affects the parsing of HTTP-based packages/bundles. HttpBaseProtocolFilter filter; filter.CacheControl().ReadBehavior(HttpCacheReadBehavior::MostRecent); instance->m_httpClient = HttpClient(filter); instance->m_requestUri = uri; instance->m_httpClient.DefaultRequestHeaders().Connection().Clear(); instance->m_httpClient.DefaultRequestHeaders().Append(L"Connection", L"Keep-Alive"); instance->m_httpClient.DefaultRequestHeaders().UserAgent().ParseAdd(Utility::ConvertToUTF16(Runtime::GetDefaultUserAgent().get())); co_await instance->PopulateInfoAsync(); co_return instance; } // this function will issue a HEAD request to determine the size of the file and the redirect URI std::future HttpClientWrapper::PopulateInfoAsync() { HttpRequestMessage request(HttpMethod::Head(), m_requestUri); HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead); switch (response.StatusCode()) { case HttpStatusCode::Ok: // All good break; case HttpStatusCode::TooManyRequests: case HttpStatusCode::ServiceUnavailable: { THROW_EXCEPTION(ServiceUnavailableException(GetRetryAfter(response))); } default: THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.StatusCode())); } // Get the length from the response if (response.Content().Headers().HasKey(L"Content-Length")) { std::wstring contentLength(response.Content().Headers().Lookup(L"Content-Length")); m_sizeInBytes = std::stoll(contentLength); } else { m_sizeInBytes = 0; } // Get the extension from the redirect URI m_redirectUri = response.RequestMessage().RequestUri(); m_contentType = response.Content().Headers().HasKey(L"Content-Type") ? response.Content().Headers().Lookup(L"Content-Type") : L""; // If the size wasn't resolved try with a GET 0-0 request if (m_sizeInBytes == 0) { co_await SendHttpRequestAsync(0, 1); } } #ifdef WINGET_DISABLE_FOR_FUZZING #pragma warning( push ) #pragma warning( disable : 4714) // HRESULT_FROM_WIN32 marked as forceinline not inlined #endif std::future HttpClientWrapper::SendHttpRequestAsync( _In_ ULONG64 startPosition, _In_ UINT32 requestedSizeInBytes) { unsigned long long endPosition = 0; winrt::check_hresult(ULong64Add(startPosition, requestedSizeInBytes, &endPosition)); // Subtracting one should be safe, as the consumer of the stream should not request // an empty range, so this number can't go negative. endPosition -= 1; std::wstring rangeHeaderValue = L"bytes=" + std::to_wstring(startPosition) + L"-" + std::to_wstring(endPosition); HttpRequestMessage request(HttpMethod::Get(), m_requestUri); request.Headers().Append(L"Range", rangeHeaderValue); if (!Utility::IsEmptyOrWhitespace(m_etagHeader)) { request.Headers().Append(L"If-Match", m_etagHeader); } if (!Utility::IsEmptyOrWhitespace(m_lastModifiedHeader)) { request.Headers().Append(L"If-Unmodified-Since", m_lastModifiedHeader); } HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead); HttpContentHeaderCollection contentHeaders = response.Content().Headers(); switch (response.StatusCode()) { case HttpStatusCode::Ok: case HttpStatusCode::PartialContent: // All good break; case HttpStatusCode::TooManyRequests: case HttpStatusCode::ServiceUnavailable: { THROW_EXCEPTION(ServiceUnavailableException(GetRetryAfter(response))); } default: THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.StatusCode())); } if (response.StatusCode() != HttpStatusCode::PartialContent && startPosition != 0) { // throw HRESULT used for range-request error THROW_HR(HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED)); } if (response.Headers().HasKey(L"Accept-Ranges") && Utility::ToLower(std::wstring(response.Headers().Lookup(L"Accept-Ranges"))) == L"none") { // throw HRESULT used for range-request error THROW_HR(HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED)); } if (Utility::IsEmptyOrWhitespace(m_etagHeader) && response.Headers().HasKey(L"ETag")) { m_etagHeader = response.Headers().Lookup(L"ETag"); } if (Utility::IsEmptyOrWhitespace(m_lastModifiedHeader) && contentHeaders.HasKey(L"Last-Modified")) { m_lastModifiedHeader = contentHeaders.Lookup(L"Last-Modified"); } // If we don't know the size, parse it from the Content-Range field. if (m_sizeInBytes == 0 && contentHeaders.HasKey(L"Content-Range")) { // format: a-b/x where x is either a number or * std::wstring contentRange(contentHeaders.Lookup(L"Content-Range")); std::wstring length = contentRange.substr(contentRange.find(L"/") + 1); m_sizeInBytes = (length == L"*") ? 0 : std::stoll(length); } co_return co_await response.Content().ReadAsBufferAsync(); } #ifdef WINGET_DISABLE_FOR_FUZZING #pragma warning( pop ) #endif std::future HttpClientWrapper::DownloadRangeAsync( const ULONG64 startPosition, const UINT32 requestedSizeInBytes, const InputStreamOptions& options) { std::vector byteArray(requestedSizeInBytes); IBuffer buffer = CryptographicBuffer::CreateFromByteArray(byteArray); co_return co_await SendHttpRequestAsync(startPosition, requestedSizeInBytes); } } ================================================ FILE: src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Utility::HttpStream { // Wrapper around HTTP client. When created, an object of this class will send a HTTP // head request to determine the size of the data source. class HttpClientWrapper { public: static std::future> CreateAsync(const winrt::Windows::Foundation::Uri& uri); std::future DownloadRangeAsync( const ULONG64 startPosition, const UINT32 requestedSizeInBytes, const winrt::Windows::Storage::Streams::InputStreamOptions& options); unsigned long long GetFullFileSize() { return m_sizeInBytes; } winrt::Windows::Foundation::Uri GetRedirectUri() { return m_redirectUri; } std::wstring GetContentType() { return m_contentType; } private: winrt::Windows::Web::Http::HttpClient m_httpClient; winrt::Windows::Foundation::Uri m_requestUri = nullptr; winrt::Windows::Foundation::Uri m_redirectUri = nullptr; std::wstring m_contentType; unsigned long long m_sizeInBytes = 0; std::wstring m_etagHeader; std::wstring m_lastModifiedHeader; std::future PopulateInfoAsync(); std::future SendHttpRequestAsync( _In_ ULONG64 startPosition, _In_ UINT32 requestedSizeInBytes); }; } ================================================ FILE: src/AppInstallerCommonCore/HttpStream/HttpLocalCache.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "HttpLocalCache.h" using namespace Windows::Storage::Streams; using namespace winrt::Windows::Storage::Streams; using namespace winrt::Windows::Security::Cryptography; // Note: this class is used by the HttpRandomAccessStream which is passed to the AppxPackaging COM API // All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. // The HRESULTs will be mapped to UI error code by the appropriate component namespace AppInstaller::Utility::HttpStream { std::future HttpLocalCache::ReadFromCacheAndDownloadIfNecessaryAsync( const ULONG64 requestedPosition, const UINT32 requestedSize, HttpClientWrapper* httpClientWrapper, InputStreamOptions httpInputStreamOptions) { // Increment cache access counter user for implementing LRU replacement m_accessCounter++; // Find all the pages for the given request, and the pages that are missing std::vector allPages; std::vector unsatisfiablePages; FindCachePages(requestedPosition, requestedSize, allPages, unsatisfiablePages); // download the missing pages co_await DownloadAndSaveToCacheAsync( unsatisfiablePages, httpClientWrapper, httpInputStreamOptions); // At this point, everything should be in the cache IBuffer constructedBuffer = {}; for (UINT32 i = 0; i < allPages.size(); i++) { UINT64 pageOffset = allPages[i]; IBuffer cachedPageBuffer = ReadPageFromCache(pageOffset); constructedBuffer = ConcatenateBuffers(constructedBuffer, cachedPageBuffer); } // trim buffer to match requested range IBuffer requestedBuffer = TrimBufferToSatisfyRequest( constructedBuffer, requestedPosition, requestedSize, allPages); VacateStaleEntriesFromCache(); co_return requestedBuffer; } void HttpLocalCache::FindCachePages( ULONG64 requestedPosition, UINT32 requestedSize, std::vector& allPages, std::vector& unsatisfiablePages) { ULONG64 requestedEndPosition; ULONG64 currentPageOffset; winrt::check_hresult(ULong64Add(requestedPosition, requestedSize, &requestedEndPosition)); winrt::check_hresult(ULong64Mult((requestedPosition / PAGE_SIZE), PAGE_SIZE, ¤tPageOffset)); // There's always at least one page for the range do { allPages.push_back(currentPageOffset); if (m_localCache.find(currentPageOffset) == m_localCache.end()) { unsatisfiablePages.push_back(currentPageOffset); } winrt::check_hresult(ULong64Add(currentPageOffset, PAGE_SIZE, ¤tPageOffset)); } while (currentPageOffset < requestedEndPosition); } // Breaks the provided buffer into smaller buffers and saves them to the cache at the corresponding // page offset position, starting at firstPageOffset. The smaller buffers are all PAGE_SIZE bytes, // except for the one corresponding to the last page in the file void HttpLocalCache::SaveBufferToCache(const IBuffer& buffer, const ULONG64 firstPageOffset) { UINT32 remainingBufferSize = buffer.Length(); UINT32 currentBufferIndex = 0; ULONG64 currentPageOffset = firstPageOffset; while (remainingBufferSize > 0) { // Extract the sub-buffer UINT32 currentPageSize = std::min(remainingBufferSize, PAGE_SIZE); IBuffer currentPageBuffer = CreateTrimmedBuffer(buffer, currentBufferIndex, currentPageSize); // Add it to the cache CachedPage currentPage; currentPage.lastAccessCounter = m_accessCounter; currentPage.buffer = currentPageBuffer; m_localCache[currentPageOffset] = currentPage; // update loop vars winrt::check_hresult(UInt32Sub(remainingBufferSize, currentPageSize, &remainingBufferSize)); winrt::check_hresult(UInt32Add(currentBufferIndex, currentPageSize, ¤tBufferIndex)); winrt::check_hresult(ULong64Add(currentPageOffset, PAGE_SIZE, ¤tPageOffset)); } } IBuffer HttpLocalCache::ReadPageFromCache(const ULONG64 pageOffset) { if (!(m_localCache.find(pageOffset) != m_localCache.end())) { THROW_HR(E_INVALIDARG); } CachedPage& page = m_localCache[pageOffset]; page.lastAccessCounter = m_accessCounter; return page.buffer; } // Trims a buffer that was constructed (by fetching pages from cache and downloading missing pages) // in order to satisfy a request and return the exact buffer the consumer asked for. IBuffer HttpLocalCache::TrimBufferToSatisfyRequest( const IBuffer& constructedBuffer, const ULONG64 requestedPosition, const UINT32 requestedSize, const std::vector allPages) { ULONG64 fullBufferStartOffset = allPages[0]; ULONG64 trimmedBufferStartRelativeIndex; winrt::check_hresult(ULong64Sub(requestedPosition, fullBufferStartOffset, &trimmedBufferStartRelativeIndex)); IBuffer requestedBuffer = CreateTrimmedBuffer( constructedBuffer, (UINT32)trimmedBufferStartRelativeIndex, // Conversion is safe as buffer size is a UINT32. requestedSize); return requestedBuffer; } // Downloads a chunk of the file, saves it to the cache, and returns the corresponding buffer // If the requested size is 0, this method returns an empty buffer without making HTTP calls std::future HttpLocalCache::DownloadAndSaveToCacheAsync( const std::vector unsatisfiablePages, HttpClientWrapper* httpClientWrapper, InputStreamOptions httpInputStreamOptions) { // Determine the download job // To make things easy, we will download the contiguous range that includes all the unsatisfiable ranges. // Note that in theory, this may include cached pages. However, this situation is rarely expected to happen, // if at all. The package reader usually reads things in chunks of 64 KB or less, so, we should expect to // always have up to two satisfiable and unsatisfiable pages in total. UINT64 fileSize = httpClientWrapper->GetFullFileSize(); ULONG64 downloadJobStartPosition = 0U; ULONG64 downloadJobEndPosition = 0U; ULONG64 downloadJobSize = 0U; if (unsatisfiablePages.size() > 0U) { downloadJobStartPosition = unsatisfiablePages[0]; ULONG64 lastUnsatisfiableJob = unsatisfiablePages[unsatisfiablePages.size() - 1]; winrt::check_hresult(ULong64Add(lastUnsatisfiableJob, PAGE_SIZE, &downloadJobEndPosition)); // make sure to not overflow file size downloadJobEndPosition = std::min(downloadJobEndPosition, fileSize); winrt::check_hresult(ULong64Sub(downloadJobEndPosition, downloadJobStartPosition, &downloadJobSize)); } if (downloadJobSize != 0U) { // start download job IBuffer downloadedBuffer = co_await httpClientWrapper->DownloadRangeAsync( downloadJobStartPosition, (UINT32)downloadJobSize, httpInputStreamOptions); SaveBufferToCache(downloadedBuffer, downloadJobStartPosition); } } void HttpLocalCache::VacateStaleEntriesFromCache() { // Copy page offsets into vector and sort by the access counter std::vector> orderedPageOffsets; for (auto pageIter = m_localCache.begin(); pageIter != m_localCache.end(); pageIter++) { orderedPageOffsets.push_back(std::pair(pageIter->first, pageIter->second.lastAccessCounter)); } // Compare function to sort by access counter auto cmp = [](std::pair const & a, std::pair const & b) { return a.second != b.second ? a.second < b.second : a.first < b.first; }; std::sort(orderedPageOffsets.begin(), orderedPageOffsets.end(), cmp); for (auto pageIter = orderedPageOffsets.begin(); pageIter != orderedPageOffsets.end(); pageIter++) { if (m_localCache.size() > MAX_PAGES) { m_localCache.erase(pageIter->first); } else { break; } } } IBuffer HttpLocalCache::CreateTrimmedBuffer( const IBuffer& originalBuffer, UINT32 trimStartIndex, UINT32 size) { uint32_t bufferLength = originalBuffer.Length(); THROW_HR_IF(E_INVALIDARG, trimStartIndex > bufferLength); originalBuffer.as<::IInspectable>(); // Get the byte array from the IBuffer object Microsoft::WRL::ComPtr bufferByteAccess; ::IInspectable* bufferAbi = (::IInspectable*)winrt::get_abi(originalBuffer); bufferAbi->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); byte* byteBuffer = nullptr; bufferByteAccess->Buffer(&byteBuffer); // Create the array of bytes holding the trimmed bytes IBuffer trimmedBuffer = CryptographicBuffer::CreateFromByteArray( { byteBuffer + trimStartIndex, std::min(size, bufferLength - trimStartIndex) }); return trimmedBuffer; } IBuffer HttpLocalCache::ConcatenateBuffers(const IBuffer& buffer1, const IBuffer& buffer2) { DataWriter writer; writer.WriteBuffer(buffer1); writer.WriteBuffer(buffer2); return writer.DetachBuffer(); } } ================================================ FILE: src/AppInstallerCommonCore/HttpStream/HttpLocalCache.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "HttpClientWrapper.h" namespace AppInstaller::Utility::HttpStream { // Represents an entry in the cache. struct CachedPage { int lastAccessCounter = 0; winrt::Windows::Storage::Streams::IBuffer buffer; }; // A cache used internally by the custom HttpRandomAccessStream to reduce round-trips class HttpLocalCache { public: static constexpr UINT32 PAGE_SIZE = 2 << 16; // each entry in the cache is 64 KB static constexpr UINT32 MAX_PAGES = 200; // cache size capped at 12.5 MB (200 * 64KB) // Returns a buffer matching the requested range by reading the parts of the range that are cached // and downloading the rest using the provided httpClientWrapper object std::future ReadFromCacheAndDownloadIfNecessaryAsync( const ULONG64 requestedPosition, const UINT32 requestedSize, HttpClientWrapper* httpClientWrapper, winrt::Windows::Storage::Streams::InputStreamOptions httpInputStreamOptions); private: std::map m_localCache; UINT32 m_accessCounter = 0U; // Returns a vector of all pages corresponding to a range, and another (subset) // vector of the pages missing from the cache. void FindCachePages( const ULONG64 requestedPosition, const UINT32 requestedSize, std::vector& allPages, std::vector& unsatisfiablePages); void SaveBufferToCache(const winrt::Windows::Storage::Streams::IBuffer& buffer, const ULONG64 firstPageOffset); winrt::Windows::Storage::Streams::IBuffer ReadPageFromCache(const ULONG64 pageOffset); void VacateStaleEntriesFromCache(); std::future DownloadAndSaveToCacheAsync( const std::vector unsatisfiablePages, HttpClientWrapper* httpClientWrapper, const winrt::Windows::Storage::Streams::InputStreamOptions httpInputStreamOptions); winrt::Windows::Storage::Streams::IBuffer TrimBufferToSatisfyRequest( const winrt::Windows::Storage::Streams::IBuffer& constructedBuffer, const ULONG64 requestedPosition, const UINT32 requestedSize, const std::vector allPages); winrt::Windows::Storage::Streams::IBuffer CreateTrimmedBuffer( const winrt::Windows::Storage::Streams::IBuffer& originalBuffer, UINT32 trimStartIndex, UINT32 size); winrt::Windows::Storage::Streams::IBuffer ConcatenateBuffers( const winrt::Windows::Storage::Streams::IBuffer& buffer1, const winrt::Windows::Storage::Streams::IBuffer& buffer2); }; } ================================================ FILE: src/AppInstallerCommonCore/HttpStream/HttpRandomAccessStream.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "HttpRandomAccessStream.h" #include "Public/AppInstallerDownloader.h" using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Storage::Streams; // Note: the HttpRandomAccessStream is passed to the AppxPackaging COM API // All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. // The HRESULTs will be mapped to UI error code by the appropriate component namespace AppInstaller::Utility::HttpStream { IAsyncOperation HttpRandomAccessStream::InitializeAsync(const Uri& uri) { auto strong_this{ get_strong() }; try { strong_this->m_httpHelper = co_await HttpClientWrapper::CreateAsync(uri); strong_this->m_size = strong_this->m_httpHelper->GetFullFileSize(); strong_this->m_httpLocalCache = std::make_unique(); } catch (const ServiceUnavailableException& e) { strong_this->m_retryAfter = e.RetryAfter(); throw; } co_return strong_this.as(); } uint64_t HttpRandomAccessStream::Size() const { return m_size; } void HttpRandomAccessStream::Size(uint64_t value) { UNREFERENCED_PARAMETER(value); THROW_HR(E_NOTIMPL); } uint64_t HttpRandomAccessStream::Position() const { return m_requestedPosition; } bool HttpRandomAccessStream::CanRead() const { return true; } bool HttpRandomAccessStream::CanWrite() const { return false; } IInputStream HttpRandomAccessStream::GetInputStreamAt(uint64_t position) const { UNREFERENCED_PARAMETER(position); THROW_HR(E_NOTIMPL); } IOutputStream HttpRandomAccessStream::GetOutputStreamAt(uint64_t position) const { UNREFERENCED_PARAMETER(position); THROW_HR(E_NOTIMPL); } IRandomAccessStream HttpRandomAccessStream::CloneStream() const { THROW_HR(E_NOTIMPL); } void HttpRandomAccessStream::Seek(uint64_t position) { m_requestedPosition = position; } IAsyncOperationWithProgress HttpRandomAccessStream::ReadAsync( IBuffer buffer, uint32_t count, InputStreamOptions options) { IBuffer result = co_await m_httpLocalCache->ReadFromCacheAndDownloadIfNecessaryAsync( m_requestedPosition, count, m_httpHelper.get(), options); winrt::check_hresult(ULong64Add(m_requestedPosition, result.Length(), &m_requestedPosition)); co_return result; } std::chrono::seconds HttpRandomAccessStream::RetryAfter() const { return m_retryAfter; } } ================================================ FILE: src/AppInstallerCommonCore/HttpStream/HttpRandomAccessStream.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "HttpClientWrapper.h" #include "HttpLocalCache.h" using namespace std::chrono_literals; namespace AppInstaller::Utility::HttpStream { // Provides an implementation of a random access stream over HTTP that supports // range-based fetching. This is intended to be used by AppxPackageReader. // // Note: If the server doesn't support HTTP ranges, this implementation will throw an exception. class HttpRandomAccessStream : public winrt::implements< HttpRandomAccessStream, winrt::Windows::Storage::Streams::IRandomAccessStream, winrt::Windows::Storage::Streams::IInputStream> { public: winrt::Windows::Foundation::IAsyncOperation InitializeAsync(const winrt::Windows::Foundation::Uri& uri); uint64_t Size() const; void Size(uint64_t value); uint64_t Position() const; bool CanRead() const; bool CanWrite() const; winrt::Windows::Storage::Streams::IInputStream GetInputStreamAt(uint64_t position) const; winrt::Windows::Storage::Streams::IOutputStream GetOutputStreamAt(uint64_t position) const; winrt::Windows::Storage::Streams::IRandomAccessStream CloneStream() const; void Seek(uint64_t position); winrt::Windows::Foundation::IAsyncOperationWithProgress ReadAsync( winrt::Windows::Storage::Streams::IBuffer buffer, uint32_t count, winrt::Windows::Storage::Streams::InputStreamOptions options); std::chrono::seconds RetryAfter() const; private: std::shared_ptr m_httpHelper; std::unique_ptr m_httpLocalCache; unsigned long long m_size = 0; unsigned long long m_requestedPosition = 0; std::chrono::seconds m_retryAfter = 0s; }; } ================================================ FILE: src/AppInstallerCommonCore/Locale.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/Locale.h" #include "AppInstallerStrings.h" #include "AppInstallerLogging.h" namespace AppInstaller::Locale { namespace { constexpr int MAX_LOCALE_SNAME_LEN = 85; // We will just leak this. The module is shared as both functions will always be together. HMODULE g_bcp47 = (HMODULE)(-1); typedef bool(WINAPI* IsWellFormedTagFunc)(PCWSTR); typedef HRESULT(WINAPI* GetDistanceOfClosestLanguageInListFunc)(PCWSTR, PCWSTR, wchar_t, double*); HMODULE LoadBcp47ModuleFrom(_In_ PCWSTR moduleName) { HMODULE module = LoadLibraryExW(moduleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); if (module != nullptr) { // All BCP47 APIs we are interested are always exported from the same dll together. So we just pick anyone for probe. IsWellFormedTagFunc func = (IsWellFormedTagFunc)(GetProcAddress(module, "IsWellFormedTag")); if (func != nullptr) { return module; } FreeLibrary(module); } return nullptr; } HMODULE LoadBcp47Module() { HMODULE module = LoadBcp47ModuleFrom(L"bcp47mrm.dll"); if (module == nullptr) { // In downlevel OS, the API is exposed by bcp47langs.dll. module = LoadBcp47ModuleFrom(L"bcp47langs.dll"); } return module; } void InitializeBcp47Module() { HMODULE comparand = (HMODULE)(-1); if (InterlockedCompareExchangePointer(reinterpret_cast(&g_bcp47), comparand, comparand) == comparand) { HMODULE module = LoadBcp47Module(); InterlockedExchangePointer(reinterpret_cast(&g_bcp47), module); } } } bool IsWellFormedBcp47Tag(std::string_view bcp47Tag) { // Before new SDK is released, we need to use LoadLibrary/GetProcAddress InitializeBcp47Module(); if (g_bcp47 == nullptr) { // Didn't find an implementation. Just return true. AICLI_LOG(Core, Warning, << "bcp47 module not found."); return true; } IsWellFormedTagFunc func = (IsWellFormedTagFunc)(GetProcAddress(g_bcp47, "IsWellFormedTag")); if (func != nullptr) { auto wBcp47Tag = Utility::ConvertToUTF16(bcp47Tag); return func(wBcp47Tag.c_str()); } // Should not reach here. return TRUE; } double GetDistanceOfLanguage(std::string_view target, std::string_view available) { // Before new SDK is released, we need to use LoadLibrary/GetProcAddress InitializeBcp47Module(); if (g_bcp47 == nullptr) { // Didn't find an implementation. Just return 0 as no match. AICLI_LOG(Core, Warning, << "bcp47 module not found."); return 0; } GetDistanceOfClosestLanguageInListFunc func = (GetDistanceOfClosestLanguageInListFunc)(GetProcAddress(g_bcp47, "GetDistanceOfClosestLanguageInList")); if (func != nullptr) { double distance = 0; auto wTarget = Utility::ConvertToUTF16(target); auto wAvailable = Utility::ConvertToUTF16(available); // Do not check HRESULT because the method returns ERROR_NO_MATCH on no match, which is a valid case. (void)func(wTarget.c_str(), wAvailable.c_str(), L';' /* Not used, we compare one at a time */, &distance); return distance; } // Should not reach here. return 0; } std::vector GetUserPreferredLanguages() { std::vector result; for (const auto& lang : winrt::Windows::System::UserProfile::GlobalizationPreferences::Languages()) { result.emplace_back(Utility::ConvertToUTF8(lang)); } return result; } std::vector GetUserPreferredLanguagesUTF16() { std::vector result; for (const auto& lang : winrt::Windows::System::UserProfile::GlobalizationPreferences::Languages()) { result.emplace_back(std::wstring(lang)); } return result; } std::string LocaleIdToBcp47Tag(LCID localeId) { WCHAR localeName[MAX_LOCALE_SNAME_LEN] = {0}; int ret = LCIDToLocaleName( localeId, localeName, MAX_LOCALE_SNAME_LEN, LOCALE_ALLOW_NEUTRAL_NAMES); if (ret <= 0) { return {}; } return Utility::ConvertToUTF8(std::wstring(localeName)); } } ================================================ FILE: src/AppInstallerCommonCore/MSStore.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include #include #include namespace AppInstaller::MSStore { using namespace std::string_view_literals; using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::ApplicationModel::Store::Preview::InstallControl; namespace { // The type of entitlement we were able to acquire/ensure. enum class EntitlementType { None, User, Device, }; EntitlementType EnsureFreeEntitlement(const std::wstring& productId, Manifest::ScopeEnum scope) { AppInstallManager installManager; AICLI_LOG(Core, Info, << "Getting entitlement for ProductId: " << Utility::ConvertToUTF8(productId)); // Verifying/Acquiring product ownership GetEntitlementResult entitlementResult{ nullptr }; EntitlementType result = EntitlementType::None; if (scope == Manifest::ScopeEnum::Machine) { AICLI_LOG(Core, Info, << "Get device entitlement (machine scope install)."); result = EntitlementType::Device; try { entitlementResult = installManager.GetFreeDeviceEntitlementAsync(productId, winrt::hstring(), winrt::hstring()).get(); } CATCH_LOG(); } else { AICLI_LOG(Core, Info, << "Get user entitlement."); result = EntitlementType::User; try { entitlementResult = installManager.GetFreeUserEntitlementAsync(productId, winrt::hstring(), winrt::hstring()).get(); } CATCH_LOG(); if (!entitlementResult || entitlementResult.Status() == GetEntitlementStatus::NoStoreAccount) { AICLI_LOG(Core, Info, << "Get device entitlement (no store account)."); result = EntitlementType::Device; try { entitlementResult = installManager.GetFreeDeviceEntitlementAsync(productId, winrt::hstring(), winrt::hstring()).get(); } CATCH_LOG(); } } if (entitlementResult && entitlementResult.Status() == GetEntitlementStatus::Succeeded) { AICLI_LOG(Core, Info, << "Get entitlement succeeded."); } else if (entitlementResult) { result = EntitlementType::None; if (entitlementResult.Status() == GetEntitlementStatus::NetworkError) { AICLI_LOG(Core, Error, << "Get entitlement failed. Network error."); } else if (entitlementResult.Status() == GetEntitlementStatus::ServerError) { AICLI_LOG(Core, Error, << "Get entitlement failed. Server error."); } else { AICLI_LOG(Core, Error, << "Get entitlement failed. Unknown status: " << static_cast(entitlementResult.Status())); } } else { result = EntitlementType::None; AICLI_LOG(Core, Error, << "Get entitlement failed. Exception."); } return result; } enum class CheckExistingItemResult { None, Restart, Cancel, }; CheckExistingItemResult CheckRestartOrCancelForPossibleExistingOperation(const IVectorView& installItems) { CheckExistingItemResult result = CheckExistingItemResult::None; for (auto const& installItem : installItems) { const auto& status = installItem.GetCurrentStatus(); switch (status.InstallState()) { case AppInstallState::Canceled: case AppInstallState::Error: // For these states, always do a cancel; result = CheckExistingItemResult::Cancel; return result; case AppInstallState::Paused: case AppInstallState::PausedLowBattery: case AppInstallState::PausedWiFiRecommended: case AppInstallState::PausedWiFiRequired: case AppInstallState::ReadyToDownload: // For these states, set result to restart and continue the loop to see if future items need cancel. result = CheckExistingItemResult::Restart; break; } } return result; } bool DoesInstallItemsContainProduct(const IVectorView& installItems, std::wstring_view productId) { for (auto const& installItem : installItems) { if (Utility::CaseInsensitiveEquals(installItem.ProductId(), productId)) { return true; } } return false; } // Returns true if Restart or Cancel happened. False otherwise. HRESULT RestartOrCancelExistingOperationIfNecessary(const IVectorView& installItems, AppInstallManager& installManager, std::wstring_view productId) { auto existingItemResult = CheckRestartOrCancelForPossibleExistingOperation(installItems); if (existingItemResult == CheckExistingItemResult::Cancel || existingItemResult == CheckExistingItemResult::Restart) { if (existingItemResult == CheckExistingItemResult::Cancel) { installManager.Cancel(productId); // Wait for at most 10 seconds for install item to be removed from queue. for (int i = 0; i < 50; ++i) { Sleep(200); if (!DoesInstallItemsContainProduct(installManager.AppInstallItems(), productId)) { return S_OK; } } RETURN_HR(HRESULT_FROM_WIN32(ERROR_TIMEOUT)); } else { installManager.Restart(productId); return S_OK; } } return S_FALSE; } // Used to detect a signal that a package update is being requested so that we can early out // on an attempt to update ourself. This is only needed for elevated processes because the // standard shutdown signals are not sent to elevated processes in the same manner. struct PackageUpdateMonitor { PackageUpdateMonitor() { if (Runtime::IsRunningAsAdmin() && Runtime::IsRunningInPackagedContext()) { m_catalog = winrt::Windows::ApplicationModel::PackageCatalog::OpenForCurrentPackage(); m_updatingEvent = m_catalog.PackageUpdating( winrt::auto_revoke, [this](winrt::Windows::ApplicationModel::PackageCatalog, winrt::Windows::ApplicationModel::PackageUpdatingEventArgs args) { // Deployment always sends a value of 0 before doing any work and a value of 100 when completely done. constexpr double minProgress = 0; auto progress = args.Progress(); if (progress > minProgress) { m_isUpdating = true; } }); } } bool IsUpdating() const { return m_isUpdating; } private: winrt::Windows::ApplicationModel::PackageCatalog m_catalog = nullptr; decltype(winrt::Windows::ApplicationModel::PackageCatalog{ nullptr }.PackageUpdating(winrt::auto_revoke, nullptr)) m_updatingEvent; std::atomic_bool m_isUpdating = false; }; HRESULT WaitForOperation(const std::wstring& productId, bool isSilentMode, IVectorView& installItems, IProgressCallback& progress, const PackageUpdateMonitor& monitor) { auto cancelIfOperationFailed = wil::scope_exit( [&]() { try { AppInstallManager installManager; installManager.Cancel(productId); } CATCH_LOG(); }); for (auto const& installItem : installItems) { AICLI_LOG(Core, Info, << "Started MSStore package execution. ProductId: " << Utility::ConvertToUTF8(installItem.ProductId()) << " PackageFamilyName: " << Utility::ConvertToUTF8(installItem.PackageFamilyName())); if (isSilentMode) { installItem.InstallInProgressToastNotificationMode(AppInstallationToastNotificationMode::NoToast); installItem.CompletedInstallToastNotificationMode(AppInstallationToastNotificationMode::NoToast); } } HRESULT errorCode = S_OK; // We are aggregating all AppInstallItem progresses into one. // Averaging every progress for now until we have a better way to find overall progress. uint64_t overallProgressMax = 100 * static_cast(installItems.Size()); uint64_t currentProgress = 0; while (currentProgress < overallProgressMax) { currentProgress = 0; for (auto const& installItem : installItems) { const auto& status = installItem.GetCurrentStatus(); currentProgress += static_cast(status.PercentComplete()); errorCode = status.ErrorCode(); if (!SUCCEEDED(errorCode)) { return errorCode; } } // It may take a while for Store client to pick up the install request. // So we show indefinite progress here to avoid a progress bar stuck at 0. if (currentProgress > 0) { progress.OnProgress(currentProgress, overallProgressMax, ProgressType::Percent); } if (progress.IsCancelledBy(CancelReason::User)) { for (auto const& installItem : installItems) { installItem.Cancel(); } } // If app shutdown then we have 30s to keep installing, keep going and hope for the best. else if (progress.IsCancelledBy(CancelReason::AppShutdown) || monitor.IsUpdating()) { for (auto const& installItem : installItems) { // Insert spiderman meme. if (installItem.ProductId() == std::wstring{ s_AppInstallerProductId }) { AICLI_LOG(Core, Info, << "Asked to shutdown while installing AppInstaller."); progress.OnProgress(overallProgressMax, overallProgressMax, ProgressType::Percent); cancelIfOperationFailed.release(); return S_OK; } } } Sleep(100); } if (SUCCEEDED(errorCode)) { cancelIfOperationFailed.release(); } return errorCode; } } HRESULT MSStoreOperation::StartAndWaitForOperation(IProgressCallback& progress) { // Best effort verifying/acquiring product ownership. std::ignore = EnsureFreeEntitlement(m_productId, m_scope); if (m_type == MSStoreOperationType::Update) { return UpdatePackage(progress); } else { return InstallPackage(progress); } } HRESULT MSStoreOperation::InstallPackage(IProgressCallback& progress) { PackageUpdateMonitor monitor; AppInstallManager installManager; AppInstallOptions installOptions; installOptions.AllowForcedAppRestart(m_force); if (m_isSilentMode) { installOptions.InstallInProgressToastNotificationMode(AppInstallationToastNotificationMode::NoToast); installOptions.CompletedInstallToastNotificationMode(AppInstallationToastNotificationMode::NoToast); } if (m_type == MSStoreOperationType::Repair) { // Attempt to repair the installation of an app that is already installed. installOptions.Repair(true); } if (m_scope == Manifest::ScopeEnum::Machine) { // TODO: There was a bug in InstallService where admin user is incorrectly identified as not admin, // causing false access denied on many OS versions. // Remove this check when the OS bug is fixed and back ported. if (!Runtime::IsRunningAsSystem()) { AICLI_LOG(Core, Error, << "Device wide install for msstore type is not supported under admin context."); return APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED; } installOptions.InstallForAllUsers(true); } IVectorView installItems = installManager.StartProductInstallAsync( m_productId, // ProductId winrt::hstring(), // FlightId L"WinGetCli", // ClientId winrt::hstring(), installOptions).get(); // Check if we need to restart or cancel existing items. auto restartOrCancelResult = RestartOrCancelExistingOperationIfNecessary(installItems, installManager, m_productId); RETURN_IF_FAILED(restartOrCancelResult); // If restart or cancel happened, try again. if (restartOrCancelResult == S_OK) { // Try again installItems = installManager.StartProductInstallAsync( m_productId, // ProductId winrt::hstring(), // FlightId L"WinGetCli", // ClientId winrt::hstring(), installOptions).get(); } return WaitForOperation(m_productId, m_isSilentMode, installItems, progress, monitor); } HRESULT MSStoreOperation::UpdatePackage(IProgressCallback& progress) { PackageUpdateMonitor monitor; AppInstallManager installManager; AppUpdateOptions updateOptions; updateOptions.AllowForcedAppRestart(m_force); // SearchForUpdateAsync will automatically trigger update if found. AppInstallItem installItem = installManager.SearchForUpdatesAsync( m_productId, // ProductId winrt::hstring(), // SkuId winrt::hstring(), winrt::hstring(), // ClientId updateOptions ).get(); if (!installItem) { return APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE; } std::vector installItemVector{ installItem }; IVectorView installItems = winrt::single_threaded_vector(std::move(installItemVector)).GetView(); // Check if we need to restart or cancel existing items. auto restartOrCancelResult = RestartOrCancelExistingOperationIfNecessary(installItems, installManager, m_productId); RETURN_IF_FAILED(restartOrCancelResult); // If restart or cancel happened, try again. if (restartOrCancelResult == S_OK) { // Try again installItem = installManager.SearchForUpdatesAsync( m_productId, // ProductId winrt::hstring(), // SkuId winrt::hstring(), winrt::hstring(), // ClientId updateOptions ).get(); if (!installItem) { return APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE; } installItemVector.clear(); installItemVector.emplace_back(installItem); installItems = winrt::single_threaded_vector(std::move(installItemVector)).GetView(); } return WaitForOperation(m_productId, m_isSilentMode, installItems, progress, monitor); } } ================================================ FILE: src/AppInstallerCommonCore/MSStoreDownload.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include "AppInstallerMsixInfo.h" #include "AppInstallerRuntime.h" #include "winget/HttpClientHelper.h" #include "winget/JsonUtil.h" #include "winget/Locale.h" #include "winget/MSStoreDownload.h" #include "winget/NetworkSettings.h" #include "winget/Rest.h" #include "winget/UserSettings.h" #ifndef WINGET_DISABLE_FOR_FUZZING #include #endif namespace AppInstaller::MSStore { using namespace std::string_view_literals; #ifndef AICLI_DISABLE_TEST_HOOKS namespace TestHooks { static std::shared_ptr s_DisplayCatalog_HttpPipelineStage_Override = nullptr; void SetDisplayCatalogHttpPipelineStage_Override(std::shared_ptr value) { s_DisplayCatalog_HttpPipelineStage_Override = value; } static std::function(std::string_view)>* s_SfsClient_AppContents_Override = nullptr; void SetSfsClientAppContents_Override(std::function(std::string_view)>* value) { s_SfsClient_AppContents_Override = value; } static std::shared_ptr s_Licensing_HttpPipelineStage_Override = nullptr; void SetLicensingHttpPipelineStage_Override(std::shared_ptr value) { s_Licensing_HttpPipelineStage_Override = value; } } #endif namespace DisplayCatalogDetails { // Default preferred sku to use constexpr std::string_view TargetSkuIdValue = "0015"sv; // Json response fields constexpr std::string_view Product = "Product"sv; constexpr std::string_view DisplaySkuAvailabilities = "DisplaySkuAvailabilities"sv; constexpr std::string_view Sku = "Sku"sv; constexpr std::string_view SkuId = "SkuId"sv; constexpr std::string_view Properties = "Properties"sv; constexpr std::string_view Packages = "Packages"sv; constexpr std::string_view Languages = "Languages"sv; constexpr std::string_view PackageFormat = "PackageFormat"sv; constexpr std::string_view PackageId = "PackageId"sv; constexpr std::string_view Architectures = "Architectures"sv; constexpr std::string_view ContentId = "ContentId"sv; constexpr std::string_view FulfillmentData = "FulfillmentData"sv; constexpr std::string_view WuCategoryId = "WuCategoryId"sv; // Display catalog rest endpoint constexpr std::string_view DisplayCatalogRestApi = R"(https://displaycatalog.mp.microsoft.com/v7.0/products/{0}?fieldsTemplate={1}&market={2}&languages={3}&catalogIds={4})"; constexpr std::string_view Details = "Details"sv; constexpr std::string_view Neutral = "Neutral"sv; constexpr std::string_view TargetCatalogId = "4"sv; enum class DisplayCatalogPackageFormatEnum { Unknown, AppxBundle, MsixBundle, Appx, Msix, }; DisplayCatalogPackageFormatEnum ConvertToPackageFormatEnum(std::string_view packageFormatStr) { std::string packageFormat = Utility::ToLower(packageFormatStr); if (packageFormat == "appxbundle") { return DisplayCatalogPackageFormatEnum::AppxBundle; } else if (packageFormat == "msixbundle") { return DisplayCatalogPackageFormatEnum::MsixBundle; } else if (packageFormat == "appx") { return DisplayCatalogPackageFormatEnum::Appx; } else if (packageFormat == "msix") { return DisplayCatalogPackageFormatEnum::Msix; } AICLI_LOG(Core, Info, << "ConvertToPackageFormatEnum: Unknown package format: " << packageFormatStr); return DisplayCatalogPackageFormatEnum::Unknown; } struct DisplayCatalogPackage { std::string PackageId; std::vector Architectures; std::vector Languages; DisplayCatalogPackageFormatEnum PackageFormat = DisplayCatalogPackageFormatEnum::Unknown; // To be used later in sfs-client std::string WuCategoryId; // To be used later in licensing std::string ContentId; }; // Display catalog package comparison logic. // The comparator follows similar logic as ManifestComparator. namespace DisplayCatalogPackageComparison { struct DisplayCatalogPackageComparisonField { DisplayCatalogPackageComparisonField(std::string_view name) : m_name(name) {} virtual ~DisplayCatalogPackageComparisonField() = default; std::string_view Name() const { return m_name; } virtual bool IsApplicable(const DisplayCatalogPackage& package) = 0; virtual bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second) = 0; private: std::string_view m_name; }; struct PackageFormatComparator : public DisplayCatalogPackageComparisonField { PackageFormatComparator() : DisplayCatalogPackageComparisonField("Package Format") {} bool IsApplicable(const DisplayCatalogPackage& package) override { return package.PackageFormat != DisplayCatalogPackageFormatEnum::Unknown; } bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second) override { return IsPackageFormatBundle(first) && !IsPackageFormatBundle(second); } private: bool IsPackageFormatBundle(const DisplayCatalogPackage& package) { return package.PackageFormat == DisplayCatalogPackageFormatEnum::AppxBundle || package.PackageFormat == DisplayCatalogPackageFormatEnum::MsixBundle; } }; struct LocaleComparator : public DisplayCatalogPackageComparisonField { LocaleComparator(std::string locale) : DisplayCatalogPackageComparisonField("Locale") { if (!locale.empty()) { m_locales.emplace_back(std::move(locale)); m_isRequirement = true; } else { m_locales = Locale::GetUserPreferredLanguages(); } AICLI_LOG(Core, Verbose, << "Locale Comparator created with locales: " << Utility::ConvertContainerToString(m_locales) << " , Is requirement: " << m_isRequirement); } bool IsApplicable(const DisplayCatalogPackage& package) override { if (m_isRequirement) { for (auto const& locale : m_locales) { double distanceScore = GetBestDistanceScoreFromList(locale, package.Languages); if (distanceScore >= Locale::MinimumDistanceScoreAsCompatibleMatch) { return true; } } return false; } else { return true; } } bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second) { for (auto const& locale : m_locales) { double firstScore = GetBestDistanceScoreFromList(locale, first.Languages); double secondScore = GetBestDistanceScoreFromList(locale, second.Languages); if (firstScore >= Locale::MinimumDistanceScoreAsCompatibleMatch || secondScore >= Locale::MinimumDistanceScoreAsCompatibleMatch) { return firstScore > secondScore; } } return false; } private: double GetBestDistanceScoreFromList(std::string_view targetLocale, const std::vector& locales) { double finalScore = 0; for (auto const& locale : locales) { double currentScore = Locale::GetDistanceOfLanguage(targetLocale, locale); if (currentScore > finalScore) { finalScore = currentScore; } } return finalScore; } std::vector m_locales; bool m_isRequirement = false; }; struct ArchitectureComparator : public DisplayCatalogPackageComparisonField { ArchitectureComparator(Utility::Architecture architecture) : DisplayCatalogPackageComparisonField("Architecture") { if (architecture != Utility::Architecture::Unknown) { m_architectures.emplace_back(architecture); m_isRequirement = true; } else { m_architectures = Utility::GetApplicableArchitectures(); } AICLI_LOG(Core, Verbose, << "Architecture Comparator created with archs: " << Utility::ConvertContainerToString(m_architectures, Utility::ToString) << " , Is requirement: " << m_isRequirement); } bool IsApplicable(const DisplayCatalogPackage& package) override { if (m_isRequirement) { for (auto arch : package.Architectures) { if (Utility::IsApplicableArchitecture(arch, m_architectures) > Utility::InapplicableArchitecture) { return true; } } return false; } else { return true; } } bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second) override { for (auto arch : m_architectures) { auto firstItr = std::find(first.Architectures.begin(), first.Architectures.end(), arch); auto secondItr = std::find(second.Architectures.begin(), second.Architectures.end(), arch); if (firstItr != first.Architectures.end() && secondItr == second.Architectures.end()) { true; } else if (secondItr != second.Architectures.end()) { return false; } } return false; } private: std::vector m_architectures; bool m_isRequirement = false; }; struct DisplayCatalogPackageComparator { DisplayCatalogPackageComparator(std::string requiredLocale, Utility::Architecture requiredArch) { // Order of comparators matters. AddComparator(std::make_unique(requiredLocale)); AddComparator(std::make_unique(requiredArch)); AddComparator(std::make_unique()); } // Gets the best installer from the manifest, if at least one is applicable. std::optional GetPreferredPackage(const std::vector& packages) { AICLI_LOG(Core, Verbose, << "Starting display catalog package selection."); const DisplayCatalogPackage* result = nullptr; for (const auto& package : packages) { if (IsApplicable(package) && (!result || IsFirstBetter(package, *result))) { result = &package; } } if (result) { return *result; } else { return {}; } } // Determines if the package is applicable. bool IsApplicable(const DisplayCatalogPackage& package) { for (const auto& comparator : m_comparators) { if (!comparator->IsApplicable(package)) { return false; } } return true; } // Determines if the first package is a better choice. bool IsFirstBetter(const DisplayCatalogPackage& first, const DisplayCatalogPackage& second) { for (const auto& comparator : m_comparators) { bool forwardCompare = comparator->IsFirstBetter(first, second); bool reverseCompare = comparator->IsFirstBetter(second, first); if (forwardCompare && reverseCompare) { AICLI_LOG(Core, Error, << "Packages are both better than each other?"); THROW_HR(E_UNEXPECTED); } if (forwardCompare && !reverseCompare) { AICLI_LOG(Core, Verbose, << "Package " << first.PackageId << " is better than " << second.PackageId); return true; } } AICLI_LOG(Core, Verbose, << "Package " << first.PackageId << " is equivalent in priority to " << second.PackageId); return false; } private: void AddComparator(std::unique_ptr&& comparator) { if (comparator) { m_comparators.emplace_back(std::move(comparator)); } } std::vector> m_comparators; }; } // Display catalog API invocation and handling utility::string_t GetDisplayCatalogRestApi(std::string_view productId, std::string_view locale) { std::vector locales; if (!locale.empty()) { locales.emplace_back(locale); } else { for (auto const& localeEntry : Locale::GetUserPreferredLanguages()) { locales.emplace_back(localeEntry); } } // Neutral is always added locales.emplace_back(Neutral); auto restEndpoint = AppInstaller::Utility::Format(std::string{ DisplayCatalogRestApi }, productId, Details, AppInstaller::Runtime::GetOSRegion(), Utility::Join(Utility::LocIndView(","), locales), TargetCatalogId); return JSON::GetUtilityString(restEndpoint); } // Response format: // { // "Product": { // "DisplaySkuAvailabilities": [ // { // "Sku": { // "SkuId": "0015", // ... Sku Contents ... // } // } // ] // } // } std::reference_wrapper GetSkuNodeFromDisplayCatalogResponse(const web::json::value& responseObject) { AICLI_LOG(Core, Info, << "Started parsing display catalog response. Try to find target sku: " << TargetSkuIdValue); if (responseObject.is_null()) { AICLI_LOG(Core, Error, << "Missing DisplayCatalog Response json object."); THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED); } std::optional> product = JSON::GetJsonValueFromNode(responseObject, JSON::GetUtilityString(Product)); if (!product) { AICLI_LOG(Core, Error, << "Missing Product node"); THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED); } auto skuEntries = JSON::GetRawJsonArrayFromJsonNode(product.value().get(), JSON::GetUtilityString(DisplaySkuAvailabilities)); if (!skuEntries) { AICLI_LOG(Core, Error, << "Missing DisplaySkuAvailabilities"); THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED); } for (const auto& skuEntry : skuEntries.value().get()) { std::optional> sku = JSON::GetJsonValueFromNode(skuEntry, JSON::GetUtilityString(Sku)); if (!sku) { AICLI_LOG(Core, Error, << "Missing Sku"); THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED); } const auto& skuValue = sku.value().get(); auto skuId = JSON::GetRawStringValueFromJsonNode(skuValue, JSON::GetUtilityString(SkuId)).value_or(""); if (TargetSkuIdValue == skuId) { AICLI_LOG(Core, Info, << "Target Sku (" << TargetSkuIdValue << ") found"); return skuValue; } } AICLI_LOG(Core, Error, << "Target Sku (" << TargetSkuIdValue << ") not found"); THROW_HR(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE); } // Response format: // { // "Sku": { // "Properties": { // "Packages": [ // { // "PackageId": "package id", // "Architectures": [ "x86", "x64" ], // "Languages": [ "en", "fr" ], // "PackageFormat": "AppxBundle", // "ContentId": "guid", // "FulfillmentData": { // "WuCategoryId": "guid", // } // } // ] // } // } // } std::vector GetDisplayCatalogPackagesFromSkuNode(const web::json::value& jsonObject) { AICLI_LOG(Core, Info, << "Started extracting display catalog packages from sku."); std::optional> properties = JSON::GetJsonValueFromNode(jsonObject, JSON::GetUtilityString(Properties)); if (!properties) { AICLI_LOG(Core, Error, << "Missing Properties"); THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED); } const auto& propertiesValue = properties.value().get(); auto packages = JSON::GetRawJsonArrayFromJsonNode(propertiesValue, JSON::GetUtilityString(Packages)); if (!packages) { AICLI_LOG(Core, Error, << "Missing Packages"); THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED); } std::vector displayCatalogPackages; for (const auto& packageEntry : packages.value().get()) { DisplayCatalogPackage catalogPackage; // Package Id catalogPackage.PackageId = JSON::GetRawStringValueFromJsonNode(packageEntry, JSON::GetUtilityString(PackageId)).value_or(""); // Architectures auto architectures = JSON::GetRawStringArrayFromJsonNode(packageEntry, JSON::GetUtilityString(Architectures)); for (const auto& arch : architectures) { auto archEnum = Utility::ConvertToArchitectureEnum(arch); if (archEnum != Utility::Architecture::Unknown) { catalogPackage.Architectures.emplace_back(archEnum); } } // Languages auto languages = JSON::GetRawStringArrayFromJsonNode(packageEntry, JSON::GetUtilityString(Languages)); for (const auto& language : languages) { catalogPackage.Languages.emplace_back(language); } // Package Format auto packageFormat = JSON::GetRawStringValueFromJsonNode(packageEntry, JSON::GetUtilityString(PackageFormat)).value_or(""); catalogPackage.PackageFormat = ConvertToPackageFormatEnum(packageFormat); // Content Id catalogPackage.ContentId = JSON::GetRawStringValueFromJsonNode(packageEntry, JSON::GetUtilityString(ContentId)).value_or(""); if (catalogPackage.ContentId.empty()) { AICLI_LOG(Core, Warning, << "Missing ContentId"); // ContentId is required for licensing. Skip this package if missing. continue; } // WuCategoryId std::optional> fulfillmentData = JSON::GetJsonValueFromNode(packageEntry, JSON::GetUtilityString(FulfillmentData)); if (!fulfillmentData) { AICLI_LOG(Core, Warning, << "Missing FulfillmentData"); // WuCategoryId is required for sfs-client. Skip this package if missing. continue; } catalogPackage.WuCategoryId = JSON::GetRawStringValueFromJsonNode(fulfillmentData.value().get(), JSON::GetUtilityString(WuCategoryId)).value_or(""); if (catalogPackage.WuCategoryId.empty()) { AICLI_LOG(Core, Warning, << "Missing WuCategoryId"); // WuCategoryId is required for sfs-client. Skip this package if missing. continue; } displayCatalogPackages.emplace_back(std::move(catalogPackage)); } return displayCatalogPackages; } DisplayCatalogPackage CallDisplayCatalogAndGetPreferredPackage(std::string_view productId, std::string_view locale, Utility::Architecture architecture, const Http::HttpClientHelper::HttpRequestHeaders& authHeaders) { AICLI_LOG(Core, Info, << "CallDisplayCatalogAndGetPreferredPackage with ProductId: " << productId << " Locale: " << locale << " Architecture: " << Utility::ToString(architecture)); auto displayCatalogApi = GetDisplayCatalogRestApi(productId, locale); AppInstaller::Http::HttpClientHelper httpClientHelper; #ifndef AICLI_DISABLE_TEST_HOOKS if (TestHooks::s_DisplayCatalog_HttpPipelineStage_Override) { httpClientHelper = AppInstaller::Http::HttpClientHelper{ TestHooks::s_DisplayCatalog_HttpPipelineStage_Override }; } #endif std::optional displayCatalogResponseObject = httpClientHelper.HandleGet(displayCatalogApi, {}, authHeaders); if (!displayCatalogResponseObject) { AICLI_LOG(Core, Error, << "No display catalog json object found"); THROW_HR(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED); } const auto& sku = GetSkuNodeFromDisplayCatalogResponse(displayCatalogResponseObject.value()); auto displayCatalogPackages = GetDisplayCatalogPackagesFromSkuNode(sku.get()); DisplayCatalogPackageComparison::DisplayCatalogPackageComparator packageComparator{ std::string{ locale }, architecture }; auto preferredPackageResult = packageComparator.GetPreferredPackage(displayCatalogPackages); if (!preferredPackageResult) { AICLI_LOG(Core, Error, << "No applicable display catalog package found for ProductId: " << productId << " , Locale: " << locale << " , Architecture: " << Utility::ToString(architecture)); THROW_HR(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE); } auto preferredPackage = preferredPackageResult.value(); AICLI_LOG(Core, Info, << "DisplayCatalog package selected. WuCategoryId: " << preferredPackage.WuCategoryId << " , ContentId: " << preferredPackage.ContentId); return preferredPackage; } } #ifndef WINGET_DISABLE_FOR_FUZZING namespace SfsClientDetails { const std::string SupportedFileTypes[] = { ".msix", ".msixbundle", ".appx", ".appxbundle" }; Manifest::PlatformEnum ConvertFromSfsPlatform(std::string_view applicability) { if (Utility::CaseInsensitiveStartsWith(applicability, "universal")) { return Manifest::PlatformEnum::Universal; } else if (Utility::CaseInsensitiveStartsWith(applicability, "desktop")) { return Manifest::PlatformEnum::Desktop; } else if (Utility::CaseInsensitiveStartsWith(applicability, "iot")) { return Manifest::PlatformEnum::IoT; } else if (Utility::CaseInsensitiveStartsWith(applicability, "analog")) { return Manifest::PlatformEnum::Holographic; } else if (Utility::CaseInsensitiveStartsWith(applicability, "ppi")) { return Manifest::PlatformEnum::Team; } return Manifest::PlatformEnum::Unknown; } // Parses a string of the form `={,}?`. struct PlatformApplicability { explicit PlatformApplicability(std::string_view input, bool extractVersion = true) : Platform(ConvertFromSfsPlatform(input)) { if (extractVersion) { THROW_HR_IF(E_INVALIDARG, input.empty()); size_t position = input.find('='); THROW_HR_IF(E_INVALIDARG, std::string_view::npos == position); position += 1; size_t length = input.size() - position; if (length > 0 && input.back() == ',') { length -= 1; } MinimumVersion = Utility::UInt64Version{ std::string{ input.substr(position, length) } }; } } Manifest::PlatformEnum Platform; std::optional MinimumVersion; }; Utility::Architecture ConvertFromSfsArchitecture(SFS::Architecture sfsArchitecture) { switch (sfsArchitecture) { case SFS::Architecture::Amd64: return Utility::Architecture::X64; case SFS::Architecture::x86: return Utility::Architecture::X86; case SFS::Architecture::Arm64: return Utility::Architecture::Arm64; case SFS::Architecture::Arm: return Utility::Architecture::Arm; case SFS::Architecture::None: return Utility::Architecture::Neutral; } return Utility::Architecture::Unknown; } std::vector GetSfsPackageFileSupportedPlatforms( const SFS::AppFile& appFile, Manifest::PlatformEnum requiredPlatform, const std::optional& targetOSVersion) { std::vector supportedPlatforms; for (auto const& applicability : appFile.GetApplicabilityDetails().GetPlatformApplicabilityForPackage()) { AICLI_LOG(Core, Verbose, << " examining platform [" << applicability << "] for applicability..."); PlatformApplicability platform(applicability, targetOSVersion.has_value()); if (platform.Platform == Manifest::PlatformEnum::Unknown) { AICLI_LOG(Core, Verbose, << " not applicable due to unknown platform"); continue; } if (platform.MinimumVersion && targetOSVersion) { if (targetOSVersion.value() < platform.MinimumVersion.value()) { AICLI_LOG(Core, Verbose, << " not applicable due to OS version; target [" << targetOSVersion.value().ToString() << "] is lower than minimum [" << platform.MinimumVersion.value().ToString() << "]"); continue; } } if (platform.Platform == requiredPlatform || requiredPlatform == Manifest::PlatformEnum::Unknown) { AICLI_LOG(Core, Verbose, << " applicable"); supportedPlatforms.emplace_back(platform.Platform); } else { AICLI_LOG(Core, Verbose, << " not applicable due to platform requirement"); } } return supportedPlatforms; } std::vector GetSfsPackageFileSupportedArchitectures(const SFS::AppFile& appFile, Utility::Architecture requiredArchitecture) { std::vector supportedArchitectures; for (auto const& sfsArchitecture : appFile.GetApplicabilityDetails().GetArchitectures()) { auto convertedArchitecture = ConvertFromSfsArchitecture(sfsArchitecture); if (convertedArchitecture == Utility::Architecture::Unknown) { continue; } if (requiredArchitecture == Utility::Architecture::Unknown || // No required architecture convertedArchitecture == requiredArchitecture) { supportedArchitectures.emplace_back(convertedArchitecture); } } return supportedArchitectures; } std::string GetSfsPackageFileExtension(const SFS::AppFile& appFile) { return std::filesystem::path{ appFile.GetFileId() }.extension().u8string(); } bool IsFileExtensionSupported(std::string_view fileExtension) { for (auto const& supportedFileType : SupportedFileTypes) { if (Utility::CaseInsensitiveEquals(supportedFileType, fileExtension)) { return true; } } return false; } // The file name will be {Name}_{Version}_{Platform list}_{Arch list}.{File Extension} // If the file name is longer than 256, file moniker will be used. std::string GetSfsPackageFileNameForDownload( const std::string& packageName, const Utility::UInt64Version& packageVersion, const std::vector& supportedPlatforms, const std::vector& supportedArchitectures, const std::string& fileExtension, const std::string& fileMoniker) { std::string platformString; for (auto platform : supportedPlatforms) { platformString += std::string{ Manifest::PlatformToString(platform, true) } + '.'; } platformString.resize(platformString.size() - 1); std::string architectureString; for (auto architecture : supportedArchitectures) { architectureString += std::string{ Utility::ToString(architecture) } + '.'; } architectureString.resize(architectureString.size() - 1); std::string fileName = packageName + '_' + packageVersion.ToString() + '_' + platformString + '_' + architectureString + fileExtension; if (fileName.length() < 256) { return fileName; } else { return fileMoniker + fileExtension; } } void SfsClientLoggingCallback(const SFS::LogData& logData) { std::string message = "Message: " + std::string{ logData.message }; message += " File: " + std::string{ logData.file }; message += " Line: " + std::to_string(logData.line); message += " Function: " + std::string{ logData.function }; switch (logData.severity) { case SFS::LogSeverity::Verbose: AICLI_LOG(Core, Verbose, << message); break; case SFS::LogSeverity::Info: AICLI_LOG(Core, Info, << message); break; case SFS::LogSeverity::Warning: AICLI_LOG(Core, Warning, << message); break; case SFS::LogSeverity::Error: AICLI_LOG(Core, Error, << message); break; } } const std::unique_ptr& GetSfsClientInstance() { static std::unique_ptr s_sfsClient; static std::once_flag s_sfsClientInitializeOnce; std::call_once(s_sfsClientInitializeOnce, [&]() { SFS::ClientConfig config; config.accountId = "storeapps"; config.instanceId = "storeapps"; config.logCallbackFn = SfsClientLoggingCallback; auto result = SFS::SFSClient::Make(config, s_sfsClient); if (!result) { AICLI_LOG(Core, Error, << "Failed to initialize SfsClient. Error code: " << result.GetCode() << " Message: " << result.GetMsg()); THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED, "Failed to initialize SfsClient. ErrorCode: %lu Message: %hs", result.GetCode(), result.GetMsg().c_str()); } }); return s_sfsClient; } std::vector PopulateSfsAppFileToMSStoreDownloadFileVector( const std::vector& appFiles, Utility::Architecture requiredArchitecture = Utility::Architecture::Unknown, Manifest::PlatformEnum requiredPlatform = Manifest::PlatformEnum::Unknown, const std::optional& targetOSVersion = std::nullopt) { using PlatformAndArchitectureKey = std::pair; // Since the server may return multiple versions of the same package, we'll use this map to record the one with latest version // for each Platform|Architecture pair. std::map downloadFilesMap; for (auto const& appFile : appFiles) { AICLI_LOG(Core, Info, << "Examining package [" << appFile.GetFileMoniker() << " (" << appFile.GetFileId() << ")] for download..."); // Filter out unsupported packages auto supportedPlatforms = GetSfsPackageFileSupportedPlatforms(appFile, requiredPlatform, targetOSVersion); if (supportedPlatforms.empty()) { AICLI_LOG(Core, Verbose, << " package has no applicable platform."); continue; } auto supportedArchitectures = GetSfsPackageFileSupportedArchitectures(appFile, requiredArchitecture); if (supportedArchitectures.empty()) { AICLI_LOG(Core, Verbose, << " package has no applicable architecture."); continue; } std::string fileExtension = GetSfsPackageFileExtension(appFile); if (!IsFileExtensionSupported(fileExtension)) { AICLI_LOG(Core, Verbose, << " package has unsupported file type [" << fileExtension << "]."); continue; } MSStoreDownloadFile downloadFile; downloadFile.Url = appFile.GetUrl(); // The sha256 hash was base64 encoded downloadFile.Sha256 = JSON::Base64Decode(appFile.GetHashes().at(SFS::HashType::Sha256)); auto packageInfo = Msix::GetPackageIdInfoFromFullName(appFile.GetFileMoniker()); downloadFile.Version = packageInfo.Version; downloadFile.FileName = GetSfsPackageFileNameForDownload( packageInfo.Name, packageInfo.Version, supportedPlatforms, supportedArchitectures, fileExtension, appFile.GetFileMoniker()); // Update the platform architecture map with latest package if applicable for (auto supportedPlatform : supportedPlatforms) { for (auto supportedArchitecture : supportedArchitectures) { PlatformAndArchitectureKey downloadFileKey{ supportedPlatform, supportedArchitecture }; if (downloadFile.Version > downloadFilesMap[downloadFileKey].Version) { downloadFilesMap[downloadFileKey] = downloadFile; } } } } // Generate MSStoreDownloadFile vector and remove duplication. std::vector result; for (auto& downloadFileEntry : downloadFilesMap) { if (std::find_if(result.begin(), result.end(), [&](const MSStoreDownloadFile& downloadFile) { return Utility::CaseInsensitiveEquals(downloadFile.FileName, downloadFileEntry.second.FileName); }) == result.end()) { result.emplace_back(std::move(downloadFileEntry.second)); } } return result; } MSStoreDownloadInfo CallSfsClientAndGetMSStoreDownloadInfo( std::string_view wuCategoryId, Utility::Architecture requiredArchitecture, Manifest::PlatformEnum requiredPlatform, const std::optional& targetOSVersion) { AICLI_LOG(Core, Info, << "CallSfsClientAndGetMSStoreDownloadInfo with WuCategoryId: " << wuCategoryId << " Architecture: " << Utility::ToString(requiredArchitecture) << " Platform: " << Manifest::PlatformToString(requiredPlatform) << " Target OS Version: " << (targetOSVersion ? targetOSVersion.value().ToString() : "any")); std::vector appContents; #ifndef AICLI_DISABLE_TEST_HOOKS if (TestHooks::s_SfsClient_AppContents_Override) { appContents = (*TestHooks::s_SfsClient_AppContents_Override)(wuCategoryId); } else #endif { SFS::RequestParams sfsClientRequest; sfsClientRequest.productRequests = { {std::string{ wuCategoryId }, {}} }; const auto& proxyUri = AppInstaller::Settings::Network().GetProxyUri(); if (proxyUri) { AICLI_LOG(Core, Info, << "Passing proxy to SFS client " << *proxyUri); sfsClientRequest.proxy = *proxyUri; } auto requestResult = GetSfsClientInstance()->GetLatestAppDownloadInfo(sfsClientRequest, appContents); if (!requestResult) { if (requestResult.GetCode() == SFS::Result::Code::HttpNotFound) { AICLI_LOG(Core, Error, << "Failed to call SfsClient GetLatestAppDownloadInfo. Package not found."); THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED, "Failed to call SfsClient GetLatestAppDownloadInfo. Package download not supported."); } else { AICLI_LOG(Core, Error, << "Failed to call SfsClient GetLatestAppDownloadInfo. Error code: " << requestResult.GetCode() << " Message: " << requestResult.GetMsg()); THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED, "Failed to call SfsClient GetLatestAppDownloadInfo. ErrorCode: %lu Message: %hs", requestResult.GetCode(), requestResult.GetMsg().c_str()); } } } THROW_HR_IF(E_UNEXPECTED, appContents.empty()); MSStoreDownloadInfo result; // Currently for app downloads, the result vector is always size 1. const auto& appContent = appContents.at(0); // Populate main packages result.MainPackages = PopulateSfsAppFileToMSStoreDownloadFileVector(appContent.GetFiles(), requiredArchitecture, requiredPlatform, targetOSVersion); // Populate dependency packages for (auto const& dependencyEntry : appContent.GetPrerequisites()) { // Not passing in required platform for dependencies. Dependencies are mostly Windows.Universal. auto dependencyPackages = PopulateSfsAppFileToMSStoreDownloadFileVector(dependencyEntry.GetFiles(), requiredArchitecture, Manifest::PlatformEnum::Unknown, targetOSVersion); std::move(dependencyPackages.begin(), dependencyPackages.end(), std::inserter(result.DependencyPackages, result.DependencyPackages.end())); } if (result.MainPackages.empty()) { AICLI_LOG(Core, Error, << "No applicable SFS main package."); THROW_HR(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE); } return result; } } #endif namespace LicensingDetails { // Json response fields constexpr std::string_view License = "license"sv; constexpr std::string_view Keys = "keys"sv; constexpr std::string_view Value = "value"sv; // Licensing rest endpoint constexpr std::string_view LicensingRestEndpoint = "https://licensing.md.mp.microsoft.com/v9.0/licenses/offlineContent"; constexpr std::string_view ContentId = "contentId"sv; constexpr std::string_view From = "From"sv; // Response: // { // "license": { // "keys": [ // returned as array for future, for now only 1 key // { // "value": "base64 encoded string" // } // ] // } // } std::vector GetLicensing(std::string_view contentId, const Http::HttpClientHelper::HttpRequestHeaders& authHeaders) { AICLI_LOG(Core, Error, << "GetLicensing with ContentId: " << contentId); AppInstaller::Http::HttpClientHelper httpClientHelper; #ifndef AICLI_DISABLE_TEST_HOOKS if (TestHooks::s_Licensing_HttpPipelineStage_Override) { httpClientHelper = AppInstaller::Http::HttpClientHelper{ TestHooks::s_Licensing_HttpPipelineStage_Override }; } #endif web::json::value requestBody; requestBody[JSON::GetUtilityString(ContentId)] = web::json::value::string(JSON::GetUtilityString(contentId)); Http::HttpClientHelper::HttpRequestHeaders requestHeaders; requestHeaders.insert_or_assign(JSON::GetUtilityString(From), L"winget-cli"); std::optional licensingResponseObject = std::nullopt; try { licensingResponseObject = httpClientHelper.HandlePost( JSON::GetUtilityString(LicensingRestEndpoint), requestBody, requestHeaders, authHeaders); } catch (const wil::ResultException& re) { if (re.GetErrorCode() == HTTP_E_STATUS_FORBIDDEN) { AICLI_LOG(CLI, Error, << "Getting MSStore package license failed. The Microsoft Entra Id account does not have privilege."); THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN); } else { AICLI_LOG(CLI, Error, << "Getting MSStore package license failed. Error code: " << re.GetErrorCode()); THROW_HR(re.GetErrorCode()); } } if (!licensingResponseObject || licensingResponseObject->is_null()) { AICLI_LOG(Core, Error, << "Empty licensing response"); THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED); } std::optional> license = JSON::GetJsonValueFromNode(licensingResponseObject.value(), JSON::GetUtilityString(License)); if (!license) { AICLI_LOG(Core, Error, << "Missing license node"); THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED); } auto keys = JSON::GetRawJsonArrayFromJsonNode(license.value().get(), JSON::GetUtilityString(Keys)); if (!keys || keys->get().size() == 0) { AICLI_LOG(Core, Error, << "Missing keys or empty keys"); THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED); } std::string base64LicenseContent = JSON::GetRawStringValueFromJsonNode(keys->get().at(0), JSON::GetUtilityString(Value)).value_or(""); if (base64LicenseContent.empty()) { AICLI_LOG(Core, Error, << "Missing license content"); THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED); } return JSON::Base64Decode(base64LicenseContent); } } namespace { Http::HttpClientHelper::HttpRequestHeaders GetAuthHeaders(std::unique_ptr& authenticator) { if (!authenticator) { return {}; } Http::HttpClientHelper::HttpRequestHeaders result; auto authResult = authenticator->AuthenticateForToken(); if (FAILED(authResult.Status)) { AICLI_LOG(Repo, Error, << "Authentication failed. Result: " << authResult.Status); THROW_HR_MSG(authResult.Status, "Failed to authenticate for MicrosoftEntraId"); } result.insert_or_assign(web::http::header_names::authorization, JSON::GetUtilityString(Authentication::CreateBearerToken(authResult.Token))); return result; } } MSStoreDownloadContext::MSStoreDownloadContext( std::string productId, AppInstaller::Utility::Architecture architecture, Manifest::PlatformEnum platform, std::string locale, AppInstaller::Authentication::AuthenticationArguments authArgs) : m_productId(std::move(productId)), m_architecture(architecture), m_platform(platform), m_locale(std::move(locale)) { #ifndef AICLI_DISABLE_TEST_HOOKS if (!TestHooks::s_DisplayCatalog_HttpPipelineStage_Override) #endif { Authentication::MicrosoftEntraIdAuthenticationInfo displayCatalogMicrosoftEntraIdAuthInfo; displayCatalogMicrosoftEntraIdAuthInfo.Resource = "https://bigcatalog.commerce.microsoft.com"; Authentication::AuthenticationInfo displayCatalogAuthInfo; displayCatalogAuthInfo.Type = Authentication::AuthenticationType::MicrosoftEntraId; displayCatalogAuthInfo.MicrosoftEntraIdInfo = std::move(displayCatalogMicrosoftEntraIdAuthInfo); m_displayCatalogAuthenticator = std::make_unique(std::move(displayCatalogAuthInfo), authArgs); } #ifndef AICLI_DISABLE_TEST_HOOKS if (!TestHooks::s_Licensing_HttpPipelineStage_Override) #endif { Authentication::MicrosoftEntraIdAuthenticationInfo licensingMicrosoftEntraIdAuthInfo; licensingMicrosoftEntraIdAuthInfo.Resource = "c5e1cb0d-5d24-4b1a-b291-ec684152b2ba"; Authentication::AuthenticationInfo licensingAuthInfo; licensingAuthInfo.Type = Authentication::AuthenticationType::MicrosoftEntraId; licensingAuthInfo.MicrosoftEntraIdInfo = std::move(licensingMicrosoftEntraIdAuthInfo); m_licensingAuthenticator = std::make_unique(std::move(licensingAuthInfo), authArgs); } } void MSStoreDownloadContext::TargetOSVersion(std::optional targetOSVersion) { m_targetOSVersion = std::move(targetOSVersion); } MSStoreDownloadInfo MSStoreDownloadContext::GetDownloadInfo() { #ifndef WINGET_DISABLE_FOR_FUZZING auto displayCatalogPackage = DisplayCatalogDetails::CallDisplayCatalogAndGetPreferredPackage(m_productId, m_locale, m_architecture, GetAuthHeaders(m_displayCatalogAuthenticator)); auto downloadInfo = SfsClientDetails::CallSfsClientAndGetMSStoreDownloadInfo(displayCatalogPackage.WuCategoryId, m_architecture, m_platform, m_targetOSVersion); downloadInfo.ContentId = displayCatalogPackage.ContentId; return downloadInfo; #else return {}; #endif } std::vector MSStoreDownloadContext::GetLicense(std::string_view contentId) { return LicensingDetails::GetLicensing(contentId, GetAuthHeaders(m_licensingAuthenticator)); } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/Manifest.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/Manifest.h" #include "winget/Locale.h" #include "winget/UserSettings.h" namespace AppInstaller::Manifest { namespace { void AddFoldedStringToSetIfNotEmpty(std::set& set, const string_t& value) { if (!value.empty()) { set.emplace(Utility::FoldCase(value)); } } } void Manifest::ApplyLocale(const std::string& locale) { CurrentLocalization = DefaultLocalization; // Get target locale from Preferred Languages settings if applicable std::vector targetLocales; if (locale.empty()) { targetLocales = Locale::GetUserPreferredLanguages(); } else { targetLocales.emplace_back(locale); } for (auto const& targetLocale : targetLocales) { const ManifestLocalization* bestLocalization = nullptr; double bestScore = Locale::GetDistanceOfLanguage(targetLocale, DefaultLocalization.Locale); for (auto const& localization : Localizations) { double score = Locale::GetDistanceOfLanguage(targetLocale, localization.Locale); if (score > bestScore) { bestLocalization = &localization; bestScore = score; } } // If there's better locale than default And is compatible with target locale, merge and return; if (bestScore >= Locale::MinimumDistanceScoreAsCompatibleMatch) { if (bestLocalization != nullptr) { CurrentLocalization.ReplaceOrMergeWith(*bestLocalization); } break; } } } std::vector Manifest::GetAggregatedTags() const { std::vector resultTags = DefaultLocalization.Get(); for (const auto& locale : Localizations) { auto tags = locale.Get(); for (const auto& tag : tags) { if (std::find(resultTags.begin(), resultTags.end(), tag) == resultTags.end()) { resultTags.emplace_back(tag); } } } return resultTags; } std::vector Manifest::GetAggregatedCommands() const { std::vector resultCommands; for (const auto& installer : Installers) { for (const auto& command : installer.Commands) { if (std::find(resultCommands.begin(), resultCommands.end(), command) == resultCommands.end()) { resultCommands.emplace_back(command); } } } return resultCommands; } Utility::VersionRange Manifest::GetArpVersionRange() const { bool arpVersionFound = false; Utility::Version minVersion; Utility::Version maxVersion; for (auto const& installer : Installers) { if (DoesInstallerTypeSupportArpVersionRange(installer.EffectiveInstallerType())) { for (auto const& entry : installer.AppsAndFeaturesEntries) { if (!entry.DisplayVersion.empty()) { Utility::Version arpVersion{ entry.DisplayVersion }; if (!arpVersionFound) { // This is the first arp version found, populate both min and max version minVersion = arpVersion; maxVersion = arpVersion; arpVersionFound = true; continue; } if (arpVersion < minVersion) { minVersion = arpVersion; } else if (arpVersion > maxVersion) { maxVersion = arpVersion; } } } } } return arpVersionFound ? Utility::VersionRange{ minVersion, maxVersion } : Utility::VersionRange{}; } std::vector Manifest::GetPackageFamilyNames() const { return GetSystemReferenceStrings( [](const ManifestInstaller& i) -> const Utility::NormalizedString& { return i.PackageFamilyName; }); } std::vector Manifest::GetProductCodes() const { return GetSystemReferenceStrings( [](const ManifestInstaller& i) -> const Utility::NormalizedString& { return i.ProductCode; }, [](const AppsAndFeaturesEntry& e) -> const Utility::NormalizedString& { return e.ProductCode; }); } std::vector Manifest::GetUpgradeCodes() const { return GetSystemReferenceStrings( {}, [](const AppsAndFeaturesEntry& e) -> const Utility::NormalizedString& { return e.UpgradeCode; }); } std::vector Manifest::GetPackageNames() const { std::set set; AddFoldedStringToSetIfNotEmpty(set, DefaultLocalization.Get()); for (const auto& loc : Localizations) { AddFoldedStringToSetIfNotEmpty(set, loc.Get()); } // In addition to the names used for our display, add the display names from the ARP entries for (const auto& installer : Installers) { for (const auto& appsAndFeaturesEntry : installer.AppsAndFeaturesEntries) { AddFoldedStringToSetIfNotEmpty(set, appsAndFeaturesEntry.DisplayName); } } std::vector result( std::make_move_iterator(set.begin()), std::make_move_iterator(set.end())); return result; } std::vector Manifest::GetPublishers() const { std::set set; AddFoldedStringToSetIfNotEmpty(set, DefaultLocalization.Get()); for (const auto& loc : Localizations) { AddFoldedStringToSetIfNotEmpty(set, loc.Get()); } // In addition to the publishers used for our display, add the publisher from the ARP entries for (const auto& installer : Installers) { for (const auto& appsAndFeaturesEntry : installer.AppsAndFeaturesEntries) { AddFoldedStringToSetIfNotEmpty(set, appsAndFeaturesEntry.Publisher); } } std::vector result( std::make_move_iterator(set.begin()), std::make_move_iterator(set.end())); return result; } std::vector Manifest::GetSystemReferenceStrings( std::function extractStringFromInstaller, std::function extractStringFromAppsAndFeaturesEntry) const { std::set set; for (const auto& installer : Installers) { if (extractStringFromInstaller) { const auto& installerString = extractStringFromInstaller(installer); AddFoldedStringToSetIfNotEmpty(set, installerString); } if (extractStringFromAppsAndFeaturesEntry) { for (const auto& entry : installer.AppsAndFeaturesEntries) { const auto& entryString = extractStringFromAppsAndFeaturesEntry(entry); AddFoldedStringToSetIfNotEmpty(set, entryString); } } } std::vector result( std::make_move_iterator(set.begin()), std::make_move_iterator(set.end())); return result; } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/ManifestCommon.h" #include "winget/ManifestValidation.h" namespace AppInstaller::Manifest { namespace { enum class CompatibilitySet { None, Exe, Msi, Msix, }; CompatibilitySet GetCompatibilitySet(InstallerTypeEnum type) { switch (type) { case InstallerTypeEnum::Inno: case InstallerTypeEnum::Nullsoft: case InstallerTypeEnum::Exe: case InstallerTypeEnum::Burn: return CompatibilitySet::Exe; case InstallerTypeEnum::Wix: case InstallerTypeEnum::Msi: return CompatibilitySet::Msi; case InstallerTypeEnum::Msix: case InstallerTypeEnum::MSStore: return CompatibilitySet::Msix; default: return CompatibilitySet::None; } } } ManifestVer::ManifestVer(std::string_view version) { bool validationSuccess = true; // Separate the extensions out size_t hyphenPos = version.find_first_of('-'); if (hyphenPos != std::string_view::npos) { // The first part is the main version Assign(std::string{ version.substr(0, hyphenPos) }, "."); // The second part is the extensions hyphenPos += 1; while (hyphenPos < version.length()) { size_t newPos = version.find_first_of('-', hyphenPos); size_t length = (newPos == std::string::npos ? version.length() : newPos) - hyphenPos; m_extensions.emplace_back(std::string{ version.substr(hyphenPos, length) }, "."); hyphenPos += length + 1; } } else { Assign(std::string{ version }, "."); } if (m_parts.size() > 3) { validationSuccess = false; } else { for (size_t i = 0; i < m_parts.size(); i++) { if (!m_parts[i].Other.empty()) { validationSuccess = false; break; } } for (const RawVersion& ext : m_extensions) { if (ext.GetParts().empty() || ext.GetParts()[0].Integer != 0) { validationSuccess = false; break; } } } if (!validationSuccess) { std::vector errors; errors.emplace_back(ManifestError::InvalidFieldValue, "ManifestVersion", std::string{ version }); THROW_EXCEPTION(ManifestException(std::move(errors))); } } bool ManifestVer::HasExtension() const { return !m_extensions.empty(); } bool ManifestVer::HasExtension(std::string_view extension) const { for (const RawVersion& ext : m_extensions) { const auto& parts = ext.GetParts(); if (!parts.empty() && parts[0].Integer == 0 && parts[0].Other == extension) { return true; } } return false; } InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in) { std::string inStrLower = Utility::ToLower(in); InstallerTypeEnum result = InstallerTypeEnum::Unknown; if (inStrLower == "inno") { result = InstallerTypeEnum::Inno; } else if (inStrLower == "wix") { result = InstallerTypeEnum::Wix; } else if (inStrLower == "msi") { result = InstallerTypeEnum::Msi; } else if (inStrLower == "nullsoft") { result = InstallerTypeEnum::Nullsoft; } else if (inStrLower == "zip") { result = InstallerTypeEnum::Zip; } else if (inStrLower == "appx" || inStrLower == "msix") { result = InstallerTypeEnum::Msix; } else if (inStrLower == "exe") { result = InstallerTypeEnum::Exe; } else if (inStrLower == "burn") { result = InstallerTypeEnum::Burn; } else if (inStrLower == "msstore") { result = InstallerTypeEnum::MSStore; } else if (inStrLower == "portable") { result = InstallerTypeEnum::Portable; } else if (inStrLower == "font") { result = InstallerTypeEnum::Font; } return result; } UpdateBehaviorEnum ConvertToUpdateBehaviorEnum(const std::string& in) { UpdateBehaviorEnum result = UpdateBehaviorEnum::Unknown; if (Utility::CaseInsensitiveEquals(in, "install")) { result = UpdateBehaviorEnum::Install; } else if (Utility::CaseInsensitiveEquals(in, "uninstallprevious")) { result = UpdateBehaviorEnum::UninstallPrevious; } else if (Utility::CaseInsensitiveEquals(in, "deny")) { result = UpdateBehaviorEnum::Deny; } return result; } ScopeEnum ConvertToScopeEnum(std::string_view in) { ScopeEnum result = ScopeEnum::Unknown; if (Utility::CaseInsensitiveEquals(in, "user")) { result = ScopeEnum::User; } else if (Utility::CaseInsensitiveEquals(in, "machine")) { result = ScopeEnum::Machine; } return result; } InstallModeEnum ConvertToInstallModeEnum(const std::string& in) { InstallModeEnum result = InstallModeEnum::Unknown; if (Utility::CaseInsensitiveEquals(in, "interactive")) { result = InstallModeEnum::Interactive; } else if (Utility::CaseInsensitiveEquals(in, "silent")) { result = InstallModeEnum::Silent; } else if (Utility::CaseInsensitiveEquals(in, "silentWithProgress")) { result = InstallModeEnum::SilentWithProgress; } return result; } PlatformEnum ConvertToPlatformEnum(std::string_view in) { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "windows.desktop") { return PlatformEnum::Desktop; } else if (inStrLower == "windows.universal") { return PlatformEnum::Universal; } return PlatformEnum::Unknown; } PlatformEnum ConvertToPlatformEnumForMSStoreDownload(std::string_view in) { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "windows.desktop") { return PlatformEnum::Desktop; } else if (inStrLower == "windows.universal") { return PlatformEnum::Universal; } else if (inStrLower == "windows.iot") { return PlatformEnum::IoT; } else if (inStrLower == "windows.team") { return PlatformEnum::Team; } else if (inStrLower == "windows.holographic") { return PlatformEnum::Holographic; } return PlatformEnum::Unknown; } ElevationRequirementEnum ConvertToElevationRequirementEnum(const std::string& in) { ElevationRequirementEnum result = ElevationRequirementEnum::Unknown; if (Utility::CaseInsensitiveEquals(in, "elevationRequired")) { result = ElevationRequirementEnum::ElevationRequired; } else if (Utility::CaseInsensitiveEquals(in, "elevationProhibited")) { result = ElevationRequirementEnum::ElevationProhibited; } else if (Utility::CaseInsensitiveEquals(in, "elevatesSelf")) { result = ElevationRequirementEnum::ElevatesSelf; } return result; } UnsupportedArgumentEnum ConvertToUnsupportedArgumentEnum(const std::string& in) { UnsupportedArgumentEnum result = UnsupportedArgumentEnum::Unknown; if (Utility::CaseInsensitiveEquals(in, "log")) { result = UnsupportedArgumentEnum::Log; } else if (Utility::CaseInsensitiveEquals(in, "location")) { result = UnsupportedArgumentEnum::Location; } return result; } ManifestTypeEnum ConvertToManifestTypeEnum(const std::string& in) { if (in == "singleton") { return ManifestTypeEnum::Singleton; } else if (in == "version") { return ManifestTypeEnum::Version; } else if (in == "installer") { return ManifestTypeEnum::Installer; } else if (in == "defaultLocale") { return ManifestTypeEnum::DefaultLocale; } else if (in == "locale") { return ManifestTypeEnum::Locale; } else if (in == "merged") { return ManifestTypeEnum::Merged; } else if (in == "shadow") { return ManifestTypeEnum::Shadow; } else { THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Unsupported ManifestType: %hs", in.c_str()); } } ExpectedReturnCodeEnum ConvertToExpectedReturnCodeEnum(const std::string& in) { std::string inStrLower = Utility::ToLower(in); ExpectedReturnCodeEnum result = ExpectedReturnCodeEnum::Unknown; if (inStrLower == "packageinuse") { result = ExpectedReturnCodeEnum::PackageInUse; } else if (inStrLower == "packageinusebyapplication") { result = ExpectedReturnCodeEnum::PackageInUseByApplication; } else if (inStrLower == "installinprogress") { result = ExpectedReturnCodeEnum::InstallInProgress; } else if (inStrLower == "fileinuse") { result = ExpectedReturnCodeEnum::FileInUse; } else if (inStrLower == "missingdependency") { result = ExpectedReturnCodeEnum::MissingDependency; } else if (inStrLower == "diskfull") { result = ExpectedReturnCodeEnum::DiskFull; } else if (inStrLower == "insufficientmemory") { result = ExpectedReturnCodeEnum::InsufficientMemory; } else if (inStrLower == "invalidparameter") { result = ExpectedReturnCodeEnum::InvalidParameter; } else if (inStrLower == "nonetwork") { result = ExpectedReturnCodeEnum::NoNetwork; } else if (inStrLower == "contactsupport") { result = ExpectedReturnCodeEnum::ContactSupport; } else if (inStrLower == "rebootrequiredtofinish") { result = ExpectedReturnCodeEnum::RebootRequiredToFinish; } else if (inStrLower == "rebootrequiredforinstall") { result = ExpectedReturnCodeEnum::RebootRequiredForInstall; } else if (inStrLower == "rebootinitiated") { result = ExpectedReturnCodeEnum::RebootInitiated; } else if (inStrLower == "cancelledbyuser") { result = ExpectedReturnCodeEnum::CancelledByUser; } else if (inStrLower == "alreadyinstalled") { result = ExpectedReturnCodeEnum::AlreadyInstalled; } else if (inStrLower == "downgrade") { result = ExpectedReturnCodeEnum::Downgrade; } else if (inStrLower == "blockedbypolicy") { result = ExpectedReturnCodeEnum::BlockedByPolicy; } else if (inStrLower == "systemnotsupported") { result = ExpectedReturnCodeEnum::SystemNotSupported; } else if (inStrLower == "custom") { result = ExpectedReturnCodeEnum::Custom; } return result; } InstalledFileTypeEnum ConvertToInstalledFileTypeEnum(const std::string& in) { std::string inStrLower = Utility::ToLower(in); InstalledFileTypeEnum result = InstalledFileTypeEnum::Unknown; if (inStrLower == "launch") { result = InstalledFileTypeEnum::Launch; } else if (inStrLower == "uninstall") { result = InstalledFileTypeEnum::Uninstall; } else if (inStrLower == "other") { result = InstalledFileTypeEnum::Other; } return result; } IconFileTypeEnum ConvertToIconFileTypeEnum(std::string_view in) { std::string inStrLower = Utility::ToLower(in); IconFileTypeEnum result = IconFileTypeEnum::Unknown; if (inStrLower == "jpeg") { result = IconFileTypeEnum::Jpeg; } else if (inStrLower == "png") { result = IconFileTypeEnum::Png; } else if (inStrLower == "ico") { result = IconFileTypeEnum::Ico; } return result; } IconThemeEnum ConvertToIconThemeEnum(std::string_view in) { std::string inStrLower = Utility::ToLower(in); IconThemeEnum result = IconThemeEnum::Unknown; if (inStrLower == "default") { result = IconThemeEnum::Default; } else if (inStrLower == "dark") { result = IconThemeEnum::Dark; } else if (inStrLower == "light") { result = IconThemeEnum::Light; } else if (inStrLower == "highcontrast") { result = IconThemeEnum::HighContrast; } return result; } IconResolutionEnum ConvertToIconResolutionEnum(std::string_view in) { std::string inStrLower = Utility::ToLower(in); IconResolutionEnum result = IconResolutionEnum::Unknown; if (inStrLower == "custom") { result = IconResolutionEnum::Custom; } else if (inStrLower == "16x16") { result = IconResolutionEnum::Square16; } else if (inStrLower == "20x20") { result = IconResolutionEnum::Square20; } else if (inStrLower == "24x24") { result = IconResolutionEnum::Square24; } else if (inStrLower == "30x30") { result = IconResolutionEnum::Square30; } else if (inStrLower == "32x32") { result = IconResolutionEnum::Square32; } else if (inStrLower == "36x36") { result = IconResolutionEnum::Square36; } else if (inStrLower == "40x40") { result = IconResolutionEnum::Square40; } else if (inStrLower == "48x48") { result = IconResolutionEnum::Square48; } else if (inStrLower == "60x60") { result = IconResolutionEnum::Square60; } else if (inStrLower == "64x64") { result = IconResolutionEnum::Square64; } else if (inStrLower == "72x72") { result = IconResolutionEnum::Square72; } else if (inStrLower == "80x80") { result = IconResolutionEnum::Square80; } else if (inStrLower == "96x96") { result = IconResolutionEnum::Square96; } else if (inStrLower == "256x256") { result = IconResolutionEnum::Square256; } return result; } std::string_view InstallerTypeToString(InstallerTypeEnum installerType) { switch (installerType) { case InstallerTypeEnum::Exe: return "exe"sv; case InstallerTypeEnum::Inno: return "inno"sv; case InstallerTypeEnum::Msi: return "msi"sv; case InstallerTypeEnum::Msix: return "msix"sv; case InstallerTypeEnum::Nullsoft: return "nullsoft"sv; case InstallerTypeEnum::Wix: return "wix"sv; case InstallerTypeEnum::Zip: return "zip"sv; case InstallerTypeEnum::Burn: return "burn"sv; case InstallerTypeEnum::MSStore: return "msstore"sv; case InstallerTypeEnum::Portable: return "portable"sv; case InstallerTypeEnum::Font: return "font"sv; } return "unknown"sv; } std::string_view InstallerSwitchTypeToString(InstallerSwitchType installerSwitchType) { switch (installerSwitchType) { case InstallerSwitchType::Custom: return "Custom"sv; case InstallerSwitchType::Silent: return "Silent"sv; case InstallerSwitchType::SilentWithProgress: return "SilentWithProgress"sv; case InstallerSwitchType::Interactive: return "Interactive"sv; case InstallerSwitchType::Language: return "Language"sv; case InstallerSwitchType::Log: return "Log"sv; case InstallerSwitchType::InstallLocation: return "InstallLocation"sv; case InstallerSwitchType::Update: return "Upgrade"sv; case InstallerSwitchType::Repair: return "Repair"sv; } return "Unknown"sv; } std::string_view ElevationRequirementToString(ElevationRequirementEnum elevationRequirement) { switch (elevationRequirement) { case ElevationRequirementEnum::ElevationRequired: return "elevationRequired"sv; case ElevationRequirementEnum::ElevationProhibited: return "elevationProhibited"sv; case ElevationRequirementEnum::ElevatesSelf: return "elevatesSelf"sv; } return "unknown"sv; } std::string_view UnsupportedArgumentToString(UnsupportedArgumentEnum unsupportedArgument) { switch (unsupportedArgument) { case UnsupportedArgumentEnum::Log: return "log"sv; case UnsupportedArgumentEnum::Location: return "location"sv; } return "unknown"sv; } std::string_view InstallModeToString(InstallModeEnum installMode) { switch (installMode) { case InstallModeEnum::Interactive: return "interactive"sv; case InstallModeEnum::Silent: return "silent"sv; case InstallModeEnum::SilentWithProgress: return "silentWithProgress"sv; } return "unknown"sv; } std::string_view PlatformToString(PlatformEnum platform, bool shortString) { switch (platform) { case PlatformEnum::Desktop: return shortString ? "Desktop" : "Windows.Desktop"sv; case PlatformEnum::Universal: return shortString ? "Universal" : "Windows.Universal"sv; case PlatformEnum::IoT: return shortString ? "IoT" : "Windows.IoT"sv; case PlatformEnum::Holographic: return shortString ? "Holographic" : "Windows.Holographic"sv; case PlatformEnum::Team: return shortString ? "Team" : "Windows.Team"sv; } return "Unknown"sv; } std::string_view UpdateBehaviorToString(UpdateBehaviorEnum updateBehavior) { switch (updateBehavior) { case UpdateBehaviorEnum::Install: return "install"sv; case UpdateBehaviorEnum::UninstallPrevious: return "uninstallPrevious"sv; case UpdateBehaviorEnum::Deny: return "deny"sv; } return "unknown"sv; } std::string_view RepairBehaviorToString(RepairBehaviorEnum repairBehavior) { switch (repairBehavior) { case AppInstaller::Manifest::RepairBehaviorEnum::Modify: return "modify"sv; case AppInstaller::Manifest::RepairBehaviorEnum::Installer: return "installer"sv; case AppInstaller::Manifest::RepairBehaviorEnum::Uninstaller: return "uninstaller"sv; } return "unknown"sv; } std::string_view ScopeToString(ScopeEnum scope) { switch (scope) { case ScopeEnum::User: return "User"sv; case ScopeEnum::Machine: return "Machine"sv; } return "Unknown"sv; } std::string_view InstalledFileTypeToString(InstalledFileTypeEnum installedFileType) { switch (installedFileType) { case InstalledFileTypeEnum::Launch: return "launch"sv; case InstalledFileTypeEnum::Uninstall: return "uninstall"sv; case InstalledFileTypeEnum::Other: return "other"sv; } return "unknown"sv; } std::string_view IconFileTypeToString(IconFileTypeEnum iconFileType) { switch (iconFileType) { case IconFileTypeEnum::Ico: return "ico"sv; case IconFileTypeEnum::Jpeg: return "jpeg"sv; case IconFileTypeEnum::Png: return "png"sv; } return "unknown"sv; } std::string_view IconThemeToString(IconThemeEnum iconTheme) { switch (iconTheme) { case IconThemeEnum::Default: return "default"sv; case IconThemeEnum::Dark: return "dark"sv; case IconThemeEnum::Light: return "light"sv; case IconThemeEnum::HighContrast: return "highContrast"sv; } return "unknown"sv; } std::string_view IconResolutionToString(IconResolutionEnum iconResolution) { switch (iconResolution) { case IconResolutionEnum::Custom: return "custom"sv; case IconResolutionEnum::Square16: return "16x16"sv; case IconResolutionEnum::Square20: return "20x20"sv; case IconResolutionEnum::Square24: return "24x24"sv; case IconResolutionEnum::Square30: return "30x30"sv; case IconResolutionEnum::Square32: return "32x32"sv; case IconResolutionEnum::Square36: return "36x36"sv; case IconResolutionEnum::Square40: return "40x40"sv; case IconResolutionEnum::Square48: return "48x48"sv; case IconResolutionEnum::Square60: return "60x60"sv; case IconResolutionEnum::Square64: return "64x64"sv; case IconResolutionEnum::Square72: return "72x72"sv; case IconResolutionEnum::Square80: return "80x80"sv; case IconResolutionEnum::Square96: return "96x96"sv; case IconResolutionEnum::Square256: return "256x256"sv; } return "unknown"sv; } std::string_view ExpectedReturnCodeToString(ExpectedReturnCodeEnum expectedReturnCode) { switch (expectedReturnCode) { case ExpectedReturnCodeEnum::AlreadyInstalled: return "alreadyInstalled"sv; case ExpectedReturnCodeEnum::PackageInUse: return "packageInUse"sv; case ExpectedReturnCodeEnum::PackageInUseByApplication: return "packageInUseByApplication"sv; case ExpectedReturnCodeEnum::InstallInProgress: return "installInProgress"sv; case ExpectedReturnCodeEnum::FileInUse: return "fileInUse"sv; case ExpectedReturnCodeEnum::MissingDependency: return "missingDependency"sv; case ExpectedReturnCodeEnum::DiskFull: return "diskFull"sv; case ExpectedReturnCodeEnum::InsufficientMemory: return "insufficientMemory"sv; case ExpectedReturnCodeEnum::InvalidParameter: return "invalidParameter"sv; case ExpectedReturnCodeEnum::NoNetwork: return "noNetwork"sv; case ExpectedReturnCodeEnum::ContactSupport: return "contactSupport"sv; case ExpectedReturnCodeEnum::RebootRequiredToFinish: return "rebootRequiredToFinish"sv; case ExpectedReturnCodeEnum::RebootRequiredForInstall: return "rebootRequiredForInstall"sv; case ExpectedReturnCodeEnum::RebootInitiated: return "rebootInitiated"sv; case ExpectedReturnCodeEnum::CancelledByUser: return "cancelledByUser"sv; case ExpectedReturnCodeEnum::Downgrade: return "downgrade"sv; case ExpectedReturnCodeEnum::BlockedByPolicy: return "blockedByPolicy"sv; case ExpectedReturnCodeEnum::SystemNotSupported: return "systemNotSupported"sv; case ExpectedReturnCodeEnum::Custom: return "custom"sv; } return "unknown"sv; } std::string_view ManifestTypeToString(ManifestTypeEnum manifestType) { switch (manifestType) { case ManifestTypeEnum::DefaultLocale: return "defaultLocale"sv; case ManifestTypeEnum::Installer: return "installer"sv; case ManifestTypeEnum::Locale: return "locale"sv; case ManifestTypeEnum::Merged: return "merged"sv; case ManifestTypeEnum::Singleton: return "singleton"sv; case ManifestTypeEnum::Version: return "version"sv; } return "unknown"sv; } bool DoesInstallerTypeUsePackageFamilyName(InstallerTypeEnum installerType) { return (installerType == InstallerTypeEnum::Msix || installerType == InstallerTypeEnum::MSStore); } bool DoAnyAppsAndFeaturesEntriesUsePackageFamilyName(const std::vector& entries) { for (const AppsAndFeaturesEntry& entry : entries) { if (DoesInstallerTypeUsePackageFamilyName(entry.InstallerType)) { return true; } } return false; } bool DoesInstallerTypeUseProductCode(InstallerTypeEnum installerType) { return ( installerType == InstallerTypeEnum::Exe || installerType == InstallerTypeEnum::Inno || installerType == InstallerTypeEnum::Msi || installerType == InstallerTypeEnum::Nullsoft || installerType == InstallerTypeEnum::Wix || installerType == InstallerTypeEnum::Burn || installerType == InstallerTypeEnum::Portable ); } bool DoesInstallerTypeWriteAppsAndFeaturesEntry(InstallerTypeEnum installerType) { return ( installerType == InstallerTypeEnum::Exe || installerType == InstallerTypeEnum::Inno || installerType == InstallerTypeEnum::Msi || installerType == InstallerTypeEnum::Nullsoft || installerType == InstallerTypeEnum::Wix || installerType == InstallerTypeEnum::Burn || installerType == InstallerTypeEnum::Portable ); } bool DoesInstallerTypeSupportArpVersionRange(InstallerTypeEnum installerType) { return ( installerType == InstallerTypeEnum::Exe || installerType == InstallerTypeEnum::Inno || installerType == InstallerTypeEnum::Msi || installerType == InstallerTypeEnum::Nullsoft || installerType == InstallerTypeEnum::Wix || installerType == InstallerTypeEnum::Burn || installerType == InstallerTypeEnum::Msix ); } bool DoesInstallerTypeIgnoreScopeFromManifest(InstallerTypeEnum installerType) { return installerType == InstallerTypeEnum::Font || installerType == InstallerTypeEnum::Portable || installerType == InstallerTypeEnum::Msix || installerType == InstallerTypeEnum::MSStore; } bool DoesInstallerTypeRequireAdminForMachineScopeInstall(InstallerTypeEnum installerType) { return installerType == InstallerTypeEnum::Font || installerType == InstallerTypeEnum::Portable || installerType == InstallerTypeEnum::MSStore || installerType == InstallerTypeEnum::Msix; } bool DoesInstallerTypeRequireRepairBehaviorForRepair(InstallerTypeEnum installerType) { return installerType == InstallerTypeEnum::Burn || installerType == InstallerTypeEnum::Inno || installerType == InstallerTypeEnum::Nullsoft || installerType == InstallerTypeEnum::Exe; } bool IsArchiveType(InstallerTypeEnum installerType) { return (installerType == InstallerTypeEnum::Zip); } bool IsPortableType(InstallerTypeEnum installerType) { return (installerType == InstallerTypeEnum::Portable); } bool DoesInstallerTypeSupportMultipleNestedInstallers(InstallerTypeEnum installerType) { return ( installerType == InstallerTypeEnum::Portable || installerType == InstallerTypeEnum::Font ); } bool IsNestedInstallerTypeSupported(InstallerTypeEnum nestedInstallerType) { return ( nestedInstallerType == InstallerTypeEnum::Exe || nestedInstallerType == InstallerTypeEnum::Inno || nestedInstallerType == InstallerTypeEnum::Msi || nestedInstallerType == InstallerTypeEnum::Nullsoft || nestedInstallerType == InstallerTypeEnum::Wix || nestedInstallerType == InstallerTypeEnum::Burn || nestedInstallerType == InstallerTypeEnum::Portable || nestedInstallerType == InstallerTypeEnum::Msix || nestedInstallerType == InstallerTypeEnum::Font ); } bool IsInstallerTypeCompatible(InstallerTypeEnum type1, InstallerTypeEnum type2) { // Unknown type cannot be compatible with any other if (type1 == InstallerTypeEnum::Unknown || type2 == InstallerTypeEnum::Unknown) { return false; } // Not unknown, so must be compatible if (type1 == type2) { return true; } CompatibilitySet set1 = GetCompatibilitySet(type1); CompatibilitySet set2 = GetCompatibilitySet(type2); // If either is none, they can't be compatible if (set1 == CompatibilitySet::None || set2 == CompatibilitySet::None) { return false; } return set1 == set2; } std::map GetDefaultKnownSwitches(InstallerTypeEnum installerType) { switch (installerType) { case InstallerTypeEnum::Burn: case InstallerTypeEnum::Wix: case InstallerTypeEnum::Msi: return { {InstallerSwitchType::Silent, ManifestInstaller::string_t("/quiet /norestart")}, {InstallerSwitchType::SilentWithProgress, ManifestInstaller::string_t("/passive /norestart")}, {InstallerSwitchType::Log, ManifestInstaller::string_t("/log \"" + std::string(ARG_TOKEN_LOGPATH) + "\"")}, {InstallerSwitchType::InstallLocation, ManifestInstaller::string_t("TARGETDIR=\"" + std::string(ARG_TOKEN_INSTALLPATH) + "\"")} }; case InstallerTypeEnum::Nullsoft: return { {InstallerSwitchType::Silent, ManifestInstaller::string_t("/S")}, {InstallerSwitchType::SilentWithProgress, ManifestInstaller::string_t("/S")}, {InstallerSwitchType::InstallLocation, ManifestInstaller::string_t("/D=" + std::string(ARG_TOKEN_INSTALLPATH))} }; case InstallerTypeEnum::Inno: return { {InstallerSwitchType::Silent, ManifestInstaller::string_t("/SP- /VERYSILENT /SUPPRESSMSGBOXES /NORESTART")}, {InstallerSwitchType::SilentWithProgress, ManifestInstaller::string_t("/SP- /SILENT /SUPPRESSMSGBOXES /NORESTART")}, {InstallerSwitchType::Log, ManifestInstaller::string_t("/LOG=\"" + std::string(ARG_TOKEN_LOGPATH) + "\"")}, {InstallerSwitchType::InstallLocation, ManifestInstaller::string_t("/DIR=\"" + std::string(ARG_TOKEN_INSTALLPATH) + "\"")} }; default: return {}; } } RepairBehaviorEnum ConvertToRepairBehaviorEnum(std::string_view in) { std::string inStrLower = Utility::ToLower(in); RepairBehaviorEnum result = RepairBehaviorEnum::Unknown; if (inStrLower == "installer") { result = RepairBehaviorEnum::Installer; } else if (inStrLower == "uninstaller") { result = RepairBehaviorEnum::Uninstaller; } else if (inStrLower == "modify") { result = RepairBehaviorEnum::Modify; } return result; } std::map GetDefaultKnownReturnCodes(InstallerTypeEnum installerType) { switch (installerType) { case InstallerTypeEnum::Burn: case InstallerTypeEnum::Wix: case InstallerTypeEnum::Msi: // See https://docs.microsoft.com/windows/win32/msi/error-codes return { { ERROR_INSTALL_ALREADY_RUNNING, ExpectedReturnCodeEnum::InstallInProgress }, { ERROR_DISK_FULL, ExpectedReturnCodeEnum::DiskFull }, { ERROR_INSTALL_SERVICE_FAILURE, ExpectedReturnCodeEnum::ContactSupport }, { ERROR_SUCCESS_REBOOT_REQUIRED, ExpectedReturnCodeEnum::RebootRequiredToFinish }, { ERROR_SUCCESS_REBOOT_INITIATED, ExpectedReturnCodeEnum::RebootInitiated }, { ERROR_INSTALL_USEREXIT, ExpectedReturnCodeEnum::CancelledByUser }, { ERROR_PRODUCT_VERSION, ExpectedReturnCodeEnum::AlreadyInstalled }, { ERROR_INSTALL_REJECTED, ExpectedReturnCodeEnum::SystemNotSupported }, { ERROR_INSTALL_PACKAGE_REJECTED, ExpectedReturnCodeEnum::BlockedByPolicy }, { ERROR_INSTALL_TRANSFORM_REJECTED, ExpectedReturnCodeEnum::BlockedByPolicy }, { ERROR_PATCH_PACKAGE_REJECTED, ExpectedReturnCodeEnum::BlockedByPolicy }, { ERROR_PATCH_REMOVAL_DISALLOWED, ExpectedReturnCodeEnum::BlockedByPolicy }, { ERROR_INSTALL_REMOTE_DISALLOWED, ExpectedReturnCodeEnum::BlockedByPolicy }, { ERROR_INVALID_PARAMETER, ExpectedReturnCodeEnum::InvalidParameter }, { ERROR_INVALID_TABLE, ExpectedReturnCodeEnum::InvalidParameter }, { ERROR_INVALID_COMMAND_LINE, ExpectedReturnCodeEnum::InvalidParameter }, { ERROR_INVALID_PATCH_XML, ExpectedReturnCodeEnum::InvalidParameter }, { ERROR_INSTALL_LANGUAGE_UNSUPPORTED, ExpectedReturnCodeEnum::SystemNotSupported }, { ERROR_INSTALL_PLATFORM_UNSUPPORTED, ExpectedReturnCodeEnum::SystemNotSupported }, }; case InstallerTypeEnum::Inno: // See https://jrsoftware.org/ishelp/index.php?topic=setupexitcodes return { { 2, ExpectedReturnCodeEnum::CancelledByUser }, { 5, ExpectedReturnCodeEnum::CancelledByUser }, { 8, ExpectedReturnCodeEnum::RebootRequiredForInstall }, }; case InstallerTypeEnum::Msix: // See https://docs.microsoft.com/en-us/windows/win32/appxpkg/troubleshooting return { { HRESULT_FROM_WIN32(ERROR_INSTALL_PREREQUISITE_FAILED), ExpectedReturnCodeEnum::MissingDependency }, { HRESULT_FROM_WIN32(ERROR_INSTALL_RESOLVE_DEPENDENCY_FAILED), ExpectedReturnCodeEnum::MissingDependency }, { HRESULT_FROM_WIN32(ERROR_INSTALL_OPTIONAL_PACKAGE_REQUIRES_MAIN_PACKAGE), ExpectedReturnCodeEnum::MissingDependency }, { HRESULT_FROM_WIN32(ERROR_INSTALL_OUT_OF_DISK_SPACE), ExpectedReturnCodeEnum::DiskFull }, { HRESULT_FROM_WIN32(ERROR_INSTALL_CANCEL), ExpectedReturnCodeEnum::CancelledByUser }, { HRESULT_FROM_WIN32(ERROR_PACKAGE_ALREADY_EXISTS), ExpectedReturnCodeEnum::AlreadyInstalled }, { HRESULT_FROM_WIN32(ERROR_INSTALL_PACKAGE_DOWNGRADE), ExpectedReturnCodeEnum::Downgrade }, { HRESULT_FROM_WIN32(ERROR_DEPLOYMENT_BLOCKED_BY_POLICY), ExpectedReturnCodeEnum::BlockedByPolicy}, { HRESULT_FROM_WIN32(ERROR_INSTALL_POLICY_FAILURE), ExpectedReturnCodeEnum::BlockedByPolicy}, { HRESULT_FROM_WIN32(ERROR_PACKAGES_IN_USE), ExpectedReturnCodeEnum::PackageInUse }, { HRESULT_FROM_WIN32(ERROR_INSTALL_WRONG_PROCESSOR_ARCHITECTURE), ExpectedReturnCodeEnum::SystemNotSupported }, { HRESULT_FROM_WIN32(ERROR_PACKAGE_NOT_SUPPORTED_ON_FILESYSTEM), ExpectedReturnCodeEnum::SystemNotSupported }, { HRESULT_FROM_WIN32(ERROR_DEPLOYMENT_OPTION_NOT_SUPPORTED), ExpectedReturnCodeEnum::SystemNotSupported }, { HRESULT_FROM_WIN32(ERROR_PACKAGE_LACKS_CAPABILITY_TO_DEPLOY_ON_HOST), ExpectedReturnCodeEnum::SystemNotSupported }, }; default: return {}; } } void DependencyList::Add(const Dependency& newDependency) { Dependency* existingDependency = this->HasDependency(newDependency); if (existingDependency != NULL) { if (newDependency.MinVersion > existingDependency->MinVersion) { existingDependency->MinVersion = newDependency.MinVersion; } } else { m_dependencies.push_back(newDependency); } } void DependencyList::Add(const DependencyList& otherDependencyList) { for (const auto& dependency : otherDependencyList.m_dependencies) { this->Add(dependency); } } bool DependencyList::HasAny() const { return !m_dependencies.empty(); } bool DependencyList::HasAnyOf(DependencyType type) const { for (const auto& dependency : m_dependencies) { if (dependency.Type == type) return true; }; return false; } Dependency* DependencyList::HasDependency(const Dependency& dependencyToSearch) { for (auto& dependency : m_dependencies) { if (dependency.Type == dependencyToSearch.Type && ICUCaseInsensitiveEquals(dependency.Id(), dependencyToSearch.Id())) { return &dependency; } } return nullptr; } // for testing purposes bool DependencyList::HasExactDependency(DependencyType type, const string_t& id, const string_t& minVersion) const { for (const auto& dependency : m_dependencies) { if (dependency.Type == type && Utility::ICUCaseInsensitiveEquals(dependency.Id(), id)) { if (!minVersion.empty()) { return dependency.MinVersion == Utility::Version{ minVersion }; } else { return true; } } } return false; } size_t DependencyList::Size() const { return m_dependencies.size(); } void DependencyList::ApplyToType(DependencyType type, std::function func) const { for (const auto& dependency : m_dependencies) { if (dependency.Type == type) func(dependency); } } void DependencyList::ApplyToAll(std::function func) const { for (const auto& dependency : m_dependencies) { func(dependency); } } bool DependencyList::Empty() const { return m_dependencies.empty(); } void DependencyList::Clear() { m_dependencies.clear(); } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/ManifestComparator.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include #include using namespace AppInstaller::Manifest; namespace AppInstaller::Manifest { std::ostream& operator<<(std::ostream& out, const ManifestInstaller& installer) { return out << '[' << AppInstaller::Utility::ToString(installer.Arch) << ',' << AppInstaller::Manifest::InstallerTypeToString(installer.EffectiveInstallerType()) << ',' << AppInstaller::Manifest::ScopeToString(installer.Scope) << ',' << installer.Locale << ']'; } } namespace AppInstaller::Manifest { namespace { struct PortableInstallFilter : public details::FilterField { PortableInstallFilter() : details::FilterField("Portable Install") {} InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { // Unvirtualized resources restricted capability is only supported for >= 10.0.18362 // TODO: Add support for OS versions that don't support virtualization. if (installer.EffectiveInstallerType() == InstallerTypeEnum::Portable && !Runtime::IsCurrentOSVersionGreaterThanOrEqual(Utility::Version("10.0.18362"))) { return InapplicabilityFlags::OSVersion; } return InapplicabilityFlags::None; } std::string ExplainInapplicable(const ManifestInstaller&) override { std::string result = "Current OS is lower than supported MinOSVersion (10.0.18362) for Portable install"; return result; } }; struct OSVersionFilter : public details::FilterField { OSVersionFilter() : details::FilterField("OS Version") {} InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { if (installer.MinOSVersion.empty() || Runtime::IsCurrentOSVersionGreaterThanOrEqual(Utility::Version(installer.MinOSVersion))) { return InapplicabilityFlags::None; } return InapplicabilityFlags::OSVersion; } std::string ExplainInapplicable(const ManifestInstaller& installer) override { std::string result = "Current OS is lower than MinOSVersion "; result += installer.MinOSVersion; return result; } }; struct MachineArchitectureComparator : public details::ComparisonField { MachineArchitectureComparator() : details::ComparisonField("Machine Architecture") {} MachineArchitectureComparator(std::vector allowedArchitectures) : details::ComparisonField("Machine Architecture"), m_allowedArchitectures(std::move(allowedArchitectures)) { AICLI_LOG(CLI, Verbose, << "Architecture Comparator created with allowed architectures: " << Utility::ConvertContainerToString(m_allowedArchitectures, Utility::ToString)); } static std::unique_ptr Create(const ManifestComparator::Options& options) { if (!options.AllowedArchitectures.empty()) { // If the incoming data contains elements, we will use them to construct a final allowed list. // The algorithm is to take elements until we find Unknown, which indicates that any architecture is // acceptable at this point. The system supported set of architectures will then be placed at the end. std::vector result; bool addRemainingApplicableArchitectures = false; for (Utility::Architecture architecture : options.AllowedArchitectures) { if (architecture == Utility::Architecture::Unknown) { addRemainingApplicableArchitectures = true; break; } // If the architecture is applicable and not already in our result set... if ((options.SkipApplicabilityCheck || Utility::IsApplicableArchitecture(architecture) != Utility::InapplicableArchitecture) && Utility::IsApplicableArchitecture(architecture, result) == Utility::InapplicableArchitecture) { result.push_back(architecture); } } if (addRemainingApplicableArchitectures) { for (Utility::Architecture architecture : Utility::GetApplicableArchitectures()) { if (Utility::IsApplicableArchitecture(architecture, result) == Utility::InapplicableArchitecture) { result.push_back(architecture); } } } return std::make_unique(std::move(result)); } else { return std::make_unique(); } } InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { if (CheckAllowedArchitecture(installer.Arch) == Utility::InapplicableArchitecture || IsSystemArchitectureUnsupportedByInstaller(installer)) { return InapplicabilityFlags::MachineArchitecture; } return InapplicabilityFlags::None; } std::string ExplainInapplicable(const ManifestInstaller& installer) override { std::string result; if (Utility::IsApplicableArchitecture(installer.Arch) == Utility::InapplicableArchitecture) { result = "Machine is not compatible with "; result += Utility::ToString(installer.Arch); } else if (IsSystemArchitectureUnsupportedByInstaller(installer)) { result = "System architecture is unsupported by installer"; } else { result = "Architecture was excluded by caller : "; result += Utility::ToString(installer.Arch); } return result; } details::ComparisonResult IsFirstBetter(const ManifestInstaller& first, const ManifestInstaller& second) override { auto arch1 = CheckAllowedArchitecture(first.Arch); auto arch2 = CheckAllowedArchitecture(second.Arch); if (arch1 > arch2) { // A match with the primary architecture is strong return (first.Arch == GetStrongArchitectureMatch() ? details::ComparisonResult::StrongPositive : details::ComparisonResult::WeakPositive); } return details::ComparisonResult::Negative; } private: int CheckAllowedArchitecture(Utility::Architecture architecture) { if (m_allowedArchitectures.empty()) { return Utility::IsApplicableArchitecture(architecture); } else { return Utility::IsApplicableArchitecture(architecture, m_allowedArchitectures); } } bool IsSystemArchitectureUnsupportedByInstaller(const ManifestInstaller& installer) { auto unsupportedItr = std::find( installer.UnsupportedOSArchitectures.begin(), installer.UnsupportedOSArchitectures.end(), Utility::GetSystemArchitecture()); return unsupportedItr != installer.UnsupportedOSArchitectures.end(); } Utility::Architecture GetStrongArchitectureMatch() { // If we have a preferential order, treat the first entry as strong. // Otherwise, treat the system architecture as strong (which is always first in the default order). return m_allowedArchitectures.empty() ? Utility::GetSystemArchitecture() : m_allowedArchitectures.front(); } std::vector m_allowedArchitectures; }; struct InstallerTypeComparator : public details::ComparisonField { InstallerTypeComparator(std::vector preference, std::vector requirement) : details::ComparisonField("Installer Type"), m_preference(std::move(preference)), m_requirement(std::move(requirement)) { m_preferenceAsString = Utility::ConvertContainerToString(m_preference, InstallerTypeToString); m_requirementAsString = Utility::ConvertContainerToString(m_requirement, InstallerTypeToString); AICLI_LOG(CLI, Verbose, << "InstallerType Comparator created with Required InstallerTypes: " << m_requirementAsString << " , Preferred InstallerTypes: " << m_preferenceAsString); } static std::unique_ptr Create(const ManifestComparator::Options& options) { std::vector preference; std::vector requirement; if (options.RequestedInstallerType) { requirement.emplace_back(options.RequestedInstallerType.value()); } else { preference = Settings::User().Get(); requirement = Settings::User().Get(); } if (!preference.empty() || !requirement.empty()) { return std::make_unique(preference, requirement); } else { return {}; } } std::string ExplainInapplicable(const ManifestInstaller& installer) override { std::string result = "InstallerType ["; result += InstallerTypeToString(installer.EffectiveInstallerType()); result += "] does not match required InstallerTypes: "; result += m_requirementAsString; return result; } InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { if (!m_requirement.empty()) { // The installer is applicable if the effective or base installer type matches. if (ContainsInstallerType(m_requirement, installer.EffectiveInstallerType()) || ContainsInstallerType(m_requirement, installer.BaseInstallerType)) { return InapplicabilityFlags::None; } return InapplicabilityFlags::InstallerType; } else { return InapplicabilityFlags::None; } } details::ComparisonResult IsFirstBetter(const ManifestInstaller& first, const ManifestInstaller& second) override { if (m_preference.empty()) { return details::ComparisonResult::Negative; } for (InstallerTypeEnum installerTypePreference : m_preference) { bool isFirstInstallerTypePreferred = first.EffectiveInstallerType() == installerTypePreference || first.BaseInstallerType == installerTypePreference; bool isSecondInstallerTypePreferred = second.EffectiveInstallerType() == installerTypePreference || second.BaseInstallerType == installerTypePreference; if (isFirstInstallerTypePreferred && isSecondInstallerTypePreferred) { return details::ComparisonResult::Negative; } else if (isFirstInstallerTypePreferred != isSecondInstallerTypePreferred) { // Treating this as a weak positive because one can use requirements to guarantee the installer type if necessary. return (isFirstInstallerTypePreferred ? details::ComparisonResult::WeakPositive : details::ComparisonResult::Negative); } } return details::ComparisonResult::Negative; } private: std::vector m_preference; std::vector m_requirement; std::string m_preferenceAsString; std::string m_requirementAsString; bool ContainsInstallerType(const std::vector& selection, InstallerTypeEnum installerType) { return std::find(selection.begin(), selection.end(), installerType) != selection.end(); } }; struct InstalledTypeFilter : public details::FilterField { InstalledTypeFilter(InstallerTypeEnum installedType) : details::FilterField("Installed Type"), m_installedType(installedType) {} static std::unique_ptr Create(const ManifestComparator::Options& options) { if (options.CurrentlyInstalledType) { InstallerTypeEnum installedType = options.CurrentlyInstalledType.value(); if (installedType != InstallerTypeEnum::Unknown) { return std::make_unique(installedType); } } return {}; } InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { return IsInstallerCompatibleWith(installer, m_installedType) ? InapplicabilityFlags::None : InapplicabilityFlags::InstalledType; } std::string ExplainInapplicable(const ManifestInstaller& installer) override { std::string result = "Installed package type '" + std::string{ InstallerTypeToString(m_installedType) } + "' is not compatible with installer type " + std::string{ InstallerTypeToString(installer.EffectiveInstallerType()) }; std::string arpInstallerTypes; for (const auto& entry : installer.AppsAndFeaturesEntries) { arpInstallerTypes += " " + std::string{ InstallerTypeToString(entry.InstallerType) }; } if (!arpInstallerTypes.empty()) { result += ", or with accepted type(s)" + arpInstallerTypes; } return result; } private: // The installer is compatible if it's type or any of its ARP entries' type matches the installed type static bool IsInstallerCompatibleWith(const ManifestInstaller& installer, InstallerTypeEnum type) { if (IsInstallerTypeCompatible(installer.EffectiveInstallerType(), type)) { return true; } auto itr = std::find_if( installer.AppsAndFeaturesEntries.begin(), installer.AppsAndFeaturesEntries.end(), [=](AppsAndFeaturesEntry arpEntry) { return IsInstallerTypeCompatible(arpEntry.InstallerType, type); }); if (itr != installer.AppsAndFeaturesEntries.end()) { return true; } return false; } InstallerTypeEnum m_installedType; }; struct InstalledScopeFilter : public details::FilterField { InstalledScopeFilter(ScopeEnum requirement) : details::FilterField("Installed Scope"), m_requirement(requirement) {} static std::unique_ptr Create(const ManifestComparator::Options& options) { // Check for an existing install and require a matching scope. if (options.CurrentlyInstalledScope) { ScopeEnum installedScope = options.CurrentlyInstalledScope.value(); if (installedScope != ScopeEnum::Unknown) { return std::make_unique(installedScope); } } return {}; } InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { // We have to assume the unknown scope will match our required scope, or the entire catalog would stop working for upgrade. if (installer.Scope == ScopeEnum::Unknown || installer.Scope == m_requirement || DoesInstallerTypeIgnoreScopeFromManifest(installer.EffectiveInstallerType())) { return InapplicabilityFlags::None; } return InapplicabilityFlags::InstalledScope; } std::string ExplainInapplicable(const ManifestInstaller& installer) override { std::string result = "Installer scope does not match currently installed scope: "; result += ScopeToString(installer.Scope); result += " != "; result += ScopeToString(m_requirement); return result; } private: ScopeEnum m_requirement; }; struct ScopeComparator : public details::ComparisonField { ScopeComparator(ScopeEnum preference, ScopeEnum requirement, bool allowUnknownInAdditionToRequired) : details::ComparisonField("Scope"), m_preference(preference), m_requirement(requirement), m_allowUnknownInAdditionToRequired(allowUnknownInAdditionToRequired) {} static std::unique_ptr Create(const ManifestComparator::Options& options) { // Preference will always come from settings ScopeEnum preference = Settings::User().Get(); // Requirement may come from args or settings; args overrides settings. ScopeEnum requirement = ScopeEnum::Unknown; if (options.RequestedInstallerScope) { requirement = options.RequestedInstallerScope.value(); } else { requirement = Settings::User().Get(); } bool allowUnknownInAdditionToRequired = false; if (options.AllowUnknownScope) { allowUnknownInAdditionToRequired = options.AllowUnknownScope.value(); // Force the required type to be preferred over Unknown if (requirement != ScopeEnum::Unknown) { preference = requirement; } } if (preference != ScopeEnum::Unknown || requirement != ScopeEnum::Unknown) { return std::make_unique(preference, requirement, allowUnknownInAdditionToRequired); } else { return {}; } } InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { // Applicable if one of: // 1. No requirement (aka is Unknown) // 2. Requirement met // 3. Installer scope is Unknown and this has been explicitly allowed // 4. The installer type is scope agnostic (we can control it) if (m_requirement == ScopeEnum::Unknown || installer.Scope == m_requirement || (installer.Scope == ScopeEnum::Unknown && m_allowUnknownInAdditionToRequired) || DoesInstallerTypeIgnoreScopeFromManifest(installer.EffectiveInstallerType())) { return InapplicabilityFlags::None; } return InapplicabilityFlags::Scope; } std::string ExplainInapplicable(const ManifestInstaller& installer) override { std::string result = "Installer scope does not match required scope: "; result += ScopeToString(installer.Scope); result += " != "; result += ScopeToString(m_requirement); return result; } details::ComparisonResult IsFirstBetter(const ManifestInstaller& first, const ManifestInstaller& second) override { if (m_preference != ScopeEnum::Unknown && first.Scope == m_preference && second.Scope != m_preference) { // When the second input is unknown, this is a weak result. If it is not (and therefore the opposite of the preference), this is strong. return (second.Scope == ScopeEnum::Unknown ? details::ComparisonResult::WeakPositive : details::ComparisonResult::StrongPositive); } return details::ComparisonResult::Negative; } private: ScopeEnum m_preference; ScopeEnum m_requirement; bool m_allowUnknownInAdditionToRequired; }; struct LocaleComparator : public details::ComparisonField { LocaleComparator(std::vector preference, std::vector requirement, bool isInstalledLocale) : details::ComparisonField("Locale"), m_preference(std::move(preference)), m_requirement(std::move(requirement)), m_isInstalledLocale(isInstalledLocale) { m_requirementAsString = Utility::ConvertContainerToString(m_requirement); m_preferenceAsString = Utility::ConvertContainerToString(m_preference); AICLI_LOG(CLI, Verbose, << "Locale Comparator created with Required Locales: " << m_requirementAsString << " , Preferred Locales: " << m_preferenceAsString << " , IsInstalledLocale: " << m_isInstalledLocale); } static std::unique_ptr Create(const ManifestComparator::Options& options) { std::vector preference; std::vector requirement; // This is for installed locale case, where the locale is a preference but requires at least compatible match. bool isInstalledLocale = false; // Requirement may come from args, previous user intent or settings; args overrides previous user intent then settings. if (options.RequestedInstallerLocale) { requirement.emplace_back(options.RequestedInstallerLocale.value()); } else if (options.PreviousUserIntentLocale) { requirement.emplace_back(options.PreviousUserIntentLocale.value()); isInstalledLocale = true; } else { if (!options.CurrentlyInstalledLocale) { // If it's an upgrade of previous package, no need to set requirements from settings // as previous installed locale will be used later. requirement = Settings::User().Get(); } } // Preference will come from previous installed locale, winget settings or Preferred Languages settings. // Previous installed locale goes first, then winget settings, then Preferred Languages settings. // Previous installed locale also requires at least compatible locale match. if (options.CurrentlyInstalledLocale) { preference.emplace_back(options.CurrentlyInstalledLocale.value()); isInstalledLocale = true; } else { preference = Settings::User().Get(); if (preference.empty()) { preference = Locale::GetUserPreferredLanguages(); } } if (!preference.empty() || !requirement.empty()) { return std::make_unique(preference, requirement, isInstalledLocale); } else { return {}; } } InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { InapplicabilityFlags inapplicableFlag = m_isInstalledLocale ? InapplicabilityFlags::InstalledLocale : InapplicabilityFlags::Locale; if (!m_requirement.empty()) { // Check if requirement is satisfied for (auto const& requiredLocale : m_requirement) { if (Locale::GetDistanceOfLanguage(requiredLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsPerfectMatch) { return InapplicabilityFlags::None; } } return inapplicableFlag; } else if (m_isInstalledLocale && !m_preference.empty()) { // For installed locale preference, check at least compatible match for preference for (auto const& preferredLocale : m_preference) { // We have to assume an unknown installer locale will match our installed locale, or the entire catalog would stop working for upgrade. if (installer.Locale.empty() || Locale::GetDistanceOfLanguage(preferredLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch) { return InapplicabilityFlags::None; } } return inapplicableFlag; } else { return InapplicabilityFlags::None; } } std::string ExplainInapplicable(const ManifestInstaller& installer) override { std::string result = "Installer locale does not match required locale: "; result += installer.Locale; result += "Required locales: "; result += m_requirementAsString; result += " Or does not satisfy compatible match for Preferred Locales: "; result += m_preferenceAsString; return result; } details::ComparisonResult IsFirstBetter(const ManifestInstaller& first, const ManifestInstaller& second) override { if (m_preference.empty()) { return details::ComparisonResult::Negative; } for (auto const& preferredLocale : m_preference) { double firstScore = first.Locale.empty() ? Locale::UnknownLanguageDistanceScore : Locale::GetDistanceOfLanguage(preferredLocale, first.Locale); double secondScore = second.Locale.empty() ? Locale::UnknownLanguageDistanceScore : Locale::GetDistanceOfLanguage(preferredLocale, second.Locale); if (firstScore >= Locale::MinimumDistanceScoreAsCompatibleMatch || secondScore >= Locale::MinimumDistanceScoreAsCompatibleMatch) { // This could probably be enriched to always check all locales and determine strong/weak based off of the MinimumDistanceScoreAsCompatibleMatch. return (firstScore > secondScore ? details::ComparisonResult::StrongPositive : details::ComparisonResult::Negative); } } // At this point, the installer locale matches no preference. // if first is unknown and second is no match for sure, we might prefer unknown one. return (first.Locale.empty() && !second.Locale.empty() ? details::ComparisonResult::WeakPositive : details::ComparisonResult::Negative); } private: std::vector m_preference; std::vector m_requirement; std::string m_requirementAsString; std::string m_preferenceAsString; bool m_isInstalledLocale = false; }; struct MarketFilter : public details::FilterField { MarketFilter(Manifest::string_t market) : details::FilterField("Market"), m_market(market) { AICLI_LOG(CLI, Verbose, << "Market Filter created with market: " << m_market); } static std::unique_ptr Create() { return std::make_unique(Runtime::GetOSRegion()); } InapplicabilityFlags IsApplicable(const ManifestInstaller& installer) override { // If both allowed and excluded lists are provided, we only need to check the allowed markets. if (!installer.Markets.AllowedMarkets.empty()) { // Inapplicable if NOT found if (!IsMarketInList(installer.Markets.AllowedMarkets)) { return InapplicabilityFlags::Market; } } else if (!installer.Markets.ExcludedMarkets.empty()) { // Inapplicable if found if (IsMarketInList(installer.Markets.ExcludedMarkets)) { return InapplicabilityFlags::Market; } } return InapplicabilityFlags::None; } std::string ExplainInapplicable(const ManifestInstaller& installer) override { std::string result = "Current market '" + m_market + "' does not match installer markets." + " Allowed markets: " + Utility::ConvertContainerToString(installer.Markets.AllowedMarkets) + " Excluded markets: " + Utility::ConvertContainerToString(installer.Markets.ExcludedMarkets); return result; } private: bool IsMarketInList(const std::vector markets) { return markets.end() != std::find_if( markets.begin(), markets.end(), [&](const auto& m) { return Utility::CaseInsensitiveEquals(m, m_market); }); } Manifest::string_t m_market; }; } ManifestComparator::ManifestComparator(const Options& options) { // Filters based on installer's MinOSVersion AddFilter(std::make_unique()); // Filters out portable installers if they are not supported by the system AddFilter(std::make_unique()); // Filters based on the scope of a currently installed package AddFilter(InstalledScopeFilter::Create(options)); // Filters based on the market region of the system AddFilter(MarketFilter::Create()); // Filters based on the installer type compatability, including with AppsAndFeaturesEntry declarations AddFilter(InstalledTypeFilter::Create(options)); // Filter order is not important, but comparison order determines priority. // Note that all comparators are also filters and their comparison function will only be called on // installers that both match the required criteria. // // The comparators are ordered by the `IsFirstBetter` method, which uses the following algorithm: // - Each comparison between two installers can return one of { Strong, Weak, Negative } // - Installers are compared in both directions, going through the list of comparators as defined here // - The first Strong result in either direction is given priority // - If no Strong results, the first Weak result is used // - If all Negative results, then the two installers are equal in priority (meaning the first one in the list is kept as "better") // // TODO: There are improvements to be made here around ordering, especially in the context of implicit vs explicit vs command line preferences. // Filters based on exact matches for requirements or compatible matches for preferences // Only applies when preference exists: // Strong if first is compatible and better match than second // Weak if first is unknown and second is not AddComparator(LocaleComparator::Create(options)); // Filters only if a requirement is present and it cannot be satisfied by the installer (including installer types that we can control scope in code) // Only applies when preference exists: // Strong if first matches preference and second does not and is not Unknown // Weak if first matches preference and second is Unknown AddComparator(ScopeComparator::Create(options)); // Filters architectures out that are not supported or are not in the preferences/requirements/inputs. // Strong if first equals the earliest architecture in the allowed list and second does not [default means the system architecture] // Weak if first is better match for system architecture than second AddComparator(MachineArchitectureComparator::Create(options)); // Filters installer types out that are not in preferences or requirements. // Only applies when preference exists: // Weak if first is in preference list and second is not AddComparator(InstallerTypeComparator::Create(options)); } InstallerAndInapplicabilities ManifestComparator::GetPreferredInstaller(const Manifest& manifest) { AICLI_LOG(CLI, Verbose, << "Starting installer selection."); const ManifestInstaller* result = nullptr; std::vector inapplicabilitiesInstallers; for (const auto& installer : manifest.Installers) { auto inapplicabilityInstaller = IsApplicable(installer); if (inapplicabilityInstaller == InapplicabilityFlags::None) { if (!result || IsFirstBetter(installer, *result)) { AICLI_LOG(CLI, Verbose, << "Installer " << installer << " is current best choice"); result = &installer; } } else { inapplicabilitiesInstallers.push_back(inapplicabilityInstaller); } } if (!result) { return { {}, std::move(inapplicabilitiesInstallers) }; } return { *result, std::move(inapplicabilitiesInstallers) }; } InapplicabilityFlags ManifestComparator::IsApplicable(const ManifestInstaller& installer) { InapplicabilityFlags inapplicabilityResult = InapplicabilityFlags::None; for (const auto& filter : m_filters) { auto inapplicability = filter->IsApplicable(installer); if (inapplicability != InapplicabilityFlags::None) { AICLI_LOG(CLI, Verbose, << "Installer " << installer << " not applicable: " << filter->ExplainInapplicable(installer)); WI_SetAllFlags(inapplicabilityResult, inapplicability); } } return inapplicabilityResult; } bool ManifestComparator::IsFirstBetter( const ManifestInstaller& first, const ManifestInstaller& second) { // The priority will still be used as a tie-break between weak results. std::optional firstWeakComparator; bool firstWeakComparatorResult = false; for (auto comparator : m_comparators) { details::ComparisonResult forwardCompare = comparator->IsFirstBetter(first, second); details::ComparisonResult reverseCompare = comparator->IsFirstBetter(second, first); // Should not happen, but if it does it points at a serious bug that should be fixed. if (forwardCompare != details::ComparisonResult::Negative && reverseCompare != details::ComparisonResult::Negative) { AICLI_LOG(CLI, Error, << "Installer " << first << " and " << second << " are both better than each other?"); THROW_HR(E_UNEXPECTED); } if (forwardCompare == details::ComparisonResult::StrongPositive) { AICLI_LOG(CLI, Verbose, << "Installer " << first << " is better [strong] than " << second << " due to: " << comparator->Name()); return true; } if (reverseCompare == details::ComparisonResult::StrongPositive) { // Second is better by this comparator, don't allow a lower priority one to override that. AICLI_LOG(CLI, Verbose, << "Installer " << second << " is better [strong] than " << first << " due to: " << comparator->Name()); return false; } // Save the first weak result that we get if (!firstWeakComparator) { if (forwardCompare == details::ComparisonResult::WeakPositive) { firstWeakComparator = comparator->Name(); firstWeakComparatorResult = true; } else if (reverseCompare == details::ComparisonResult::WeakPositive) { firstWeakComparator = comparator->Name(); firstWeakComparatorResult = false; } } } // If we found a weak result (and no strong result because we made it here), return it. if (firstWeakComparator) { if (firstWeakComparatorResult) { AICLI_LOG(CLI, Verbose, << "Installer " << first << " is better [weak] than " << second << " due to: " << *firstWeakComparator); } else { AICLI_LOG(CLI, Verbose, << "Installer " << second << " is better [weak] than " << first << " due to: " << *firstWeakComparator); } return firstWeakComparatorResult; } // Equal, and thus not better AICLI_LOG(CLI, Verbose, << "Installer " << first << " and " << second << " are equivalent in priority"); return false; } void ManifestComparator::AddFilter(std::unique_ptr&& filter) { if (filter) { m_filters.emplace_back(std::move(filter)); } } void ManifestComparator::AddComparator(std::unique_ptr&& comparator) { if (comparator) { m_comparators.push_back(comparator.get()); m_filters.emplace_back(std::move(comparator)); } } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/Yaml.h" #include "winget/JsonSchemaValidation.h" #include "winget/ManifestCommon.h" #include "winget/ManifestSchemaValidation.h" #include "winget/ManifestYamlParser.h" #include "winget/Resources.h" #include namespace AppInstaller::Manifest::YamlParser { using namespace std::string_view_literals; namespace { enum class YamlScalarType { String, Int, Bool }; // List of fields that use non string scalar types const std::map ManifestFieldTypes = { { "InstallerSuccessCodes"sv, YamlScalarType::Int }, { "InstallerAbortsTerminal"sv, YamlScalarType::Bool }, { "InstallLocationRequired"sv, YamlScalarType::Bool }, { "RequireExplicitUpgrade"sv, YamlScalarType::Bool }, { "DisplayInstallWarnings"sv, YamlScalarType::Bool }, { "InstallerReturnCode"sv, YamlScalarType::Int }, { "DownloadCommandProhibited", YamlScalarType::Bool }, { "ArchiveBinariesDependOnPath", YamlScalarType::Bool } }; YamlScalarType GetManifestScalarValueType(const std::string& key) { auto iter = ManifestFieldTypes.find(key); if (iter != ManifestFieldTypes.end()) { return iter->second; } return YamlScalarType::String; } Json::Value YamlScalarNodeToJson(const YAML::Node& scalarNode, YamlScalarType scalarType) { if (scalarType == YamlScalarType::Int) { return Json::Value(scalarNode.as()); } else if (scalarType == YamlScalarType::Bool) { return Json::Value(scalarNode.as()); } else { return Json::Value(scalarNode.as()); } } Json::Value ManifestYamlNodeToJson(const YAML::Node& rootNode, YamlScalarType scalarType = YamlScalarType::String) { Json::Value result; if (rootNode.IsNull()) { result = Json::Value::nullSingleton(); } else if (rootNode.IsMap()) { for (auto const& keyValuePair : rootNode.Mapping()) { // We only support string type as key in our manifest auto key = keyValuePair.first.as(); result[keyValuePair.first.as()] = ManifestYamlNodeToJson(keyValuePair.second, GetManifestScalarValueType(key)); } } else if (rootNode.IsSequence()) { for (auto const& value : rootNode.Sequence()) { result.append(ManifestYamlNodeToJson(value, scalarType)); } } else if (rootNode.IsScalar()) { result = YamlScalarNodeToJson(rootNode, scalarType); } else { THROW_HR(E_UNEXPECTED); } return result; } std::vector ParseSchemaHeaderString(const YamlManifestInfo& manifestInfo, const ValidationError::Level& errorLevel, std::string& schemaHeaderUrlString) { std::vector errors; std::string schemaHeader = manifestInfo.DocumentSchemaHeader.SchemaHeader; // Remove the leading '#' and any leading/trailing whitespaces if (schemaHeader[0] == '#') { schemaHeader = schemaHeader.substr(1); // Remove the leading '#' schemaHeader = Utility::Trim(schemaHeader); // Trim leading/trailing whitespaces } // Parse the schema header string as YAML string to get the schema header URL try { auto root = YAML::Load(schemaHeader); if (root.IsNull() || (!root.IsNull() && !root.IsDefined())) { errors.emplace_back(ValidationError::MessageContextValueLineLevelWithFile(ManifestError::InvalidSchemaHeader, "", schemaHeader, manifestInfo.DocumentSchemaHeader.Mark.line, manifestInfo.DocumentSchemaHeader.Mark.column, errorLevel, manifestInfo.FileName)); } else { schemaHeaderUrlString = root[YAML::DocumentSchemaHeader::YamlLanguageServerKey].as(); } } catch (const YAML::Exception&) { errors.emplace_back(ValidationError::MessageContextValueLineLevelWithFile(ManifestError::InvalidSchemaHeader, "", schemaHeader, manifestInfo.DocumentSchemaHeader.Mark.line, manifestInfo.DocumentSchemaHeader.Mark.column, errorLevel, manifestInfo.FileName)); } catch (const std::exception&) { errors.emplace_back(ValidationError::MessageContextValueLineLevelWithFile(ManifestError::InvalidSchemaHeader, "", schemaHeader, manifestInfo.DocumentSchemaHeader.Mark.line, manifestInfo.DocumentSchemaHeader.Mark.column, errorLevel, manifestInfo.FileName)); } return errors; } bool ParseSchemaHeaderUrl(const std::string& schemaHeaderValue, std::string& schemaType, std::string& schemaVersion) { // Use regex to match the pattern of @"winget-manifest\.(?\w+)\.(?[\d\.]+)\.schema\.json$" std::regex schemaUrlPattern(R"(winget-manifest\.(\w+)\.([\d\.]+)\.schema\.json$)"); std::smatch match; if (std::regex_search(schemaHeaderValue, match, schemaUrlPattern)) { schemaType = match[1].str(); schemaVersion = match[2].str(); return true; } return false; } std::vector ValidateSchemaHeaderType(const std::string& headerManifestType, const ManifestTypeEnum& expectedManifestType, const YamlManifestInfo& manifestInfo, ValidationError::Level errorLevel) { std::vector errors; ManifestTypeEnum actualManifestType = ConvertToManifestTypeEnum(headerManifestType); size_t schemaHeaderTypeIndex = manifestInfo.DocumentSchemaHeader.SchemaHeader.find(headerManifestType) + 1; if (actualManifestType != expectedManifestType) { errors.emplace_back(ValidationError::MessageContextValueLineLevelWithFile(ManifestError::SchemaHeaderManifestTypeMismatch, "", headerManifestType, manifestInfo.DocumentSchemaHeader.Mark.line, schemaHeaderTypeIndex, errorLevel, manifestInfo.FileName)); } return errors; } std::vector ValidateSchemaHeaderVersion(const std::string& headerManifestVersion, const ManifestVer& expectedManifestVersion, const YamlManifestInfo& manifestInfo, ValidationError::Level errorLevel) { std::vector errors; ManifestVer actualHeaderVersion(headerManifestVersion); size_t schemaHeaderVersionIndex = manifestInfo.DocumentSchemaHeader.SchemaHeader.find(headerManifestVersion) + 1; if (actualHeaderVersion != expectedManifestVersion) { errors.emplace_back(ValidationError::MessageContextValueLineLevelWithFile(ManifestError::SchemaHeaderManifestVersionMismatch, "", headerManifestVersion, manifestInfo.DocumentSchemaHeader.Mark.line, schemaHeaderVersionIndex, errorLevel, manifestInfo.FileName)); } return errors; } bool IsValidSchemaHeaderUrl(const std::string& schemaHeaderUrlString, const YamlManifestInfo& manifestInfo, const ManifestVer& manifestVersion) { // Load the schema file to compare the schema header URL with the schema ID in the schema file Json::Value schemaFile = LoadSchemaDoc(manifestVersion, manifestInfo.ManifestType); if (schemaFile.isMember("$id")) { std::string schemaId = schemaFile["$id"].asString(); // Prefix schema ID with "schema=" to match the schema header URL pattern and compare it with the schema header URL schemaId = "$schema=" + schemaId; if (Utility::CaseInsensitiveEquals(schemaId, schemaHeaderUrlString)) { return true; } } return false; } ValidationError GetSchemaHeaderUrlPatternMismatchError(const std::string& schemaHeaderUrlString, const YamlManifestInfo& manifestInfo, const ValidationError::Level& errorLevel) { size_t schemaHeaderUrlIndex = manifestInfo.DocumentSchemaHeader.SchemaHeader.find(schemaHeaderUrlString) + 1; return ValidationError::MessageContextValueLineLevelWithFile(ManifestError::SchemaHeaderUrlPatternMismatch, "", manifestInfo.DocumentSchemaHeader.SchemaHeader, manifestInfo.DocumentSchemaHeader.Mark.line, schemaHeaderUrlIndex, errorLevel, manifestInfo.FileName); } std::vector ValidateSchemaHeaderUrl(const YamlManifestInfo& manifestInfo, const ManifestVer& manifestVersion, const ValidationError::Level& errorLevel) { std::vector errors; std::string schemaHeaderUrlString; // Parse the schema header string to get the schema header URL auto parserErrors = ParseSchemaHeaderString(manifestInfo, errorLevel, schemaHeaderUrlString); std::move(parserErrors.begin(), parserErrors.end(), std::inserter(errors, errors.end())); if (!errors.empty()) { return errors; } std::string manifestTypeString; std::string manifestVersionString; // Parse the schema header URL to get the manifest type and version if (ParseSchemaHeaderUrl(schemaHeaderUrlString, manifestTypeString, manifestVersionString)) { auto headerManifestTypeErrors = ValidateSchemaHeaderType(manifestTypeString, manifestInfo.ManifestType, manifestInfo, errorLevel); std::move(headerManifestTypeErrors.begin(), headerManifestTypeErrors.end(), std::inserter(errors, errors.end())); auto headerManifestVersionErrors = ValidateSchemaHeaderVersion(manifestVersionString, manifestVersion, manifestInfo, errorLevel); std::move(headerManifestVersionErrors.begin(), headerManifestVersionErrors.end(), std::inserter(errors, errors.end())); // Finally, match the entire schema header URL with the schema ID in the schema file to ensure the URL domain matches the schema definition file. if (!IsValidSchemaHeaderUrl(schemaHeaderUrlString, manifestInfo, manifestVersion)) { errors.emplace_back(GetSchemaHeaderUrlPatternMismatchError(schemaHeaderUrlString, manifestInfo, errorLevel)); } } else { errors.emplace_back(GetSchemaHeaderUrlPatternMismatchError(schemaHeaderUrlString, manifestInfo, errorLevel)); } return errors; } std::vector ValidateYamlManifestSchemaHeader(const YamlManifestInfo& manifestInfo, const ManifestVer& manifestVersion, ValidationError::Level errorLevel) { std::vector errors; std::string schemaHeaderString; if (manifestInfo.DocumentSchemaHeader.SchemaHeader.empty()) { errors.emplace_back(ValidationError::MessageLevelWithFile(ManifestError::SchemaHeaderNotFound, errorLevel, manifestInfo.FileName)); return errors; } auto parserErrors = ValidateSchemaHeaderUrl(manifestInfo, manifestVersion, errorLevel); std::move(parserErrors.begin(), parserErrors.end(), std::inserter(errors, errors.end())); return errors; } } Json::Value LoadSchemaDoc(const ManifestVer& manifestVersion, ManifestTypeEnum manifestType) { int idx = MANIFESTSCHEMA_NO_RESOURCE; std::map resourceMap; if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_28 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_28_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_28_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_28_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_28_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_28_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_12 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_12_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_12_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_12_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_12_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_12_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_10 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_10_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_10_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_10_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_10_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_10_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_9 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_9_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_9_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_9_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_9_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_9_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_7 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_7_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_7_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_7_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_7_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_7_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_6 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_6_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_6_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_6_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_6_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_6_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_5 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_5_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_5_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_5_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_5_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_5_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_4 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_4_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_4_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_4_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_4_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_4_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_2 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_2_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_2_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_2_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_2_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_2_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_1 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_1_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_1_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_1_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_1_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_1_LOCALE }, }; } else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1 }) { resourceMap = { { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_SINGLETON }, { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_VERSION }, { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_INSTALLER }, { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_DEFAULTLOCALE }, { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_LOCALE }, }; } else { resourceMap = { { ManifestTypeEnum::Preview, IDX_MANIFEST_SCHEMA_PREVIEW }, }; } auto iter = resourceMap.find(manifestType); if (iter != resourceMap.end()) { idx = iter->second; } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } std::string_view schemaStr = Resource::GetResourceAsString(idx, MANIFESTSCHEMA_RESOURCE_TYPE); return JsonSchema::LoadSchemaDoc(schemaStr); } std::vector ValidateAgainstSchema(const std::vector& manifestList, const ManifestVer& manifestVersion) { std::vector errors; // A list of schema validator to avoid multiple loadings of same schema std::map schemaList; for (const auto& entry : manifestList) { if (entry.ManifestType == ManifestTypeEnum::Shadow) { // There's no schema for a shadow manifest. continue; } if (schemaList.find(entry.ManifestType) == schemaList.end()) { // Copy constructor of valijson::Schema was private valijson::Schema& newSchema = schemaList.emplace( std::piecewise_construct, std::make_tuple(entry.ManifestType), std::make_tuple()).first->second; Json::Value schemaJson = LoadSchemaDoc(manifestVersion, entry.ManifestType); JsonSchema::PopulateSchema(schemaJson, newSchema); } const auto& schema = schemaList.find(entry.ManifestType)->second; Json::Value manifestJson = ManifestYamlNodeToJson(entry.Root); valijson::ValidationResults results; if (!JsonSchema::Validate(schema, manifestJson, results)) { errors.emplace_back(ValidationError::MessageContextWithFile(ManifestError::SchemaError, JsonSchema::GetErrorStringFromResults(results), entry.FileName)); } } return errors; } std::vector ValidateYamlManifestsSchemaHeader(const std::vector& manifestList, const ManifestVer& manifestVersion, bool treatErrorAsWarning) { std::vector errors; ValidationError::Level errorLevel = treatErrorAsWarning ? ValidationError::Level::Warning : ValidationError::Level::Error; // Read the manifest schema header and ensure it exists for (const auto& entry : manifestList) { if (entry.ManifestType == ManifestTypeEnum::Shadow) { // There's no schema for a shadow manifest. continue; } auto schemaHeaderErrors = ValidateYamlManifestSchemaHeader(entry, manifestVersion, errorLevel); std::move(schemaHeaderErrors.begin(), schemaHeaderErrors.end(), std::inserter(errors, errors.end())); } return errors; } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerLogging.h" #include "AppInstallerMsixInfo.h" #include "winget/MsixManifest.h" #include "winget/ManifestValidation.h" #include "winget/MsixManifestValidation.h" #include "winget/Locale.h" #include "winget/Filesystem.h" namespace AppInstaller::Manifest { namespace { constexpr std::array s_AllowedPortableFiletypes = { L".exe", }; constexpr std::array s_AllowedFontFiletypes = { L".otf", // OpenType Font L".ttf", // TrueType Font L".fnt", // Font L".ttc", // TrueType Font Collection L".otc", // OpenType Font Collection }; const auto& GetErrorIdToMessageMap() { static std::map ErrorIdToMessageMap = { { AppInstaller::Manifest::ManifestError::InvalidRootNode, "Encountered unexpected root node."sv }, { AppInstaller::Manifest::ManifestError::FieldUnknown, "Unknown field."sv }, { AppInstaller::Manifest::ManifestError::FieldIsNotPascalCase, "All field names should be PascalCased."sv }, { AppInstaller::Manifest::ManifestError::FieldDuplicate, "Duplicate field found in the manifest."sv }, { AppInstaller::Manifest::ManifestError::RequiredFieldEmpty, "Required field with empty value."sv }, { AppInstaller::Manifest::ManifestError::RequiredFieldMissing, "Required field missing."sv }, { AppInstaller::Manifest::ManifestError::InvalidFieldValue, "Invalid field value."sv }, { AppInstaller::Manifest::ManifestError::ExeInstallerMissingSilentSwitches, "Silent and SilentWithProgress switches are not specified for InstallerType exe. Please make sure the installer can run unattended."sv }, { AppInstaller::Manifest::ManifestError::FieldNotSupported, "Field is not supported."sv }, { AppInstaller::Manifest::ManifestError::FieldValueNotSupported, "Field value is not supported."sv }, { AppInstaller::Manifest::ManifestError::DuplicateInstallerEntry, "Duplicate installer entry found."sv }, { AppInstaller::Manifest::ManifestError::DuplicateInstallerHash, "Multiple Installer URLs found with the same InstallerSha256. Please ensure the accuracy of the URLs."sv }, { AppInstaller::Manifest::ManifestError::InstallerTypeDoesNotSupportPackageFamilyName, "The specified installer type does not support PackageFamilyName."sv }, { AppInstaller::Manifest::ManifestError::InstallerTypeDoesNotSupportProductCode, "The specified installer type does not support ProductCode."sv }, { AppInstaller::Manifest::ManifestError::InstallerTypeDoesNotWriteAppsAndFeaturesEntry, "The specified installer type does not write to Apps and Features entry."sv }, { AppInstaller::Manifest::ManifestError::IncompleteMultiFileManifest, "The multi file manifest is incomplete.A multi file manifest must contain at least version, installer and defaultLocale manifest."sv }, { AppInstaller::Manifest::ManifestError::InconsistentMultiFileManifestFieldValue, "The multi file manifest has inconsistent field values."sv }, { AppInstaller::Manifest::ManifestError::DuplicatePortableCommandAlias, "Duplicate portable command alias found."sv }, { AppInstaller::Manifest::ManifestError::DuplicateRelativeFilePath, "Duplicate relative file path found."sv }, { AppInstaller::Manifest::ManifestError::DuplicateMultiFileManifestType, "The multi file manifest should contain only one file with the particular ManifestType."sv }, { AppInstaller::Manifest::ManifestError::DuplicateMultiFileManifestLocale, "The multi file manifest contains duplicate PackageLocale."sv }, { AppInstaller::Manifest::ManifestError::UnsupportedMultiFileManifestType, "The multi file manifest should not contain file with the particular ManifestType."sv }, { AppInstaller::Manifest::ManifestError::InconsistentInstallerHash, "The values of InstallerSha256 do not match for all instances of the same InstallerUrl."sv }, { AppInstaller::Manifest::ManifestError::InconsistentMultiFileManifestDefaultLocale, "DefaultLocale value in version manifest does not match PackageLocale value in defaultLocale manifest."sv }, { AppInstaller::Manifest::ManifestError::FieldFailedToProcess, "Failed to process field."sv }, { AppInstaller::Manifest::ManifestError::InvalidBcp47Value, "The locale value is not a well formed bcp47 language tag."sv }, { AppInstaller::Manifest::ManifestError::BothAllowedAndExcludedMarketsDefined, "Both AllowedMarkets and ExcludedMarkets defined."sv }, { AppInstaller::Manifest::ManifestError::DuplicateReturnCodeEntry, "Duplicate installer return code found."sv }, { AppInstaller::Manifest::ManifestError::FieldRequireVerifiedPublisher, "Field usage requires verified publishers."sv }, { AppInstaller::Manifest::ManifestError::SingleManifestPackageHasDependencies, "Package has a single manifest and is a dependency of other manifests."sv }, { AppInstaller::Manifest::ManifestError::MultiManifestPackageHasDependencies, "Deleting the manifest will be break the following dependencies."sv }, { AppInstaller::Manifest::ManifestError::MissingManifestDependenciesNode, "Dependency not found: "sv }, { AppInstaller::Manifest::ManifestError::NoSuitableMinVersionDependency,"No Suitable Minimum Version: "sv }, { AppInstaller::Manifest::ManifestError::FoundDependencyLoop, "Loop found."sv }, { AppInstaller::Manifest::ManifestError::ExceededAppsAndFeaturesEntryLimit, "Only zero or one entry for Apps and Features may be specified for InstallerType portable."sv }, { AppInstaller::Manifest::ManifestError::ExceededCommandsLimit, "Only zero or one value for Commands may be specified for InstallerType portable."sv }, { AppInstaller::Manifest::ManifestError::ScopeNotSupported, "Scope is not supported for InstallerType portable."sv }, { AppInstaller::Manifest::ManifestError::InstallerMsixInconsistencies, "Inconsistent value in the manifest."sv }, { AppInstaller::Manifest::ManifestError::OptionalFieldMissing, "Optional field missing."sv }, { AppInstaller::Manifest::ManifestError::InstallerFailedToProcess, "Failed to process installer."sv }, { AppInstaller::Manifest::ManifestError::NoSupportedPlatforms, "No supported platforms."sv }, { AppInstaller::Manifest::ManifestError::ApproximateVersionNotAllowed, "Approximate version not allowed."sv }, { AppInstaller::Manifest::ManifestError::ArpVersionOverlapWithIndex, "DisplayVersion declared in the manifest has overlap with existing DisplayVersion range in the index. Existing DisplayVersion range in index: "sv }, { AppInstaller::Manifest::ManifestError::ArpVersionValidationInternalError, "Internal error while validating DisplayVersion against index."sv }, { AppInstaller::Manifest::ManifestError::ExceededNestedInstallerFilesLimit, "Only one entry for NestedInstallerFiles can be specified for non-portable InstallerTypes."sv }, { AppInstaller::Manifest::ManifestError::RelativeFilePathEscapesDirectory, "Relative file path must not point to a location outside of archive directory."sv }, { AppInstaller::Manifest::ManifestError::ArpValidationError, "Arp Validation Error."sv }, { AppInstaller::Manifest::ManifestError::SchemaError, "Schema Error."sv }, { AppInstaller::Manifest::ManifestError::MsixSignatureHashFailed, "Failed to calculate MSIX signature hash.Please verify that the input file is a valid, signed MSIX."sv }, { AppInstaller::Manifest::ManifestError::ShadowManifestNotAllowed, "Shadow manifest is not allowed." }, { AppInstaller::Manifest::ManifestError::SchemaHeaderNotFound, "Schema header not found." }, { AppInstaller::Manifest::ManifestError::InvalidSchemaHeader , "The schema header is invalid. Please verify that the schema header is present and formatted correctly."sv }, { AppInstaller::Manifest::ManifestError::SchemaHeaderManifestTypeMismatch , "The manifest type in the schema header does not match the ManifestType property value in the manifest."sv }, { AppInstaller::Manifest::ManifestError::SchemaHeaderManifestVersionMismatch, "The manifest version in the schema header does not match the ManifestVersion property value in the manifest."sv }, { AppInstaller::Manifest::ManifestError::SchemaHeaderUrlPatternMismatch, "The schema header URL does not match the expected pattern."sv }, { AppInstaller::Manifest::ManifestError::InvalidPortableFiletype, "The file type of the referenced file is not allowed."sv }, { AppInstaller::Manifest::ManifestError::InvalidFontFiletype, "The file type of the referenced file is not a supported font file type."sv }, }; return ErrorIdToMessageMap; } } std::vector ValidateManifest(const Manifest& manifest, bool fullValidation) { std::vector resultErrors; // Channel is not supported currently if (!manifest.Channel.empty()) { resultErrors.emplace_back(ManifestError::FieldNotSupported, "Channel", manifest.Channel); } try { // Version value should be successfully parsed Utility::Version testVersion{ manifest.Version }; if (testVersion.IsApproximate()) { resultErrors.emplace_back(ManifestError::ApproximateVersionNotAllowed, "PackageVersion", manifest.Version); } } catch (const std::exception&) { resultErrors.emplace_back(ManifestError::InvalidFieldValue, "PackageVersion", manifest.Version); } auto defaultLocErrors = ValidateManifestLocalization(manifest.DefaultLocalization, !fullValidation); std::move(defaultLocErrors.begin(), defaultLocErrors.end(), std::inserter(resultErrors, resultErrors.end())); // Comparison function to check duplicate installer entry. {installerType, arch, language and scope} combination is the key. // Todo: use the comparator from ManifestComparator when that one is fully implemented. auto installerCmp = [](const ManifestInstaller& in1, const ManifestInstaller& in2) { if (in1.BaseInstallerType != in2.BaseInstallerType) { return in1.BaseInstallerType < in2.BaseInstallerType; } else if (IsArchiveType(in1.BaseInstallerType)) { // Compare nested installer type if base installer type is archive. if (in1.NestedInstallerType != in2.NestedInstallerType) { return in1.NestedInstallerType < in2.NestedInstallerType; } } if (in1.Arch != in2.Arch) { return in1.Arch < in2.Arch; } if (in1.Locale != in2.Locale) { return in1.Locale < in2.Locale; } // Unknown is considered equal to all other values for uniqueness. // If either value is unknown, don't compare them. if (in1.Scope != in2.Scope && in1.Scope != ScopeEnum::Unknown && in2.Scope != ScopeEnum::Unknown) { return in1.Scope < in2.Scope; } return false; }; std::set installerSet(installerCmp); bool duplicateInstallerFound = false; // Set up maps for checking uniqueness across hash <-> url pairs std::unordered_map urlToChecksum; std::unordered_map checksumToUrl; // Validate installers for (auto const& installer : manifest.Installers) { // If not full validation, for future compatibility, skip validating unknown installers. if (installer.EffectiveInstallerType() == InstallerTypeEnum::Unknown && !fullValidation) { continue; } if (!duplicateInstallerFound && !installerSet.insert(installer).second) { AICLI_LOG(Core, Error, << "Duplicate installer: Type [" << InstallerTypeToString(installer.EffectiveInstallerType()) << "], Architecture [" << Utility::ToString(installer.Arch) << "], Locale [" << installer.Locale << "], Scope [" << ScopeToString(installer.Scope) << "]"); resultErrors.emplace_back(ManifestError::DuplicateInstallerEntry); duplicateInstallerFound = true; } if (installer.Arch == Utility::Architecture::Unknown) { resultErrors.emplace_back(ManifestError::InvalidFieldValue, "Architecture"); } if (installer.EffectiveInstallerType() == InstallerTypeEnum::Unknown) { resultErrors.emplace_back(ManifestError::InvalidFieldValue, "InstallerType"); } if (installer.UpdateBehavior == UpdateBehaviorEnum::Unknown) { resultErrors.emplace_back(ManifestError::InvalidFieldValue, "UpgradeBehavior"); } // Validate system reference strings if they are set at the installer level // Allow PackageFamilyName to be declared with non msix installers to support nested installer scenarios. But still report as warning to notify user of this uncommon case. if (!installer.PackageFamilyName.empty() && !(DoesInstallerTypeUsePackageFamilyName(installer.EffectiveInstallerType()) || DoAnyAppsAndFeaturesEntriesUsePackageFamilyName(installer.AppsAndFeaturesEntries))) { resultErrors.emplace_back(ManifestError::InstallerTypeDoesNotSupportPackageFamilyName, "InstallerType", std::string{ InstallerTypeToString(installer.EffectiveInstallerType()) }, ValidationError::Level::Warning); } if (!installer.ProductCode.empty() && !DoesInstallerTypeUseProductCode(installer.EffectiveInstallerType())) { resultErrors.emplace_back(ManifestError::InstallerTypeDoesNotSupportProductCode, "InstallerType", InstallerTypeToString(installer.EffectiveInstallerType())); } if (!installer.AppsAndFeaturesEntries.empty() && !DoesInstallerTypeWriteAppsAndFeaturesEntry(installer.EffectiveInstallerType())) { resultErrors.emplace_back(ManifestError::InstallerTypeDoesNotWriteAppsAndFeaturesEntry, "InstallerType", InstallerTypeToString(installer.EffectiveInstallerType())); } if (installer.EffectiveInstallerType() == InstallerTypeEnum::MSStore) { if (fullValidation) { // MSStore type is not supported in community repo resultErrors.emplace_back( ManifestError::FieldValueNotSupported, "InstallerType", InstallerTypeToString(installer.EffectiveInstallerType())); } if (installer.ProductId.empty()) { resultErrors.emplace_back(ManifestError::RequiredFieldMissing, "ProductId"); } } else { // For other types, Url and Sha256 are required if (installer.Url.empty()) { resultErrors.emplace_back(ManifestError::RequiredFieldMissing, "InstallerUrl"); } if (installer.Sha256.empty()) { resultErrors.emplace_back(ManifestError::RequiredFieldMissing, "InstallerSha256"); } // ProductId should not be used if (!installer.ProductId.empty()) { resultErrors.emplace_back(ManifestError::FieldNotSupported, "ProductId"); } // Ensure that each URL has a one to one mapping with a Sha256 and // warn if a Sha256 has a one to many mapping with a URL if (fullValidation && !installer.Url.empty() && !installer.Sha256.empty()) { std::string checksum = Utility::SHA256::ConvertToString(installer.Sha256); std::string url = installer.Url; auto [urlIterator, urlInserted] = urlToChecksum.try_emplace(url, checksum); auto [checksumIterator, checksumInserted] = checksumToUrl.try_emplace(checksum, url); if (!urlInserted && urlIterator->second != checksum) { // If the URL was not inserted, and the value in the map does not match the current Sha256, then // a single URL corresponds to multiple SHA256 and an error should be thrown resultErrors.emplace_back(ManifestError::InconsistentInstallerHash, "InstallerUrl", url); } if (!checksumInserted && checksumIterator->second != url) { // If the SHA256 was not inserted, and the value in the map does not match the current URL, then // a single SHA256 corresponds to multiple URLS and a warning should be thrown resultErrors.emplace_back(ManifestError::DuplicateInstallerHash, "InstallerSha256", checksum, ValidationError::Level::Warning); } } } if (installer.EffectiveInstallerType() == InstallerTypeEnum::Exe && (installer.Switches.find(InstallerSwitchType::SilentWithProgress) == installer.Switches.end() || installer.Switches.find(InstallerSwitchType::Silent) == installer.Switches.end())) { resultErrors.emplace_back(ManifestError::ExeInstallerMissingSilentSwitches, ValidationError::Level::Warning); } // The command field restriction only applies if the base installer type is Portable. if (installer.BaseInstallerType == InstallerTypeEnum::Portable) { if (installer.Commands.size() > 1) { resultErrors.emplace_back(ManifestError::ExceededCommandsLimit); } } if (installer.EffectiveInstallerType() == InstallerTypeEnum::Portable) { if (installer.AppsAndFeaturesEntries.size() > 1) { resultErrors.emplace_back(ManifestError::ExceededAppsAndFeaturesEntryLimit); } if (installer.Scope != ScopeEnum::Unknown) { resultErrors.emplace_back(ManifestError::ScopeNotSupported, ValidationError::Level::Warning); } } if (IsArchiveType(installer.BaseInstallerType)) { bool isPortable = installer.NestedInstallerType == InstallerTypeEnum::Portable; bool isFont = installer.NestedInstallerType == InstallerTypeEnum::Font; if (installer.NestedInstallerType == InstallerTypeEnum::Unknown) { resultErrors.emplace_back(ManifestError::RequiredFieldMissing, "NestedInstallerType"); } if (installer.NestedInstallerFiles.size() == 0) { resultErrors.emplace_back(ManifestError::RequiredFieldMissing, "NestedInstallerFiles"); } if (!isPortable && !isFont && installer.NestedInstallerFiles.size() != 1) { resultErrors.emplace_back(ManifestError::ExceededNestedInstallerFilesLimit, "NestedInstallerFiles"); } std::set commandAliasSet; std::set relativeFilePathSet; for (const auto& nestedInstallerFile : installer.NestedInstallerFiles) { if (nestedInstallerFile.RelativeFilePath.empty()) { resultErrors.emplace_back(ManifestError::RequiredFieldMissing, "RelativeFilePath"); break; } // Check that the relative file path does not escape base directory. const std::filesystem::path& basePath = std::filesystem::current_path(); const std::filesystem::path& fullPath = basePath / ConvertToUTF16(nestedInstallerFile.RelativeFilePath); if (AppInstaller::Filesystem::PathEscapesBaseDirectory(fullPath, basePath)) { resultErrors.emplace_back(ManifestError::RelativeFilePathEscapesDirectory, "RelativeFilePath"); } // Check for duplicate relative filepath values. if (!relativeFilePathSet.insert(Utility::ToLower(nestedInstallerFile.RelativeFilePath)).second) { resultErrors.emplace_back(ManifestError::DuplicateRelativeFilePath, "RelativeFilePath"); } // Check for duplicate portable command alias values. const auto& alias = Utility::ToLower(nestedInstallerFile.PortableCommandAlias); if (!alias.empty() && !commandAliasSet.insert(alias).second) { resultErrors.emplace_back(ManifestError::DuplicatePortableCommandAlias, "PortableCommandAlias"); break; } // If running full validation, check filetype if (fullValidation) { if (isPortable) { if (fullPath.has_extension() && std::find(s_AllowedPortableFiletypes.begin(), s_AllowedPortableFiletypes.end(), fullPath.extension()) == s_AllowedPortableFiletypes.end()) { resultErrors.emplace_back(ManifestError::InvalidPortableFiletype, "RelativeFilePath", nestedInstallerFile.RelativeFilePath); } } if (isFont) { if (fullPath.has_extension() && std::find(s_AllowedFontFiletypes.begin(), s_AllowedFontFiletypes.end(), fullPath.extension()) == s_AllowedFontFiletypes.end()) { resultErrors.emplace_back(ManifestError::InvalidFontFiletype, "RelativeFilePath", nestedInstallerFile.RelativeFilePath); } } } } } // Check empty string before calling IsValidUrl to avoid duplicate error reporting. if (!installer.Url.empty() && IsValidURL(NULL, Utility::ConvertToUTF16(installer.Url).c_str(), 0) == S_FALSE) { resultErrors.emplace_back(ManifestError::InvalidFieldValue, "InstallerUrl", installer.Url); } if (!installer.Locale.empty() && !Locale::IsWellFormedBcp47Tag(installer.Locale)) { resultErrors.emplace_back(ManifestError::InvalidBcp47Value, "InstallerLocale", installer.Locale); } if (!installer.Markets.AllowedMarkets.empty() && !installer.Markets.ExcludedMarkets.empty()) { resultErrors.emplace_back(ManifestError::BothAllowedAndExcludedMarketsDefined); } // Check expected return codes for duplicates between successful and expected error codes std::set returnCodeSet; returnCodeSet.insert(installer.InstallerSuccessCodes.begin(), installer.InstallerSuccessCodes.end()); for (const auto& code : installer.ExpectedReturnCodes) { if (!returnCodeSet.insert(code.first).second) { resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); // Stop checking to avoid repeated errors break; } } // Check no approximate version declared for DisplayVersion in AppsAndFeatureEntries for (auto const& entry : installer.AppsAndFeaturesEntries) { if (!entry.DisplayVersion.empty()) { try { Utility::Version displayVersion{ entry.DisplayVersion }; if (displayVersion.IsApproximate()) { resultErrors.emplace_back(ManifestError::ApproximateVersionNotAllowed, "DisplayVersion", entry.DisplayVersion); } } catch (const std::exception&) { resultErrors.emplace_back(ManifestError::InvalidFieldValue, "DisplayVersion", entry.DisplayVersion); } } } // Check AuthInfo validity. For full validation (community repo), authentication type must be none. if (installer.AuthInfo.Type != Authentication::AuthenticationType::None) { if (fullValidation) { // Authentication is not supported (must be none) in community repo. resultErrors.emplace_back(ManifestError::FieldNotSupported, "Authentication"); } if (!installer.AuthInfo.ValidateIntegrity()) { resultErrors.emplace_back(ManifestError::InvalidFieldValue, "Authentication"); } } if (fullValidation) { for (const auto& container : installer.DesiredStateConfiguration) { if (container.Type == DesiredStateConfigurationContainerType::PowerShell) { // PowerShell DSC is not supported in community repo. resultErrors.emplace_back(ManifestError::FieldNotSupported, "DesiredStateConfiguration.PowerShell"); break; } } } } // Validate localizations for (auto const& localization : manifest.Localizations) { auto locErrors = ValidateManifestLocalization(localization, !fullValidation); std::move(locErrors.begin(), locErrors.end(), std::inserter(resultErrors, resultErrors.end())); } return resultErrors; } std::vector ValidateManifestLocalization(const ManifestLocalization& localization, bool treatErrorAsWarning) { std::vector resultErrors; if (!localization.Locale.empty() && !Locale::IsWellFormedBcp47Tag(localization.Locale)) { resultErrors.emplace_back(ManifestError::InvalidBcp47Value, "PackageLocale", localization.Locale, treatErrorAsWarning ? ValidationError::Level::Warning : ValidationError::Level::Error); } if (localization.Contains(Localization::Agreements)) { const auto& agreements = localization.Get(); for (const auto& agreement : agreements) { // At least one must be present if (agreement.Label.empty() && agreement.AgreementText.empty() && agreement.AgreementUrl.empty()) { resultErrors.emplace_back(ManifestError::InvalidFieldValue, "Agreements", treatErrorAsWarning ? ValidationError::Level::Warning : ValidationError::Level::Error); } } } return resultErrors; } std::vector ValidateManifestInstallers(const Manifest& manifest, bool treatErrorAsWarning) { std::vector errors; auto validationErrorLevel = treatErrorAsWarning ? ValidationError::Level::Warning : ValidationError::Level::Error; MsixManifestValidation msixManifestValidation(validationErrorLevel); for (const auto& installer : manifest.Installers) { // Installer msix or msixbundle if (installer.EffectiveInstallerType() == InstallerTypeEnum::Msix) { auto installerErrors = msixManifestValidation.Validate(manifest, installer); std::move(installerErrors.begin(), installerErrors.end(), std::inserter(errors, errors.end())); } } return errors; } std::string ValidationError::GetErrorMessage() const { const auto& ErrorIdToMessageMap = GetErrorIdToMessageMap(); const auto itr = ErrorIdToMessageMap.find(Message); if (itr != ErrorIdToMessageMap.end()) { return std::string(itr->second); } return Utility::ConvertToUTF8(Message); } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerSHA256.h" #include "winget/ManifestYamlPopulator.h" namespace AppInstaller::Manifest { using ValidationErrors = std::vector; using ExpectedReturnCodeInfo = AppInstaller::Manifest::ManifestInstaller::ExpectedReturnCodeInfo; namespace { template Ptr* variant_ptr(const VariantManifestPtr& v) { return std::get(v); } ManifestInstaller* GetManifestInstallerPtrFromManifest(const VariantManifestPtr& v) { return &(variant_ptr(v)->DefaultInstallerInfo); } ManifestLocalization* GetManifestLocalizationPtrFromManifest(const VariantManifestPtr& v) { return &(variant_ptr(v)->DefaultLocalization); } ManifestInstaller* GetManifestInstallerPtr(const VariantManifestPtr& v) { if (auto installer = std::get_if(&v)) { return *installer; } return GetManifestInstallerPtrFromManifest(v); } ManifestLocalization* GetManifestLocalizationPtr(const VariantManifestPtr& v) { if (auto localization = std::get_if(&v)) { return *localization; } return GetManifestLocalizationPtrFromManifest(v); } // Only used in preview manifest std::vector SplitMultiValueField(const std::string& input) { if (input.empty()) { return {}; } std::vector result; size_t currentPos = 0; while (currentPos < input.size()) { size_t splitPos = input.find(',', currentPos); if (splitPos == std::string::npos) { splitPos = input.size(); } std::string splitVal = input.substr(currentPos, splitPos - currentPos); Utility::Trim(splitVal); if (!splitVal.empty()) { result.emplace_back(std::move(splitVal)); } currentPos = splitPos + 1; } return result; } std::vector ProcessStringSequenceNode(const YAML::Node& node, bool trim = true) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); std::vector result; for (auto const& entry : node.Sequence()) { std::string value = entry.as(); if (trim) { Utility::Trim(value); } result.emplace_back(std::move(value)); } return result; } std::vector ProcessInstallerSuccessCodeSequenceNode(const YAML::Node& node) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); std::vector result; for (auto const& entry : node.Sequence()) { result.emplace_back(static_cast(entry.as())); } return result; } std::vector ProcessPlatformSequenceNode(const YAML::Node& node) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); std::vector result; for (auto const& entry : node.Sequence()) { result.emplace_back(ConvertToPlatformEnum(entry.as())); } return result; } std::vector ProcessInstallModeSequenceNode(const YAML::Node& node) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); std::vector result; for (auto const& entry : node.Sequence()) { result.emplace_back(ConvertToInstallModeEnum(entry.as())); } return result; } std::vector ProcessArchitectureSequenceNode(const YAML::Node& node) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); std::vector result; for (auto const& entry : node.Sequence()) { result.emplace_back(Utility::ConvertToArchitectureEnum(entry.as())); } return result; } std::vector ProcessUnsupportedArgumentsSequenceNode(const YAML::Node& node) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); std::vector result; for (auto const& entry : node.Sequence()) { result.emplace_back(ConvertToUnsupportedArgumentEnum(entry.as())); } return result; } void ProcessDependenciesNode(DependencyType type, const YAML::Node& node, DependencyList* dependencyList) { const auto& ids = ProcessStringSequenceNode(node); for (auto id : ids) { dependencyList->Add(Dependency(type, id)); } } } std::vector ManifestYamlPopulator::GetRootFieldProcessInfo() { // Common fields across versions std::vector result = { { "ManifestVersion", [](const YAML::Node&, const VariantManifestPtr&)->ValidationErrors { /* ManifestVersion already populated. Field listed here for duplicate and PascalCase check */ return {}; } }, { "Installers", [this](const YAML::Node& value, const VariantManifestPtr&)->ValidationErrors { m_p_installersNode = &value; return {}; } }, { "Localization", [this](const YAML::Node& value, const VariantManifestPtr&)->ValidationErrors { m_p_localizationsNode = &value; return {}; } }, { "Channel", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Channel = Utility::Trim(value.as()); return {}; } }, }; // Additional version specific fields if (m_manifestVersion.get().Major() == 0) { std::vector previewRootFields { { "Id", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Id = Utility::Trim(value.as()); return {}; } }, { "Version", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Version = Utility::Trim(value.as()); return {}; } }, { "AppMoniker", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Moniker = Utility::Trim(value.as()); return {}; } }, }; std::move(previewRootFields.begin(), previewRootFields.end(), std::inserter(result, result.end())); } else if (m_manifestVersion.get().Major() == 1) { // Starting v1, we should be only adding new fields for each minor version increase if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1 }) { std::vector v1RootFields { { "PackageIdentifier", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Id = Utility::Trim(value.as()); return {}; } }, { "PackageVersion", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Version = Utility::Trim(value.as()); return {}; } }, { "Moniker", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Moniker = Utility::Trim(value.as()); return {}; } }, { "ManifestType", [](const YAML::Node&, const VariantManifestPtr&)->ValidationErrors { /* ManifestType already checked. Field listed here for duplicate and PascalCase check */ return {}; } }, }; std::move(v1RootFields.begin(), v1RootFields.end(), std::inserter(result, result.end())); } } // Root fields mapped as Installer and Localization values auto rootInstallerFields = GetInstallerFieldProcessInfo(true); std::move(rootInstallerFields.begin(), rootInstallerFields.end(), std::inserter(result, result.end())); auto rootLocalizationFields = GetLocalizationFieldProcessInfo(true); std::move(rootLocalizationFields.begin(), rootLocalizationFields.end(), std::inserter(result, result.end())); return result; } std::vector ManifestYamlPopulator::GetInstallerFieldProcessInfo(bool forRootFields) { // Common fields across versions std::vector result = { { "InstallerType", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->BaseInstallerType = ConvertToInstallerTypeEnum(value.as()); return {}; } }, { "PackageFamilyName", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->PackageFamilyName = value.as(); return {}; } }, { "ProductCode", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->ProductCode = value.as(); return {}; } }, }; // Additional version specific fields if (m_manifestVersion.get().Major() == 0) { // Root level and Localization node level std::vector previewCommonFields = { { "UpdateBehavior", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->UpdateBehavior = ConvertToUpdateBehaviorEnum(value.as()); return {}; } }, { "Switches", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ValidateAndProcessFields(value, SwitchesFieldInfos, VariantManifestPtr(&(GetManifestInstallerPtr(v)->Switches))); }}, }; std::move(previewCommonFields.begin(), previewCommonFields.end(), std::inserter(result, result.end())); if (!forRootFields) { // Installer node only std::vector installerOnlyFields = { { "Arch", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Arch = Utility::ConvertToArchitectureEnum(value.as()); return {}; } }, { "Url", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Url = value.as(); return {}; } }, { "Sha256", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Sha256 = Utility::SHA256::ConvertToBytes(value.as()); return {}; } }, { "SignatureSha256", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->SignatureSha256 = Utility::SHA256::ConvertToBytes(value.as()); return {}; } }, { "Language", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Locale = value.as(); return {}; } }, { "Scope", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Scope = ConvertToScopeEnum(value.as()); return {}; } }, }; if (m_manifestVersion.get().HasExtension(s_MSStoreExtension)) { installerOnlyFields.emplace_back("ProductId", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->ProductId = value.as(); return {}; }); } std::move(installerOnlyFields.begin(), installerOnlyFields.end(), std::inserter(result, result.end())); } else { // Root node only std::vector rootOnlyFields = { { "MinOSVersion", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtrFromManifest(v)->MinOSVersion = value.as(); return {}; } }, { "Commands", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtrFromManifest(v)->Commands = SplitMultiValueField(value.as()); return {}; } }, { "Protocols", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtrFromManifest(v)->Protocols = SplitMultiValueField(value.as()); return {}; } }, { "FileExtensions", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtrFromManifest(v)->FileExtensions = SplitMultiValueField(value.as()); return {}; } }, }; std::move(rootOnlyFields.begin(), rootOnlyFields.end(), std::inserter(result, result.end())); } } else if (m_manifestVersion.get().Major() == 1) { // Starting v1, we should be only adding new fields for each minor version increase if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1 }) { // Root level and Installer node level std::vector v1CommonFields = { { "InstallerLocale", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->Locale = value.as(); return {}; } }, { "Platform", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->Platform = ProcessPlatformSequenceNode(value); return {}; } }, { "MinimumOSVersion", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->MinOSVersion = value.as(); return {}; } }, { "Scope", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->Scope = ConvertToScopeEnum(value.as()); return {}; } }, { "InstallModes", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->InstallModes = ProcessInstallModeSequenceNode(value); return {}; } }, { "InstallerSwitches", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ValidateAndProcessFields(value, SwitchesFieldInfos, VariantManifestPtr(&(GetManifestInstallerPtr(v)->Switches))); }}, { "InstallerSuccessCodes", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->InstallerSuccessCodes = ProcessInstallerSuccessCodeSequenceNode(value); return {}; } }, { "UpgradeBehavior", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->UpdateBehavior = ConvertToUpdateBehaviorEnum(value.as()); return {}; } }, { "Commands", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->Commands = ProcessStringSequenceNode(value); return {}; } }, { "Protocols", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->Protocols = ProcessStringSequenceNode(value); return {}; } }, { "FileExtensions", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->FileExtensions = ProcessStringSequenceNode(value); return {}; } }, { "Dependencies", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ValidateAndProcessFields(value, DependenciesFieldInfos, VariantManifestPtr(&(GetManifestInstallerPtr(v)->Dependencies))); }}, { "Capabilities", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->Capabilities = ProcessStringSequenceNode(value); return {}; } }, { "RestrictedCapabilities", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->RestrictedCapabilities = ProcessStringSequenceNode(value); return {}; } }, }; std::move(v1CommonFields.begin(), v1CommonFields.end(), std::inserter(result, result.end())); if (!forRootFields) { // Installer level only fields std::vector v1InstallerFields = { { "Architecture", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Arch = Utility::ConvertToArchitectureEnum(value.as()); return {}; } }, { "InstallerUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Url = value.as(); return {}; } }, { "InstallerSha256", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Sha256 = Utility::SHA256::ConvertToBytes(value.as()); return {}; } }, { "SignatureSha256", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->SignatureSha256 = Utility::SHA256::ConvertToBytes(value.as()); return {}; } }, // No custom validation needed at field populating time since we have semantic validation later to block msstore and productId from community repo. { "MSStoreProductIdentifier", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->ProductId = value.as(); return {}; } }, }; std::move(v1InstallerFields.begin(), v1InstallerFields.end(), std::inserter(result, result.end())); } } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_1 }) { std::vector fields_v1_1 = { { "InstallerAbortsTerminal", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->InstallerAbortsTerminal = value.as(); return {}; } }, { "InstallLocationRequired", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->InstallLocationRequired = value.as(); return {}; } }, { "RequireExplicitUpgrade", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->RequireExplicitUpgrade = value.as(); return {}; } }, { "ReleaseDate", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->ReleaseDate = Utility::Trim(value.as()); return {}; } }, { "UnsupportedOSArchitectures", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->UnsupportedOSArchitectures = ProcessArchitectureSequenceNode(value); return {}; } }, { "ElevationRequirement", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->ElevationRequirement = ConvertToElevationRequirementEnum(value.as()); return {}; } }, { "Markets", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessMarketsNode(value, GetManifestInstallerPtr(v)); } }, { "AppsAndFeaturesEntries", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessAppsAndFeaturesEntriesNode(value, GetManifestInstallerPtr(v)); } }, { "ExpectedReturnCodes", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessExpectedReturnCodesNode(value, GetManifestInstallerPtr(v)); } }, }; std::move(fields_v1_1.begin(), fields_v1_1.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_2 }) { std::vector fields_v1_2 = { { "UnsupportedArguments", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->UnsupportedArguments = ProcessUnsupportedArgumentsSequenceNode(value); return {}; } }, { "DisplayInstallWarnings", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->DisplayInstallWarnings = value.as(); return {}; } }, }; std::move(fields_v1_2.begin(), fields_v1_2.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_4 }) { std::vector fields_v1_4 = { { "NestedInstallerType", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->NestedInstallerType = ConvertToInstallerTypeEnum(value.as()); return {}; } }, { "NestedInstallerFiles", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessNestedInstallerFilesNode(value, GetManifestInstallerPtr(v)); } }, { "InstallationMetadata", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ValidateAndProcessFields(value, InstallationMetadataFieldInfos, VariantManifestPtr(&(GetManifestInstallerPtr(v)->InstallationMetadata))); }}, }; std::move(fields_v1_4.begin(), fields_v1_4.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_6 }) { std::vector fields_v1_6 = { { "DownloadCommandProhibited", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->DownloadCommandProhibited = value.as(); return {}; }, true }, }; std::move(fields_v1_6.begin(), fields_v1_6.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_7 }) { std::vector fields_v1_7 = { { "RepairBehavior", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->RepairBehavior = ConvertToRepairBehaviorEnum(value.as()); return {}; } }, }; std::move(fields_v1_7.begin(), fields_v1_7.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_9 }) { std::vector fields_v1_9 = { { "ArchiveBinariesDependOnPath", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->ArchiveBinariesDependOnPath = value.as(); return {}; } }, }; std::move(fields_v1_9.begin(), fields_v1_9.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_10 }) { std::vector fields_v1_10 = { { "Authentication", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestInstallerPtr(v)->AuthInfo = {}; auto errors = ValidateAndProcessFields(value, AuthenticationFieldInfos, VariantManifestPtr(&(GetManifestInstallerPtr(v)->AuthInfo))); GetManifestInstallerPtr(v)->AuthInfo.UpdateRequiredFieldsIfNecessary(); return errors; }, true}, }; std::move(fields_v1_10.begin(), fields_v1_10.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 }) { std::vector fields_v1_28 = { { "DesiredStateConfiguration", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { auto* installer = GetManifestInstallerPtr(v); installer->DesiredStateConfiguration.clear(); return ValidateAndProcessFields(value, DesiredStateConfigurationFieldInfos, VariantManifestPtr(&(installer->DesiredStateConfiguration))); } }, }; std::move(fields_v1_28.begin(), fields_v1_28.end(), std::inserter(result, result.end())); } } return result; } std::vector ManifestYamlPopulator::GetSwitchesFieldProcessInfo() { // Common fields across versions std::vector result = { { "Custom", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::Custom] = value.as(); return{}; } }, { "Silent", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::Silent] = value.as(); return{}; } }, { "SilentWithProgress", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::SilentWithProgress] = value.as(); return{}; } }, { "Interactive", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::Interactive] = value.as(); return{}; } }, { "Log", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::Log] = value.as(); return{}; } }, { "InstallLocation", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::InstallLocation] = value.as(); return{}; } }, }; // Additional version specific fields if (m_manifestVersion.get().Major() == 0) { // Language only exists in preview manifests. Though we don't use it in our code yet, keep it here to be consistent with schema. result.emplace_back("Language", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::Language] = value.as(); return{}; }); result.emplace_back("Update", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::Update] = value.as(); return{}; }); } else if (m_manifestVersion.get().Major() == 1) { result.emplace_back("Upgrade", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::Update] = value.as(); return{}; }); if (m_manifestVersion.get() >= ManifestVer{s_ManifestVersionV1_7}) { result.emplace_back("Repair", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { (*variant_ptr>(v))[InstallerSwitchType::Repair] = value.as(); return{}; }); }; } return result; } std::vector ManifestYamlPopulator::GetExpectedReturnCodesFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_1 }) { result.emplace_back("InstallerReturnCode", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->InstallerReturnCode = static_cast(value.as()); return {}; }); result.emplace_back("ReturnResponse", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->ReturnResponse = ConvertToExpectedReturnCodeEnum(value.as()); return {}; }); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_2 }) { result.emplace_back("ReturnResponseUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->ReturnResponseUrl = value.as(); return {}; }); } return result; } std::vector ManifestYamlPopulator::GetLocalizationFieldProcessInfo(bool forRootFields) { // Common fields across versions std::vector result = { { "Description", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(Utility::Trim(value.as())); return {}; } }, { "LicenseUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, }; // Additional version specific fields if (m_manifestVersion.get().Major() == 0) { // Root level and Localization node level result.emplace_back("Homepage", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; }); if (!forRootFields) { // Localization node only result.emplace_back("Language", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Locale = value.as(); return {}; }); } else { // Root node only std::vector rootOnlyFields = { { "Name", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtrFromManifest(v)->Add(Utility::Trim(value.as())); return {}; } }, { "Publisher", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtrFromManifest(v)->Add(value.as()); return {}; } }, { "Author", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtrFromManifest(v)->Add(value.as()); return {}; } }, { "License", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtrFromManifest(v)->Add(value.as()); return {}; } }, { "Tags", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtrFromManifest(v)->Add(SplitMultiValueField(value.as())); return {}; } }, }; std::move(rootOnlyFields.begin(), rootOnlyFields.end(), std::inserter(result, result.end())); } } else if (m_manifestVersion.get().Major() == 1) { // Starting v1, we should be only adding new fields for each minor version increase if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1 }) { // Root level and Localization node level std::vector v1CommonFields = { { "PackageLocale", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Locale = value.as(); return {}; } }, { "Publisher", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "PublisherUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "PublisherSupportUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "PrivacyUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "Author", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "PackageName", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(Utility::Trim(value.as())); return {}; } }, { "PackageUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "License", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "Copyright", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "CopyrightUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "ShortDescription", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(Utility::Trim(value.as())); return {}; } }, { "Tags", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(ProcessStringSequenceNode(value)); return {}; } }, }; std::move(v1CommonFields.begin(), v1CommonFields.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_1 }) { std::vector fields_v1_1 = { { "Agreements", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessAgreementsNode(value, GetManifestLocalizationPtr(v)); } }, { "ReleaseNotes", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "ReleaseNotesUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, }; std::move(fields_v1_1.begin(), fields_v1_1.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_2 }) { std::vector fields_v1_2 = { { "PurchaseUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "InstallationNotes", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Add(value.as()); return {}; } }, { "Documentations", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessDocumentationsNode(value, GetManifestLocalizationPtr(v)); } }, }; std::move(fields_v1_2.begin(), fields_v1_2.end(), std::inserter(result, result.end())); } if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_5 }) { std::vector fields_v1_5 = { { "Icons", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessIconsNode(value, GetManifestLocalizationPtr(v)); }, true }, }; std::move(fields_v1_5.begin(), fields_v1_5.end(), std::inserter(result, result.end())); } } return result; } std::vector ManifestYamlPopulator::GetDependenciesFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1 }) { result = { { "WindowsFeatures", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { ProcessDependenciesNode(DependencyType::WindowsFeature, value, variant_ptr(v)); return {}; } }, { "WindowsLibraries", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { ProcessDependenciesNode(DependencyType::WindowsLibrary, value, variant_ptr(v)); return {}; } }, { "PackageDependencies", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { ProcessPackageDependenciesNode(value, variant_ptr(v)); return {}; } }, { "ExternalDependencies", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { ProcessDependenciesNode(DependencyType::External, value, variant_ptr(v)); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetPackageDependenciesFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1 }) { result = { { "PackageIdentifier", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->SetId(Utility::Trim(value.as())); return {}; } }, { "MinimumVersion", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->MinVersion = Utility::Version(Utility::Trim(value.as())); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetAgreementFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_1 }) { result = { { "AgreementLabel", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Label = Utility::Trim(value.as()); return {}; } }, { "Agreement", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->AgreementText = Utility::Trim(value.as()); return {}; }, true }, { "AgreementUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->AgreementUrl = Utility::Trim(value.as()); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetMarketsFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_1 }) { result = { { "AllowedMarkets", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->AllowedMarkets = ProcessStringSequenceNode(value); return {}; } }, { "ExcludedMarkets", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->ExcludedMarkets = ProcessStringSequenceNode(value); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetAppsAndFeaturesEntryFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_1 }) { result = { { "DisplayName", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->DisplayName = Utility::Trim(value.as()); return {}; } }, { "Publisher", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Publisher = Utility::Trim(value.as()); return {}; } }, { "DisplayVersion", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->DisplayVersion = Utility::Trim(value.as()); return {}; } }, { "ProductCode", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->ProductCode = Utility::Trim(value.as()); return {}; } }, { "UpgradeCode", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->UpgradeCode = Utility::Trim(value.as()); return {}; } }, { "InstallerType", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->InstallerType = ConvertToInstallerTypeEnum(value.as()); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetDocumentationFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_2 }) { result = { { "DocumentLabel", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->DocumentLabel = Utility::Trim(value.as()); return {}; } }, { "DocumentUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->DocumentUrl = Utility::Trim(value.as()); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetIconFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_5 }) { result = { { "IconUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Url = Utility::Trim(value.as()); return {}; } }, { "IconFileType", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->FileType = ConvertToIconFileTypeEnum(value.as()); return {}; } }, { "IconResolution", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Resolution = ConvertToIconResolutionEnum(value.as()); return {}; } }, { "IconTheme", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Theme = ConvertToIconThemeEnum(value.as()); return {}; } }, { "IconSha256", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Sha256 = Utility::SHA256::ConvertToBytes(value.as()); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetNestedInstallerFileFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_4 }) { result = { { "RelativeFilePath", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->RelativeFilePath = Utility::Trim(value.as()); return {}; } }, { "PortableCommandAlias", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->PortableCommandAlias = Utility::Trim(value.as()); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetInstallationMetadataFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_4 }) { result = { { "DefaultInstallLocation", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->DefaultInstallLocation = Utility::Trim(value.as()); return {}; } }, { "Files", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessInstallationMetadataFilesNode(value, variant_ptr(v)); } }, }; } return result; } std::vector ManifestYamlPopulator::GetInstallationMetadataFilesFieldProcessInfo() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_4 }) { result = { { "RelativeFilePath", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->RelativeFilePath = Utility::Trim(value.as()); return {}; } }, { "FileSha256", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->FileSha256 = Utility::SHA256::ConvertToBytes(value.as()); return {}; } }, { "FileType", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->FileType = ConvertToInstalledFileTypeEnum(value.as()); return {}; } }, { "InvocationParameter", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->InvocationParameter = Utility::Trim(value.as()); return {}; } }, { "DisplayName", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->DisplayName = Utility::Trim(value.as()); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetAuthenticationFieldInfos() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_10 }) { result = { { "AuthenticationType", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Type = Authentication::ConvertToAuthenticationType(value.as()); return {}; } }, { "MicrosoftEntraIdAuthenticationInfo", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->MicrosoftEntraIdInfo.emplace(); return ValidateAndProcessFields(value, MicrosoftEntraIdAuthenticationInfoFieldInfos, VariantManifestPtr(&(variant_ptr(v)->MicrosoftEntraIdInfo.value()))); }}, }; } return result; } std::vector ManifestYamlPopulator::GetMicrosoftEntraIdAuthenticationInfoFieldInfos() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_10 }) { result = { { "Resource", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Resource = Utility::Trim(value.as()); return {}; } }, { "Scope", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Scope = Utility::Trim(value.as()); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetShadowRootFieldProcessInfo() { std::vector result; if (m_manifestVersion.get().Major() == 1) { if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_5 }) { std::vector fields_v1_5 = { { { "Localization", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessShadowLocalizationNode(value, variant_ptr(v)); } }, { "ManifestType", [](const YAML::Node&, const VariantManifestPtr&)->ValidationErrors { return {}; } }, { "PackageIdentifier", [](const YAML::Node&, const VariantManifestPtr&)->ValidationErrors { return {}; } }, { "PackageVersion", [](const YAML::Node&, const VariantManifestPtr&)->ValidationErrors { return {}; } }, { "ManifestVersion", [](const YAML::Node&, const VariantManifestPtr&)->ValidationErrors { return {}; } }, }, }; std::move(fields_v1_5.begin(), fields_v1_5.end(), std::inserter(result, result.end())); } } auto rootLocalizationFields = GetShadowLocalizationFieldProcessInfo(); std::move(rootLocalizationFields.begin(), rootLocalizationFields.end(), std::inserter(result, result.end())); return result; } std::vector ManifestYamlPopulator::GetShadowLocalizationFieldProcessInfo() { std::vector result; if (m_manifestVersion.get().Major() == 1) { if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_5 }) { std::vector fields_v1_5 = { { "PackageLocale", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { GetManifestLocalizationPtr(v)->Locale = value.as(); return {}; } }, { "Icons", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessIconsNode(value, GetManifestLocalizationPtr(v)); } }, }; std::move(fields_v1_5.begin(), fields_v1_5.end(), std::inserter(result, result.end())); } } return result; } std::vector ManifestYamlPopulator::GetDesiredStateConfigurationFieldInfos() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 }) { result = { { "PowerShell", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessDSC_PowerShellModuleNode(value, variant_ptr>(v)); } }, { "DSCv3", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { auto* variantValue = variant_ptr>(v); variantValue->emplace_back(DesiredStateConfigurationContainerType::DSCv3); return ValidateAndProcessFields(value, DesiredStateConfigurationDSCv3FieldInfos, VariantManifestPtr(&variantValue->back())); } }, }; } return result; } std::vector ManifestYamlPopulator::GetDesiredStateConfigurationPowerShellModuleFieldInfos() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 }) { result = { { "RepositoryUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->RepositoryURL = Utility::Trim(value.as()); return {}; } }, { "ModuleName", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->ModuleName = Utility::Trim(value.as()); return {}; } }, { "Resources", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessDSC_PowerShellResourcesNode(value, variant_ptr(v)); } }, }; } return result; } std::vector ManifestYamlPopulator::GetDesiredStateConfigurationPowerShellResourceFieldInfos() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 }) { result = { { "Name", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Name = Utility::Trim(value.as()); return {}; } }, }; } return result; } std::vector ManifestYamlPopulator::GetDesiredStateConfigurationDSCv3FieldInfos() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 }) { result = { { "Resources", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessDSCv3ResourcesNode(value, variant_ptr(v)); } }, }; } return result; } std::vector ManifestYamlPopulator::GetDesiredStateConfigurationDSCv3ResourceFieldInfos() { std::vector result = {}; if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 }) { result = { { "Type", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Name = Utility::Trim(value.as()); return {}; } }, }; } return result; } ValidationErrors ManifestYamlPopulator::ValidateAndProcessFields( const YAML::Node& rootNode, const std::vector& fieldInfos, const VariantManifestPtr& v) { ValidationErrors resultErrors; if (!rootNode.IsMap() || rootNode.size() == 0) { resultErrors.emplace_back(ManifestError::InvalidRootNode, "", "", m_isMergedManifest ? 0 : rootNode.Mark().line, m_isMergedManifest ? 0 : rootNode.Mark().column); return resultErrors; } // Keeps track of already processed fields. Used to check duplicate fields. std::set processedFields; for (auto const& keyValuePair : rootNode.Mapping()) { std::string key = keyValuePair.first.as(); const YAML::Node& valueNode = keyValuePair.second; // We'll do case-insensitive search first and validate correct case later. auto fieldIter = std::find_if(fieldInfos.begin(), fieldInfos.end(), [&](auto const& s) { return Utility::CaseInsensitiveEquals(s.Name, key); }); if (fieldIter != fieldInfos.end()) { const FieldProcessInfo& fieldInfo = *fieldIter; // Make sure the found key is in Pascal Case if (key != fieldInfo.Name) { resultErrors.emplace_back(ManifestError::FieldIsNotPascalCase, key, "", m_isMergedManifest ? 0 : keyValuePair.first.Mark().line, m_isMergedManifest ? 0 : keyValuePair.first.Mark().column); } // Make sure it's not a duplicate key if (!processedFields.insert(fieldInfo.Name).second) { resultErrors.emplace_back(ManifestError::FieldDuplicate, fieldInfo.Name, "", m_isMergedManifest ? 0 : keyValuePair.first.Mark().line, m_isMergedManifest ? 0 : keyValuePair.first.Mark().column); } if (fieldInfo.RequireVerifiedPublisher) { resultErrors.emplace_back(ManifestError::FieldRequireVerifiedPublisher, fieldInfo.Name, "", m_isMergedManifest ? 0 : keyValuePair.first.Mark().line, m_isMergedManifest ? 0 : keyValuePair.first.Mark().column, m_validateOption.ErrorOnVerifiedPublisherFields ? ValidationError::Level::Error : ValidationError::Level::Warning); } if (!valueNode.IsNull()) { try { auto errors = fieldInfo.ProcessFunc(valueNode, v); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } catch (const std::exception&) { resultErrors.emplace_back(ManifestError::FieldFailedToProcess, fieldInfo.Name); } } } else { // For full validation, also reports unrecognized fields as warning if (m_validateOption.FullValidation) { resultErrors.emplace_back(ManifestError::FieldUnknown, key, "", m_isMergedManifest ? 0 : keyValuePair.first.Mark().line, m_isMergedManifest ? 0 : keyValuePair.first.Mark().column, ValidationError::Level::Warning); } } } return resultErrors; } ValidationErrors ManifestYamlPopulator::ProcessPackageDependenciesNode(const YAML::Node& rootNode, DependencyList* dependencyList) { ValidationErrors resultErrors; for (auto const& entry : rootNode.Sequence()) { Dependency packageDependency = Dependency(DependencyType::Package); auto errors = ValidateAndProcessFields(entry, PackageDependenciesFieldInfos, VariantManifestPtr(&packageDependency)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); dependencyList->Add(std::move(packageDependency)); } return resultErrors; } ValidationErrors ManifestYamlPopulator::ProcessAgreementsNode(const YAML::Node& agreementsNode, ManifestLocalization* localization) { THROW_HR_IF(E_INVALIDARG, !agreementsNode.IsSequence()); ValidationErrors resultErrors; std::vector agreements; for (auto const& entry : agreementsNode.Sequence()) { Agreement agreement; auto errors = ValidateAndProcessFields(entry, AgreementFieldInfos, VariantManifestPtr(&agreement)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); agreements.emplace_back(std::move(agreement)); } if (!agreements.empty()) { localization->Add(std::move(agreements)); } return resultErrors; } std::vector ManifestYamlPopulator::ProcessMarketsNode(const YAML::Node& marketsNode, ManifestInstaller* installer) { MarketsInfo markets; auto errors = ValidateAndProcessFields(marketsNode, MarketsFieldInfos, VariantManifestPtr(&markets)); installer->Markets = markets; return errors; } std::vector ManifestYamlPopulator::ProcessAppsAndFeaturesEntriesNode(const YAML::Node& appsAndFeaturesEntriesNode, ManifestInstaller* installer) { THROW_HR_IF(E_INVALIDARG, !appsAndFeaturesEntriesNode.IsSequence()); ValidationErrors resultErrors; std::vector appsAndFeaturesEntries; for (auto const& entry : appsAndFeaturesEntriesNode.Sequence()) { AppsAndFeaturesEntry appsAndFeaturesEntry; auto errors = ValidateAndProcessFields(entry, AppsAndFeaturesEntryFieldInfos, VariantManifestPtr(&appsAndFeaturesEntry)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); appsAndFeaturesEntries.emplace_back(std::move(appsAndFeaturesEntry)); } installer->AppsAndFeaturesEntries = appsAndFeaturesEntries; return resultErrors; } ValidationErrors ManifestYamlPopulator::ProcessExpectedReturnCodesNode(const YAML::Node& returnCodesNode, ManifestInstaller* installer) { THROW_HR_IF(E_INVALIDARG, !returnCodesNode.IsSequence()); ValidationErrors resultErrors; std::map returnCodes; for (auto const& entry : returnCodesNode.Sequence()) { ExpectedReturnCode returnCode; auto errors = ValidateAndProcessFields(entry, ExpectedReturnCodesFieldInfos, VariantManifestPtr(&returnCode)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); if (!returnCodes.insert({ returnCode.InstallerReturnCode, {returnCode.ReturnResponse, returnCode.ReturnResponseUrl} }).second) { resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); } } installer->ExpectedReturnCodes = returnCodes; return resultErrors; } ValidationErrors ManifestYamlPopulator::ProcessDocumentationsNode(const YAML::Node& documentationsNode, ManifestLocalization* localization) { THROW_HR_IF(E_INVALIDARG, !documentationsNode.IsSequence()); ValidationErrors resultErrors; std::vector documentations; for (auto const& entry : documentationsNode.Sequence()) { Documentation documentation; auto errors = ValidateAndProcessFields(entry, DocumentationFieldInfos, VariantManifestPtr(&documentation)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); documentations.emplace_back(std::move(documentation)); } if (!documentations.empty()) { localization->Add(std::move(documentations)); } return resultErrors; } std::vector ManifestYamlPopulator::ProcessIconsNode(const YAML::Node& iconsNode, ManifestLocalization* localization) { THROW_HR_IF(E_INVALIDARG, !iconsNode.IsSequence()); ValidationErrors resultErrors; std::vector icons; for (auto const& entry : iconsNode.Sequence()) { Icon icon; auto errors = ValidateAndProcessFields(entry, IconFieldInfos, VariantManifestPtr(&icon)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); icons.emplace_back(std::move(icon)); } if (!icons.empty()) { localization->Add(std::move(icons)); } return resultErrors; } ValidationErrors ManifestYamlPopulator::ProcessNestedInstallerFilesNode(const YAML::Node& nestedInstallerFilesNode, ManifestInstaller* installer) { THROW_HR_IF(E_INVALIDARG, !nestedInstallerFilesNode.IsSequence()); ValidationErrors resultErrors; std::vector nestedInstallerFiles; for (auto const& entry : nestedInstallerFilesNode.Sequence()) { NestedInstallerFile nestedInstallerFile; auto errors = ValidateAndProcessFields(entry, NestedInstallerFileFieldInfos, VariantManifestPtr(&nestedInstallerFile)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); nestedInstallerFiles.emplace_back(std::move(nestedInstallerFile)); } if (!nestedInstallerFiles.empty()) { installer->NestedInstallerFiles = nestedInstallerFiles; } return resultErrors; } std::vector ManifestYamlPopulator::ProcessInstallationMetadataFilesNode(const YAML::Node& installedFilesNode, InstallationMetadataInfo* installationMetadata) { THROW_HR_IF(E_INVALIDARG, !installedFilesNode.IsSequence()); ValidationErrors resultErrors; std::vector installedFiles; for (auto const& entry : installedFilesNode.Sequence()) { InstalledFile installedFile; auto errors = ValidateAndProcessFields(entry, InstallationMetadataFilesFieldInfos, VariantManifestPtr(&installedFile)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); installedFiles.emplace_back(std::move(installedFile)); } if (!installedFiles.empty()) { installationMetadata->Files = installedFiles; } return resultErrors; } std::vector ManifestYamlPopulator::ProcessShadowLocalizationNode(const YAML::Node& localizationNode, Manifest* manifest) { THROW_HR_IF(E_INVALIDARG, !localizationNode.IsSequence()); ValidationErrors resultErrors; auto shadowLocalizationFields = GetShadowLocalizationFieldProcessInfo(); for (auto const& entry : localizationNode.Sequence()) { ManifestLocalization localization; auto errors = ValidateAndProcessFields(entry, shadowLocalizationFields, VariantManifestPtr(&localization)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); manifest->Localizations.emplace_back(std::move(std::move(localization))); } return resultErrors; } std::vector ManifestYamlPopulator::ProcessDSC_PowerShellModuleNode(const YAML::Node& node, std::vector* containers) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); ValidationErrors resultErrors; for (auto const& entry : node.Sequence()) { auto& containerInfo = containers->emplace_back(DesiredStateConfigurationContainerType::PowerShell); auto errors = ValidateAndProcessFields(entry, DesiredStateConfigurationPowerShellModuleFieldInfos, VariantManifestPtr(&containerInfo)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } return resultErrors; } std::vector ManifestYamlPopulator::ProcessDSC_PowerShellResourcesNode(const YAML::Node& node, DesiredStateConfigurationContainerInfo* container) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); ValidationErrors resultErrors; for (auto const& entry : node.Sequence()) { auto& resourceInfo = container->Resources.emplace_back(); auto errors = ValidateAndProcessFields(entry, DesiredStateConfigurationPowerShellResourceFieldInfos, VariantManifestPtr(&resourceInfo)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } return resultErrors; } std::vector ManifestYamlPopulator::ProcessDSCv3ResourcesNode(const YAML::Node& node, DesiredStateConfigurationContainerInfo* container) { THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); ValidationErrors resultErrors; for (auto const& entry : node.Sequence()) { auto& resourceInfo = container->Resources.emplace_back(); auto errors = ValidateAndProcessFields(entry, DesiredStateConfigurationDSCv3ResourceFieldInfos, VariantManifestPtr(&resourceInfo)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } return resultErrors; } ManifestYamlPopulator::ManifestYamlPopulator(YAML::Node& rootNode, Manifest& manifest, const ManifestVer& manifestVersion, ManifestValidateOption validateOption) : m_rootNode(rootNode), m_manifest(manifest), m_manifestVersion(manifestVersion), m_validateOption(validateOption) { m_isMergedManifest = !m_rootNode.get()["ManifestType"sv].IsNull() && m_rootNode.get()["ManifestType"sv].as() == "merged"; m_manifest.get().ManifestVersion = m_manifestVersion; } ValidationErrors ManifestYamlPopulator::PopulateManifestInternal() { const YAML::Node& rootNode = m_rootNode; ValidationErrors resultErrors; // Prepare field infos RootFieldInfos = GetRootFieldProcessInfo(); InstallerFieldInfos = GetInstallerFieldProcessInfo(); SwitchesFieldInfos = GetSwitchesFieldProcessInfo(); ExpectedReturnCodesFieldInfos = GetExpectedReturnCodesFieldProcessInfo(); DependenciesFieldInfos = GetDependenciesFieldProcessInfo(); PackageDependenciesFieldInfos = GetPackageDependenciesFieldProcessInfo(); LocalizationFieldInfos = GetLocalizationFieldProcessInfo(); AgreementFieldInfos = GetAgreementFieldProcessInfo(); MarketsFieldInfos = GetMarketsFieldProcessInfo(); AppsAndFeaturesEntryFieldInfos = GetAppsAndFeaturesEntryFieldProcessInfo(); DocumentationFieldInfos = GetDocumentationFieldProcessInfo(); IconFieldInfos = GetIconFieldProcessInfo(); NestedInstallerFileFieldInfos = GetNestedInstallerFileFieldProcessInfo(); InstallationMetadataFieldInfos = GetInstallationMetadataFieldProcessInfo(); InstallationMetadataFilesFieldInfos = GetInstallationMetadataFilesFieldProcessInfo(); AuthenticationFieldInfos = GetAuthenticationFieldInfos(); MicrosoftEntraIdAuthenticationInfoFieldInfos = GetMicrosoftEntraIdAuthenticationInfoFieldInfos(); DesiredStateConfigurationFieldInfos = GetDesiredStateConfigurationFieldInfos(); DesiredStateConfigurationPowerShellModuleFieldInfos = GetDesiredStateConfigurationPowerShellModuleFieldInfos(); DesiredStateConfigurationPowerShellResourceFieldInfos = GetDesiredStateConfigurationPowerShellResourceFieldInfos(); DesiredStateConfigurationDSCv3FieldInfos = GetDesiredStateConfigurationDSCv3FieldInfos(); DesiredStateConfigurationDSCv3ResourceFieldInfos = GetDesiredStateConfigurationDSCv3ResourceFieldInfos(); resultErrors = ValidateAndProcessFields(rootNode, RootFieldInfos, VariantManifestPtr(&(m_manifest.get()))); if (!m_p_installersNode) { return resultErrors; } // Populate installers for (auto const& entry : m_p_installersNode->Sequence()) { ManifestInstaller installer = m_manifest.get().DefaultInstallerInfo; #define WINGET_STASH_INSTALLER_PROPERTY(_property_,_clear_) \ auto stashed ## _property_ = std::move(installer. _property_); \ installer. _property_ . _clear_ (); #define WINGET_UNSTASH_INSTALLER_PROPERTY(_property_) \ installer. _property_ = std::move(stashed ## _property_); // Clear these defaults as PackageFamilyName, ProductCode, AppsAndFeaturesEntries need to be copied based on InstallerType WINGET_STASH_INSTALLER_PROPERTY(PackageFamilyName, clear); WINGET_STASH_INSTALLER_PROPERTY(ProductCode, clear); WINGET_STASH_INSTALLER_PROPERTY(AppsAndFeaturesEntries, clear); // Clear dependencies as installer specific overrides root WINGET_STASH_INSTALLER_PROPERTY(Dependencies, Clear); // Clear nested installers as it should only be copied for zip installerType. installer.NestedInstallerType = InstallerTypeEnum::Unknown; WINGET_STASH_INSTALLER_PROPERTY(NestedInstallerFiles, clear); auto errors = ValidateAndProcessFields(entry, InstallerFieldInfos, VariantManifestPtr(&installer)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); // Set installer type back before attempting to use it in any of the EffectiveInstallerType calls below if (IsArchiveType(installer.BaseInstallerType)) { if (installer.NestedInstallerFiles.empty()) { WINGET_UNSTASH_INSTALLER_PROPERTY(NestedInstallerFiles); } if (installer.NestedInstallerType == InstallerTypeEnum::Unknown) { installer.NestedInstallerType = m_manifest.get().DefaultInstallerInfo.NestedInstallerType; } } // Copy in system reference strings from the root if not set in the installer and appropriate if (installer.AppsAndFeaturesEntries.empty() && DoesInstallerTypeWriteAppsAndFeaturesEntry(installer.EffectiveInstallerType())) { WINGET_UNSTASH_INSTALLER_PROPERTY(AppsAndFeaturesEntries); } if (installer.PackageFamilyName.empty() && (DoesInstallerTypeUsePackageFamilyName(installer.EffectiveInstallerType()) || DoAnyAppsAndFeaturesEntriesUsePackageFamilyName(installer.AppsAndFeaturesEntries))) { WINGET_UNSTASH_INSTALLER_PROPERTY(PackageFamilyName); } if (installer.ProductCode.empty() && DoesInstallerTypeUseProductCode(installer.EffectiveInstallerType())) { WINGET_UNSTASH_INSTALLER_PROPERTY(ProductCode); } // If there are no dependencies on installer use default ones if (!installer.Dependencies.HasAny()) { WINGET_UNSTASH_INSTALLER_PROPERTY(Dependencies); } // Populate installer default switches if not exists auto defaultSwitches = GetDefaultKnownSwitches(installer.EffectiveInstallerType()); for (auto const& defaultSwitch : defaultSwitches) { if (installer.Switches.find(defaultSwitch.first) == installer.Switches.end()) { installer.Switches[defaultSwitch.first] = defaultSwitch.second; } } // Populate installer default return codes if not present in ExpectedReturnCodes and InstallerSuccessCodes auto defaultReturnCodes = GetDefaultKnownReturnCodes(installer.EffectiveInstallerType()); for (auto const& defaultReturnCode : defaultReturnCodes) { if (installer.ExpectedReturnCodes.find(defaultReturnCode.first) == installer.ExpectedReturnCodes.end() && std::find(installer.InstallerSuccessCodes.begin(), installer.InstallerSuccessCodes.end(), defaultReturnCode.first) == installer.InstallerSuccessCodes.end()) { installer.ExpectedReturnCodes[defaultReturnCode.first].ReturnResponseEnum = defaultReturnCode.second; } } m_manifest.get().Installers.emplace_back(std::move(installer)); } // Populate additional localizations if (m_p_localizationsNode && m_p_localizationsNode->IsSequence()) { for (auto const& entry : m_p_localizationsNode->Sequence()) { ManifestLocalization localization; auto errors = ValidateAndProcessFields(entry, LocalizationFieldInfos, VariantManifestPtr(&localization)); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); m_manifest.get().Localizations.emplace_back(std::move(std::move(localization))); } } return resultErrors; } ValidationErrors ManifestYamlPopulator::InsertShadow(const YAML::Node& shadowNode) { Manifest shadowManifest; // Process shadow node. auto resultErrors = ValidateAndProcessFields(shadowNode, GetShadowRootFieldProcessInfo(), VariantManifestPtr(&shadowManifest)); // Merge. if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_5 }) { // Default localization if (Utility::ICUCaseInsensitiveEquals(m_manifest.get().DefaultLocalization.Locale, shadowManifest.DefaultLocalization.Locale)) { // Icons if (!m_manifest.get().DefaultLocalization.Contains(Localization::Icons) && shadowManifest.DefaultLocalization.Contains(Localization::Icons)) { m_manifest.get().DefaultLocalization.Add(std::move(shadowManifest.DefaultLocalization.Get())); YAML::Node key{ YAML::Node::Type::Scalar, "", YAML::Mark() }; key.SetScalar("Icons"); YAML::Node value = shadowNode.GetChildNode("Icons"); m_rootNode.get().AddMappingNode(std::move(key), std::move(value)); } } // Localizations if (!shadowManifest.Localizations.empty()) { // Merge manifest object for (auto const& shadowLocalization : shadowManifest.Localizations) { // Manifest if (auto iter = std::find_if(m_manifest.get().Localizations.begin(), m_manifest.get().Localizations.end(), [&](auto const& l) { return Utility::ICUCaseInsensitiveEquals(l.Locale, shadowLocalization.Locale); }); iter != m_manifest.get().Localizations.end()) { if (!(*iter).Contains(Localization::Icons) && shadowLocalization.Contains(Localization::Icons)) { (*iter).Add(std::move(shadowLocalization.Get())); } } else { ManifestLocalization localization = shadowLocalization; m_manifest.get().Localizations.emplace_back(std::move(std::move(localization))); } } // Merge yaml auto shadowLocalizationsNode = shadowNode.GetChildNode("Localization"); if (m_p_localizationsNode) { m_rootNode.get().GetChildNode("Localization").MergeSequenceNode(shadowLocalizationsNode, "PackageLocale", true); } else { YAML::Node key{ YAML::Node::Type::Scalar, "", YAML::Mark() }; key.SetScalar("Localization"); m_rootNode.get().AddMappingNode(std::move(key), std::move(shadowLocalizationsNode)); } } } return resultErrors; } ValidationErrors ManifestYamlPopulator::PopulateManifest( YAML::Node& rootNode, Manifest& manifest, const ManifestVer& manifestVersion, ManifestValidateOption validateOption, const std::optional& shadowNode) { ManifestYamlPopulator manifestPopulator(rootNode, manifest, manifestVersion, validateOption); auto errors = manifestPopulator.PopulateManifestInternal(); if (shadowNode.has_value()) { auto shadowErrors = manifestPopulator.InsertShadow(shadowNode.value()); std::move(shadowErrors.begin(), shadowErrors.end(), std::inserter(errors, errors.end())); } return errors; } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/MsixManifestValidation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerLogging.h" #include "AppInstallerRuntime.h" #include "AppInstallerDownloader.h" #include "winget/MsixManifestValidation.h" namespace AppInstaller::Manifest { std::vector MsixManifestValidation::Validate( const Manifest& manifest, const ManifestInstaller& installer) { std::vector errors; auto msixInfo = GetMsixInfo(installer.Url); if (msixInfo) { ValidateMsixManifestSignatureHash(msixInfo, installer.SignatureSha256, errors); auto msixManifests = msixInfo->GetAppPackageManifests(); auto installerMinOSVersion = GetManifestInstallerMinOSVersion(installer.MinOSVersion, errors); for (const auto& msixManifest : msixManifests) { auto msixManifestIdentity = msixManifest.GetIdentity(); ValidateMsixManifestPackageFamilyName(msixManifestIdentity.GetPackageFamilyName(), installer.PackageFamilyName, errors); ValidateMsixManifestPackageVersion(msixManifestIdentity.GetVersion(), manifest.Version, errors); ValidateMsixManifestMinOSVersion(msixManifest.GetMinimumOSVersionForSupportedPlatforms(), installerMinOSVersion, installer.Url, errors); } } else { errors.emplace_back(ManifestError::InstallerFailedToProcess, "InstallerUrl", installer.Url); } return errors; } MsixManifestValidation::~MsixManifestValidation() { AICLI_LOG(Core, Info, << "Removing downloaded installers"); for (const auto& installerPath : m_downloadedInstallers) { try { std::filesystem::remove(installerPath); } catch (...) { AICLI_LOG(Core, Warning, << "Failed to remove downloaded installer: " << installerPath); } } } std::optional MsixManifestValidation::DownloadInstaller(std::string installerUrl, int retryCount) { while (retryCount-- > 0) { try { AICLI_LOG(Core, Info, << "Start downloading installer"); auto tempFile = Runtime::GetNewTempFilePath(); ProgressCallback emptyCallback; Utility::Download(installerUrl, tempFile, Utility::DownloadType::Installer, emptyCallback); m_downloadedInstallers.push_back(tempFile); return tempFile; } catch (...) { AICLI_LOG(Core, Error, << "Downloading installer failed. Remaining attempts: " << retryCount); } } return std::nullopt; } std::shared_ptr MsixManifestValidation::GetMsixInfoFromUrl(std::string installerUrl) { try { AICLI_LOG(Core, Info, << "Fetching Msix info from installer url"); return std::make_shared(installerUrl); } catch (...) { AICLI_LOG(Core, Error, << "Error fetching Msix info from the installer url."); return nullptr; } } std::shared_ptr MsixManifestValidation::GetMsixInfoFromLocalPath(std::string installerUrl) { int maxRetry = 3; std::shared_ptr msixInfo; auto installerPath = DownloadInstaller(installerUrl, maxRetry); if (installerPath.has_value()) { try { AICLI_LOG(Core, Info, << "Fetching Msix info from installer local path"); msixInfo = std::make_shared(installerPath.value()); } catch (...) { AICLI_LOG(Core, Error, << "Error fetching Msix info from the installer local path."); } } else { AICLI_LOG(Core, Error, << "Failed to download installer."); } return msixInfo; } std::shared_ptr MsixManifestValidation::GetMsixInfo(std::string installerUrl) { std::shared_ptr msixInfo; // Cache Msix info for new installer url auto findMsixInfo = m_msixInfoCache.find(installerUrl); if (findMsixInfo == m_msixInfoCache.end()) { msixInfo = GetMsixInfoFromUrl(installerUrl); if (!msixInfo) { AICLI_LOG(Core, Warning, << "Failed to get Msix info directly from the installer url. " << "Downloading installer instead."); msixInfo = GetMsixInfoFromLocalPath(installerUrl); } if (msixInfo) { m_msixInfoCache.insert({ installerUrl, msixInfo }); } else { AICLI_LOG(Core, Error, << "Msix info could not be obtained."); } } else { msixInfo = findMsixInfo->second; } return msixInfo; } std::optional MsixManifestValidation::GetManifestInstallerMinOSVersion( std::string minOSVersion, std::vector& errors) { try { if (!minOSVersion.empty()) { return std::make_optional(minOSVersion); } } catch (const std::exception&) { errors.emplace_back(ManifestError::InvalidFieldValue, "MinimumOSVersion", minOSVersion); } return std::nullopt; } void MsixManifestValidation::ValidateMsixManifestPackageFamilyName( Utility::NormalizedString msixPackageFamilyName, Utility::NormalizedString manifestPackageFamilyName, std::vector& errors) { if (!manifestPackageFamilyName.empty()) { if (manifestPackageFamilyName != msixPackageFamilyName) { errors.emplace_back(ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", msixPackageFamilyName); } } else { errors.emplace_back(ManifestError::OptionalFieldMissing, "PackageFamilyName", msixPackageFamilyName, m_validationErrorLevel); } } void MsixManifestValidation::ValidateMsixManifestPackageVersion( const Msix::PackageVersion& msixPackageVersion, const string_t& manifestPackageVersionStr, std::vector& errors) { std::optional manifestPackageVersion; try { manifestPackageVersion = { manifestPackageVersionStr }; } catch (...) { AICLI_LOG(Core, Error, << "Failed to parse package version to UINT64"); } if (!manifestPackageVersion.has_value() || msixPackageVersion != manifestPackageVersion.value()) { errors.emplace_back(ManifestError::InstallerMsixInconsistencies, "PackageVersion", msixPackageVersion.ToString()); } } void MsixManifestValidation::ValidateMsixManifestMinOSVersion( const std::optional& msixMinOSVersion, const std::optional& manifestMinOSVersion, std::string installerUrl, std::vector& errors) { if (!msixMinOSVersion.has_value()) { errors.emplace_back(ManifestError::NoSupportedPlatforms, "InstallerUrl", installerUrl); } else if (manifestMinOSVersion.has_value()) { if (msixMinOSVersion.value() != manifestMinOSVersion.value()) { errors.emplace_back(ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", msixMinOSVersion.value().ToString()); } } else { errors.emplace_back( ManifestError::OptionalFieldMissing, "MinimumOSVersion", msixMinOSVersion.value().ToString(), m_validationErrorLevel); } } void MsixManifestValidation::ValidateMsixManifestSignatureHash( const std::shared_ptr msixInfo, const Utility::SHA256::HashBuffer& manifestSignatureHash, std::vector& errors) { try { if (!manifestSignatureHash.empty()) { auto msixSignatureHash = msixInfo->GetSignatureHash(); if (msixSignatureHash != manifestSignatureHash) { auto msixSignatureHashString = Utility::SHA256::ConvertToString(msixSignatureHash); errors.emplace_back(ManifestError::InstallerMsixInconsistencies, "SignatureSha256", msixSignatureHashString); } } } catch (const wil::ResultException&) { errors.emplace_back(ManifestError::MsixSignatureHashFailed); } } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/YamlParser.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerSHA256.h" #include "winget/Yaml.h" #include "winget/ManifestSchemaValidation.h" #include "winget/ManifestYamlPopulator.h" #include "winget/ManifestYamlParser.h" namespace AppInstaller::Manifest::YamlParser { namespace { // Basic V1 manifest required fields check for later manifest consistency check void ValidateV1ManifestInput(const YamlManifestInfo& entry) { std::vector errors; if (!entry.Root.IsMap()) { THROW_EXCEPTION_MSG(ManifestException(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST), "The manifest does not contain a valid root. File: %hs", entry.FileName.c_str()); } if (!entry.Root["PackageIdentifier"]) { errors.emplace_back(ValidationError::MessageContextWithFile( ManifestError::RequiredFieldMissing, "PackageIdentifier", entry.FileName)); } if (!entry.Root["PackageVersion"]) { errors.emplace_back(ValidationError::MessageContextWithFile( ManifestError::RequiredFieldMissing, "PackageVersion", entry.FileName)); } if (!entry.Root["ManifestVersion"]) { errors.emplace_back(ValidationError::MessageContextWithFile( ManifestError::RequiredFieldMissing, "ManifestVersion", entry.FileName)); } if (!entry.Root["ManifestType"]) { errors.emplace_back(ValidationError::MessageContextWithFile( ManifestError::InconsistentMultiFileManifestFieldValue, "ManifestType", entry.FileName)); } else { auto manifestType = ConvertToManifestTypeEnum(entry.Root["ManifestType"].as()); switch (manifestType) { case ManifestTypeEnum::Version: if (!entry.Root["DefaultLocale"]) { errors.emplace_back(ValidationError::MessageContextWithFile( ManifestError::RequiredFieldMissing, "DefaultLocale", entry.FileName)); } break; case ManifestTypeEnum::Singleton: case ManifestTypeEnum::Locale: case ManifestTypeEnum::DefaultLocale: if (!entry.Root["PackageLocale"]) { errors.emplace_back(ValidationError::MessageContextWithFile( ManifestError::RequiredFieldMissing, "PackageLocale", entry.FileName)); } break; } } if (!errors.empty()) { ManifestException ex{ std::move(errors) }; THROW_EXCEPTION(ex); } } // Input validations: // - Determine manifest version // - Check multi file manifest input integrity // - All manifests use same PackageIdentifier, PackageVersion, ManifestVersion // - All required types exist and exist only once. i.e. version, installer, defaultLocale // - No duplicate locales across manifests // - DefaultLocale matches in version manifest and defaultLocale manifest // - Validate manifest type correctness // - Allowed file type in multi file manifest: version, installer, defaultLocale, locale // - Allowed file type in single file manifest: preview manifest, merged and singleton ManifestVer ValidateInput(std::vector& input, ManifestValidateOption validateOption) { std::vector errors; std::string manifestVersionStr; ManifestVer manifestVersion; ManifestVer ManifestVersionV1{ s_ManifestVersionV1 }; bool isMultifileManifest = input.size() > 1; // Use the first manifest doc to determine ManifestVersion, there'll be checks for manifest version consistency later auto& firstYamlManifest = input[0]; if (!firstYamlManifest.Root.IsMap()) { THROW_EXCEPTION_MSG(ManifestException(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST), "The manifest does not contain a valid root. File: %hs", firstYamlManifest.FileName.c_str()); } if (firstYamlManifest.Root["ManifestVersion"sv]) { manifestVersionStr = firstYamlManifest.Root["ManifestVersion"sv].as(); } else { manifestVersionStr = s_DefaultManifestVersion; } manifestVersion = ManifestVer{ manifestVersionStr }; // Check max supported version if (manifestVersion.Major() > s_MaxSupportedMajorVersion) { THROW_EXCEPTION_MSG(ManifestException(APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION), "Unsupported ManifestVersion: %hs", manifestVersion.ToString().c_str()); } // Preview manifest validations if (manifestVersion < ManifestVersionV1) { // multi file manifest is only supported starting ManifestVersion 1.0.0 if (isMultifileManifest) { THROW_EXCEPTION_MSG(ManifestException(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST), "Preview manifest does not support multi file manifest format."); } firstYamlManifest.ManifestType = ManifestTypeEnum::Preview; } // V1 manifest validations else { // Check required fields used by later consistency check for better error message instead of // Field Type Not Match error. for (auto const& entry : input) { ValidateV1ManifestInput(entry); } if (isMultifileManifest) { // Populates the PackageIdentifier and PackageVersion from first doc for later consistency check std::string packageId = firstYamlManifest.Root["PackageIdentifier"].as(); std::string packageVersion = firstYamlManifest.Root["PackageVersion"].as(); std::set localesSet; bool isVersionManifestFound = false; bool isInstallerManifestFound = false; bool isDefaultLocaleManifestFound = false; bool isShadowManifestFound = false; std::string defaultLocaleFromVersionManifest; std::string defaultLocaleFromDefaultLocaleManifest; for (auto& entry : input) { std::string localPackageId = entry.Root["PackageIdentifier"].as(); if (localPackageId != packageId) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::InconsistentMultiFileManifestFieldValue, "PackageIdentifier", localPackageId, entry.FileName)); } std::string localPackageVersion = entry.Root["PackageVersion"].as(); if (localPackageVersion != packageVersion) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::InconsistentMultiFileManifestFieldValue, "PackageVersion", localPackageVersion, entry.FileName)); } std::string localManifestVersion = entry.Root["ManifestVersion"].as(); if (localManifestVersion != manifestVersionStr) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::InconsistentMultiFileManifestFieldValue, "ManifestVersion", localManifestVersion, entry.FileName)); } std::string manifestTypeStr = entry.Root["ManifestType"sv].as(); ManifestTypeEnum manifestType = ConvertToManifestTypeEnum(manifestTypeStr); entry.ManifestType = manifestType; switch (manifestType) { case ManifestTypeEnum::Version: if (isVersionManifestFound) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::DuplicateMultiFileManifestType, "ManifestType", manifestTypeStr, entry.FileName)); } else { isVersionManifestFound = true; defaultLocaleFromVersionManifest = entry.Root["DefaultLocale"sv].as(); } break; case ManifestTypeEnum::Installer: if (isInstallerManifestFound) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::DuplicateMultiFileManifestType, "ManifestType", manifestTypeStr, entry.FileName)); } else { isInstallerManifestFound = true; } break; case ManifestTypeEnum::DefaultLocale: if (isDefaultLocaleManifestFound) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::DuplicateMultiFileManifestType, "ManifestType", manifestTypeStr, entry.FileName)); } else { isDefaultLocaleManifestFound = true; auto packageLocale = entry.Root["PackageLocale"sv].as(); defaultLocaleFromDefaultLocaleManifest = packageLocale; if (localesSet.find(packageLocale) != localesSet.end()) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::DuplicateMultiFileManifestLocale, "PackageLocale", packageLocale, entry.FileName)); } else { localesSet.insert(packageLocale); } } break; case ManifestTypeEnum::Locale: { auto packageLocale = entry.Root["PackageLocale"sv].as(); if (localesSet.find(packageLocale) != localesSet.end()) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::DuplicateMultiFileManifestLocale, "PackageLocale", packageLocale, entry.FileName)); } else { localesSet.insert(packageLocale); } break; } case ManifestTypeEnum::Shadow: { if (!validateOption.AllowShadowManifest) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::ShadowManifestNotAllowed, "ManifestType", manifestTypeStr, entry.FileName)); } else if (isShadowManifestFound) { errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::DuplicateMultiFileManifestType, "ManifestType", manifestTypeStr, entry.FileName)); } else { isShadowManifestFound = true; } break; } default: errors.emplace_back(ValidationError::MessageContextValueWithFile( ManifestError::UnsupportedMultiFileManifestType, "ManifestType", manifestTypeStr, entry.FileName)); } } if (isVersionManifestFound && isDefaultLocaleManifestFound && defaultLocaleFromDefaultLocaleManifest != defaultLocaleFromVersionManifest) { errors.emplace_back(ManifestError::InconsistentMultiFileManifestDefaultLocale); } if (!validateOption.SchemaValidationOnly && !(isVersionManifestFound && isInstallerManifestFound && isDefaultLocaleManifestFound)) { errors.emplace_back(ManifestError::IncompleteMultiFileManifest); } } else { std::string manifestTypeStr = firstYamlManifest.Root["ManifestType"sv].as(); ManifestTypeEnum manifestType = ConvertToManifestTypeEnum(manifestTypeStr); firstYamlManifest.ManifestType = manifestType; if (validateOption.FullValidation && manifestType == ManifestTypeEnum::Merged) { errors.emplace_back(ValidationError::MessageContextValueWithFile(ManifestError::FieldValueNotSupported, "ManifestType", manifestTypeStr, firstYamlManifest.FileName)); } if (!validateOption.SchemaValidationOnly && manifestType != ManifestTypeEnum::Merged && manifestType != ManifestTypeEnum::Singleton) { errors.emplace_back(ValidationError::MessageWithFile(ManifestError::IncompleteMultiFileManifest, firstYamlManifest.FileName)); } } } if (!errors.empty()) { ManifestException ex{ std::move(errors) }; THROW_EXCEPTION(ex); } return manifestVersion; } // Find a unique required manifest from the input in multi manifest case const YAML::Node& FindUniqueRequiredDocFromMultiFileManifest(const std::vector& input, ManifestTypeEnum manifestType) { auto iter = std::find_if(input.begin(), input.end(), [=](auto const& s) { return s.ManifestType == manifestType; }); THROW_HR_IF(E_UNEXPECTED, iter == input.end()); return iter->Root; } std::optional FindUniqueOptionalDocFromMultiFileManifest(std::vector& input, ManifestTypeEnum manifestType) { auto iter = std::find_if(input.begin(), input.end(), [=](auto const& s) { return s.ManifestType == manifestType; }); if (iter != input.end()) { return iter->Root; } return {}; } // Merge one manifest file to the final merged manifest, basically copying the mapping but excluding certain common fields void MergeOneManifestToMultiFileManifest(const YAML::Node& input, YAML::Node& destination) { THROW_HR_IF(E_UNEXPECTED, !input.IsMap()); THROW_HR_IF(E_UNEXPECTED, !destination.IsMap()); const std::vector FieldsToIgnore = { "PackageIdentifier", "PackageVersion", "ManifestType", "ManifestVersion" }; for (auto const& keyValuePair : input.Mapping()) { // We only support string type as key in our manifest if (std::find(FieldsToIgnore.begin(), FieldsToIgnore.end(), keyValuePair.first.as()) == FieldsToIgnore.end()) { YAML::Node key = keyValuePair.first; YAML::Node value = keyValuePair.second; destination.AddMappingNode(std::move(key), std::move(value)); } } } YAML::Node MergeMultiFileManifest(const std::vector& input) { // Starts with installer manifest YAML::Node result = FindUniqueRequiredDocFromMultiFileManifest(input, ManifestTypeEnum::Installer); // Copy default locale manifest content into manifest root YAML::Node defaultLocaleManifest = FindUniqueRequiredDocFromMultiFileManifest(input, ManifestTypeEnum::DefaultLocale); MergeOneManifestToMultiFileManifest(defaultLocaleManifest, result); // Copy additional locale manifests YAML::Node localizations{ YAML::Node::Type::Sequence, "", YAML::Mark() }; for (const auto& entry : input) { if (entry.ManifestType == ManifestTypeEnum::Locale) { YAML::Node localization{ YAML::Node::Type::Mapping, "", YAML::Mark() }; MergeOneManifestToMultiFileManifest(entry.Root, localization); localizations.AddSequenceNode(std::move(localization)); } } if (localizations.size() > 0) { YAML::Node key{ YAML::Node::Type::Scalar, "", YAML::Mark() }; key.SetScalar("Localization"); result.AddMappingNode(std::move(key), std::move(localizations)); } result["ManifestType"sv].SetScalar("merged"); return result; } void EmitYamlNode(const YAML::Node& input, YAML::Emitter& emitter) { if (input.IsMap()) { emitter << YAML::BeginMap; for (auto const& keyValuePair : input.Mapping()) { emitter << YAML::Key; EmitYamlNode(keyValuePair.first, emitter); emitter << YAML::Value; EmitYamlNode(keyValuePair.second, emitter); } emitter << YAML::EndMap; } else if (input.IsSequence()) { emitter << YAML::BeginSeq; for (auto const& value : input.Sequence()) { EmitYamlNode(value, emitter); } emitter << YAML::EndSeq; } else if (input.IsScalar()) { emitter << input.as(); } else if (input.IsNull()) { emitter << ""; } else { THROW_HR(E_UNEXPECTED); } } void OutputYamlDoc(const YAML::Node& input, const std::filesystem::path& out) { THROW_HR_IF(E_UNEXPECTED, !input.IsMap()); YAML::Emitter emitter; EmitYamlNode(input, emitter); std::filesystem::create_directories(out.parent_path()); std::ofstream outFileStream(out); emitter.Emit(outFileStream); outFileStream.close(); } std::vector ParseManifestImpl( std::vector& input, Manifest& manifest, const std::filesystem::path& mergedManifestPath, ManifestValidateOption validateOption) { THROW_HR_IF_MSG(E_INVALIDARG, input.size() == 0, "No manifest file found"); THROW_HR_IF_MSG(E_INVALIDARG, validateOption.SchemaValidationOnly && !mergedManifestPath.empty(), "Manifest cannot be merged if only schema validation is performed"); THROW_HR_IF_MSG(E_INVALIDARG, input.size() == 1 && !mergedManifestPath.empty(), "Manifest cannot be merged from a single manifest"); auto manifestVersion = ValidateInput(input, validateOption); std::vector resultErrors; if (validateOption.FullValidation || validateOption.SchemaValidationOnly) { resultErrors = ValidateAgainstSchema(input, manifestVersion); } if (validateOption.SchemaValidationOnly) { return resultErrors; } // Merge manifests in multi file manifest case bool isMultiFile = input.size() > 1; YAML::Node& manifestDoc = input[0].Root; if (isMultiFile) { manifestDoc = MergeMultiFileManifest(input); } auto shadowNode = isMultiFile ? FindUniqueOptionalDocFromMultiFileManifest(input, ManifestTypeEnum::Shadow) : std::optional{}; auto errors = ManifestYamlPopulator::PopulateManifest(manifestDoc, manifest, manifestVersion, validateOption, shadowNode); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); // Extra semantic validations after basic validation and field population if (validateOption.FullValidation) { errors = ValidateManifest(manifest); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); // Validate the schema header for manifest version 1.7 and above if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_7 }) { // Validate the schema header. errors = ValidateYamlManifestsSchemaHeader(input, manifestVersion, validateOption.SchemaHeaderValidationAsWarning); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } } if (validateOption.InstallerValidation) { errors = ValidateManifestInstallers(manifest); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } // Output merged manifest if requested if (!mergedManifestPath.empty()) { OutputYamlDoc(manifestDoc, mergedManifestPath); } // If there is only one input file, use its hash for the stream if (input.size() == 1) { manifest.StreamSha256 = std::move(input[0].StreamSha256); } return resultErrors; } } Manifest CreateFromPath( const std::filesystem::path& inputPath, ManifestValidateOption validateOption, const std::filesystem::path& mergedManifestPath) { std::vector docList; try { if (std::filesystem::is_directory(inputPath)) { for (const auto& file : std::filesystem::directory_iterator(inputPath)) { THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_DIRECTORY_NOT_SUPPORTED), std::filesystem::is_directory(file.path()), "Subdirectory not supported in manifest path"); YamlManifestInfo manifestInfo; YAML::Document doc = YAML::LoadDocument(file.path()); manifestInfo.DocumentSchemaHeader = doc.GetSchemaHeader(); manifestInfo.Root = std::move(doc).GetRoot(); manifestInfo.FileName = file.path().filename().u8string(); docList.emplace_back(std::move(manifestInfo)); } } else { YamlManifestInfo manifestInfo; YAML::Document doc = YAML::LoadDocument(inputPath, manifestInfo.StreamSha256); manifestInfo.DocumentSchemaHeader = doc.GetSchemaHeader(); manifestInfo.Root = std::move(doc).GetRoot(); manifestInfo.FileName = inputPath.filename().u8string(); docList.emplace_back(std::move(manifestInfo)); } } catch (const std::exception& e) { THROW_EXCEPTION_MSG(ManifestException(), "%hs", e.what()); } return ParseManifest(docList, validateOption, mergedManifestPath); } Manifest Create( const std::string& input, ManifestValidateOption validateOption, const std::filesystem::path& mergedManifestPath) { std::vector docList; try { YamlManifestInfo manifestInfo; YAML::Document doc = YAML::LoadDocument(input); manifestInfo.DocumentSchemaHeader = doc.GetSchemaHeader(); manifestInfo.Root = std::move(doc).GetRoot(); docList.emplace_back(std::move(manifestInfo)); } catch (const std::exception& e) { THROW_EXCEPTION_MSG(ManifestException(), "%hs", e.what()); } return ParseManifest(docList, validateOption, mergedManifestPath); } Manifest ParseManifest( std::vector& input, ManifestValidateOption validateOption, const std::filesystem::path& mergedManifestPath) { Manifest manifest; std::vector errors; try { errors = ParseManifestImpl(input, manifest, mergedManifestPath, validateOption); } catch (const ManifestException&) { // Prevent ManifestException from being wrapped in another ManifestException throw; } catch (const std::exception& e) { THROW_EXCEPTION_MSG(ManifestException(), "%hs", e.what()); } if (!errors.empty()) { ManifestException ex{ std::move(errors) }; if (validateOption.ThrowOnWarning || !ex.IsWarningOnly()) { THROW_EXCEPTION(ex); } } return manifest; } } ================================================ FILE: src/AppInstallerCommonCore/Manifest/YamlWriter.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerStrings.h" #include "AppInstallerSHA256.h" #include "winget/Yaml.h" #include "winget/ManifestYamlWriter.h" namespace AppInstaller::Manifest::YamlWriter { using namespace Utility::literals; namespace { constexpr std::string_view PackageIdentifier = "PackageIdentifier"sv; constexpr std::string_view PackageFamilyName = "PackageFamilyName"sv; constexpr std::string_view ProductCode = "ProductCode"sv; constexpr std::string_view Versions = "Versions"sv; constexpr std::string_view PackageVersion = "PackageVersion"sv; constexpr std::string_view Channel = "Channel"sv; constexpr std::string_view ManifestVersion = "ManifestVersion"sv; constexpr std::string_view ManifestType = "ManifestType"sv; // Installer constexpr std::string_view Installers = "Installers"sv; constexpr std::string_view InstallerIdentifier = "InstallerIdentifier"sv; constexpr std::string_view InstallerSha256 = "InstallerSha256"sv; constexpr std::string_view InstallerUrl = "InstallerUrl"sv; constexpr std::string_view Architecture = "Architecture"sv; constexpr std::string_view InstallerLocale = "InstallerLocale"sv; constexpr std::string_view Platform = "Platform"sv; constexpr std::string_view InstallerType = "InstallerType"sv; constexpr std::string_view Scope = "Scope"sv; constexpr std::string_view SignatureSha256 = "SignatureSha256"sv; constexpr std::string_view InstallModes = "InstallModes"sv; constexpr std::string_view MSStoreProductIdentifier = "MSStoreProductIdentifier"sv; constexpr std::string_view ReleaseDate = "ReleaseDate"sv; constexpr std::string_view InstallerAbortsTerminal = "InstallerAbortsTerminal"sv; constexpr std::string_view InstallLocationRequired = "InstallLocationRequired"sv; constexpr std::string_view RequireExplicitUpgrade = "RequireExplicitUpgrade"sv; constexpr std::string_view UnsupportedOSArchitectures = "UnsupportedOSArchitectures"sv; constexpr std::string_view AppsAndFeaturesEntries = "AppsAndFeaturesEntries"sv; constexpr std::string_view DisplayVersion = "DisplayVersion"sv; constexpr std::string_view UpgradeCode = "UpgradeCode"sv; constexpr std::string_view Markets = "Markets"sv; constexpr std::string_view AllowedMarkets = "AllowedMarkets"sv; constexpr std::string_view ExcludedMarkets = "ExcludedMarkets"sv; constexpr std::string_view ElevationRequirement = "ElevationRequirement"sv; constexpr std::string_view ExpectedReturnCodes = "ExpectedReturnCodes"sv; constexpr std::string_view InstallerReturnCode = "InstallerReturnCode"sv; constexpr std::string_view ReturnResponse = "ReturnResponse"sv; constexpr std::string_view ReturnResponseUrl = "ReturnResponseUrl"sv; constexpr std::string_view NestedInstallerType = "NestedInstallerType"sv; constexpr std::string_view DisplayInstallWarnings = "DisplayInstallWarnings"sv; constexpr std::string_view UnsupportedArguments = "UnsupportedArguments"sv; constexpr std::string_view NestedInstallerFiles = "NestedInstallerFiles"sv; constexpr std::string_view NestedInstallerFileRelativeFilePath = "RelativeFilePath"sv; constexpr std::string_view PortableCommandAlias = "PortableCommandAlias"sv; constexpr std::string_view InstallationMetadata = "InstallationMetadata"sv; constexpr std::string_view DefaultInstallLocation = "DefaultInstallLocation"sv; constexpr std::string_view InstallationMetadataFiles = "Files"sv; constexpr std::string_view InstallationMetadataRelativeFilePath = "RelativeFilePath"sv; constexpr std::string_view FileSha256 = "FileSha256"sv; constexpr std::string_view FileType = "FileType"sv; constexpr std::string_view InvocationParameter = "InvocationParameter"sv; constexpr std::string_view DisplayName = "DisplayName"sv; constexpr std::string_view MinimumOSVersion = "MinimumOSVersion"sv; constexpr std::string_view DownloadCommandProhibited = "DownloadCommandProhibited"sv; constexpr std::string_view RepairBehavior = "RepairBehavior"sv; constexpr std::string_view ArchiveBinariesDependOnPath = "ArchiveBinariesDependOnPath"sv; constexpr std::string_view Authentication = "Authentication"sv; constexpr std::string_view AuthenticationType = "AuthenticationType"sv; constexpr std::string_view MicrosoftEntraIdAuthenticationInfo = "MicrosoftEntraIdAuthenticationInfo"sv; constexpr std::string_view MicrosoftEntraIdResource = "Resource"sv; constexpr std::string_view MicrosoftEntraIdScope = "Scope"sv; constexpr std::string_view DesiredStateConfiguration = "DesiredStateConfiguration"sv; constexpr std::string_view DesiredStateConfigurationResources = "Resources"sv; constexpr std::string_view DesiredStateConfigurationPowerShell = "PowerShell"sv; constexpr std::string_view DesiredStateConfigurationPowerShellRepositoryURL = "RepositoryUrl"sv; constexpr std::string_view DesiredStateConfigurationPowerShellModuleName = "ModuleName"sv; constexpr std::string_view DesiredStateConfigurationPowerShellResourceName = "Name"sv; constexpr std::string_view DesiredStateConfigurationDSCv3 = "DSCv3"sv; constexpr std::string_view DesiredStateConfigurationDSCv3ResourceType = "Type"sv; // Installer switches constexpr std::string_view InstallerSwitches = "InstallerSwitches"sv; constexpr std::string_view Silent = "Silent"sv; constexpr std::string_view SilentWithProgress = "SilentWithProgress"sv; constexpr std::string_view Interactive = "Interactive"sv; constexpr std::string_view InstallLocation = "InstallLocation"sv; constexpr std::string_view Log = "Log"sv; constexpr std::string_view Upgrade = "Upgrade"sv; constexpr std::string_view Custom = "Custom"sv; constexpr std::string_view Repair = "Repair"sv; constexpr std::string_view InstallerSuccessCodes = "InstallerSuccessCodes"sv; constexpr std::string_view UpgradeBehavior = "UpgradeBehavior"sv; constexpr std::string_view Commands = "Commands"sv; constexpr std::string_view Protocols = "Protocols"sv; constexpr std::string_view FileExtensions = "FileExtensions"sv; // Dependencies constexpr std::string_view Dependencies = "Dependencies"sv; constexpr std::string_view WindowsFeatures = "WindowsFeatures"sv; constexpr std::string_view WindowsLibraries = "WindowsLibraries"sv; constexpr std::string_view PackageDependencies = "PackageDependencies"sv; constexpr std::string_view MinimumVersion = "MinimumVersion"sv; constexpr std::string_view ExternalDependencies = "ExternalDependencies"sv; constexpr std::string_view Capabilities = "Capabilities"sv; constexpr std::string_view RestrictedCapabilities = "RestrictedCapabilities"sv; // Locale constexpr std::string_view Localization = "Localization"sv; constexpr std::string_view InstallationNotes = "InstallationNotes"sv; constexpr std::string_view PurchaseUrl = "PurchaseUrl"sv; constexpr std::string_view Documentations = "Documentations"sv; constexpr std::string_view DocumentLabel = "DocumentLabel"sv; constexpr std::string_view DocumentUrl = "DocumentUrl"sv; constexpr std::string_view Icons = "Icons"sv; constexpr std::string_view IconUrl = "IconUrl"sv; constexpr std::string_view IconFileType = "IconFileType"sv; constexpr std::string_view IconResolution = "IconResolution"sv; constexpr std::string_view IconTheme = "IconTheme"sv; constexpr std::string_view IconSha256 = "IconSha256"sv; constexpr std::string_view ReleaseNotes = "ReleaseNotes"sv; constexpr std::string_view ReleaseNotesUrl = "ReleaseNotesUrl"sv; constexpr std::string_view Agreements = "Agreements"sv; constexpr std::string_view AgreementLabel = "AgreementLabel"sv; constexpr std::string_view Agreement = "Agreement"sv; constexpr std::string_view AgreementUrl = "AgreementUrl"sv; constexpr std::string_view DefaultLocale = "DefaultLocale"sv; constexpr std::string_view Locales = "Locales"sv; constexpr std::string_view PackageLocale = "PackageLocale"sv; constexpr std::string_view Publisher = "Publisher"sv; constexpr std::string_view PublisherUrl = "PublisherUrl"sv; constexpr std::string_view PublisherSupportUrl = "PublisherSupportUrl"sv; constexpr std::string_view PrivacyUrl = "PrivacyUrl"sv; constexpr std::string_view Author = "Author"sv; constexpr std::string_view PackageName = "PackageName"sv; constexpr std::string_view PackageUrl = "PackageUrl"sv; constexpr std::string_view License = "License"sv; constexpr std::string_view LicenseUrl = "LicenseUrl"sv; constexpr std::string_view Copyright = "Copyright"sv; constexpr std::string_view CopyrightUrl = "CopyrightUrl"sv; constexpr std::string_view ShortDescription = "ShortDescription"sv; constexpr std::string_view Description = "Description"sv; constexpr std::string_view Tags = "Tags"sv; constexpr std::string_view Moniker = "Moniker"sv; #define WRITE_PROPERTY(emitter, key, value) \ { \ emitter << YAML::Key << key << YAML::Value << value; \ } #define WRITE_BOOL_PROPERTY(emitter, key, value) \ { \ emitter << YAML::Key << key << YAML::Value << Utility::ConvertBoolToString(value); \ } #define WRITE_PROPERTY_IF_EXISTS(emitter, key, value) \ { \ if (!value.empty()) \ { \ WRITE_PROPERTY(emitter, key, value) \ } \ } #define WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(emitter, key, field) \ { \ if (!field.empty()) \ { \ WRITE_PROPERTY(emitter, key, Utility::SHA256::ConvertToString(field)) \ } \ } #define WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(emitter, key, value, enumField, enumType) \ { \ if (enumField != enumType::Unknown) \ { \ WRITE_PROPERTY(emitter, key, value) \ } \ } void ProcessAgreements(YAML::Emitter& out, const std::vector& agreements) { if (agreements.empty()) { return; } out << YAML::Key << Agreements; out << YAML::BeginSeq; for (const auto& agreement : agreements) { out << YAML::BeginMap; WRITE_PROPERTY_IF_EXISTS(out, AgreementLabel, agreement.Label); WRITE_PROPERTY_IF_EXISTS(out, AgreementUrl, agreement.AgreementUrl); WRITE_PROPERTY_IF_EXISTS(out, Agreement, agreement.AgreementText); out << YAML::EndMap; } out << YAML::EndSeq; } void ProcessDocumentations(YAML::Emitter& out, const std::vector& documentations) { if (documentations.empty()) { return; } out << YAML::Key << Documentations; out << YAML::BeginSeq; for (const auto& document : documentations) { out << YAML::BeginMap; WRITE_PROPERTY_IF_EXISTS(out, DocumentLabel, document.DocumentLabel); WRITE_PROPERTY_IF_EXISTS(out, DocumentUrl, document.DocumentUrl); out << YAML::EndMap; } out << YAML::EndSeq; } void ProcessIcons(YAML::Emitter& out, std::vector icons) { if (icons.empty()) { return; } out << YAML::Key << Icons; out << YAML::BeginSeq; for (const auto& icon : icons) { out << YAML::BeginMap; WRITE_PROPERTY(out, IconUrl, icon.Url); WRITE_PROPERTY(out, IconFileType, IconFileTypeToString(icon.FileType)); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, IconResolution, IconResolutionToString(icon.Resolution), icon.Resolution, IconResolutionEnum); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, IconTheme, IconThemeToString(icon.Theme), icon.Theme, IconThemeEnum); WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(out, IconSha256, icon.Sha256); out << YAML::EndMap; } out << YAML::EndSeq; } // Generic method for handling a list of strings (i.e. tags) void ProcessStringSequence(YAML::Emitter& out, std::string_view name, std::vector items) { if (items.empty()) { return; } out << YAML::Key << name; out << YAML::BeginSeq; for (const auto& item : items) { if (!item.empty()) { out << item; } } out << YAML::EndSeq; } void ProcessLocaleFields(YAML::Emitter& out, const ManifestLocalization& manifest) { ProcessAgreements(out, manifest.Get()); ProcessDocumentations(out, manifest.Get()); ProcessIcons(out, manifest.Get()); ProcessStringSequence(out, Tags, manifest.Get()); WRITE_PROPERTY(out, PackageLocale, manifest.Locale); WRITE_PROPERTY_IF_EXISTS(out, Author, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, Copyright, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, CopyrightUrl, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, Description, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, ShortDescription, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, License, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, LicenseUrl, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, PackageName, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, PackageUrl, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, PrivacyUrl, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, Publisher, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, PublisherSupportUrl, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, PublisherUrl, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, PurchaseUrl, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, ReleaseNotes, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, ReleaseNotesUrl, manifest.Get()); WRITE_PROPERTY_IF_EXISTS(out, InstallationNotes, manifest.Get()); } void ProcessAppsAndFeaturesEntries(YAML::Emitter& out, const std::vector& appsAndFeaturesEntries) { if (appsAndFeaturesEntries.empty()) { return; } out << YAML::Key << AppsAndFeaturesEntries; out << YAML::BeginSeq; for (const auto& appsAndFeatureEntry : appsAndFeaturesEntries) { out << YAML::BeginMap; WRITE_PROPERTY_IF_EXISTS(out, DisplayName, appsAndFeatureEntry.DisplayName); WRITE_PROPERTY_IF_EXISTS(out, DisplayVersion, appsAndFeatureEntry.DisplayVersion); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, InstallerType, InstallerTypeToString(appsAndFeatureEntry.InstallerType), appsAndFeatureEntry.InstallerType, InstallerTypeEnum); WRITE_PROPERTY_IF_EXISTS(out, ProductCode, appsAndFeatureEntry.ProductCode); WRITE_PROPERTY_IF_EXISTS(out, Publisher, appsAndFeatureEntry.Publisher); WRITE_PROPERTY_IF_EXISTS(out, UpgradeCode, appsAndFeatureEntry.UpgradeCode); out << YAML::EndMap; } out << YAML::EndSeq; } void ProcessInstallerSwitches(YAML::Emitter& out, const std::map& installerSwitches) { if (installerSwitches.empty()) { return; } out << YAML::Key << InstallerSwitches; out << YAML::BeginMap; for (auto const& [type, value] : installerSwitches) { WRITE_PROPERTY_IF_EXISTS(out, InstallerSwitchTypeToString(type), value); } out << YAML::EndMap; } void ProcessExpectedReturnCodes(YAML::Emitter& out, const std::map& expectedReturnCodes) { if (expectedReturnCodes.empty()) { return; } out << YAML::Key << ExpectedReturnCodes; out << YAML::BeginSeq; for (const auto& expectedReturnCode : expectedReturnCodes) { out << YAML::BeginMap; WRITE_PROPERTY(out, InstallerReturnCode, std::to_string(expectedReturnCode.first)); WRITE_PROPERTY(out, ReturnResponse, ExpectedReturnCodeToString(expectedReturnCode.second.ReturnResponseEnum)); WRITE_PROPERTY_IF_EXISTS(out, ReturnResponseUrl, expectedReturnCode.second.ReturnResponseUrl); out << YAML::EndMap; } out << YAML::EndSeq; } void ProcessUnsupportedArguments(YAML::Emitter& out, const std::vector& unsupportedArguments) { if (unsupportedArguments.empty()) { return; } out << YAML::Key << UnsupportedArguments; out << YAML::BeginSeq; for (auto const& unsupportedArg : unsupportedArguments) { if (unsupportedArg != UnsupportedArgumentEnum::Unknown) { out << UnsupportedArgumentToString(unsupportedArg); } } out << YAML::EndSeq; } void ProcessUnsupportedOSArchitecture(YAML::Emitter& out, const std::vector& architectures) { if (architectures.empty()) { return; } out << YAML::Key << UnsupportedOSArchitectures; out << YAML::BeginSeq; for (auto const& architecture : architectures) { if (architecture != AppInstaller::Utility::Architecture::Unknown) { out << Utility::ToLower(ToString(architecture)); } } out << YAML::EndSeq; } void ProcessInstallModes(YAML::Emitter& out, const std::vector& installModes) { if (installModes.empty()) { return; } out << YAML::Key << InstallModes; out << YAML::BeginSeq; for (auto const& installMode : installModes) { if (installMode != InstallModeEnum::Unknown) { out << InstallModeToString(installMode); } } out << YAML::EndSeq; } void ProcessPlatforms(YAML::Emitter& out, const std::vector& platforms) { if (platforms.empty()) { return; } out << YAML::Key << Platform; out << YAML::BeginSeq; for (auto const& platform : platforms) { if (platform != PlatformEnum::Unknown) { out << PlatformToString(platform); } } out << YAML::EndSeq; } void ProcessInstallerSuccessCodes(YAML::Emitter& out, const std::vector& installerSuccessCodes) { if (installerSuccessCodes.empty()) { return; } out << YAML::Key << InstallerSuccessCodes; out << YAML::BeginSeq; for (auto const& installerSuccessCode : installerSuccessCodes) { out << std::to_string(installerSuccessCode); } out << YAML::EndSeq; } void ProcessMarkets(YAML::Emitter& out, const MarketsInfo& marketsInfo) { if (marketsInfo.AllowedMarkets.empty() && marketsInfo.ExcludedMarkets.empty()) { return; } out << YAML::Key << Markets; out << YAML::BeginMap; ProcessStringSequence(out, AllowedMarkets, marketsInfo.AllowedMarkets); ProcessStringSequence(out, ExcludedMarkets, marketsInfo.ExcludedMarkets); out << YAML::EndMap; } void ProcessNestedInstallerFiles(YAML::Emitter& out, const std::vector& nestedInstallerFiles) { if (nestedInstallerFiles.empty()) { return; } out << YAML::Key << NestedInstallerFiles; out << YAML::BeginSeq; for (const auto& nestedInstallerFile : nestedInstallerFiles) { out << YAML::BeginMap; WRITE_PROPERTY(out, NestedInstallerFileRelativeFilePath, nestedInstallerFile.RelativeFilePath); WRITE_PROPERTY_IF_EXISTS(out, PortableCommandAlias, nestedInstallerFile.PortableCommandAlias); out << YAML::EndMap; } out << YAML::EndSeq; } void ProcessInstallationMetadataInstalledFiles(YAML::Emitter& out, const std::vector& installedFiles) { if (installedFiles.empty()) { return; } out << YAML::Key << InstallationMetadataFiles; out << YAML::BeginSeq; for (const auto& installedFile : installedFiles) { out << YAML::BeginMap; WRITE_PROPERTY(out, InstallationMetadataRelativeFilePath, installedFile.RelativeFilePath); WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(out, FileSha256, installedFile.FileSha256); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, FileType, InstalledFileTypeToString(installedFile.FileType), installedFile.FileType, InstalledFileTypeEnum); WRITE_PROPERTY_IF_EXISTS(out, InvocationParameter, installedFile.InvocationParameter); WRITE_PROPERTY_IF_EXISTS(out, DisplayName, installedFile.DisplayName); out << YAML::EndMap; } out << YAML::EndSeq; } void ProcessInstallationMetadata(YAML::Emitter& out, const InstallationMetadataInfo& installationMetadata) { if (installationMetadata.DefaultInstallLocation.empty() && installationMetadata.Files.empty()) { return; } out << YAML::Key << InstallationMetadata; out << YAML::BeginMap; WRITE_PROPERTY_IF_EXISTS(out, DefaultInstallLocation, installationMetadata.DefaultInstallLocation); ProcessInstallationMetadataInstalledFiles(out, installationMetadata.Files); out << YAML::EndMap; } void ProcessAuthentication(YAML::Emitter& out, const Authentication::AuthenticationInfo& authInfo) { if (authInfo.Type == Authentication::AuthenticationType::None) { return; } out << YAML::Key << Authentication; out << YAML::BeginMap; WRITE_PROPERTY(out, AuthenticationType, Authentication::AuthenticationTypeToString(authInfo.Type)); if (authInfo.MicrosoftEntraIdInfo) { out << YAML::Key << MicrosoftEntraIdAuthenticationInfo; out << YAML::BeginMap; WRITE_PROPERTY_IF_EXISTS(out, MicrosoftEntraIdResource, authInfo.MicrosoftEntraIdInfo->Resource); WRITE_PROPERTY_IF_EXISTS(out, MicrosoftEntraIdScope, authInfo.MicrosoftEntraIdInfo->Scope); out << YAML::EndMap; } out << YAML::EndMap; } void ProcessDependencies(YAML::Emitter& out, const DependencyList& dependencies) { if (!dependencies.HasAny()) { return; } out << YAML::Key << Dependencies; out << YAML::BeginMap; if (dependencies.HasAnyOf(DependencyType::WindowsFeature)) { out << YAML::Key << WindowsFeatures; out << YAML::BeginSeq; dependencies.ApplyToType(DependencyType::WindowsFeature, [&out](Dependency dependency) { out << dependency.Id(); }); out << YAML::EndSeq; } if (dependencies.HasAnyOf(DependencyType::WindowsLibrary)) { out << YAML::Key << WindowsLibraries; out << YAML::BeginSeq; dependencies.ApplyToType(DependencyType::WindowsLibrary, [&out](Dependency dependency) { out << dependency.Id(); }); out << YAML::EndSeq; } if (dependencies.HasAnyOf(DependencyType::Package)) { out << YAML::Key << PackageDependencies; out << YAML::BeginSeq; dependencies.ApplyToType(DependencyType::Package, [&out](Dependency dependency) { out << YAML::BeginMap; WRITE_PROPERTY(out, PackageIdentifier, dependency.Id()); if (dependency.MinVersion.has_value()) { WRITE_PROPERTY_IF_EXISTS(out, MinimumVersion, dependency.MinVersion.value().ToString()); } out << YAML::EndMap; }); out << YAML::EndSeq; } if (dependencies.HasAnyOf(DependencyType::External)) { out << YAML::Key << ExternalDependencies; out << YAML::BeginSeq; dependencies.ApplyToType(DependencyType::External, [&out](Dependency dependency) { out << dependency.Id(); }); out << YAML::EndSeq; } out << YAML::EndMap; } void ProcessDesiredStateConfiguration(YAML::Emitter& out, const std::vector& containers) { if (containers.empty()) { return; } out << YAML::Key << DesiredStateConfiguration; out << YAML::BeginMap; // Process PowerShell containers bool firstPowerShellContainer = true; for (const auto& container : containers) { if (container.Type == DesiredStateConfigurationContainerType::PowerShell) { if (firstPowerShellContainer) { out << YAML::Key << DesiredStateConfigurationPowerShell; out << YAML::BeginSeq; firstPowerShellContainer = false; } out << YAML::BeginMap; WRITE_PROPERTY(out, DesiredStateConfigurationPowerShellRepositoryURL, container.RepositoryURL); WRITE_PROPERTY(out, DesiredStateConfigurationPowerShellModuleName, container.ModuleName); out << YAML::Key << DesiredStateConfigurationResources; out << YAML::BeginSeq; for (const auto& resource : container.Resources) { out << YAML::BeginMap; WRITE_PROPERTY(out, DesiredStateConfigurationPowerShellResourceName, resource.Name); out << YAML::EndMap; } out << YAML::EndSeq; out << YAML::EndMap; } } if (!firstPowerShellContainer) { out << YAML::EndSeq; } // Process DSCv3 container bool firstDSCv3Container = true; for (const auto& container : containers) { if (container.Type == DesiredStateConfigurationContainerType::DSCv3) { if (firstDSCv3Container) { out << YAML::Key << DesiredStateConfigurationDSCv3; out << YAML::BeginMap; firstDSCv3Container = false; } else { THROW_HR_MSG(E_INVALIDARG, "Cannot have multiple DSCv3 containers."); } out << YAML::Key << DesiredStateConfigurationResources; out << YAML::BeginSeq; for (const auto& resource : container.Resources) { out << YAML::BeginMap; WRITE_PROPERTY(out, DesiredStateConfigurationDSCv3ResourceType, resource.Name); out << YAML::EndMap; } out << YAML::EndSeq; out << YAML::EndMap; } } out << YAML::EndMap; } void ProcessInstallerFields(YAML::Emitter& out, const ManifestInstaller& installer) { WRITE_PROPERTY(out, Architecture, Utility::ToLower(ToString(installer.Arch))); WRITE_PROPERTY(out, InstallerType, InstallerTypeToString(installer.BaseInstallerType)); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, NestedInstallerType, InstallerTypeToString(installer.NestedInstallerType), installer.NestedInstallerType, InstallerTypeEnum); WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(out, InstallerSha256, installer.Sha256); WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(out, SignatureSha256, installer.SignatureSha256); WRITE_PROPERTY_IF_EXISTS(out, InstallerUrl, installer.Url); WRITE_PROPERTY_IF_EXISTS(out, MSStoreProductIdentifier, installer.ProductId); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, Scope, Utility::ToLower(ScopeToString(installer.Scope)), installer.Scope, ScopeEnum); WRITE_PROPERTY_IF_EXISTS(out, InstallerLocale, installer.Locale); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, ElevationRequirement, ElevationRequirementToString(installer.ElevationRequirement), installer.ElevationRequirement, ElevationRequirementEnum); WRITE_PROPERTY_IF_EXISTS(out, PackageFamilyName, installer.PackageFamilyName); WRITE_PROPERTY_IF_EXISTS(out, ReleaseDate, installer.ReleaseDate); WRITE_BOOL_PROPERTY(out, InstallerAbortsTerminal, installer.InstallerAbortsTerminal); WRITE_BOOL_PROPERTY(out, InstallLocationRequired, installer.InstallLocationRequired); WRITE_BOOL_PROPERTY(out, RequireExplicitUpgrade, installer.RequireExplicitUpgrade); WRITE_BOOL_PROPERTY(out, DisplayInstallWarnings, installer.DisplayInstallWarnings); WRITE_BOOL_PROPERTY(out, DownloadCommandProhibited, installer.DownloadCommandProhibited); WRITE_BOOL_PROPERTY(out, ArchiveBinariesDependOnPath, installer.ArchiveBinariesDependOnPath); WRITE_PROPERTY_IF_EXISTS(out, MinimumOSVersion, installer.MinOSVersion); WRITE_PROPERTY_IF_EXISTS(out, ProductCode, installer.ProductCode); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, UpgradeBehavior, UpdateBehaviorToString(installer.UpdateBehavior), installer.UpdateBehavior, UpdateBehaviorEnum); WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, RepairBehavior, RepairBehaviorToString(installer.RepairBehavior), installer.RepairBehavior, RepairBehaviorEnum); ProcessStringSequence(out, Capabilities, installer.Capabilities); ProcessStringSequence(out, Commands, installer.Commands); ProcessStringSequence(out, FileExtensions, installer.FileExtensions); ProcessStringSequence(out, Protocols, installer.Protocols); ProcessStringSequence(out, RestrictedCapabilities, installer.RestrictedCapabilities); ProcessAppsAndFeaturesEntries(out, installer.AppsAndFeaturesEntries); ProcessDependencies(out, installer.Dependencies); ProcessExpectedReturnCodes(out, installer.ExpectedReturnCodes); ProcessInstallerSwitches(out, installer.Switches); ProcessInstallationMetadata(out, installer.InstallationMetadata); ProcessInstallerSuccessCodes(out, installer.InstallerSuccessCodes); ProcessInstallModes(out, installer.InstallModes); ProcessMarkets(out, installer.Markets); ProcessNestedInstallerFiles(out, installer.NestedInstallerFiles); ProcessPlatforms(out, installer.Platform); ProcessUnsupportedArguments(out, installer.UnsupportedArguments); ProcessUnsupportedOSArchitecture(out, installer.UnsupportedOSArchitectures); ProcessAuthentication(out, installer.AuthInfo); ProcessDesiredStateConfiguration(out, installer.DesiredStateConfiguration); } void ProcessInstaller(YAML::Emitter& out, const ManifestInstaller& installer) { out << YAML::Key << Installers; out << YAML::BeginSeq; out << YAML::BeginMap; ProcessInstallerFields(out, installer); out << YAML::EndMap; out << YAML::EndSeq; } void ProcessLocalizations(YAML::Emitter& out, const std::vector& localizations) { if (!localizations.empty()) { out << YAML::Key << Localization; out << YAML::BeginSeq; for (const auto& localization : localizations) { out << YAML::BeginMap; ProcessLocaleFields(out, localization); out << YAML::EndMap; } out << YAML::EndSeq; } } void PopulateManifestYamlEmitter(YAML::Emitter& out, const Manifest& manifest, const ManifestInstaller& installer) { // Currently, exporting the yaml only supports outputting a single installer. // TODO: If no single installer is provided, output all installers. out << YAML::BeginMap; WRITE_PROPERTY(out, PackageIdentifier, manifest.Id); WRITE_PROPERTY(out, PackageVersion, manifest.Version); WRITE_PROPERTY_IF_EXISTS(out, Channel, manifest.Channel); WRITE_PROPERTY_IF_EXISTS(out, Moniker, manifest.Moniker); ProcessLocaleFields(out, manifest.DefaultLocalization); ProcessLocalizations(out, manifest.Localizations); ProcessInstaller(out, installer); WRITE_PROPERTY(out, ManifestVersion, manifest.ManifestVersion.ToString()); ManifestTypeEnum manifestType = ManifestTypeEnum::Merged; WRITE_PROPERTY(out, ManifestType, ManifestTypeToString(manifestType)); out << YAML::EndMap; } } std::string YamlWriter::ManifestToYamlString(const Manifest& manifest, const ManifestInstaller& installer) { YAML::Emitter out; PopulateManifestYamlEmitter(out, manifest, installer); return out.str(); } void YamlWriter::OutputYamlFile(const Manifest& manifest, const ManifestInstaller& installer, const std::filesystem::path& out) { const std::filesystem::path& parentDirectory = out.parent_path(); if (!std::filesystem::exists(parentDirectory)) { std::filesystem::create_directories(parentDirectory); } else { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE), !std::filesystem::is_directory(parentDirectory)); } YAML::Emitter emitter; PopulateManifestYamlEmitter(emitter, manifest, installer); std::ofstream outFileStream(out); emitter.Emit(outFileStream); outFileStream.close(); } } ================================================ FILE: src/AppInstallerCommonCore/MsiExecArguments.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/MsiExecArguments.h" #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" namespace AppInstaller::Msi { using namespace std::string_view_literals; namespace { const char MsiExecQuietOption = 'q'; const char MsiExecLogOption = 'l'; // Description of how a long option is replaced by a short option. struct TokenReplacement { TokenReplacement(std::string_view longOption, std::string_view shortOption) : LongOption(longOption), ShortOption({ shortOption }) {} TokenReplacement(std::string_view longOption, std::vector&& shortOption) : LongOption(longOption), ShortOption(std::move(shortOption)) {} std::string_view LongOption; std::vector ShortOption; }; // Determines whether an argument token is a switch/option. bool IsSwitch(std::string_view token) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR, token.empty()); return token[0] == '-' || token[0] == '/'; } // Parses the log mode and log file for the Log (/l) option. // The option has a modifier specifying the log mode (what is logged) // and a value specifying the log file. // E.g. /l* log.txt, /lw warnings.txt void ParseLogOption(std::string_view logModeString, std::string_view logFile, MsiParsedArguments& parsedArgs) { if (Utility::IsEmptyOrWhitespace(logFile)) { AICLI_LOG(Core, Error, << "MSI log file path cannot be empty"); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } INSTALLLOGMODE logMode = {}; INSTALLLOGATTRIBUTES logAttributes = {}; // Note: These flags are mostly consecutive bits in the order given, except where indicated. // Skipped flags are not mapped to a command line option. std::map ValidLogModes { { 'm', INSTALLLOGMODE_FATALEXIT }, { 'e', INSTALLLOGMODE_ERROR }, { 'w', INSTALLLOGMODE_WARNING }, { 'u', INSTALLLOGMODE_USER }, { 'i', INSTALLLOGMODE_INFO }, // FILESINUSE // RESOLVESOURCE { 'o', INSTALLLOGMODE_OUTOFDISKSPACE }, { 'a', INSTALLLOGMODE_ACTIONSTART }, { 'r', INSTALLLOGMODE_ACTIONDATA }, { 'p', INSTALLLOGMODE_PROPERTYDUMP }, { 'c', INSTALLLOGMODE_COMMONDATA }, { 'v', INSTALLLOGMODE_VERBOSE }, { 'x', INSTALLLOGMODE_EXTRADEBUG }, // LOGONLYONERROR // LOGPERFORMANCE }; std::map ValidLogAttributes { { '+', INSTALLLOGATTRIBUTES_APPEND }, { '!', INSTALLLOGATTRIBUTES_FLUSHEACHLINE }, }; bool isLogModeSet = false; for (char c : logModeString) { // Log-all option if (c == '*') { logMode |= AllLogMode; isLogModeSet = true; continue; } auto modeItr = ValidLogModes.find(c); if (modeItr != ValidLogModes.end()) { logMode |= modeItr->second; isLogModeSet = true; continue; } auto attributeItr = ValidLogAttributes.find(c); if (attributeItr != ValidLogAttributes.end()) { logAttributes |= attributeItr->second; continue; } AICLI_LOG(Core, Error, << "Unknown msiexec log modifier: " << c); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } if (!isLogModeSet) { logMode = DefaultLogMode; } parsedArgs.LogMode = logMode; parsedArgs.LogAttributes = logAttributes; parsedArgs.LogFile = Utility::ConvertToUTF16(logFile); } // Parses the modifier for the UI Level option (/q) // The modifier starts with a base (b, f, n, r), followed by extra flags (+, -, !). // E.g. /qn, /qb-! void ParseQuietOption(std::string_view modifier, MsiParsedArguments& parsedArgs) { if (modifier.empty()) { // /q is treated as equivalent to /qn modifier = "n"sv; } // Lower values in INSTALLUILEVEL work like a base enum (e.g. None=2, Basic=3) // with higher values being modifying flags (e.g. HideCancel=0x20, ProgressOnly=0x40). // Some steps depend on the base enum, so we keep it separate for easier checking. INSTALLUILEVEL uiLevelBase = {}; INSTALLUILEVEL uiLevelModifiers = {}; // Parse the base level switch (std::tolower(modifier[0])) { case 'f': uiLevelBase = INSTALLUILEVEL_FULL; break; case 'r': uiLevelBase = INSTALLUILEVEL_REDUCED; break; case 'b': uiLevelBase = INSTALLUILEVEL_BASIC; break; case '+': uiLevelBase = INSTALLUILEVEL_NONE; uiLevelModifiers = INSTALLUILEVEL_ENDDIALOG; break; case 'n': uiLevelBase = INSTALLUILEVEL_NONE; break; default: AICLI_LOG(Core, Error, << "Invalid modifier for msiexec /q argument: " << modifier); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); }; // Parse the modifiers for (size_t i = 1; i < modifier.size(); ++i) { const char c = modifier[i]; if (c == '+') { WI_SetFlag(uiLevelModifiers, INSTALLUILEVEL_ENDDIALOG); } else if (c == '-') { if (uiLevelBase == INSTALLUILEVEL_BASIC) { WI_SetFlag(uiLevelModifiers, INSTALLUILEVEL_PROGRESSONLY); } else { AICLI_LOG(Core, Error, << "msiexec UI option Progress Only (-) is only valid with UI level Basic (b)"); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } } else if (c == '!') { if (uiLevelBase == INSTALLUILEVEL_BASIC) { WI_SetFlag(uiLevelModifiers, INSTALLUILEVEL_HIDECANCEL); } else { AICLI_LOG(Core, Error, << "msiexec UI option Hide Cancel (!) is only valid with UI level Basic (b)"); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } } } // Only deviation from msiexec: // When using UI Level None, allow showing the UAC prompt. WI_SetFlagIf(uiLevelModifiers, INSTALLUILEVEL_UACONLY, uiLevelBase == INSTALLUILEVEL_NONE); parsedArgs.UILevel = uiLevelBase | uiLevelModifiers; } bool IsWhiteSpace(char c) { return c == ' ' || c == '\t'; } // Gets the next token found in the arguments string, starting the search on the given position. // If there are no more tokens, return empty. // After finding the token, updates `start` to point to the next place we need to start the next token search. std::string_view GetNextToken(std::string_view arguments, size_t& start) { // Eat leading whitespace while (start < arguments.size() && IsWhiteSpace(arguments[start])) { ++start; } if (start >= arguments.size()) { // We reached the end return {}; } size_t pos = start; bool seekingSpaceSeparator = ('"' != arguments[pos]); bool withinQuotes = false; // Start looking from the next character ++pos; // Advance until we hit the end or the next separator while (pos < arguments.size()) { bool isSpace = IsWhiteSpace(arguments[pos]); bool isQuote = ('"' == arguments[pos]); if (isSpace || isQuote) { // We've encountered one of the two separators we're interested in if (seekingSpaceSeparator) { if (isQuote) { // We will ignore space characters enclosed between double quotes withinQuotes = !withinQuotes; } else { // This is a space character. If it is between quotes we ignore it; // otherwise it is a separator. if (!withinQuotes) { break; } } } else { if (isQuote) { // we've got what we needed, it is OK to stop break; } } } ++pos; } if (!seekingSpaceSeparator) { // We were looking for a terminating " character. if (pos < arguments.size()) { // We move past the " character (it is OK for the end of the line // to act as the matching " character in some cases) ++pos; } } auto result = arguments.substr(start, pos - start); start = pos; return result; } // Split the arguments string into tokens. Tokens are delimited by whitespace // unless quoted. Each token represents an option (like /q), an argument // for an option, or a property. std::list TokenizeMsiArguments(std::string_view arguments) { size_t start = 0; std::list result; auto token = GetNextToken(arguments, start); while (!token.empty()) { result.emplace_back(token); token = GetNextToken(arguments, start); } return result; } // Parses a token that represents an argument to an option. // If the value is unquoted, returns it as is. // If the value is quoted, removes the quotes and replaces escaped characters. std::string ParseValue(std::string_view valueToken) { if (valueToken.empty() || valueToken[0] != '"') { // Nothing to do for empty or unquoted tokens return std::string{ valueToken }; } // Copy the string ignoring the quotes and replacing escaped characters. // In quoted tokens, the back quote represents double quotes (` means ") // and can be escaped with back slash (\` means `). // Note that we accept quoted values with a missing closing quote (the end // of string signals the end). std::string result; for (size_t i = 1; i < valueToken.size(); ++i) { if (valueToken[i] == '"') { // The tokenizer can leave several pairs of quotes in the token // but they are not accepted in this case. We only accept the final // closing quotes. if (i + 1 == valueToken.size()) { break; } else { AICLI_LOG(Core, Error, << "Invalid msiexec argument: " << valueToken); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } } if (i + 1 < valueToken.size() && valueToken[i] == '\\' && valueToken[i + 1] == '`') { result += '`'; ++i; } else if (valueToken[i] == '`') { result += '"'; } else { result += valueToken[i]; } } return result; } // Validates that a token represents a property. // This checks that the property has the form PropertyName=Value, // with the value optionally quoted. bool IsValidPropertyToken(std::string_view token) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR, token.empty()); if (token[0] != '%' && !IsCharAlphaNumericA(token[0])) { AICLI_LOG(Core, Error, << "Bad property for msiexec: " << token); return false; } // Find the = separator at the end of the property name size_t pos = 0; while (pos < token.size() && !IsWhiteSpace(token[pos]) && token[pos] != '=') { ++pos; } if (pos == token.size() || token[pos] != '=') { AICLI_LOG(Core, Error, << "Expected property for call to msiexec, but couldn't find separator: " << token); return false; } // Validate the property value. // It should be completely enclosed in quotes, or not contain white space. // If quoted, there can be pairs of consecutive quotes that work as escape sequences. // We accept empty property values. ++pos; if (pos == token.size()) { // Empty value return true; } // If quoted, we will only inspect the values between the quotes. bool quoted = false; size_t end = token.size(); if (token[pos] == '"') { ++pos; if (pos >= end || token.back() != '"') { AICLI_LOG(Core, Error, << "Badly quoted msiexec property: " << token); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } --end; quoted = true; } while (pos < end) { if (quoted) { // For quoted values, any internal quote must be followed by another one. if (token[pos] == '"') { if (pos + 1 < end && token[pos + 1] == '"') { // Skip the two quotes ++pos; } else { AICLI_LOG(Core, Error, << "Unexpected quotes in msiexec property arg: " << token); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } } } else { // For unquoted values, we only check that there is no whitespace if (IsWhiteSpace(token[pos])) { AICLI_LOG(Core, Error, << "Unexpected space in msiexec property arg: " << token); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } } ++pos; } return true; } // Replaces long options in the arguments (e.g. /quiet), by their short equivalents // (e.g. /qn). The replacement is done in-place. void ReplaceLongOptions(std::list& tokens) { // We don't handle all possible options because we don't need to. // Options not handled: // /update // /uninstall // /package // /help const std::vector Replacements { { "quiet"sv, "/qn"sv }, { "passive"sv, { "/qb!-"sv, "REBOOTPROMPT=S"sv } }, { "norestart"sv, "REBOOT=ReallySuppress"sv }, { "forcerestart"sv, "REBOOT=Force"sv }, { "promptrestart"sv, "REBOOTPROMPT=\"\""sv }, { "log"sv, "/l*"sv }, }; auto itr = tokens.begin(); while (itr != tokens.end()) { if (!IsSwitch(*itr)) { // We only need to replace switches. ++itr; continue; } // Find if there is a replacement for this option. // We ignore the leading / or - when comparing. auto option = std::string_view(*itr).substr(1); auto replacementItr = std::find_if(Replacements.begin(), Replacements.end(), [&](const TokenReplacement& replacement) { return Utility::CaseInsensitiveEquals(replacement.LongOption, option); }); if (replacementItr == Replacements.end()) { // There is no replacement for this switch; ++itr; continue; } // Add all the replacements tokens needed before this one, then delete the existing token. tokens.insert(itr, replacementItr->ShortOption.begin(), replacementItr->ShortOption.end()); // Delete the current token an move to the next one. // We don't need to do anything more to the newly added tokens. itr = tokens.erase(itr); } } // Consumes the next argument token in the list. If the token is an option // that takes an argument, also consumes it. After consuming the token(s), // removes it from the list and updates the parsed arguments accordingly. void ConsumeNextToken(std::list& tokens, MsiParsedArguments& parsedArgs) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR, tokens.empty()); auto token = std::move(tokens.front()); tokens.pop_front(); if (!IsSwitch(token)) { // Token is a property, i.e. NAME=value. Add it to the parsed args. THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT, !IsValidPropertyToken(token)); parsedArgs.Properties += L" " + Utility::ConvertToUTF16(token); return; } // Token is an option. if (token.size() <= 1) { AICLI_LOG(Core, Error, << "Invalid command line argument for msiexec: " << token); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } char option = token[1]; auto optionModifier = ParseValue(std::string_view(token).substr(2)); // Options are case-insensitive switch (std::tolower(option)) { case MsiExecQuietOption: { ParseQuietOption(optionModifier, parsedArgs); break; } case MsiExecLogOption: { if (tokens.empty()) { // Log option must be followed by an option argument AICLI_LOG(Core, Error, << "msiexec option " << token << " must be followed by a value"); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } const auto optionValue = ParseValue(tokens.front()); tokens.pop_front(); ParseLogOption(optionModifier, optionValue, parsedArgs); break; } default: { AICLI_LOG(Core, Error, << "Invalid option for msiexec: " << token); THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT); } } } } MsiParsedArguments ParseMSIArguments(std::string_view arguments) { // Split the arguments into tokens, which we will process one by one. auto argumentTokens = TokenizeMsiArguments(arguments); // Replace long options so we can work only with short ones. ReplaceLongOptions(argumentTokens); // Process the arguments. MsiParsedArguments result; while (!argumentTokens.empty()) { ConsumeNextToken(argumentTokens, result); } return result; } } ================================================ FILE: src/AppInstallerCommonCore/MsixInfo.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerMsixInfo.h" #include "HttpStream/HttpRandomAccessStream.h" #include "Public/AppInstallerDownloader.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerDownloader.h" #include "Public/AppInstallerRuntime.h" using namespace winrt::Windows::Storage::Streams; using namespace Microsoft::WRL; using namespace AppInstaller::Utility::HttpStream; using namespace winrt::Windows::Management::Deployment; namespace AppInstaller::Msix { namespace { // MSIX-specific header placed in the P7X file, before the actual signature const byte P7xFileId[] = { 0x50, 0x4b, 0x43, 0x58 }; const DWORD P7xFileIdSize = sizeof(P7xFileId); // Gets the version from the manifest reader. UINT64 GetVersionFromManifestReader(IAppxManifestReader* reader) { ComPtr packageId; THROW_IF_FAILED(reader->GetPackageId(&packageId)); UINT64 result = 0; THROW_IF_FAILED(packageId->GetVersion(&result)); return result; } // Gets the UINT64 version from the version struct. UINT64 GetVersionFromVersion(const winrt::Windows::ApplicationModel::PackageVersion& version) { UINT64 result = version.Major; result = (result << 16) | version.Minor; result = (result << 16) | version.Build; result = (result << 16) | version.Revision; return result; } // Writes the stream (from current location) to the given file. void WriteStreamToFile(IStream* stream, UINT64 expectedSize, const std::filesystem::path& target, IProgressCallback& progress) { std::filesystem::path tempFile = target; tempFile += ".dnld"; { std::ofstream file(tempFile, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); constexpr ULONG bufferSize = 1 << 20; std::unique_ptr buffer = std::make_unique(bufferSize); UINT64 totalBytesRead = 0; while (!progress.IsCancelledBy(CancelReason::Any)) { ULONG bytesRead = 0; HRESULT hr = stream->Read(buffer.get(), bufferSize, &bytesRead); if (bytesRead) { // If we got bytes, just accept them and keep going. LOG_IF_FAILED(hr); THROW_HR_IF_MSG(E_UNEXPECTED, expectedSize && totalBytesRead + bytesRead > expectedSize, "Read more bytes than expected size"); file.write(buffer.get(), bytesRead); totalBytesRead += bytesRead; progress.OnProgress(totalBytesRead, expectedSize, ProgressType::Bytes); } else { // If given a size, and we have read it all, quit if (expectedSize && totalBytesRead == expectedSize) { break; } // If the stream returned an error, throw it THROW_IF_FAILED(hr); // If we were given a size and didn't reach it, throw our own error; // otherwise assume that this is just normal EOF. if (expectedSize) { THROW_WIN32(ERROR_HANDLE_EOF); } else { break; } } } } std::filesystem::path backupFile = target; backupFile += ".bkup"; if (std::filesystem::exists(target)) { if (std::filesystem::exists(backupFile)) { std::filesystem::remove(backupFile); } std::filesystem::rename(target, backupFile); } std::filesystem::rename(tempFile, target); } // Writes the appx file to the given file. void WriteAppxFileToFile(IAppxFile* appxFile, const std::filesystem::path& target, IProgressCallback& progress) { UINT64 size = 0; THROW_IF_FAILED(appxFile->GetSize(&size)); ComPtr stream; THROW_IF_FAILED(appxFile->GetStream(&stream)); WriteStreamToFile(stream.Get(), size, target, progress); } // Writes the stream (from current location) to the given file handle. void WriteStreamToFileHandle(IStream* stream, UINT64 expectedSize, HANDLE target, IProgressCallback& progress) { constexpr ULONG bufferSize = 1 << 20; std::unique_ptr buffer = std::make_unique(bufferSize); UINT64 totalBytesRead = 0; while (!progress.IsCancelledBy(CancelReason::Any)) { ULONG bytesRead = 0; HRESULT hr = stream->Read(buffer.get(), bufferSize, &bytesRead); if (bytesRead) { // If we got bytes, just accept them and keep going. LOG_IF_FAILED(hr); THROW_HR_IF_MSG(E_UNEXPECTED, expectedSize && totalBytesRead + bytesRead > expectedSize, "Read more bytes than expected size"); DWORD bytesWritten = 0; THROW_LAST_ERROR_IF(!WriteFile(target, buffer.get(), bytesRead, &bytesWritten, nullptr)); THROW_HR_IF(E_UNEXPECTED, bytesRead != bytesWritten); totalBytesRead += bytesRead; progress.OnProgress(totalBytesRead, expectedSize, ProgressType::Bytes); } else { // If given a size, and we have read it all, quit if (expectedSize && totalBytesRead == expectedSize) { break; } // If the stream returned an error, throw it THROW_IF_FAILED(hr); // If we were given a size and didn't reach it, throw our own error; // otherwise assume that this is just normal EOF. if (expectedSize) { THROW_WIN32(ERROR_HANDLE_EOF); } else { break; } } } } // Writes the appx file to the given file handle. void WriteAppxFileToFileHandle(IAppxFile* appxFile, HANDLE target, IProgressCallback& progress) { UINT64 size = 0; THROW_IF_FAILED(appxFile->GetSize(&size)); ComPtr stream; THROW_IF_FAILED(appxFile->GetStream(&stream)); WriteStreamToFileHandle(stream.Get(), size, target, progress); } bool ValidateMsixTrustInfo(const std::filesystem::path& msixPath, bool verifyMicrosoftOrigin) { bool result = false; AICLI_LOG(Core, Info, << "Started trust validation of msix at: " << msixPath); try { bool verifyChainResult = false; // First verify certificate chain if requested. if (verifyMicrosoftOrigin) { auto [certContext, certStore] = GetCertContextFromMsix(msixPath); // Get certificate chain context for validation CERT_CHAIN_PARA certChainParameters = { 0 }; certChainParameters.cbSize = sizeof(CERT_CHAIN_PARA); certChainParameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; DWORD certChainFlags = CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL; wil::unique_cert_chain_context certChainContext; THROW_LAST_ERROR_IF(!CertGetCertificateChain( HCCE_LOCAL_MACHINE, certContext.get(), NULL, // Use the current system time for CRL validation certStore.get(), &certChainParameters, certChainFlags, NULL, // Reserved parameter; must be NULL &certChainContext)); // Validate that the certificate chain is rooted in one of the well-known Microsoft root certs CERT_CHAIN_POLICY_PARA policyParameters = { 0 }; policyParameters.cbSize = sizeof(CERT_CHAIN_POLICY_PARA); policyParameters.dwFlags = MICROSOFT_ROOT_CERT_CHAIN_POLICY_CHECK_APPLICATION_ROOT_FLAG; CERT_CHAIN_POLICY_STATUS policyStatus = { 0 }; policyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS); LPCSTR policyOid = CERT_CHAIN_POLICY_MICROSOFT_ROOT; BOOL certChainVerifySucceeded = CertVerifyCertificateChainPolicy( policyOid, certChainContext.get(), &policyParameters, &policyStatus); AICLI_LOG(Core, Info, << "Result for certificate chain validation of Microsoft origin: " << policyStatus.dwError); verifyChainResult = certChainVerifySucceeded && policyStatus.dwError == ERROR_SUCCESS; } else { verifyChainResult = true; } // If certificate chain origin validation is success or not requested, then validate the trust info of the file. if (verifyChainResult) { // Set up the structures needed for the WinVerifyTrust call WINTRUST_FILE_INFO fileInfo = { 0 }; fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO); fileInfo.pcwszFilePath = msixPath.c_str(); WINTRUST_DATA trustData = { 0 }; trustData.cbStruct = sizeof(WINTRUST_DATA); trustData.dwUIChoice = WTD_UI_NONE; trustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; trustData.dwUnionChoice = WTD_CHOICE_FILE; trustData.dwStateAction = WTD_STATEACTION_VERIFY; trustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL; trustData.pFile = &fileInfo; GUID verifyActionId = WINTRUST_ACTION_GENERIC_VERIFY_V2; HRESULT verifyTrustResult = static_cast(WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &verifyActionId, &trustData)); AICLI_LOG(Core, Info, << "Result for trust info validation of the msix: " << verifyTrustResult); result = verifyTrustResult == S_OK; } } catch (const wil::ResultException& re) { AICLI_LOG(Core, Error, << "Failed during msix trust validation. Error: " << re.GetErrorCode()); result = false; } catch (...) { AICLI_LOG(Core, Error, << "Failed during msix trust validation."); result = false; } return result; } } bool GetBundleReader( IStream* inputStream, IAppxBundleReader** reader) { ComPtr bundleFactory; // Create a new Appxbundle factory THROW_IF_FAILED(CoCreateInstance( __uuidof(AppxBundleFactory), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IAppxBundleFactory), (LPVOID*)(&bundleFactory))); HRESULT hr = bundleFactory->CreateBundleReader(inputStream, reader); if (SUCCEEDED(hr)) { return true; } else if (hr == APPX_E_MISSING_REQUIRED_FILE) { // APPX_E_MISSING_REQUIRED_FILE returned when trying to open // an *.msix as an *.msixbundle or vice-versa. return false; } else { THROW_HR(hr); } } bool GetPackageReader( IStream* inputStream, IAppxPackageReader** reader) { ComPtr appxFactory; // Create a new Appx factory THROW_IF_FAILED(CoCreateInstance( __uuidof(AppxFactory), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IAppxFactory), (LPVOID*)(&appxFactory))); // Create a new package reader using the factory. HRESULT hr = appxFactory->CreatePackageReader(inputStream, reader); if (SUCCEEDED(hr)) { return true; } else if (hr == APPX_E_MISSING_REQUIRED_FILE) { // APPX_E_MISSING_REQUIRED_FILE returned when trying to open // an *.msix as an *.msixbundle or vice-versa. return false; } else { THROW_HR(hr); } } void GetManifestReader( IStream* inputStream, IAppxManifestReader** reader) { ComPtr appxFactory; THROW_IF_FAILED(CoCreateInstance( __uuidof(AppxFactory), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IAppxFactory), (LPVOID*)(&appxFactory))); THROW_IF_FAILED(appxFactory->CreateManifestReader(inputStream, reader)); } std::optional GetPackageFullNameFromFamilyName(std::string_view familyName) { PackageManager packageManager; std::wstring pfn = Utility::ConvertToUTF16(familyName); // PackageManager.FindPackages() can find all packages (including provisioned ones) but requires admin. // For non admin callers, use FindPackagesByPackageFamily where only packages registered to current user will be found. if (Runtime::IsRunningAsAdmin()) { auto packages = packageManager.FindPackages(pfn); std::optional result; for (const auto& package : packages) { if (result.has_value()) { // More than 1 package found. Don't directly error, let caller deal with it. AICLI_LOG(Core, Error, << "Multiple packages found for family name: " << familyName); return {}; } result = Utility::ConvertToUTF8(package.Id().FullName()); } return result; } else { UINT32 fullNameCount = 0; UINT32 bufferLength = 0; UINT32 properties = 0; LONG findResult = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &fullNameCount, nullptr, &bufferLength, nullptr, &properties); if (findResult == ERROR_SUCCESS || fullNameCount == 0) { // No package found return {}; } else if (findResult != ERROR_INSUFFICIENT_BUFFER) { THROW_WIN32(findResult); } else if (fullNameCount != 1) { // Don't directly error, let caller deal with it AICLI_LOG(Core, Error, << "Multiple packages found for family name: " << fullNameCount); return {}; } // fullNameCount == 1 at this point PWSTR fullNamePtr; std::wstring buffer(static_cast(bufferLength) + 1, '\0'); THROW_IF_WIN32_ERROR(FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &fullNameCount, &fullNamePtr, &bufferLength, &buffer[0], &properties)); if (fullNameCount != 1 || bufferLength == 0) { // Something changed in between, abandon AICLI_LOG(Core, Error, << "Packages found for family name: " << fullNameCount); return {}; } buffer.resize(bufferLength - 1); return Utility::ConvertToUTF8(buffer); } } std::string GetPackageFamilyNameFromFullName(std::string_view fullName) { std::wstring result; result.resize(PACKAGE_FAMILY_NAME_MAX_LENGTH + 1); UINT32 size = static_cast(result.size()); THROW_IF_WIN32_ERROR(PackageFamilyNameFromFullName(Utility::ConvertToUTF16(fullName).c_str(), &size, &result[0])); result.resize(size - 1); return Utility::ConvertToUTF8(result); } std::optional GetPackageLocationFromFullName(std::string_view fullName) { std::wstring fn = Utility::ConvertToUTF16(fullName); UINT32 length = 0; LONG returnVal = GetStagedPackagePathByFullName(fn.c_str(), &length, nullptr); if (returnVal != ERROR_INSUFFICIENT_BUFFER) { LOG_WIN32(returnVal); return {}; } THROW_HR_IF(E_UNEXPECTED, length == 0); std::wstring result; result.resize(length); returnVal = GetStagedPackagePathByFullName(fn.c_str(), &length, &result[0]); if (returnVal != ERROR_SUCCESS) { LOG_WIN32(returnVal); return {}; } result.resize(length - 1); return { result }; } Msix::PackageIdInfo GetPackageIdInfoFromFullName(std::string_view fullName) { std::wstring fullNameWide = Utility::ConvertToUTF16(fullName); UINT32 length = 0; LONG returnVal = PackageIdFromFullName(fullNameWide.c_str(), PACKAGE_INFORMATION_BASIC, &length, nullptr); if (returnVal != ERROR_INSUFFICIENT_BUFFER) { LOG_WIN32(returnVal); return {}; } THROW_HR_IF(E_UNEXPECTED, length == 0); std::unique_ptr packageIdContent = std::make_unique(length); returnVal = PackageIdFromFullName(fullNameWide.c_str(), PACKAGE_INFORMATION_BASIC, &length, packageIdContent.get()); if (returnVal != ERROR_SUCCESS) { LOG_WIN32(returnVal); return {}; } PACKAGE_ID* packageId = (PACKAGE_ID*)packageIdContent.get(); return { Utility::ConvertToUTF8(packageId->name), packageId->version.Version }; } GetCertContextResult GetCertContextFromMsix(const std::filesystem::path& msixPath) { // Retrieve raw signature from msix MsixInfo msixInfo{ msixPath }; auto signature = msixInfo.GetSignature(true); // Get the cert content wil::unique_any signedMessage; wil::unique_hcertstore certStore; CRYPT_DATA_BLOB signatureBlob = { 0 }; signatureBlob.cbData = static_cast(signature.size()); signatureBlob.pbData = signature.data(); THROW_LAST_ERROR_IF(!CryptQueryObject( CERT_QUERY_OBJECT_BLOB, &signatureBlob, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, // Reserved parameter NULL, // No encoding info needed NULL, NULL, &certStore, &signedMessage, NULL)); // Get the signer size and information from the signed data message // The properties of the signer info will be used to uniquely identify the signing certificate in the certificate store DWORD signerInfoSize = 0; THROW_LAST_ERROR_IF(!CryptMsgGetParam( signedMessage.get(), CMSG_SIGNER_INFO_PARAM, 0, NULL, &signerInfoSize)); // Check that the signer info size is within reasonable bounds; under the max length of a string for the issuer field THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), !(signerInfoSize > 0 && signerInfoSize < STRSAFE_MAX_CCH)); std::vector signerInfoBuffer; signerInfoBuffer.resize(signerInfoSize); THROW_LAST_ERROR_IF(!CryptMsgGetParam( signedMessage.get(), CMSG_SIGNER_INFO_PARAM, 0, signerInfoBuffer.data(), &signerInfoSize)); // Get the signing certificate from the certificate store based on the issuer and serial number of the signer info CMSG_SIGNER_INFO* signerInfo = reinterpret_cast(signerInfoBuffer.data()); CERT_INFO certInfo; certInfo.Issuer = signerInfo->Issuer; certInfo.SerialNumber = signerInfo->SerialNumber; wil::unique_cert_context certContext; certContext.reset(CertGetSubjectCertificateFromStore( certStore.get(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &certInfo)); THROW_LAST_ERROR_IF(!certContext.get()); return { std::move(certContext), std::move(certStore) }; } MsixInfo::MsixInfo(std::string_view uriStr) { m_stream = Utility::GetReadOnlyStreamFromURI(uriStr); if (GetBundleReader(m_stream.Get(), &m_bundleReader)) { m_isBundle = true; } else if (GetPackageReader(m_stream.Get(), &m_packageReader)) { m_isBundle = false; } else { THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_INSTALL_OPEN_PACKAGE_FAILED), "Failed to open uri as msix package or bundle. Uri: %hs", uriStr.data()); } } std::vector MsixInfo::GetSignature(bool skipP7xFileId) { ComPtr signatureFile; if (m_isBundle) { THROW_IF_FAILED(m_bundleReader->GetFootprintFile(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_SIGNATURE, &signatureFile)); } else { THROW_IF_FAILED(m_packageReader->GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE_SIGNATURE, &signatureFile)); } std::vector signatureContent; DWORD signatureSize; ComPtr signatureStream; THROW_IF_FAILED(signatureFile->GetStream(&signatureStream)); STATSTG stat = { 0 }; THROW_IF_FAILED(signatureStream->Stat(&stat, STATFLAG_NONAME)); THROW_HR_IF(E_UNEXPECTED, stat.cbSize.HighPart != 0); // Signature size should be small signatureSize = stat.cbSize.LowPart; THROW_HR_IF(E_UNEXPECTED, signatureSize <= P7xFileIdSize); if (skipP7xFileId) { // Validate msix signature header byte headerBuffer[P7xFileIdSize]; DWORD headerRead; THROW_IF_FAILED(signatureStream->Read(headerBuffer, P7xFileIdSize, &headerRead)); THROW_HR_IF_MSG(E_UNEXPECTED, headerRead != P7xFileIdSize, "Failed to read signature header"); THROW_HR_IF_MSG(E_UNEXPECTED, !std::equal(P7xFileId, P7xFileId + P7xFileIdSize, headerBuffer), "Unexpected msix signature header"); signatureSize -= P7xFileIdSize; } signatureContent.resize(signatureSize); DWORD signatureRead; THROW_IF_FAILED(signatureStream->Read(signatureContent.data(), signatureSize, &signatureRead)); THROW_HR_IF_MSG(E_UNEXPECTED, signatureRead != signatureSize, "Failed to read the whole signature stream"); return signatureContent; } Utility::SHA256::HashBuffer MsixInfo::GetSignatureHash() { auto signature = GetSignature(); return Utility::SHA256::ComputeHash(signature.data(), static_cast(signature.size())); } std::wstring MsixInfo::GetDigest() { ComPtr digestProvider; if (m_isBundle) { THROW_IF_FAILED(m_bundleReader.As(&digestProvider)); } else { THROW_IF_FAILED(m_packageReader.As(&digestProvider)); } wil::unique_cotaskmem_string result; THROW_IF_FAILED(digestProvider->GetDigest(&result)); return result.get(); } std::wstring MsixInfo::GetPackageFullNameWide() { ComPtr packageId; if (m_isBundle) { ComPtr manifestReader; THROW_IF_FAILED(m_bundleReader->GetManifest(&manifestReader)); THROW_IF_FAILED(manifestReader->GetPackageId(&packageId)); } else { ComPtr manifestReader; THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader)); THROW_IF_FAILED(manifestReader->GetPackageId(&packageId)); } wil::unique_cotaskmem_string fullName; THROW_IF_FAILED(packageId->GetPackageFullName(&fullName)); return { fullName.get() }; } std::string MsixInfo::GetPackageFullName() { return Utility::ConvertToUTF8(GetPackageFullNameWide()); } std::vector> MsixInfo::GetAppPackages(bool includeStub) const { if (!m_isBundle) { return { m_packageReader }; } std::vector> packages; ComPtr manifestReader; THROW_IF_FAILED(m_bundleReader->GetManifest(&manifestReader)); ComPtr packageInfoItems; THROW_IF_FAILED(manifestReader->GetPackageInfoItems(&packageInfoItems)); BOOL hasCurrent = FALSE; THROW_IF_FAILED(packageInfoItems->GetHasCurrent(&hasCurrent)); while (hasCurrent) { ComPtr packageInfo; THROW_IF_FAILED(packageInfoItems->GetCurrent(&packageInfo)); APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType; THROW_IF_FAILED(packageInfo->GetPackageType(&packageType)); // Check flat bundle case. UINT64 offset; THROW_IF_FAILED(packageInfo->GetOffset(&offset)); bool isContained = offset != 0; // Check stub package case. ComPtr packageInfo4; THROW_IF_FAILED(packageInfo.As(&packageInfo4)); BOOL isStub = FALSE; THROW_IF_FAILED(packageInfo4->GetIsStub(&isStub)); if (isContained && (includeStub || !isStub) && packageType == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION) { wil::unique_cotaskmem_string fileName; THROW_IF_FAILED(packageInfo->GetFileName(&fileName)); ComPtr packageFile; THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileName.get(), &packageFile)); ComPtr stream; THROW_IF_FAILED(packageFile->GetStream(&stream)); ComPtr packageReader; if (GetPackageReader(stream.Get(), &packageReader)) { packages.emplace_back(std::move(packageReader)); } else { AICLI_LOG(Core, Warning, << "Could not get package reader for bundle payload."); } } THROW_IF_FAILED(packageInfoItems->MoveNext(&hasCurrent)); } return packages; } std::vector MsixInfo::GetAppPackageManifests(bool includeStub) const { std::vector manifests; auto packages = GetAppPackages(includeStub); for (const auto& package : packages) { ComPtr manifestReader; THROW_IF_FAILED(package->GetManifest(&manifestReader)); manifests.emplace_back(std::move(manifestReader)); } return manifests; } bool MsixInfo::IsNewerThan(const std::filesystem::path& otherPackage) { THROW_HR_IF(E_NOT_VALID_STATE, m_isBundle); MsixInfo other{ otherPackage }; THROW_HR_IF(E_INVALIDARG, other.m_isBundle); ComPtr otherReader; THROW_IF_FAILED(other.m_packageReader->GetManifest(&otherReader)); ComPtr manifestReader; THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader)); return (GetVersionFromManifestReader(manifestReader.Get()) > GetVersionFromManifestReader(otherReader.Get())); } bool MsixInfo::IsNewerThan(const winrt::Windows::ApplicationModel::PackageVersion& otherVersion) { THROW_HR_IF(E_NOT_VALID_STATE, m_isBundle); ComPtr manifestReader; THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader)); return (GetVersionFromManifestReader(manifestReader.Get()) > GetVersionFromVersion(otherVersion)); } void MsixInfo::WriteToFile(std::string_view packageFile, const std::filesystem::path& target, IProgressCallback& progress) { std::wstring fileUTF16 = Utility::ConvertToUTF16(packageFile); ComPtr appxFile; if (m_isBundle) { THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileUTF16.c_str(), &appxFile)); } else { THROW_IF_FAILED(m_packageReader->GetPayloadFile(fileUTF16.c_str(), &appxFile)); } WriteAppxFileToFile(appxFile.Get(), target, progress); } void MsixInfo::WriteManifestToFile(const std::filesystem::path& target, IProgressCallback& progress) { ComPtr appxFile; if (m_isBundle) { THROW_IF_FAILED(m_bundleReader->GetFootprintFile(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_MANIFEST, &appxFile)); } else { THROW_IF_FAILED(m_packageReader->GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE_MANIFEST, &appxFile)); } WriteAppxFileToFile(appxFile.Get(), target, progress); } void MsixInfo::WriteToFileHandle(std::string_view packageFile, HANDLE target, IProgressCallback& progress) { std::wstring fileUTF16 = Utility::ConvertToUTF16(packageFile); ComPtr appxFile; if (m_isBundle) { THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileUTF16.c_str(), &appxFile)); } else { THROW_IF_FAILED(m_packageReader->GetPayloadFile(fileUTF16.c_str(), &appxFile)); } WriteAppxFileToFileHandle(appxFile.Get(), target, progress); } WriteLockedMsixFile::WriteLockedMsixFile(const std::filesystem::path& path) { m_file = Utility::ManagedFile::OpenWriteLockedFile(path, 0); } bool WriteLockedMsixFile::ValidateTrustInfo(bool checkMicrosoftOrigin) const { return ValidateMsixTrustInfo(m_file.GetFilePath(), checkMicrosoftOrigin); } } ================================================ FILE: src/AppInstallerCommonCore/MsixManifest.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include #include #include using namespace Microsoft::WRL; namespace AppInstaller::Msix { bool IsSupportedPlatform(MsixPackageManifestTargetDeviceFamily::Platform platform) { return platform == MsixPackageManifestTargetDeviceFamily::Platform::WindowsDesktop || platform == MsixPackageManifestTargetDeviceFamily::Platform::WindowsUniversal; } Utility::NormalizedString MsixPackageManifestIdentity::GetPackageFamilyName() const { wil::unique_cotaskmem_string familyName; THROW_IF_FAILED(m_packageId->GetPackageFamilyName(&familyName)); return { familyName.get() }; } PackageVersion MsixPackageManifestIdentity::GetVersion() const { UINT64 version = 0; THROW_IF_FAILED(m_packageId->GetVersion(&version)); return PackageVersion{ version }; } MsixPackageManifestIdentity MsixPackageManifest::GetIdentity() const { ComPtr manifestPackageId; THROW_IF_FAILED(m_manifestReader->GetPackageId(&manifestPackageId)); return MsixPackageManifestIdentity{ std::move(manifestPackageId) }; } std::optional MsixPackageManifest::GetMinimumOSVersionForSupportedPlatforms() const { std::optional minOSVersion; auto targetDeviceFamilies = GetTargetDeviceFamilies(); for (const auto& targetDeviceFamily : targetDeviceFamilies) { if (IsSupportedPlatform(targetDeviceFamily.GetPlatform())) { auto targetDeviceFamilyMinVersion = targetDeviceFamily.GetMinVersion(); if (!minOSVersion.has_value() || targetDeviceFamilyMinVersion < minOSVersion.value()) { minOSVersion = targetDeviceFamilyMinVersion; } } } return minOSVersion; } std::vector MsixPackageManifest::GetTargetDeviceFamilies() const { std::vector targetDeviceFamilies; ComPtr manifestReader3; THROW_IF_FAILED(m_manifestReader.As(&manifestReader3)); ComPtr targetDeviceFamiliesIter; THROW_IF_FAILED(manifestReader3->GetTargetDeviceFamilies(&targetDeviceFamiliesIter)); BOOL hasCurrent = FALSE; THROW_IF_FAILED(targetDeviceFamiliesIter->GetHasCurrent(&hasCurrent)); while (hasCurrent) { ComPtr targetDeviceFamily; THROW_IF_FAILED(targetDeviceFamiliesIter->GetCurrent(&targetDeviceFamily)); targetDeviceFamilies.emplace_back(std::move(targetDeviceFamily)); THROW_IF_FAILED(targetDeviceFamiliesIter->MoveNext(&hasCurrent)); } return targetDeviceFamilies; } std::string MsixPackageManifestTargetDeviceFamily::GetName() const { wil::unique_cotaskmem_string name; THROW_IF_FAILED(m_targetDeviceFamily->GetName(&name)); return Utility::ConvertToUTF8(name.get()); } OSVersion MsixPackageManifestTargetDeviceFamily::GetMinVersion() const { UINT64 minVersion = 0; THROW_IF_FAILED(m_targetDeviceFamily->GetMinVersion(&minVersion)); return OSVersion{ minVersion }; } MsixPackageManifestTargetDeviceFamily::Platform MsixPackageManifestTargetDeviceFamily::GetPlatform() const { auto name = GetName(); if (Utility::CaseInsensitiveEquals(name, WindowsDesktopName)) { return WindowsDesktop; } if (Utility::CaseInsensitiveEquals(name, WindowsUniversalName)) { return WindowsUniversal; } return Other; } } ================================================ FILE: src/AppInstallerCommonCore/NameNormalization.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/NameNormalization.h" #include "Public/AppInstallerStrings.h" #include "Public/winget/Regex.h" namespace AppInstaller::Utility { namespace { struct InterimNameNormalizationResult { std::wstring Name; Architecture Architecture = Architecture::Unknown; std::wstring Locale; }; struct InterimPublisherNormalizationResult { std::wstring Publisher; }; // To maintain consistency, changes that result in different output must be done in a new version. // This can potentially be ignored (if thought through) when the changes will only increase the // number of matches being made, with no impact to existing matches. For instance, removing an // arbitrary new processor architecture from names would hopefully only affect existing packages // that were not matching properly. Fixing a bug that was causing bad strings to be produced would // be impactful, and thus should likely result in a new iteration. class NormalizationInitial : public details::INameNormalizer { static std::wstring PrepareForValidation(std::string_view value) { std::wstring result = Utility::Normalize(ConvertToUTF16(value)); Trim(result); size_t atPos = result.find(L"@@", 3); if (atPos != std::wstring::npos) { result = result.substr(0, atPos); } return result; } // If the string is wrapped with some character groups, remove them. // Returns true if string was wrapped; false if not. static bool Unwrap(std::wstring& value) { if (value.length() >= 2) { bool unwrap = false; switch (value[0]) { case L'"': unwrap = value.back() == L'"'; break; case L'(': unwrap = value.back() == L')'; break; } if (unwrap) { value = value.substr(1, value.length() - 2); return true; } } return false; } // Removes all matches from the input string. static bool Remove(const Regex::Expression& re, std::wstring& input) { std::wstring output = re.Replace(input, {}); bool result = (output != input); input = std::move(output); return result; } // Removes the architecture and returns the value, if any Architecture RemoveArchitecture(std::wstring& value) const { Architecture result = Architecture::Unknown; // Must detect this first because "32/64 bit" is a superstring of "64 bit" if (Remove(Architecture32Or64Bit, value)) { // If the program is 32 and 64 bit in the same installer, leave as unknown. } // Must detect 64 bit before 32 bit because of "x86-64" being a superstring of "x86" else if (Remove(ArchitectureX64, value) || Remove(Architecture64Bit, value)) { result = Architecture::X64; } else if (Remove(ArchitectureX32, value) || Remove(Architecture32Bit, value)) { result = Architecture::X86; } return result; } // Removes all matches for the given regular expressions static bool RemoveAll(const std::vector& regexes, std::wstring& value) { bool result = false; for (const auto& re : regexes) { result = Remove(*re, value) || result; } return result; } // Removes all locales and returns the common value, if any std::wstring RemoveLocale(std::wstring& value) const { bool localeFound = false; std::wstring result; std::wstring newValue; auto newValueInserter = std::back_inserter(newValue); Locale.ForEach(value, [&](bool isMatch, std::wstring_view text) { bool copy = !isMatch; if (isMatch) { std::wstring foldedText = ConvertToUTF16(FoldCase(text)); // Ensure that the value is in the locale list auto bound = std::lower_bound(Locales.begin(), Locales.end(), foldedText); if (bound == Locales.end() || *bound != foldedText) { // Match was not a locale in our list, so copy it out copy = true; } else if (!localeFound) { // First/only match, just extract the value result = foldedText; localeFound = true; } else if (!result.empty()) { // For some reason, there are multiple locales listed. // See if they have anything in common. if (result != foldedText) { // Not completely the same (expected), see if they are at least the same language result.erase(result.find(L'-')); foldedText.erase(foldedText.find(L'-')); if (result != foldedText) { // Not the same language, abandon having a locale and just clean them result.clear(); } } } } if (copy) { std::copy(text.begin(), text.end(), newValueInserter); } return true; }); value = std::move(newValue); return result; } // Splits the string based on the regex matches, excluding empty/whitespace strings // and any values found in the exclusions. static std::vector Split(const Regex::Expression& re, const std::wstring& value, const std::vector& exclusions, bool stopOnExclusion = false) { std::vector result; re.ForEach(value, [&](bool, std::wstring_view text) { if (IsEmptyOrWhitespace(text)) { return true; } // Do not stop for an exclusion if it is the first word found if (!result.empty()) { std::wstring foldedText = ConvertToUTF16(FoldCase(text)); auto bound = std::lower_bound(exclusions.begin(), exclusions.end(), foldedText); if (bound != exclusions.end() && *bound == foldedText) { return !stopOnExclusion; } } result.emplace_back(std::wstring{ text }); return true; }); return result; } // Joins all of the given strings into a single value static std::wstring Join(const std::vector& values, const std::wstring& separator = {}) { std::wstring result; bool isFirst = true; for (const auto& v : values) { if (isFirst) { isFirst = false; } else { result += separator; } result += v; } return result; } static constexpr Regex::Options reOptions = Regex::Options::CaseInsensitive; // Architecture Regex::Expression ArchitectureX32{ R"((?<=^|[^\p{L}\p{Nd}])(X32|X86)(?=\P{Nd}|$)(?:\sEDITION)?)", reOptions }; Regex::Expression ArchitectureX64{ R"((?<=^|[^\p{L}\p{Nd}])(X64|AMD64|X86([\p{Pd}\p{Pc}]64))(?=\P{Nd}|$)(?:\sEDITION)?)", reOptions }; Regex::Expression Architecture32Bit{ R"((?<=^|[^\p{L}\p{Nd}])(32[\p{Pd}\p{Pc}\p{Z}]?BIT)S?(?:\sEDITION)?)", reOptions }; Regex::Expression Architecture64Bit{ R"((?<=^|[^\p{L}\p{Nd}])(64[\p{Pd}\p{Pc}\p{Z}]?BIT)S?(?:\sEDITION)?)", reOptions }; Regex::Expression Architecture32Or64Bit{ R"((?<=^|[^\p{L}\p{Nd}])((64[\\\/]32|32[\\\/]64)[\p{Pd}\p{Pc}\p{Z}]?BIT)S?(?:\sEDITION)?)", reOptions }; // Locale Regex::Expression Locale{ R"((? ProgramNameRegexes { &Roblox, &Bomgar, &PrefixParens, &EmptyParens, &FilePathGHS, &FilePathParens, &FilePathQuotes, &FilePath, &VersionLetter, &VersionDelimited, &Version, &EN, &NonNestedBracket, &BracketEnclosed, &URIProtocol, &LeadingSymbols, &TrailingSymbols }; const std::vector PublisherNameRegexes { &VersionDelimited, &Version, &NonNestedBracket, &BracketEnclosed, &URIProtocol, &NonLetters, &TrailingNonLetters, &AcronymSeparators }; // Add values here but use Locales in code. const std::vector LocaleViews { L"AF-ZA", L"AM-ET", L"AR-AE", L"AR-BH", L"AR-DZ", L"AR-EG", L"AR-IQ", L"AR-JO", L"AR-KW", L"AR-LB", L"AR-LY", L"AR-MA", L"ARN-CL", L"AR-OM", L"AR-QA", L"AR-SA", L"AR-SY", L"AR-TN", L"AR-YE", L"AS-IN", L"BA-RU", L"BE-BY", L"BG-BG", L"BN-BD", L"BN-IN", L"BO-CN", L"BR-FR", L"CA-ES", L"CA-ES-VALENCIA", L"CO-FR", L"CS-CZ", L"CY-GB", L"DA-DK", L"DE-AT", L"DE-CH", L"DE-DE", L"DE-LI", L"DE-LU", L"DSB-DE", L"DV-MV", L"EL-GR", L"EN-AU", L"EN-BZ", L"EN-CA", L"EN-GB", L"EN-IE", L"EN-IN", L"EN-JM", L"EN-MY", L"EN-NZ", L"EN-PH", L"EN-SG", L"EN-TT", L"EN-US", L"EN-ZA", L"EN-ZW", L"ES-AR", L"ES-BO", L"ES-CL", L"ES-CO", L"ES-CR", L"ES-DO", L"ES-EC", L"ES-ES", L"ES-GT", L"ES-HN", L"ES-MX", L"ES-NI", L"ES-PA", L"ES-PE", L"ES-PR", L"ES-PY", L"ES-SV", L"ES-US", L"ES-UY", L"ES-VE", L"ET-EE", L"EU-ES", L"FA-IR", L"FI-FI", L"FIL-PH", L"FO-FO", L"FR-BE", L"FR-CA", L"FR-CH", L"FR-FR", L"FR-LU", L"FR-MC", L"FY-NL", L"GA-IE", L"GD-DB", L"GL-ES", L"GSW-FR", L"GU-IN", L"HE-IL", L"HI-IN", L"HR-BA", L"HR-HR", L"HSB-DE", L"HU-HU", L"HY-AM", L"ID-ID", L"IG-NG", L"II-CN", L"IS-IS", L"IT-CH", L"IT-IT", L"JA-JP", L"KA-GE", L"KK-KZ", L"KL-GL", L"KM-KH", L"KN-IN", L"KOK-IN", L"KO-KR", L"KY-KG", L"LB-LU", L"LO-LA", L"LT-LT", L"LV-LV", L"MI-NZ", L"MK-MK", L"ML-IN", L"MN-MN", L"MOH-CA", L"MR-IN", L"MS-BN", L"MS-MY", L"MT-MT", L"NB-NO", L"NE-NP", L"NL-BE", L"NL-NL", L"NN-NO", L"NSO-ZA", L"OC-FR", L"OR-IN", L"PA-IN", L"PL-PL", L"PRS-AF", L"PS-AF", L"PT-BR", L"PT-PT", L"QUT-GT", L"QUZ-BO", L"QUZ-EC", L"QUZ-PE", L"RM-CH", L"RO-RO", L"RU-RU", L"RW-RW", L"SAH-RU", L"SA-IN", L"SE-FI", L"SE-NO", L"SE-SE", L"SI-LK", L"SK-SK", L"SL-SI", L"SMA-NO", L"SMA-SE", L"SMJ-NO", L"SMJ-SE", L"SMN-FI", L"SMS-FI", L"SQ-AL", L"SV-FI", L"SV-SE", L"SW-KE", L"SYR-SY", L"TA-IN", L"TE-IN", L"TH-TH", L"TK-TM", L"TN-ZA", L"TR-TR", L"TT-RU", L"UG-CN", L"UK-UA", L"UR-PK", L"VI-VN", L"WO-SN", L"XH-ZA", L"YO-NG", L"ZH-CN", L"ZH-HK", L"ZH-MO", L"ZH-SG", L"ZH-TW", L"ZU-ZA", L"AZ-CYRL-AZ", L"AZ-LATN-AZ", L"BS-CYRL-BA", L"BS-LATN-BA", L"HA-LATN-NG", L"IU-CANS-CA", L"IU-LATN-CA", L"MN-MONG-CN", L"SR-CYRL-BA", L"SR-CYRL-CS", L"SR-CYRL-ME", L"SR-CYRL-RS", L"SR-LATN-BA", L"SR-LATN-CS", L"SR-LATN-ME", L"SR-LATN-RS", L"TG-CYRL-TJ", L"TZM-LATN-DZ", L"UZ-CYRL-UZ", L"UZ-LATN-UZ", }; // The folded and sorted version of LocaleViews. const std::vector Locales; // Add values here but use LegalEntitySuffixes in code. const std::vector LegalEntitySuffixViews { // Acronyms L"AB", L"AD", L"AG", L"APS", L"AS", L"ASA", L"BV", L"CO", L"CV", L"DOO", L"eV", L"GES", L"GESMBH", L"GMBH", L"INC", L"KG", L"KS", L"PS", L"LLC", L"LP", L"LTD", L"LTDA", L"MBH", L"NV", L"PLC", L"SL", L"PTY", L"PVT", L"SA", L"SARL", L"SC", L"SCA", L"SL", L"SP", L"SPA", L"SRL", L"SRO", // Words L"COMPANY", L"CORP", L"CORPORATION", L"HOLDING", L"HOLDINGS", L"INCORPORATED", L"LIMITED", L"SUBSIDIARY" }; // The folded and sorted version of LocaleViews. const std::vector LegalEntitySuffixes; const bool PreserveWhiteSpace; static std::vector FoldAndSort(const std::vector& input) { std::vector result; std::transform(input.begin(), input.end(), std::back_inserter(result), [](const std::wstring_view wsv) { return Utility::ConvertToUTF16(Utility::FoldCase(wsv)); }); std::sort(result.begin(), result.end()); return result; } InterimNameNormalizationResult NormalizeNameInternal(std::string_view name) const { InterimNameNormalizationResult result; result.Name = PrepareForValidation(name); while (Unwrap(result.Name)); // remove wrappers // handle (large majority of) SAP Business Object programs if (SAPPackage.IsMatch(result.Name)) { return result; } result.Architecture = RemoveArchitecture(result.Name); result.Locale = RemoveLocale(result.Name); // Extract KB numbers from their parens and preserve them result.Name = KBNumbers.Replace(result.Name, L"$1"); // Repeatedly remove matches for the regexes to create the minimum name while (RemoveAll(ProgramNameRegexes, result.Name)); auto tokens = Split(ProgramNameSplit, result.Name, LegalEntitySuffixes); // Re-join the tokens and drop all undesired characters if (PreserveWhiteSpace) { result.Name = Join(tokens, L" "); Remove(NonLetterDigitOrSpace, result.Name); } else { result.Name = Join(tokens); Remove(NonLettersAndDigits, result.Name); } return result; } InterimPublisherNormalizationResult NormalizePublisherInternal(std::string_view publisher) const { InterimPublisherNormalizationResult result; result.Publisher = PrepareForValidation(publisher); while (Unwrap(result.Publisher)); // remove wrappers while (RemoveAll(PublisherNameRegexes, result.Publisher)); auto tokens = Split(PublisherNameSplit, result.Publisher, LegalEntitySuffixes, true); // Re-join the tokens and drop all undesired characters if (PreserveWhiteSpace) { result.Publisher = Join(tokens, L" "); Remove(NonLetterDigitOrSpace, result.Publisher); } else { result.Publisher = Join(tokens); Remove(NonLettersAndDigits, result.Publisher); } return result; } public: NormalizationInitial(bool preserveWhiteSpace) : Locales(FoldAndSort(LocaleViews)), LegalEntitySuffixes(FoldAndSort(LegalEntitySuffixViews)), PreserveWhiteSpace(preserveWhiteSpace) { } NormalizedName Normalize(std::string_view name, std::string_view publisher) const override { InterimNameNormalizationResult nameResult = NormalizeNameInternal(name); InterimPublisherNormalizationResult pubResult = NormalizePublisherInternal(publisher); NormalizedName result; result.Name(ConvertToUTF8(nameResult.Name)); result.Architecture(nameResult.Architecture); result.Locale(ConvertToUTF8(nameResult.Locale)); result.Publisher(ConvertToUTF8(pubResult.Publisher)); return result; } NormalizedName NormalizeName(std::string_view name) const override { InterimNameNormalizationResult nameResult = NormalizeNameInternal(name); NormalizedName result; result.Name(ConvertToUTF8(nameResult.Name)); result.Architecture(nameResult.Architecture); result.Locale(ConvertToUTF8(nameResult.Locale)); return result; } std::string NormalizePublisher(std::string_view publisher) const override { InterimPublisherNormalizationResult pubResult = NormalizePublisherInternal(publisher); return ConvertToUTF8(pubResult.Publisher); } }; } NameNormalizer::NameNormalizer(NormalizationVersion version) { switch (version) { case AppInstaller::Utility::NormalizationVersion::Initial: m_normalizer = std::make_unique(false); break; case AppInstaller::Utility::NormalizationVersion::InitialPreserveWhiteSpace: m_normalizer = std::make_unique(true); break; default: THROW_HR(E_INVALIDARG); } } NormalizedName NameNormalizer::Normalize(std::string_view name, std::string_view publisher) const { return m_normalizer->Normalize(name, publisher); } NormalizedName NameNormalizer::NormalizeName(std::string_view name) const { return m_normalizer->NormalizeName(name); } std::string NameNormalizer::NormalizePublisher(std::string_view publisher) const { return m_normalizer->NormalizePublisher(publisher); } std::string NormalizedName::GetNormalizedName(NormalizationField fieldsToInclude) const { std::string result = Name(); if (WI_IsFlagSet(fieldsToInclude, NormalizationField::Architecture) && m_arch != Utility::Architecture::Unknown) { result += '(' + std::string(Utility::ToString(m_arch)) + ')'; } return result; } NormalizationField NormalizedName::GetNormalizedFields() const { NormalizationField result = NormalizationField::None; if (m_arch != Utility::Architecture::Unknown) { result |= NormalizationField::Architecture; } return result; } } ================================================ FILE: src/AppInstallerCommonCore/NetworkSettings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/NetworkSettings.h" #include "winget/AdminSettings.h" #include "AppInstallerLogging.h" namespace AppInstaller::Settings { void NetworkSettings::SetProxyUri(const std::optional& proxyUri) { AICLI_LOG(Core, Info, << "Setting proxy"); if (proxyUri) { m_proxyUri = proxyUri.value(); AICLI_LOG(Core, Info, << "New value for proxy is " << m_proxyUri.value()); } else { m_proxyUri.reset(); AICLI_LOG(Core, Info, << "Proxy will not be used"); } } InstallerDownloader NetworkSettings::GetInstallerDownloader() const { // The default is DeliveryOptimization. // We use WinINet if specified by settings, or if we want to use proxy (as DO does not support that) InstallerDownloader setting = User().Get(); if (m_proxyUri && setting != InstallerDownloader::WinInet) { AICLI_LOG(Core, Info, << "Forcing use of wininet for download as DO does not support proxy"); return InstallerDownloader::WinInet; } else if (setting == InstallerDownloader::Default) { return InstallerDownloader::DeliveryOptimization; } else { return setting; } } NetworkSettings::NetworkSettings() { // Get the default proxy try { m_proxyUri = GetAdminSetting(StringAdminSetting::DefaultProxy); } CATCH_LOG() AICLI_LOG(Core, Info, << "Default proxy is " << (m_proxyUri ? m_proxyUri.value() : "not set")); } NetworkSettings& NetworkSettings::Instance() { static NetworkSettings networkSettings; return networkSettings; } NetworkSettings& Network() { return NetworkSettings::Instance(); } } ================================================ FILE: src/AppInstallerCommonCore/OutputDebugStringLogger.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/OutputDebugStringLogger.h" namespace AppInstaller::Logging { namespace { static constexpr std::string_view s_OutputDebugStringLoggerName = "OutputDebugStringLogger"; } std::string OutputDebugStringLogger::GetName() const { return std::string{ s_OutputDebugStringLoggerName }; } void OutputDebugStringLogger::Write(Channel channel, Level, std::string_view message) noexcept try { std::stringstream strstr; strstr << "[" << std::setw(GetMaxChannelNameLength()) << std::left << std::setfill(' ') << GetChannelName(channel) << "] " << message << std::endl; std::string formattedMessage = std::move(strstr).str(); OutputDebugStringA(formattedMessage.c_str()); } catch (...) { // Just eat any exceptions here; better than losing logs } void OutputDebugStringLogger::WriteDirect(Channel, Level, std::string_view message) noexcept try { std::string nullTerminatedMessage{ message }; OutputDebugStringA(nullTerminatedMessage.c_str()); } catch (...) { // Just eat any exceptions here; better than losing logs } void OutputDebugStringLogger::Add() { Log().AddLogger(std::make_unique()); } void OutputDebugStringLogger::Remove() { Log().RemoveLogger(std::string{ s_OutputDebugStringLoggerName }); } } ================================================ FILE: src/AppInstallerCommonCore/PackageDependenciesValidationUtil.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include namespace AppInstaller::Manifest { WinGetManifestDependenciesErrorResult GetDependenciesValidationResultFromException(const AppInstaller::Manifest::ManifestException& manifestException) { auto result = WinGetManifestDependenciesErrorResult::None; auto validationErrors = manifestException.Errors(); for (const auto& validationError : validationErrors) { auto message = validationError.Message; std::map dependenciesErrorMessageMap = { {ManifestError::SingleManifestPackageHasDependencies, WinGetManifestDependenciesErrorResult::SingleManifestPackageHasDependencies}, {ManifestError::MultiManifestPackageHasDependencies, WinGetManifestDependenciesErrorResult::MultiManifestPackageHasDependencies }, {ManifestError::MissingManifestDependenciesNode, WinGetManifestDependenciesErrorResult::MissingManifestDependenciesNode }, {ManifestError::NoSuitableMinVersionDependency, WinGetManifestDependenciesErrorResult::NoSuitableMinVersionDependency }, {ManifestError::FoundDependencyLoop, WinGetManifestDependenciesErrorResult::FoundDependencyLoop } }; auto itr = dependenciesErrorMessageMap.find(message); if (itr != dependenciesErrorMessageMap.end()) { result |= itr->second; } } return result; } } ================================================ FILE: src/AppInstallerCommonCore/PackageVersionDataManifest.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/PackageVersionDataManifest.h" #include "Public/winget/Yaml.h" #include #include #include using namespace std::string_view_literals; namespace AppInstaller::Manifest { // These shortened names save some bytes since humans are neither authoring them nor reading them (except to debug). static constexpr std::string_view s_FieldName_SchemaVersion = "sV"sv; static constexpr std::string_view s_FieldName_VersionData = "vD"sv; static constexpr std::string_view s_FieldName_Version = "v"sv; static constexpr std::string_view s_FieldName_ArpMinVersion = "aMiV"sv; static constexpr std::string_view s_FieldName_ArpMaxVersion = "aMaV"sv; static constexpr std::string_view s_FieldName_RelativePath = "rP"sv; static constexpr std::string_view s_FieldName_Sha256Hash = "s256H"sv; static constexpr std::string_view s_SchemaVersion_1_0 = "1.0"sv; static constexpr DWORD CompressionAlgorithm = COMPRESS_ALGORITHM_MSZIP; static constexpr bool CompressionSetLevel1 = false; namespace anon { std::string GetRequiredChildString(const YAML::Node& node, std::string_view childName) { const YAML::Node& childNode = node.GetChildNode(childName); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !childNode.IsScalar()); return childNode.as(); } std::optional GetOptionalChildString(const YAML::Node& node, std::string_view childName) { const YAML::Node& childNode = node.GetChildNode(childName); return childNode.IsScalar() ? std::make_optional(childNode.as()) : std::nullopt; } void Deserialize_1_0(const YAML::Node& document, PackageVersionDataManifest& manifest) { const YAML::Node& versionDataItems = document.GetChildNode(s_FieldName_VersionData); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !versionDataItems.IsSequence()); for (const YAML::Node& item : versionDataItems.Sequence()) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !item.IsMap()); PackageVersionDataManifest::VersionData versionData; versionData.Version.Assign(GetRequiredChildString(item, s_FieldName_Version)); versionData.ArpMinVersion = GetOptionalChildString(item, s_FieldName_ArpMinVersion); versionData.ArpMaxVersion = GetOptionalChildString(item, s_FieldName_ArpMaxVersion); versionData.ManifestRelativePath = GetRequiredChildString(item, s_FieldName_RelativePath); versionData.ManifestHash = GetRequiredChildString(item, s_FieldName_Sha256Hash); manifest.AddVersion(std::move(versionData)); } } } std::string_view PackageVersionDataManifest::VersionManifestFileName() { return "versionData.yml"sv; } std::string_view PackageVersionDataManifest::VersionManifestCompressedFileName() { return "versionData.mszyml"sv; } std::filesystem::path PackageVersionDataManifest::GetRelativeDirectoryPath(std::string_view packageIdentifier, std::string_view manifestHash) { std::filesystem::path result = "packages"; result /= Utility::ConvertToUTF16(packageIdentifier); result /= manifestHash.substr(0, 8); return result; } Compression::Compressor PackageVersionDataManifest::CreateCompressor() { Compression::Compressor result(CompressionAlgorithm); if constexpr (CompressionSetLevel1) { result.SetInformation(COMPRESS_INFORMATION_CLASS_LEVEL, 1); } return result; } Compression::Decompressor PackageVersionDataManifest::CreateDecompressor() { return Compression::Decompressor(CompressionAlgorithm); } PackageVersionDataManifest::VersionData::VersionData( const Utility::VersionAndChannel& versionAndChannel, std::optional arpMinVersion, std::optional arpMaxVersion, std::optional relativePath, std::optional manifestHash) : Version(versionAndChannel.GetVersion()), ArpMinVersion(std::move(arpMinVersion)), ArpMaxVersion(std::move(arpMaxVersion)), ManifestRelativePath(std::move(relativePath).value_or("")), ManifestHash(std::move(manifestHash).value_or("")) { if (ArpMinVersion && ArpMinVersion->empty()) { ArpMinVersion.reset(); } if (ArpMaxVersion && ArpMaxVersion->empty()) { ArpMaxVersion.reset(); } } void PackageVersionDataManifest::AddVersion(VersionData&& versionData) { m_versions.emplace_back(std::move(versionData)); } const std::vector& PackageVersionDataManifest::Versions() const { return m_versions; } std::string PackageVersionDataManifest::Serialize() { YAML::Emitter out; out << YAML::BeginMap; out << YAML::Key << s_FieldName_SchemaVersion << YAML::Value << s_SchemaVersion_1_0; out << YAML::Key << s_FieldName_VersionData; out << YAML::BeginSeq; for (const auto& version : m_versions) { out << YAML::BeginMap; out << YAML::Key << s_FieldName_Version << YAML::Value << version.Version.ToString(); if (version.ArpMinVersion) { out << YAML::Key << s_FieldName_ArpMinVersion << YAML::Value << version.ArpMinVersion.value(); } if (version.ArpMaxVersion) { out << YAML::Key << s_FieldName_ArpMaxVersion << YAML::Value << version.ArpMaxVersion.value(); } out << YAML::Key << s_FieldName_RelativePath << YAML::Value << version.ManifestRelativePath; out << YAML::Key << s_FieldName_Sha256Hash << YAML::Value << version.ManifestHash; out << YAML::EndMap; } out << YAML::EndSeq; out << YAML::EndMap; return out.str(); } void PackageVersionDataManifest::Deserialize(std::string_view input) { AICLI_LOG_LARGE_STRING(Core, Verbose, << "PackageVersionDataManifest deserializing:", input); YAML::Node document = YAML::Load(input); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !document.IsMap()); const YAML::Node& schemaVersionNode = document.GetChildNode(s_FieldName_SchemaVersion); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, !schemaVersionNode.IsScalar()); Utility::Version schemaVersion{ schemaVersionNode.as() }; if (schemaVersion.PartAt(0).Integer == 1) { anon::Deserialize_1_0(document, *this); } else { THROW_HR(APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION); } } void PackageVersionDataManifest::Deserialize(const std::vector& input) { Deserialize(std::string_view{ reinterpret_cast(input.data()), input.size() }); } } ================================================ FILE: src/AppInstallerCommonCore/PathVariable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/PathVariable.h" #include using namespace AppInstaller::Utility; namespace AppInstaller::Registry::Environment { namespace { constexpr std::wstring_view s_PathName = L"Path"; constexpr std::wstring_view s_PathSubkey_User = L"Environment"; constexpr std::wstring_view s_PathSubkey_Machine = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; void EnsurePathValueEndsWithSemicolon(std::string& value) { if (value.back() != ';') { value += ';'; } } std::string ExpandPathValue(const std::string& value) { std::string result; std::vector pathEntries = Split(value, ';'); for (std::string& pathEntry : pathEntries) { if (!pathEntry.empty()) { result += AppInstaller::Filesystem::GetExpandedPath(pathEntry).u8string(); result += ';'; } } return result; } } PathVariable::PathVariable(Manifest::ScopeEnum scope, bool readOnly) : m_scope(scope), m_readOnly(readOnly) { if (m_readOnly) { if (m_scope == Manifest::ScopeEnum::Machine) { m_key = Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, std::wstring{ s_PathSubkey_Machine }); } else { m_key = Registry::Key::OpenIfExists(HKEY_CURRENT_USER, std::wstring{ s_PathSubkey_User }); } } else { if (m_scope == Manifest::ScopeEnum::Machine) { m_key = Registry::Key::Create(HKEY_LOCAL_MACHINE, std::wstring{ s_PathSubkey_Machine }); } else { m_key = Registry::Key::Create(HKEY_CURRENT_USER, std::wstring{ s_PathSubkey_User }); } } } std::string PathVariable::GetPathValue() { std::wstring pathName = std::wstring{ s_PathName }; return Normalize(m_key[pathName]->GetValue()); } bool PathVariable::Contains(const std::filesystem::path& target) { std::string targetString = Normalize(target.u8string()); return (GetPathValue().find(targetString) != std::string::npos); } bool PathVariable::Remove(const std::filesystem::path& target) { THROW_HR_IF(E_ACCESSDENIED, m_readOnly); if (Contains(target)) { std::string targetString = Normalize(target.u8string()); std::string pathValue = GetPathValue(); FindAndReplace(pathValue, targetString, ""); FindAndReplace(pathValue, ";;", ";"); SetPathValue(pathValue); return true; } else { return false; } } bool PathVariable::Append(const std::filesystem::path& target) { THROW_HR_IF(E_ACCESSDENIED, m_readOnly); if (!Contains(target)) { std::string targetString = Normalize(target.u8string()); std::string pathValue = GetPathValue(); EnsurePathValueEndsWithSemicolon(pathValue); pathValue += targetString; EnsurePathValueEndsWithSemicolon(pathValue); SetPathValue(pathValue); return true; } else { return false; } } void PathVariable::SetPathValue(const std::string& value) { THROW_HR_IF(E_ACCESSDENIED, m_readOnly); std::wstring pathName = std::wstring{ s_PathName }; m_key.SetValue(pathName, ConvertToUTF16(value), REG_EXPAND_SZ); SendNotifyMessageW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Environment")); } bool RefreshPathVariableForCurrentProcess() { // Path values must be expanded before assigning to process environment for proper refresh. std::string systemPathValue = ExpandPathValue(PathVariable(Manifest::ScopeEnum::Machine, true).GetPathValue()); std::string userPathValue = ExpandPathValue(PathVariable(Manifest::ScopeEnum::User, true).GetPathValue()); std::wstring pathValue = ConvertToUTF16(systemPathValue + userPathValue); return _wputenv_s(L"PATH", pathValue.c_str()) == 0; } } ================================================ FILE: src/AppInstallerCommonCore/Pin.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/Pin.h" using namespace AppInstaller::Utility; namespace AppInstaller::Pinning { using namespace std::string_view_literals; namespace { // Source ID to use for the installed source; it does not match any installed source. // This does match with the actual ID of the source, but it doesn't really matter // as it is handled specially when we see it. constexpr std::string_view s_installedSourceId = "*PredefinedInstalledSource"sv; } PinType ConvertToPinTypeEnum(std::string_view in) { if (Utility::CaseInsensitiveEquals(in, "Blocking"sv)) { return PinType::Blocking; } else if (Utility::CaseInsensitiveEquals(in, "Pinning"sv)) { return PinType::Pinning; } else if (Utility::CaseInsensitiveEquals(in, "Gating"sv)) { return PinType::Gating; } else if (Utility::CaseInsensitiveEquals(in, "PinnedByManifest"sv)) { return PinType::PinnedByManifest; } else { return PinType::Unknown; } } std::string_view ToString(PinType type) { switch (type) { case PinType::Blocking: return "Blocking"sv; case PinType::Pinning: return "Pinning"sv; case PinType::Gating: return "Gating"sv; case PinType::PinnedByManifest: return "PinnedByManifest"sv; case PinType::Unknown: default: return "Unknown"; } } bool IsStricter(PinType first, PinType second) { return first > second; } PinType Stricter(PinType first, PinType second) { return IsStricter(first, second) ? first : second; } std::string PinKey::ToString() const { std::stringstream ss; ss << "Package=[" << PackageId << "] Source=[" << SourceId << "]"; return ss.str(); } PinKey PinKey::GetPinKeyForInstalled(std::string_view systemReferenceString) { return { systemReferenceString, s_installedSourceId }; } bool PinKey::IsForInstalled() const { return SourceId == s_installedSourceId; } std::string Pin::ToString() const { std::stringstream ss; ss << m_key.ToString() << " Type=[" << Pinning::ToString(m_type) << ']'; if (m_type == PinType::Gating) { ss << " GatedVersion=[" << m_gatedVersion.ToString() << ']'; } return ss.str(); } Pin Pin::CreateBlockingPin(PinKey&& pinKey) { return { PinType::Blocking, std::move(pinKey) }; } Pin Pin::CreatePinningPin(PinKey&& pinKey) { return { PinType::Pinning, std::move(pinKey) }; } Pin Pin::CreateGatingPin(PinKey&& pinKey, GatedVersion&& gatedVersion) { return { PinType::Gating, std::move(pinKey), std::move(gatedVersion) }; } bool Pin::operator==(const Pin& other) const { return m_type == other.m_type && m_key == other.m_key && m_gatedVersion == other.m_gatedVersion; } } ================================================ FILE: src/AppInstallerCommonCore/PortableARPEntry.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/PortableARPEntry.h" #include "winget/Manifest.h" using namespace AppInstaller::Utility; #define VALUENAMECASE(valueName) case PortableValueName::valueName: return s_##valueName; namespace AppInstaller::Registry::Portable { namespace { constexpr std::wstring_view s_UninstallRegistryX64 = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; constexpr std::wstring_view s_UninstallRegistryX86 = L"Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; constexpr std::wstring_view s_DisplayName = L"DisplayName"; constexpr std::wstring_view s_DisplayVersion = L"DisplayVersion"; constexpr std::wstring_view s_Publisher = L"Publisher"; constexpr std::wstring_view s_InstallDate = L"InstallDate"; constexpr std::wstring_view s_URLInfoAbout = L"URLInfoAbout"; constexpr std::wstring_view s_HelpLink = L"HelpLink"; constexpr std::wstring_view s_UninstallString = L"UninstallString"; constexpr std::wstring_view s_WinGetInstallerType = L"WinGetInstallerType"; constexpr std::wstring_view s_InstallLocation = L"InstallLocation"; constexpr std::wstring_view s_PortableTargetFullPath = L"TargetFullPath"; constexpr std::wstring_view s_PortableSymlinkFullPath = L"SymlinkFullPath"; constexpr std::wstring_view s_SHA256 = L"SHA256"; constexpr std::wstring_view s_WinGetPackageIdentifier = L"WinGetPackageIdentifier"; constexpr std::wstring_view s_WinGetSourceIdentifier = L"WinGetSourceIdentifier"; constexpr std::wstring_view s_InstallDirectoryCreated = L"InstallDirectoryCreated"; constexpr std::wstring_view s_InstallDirectoryAddedToPath = L"InstallDirectoryAddedToPath"; } PortableARPEntry::PortableARPEntry(Manifest::ScopeEnum scope, Utility::Architecture arch, const std::string& productCode) { m_scope = scope; m_arch = arch; m_productCode = productCode; if (m_scope == Manifest::ScopeEnum::Machine) { m_root = HKEY_LOCAL_MACHINE; if (m_arch == Utility::Architecture::X64) { m_subKey = s_UninstallRegistryX64; m_samDesired = KEY_WOW64_64KEY; } else { m_subKey = s_UninstallRegistryX86; m_samDesired = KEY_WOW64_32KEY; } } else { // HKCU uninstall registry share the x64 registry view. m_root = HKEY_CURRENT_USER; m_subKey = s_UninstallRegistryX64; m_samDesired = KEY_WOW64_64KEY; } m_subKey += L"\\" + ConvertToUTF16(m_productCode); m_key = Key::OpenIfExists(m_root, m_subKey, 0, KEY_ALL_ACCESS); if (m_key != NULL) { m_exists = true; } else { m_exists = false; m_key = Key::Create(m_root, m_subKey); } } std::wstring_view ToString(PortableValueName valueName) { switch (valueName) { VALUENAMECASE(DisplayName); VALUENAMECASE(DisplayVersion); VALUENAMECASE(Publisher); VALUENAMECASE(InstallDate); VALUENAMECASE(URLInfoAbout); VALUENAMECASE(HelpLink); VALUENAMECASE(UninstallString); VALUENAMECASE(WinGetInstallerType); VALUENAMECASE(InstallLocation); VALUENAMECASE(PortableTargetFullPath); VALUENAMECASE(PortableSymlinkFullPath); VALUENAMECASE(SHA256); VALUENAMECASE(WinGetPackageIdentifier); VALUENAMECASE(WinGetSourceIdentifier); VALUENAMECASE(InstallDirectoryCreated); VALUENAMECASE(InstallDirectoryAddedToPath); default: return {}; } } std::optional PortableARPEntry::operator[](PortableValueName valueName) const { return m_key[std::wstring{ ToString(valueName) }]; } void PortableARPEntry::SetValue(PortableValueName valueName, const std::wstring& value) { m_key.SetValue(std::wstring{ ToString(valueName) }, value, REG_SZ); } void PortableARPEntry::SetValue(PortableValueName valueName, const std::string_view& value) { m_key.SetValue(std::wstring{ ToString(valueName) }, ConvertToUTF16(value), REG_SZ); } void PortableARPEntry::SetValue(PortableValueName valueName, bool& value) { m_key.SetValue(std::wstring{ ToString(valueName) }, value); } void PortableARPEntry::Delete() { Registry::Key::Delete(m_root, m_subKey, m_samDesired); } } ================================================ FILE: src/AppInstallerCommonCore/Progress.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerProgress.h" #include namespace AppInstaller { HRESULT ToHRESULT(CancelReason reason) { HRESULT hr = E_ABORT; switch (reason) { case CancelReason::CtrlCSignal: hr = APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED; break; case CancelReason::AppShutdown: hr = APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED; break; } return hr; } ProgressCallback::ProgressCallback(IProgressSink* sink) : m_sink(sink) { } void ProgressCallback::BeginProgress() { IProgressSink* sink = GetSink(); if (sink) { sink->BeginProgress(); } } void ProgressCallback::OnProgress(uint64_t current, uint64_t maximum, ProgressType type) { IProgressSink* sink = GetSink(); if (sink) { sink->OnProgress(current, maximum, type); } } void ProgressCallback::SetProgressMessage(std::string_view message) { IProgressSink* sink = GetSink(); if (sink) { sink->SetProgressMessage(message); } } void ProgressCallback::EndProgress(bool hideProgressWhenDone) { IProgressSink* sink = GetSink(); if (sink) { sink->EndProgress(hideProgressWhenDone); } }; bool ProgressCallback::IsCancelledBy(CancelReason cancelReasons) { THROW_HR_IF(E_UNEXPECTED, cancelReasons == CancelReason::None); return WI_IsAnyFlagSet(cancelReasons, m_cancelReason); } [[nodiscard]] IProgressCallback::CancelFunctionRemoval ProgressCallback::SetCancellationFunction(std::function&& f) { m_cancellationFunction = std::move(f); if (m_cancellationFunction) { return IProgressCallback::CancelFunctionRemoval(this); } else { return {}; } } bool ProgressCallback::Wait(IProgressCallback& progress, std::chrono::milliseconds millisecondsToWait) { wil::unique_event calledEvent; calledEvent.create(); auto cancellationFunc = progress.SetCancellationFunction([&calledEvent]() { calledEvent.SetEvent(); }); if (calledEvent.wait(static_cast(millisecondsToWait.count()))) { return false; } return true; } void ProgressCallback::Cancel(CancelReason reason) { m_cancelReason = reason; if (m_cancellationFunction) { m_cancellationFunction(); } } IProgressSink* ProgressCallback::GetSink() { return m_sink.load(); } PartialPercentProgressCallback::PartialPercentProgressCallback(IProgressCallback& baseCallback, uint64_t globalMax) : m_baseCallback(baseCallback), m_globalMax(globalMax) { } void PartialPercentProgressCallback::BeginProgress() { THROW_HR(E_NOTIMPL); } void PartialPercentProgressCallback::OnProgress(uint64_t current, uint64_t maximum, ProgressType type) { THROW_HR_IF(E_UNEXPECTED, ProgressType::Percent != type); m_baseCallback.OnProgress(m_rangeMin + (m_rangeMax - m_rangeMin) * current / maximum, m_globalMax, type); } void PartialPercentProgressCallback::SetProgressMessage(std::string_view message) { m_baseCallback.SetProgressMessage(message); } void PartialPercentProgressCallback::EndProgress(bool) { THROW_HR(E_NOTIMPL); } IProgressCallback::CancelFunctionRemoval PartialPercentProgressCallback::SetCancellationFunction(std::function&& f) { return m_baseCallback.SetCancellationFunction(std::move(f)); } void PartialPercentProgressCallback::SetRange(uint64_t rangeMin, uint64_t rangeMax) { THROW_HR_IF(E_INVALIDARG, rangeMin > rangeMax || rangeMax > m_globalMax); m_rangeMin = rangeMin; m_rangeMax = rangeMax; } } ================================================ FILE: src/AppInstallerCommonCore/PropertySheet.props ================================================ ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::Utility { static const int InapplicableArchitecture = -1; enum class Architecture { Unknown = -1, Neutral, X86, X64, Arm, Arm64 }; // Converts a string to corresponding enum Architecture ConvertToArchitectureEnum(std::string_view archStr); // Converts an ProcessorArchitecture to an Architecture std::optional ConvertToArchitectureEnum(winrt::Windows::System::ProcessorArchitecture architecture); // Converts an Architecture to a string_view LocIndView ToString(Architecture architecture); // Gets the system's architecture as Architecture enum AppInstaller::Utility::Architecture GetSystemArchitecture(); // Gets the set of architectures that are applicable to the current system const std::vector& GetApplicableArchitectures(); // Gets the set of architectures that are supported by winget const std::vector& GetAllArchitectures(); // Gets if an architecture is applicable to the system // Returns the priority in the applicable architecture list if the architecture is applicable. 0 has lowest priority. // Returns -1 if the architecture is not applicable. int IsApplicableArchitecture(Architecture arch); // Gets if an architecture is applicable to the given list // Returns the priority in the applicable architecture list if the architecture is applicable. 0 has lowest priority. // Returns -1 if the architecture is not applicable. int IsApplicableArchitecture(Architecture arch, const std::vector& allowedArchitectures); } ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerDeployment.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Deployment { // A set of optional values useful across many of the deployment functions. struct Options { Options() = default; explicit Options(bool skipReputationCheck) : SkipReputationCheck(skipReputationCheck) {} // Avoid using APIs that make a reputation check. bool SkipReputationCheck = false; // The pairs of URI+Digest to enforce. std::vector> ExpectedDigests; }; // Calls winrt::Windows::Management::Deployment::PackageManager::AddPackageAsync if skipSmartScreen is true, // Otherwise, calls winrt::Windows::Management::Deployment::PackageManager::RequestAddPackageAsync void AddPackage( const winrt::Windows::Foundation::Uri& uri, const Options& options, IProgressCallback& callback); // Calls winrt::Windows::Management::Deployment::PackageManager::AddPackageAsync if skipSmartScreen is true, // Otherwise, calls winrt::Windows::Management::Deployment::PackageManager::RequestAddPackageAsync. // If the Add function fails due to the package being in use, we fall back to stage and register, which allows // a deferred registration. // Returns true if the registration was deferred; false if not. bool AddPackageWithDeferredFallback( std::string_view uri, const Options& options, IProgressCallback& callback); // Calls winrt::Windows::Management::Deployment::PackageManager::RemovePackageAsync void RemovePackage( std::string_view packageFullName, winrt::Windows::Management::Deployment::RemovalOptions options, IProgressCallback& callback); // Calls winrt::Windows::Management::Deployment::PackageManager::StagePackageAsync // winrt::Windows::Management::Deployment::PackageManager::ProvisionPackageForAllUsersAsync // winrt::Windows::Management::Deployment::PackageManager::RegisterPackageByFullNameAsync if not running as system bool AddPackageMachineScope( std::string_view uri, const Options& options, IProgressCallback& callback); // Calls winrt::Windows::Management::Deployment::PackageManager::DeprovisionPackageForAllUsersAsync // winrt::Windows::Management::Deployment::PackageManager::RemovePackageAsync with RemoveForAllUsers void RemovePackageMachineScope( std::string_view packageFamilyName, std::string_view packageFullName, IProgressCallback& callback); // Calls winrt::Windows::Management::Deployment::PackageManager::FindPackagesForUser bool IsRegistered(std::string_view packageFamilyName); // Calls winrt::Windows::Management::Deployment::PackageManager::RegisterPackageByFamilyNameAsync void RegisterPackage( std::string_view packageFamilyName, IProgressCallback& callback); // Determines if the ExpectedDigests property (and thus feture) is supported on the current version of Windows. bool IsExpectedDigestsSupported(); } ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerDownloader.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; namespace AppInstaller::Utility { // The type of data being downloaded; determines what code should // be used when downloading. enum class DownloadType { Index, Manifest, WinGetUtil, Installer, InstallerMetadataCollectionInput, ConfigurationFile, }; struct DownloadRequestHeader { std::string Name; std::string Value; bool IsAuth = false; }; // Extra metadata about a download for use by certain downloaders (Delivery Optimization for instance). // Extra download request headers. struct DownloadInfo { std::string DisplayName; std::string ContentId; std::vector RequestHeaders; }; // Properties about the downloaded file. struct DownloadResult { std::vector Sha256Hash; uint64_t SizeInBytes = 0; std::optional ContentType; }; // An exception that indicates that a remote service is too busy/unavailable and may contain data on when to try again. struct ServiceUnavailableException : public wil::ResultException { ServiceUnavailableException(std::chrono::seconds retryAfter = 0s) : wil::ResultException(APPINSTALLER_CLI_ERROR_SERVICE_UNAVAILABLE), m_retryAfter(retryAfter) {} std::chrono::seconds RetryAfter() const { return m_retryAfter; } private: std::chrono::seconds m_retryAfter; }; // Downloads a file from the given URL and places it in the given location. // url: The url to be downloaded from. http->https redirection is allowed. // dest: The stream to be downloaded to. // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. // downloadInfo: Optional. Currently only used by DO to identify the download. DownloadResult DownloadToStream( const std::string& url, std::ostream& dest, DownloadType type, IProgressCallback& progress, std::optional downloadInfo = {}); // Downloads a file from the given URL and places it in the given location. // url: The url to be downloaded from. http->https redirection is allowed. // dest: The path to local file to be downloaded to. // computeHash: Optional. Indicates if SHA256 hash should be calculated when downloading. // downloadInfo: Optional. Currently only used by DO to identify the download. DownloadResult Download( const std::string& url, const std::filesystem::path& dest, DownloadType type, IProgressCallback& progress, std::optional downloadInfo = {}); // Gets the headers for the given URL. std::map GetHeaders(std::string_view url); // Determines if the given url is a remote location. bool IsUrlRemote(std::string_view url); // Determines if the given url is secured. bool IsUrlSecure(std::string_view url); // Apply Mark of the web if the target file is on NTFS, otherwise does nothing. void ApplyMotwIfApplicable(const std::filesystem::path& filePath, URLZONE zone); // Remove Mark of the web if the target file is on NTFS, otherwise does nothing. void RemoveMotwIfApplicable(const std::filesystem::path& filePath); // Apply Mark of the web using IAttachmentExecute::Save if the target file is on NTFS, otherwise does nothing. // This method only does a best effort since Attachment Execution Service may be disabled. // If IAttachmentExecute::Save is successfully invoked and the scan failed, the failure HRESULT is returned. // zoneIfScanFailure: URLZONE to apply if IAttachmentExecute::Save scan failed. HRESULT ApplyMotwUsingIAttachmentExecuteIfApplicable(const std::filesystem::path& filePath, const std::string& source, URLZONE zoneIfScanFailure); // Function to read-only create a stream from a uri string (url address or file system path) ::Microsoft::WRL::ComPtr GetReadOnlyStreamFromURI(std::string_view uriStr); // Gets the retry after value in terms of a delay in seconds. std::chrono::seconds GetRetryAfter(const std::wstring& retryAfter); // Gets the retry after value in terms of a delay in seconds. std::chrono::seconds GetRetryAfter(const winrt::Windows::Web::Http::HttpResponseMessage& response); // Data about the cache-control header. struct CacheControlPolicy { // Limit max age to a year static constexpr unsigned long long MaximumMaxAge = 60 * 60 * 24 * 365; CacheControlPolicy() = default; CacheControlPolicy(std::wstring_view header); // True only if the cache-control header was present and contained at least one directive. bool Present = false; // The max-age directive; in seconds. unsigned long long MaxAge = 0; // The no-cache directive; indicates that the cache should always revalidate. bool NoCache = false; // The no-store directive; indicates that the data should not be cached. bool NoStore = false; // The public directive; indicates that the data is not user specific. bool Public = false; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerFileLogger.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Logging { // Logs to a file. struct FileLogger : public ILogger { FileLogger(); explicit FileLogger(const std::filesystem::path& filePath); explicit FileLogger(const std::string_view fileNamePrefix); ~FileLogger(); FileLogger(const FileLogger&) = delete; FileLogger& operator=(const FileLogger&) = delete; FileLogger(FileLogger&&) = default; FileLogger& operator=(FileLogger&&) = default; // The default value for the maximum size comes from settings. // Setting the maximum size to 0 will disable the maximum. FileLogger& SetMaximumSize(std::ofstream::off_type maximumSize); static std::string GetNameForPath(const std::filesystem::path& filePath); static std::string_view DefaultPrefix(); static std::string_view DefaultExt(); // ILogger std::string GetName() const override; void Write(Channel channel, Level level, std::string_view message) noexcept override; void WriteDirect(Channel channel, Level level, std::string_view message) noexcept override; void SetTag(Tag tag) noexcept override; // Adds a FileLogger to the current Log static void Add(); static void Add(const std::filesystem::path& filePath); static void Add(std::string_view fileNamePrefix); // Starts a background task to clean up old log files. static void BeginCleanup(); static void BeginCleanup(const std::filesystem::path& filePath); private: std::string m_name; std::filesystem::path m_filePath; std::ofstream m_stream; std::ofstream::pos_type m_headersEnd = 0; std::ofstream::off_type m_maximumSize = 0; void OpenFileLoggerStream(); // Initializes the default maximum file size. void InitializeDefaultMaximumFileSize(); // Determines if the logger needs to wrap back to the beginning, doing so when needed. // May also shrink the given view if it exceeds the overall maximum. void HandleMaximumFileSize(std::string_view& currentLog); // Resets the log file state so that it will overwrite the data portion. void WrapLogFile(); }; } ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerMsixInfo.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AppInstallerProgress.h" #include "winget/ManagedFile.h" #include "winget/Manifest.h" #include "winget/MsixManifest.h" #include #include #include #include #include #include #include #include #include namespace AppInstaller::Msix { // Function to create an AppxBundle package reader given the input file name. // Returns true if success, false if the input stream is of wrong type. bool GetBundleReader( IStream* inputStream, IAppxBundleReader** reader); // Function to create an Appx package reader given the input file name. // Returns true if success, false if the input stream is of wrong type. bool GetPackageReader( IStream* inputStream, IAppxPackageReader** reader); // Function to create an Appx manifest reader given the input file name. void GetManifestReader( IStream* inputStream, IAppxManifestReader** reader); // Gets the package full name from the family name. // This will be the one registered for the current user, if any. std::optional GetPackageFullNameFromFamilyName(std::string_view familyName); // Gets the package family name from the given full name. std::string GetPackageFamilyNameFromFullName(std::string_view fullName); // Gets the package location from the given full name. std::optional GetPackageLocationFromFullName(std::string_view fullName); struct PackageIdInfo { std::string Name; AppInstaller::Utility::UInt64Version Version; }; // Gets the package id info from the given full name. PackageIdInfo GetPackageIdInfoFromFullName(std::string_view fullName); // MsixInfo class handles all appx/msix related query. struct MsixInfo { MsixInfo(std::string_view uriStr); template, int> = 0> MsixInfo(const T& path) : MsixInfo(path.u8string()) {} MsixInfo(const MsixInfo&) = default; MsixInfo& operator=(const MsixInfo&) = default; MsixInfo(MsixInfo&&) = default; MsixInfo& operator=(MsixInfo&&) = default; inline bool GetIsBundle() { return m_isBundle; } // Full content of AppxSignature.p7x // If skipP7xFileId is true, returns content of converted .p7s std::vector GetSignature(bool skipP7xFileId = false); // Gets the signature sha256 hash. Utility::SHA256::HashBuffer GetSignatureHash(); // Gets the digest of the package. std::wstring GetDigest(); // Gets the package full name. std::wstring GetPackageFullNameWide(); std::string GetPackageFullName(); // Gets a value indicating whether the referenced info is newer than the given package. bool IsNewerThan(const std::filesystem::path& otherPackage); bool IsNewerThan(const winrt::Windows::ApplicationModel::PackageVersion& otherVersion); // Writes the package file to the given path. void WriteToFile(std::string_view packageFile, const std::filesystem::path& target, IProgressCallback& progress); // Writes the package's manifest to the given path. void WriteManifestToFile(const std::filesystem::path& target, IProgressCallback& progress); // Writes the package file to the given file handle. void WriteToFileHandle(std::string_view packageFile, HANDLE target, IProgressCallback& progress); // Get application package manifests from msix and msixbundle. std::vector GetAppPackageManifests(bool includeStub = false) const; private: bool m_isBundle; Microsoft::WRL::ComPtr m_stream; Microsoft::WRL::ComPtr m_bundleReader; Microsoft::WRL::ComPtr m_packageReader; // Get application packages. Ignore stub packages if any. std::vector> GetAppPackages(bool includeStub = false) const; }; struct GetCertContextResult { wil::unique_cert_context CertContext; wil::unique_hcertstore CertStore; }; // Get cert context from a signed msix/msixbundle file. GetCertContextResult GetCertContextFromMsix(const std::filesystem::path& msixPath); struct WriteLockedMsixFile { WriteLockedMsixFile(const std::filesystem::path& path); bool ValidateTrustInfo(bool checkMicrosoftOrigin) const; private: Utility::ManagedFile m_file; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerProgress.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller { // Forward declaration struct ProgressCallback; struct IProgressCallback; namespace details { // For SetCancellationFunction return. inline void RemoveCancellationFunction(IProgressCallback* callback); } // The semantic meaning of the progress values. enum class ProgressType : uint32_t { // Progress will not be sent. None, Bytes, Percent, }; // The reason why progress is cancelled. enum class CancelReason : uint32_t { None = 0x0, Abort = 0x1, CtrlCSignal = 0x2, User = Abort | CtrlCSignal, AppShutdown = 0x4, Any = 0xFFFFFFFF }; DEFINE_ENUM_FLAG_OPERATORS(CancelReason); // Gets the HRESULT associated with the given reason. HRESULT ToHRESULT(CancelReason reason); // Interface that provides a callback to inform of cancellation. struct ICancellable { // Inform of cancellation with provided reason. // When `force` is true, it is expected to happen regardless of user intent. virtual void Cancel(CancelReason reason, bool force) = 0; }; // Interface that only receives progress, and does not participate in cancellation. // This allows a sink be simple, and let ProgressCallback handle the complications // of cancel state. struct IProgressSink { // Called as progress is made. // If maximum is 0, the maximum is unknown. virtual void OnProgress(uint64_t current, uint64_t maximum, ProgressType type) = 0; // Sets a message for the current progress state. virtual void SetProgressMessage(std::string_view message) = 0; // Called as progress begins. virtual void BeginProgress() = 0; // Called as progress ends. virtual void EndProgress(bool hideProgressWhenDone) = 0; }; // Callback interface given to the worker to report to. // Also enables the caller to request cancellation. struct IProgressCallback : public IProgressSink { using CancelFunctionRemoval = wil::unique_any; // Returns a value indicating if the future has been cancelled. virtual bool IsCancelledBy(CancelReason cancelReasons) = 0; // Sets a cancellation function that will be called when the operation is to be cancelled. [[nodiscard]] virtual CancelFunctionRemoval SetCancellationFunction(std::function&& f) = 0; }; // Implementation of IProgressCallback. struct ProgressCallback : public IProgressCallback { ProgressCallback() = default; ProgressCallback(IProgressSink* sink); static bool Wait(IProgressCallback& progress, std::chrono::milliseconds ms); void BeginProgress() override; void OnProgress(uint64_t current, uint64_t maximum, ProgressType type) override; void SetProgressMessage(std::string_view message) override; void EndProgress(bool hideProgressWhenDone) override; bool IsCancelledBy(CancelReason cancelReasons) override; [[nodiscard]] IProgressCallback::CancelFunctionRemoval SetCancellationFunction(std::function&& f) override; void Cancel(CancelReason reason = CancelReason::Abort); IProgressSink* GetSink(); private: std::atomic m_sink = nullptr; std::function m_cancellationFunction; CancelReason m_cancelReason = CancelReason::None; }; // A progress callback that reports its progress as a partial range of percentage to its base progress callback struct PartialPercentProgressCallback : public ProgressCallback { PartialPercentProgressCallback(IProgressCallback& baseCallback, uint64_t globalMax); void BeginProgress() override; void OnProgress(uint64_t current, uint64_t maximum, ProgressType type) override; void SetProgressMessage(std::string_view message) override; void EndProgress(bool hideProgressWhenDone) override; [[nodiscard]] IProgressCallback::CancelFunctionRemoval SetCancellationFunction(std::function&& f) override; void SetRange(uint64_t rangeMin, uint64_t rangeMax); private: IProgressCallback& m_baseCallback; uint64_t m_rangeMin = 0; uint64_t m_rangeMax = 0; uint64_t m_globalMax = 0; }; namespace details { inline void RemoveCancellationFunction(IProgressCallback* callback) { (void)callback->SetCancellationFunction(nullptr); } } } ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerRuntime.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include namespace AppInstaller::Runtime { // Sets the runtime path state name globally. void SetRuntimePathStateName(std::string name); // A path to be retrieved based on the runtime. enum class PathName { // The temporary file location. Temp, // The local state (file) storage location. LocalState, // The default location where log files are located. DefaultLogLocation, // The location that standard type settings are stored. // In a packaged context, this returns a prepend value for the container name. StandardSettings, // The location that user file type settings are stored. UserFileSettings, // The location where secure settings data is stored (for reading). SecureSettingsForRead, // The location where secure settings data is stored (for writing). SecureSettingsForWrite, // The value of %USERPROFILE%. UserProfile, // The location where portable packages are installed to with user scope. PortablePackageUserRoot, // The location where portable packages are installed to with machine scope. PortablePackageMachineRoot, // The location where portable packages are installed to with machine scope (x86). PortablePackageMachineRootX86, // The location where symlinks to portable packages are stored under user scope. PortableLinksUserLocation, // The location where symlinks to portable packages are stored under machine scope. PortableLinksMachineLocation, // The root location for the package containing the winget application. SelfPackageRoot, // The location where user downloads are stored. UserProfileDownloads, // The location where configuration modules are stored. ConfigurationModules, // The location where checkpoints are stored. CheckpointsLocation, // The location of the CLI executable file. CLIExecutable, // The directory containing the CLI executable file. MCPExecutable, // The location of the image assets, if it exists. ImageAssets, // The location where fonts are installed with user scope. FontsUserInstallLocation, // The location where fonts are installed with machine scope. FontsMachineInstallLocation, // The location that standard type settings are stored in files. StandardFileSettings, // Always one more than the last path; for being able to iterate paths in tests. Max }; // Gets the PathDetails used for the given path. // This is exposed primarily to allow for testing, GetPathTo should be preferred. Filesystem::PathDetails GetPathDetailsFor(PathName path, bool forDisplay = false); // Gets the path to the requested location. inline std::filesystem::path GetPathTo(PathName path, bool forDisplay = false) { return Filesystem::GetPathTo(path, forDisplay); } // Replaces the substring in the path with the user profile environment variable. void ReplaceProfilePathsWithEnvironmentVariable(std::filesystem::path& path); // Gets a new temp file path. std::filesystem::path GetNewTempFilePath(); // Determines whether developer mode is enabled. bool IsDevModeEnabled(); // Gets the default user agent string for the Windows Package Manager. Utility::LocIndString GetDefaultUserAgent(); // Gets the user agent string from passed in caller for the Windows Package Manager. Utility::LocIndString GetUserAgent(std::string_view caller); } ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerSynchronization.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include using namespace std::chrono_literals; namespace AppInstaller::Synchronization { // This is a standard named mutex. // It must be acquired and released (or destroyed) on the same thread, just as all Windows mutexes must be. struct CrossProcessLock { CrossProcessLock(std::string_view name); CrossProcessLock(const std::wstring& name); ~CrossProcessLock(); CrossProcessLock(const CrossProcessLock&) = delete; CrossProcessLock& operator=(const CrossProcessLock&) = delete; CrossProcessLock(CrossProcessLock&&) = default; CrossProcessLock& operator=(CrossProcessLock&&) = default; // Acquires the lock; cancellation is enabled via the progress object. // Returns true when the lock is acquired and false if the wait is cancelled. bool Acquire(IProgressCallback& progress); // Optionally release the lock before destroying the object. void Release(); // Attempts to acquire the mutex without a wait. // Returns true if it was able, false if not. bool TryAcquireNoWait(); // Indicates whether the lock is held. operator bool() const; private: wil::unique_mutex m_mutex; wil::mutex_release_scope_exit m_lock; DWORD m_lockThreadId = 0; }; // This lock is used to prevent multiple winget related processes from attempting to install (or uninstall) at the same time. // It must be acquired and released (or destroyed) on the same thread, just as all Windows mutexes must be. struct CrossProcessInstallLock : public CrossProcessLock { CrossProcessInstallLock() : CrossProcessLock(L"WinGetCrossProcessInstallLock") {} }; } ================================================ FILE: src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Settings { struct UserSettings; } namespace AppInstaller::Logging { enum class FailureTypeEnum : UINT32 { None = 0x0, // Failure type from FailureInfo in result_macros.h ResultException = 0x1, // THROW_... ResultReturn = 0x2, // RETURN_..._LOG or RETURN_..._MSG ResultLog = 0x3, // LOG_... ResultFailFast = 0x4, // FAIL_FAST_... // Other failure types from LogException() Unknown = 0x10000, WinrtHResultError = 0x10001, ResourceOpen = 0x10002, StdException = 0x10003, // Command termination CommandTermination = 0x20000, }; // Contains all fields logged through the TelemetryTraceLogger. Last write wins. // This will be used to report a summary event upon destruction of the TelemetryTraceLogger. struct TelemetrySummary { TelemetrySummary() = default; // Selectively copy member fields for copy constructor; TelemetrySummary(const TelemetrySummary& other); TelemetrySummary& operator=(const TelemetrySummary&) = default; TelemetrySummary(TelemetrySummary&&) = default; TelemetrySummary& operator=(TelemetrySummary&&) = default; // Log wil failure, exception, command termination HRESULT FailureHResult = S_OK; std::wstring FailureMessage; std::string FailureModule; UINT32 FailureThreadId = 0; FailureTypeEnum FailureType = FailureTypeEnum::None; std::string FailureFile; UINT32 FailureLine = 0; // LogStartup bool IsCOMCall = false; // LogCommand std::string Command; // LogCommandSuccess bool CommandSuccess = false; // LogIsManifestLocal bool IsManifestLocal = false; // LogManifestFields, LogAppFound std::string PackageIdentifier; std::string PackageName; std::string PackageVersion; std::string Channel; std::string SourceIdentifier; // LogSelectedInstaller INT32 InstallerArchitecture = -1; std::string InstallerUrl; std::string InstallerType; std::string InstallerScope; std::string InstallerLocale; // LogSearchRequest std::string SearchType; std::string SearchQuery; std::string SearchId; std::string SearchName; std::string SearchMoniker; std::string SearchTag; std::string SearchCommand; UINT64 SearchMaximum = 0; std::string SearchRequest; // LogSearchResultCount UINT64 SearchResultCount = 0; // LogInstallerHashMismatch std::vector HashMismatchExpected; std::vector HashMismatchActual; bool HashMismatchOverride = false; uint64_t HashMismatchActualSize = 0; std::string HashMismatchContentType; // LogInstallerFailure std::string InstallerExecutionType; UINT32 InstallerErrorCode = 0; // LogUninstallerFailure std::string UninstallerExecutionType; UINT32 UninstallerErrorCode = 0; // LogRepairFailure std::string RepairExecutionType; UINT32 RepairErrorCode = 0; // LogSuccessfulInstallARPChange UINT64 ChangesToARP = 0; UINT64 MatchesInARP = 0; UINT64 ChangesThatMatch = 0; UINT64 ARPLanguage = 0; std::string ARPName; std::string ARPVersion; std::string ARPPublisher; // LogNonFatalDOError std::string DOUrl; HRESULT DOHResult = S_OK; }; // This type contains the registration lifetime of the telemetry trace logging provider. // Due to the nature of trace logging, specific methods should be added per desired trace. // As there should not be a significantly large number of individual telemetry events, // this should not become a burden. struct TelemetryTraceLogger { TelemetryTraceLogger(bool useSummary = true); ~TelemetryTraceLogger(); TelemetryTraceLogger(const TelemetryTraceLogger&) = default; TelemetryTraceLogger& operator=(const TelemetryTraceLogger&) = default; TelemetryTraceLogger(TelemetryTraceLogger&&) = default; TelemetryTraceLogger& operator=(TelemetryTraceLogger&&) = default; // Control whether this trace logger is enabled at runtime. bool DisableRuntime(); void EnableRuntime(); // Return address of m_activityId const GUID* GetActivityId() const; // Return address of m_parentActivityId const GUID* GetParentActivityId() const; // Capture if UserSettings is enabled and set user profile path void Initialize(); // Try to capture if UserSettings is enabled and set user profile path, returns whether the action is successfully completed. // There is a possible circular dependency with the user settings. When initializing the telemetry, we need to read the settings // to make sure it's not disabled, but a failure when reading the settings would trigger a telemetry event. We work around that // by avoiding initialization (and thus disabling telemetry) until we have successfully read the settings. Subsequent calls to // TryInitialize() would finish the initialization. bool TryInitialize(); // Store the passed in name of the Caller for COM calls void SetCaller(const std::string& caller); // Store the passed in Telemetry Correlation Json for COM calls void SetTelemetryCorrelationJson(const std::wstring_view jsonStr_view) noexcept; void SetExecutionStage(uint32_t stage) noexcept; std::unique_ptr CreateSubTraceLogger() const; // Logs the failure info. void LogFailure(const wil::FailureInfo& failure) const noexcept; // Logs the initial process startup. void LogStartup(bool isCOMCall = false) const noexcept; // Logs the invoked command. void LogCommand(std::string_view commandName) const noexcept; // Logs the invoked command success. void LogCommandSuccess(std::string_view commandName) const noexcept; // Logs the invoked command termination. void LogCommandTermination(HRESULT hr, std::string_view file, size_t line) const noexcept; // Logs the invoked command termination. void LogException(FailureTypeEnum type, std::string_view message) const noexcept; // Logs whether the manifest used in workflow is local void LogIsManifestLocal(bool isLocalManifest) const noexcept; // Logs the Manifest fields. void LogManifestFields(std::string_view id, std::string_view name, std::string_view version) const noexcept; // Logs when there is no matching App found for search void LogNoAppMatch() const noexcept; // Logs when there is multiple matching Apps found for search void LogMultiAppMatch() const noexcept; // Logs the name and Id of app found void LogAppFound(std::string_view name, std::string_view id) const noexcept; // Logs the selected installer details void LogSelectedInstaller(int arch, std::string_view url, std::string_view installerType, std::string_view scope, std::string_view language) const noexcept; // Logs details of a search request. void LogSearchRequest( std::string_view type, std::string_view query, std::string_view id, std::string_view name, std::string_view moniker, std::string_view tag, std::string_view command, size_t maximum, std::string_view request) const noexcept; // Logs the Search Result void LogSearchResultCount(uint64_t resultCount) const noexcept; // Logs a mismatch between the expected and actual hash values. void LogInstallerHashMismatch( std::string_view id, std::string_view version, std::string_view channel, const std::vector& expected, const std::vector& actual, bool overrideHashMismatch, uint64_t downloadSizeInBytes, const std::optional& contentType) const noexcept; // Logs a failed installation attempt. void LogInstallerFailure(std::string_view id, std::string_view version, std::string_view channel, std::string_view type, uint32_t errorCode) const noexcept; // Logs a failed uninstallation attempt. void LogUninstallerFailure(std::string_view id, std::string_view version, std::string_view type, uint32_t errorCode) const noexcept; // Logs a failed repair attempt. void LogRepairFailure(std::string_view id, std::string_view version, std::string_view type, uint32_t errorCode) const noexcept; // Logs data about the changes that ocurred in the ARP entries based on an install. // First 4 arguments are well known values for the package that we installed. // The next 3 are counts of the number of packages in each category. // The last 4 are the fields directly from the ARP entry that has been determined to be related to the package that // was installed, or they will be empty if there is no data or ambiguity about which entry should be logged. virtual void LogSuccessfulInstallARPChange( std::string_view sourceIdentifier, std::string_view packageIdentifier, std::string_view packageVersion, std::string_view packageChannel, size_t changesToARP, size_t matchesInARP, size_t countOfIntersectionOfChangesAndMatches, std::string_view arpName, std::string_view arpVersion, std::string_view arpPublisher, std::string_view arpLanguage) const noexcept; void LogNonFatalDOError(std::string_view url, HRESULT hr) const noexcept; protected: bool IsTelemetryEnabled() const noexcept; // Initializes flags that determine whether telemetry is enabled. void InitializeInternal(const AppInstaller::Settings::UserSettings& userSettings); // Used to anonymize a string to the best of our ability. // Should primarily be used on failure messages or paths if needed. std::wstring AnonymizeString(const wchar_t* input) const noexcept; std::wstring AnonymizeString(std::wstring_view input) const noexcept; // Flags used to determine whether to send telemetry. All of them are set during initialization and // are CopyConstructibleAtomic to minimize the impact of multiple simultaneous initialization attempts. // m_isSettingEnabled starts as false so we can don't send telemetry until we have read the // settings and confirmed that it is enabled. CopyConstructibleAtomic m_isSettingEnabled{ false }; // We may decide to disable telemetry at runtime, for example, for command line completion. CopyConstructibleAtomic m_isRuntimeEnabled{ true }; // We wait for initialization of the other flags before sending any events. CopyConstructibleAtomic m_isInitialized{ false }; CopyConstructibleAtomic m_executionStage{ 0 }; GUID m_activityId = GUID_NULL; GUID m_parentActivityId = GUID_NULL; std::wstring m_telemetryCorrelationJsonW = L"{}"; std::string m_caller; bool m_useSummary = true; mutable TelemetrySummary m_summary; // TODO: This and all related code could be removed after transition to summary event in back end. uint32_t m_subExecutionId; }; // Helper to make the call sites look clean. TelemetryTraceLogger& Telemetry(); // Turns on wil failure telemetry and logging. void EnableWilFailureTelemetry(); // TODO: Temporary code to keep existing telemetry behavior for command execution cases. void UseGlobalTelemetryLoggerActivityIdOnly(); // An RAII object to disable telemetry during its lifetime. // Primarily used by the complete command to prevent messy input from spamming us. struct DisableTelemetryScope { DisableTelemetryScope(); DisableTelemetryScope(const DisableTelemetryScope&) = delete; DisableTelemetryScope& operator=(const DisableTelemetryScope&) = delete; DisableTelemetryScope(DisableTelemetryScope&&) = default; DisableTelemetryScope& operator=(DisableTelemetryScope&&) = default; ~DisableTelemetryScope(); private: DestructionToken m_token; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/AdminSettings.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "winget/GroupPolicy.h" namespace AppInstaller::Settings { // Enum of admin settings. enum class BoolAdminSetting : size_t { Unknown = 0, LocalManifestFiles, BypassCertificatePinningForMicrosoftStore, InstallerHashOverride, LocalArchiveMalwareScanOverride, ProxyCommandLineOptions, Max, }; enum class StringAdminSetting : size_t { Unknown = 0, DefaultProxy, Max, }; BoolAdminSetting StringToBoolAdminSetting(std::string_view in); StringAdminSetting StringToStringAdminSetting(std::string_view in); Utility::LocIndView AdminSettingToString(BoolAdminSetting setting); Utility::LocIndView AdminSettingToString(StringAdminSetting setting); // Returns true if the value is set. // Group policy overriding the setting can prevent the value from being set bool EnableAdminSetting(BoolAdminSetting setting); bool DisableAdminSetting(BoolAdminSetting setting); bool SetAdminSetting(StringAdminSetting setting, std::string_view value); bool ResetAdminSetting(StringAdminSetting setting); void ResetAllAdminSettings(); bool IsAdminSettingEnabled(BoolAdminSetting setting); std::optional GetAdminSetting(StringAdminSetting setting); std::vector GetAllBoolAdminSettings(); std::vector GetAllStringAdminSettings(); TogglePolicy::Policy GetAdminSettingPolicy(BoolAdminSetting setting); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Archive.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::Archive { enum class ExtractionMethod { // Default archive extraction method is ShellApi. ShellApi, Tar, }; HRESULT TryExtractArchive(const std::filesystem::path& archivePath, const std::filesystem::path& destPath); bool ScanZipFile(const std::filesystem::path& zipPath); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Authentication.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include "AppInstallerErrors.h" namespace AppInstaller::Authentication { // The authentication type supported enum class AuthenticationType { Unknown, None, MicrosoftEntraId, MicrosoftEntraIdForAzureBlobStorage, }; std::string_view AuthenticationTypeToString(AuthenticationType in); AuthenticationType ConvertToAuthenticationType(std::string_view in); // The authentication modes enum class AuthenticationMode { Unknown, // Always do interactive authentication on first request, following requests may use cached result. Interactive, // Try silent flow first. If failed, use interactive flow. SilentPreferred, // Only do silent flow. If failed, the authentication failed. Silent, }; std::string_view AuthenticationModeToString(AuthenticationMode in); AuthenticationMode ConvertToAuthenticationMode(std::string_view in); // Authentication info for Microsoft Entra Id authentication; struct MicrosoftEntraIdAuthenticationInfo { // Resource is required std::string Resource; // Scope is optional std::string Scope; bool operator<(const MicrosoftEntraIdAuthenticationInfo& other) const; }; // Authentication info struct used to initialize Authenticator, this is from source information. struct AuthenticationInfo { AuthenticationType Type = AuthenticationType::None; std::optional MicrosoftEntraIdInfo; bool operator<(const AuthenticationInfo& other) const; // Update default values for missing required fields for known authentication type. void UpdateRequiredFieldsIfNecessary(); // Validates data integrity against known authentication type. bool ValidateIntegrity() const; }; // Authentication arguments struct used to initialize Authenticator, this is from user input. struct AuthenticationArguments { AuthenticationMode Mode = AuthenticationMode::Unknown; // Optional. If set, the value will be used to acquire the specific account and also be validated with authentication result. std::string AuthenticationAccount; }; // The authentication result struct AuthenticationResult { // Default to failed. S_OK on authentication success. HRESULT Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED; // The token result on authentication success. std::string Token; }; // Individual authentication provider interface. Authenticator will delegate authentication to authentication provider. struct IAuthenticationProvider { virtual ~IAuthenticationProvider() = default; // Authenticate and return string result. virtual AuthenticationResult AuthenticateForToken() = 0; }; // The public facing authenticator struct Authenticator { Authenticator(AuthenticationInfo info, AuthenticationArguments args); // Authenticate and return string result. AuthenticationResult AuthenticateForToken(); private: std::unique_ptr m_authProvider; }; // This is the class for authentication window parent window. // When authenticating interactively, some api needs handle to a parent window. // This class will initiate a new thread and create a hidden window but with foreground priority (best effort). // This class handles terminating the window thread on destruction struct AuthenticationWindowBase { // The constructor will initiate the authentication parent window and thread AuthenticationWindowBase(); AuthenticationWindowBase(const AuthenticationWindowBase&) = delete; AuthenticationWindowBase& operator=(const AuthenticationWindowBase&) = delete; AuthenticationWindowBase(AuthenticationWindowBase&&) = delete; AuthenticationWindowBase& operator=(AuthenticationWindowBase&&) = delete; // Get the native window handle HWND GetHandle(); // The destructor will terminate the authentication parent window and thread ~AuthenticationWindowBase(); private: HWND m_windowHandle; DWORD m_windowThreadId; std::thread m_windowThread; // In case PostThreadMessage() fails, let window thread exit immediately. std::atomic m_terminateWindowThread = false; void InitializeWindowThread(); static LRESULT WINAPI WindowProcessFunction(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); }; // Create bearer token from a raw token std::string CreateBearerToken(std::string rawToken); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Debugging.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::Debugging { // Enables a self initiated minidump on certain process level failures. // Only the first call to EnableSelfInitiatedMinidump has any effect. void EnableSelfInitiatedMinidump(); // Enables a self initiated minidump on certain process level failures. // Creates the minidump in the given location. // Only the first call to EnableSelfInitiatedMinidump has any effect. void EnableSelfInitiatedMinidump(const std::filesystem::path& filePath); // Forces the minidump to be written. void WriteMinidump(); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/DependenciesGraph.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winget/ManifestCommon.h" namespace AppInstaller::Manifest { struct DependencyGraph { // this constructor was intended for use during installation flow (we already have installer dependencies and there's no need to search the source again) DependencyGraph(const Dependency& root, const DependencyList& rootDependencies, std::function infoFunction); DependencyGraph(const Dependency& root, std::function infoFunction); void BuildGraph(); void AddNode(const Dependency& node); void AddAdjacent(const Dependency& node, const Dependency& adjacent); bool HasNode(const Dependency& dependency); bool HasLoop(); void CheckForLoopsAndGetOrder(); std::vector GetInstallationOrder(); private: // TODO make this function iterative bool HasLoopDFS(std::set visited, const Dependency& node); const Dependency& m_root; std::map> m_adjacents; std::function getDependencies; bool m_HasLoop = false; bool m_rootDependencyEvaluated = false; std::vector m_installationOrder; std::vector m_toCheck; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include "AppInstallerStrings.h" namespace AppInstaller::Settings { using namespace std::string_view_literals; struct UserSettings; struct ExperimentalFeature { // To add an experimental feature // 1 - add a flag in this enum, before Max // 2 - add a setting in Setting enum in UserSettings.h // 3 - follow how to add setting instructions // 4 - provide implementation in ExperimentalFeature.cpp enum class Feature : unsigned { None = 0x0, // Before making DirectMSI non-experimental, it should be part of manifest validation. DirectMSI = 0x1, Resume = 0x2, Font = 0x4, SourcePriority = 0x8, Max, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command // This can be used to hide highly experimental features (or these example ones) ExperimentalCmd = 0x10000, ExperimentalArg = 0x20000, }; using Feature_t = std::underlying_type_t; ExperimentalFeature(std::string_view name, std::string_view jsonName, std::string_view link, Feature feature) : m_name(name), m_jsonName(jsonName), m_link(link), m_feature(feature) {} ~ExperimentalFeature() = default; ExperimentalFeature(const ExperimentalFeature&) = default; ExperimentalFeature& operator=(const ExperimentalFeature&) = default; ExperimentalFeature(ExperimentalFeature&&) = default; ExperimentalFeature& operator=(ExperimentalFeature&&) = default; static bool IsEnabled(Feature feature); #ifndef AICLI_DISABLE_TEST_HOOKS static bool IsEnabled(Feature feature, const UserSettings& userSettings); #endif static ExperimentalFeature GetFeature(ExperimentalFeature::Feature feature); static std::vector GetAllFeatures(); std::string_view Name() const { return m_name; } Utility::LocIndView JsonName() const { return m_jsonName; } std::string_view Link() const { return m_link; } Feature GetFeature() const { return m_feature; } private: std::string_view m_name; Utility::LocIndView m_jsonName; std::string_view m_link; Feature m_feature; }; inline ExperimentalFeature::Feature operator|(ExperimentalFeature::Feature lhs, ExperimentalFeature::Feature rhs) { return static_cast ( static_cast(lhs) | static_cast(rhs)); } inline ExperimentalFeature::Feature& operator|=(ExperimentalFeature::Feature& lhs, ExperimentalFeature::Feature rhs) { lhs = lhs | rhs; return lhs; } inline ExperimentalFeature::Feature operator&(ExperimentalFeature::Feature lhs, ExperimentalFeature::Feature rhs) { return static_cast( static_cast(lhs) & static_cast(rhs)); } inline ExperimentalFeature::Feature& operator&=(ExperimentalFeature::Feature& lhs, ExperimentalFeature::Feature rhs) { lhs = lhs & rhs; return lhs; } } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ExtensionCatalog.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Deployment { using namespace std::string_view_literals; constexpr std::wstring_view SourceExtensionName = L"com.microsoft.winget.source"sv; constexpr std::wstring_view IndexDBId = L"IndexDB"sv; // Wraps an AppExtension. struct Extension { Extension(winrt::Windows::ApplicationModel::AppExtensions::AppExtension extension); // Gets the location of the package root. std::filesystem::path GetPackagePath() const; // Gets the location of the directory shared by the extension. std::filesystem::path GetPublicFolderPath() const; // Get the version of the package. winrt::Windows::ApplicationModel::PackageVersion GetPackageVersion() const; // Verifies the integrity of the extension. bool VerifyContentIntegrity(IProgressCallback& progress); private: winrt::Windows::ApplicationModel::AppExtensions::AppExtension m_extension; }; // Wraps an AppExtensionCatalog. struct ExtensionCatalog { ExtensionCatalog(std::wstring_view extensionName); // Finds an extension by its package family name and id. std::optional FindByPackageFamilyAndId(std::string_view packageFamilyName, std::wstring_view id) const; private: winrt::Windows::ApplicationModel::AppExtensions::AppExtensionCatalog m_catalog = nullptr; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/FileCache.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Caching { // A file cache for relatively small files (they are always full loaded into memory due to the hash enforcement). struct FileCache { // The supported file cache types. enum class Type { // Manifests for index V1. IndexV1_Manifest, // Package version data files for index V2. IndexV2_PackageVersionData, // Manifests for index V2. IndexV2_Manifest, // Icon for use during show command when sixel rendering is enabled. Icon, #ifndef AICLI_DISABLE_TEST_HOOKS // The test type. Tests, #endif }; // Contains information about a specific file cache instance. struct Details { Details(Type type, std::string identifier); Runtime::PathName BasePath; Type Type; std::string Identifier; // Gets the full path to the cache directory. std::filesystem::path GetCachePath() const; }; // Construct a file cache for the given instance. FileCache(Type type, std::string identifier, std::vector sources); // Gets the details for this file cache. const Details& GetDetails() const; // Gets a stream containing the contents of the requested file. // The hash must match for this function to return successfully. std::unique_ptr GetFile(const std::filesystem::path& relativePath, const Utility::SHA256::HashBuffer& expectedHash) const; private: // Gets a stream containing the contents of the requested file from an upstream source. // The hash must match for this function to return successfully. std::unique_ptr GetUpstreamFile(std::string relativePath, const Utility::SHA256::HashBuffer& expectedHash) const; Details m_details; std::vector m_sources; std::filesystem::path m_cacheBase; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/FolderFileWatcher.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #pragma warning( push ) #pragma warning ( disable : 6387 28196 ) #include #pragma warning( pop ) namespace AppInstaller::Utility { // Watch for new/renamed files recursively in a given directory with an optional extension. struct FolderFileWatcher { FolderFileWatcher(const std::filesystem::path& path, const std::optional& ext = std::nullopt); ~FolderFileWatcher() {}; FolderFileWatcher(const FolderFileWatcher&) = delete; FolderFileWatcher& operator=(const FolderFileWatcher&) = delete; FolderFileWatcher(FolderFileWatcher&&) = default; FolderFileWatcher& operator=(FolderFileWatcher&&) = default; void Start(); void Stop(); const std::unordered_set& Files() { return m_files; } const std::filesystem::path& FolderPath() { return m_path; } private: std::filesystem::path m_path; std::optional m_ext; std::unordered_set m_files; wil::unique_folder_change_reader m_changeReader; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Fonts.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Fonts { enum class InstallerSource { // Installer source is not guaranteed to be absolutely correct, as it is just // filesystem and registry. We make assumptions based on the structure of how // the font is installed to make a best-effort determination. Unknown, // Can be any external font installer WinGet, // Font installed by WinGet }; enum class FontStatus { // The current status of a particular font file / package. Unknown, Absent, // Font is not present at all. OK, // Font is present and in a good state. Corrupt, // Font is partially installed (has a file, but no registry, or vice-versa) }; enum class FontResult { Unknown, Success, Error, }; struct FontFace { std::wstring Name; std::vector FilePaths; Utility::OpenTypeFontVersion Version; }; struct FontFamily { std::wstring Name; std::vector Faces; }; // Represents information about a font file used for its installation, query, and removal. struct FontFileInfo { std::filesystem::path FilePath; // Where the file currently is std::filesystem::path InstallPath; // Where the file should be if installed. std::wstring Title; Manifest::ScopeEnum Scope = Manifest::ScopeEnum::Unknown; DWRITE_FONT_FILE_TYPE FileType = DWRITE_FONT_FILE_TYPE_UNKNOWN; InstallerSource InstallerSource = InstallerSource::Unknown; FontStatus Status = FontStatus::Unknown; bool WinGetSupported = false; bool IsFontFileInstalled = false; bool IsFontFileRegistered = false; std::wstring PackageId; std::wstring PackageVersion; // Fonts that are not winget-packaged will use this for an identifier. std::wstring PackageIdentifier; // Registry Install path is where the installation with the system is located. std::wstring RegistryInstallPath; // Registry Source path is where package mapping of the package is. std::wstring RegistryPackagePath; }; // Represents information about a WinGet-installed font package. struct FontPackageInfo { Manifest::ScopeEnum Scope = Manifest::ScopeEnum::Unknown; FontStatus Status = FontStatus::Unknown; std::wstring PackageId; std::wstring PackageVersion; std::wstring PackageIdentifier; }; struct FontContext { InstallerSource InstallerSource = InstallerSource::Unknown; Manifest::ScopeEnum Scope = Manifest::ScopeEnum::Unknown; std::vector PackageFiles = std::vector(); std::wstring PackageId; std::wstring PackageVersion; bool Force = false; void AddPackageFile(const std::filesystem::path& filePath); Manifest::AppsAndFeaturesEntry GetAppsAndFeaturesEntry() const; std::wstring GetPackageIdentifier() const; }; struct FontValidationResult { FontResult Result = FontResult::Unknown; FontStatus Status = FontStatus::Unknown; bool HasUnsupportedFonts = false; winrt::hresult HResult = winrt::hresult(S_OK); std::vector FontFileInfos = std::vector(); }; struct FontOperationResult { winrt::hresult HResult = winrt::hresult(S_OK); FontResult Result() const; // TODO: Add optional rollback context to unwind installs if one operation succeeded but a subsequent one failed. }; std::vector GetInstalledFontFiles(); std::vector GetInstalledFontPackages(Manifest::ScopeEnum scope); FontFileInfo CreateFontFileInfo(const FontContext& context, const std::filesystem::path& filePath, const std::wstring& title = std::wstring()); FontValidationResult ValidateFontPackage(FontContext& context); FontOperationResult InstallFontPackage(FontContext& context); FontOperationResult UninstallFontPackage(FontContext& context); std::wstring GetFontRegistryRoot(); struct FontCatalog { FontCatalog(); // Gets all installed font families on the system. If an exact family name is provided and found, returns the installed font family. std::vector GetInstalledFontFamilies(std::optional familyName = {}); // Returns a boolean value indicating whether the specified file path is a valid font file. bool IsFontFileSupported(const std::filesystem::path& filePath, DWRITE_FONT_FILE_TYPE& fileType); std::filesystem::path GetRootFontPath(Manifest::ScopeEnum scope); private: FontFamily GetFontFamilyByIndex(const wil::com_ptr& collection, UINT32 index); std::wstring GetLocalizedStringFromFont(const wil::com_ptr& localizedStringCollection); std::wstring GetFontFamilyName(const wil::com_ptr& fontFamily); std::wstring GetFontFaceName(const wil::com_ptr& font); Utility::OpenTypeFontVersion GetFontFaceVersion(const wil::com_ptr& font); wil::com_ptr m_factory; std::vector m_preferredLocales; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/HttpClientHelper.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include namespace AppInstaller::Http { struct HttpClientHelper { using HttpRequestHeaders = std::unordered_map; struct HttpResponseHandlerResult { // The custom response handler result. Default is empty. std::optional Result = std::nullopt; // Indicates whether to use default handling logic by HttpClientHelper instead (i.e. the custom response handler does not handle the specific response). bool UseDefaultHandling = false; }; using HttpResponseHandler = std::function; HttpClientHelper(std::shared_ptr = {}); pplx::task Post(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; std::optional HandlePost(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}, const HttpResponseHandler& customHandler = {}) const; pplx::task Get(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; std::optional HandleGet(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}, const HttpResponseHandler& customHandler = {}) const; void SetPinningConfiguration(const Certificates::PinningConfiguration& configuration, std::shared_ptr threadGlobals = {}); protected: std::optional ValidateAndExtractResponse(const web::http::http_response& response) const; std::optional ExtractJsonResponse(const web::http::http_response& response) const; private: web::http::client::http_client GetClient(const utility::string_t& uri) const; // Translates a cpprestsdk http_exception to a WIL exception. static void RethrowAsWilException(const web::http::http_exception& exception); std::shared_ptr m_defaultRequestHandlerStage; web::http::client::http_client_config m_clientConfig; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Locale.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Locale { static constexpr double MinimumDistanceScoreAsPerfectMatch = 1.0; static constexpr double MinimumDistanceScoreAsCompatibleMatch = 0.9; static constexpr double UnknownLanguageDistanceScore = 0.0; // Check if a bcp47 language tag is well formed bool IsWellFormedBcp47Tag(std::string_view bcp47Tag); // Get a score of language distance between target and available. The return value range is 0 to 1. // With 1 meaning perfect match and 0 meaning no match. double GetDistanceOfLanguage(std::string_view target, std::string_view available); // Get the list of user Preferred Languages from settings. Returns an empty vector in rare cases of failure. std::vector GetUserPreferredLanguages(); // Get the list of user Preferred Languages from settings. Returns an empty vector in rare cases of failure. std::vector GetUserPreferredLanguagesUTF16(); // Get the bcp47 tag from a locale id. Returns empty string if conversion cannot be performed. std::string LocaleIdToBcp47Tag(LCID localeId); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/MSStore.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Manifest.h" #include #include #include #include namespace AppInstaller::MSStore { using namespace std::string_view_literals; static constexpr std::wstring_view s_AppInstallerProductId = L"9NBLGGH4NNS1"sv; enum class MSStoreOperationType { Install, Update, Repair, }; struct MSStoreOperation { MSStoreOperation(MSStoreOperationType type, const std::wstring& productId, Manifest::ScopeEnum scope, bool isSilentMode, bool force) : m_type(type), m_productId(productId), m_scope(scope), m_isSilentMode(isSilentMode), m_force(force) { } MSStoreOperation(const MSStoreOperation&) = delete; MSStoreOperation& operator=(const MSStoreOperation&) = delete; MSStoreOperation(MSStoreOperation&&) = delete; MSStoreOperation& operator=(MSStoreOperation&&) = delete; HRESULT StartAndWaitForOperation(IProgressCallback& progress); private: HRESULT InstallPackage(IProgressCallback& progress); HRESULT UpdatePackage(IProgressCallback& progress); MSStoreOperationType m_type; std::wstring m_productId; Manifest::ScopeEnum m_scope; bool m_isSilentMode; bool m_force; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/MSStoreDownload.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include "winget/Authentication.h" #include "winget/ManifestCommon.h" #include #include #include #include #include namespace AppInstaller::MSStore { // Struct representing 1 MSStore package file download info struct MSStoreDownloadFile { std::string Url; AppInstaller::Utility::SHA256::HashBuffer Sha256; std::string FileName; AppInstaller::Utility::UInt64Version Version = 0; }; struct MSStoreDownloadInfo { std::vector MainPackages; std::vector DependencyPackages; std::string ContentId; }; struct MSStoreDownloadContext { MSStoreDownloadContext( std::string productId, AppInstaller::Utility::Architecture architecture, AppInstaller::Manifest::PlatformEnum platform, std::string locale, AppInstaller::Authentication::AuthenticationArguments authArgs); void TargetOSVersion(std::optional targetOSVersion); // Calls display catalog API and sfs-client to get download info. MSStoreDownloadInfo GetDownloadInfo(); // Gets license for the corresponding packages std::vector GetLicense(std::string_view contentId); private: std::string m_productId; AppInstaller::Utility::Architecture m_architecture = AppInstaller::Utility::Architecture::Unknown; AppInstaller::Manifest::PlatformEnum m_platform = AppInstaller::Manifest::PlatformEnum::Unknown; std::optional m_targetOSVersion = std::nullopt; std::string m_locale; std::unique_ptr m_displayCatalogAuthenticator; std::unique_ptr m_licensingAuthenticator; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Manifest.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Manifest { // Representation of the parsed manifest file. struct Manifest { using string_t = Utility::NormalizedString; string_t Id; string_t Version; string_t Channel; string_t Moniker; ManifestVer ManifestVersion; ManifestInstaller DefaultInstallerInfo; std::vector Installers; ManifestLocalization DefaultLocalization; std::vector Localizations; ManifestLocalization CurrentLocalization; // ApplyLocale will update the CurrentLocalization according to the specified locale // If locale is empty, user setting locale will be used void ApplyLocale(const std::string& locale = {}); // Get all tags across localizations std::vector GetAggregatedTags() const; // Get all commands across installers std::vector GetAggregatedCommands() const; // Gets ARP version range if declared, otherwise an empty range is returned Utility::VersionRange GetArpVersionRange() const; // Get package family names across installers, Case folded. std::vector GetPackageFamilyNames() const; // Get product codes across installers, Case folded. std::vector GetProductCodes() const; // Get upgrade codes across installers, Case folded. std::vector GetUpgradeCodes() const; // Get package names across localizations and installers, Case folded. std::vector GetPackageNames() const; // Get publishers across localizations and installers, Case folded. std::vector GetPublishers() const; // If not empty, the SHA256 hash of the manifest stream itself. Utility::SHA256::HashBuffer StreamSha256; private: std::vector GetSystemReferenceStrings( std::function extractStringFromInstaller = {}, std::function extractStringFromAppsAndFeaturesEntry = {}) const; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestCommon.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include namespace AppInstaller::Manifest { // Forward declaration struct ManifestInstaller; using string_t = Utility::NormalizedString; using namespace std::string_view_literals; using Utility::RawVersion; // The maximum supported major version known about by this code. constexpr uint64_t s_MaxSupportedMajorVersion = 1; // The default manifest version assigned to manifests without a ManifestVersion field. constexpr std::string_view s_DefaultManifestVersion = "0.1.0"sv; // V1 manifest version for GA constexpr std::string_view s_ManifestVersionV1 = "1.0.0"sv; // V1.1 manifest version constexpr std::string_view s_ManifestVersionV1_1 = "1.1.0"sv; // V1.2 manifest version constexpr std::string_view s_ManifestVersionV1_2 = "1.2.0"sv; // V1.4 manifest version constexpr std::string_view s_ManifestVersionV1_4 = "1.4.0"sv; // V1.5 manifest version constexpr std::string_view s_ManifestVersionV1_5 = "1.5.0"sv; // V1.6 manifest version constexpr std::string_view s_ManifestVersionV1_6 = "1.6.0"sv; // V1.7 manifest version constexpr std::string_view s_ManifestVersionV1_7 = "1.7.0"sv; // V1.9 manifest version constexpr std::string_view s_ManifestVersionV1_9 = "1.9.0"sv; // V1.10 manifest version constexpr std::string_view s_ManifestVersionV1_10 = "1.10.0"sv; // V1.12 manifest version constexpr std::string_view s_ManifestVersionV1_12 = "1.12.0"sv; // V1.28 manifest version constexpr std::string_view s_ManifestVersionV1_28 = "1.28.0"sv; // Any new manifest version must also be added to src\WinGetUtilInterop\Manifest\ManifestVersion.cs. // The manifest extension for the MS Store constexpr std::string_view s_MSStoreExtension = "msstore"sv; struct ManifestValidateOption { bool SchemaValidationOnly = false; bool ErrorOnVerifiedPublisherFields = false; bool InstallerValidation = false; // Options not exposed in winget util bool FullValidation = false; bool ThrowOnWarning = false; bool AllowShadowManifest = false; bool SchemaHeaderValidationAsWarning = false; }; // ManifestVer is inherited from Utility::Version and is a more restricted version. // ManifestVer is used to specify the version of app manifest itself. // ManifestVer is a 3 part version in the format of [0-65535].[0-65535].[0-65535] // and optionally a following tag in the format of -[SomeString] for experimental purpose. struct ManifestVer : public Utility::Version { ManifestVer() = default; ManifestVer(std::string_view version); uint64_t Major() const { return m_parts.size() > 0 ? m_parts[0].Integer : 0; } uint64_t Minor() const { return m_parts.size() > 1 ? m_parts[1].Integer : 0; } uint64_t Patch() const { return m_parts.size() > 2 ? m_parts[2].Integer : 0; } bool HasExtension() const; bool HasExtension(std::string_view extension) const; private: std::vector m_extensions; }; enum class InstallerTypeEnum { Unknown, Inno, Wix, Msi, Nullsoft, Zip, Msix, Exe, Burn, MSStore, Portable, Font, }; enum class UpdateBehaviorEnum { Unknown, Install, UninstallPrevious, Deny, }; enum class InstallerSwitchType { Custom, Silent, SilentWithProgress, Interactive, Language, Log, InstallLocation, Update, Repair, }; enum class RepairBehaviorEnum { Unknown, Modify, Installer, Uninstaller, }; enum class ScopeEnum { Unknown, User, Machine, }; enum class InstallModeEnum { Unknown, Interactive, Silent, SilentWithProgress, }; enum class ExpectedReturnCodeEnum { Unknown, PackageInUse, PackageInUseByApplication, InstallInProgress, FileInUse, MissingDependency, DiskFull, InsufficientMemory, InvalidParameter, NoNetwork, ContactSupport, RebootRequiredToFinish, RebootRequiredForInstall, RebootInitiated, CancelledByUser, AlreadyInstalled, Downgrade, BlockedByPolicy, SystemNotSupported, Custom, }; enum class PlatformEnum { Unknown, Universal, Desktop, IoT, Team, Holographic, }; enum class ElevationRequirementEnum { Unknown, ElevationRequired, ElevationProhibited, ElevatesSelf, }; enum class UnsupportedArgumentEnum { Unknown, Log, Location }; enum class InstalledFileTypeEnum { Unknown, Launch, Uninstall, Other, }; enum class ManifestTypeEnum { Singleton, Version, Installer, DefaultLocale, Locale, Merged, Preview, Shadow, }; enum class DependencyType { WindowsFeature, WindowsLibrary, Package, External }; enum class IconFileTypeEnum { Unknown, Jpeg, Png, Ico, }; enum class IconThemeEnum { Unknown, Default, Light, Dark, HighContrast, }; // Icon resolutions from https://learn.microsoft.com/en-us/windows/apps/design/style/iconography/app-icon-construction#app-icon enum class IconResolutionEnum { Unknown, Custom, Square16, Square20, Square24, Square30, Square32, Square36, Square40, Square48, Square60, Square64, Square72, Square80, Square96, Square256, }; struct ExpectedReturnCode { DWORD InstallerReturnCode = 0; ExpectedReturnCodeEnum ReturnResponse = ExpectedReturnCodeEnum::Unknown; string_t ReturnResponseUrl; }; struct Dependency { DependencyType Type; const string_t& Id() const { return m_id; }; std::optional MinVersion; Dependency(DependencyType type, string_t id, string_t minVersion) : Type(type), m_id(std::move(id)), MinVersion(Utility::Version(std::move(minVersion))), m_foldedId(FoldCase(m_id)) {} Dependency(DependencyType type, string_t id) : Type(type), m_id(std::move(id)), m_foldedId(FoldCase(m_id)){} Dependency(DependencyType type) : Type(type) {} bool operator ==(const Dependency& rhs) const { return Type == rhs.Type && m_foldedId == rhs.m_foldedId && MinVersion == rhs.MinVersion; } bool operator <(const Dependency& rhs) const { return m_foldedId < rhs.m_foldedId; } bool IsVersionOk(const Utility::Version& version) { return MinVersion <= version; } // m_foldedId should be set whenever Id is set void SetId(string_t id) { m_id = std::move(id); m_foldedId = FoldCase(m_id); } private: string_t m_id; std::string m_foldedId; }; struct DependencyList { void Add(const Dependency& newDependency); void Add(const DependencyList& otherDependencyList); bool HasAny() const; bool HasAnyOf(DependencyType type) const; Dependency* HasDependency(const Dependency& dependencyToSearch); void ApplyToType(DependencyType type, std::function func) const; void ApplyToAll(std::function func) const; bool Empty() const; void Clear(); bool HasExactDependency(DependencyType type, const string_t& id, const string_t& minVersion = "") const; size_t Size() const; private: std::vector m_dependencies; }; struct AppsAndFeaturesEntry { string_t DisplayName; string_t Publisher; string_t DisplayVersion; string_t ProductCode; string_t UpgradeCode; InstallerTypeEnum InstallerType = InstallerTypeEnum::Unknown; }; struct MarketsInfo { std::vector AllowedMarkets; std::vector ExcludedMarkets; }; struct NestedInstallerFile { string_t RelativeFilePath; string_t PortableCommandAlias; }; struct InstalledFile { string_t RelativeFilePath; std::vector FileSha256; InstalledFileTypeEnum FileType = InstalledFileTypeEnum::Other; string_t InvocationParameter; string_t DisplayName; }; struct InstallationMetadataInfo { string_t DefaultInstallLocation; std::vector Files; // Checks if there are any installation metadata available. bool HasData() const { return !DefaultInstallLocation.empty() || !Files.empty(); } void Clear() { DefaultInstallLocation.clear(); Files.clear(); } }; // Information about a specific DSC resource. struct DesiredStateConfigurationResourceInfo { string_t Name; }; // The type of resource container. enum class DesiredStateConfigurationContainerType { PowerShell, DSCv3, }; // Information about a DSC container. // Contains the union of properties relevant to all container types. struct DesiredStateConfigurationContainerInfo { DesiredStateConfigurationContainerInfo(DesiredStateConfigurationContainerType type) : Type(type) {} DesiredStateConfigurationContainerInfo(const string_t& repositoryUrl, const string_t& moduleName, std::vector resources) : Type(DesiredStateConfigurationContainerType::PowerShell), RepositoryURL(repositoryUrl), ModuleName(moduleName), Resources(std::move(resources)) {} DesiredStateConfigurationContainerInfo(std::vector resources) : Type(DesiredStateConfigurationContainerType::DSCv3), Resources(std::move(resources)) {} DesiredStateConfigurationContainerType Type; // For Type == PowerShell string_t RepositoryURL; string_t ModuleName; // For all types std::vector Resources; }; InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); UpdateBehaviorEnum ConvertToUpdateBehaviorEnum(const std::string& in); ScopeEnum ConvertToScopeEnum(std::string_view in); InstallModeEnum ConvertToInstallModeEnum(const std::string& in); PlatformEnum ConvertToPlatformEnum(std::string_view in); PlatformEnum ConvertToPlatformEnumForMSStoreDownload(std::string_view in); ElevationRequirementEnum ConvertToElevationRequirementEnum(const std::string& in); UnsupportedArgumentEnum ConvertToUnsupportedArgumentEnum(const std::string& in); ManifestTypeEnum ConvertToManifestTypeEnum(const std::string& in); ExpectedReturnCodeEnum ConvertToExpectedReturnCodeEnum(const std::string& in); InstalledFileTypeEnum ConvertToInstalledFileTypeEnum(const std::string& in); IconFileTypeEnum ConvertToIconFileTypeEnum(std::string_view in); IconThemeEnum ConvertToIconThemeEnum(std::string_view in); IconResolutionEnum ConvertToIconResolutionEnum(std::string_view in); RepairBehaviorEnum ConvertToRepairBehaviorEnum(std::string_view in); std::string_view InstallerTypeToString(InstallerTypeEnum installerType); std::string_view InstallerSwitchTypeToString(InstallerSwitchType installerSwitchType); std::string_view ElevationRequirementToString(ElevationRequirementEnum elevationRequirement); std::string_view InstallModeToString(InstallModeEnum installMode); std::string_view UnsupportedArgumentToString(UnsupportedArgumentEnum unsupportedArgument); std::string_view UpdateBehaviorToString(UpdateBehaviorEnum updateBehavior); std::string_view RepairBehaviorToString(RepairBehaviorEnum repairBehavior); // Short string representation does not contain "Windows." std::string_view PlatformToString(PlatformEnum platform, bool shortString = false); std::string_view ScopeToString(ScopeEnum scope); std::string_view InstalledFileTypeToString(InstalledFileTypeEnum installedFileType); std::string_view IconFileTypeToString(IconFileTypeEnum iconFileType); std::string_view IconThemeToString(IconThemeEnum iconTheme); std::string_view IconResolutionToString(IconResolutionEnum iconResolution); std::string_view ManifestTypeToString(ManifestTypeEnum manifestType); std::string_view ExpectedReturnCodeToString(ExpectedReturnCodeEnum expectedReturnCode); // Gets a value indicating whether the given installer uses the PackageFamilyName system reference. bool DoesInstallerTypeUsePackageFamilyName(InstallerTypeEnum installerType); // Gets a value indicating whether any of the ARP entries uses the PackageFamilyName system reference. bool DoAnyAppsAndFeaturesEntriesUsePackageFamilyName(const std::vector& entries); // Gets a value indicating whether the given installer uses the ProductCode system reference. bool DoesInstallerTypeUseProductCode(InstallerTypeEnum installerType); // Gets a value indicating whether the given installer writes ARP entry. bool DoesInstallerTypeWriteAppsAndFeaturesEntry(InstallerTypeEnum installerType); // Gets a value indicating whether the given installer type supports ARP version range. bool DoesInstallerTypeSupportArpVersionRange(InstallerTypeEnum installer); // Gets a value indicating whether the given installer ignores the Scope value from the manifest. bool DoesInstallerTypeIgnoreScopeFromManifest(InstallerTypeEnum installerType); // Gets a value indicating whether the given installer requires admin for install. bool DoesInstallerTypeRequireAdminForMachineScopeInstall(InstallerTypeEnum installerType); // Gets a value indicating whether the given installer requires RepairBehavior for repair. bool DoesInstallerTypeRequireRepairBehaviorForRepair(InstallerTypeEnum installerType); // Gets a value indicating whether the given installer type is an archive. bool IsArchiveType(InstallerTypeEnum installerType); // Gets a value indicating whether the given installer type is a portable. bool IsPortableType(InstallerTypeEnum installerType); // Gets a value indicating whether the given installer type supports multiple nested installers. bool DoesInstallerTypeSupportMultipleNestedInstallers(InstallerTypeEnum installerType); // Gets a value indicating whether the given nested installer type is supported. bool IsNestedInstallerTypeSupported(InstallerTypeEnum nestedInstallerType); // Checks whether 2 installer types are compatible. E.g. inno and exe are update compatible bool IsInstallerTypeCompatible(InstallerTypeEnum type1, InstallerTypeEnum type2); // Get a list of default switches for known installer types std::map GetDefaultKnownSwitches(InstallerTypeEnum installerType); // Get a list of default return codes for known installer types std::map GetDefaultKnownReturnCodes(InstallerTypeEnum installerType); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestComparator.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include namespace AppInstaller::Manifest { // Flags to indicate why an installer was not applicable enum class InapplicabilityFlags : int { None = 0x0, OSVersion = 0x1, InstalledScope = 0x2, InstalledType = 0x4, InstalledLocale = 0x8, Locale = 0x10, Scope = 0x20, MachineArchitecture = 0x40, Market = 0x80, InstallerType = 0x100, }; DEFINE_ENUM_FLAG_OPERATORS(InapplicabilityFlags); namespace details { // An interface for defining new filters based on user inputs. struct FilterField { FilterField(std::string_view name) : m_name(name) {} virtual ~FilterField() = default; std::string_view Name() const { return m_name; } // Determines if the installer is applicable based on this field alone. virtual InapplicabilityFlags IsApplicable(const AppInstaller::Manifest::ManifestInstaller& installer) = 0; // Explains why the filter regarded this installer as inapplicable. // Will only be called when IsApplicable returns false. virtual std::string ExplainInapplicable(const AppInstaller::Manifest::ManifestInstaller& installer) = 0; private: std::string_view m_name; }; // The result of ComparisonField::IsFirstBetter enum class ComparisonResult { // The first input is not better than the second input. Negative, // The first input is somewhat better than the second input. // If another comparison has a strong positive result, it will override a weak result. WeakPositive, // The first input is definitely better than the second input. StrongPositive, }; // An interface for defining new comparisons based on user inputs. struct ComparisonField : public FilterField { using FilterField::FilterField; virtual ~ComparisonField() = default; // Determines if the first installer is a better choice based on this field alone. virtual ComparisonResult IsFirstBetter(const AppInstaller::Manifest::ManifestInstaller& first, const AppInstaller::Manifest::ManifestInstaller& second) = 0; }; } struct InstallerAndInapplicabilities { std::optional installer; std::vector inapplicabilitiesInstaller; }; // Class in charge of comparing manifest entries struct ManifestComparator { // Options that affect the comparisons. struct Options { // The allowed architectures and a value indicating whether to perform applicability checks. std::vector AllowedArchitectures; bool SkipApplicabilityCheck = false; // The requested installer type. std::optional RequestedInstallerType; // The currently installed type. std::optional CurrentlyInstalledType; // The requested installer scope and a value indicating whether and unknown scope is acceptable. std::optional RequestedInstallerScope; std::optional AllowUnknownScope; // The currently installed scope. std::optional CurrentlyInstalledScope; // The requested installer locale. std::optional RequestedInstallerLocale; // Get the currently installed locale intent and value. std::optional PreviousUserIntentLocale; std::optional CurrentlyInstalledLocale; }; ManifestComparator(const Options& options); // Gets the best installer from the manifest, if at least one is applicable. InstallerAndInapplicabilities GetPreferredInstaller(const AppInstaller::Manifest::Manifest& manifest); // Determines if an installer is applicable. InapplicabilityFlags IsApplicable(const AppInstaller::Manifest::ManifestInstaller& installer); // Determines if the first installer is a better choice. bool IsFirstBetter( const AppInstaller::Manifest::ManifestInstaller& first, const AppInstaller::Manifest::ManifestInstaller& second); private: void AddFilter(std::unique_ptr&& filter); void AddComparator(std::unique_ptr&& comparator); std::vector> m_filters; // Non-owning pointers to values in m_filters. std::vector m_comparators; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Manifest { using namespace std::string_view_literals; // Token specified in installer args will be replaced by proper value. static constexpr std::string_view ARG_TOKEN_LOGPATH = ""sv; static constexpr std::string_view ARG_TOKEN_INSTALLPATH = ""sv; struct ManifestInstaller { using string_t = Utility::NormalizedString; AppInstaller::Utility::Architecture Arch = AppInstaller::Utility::Architecture::Unknown; string_t Url; std::vector Sha256; // Optional. Only used by appx/msix type. If provided, Appinstaller will // validate appx/msix signature and perform streaming install. std::vector SignatureSha256; // Store Product Id string_t ProductId; string_t Locale; std::vector Platform; string_t MinOSVersion; // If present, has more precedence than root InstallerTypeEnum BaseInstallerType = InstallerTypeEnum::Unknown; InstallerTypeEnum NestedInstallerType = InstallerTypeEnum::Unknown; InstallerTypeEnum EffectiveInstallerType() const { return IsArchiveType(BaseInstallerType) ? NestedInstallerType : BaseInstallerType; } std::vector NestedInstallerFiles; ScopeEnum Scope = ScopeEnum::Unknown; std::vector InstallModes; // If present, has more precedence than root std::map Switches; std::vector InstallerSuccessCodes; struct ExpectedReturnCodeInfo { ExpectedReturnCodeEnum ReturnResponseEnum = ExpectedReturnCodeEnum::Unknown; string_t ReturnResponseUrl; }; std::map ExpectedReturnCodes; UpdateBehaviorEnum UpdateBehavior = UpdateBehaviorEnum::Install; RepairBehaviorEnum RepairBehavior = RepairBehaviorEnum::Unknown; std::vector Commands; std::vector Protocols; std::vector FileExtensions; // Package family name for MSIX packaged installers. string_t PackageFamilyName; // Product code for ARP (Add/Remove Programs) installers. string_t ProductCode; // For msix only std::vector Capabilities; // For msix only std::vector RestrictedCapabilities; DependencyList Dependencies; bool InstallerAbortsTerminal = false; string_t ReleaseDate; bool InstallLocationRequired = false; bool RequireExplicitUpgrade = false; bool DisplayInstallWarnings = false; std::vector UnsupportedArguments; std::vector UnsupportedOSArchitectures; std::vector AppsAndFeaturesEntries; ElevationRequirementEnum ElevationRequirement = ElevationRequirementEnum::Unknown; MarketsInfo Markets; InstallationMetadataInfo InstallationMetadata; bool DownloadCommandProhibited = false; bool ArchiveBinariesDependOnPath = false; Authentication::AuthenticationInfo AuthInfo; std::vector DesiredStateConfiguration; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Manifest { using string_t = Utility::NormalizedString; enum class Localization : size_t { Publisher, PublisherUrl, PublisherSupportUrl, PrivacyUrl, Author, PackageName, PackageUrl, License, LicenseUrl, Copyright, CopyrightUrl, ShortDescription, Description, Tags, Agreements, Documentations, ReleaseNotes, ReleaseNotesUrl, PurchaseUrl, InstallationNotes, Icons, Max }; struct Agreement { string_t Label; string_t AgreementText; string_t AgreementUrl; }; struct Documentation { string_t DocumentLabel; string_t DocumentUrl; }; struct Icon { string_t Url; IconFileTypeEnum FileType = IconFileTypeEnum::Unknown; IconResolutionEnum Resolution = IconResolutionEnum::Unknown; IconThemeEnum Theme = IconThemeEnum::Unknown; std::vector Sha256; }; namespace details { template struct LocalizationMapping { using value_t = string_t; }; template <> struct LocalizationMapping { using value_t = std::vector; }; template <> struct LocalizationMapping { using value_t = std::vector; }; template <> struct LocalizationMapping { using value_t = std::vector; }; template <> struct LocalizationMapping { using value_t = std::vector; }; // Used to deduce the LocalizationVariant type; making a variant that includes std::monostate and all LocalizationMapping types. template inline auto Deduce(std::index_sequence) { return std::variant(I)>::value_t...>{}; } // Holds data of any type listed in a LocalizationMapping. using LocalizationVariant = decltype(Deduce(std::make_index_sequence(Localization::Max)>())); // Gets the index into the variant for the given Localization. constexpr inline size_t LocalizationIndex(Localization l) { return static_cast(l) + 1; } } struct ManifestLocalization { string_t Locale; // Adds a value to the Localization data, or overwrites an existing entry. template void Add(typename details::LocalizationMapping::value_t&& v) { m_data[L].emplace(std::forward::value_t>(v)); } template void Add(const typename details::LocalizationMapping::value_t& v) { m_data[L].emplace(v); } // Return a value indicating whether the given localization type exists. bool Contains(Localization l) const { return (m_data.find(l) != m_data.end()); } // Gets the localization value if exists, otherwise empty for easier access template typename details::LocalizationMapping::value_t Get() const { auto itr = m_data.find(L); if (itr == m_data.end()) { return {}; } else { return std::get(itr->second); } } void ReplaceOrMergeWith(const ManifestLocalization& other) { for (auto const& entry : other.m_data) { this->m_data[entry.first] = entry.second; } this->Locale = other.Locale; } private: std::map m_data; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestSchemaValidation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ManifestCommon.h" #include "ManifestValidation.h" #include namespace AppInstaller::Manifest::YamlParser { // Forward declarations struct YamlManifestInfo; // Load manifest schema as parsed json doc Json::Value LoadSchemaDoc(const ManifestVer& manifestVersion, ManifestTypeEnum manifestType); // Validate a list of individual manifests against schema std::vector ValidateAgainstSchema( const std::vector& manifestList, const ManifestVer& manifestVersion); // Validate the schema header of a list of manifests std::vector ValidateYamlManifestsSchemaHeader( const std::vector& manifestList, const ManifestVer& manifestVersion, bool treatErrorAsWarning = true); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestValidation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace YAML { class Node; } namespace AppInstaller::Manifest { namespace ManifestError { const char* const ErrorMessagePrefix = "Manifest Error: "; const char* const WarningMessagePrefix = "Manifest Warning: "; WINGET_DEFINE_RESOURCE_STRINGID(ApproximateVersionNotAllowed); WINGET_DEFINE_RESOURCE_STRINGID(ArpValidationError); WINGET_DEFINE_RESOURCE_STRINGID(ArpVersionOverlapWithIndex); WINGET_DEFINE_RESOURCE_STRINGID(ArpVersionValidationInternalError); WINGET_DEFINE_RESOURCE_STRINGID(BothAllowedAndExcludedMarketsDefined); WINGET_DEFINE_RESOURCE_STRINGID(DuplicatePortableCommandAlias); WINGET_DEFINE_RESOURCE_STRINGID(DuplicateRelativeFilePath); WINGET_DEFINE_RESOURCE_STRINGID(DuplicateMultiFileManifestLocale); WINGET_DEFINE_RESOURCE_STRINGID(DuplicateMultiFileManifestType); WINGET_DEFINE_RESOURCE_STRINGID(DuplicateInstallerEntry); WINGET_DEFINE_RESOURCE_STRINGID(DuplicateInstallerHash); WINGET_DEFINE_RESOURCE_STRINGID(DuplicateReturnCodeEntry); WINGET_DEFINE_RESOURCE_STRINGID(ExceededAppsAndFeaturesEntryLimit); WINGET_DEFINE_RESOURCE_STRINGID(ExceededCommandsLimit); WINGET_DEFINE_RESOURCE_STRINGID(ExceededNestedInstallerFilesLimit); WINGET_DEFINE_RESOURCE_STRINGID(ExeInstallerMissingSilentSwitches); WINGET_DEFINE_RESOURCE_STRINGID(FieldDuplicate); WINGET_DEFINE_RESOURCE_STRINGID(FieldFailedToProcess); WINGET_DEFINE_RESOURCE_STRINGID(FieldIsNotPascalCase); WINGET_DEFINE_RESOURCE_STRINGID(FieldNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(FieldRequireVerifiedPublisher); WINGET_DEFINE_RESOURCE_STRINGID(FieldUnknown); WINGET_DEFINE_RESOURCE_STRINGID(FieldValueNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(FoundDependencyLoop); WINGET_DEFINE_RESOURCE_STRINGID(IncompleteMultiFileManifest); WINGET_DEFINE_RESOURCE_STRINGID(InconsistentInstallerHash); WINGET_DEFINE_RESOURCE_STRINGID(InconsistentMultiFileManifestDefaultLocale); WINGET_DEFINE_RESOURCE_STRINGID(InconsistentMultiFileManifestFieldValue); WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedToProcess); WINGET_DEFINE_RESOURCE_STRINGID(InstallerMsixInconsistencies); WINGET_DEFINE_RESOURCE_STRINGID(InstallerTypeDoesNotSupportPackageFamilyName); WINGET_DEFINE_RESOURCE_STRINGID(InstallerTypeDoesNotSupportProductCode); WINGET_DEFINE_RESOURCE_STRINGID(InstallerTypeDoesNotWriteAppsAndFeaturesEntry); WINGET_DEFINE_RESOURCE_STRINGID(InvalidBcp47Value); WINGET_DEFINE_RESOURCE_STRINGID(InvalidFieldValue); WINGET_DEFINE_RESOURCE_STRINGID(InvalidRootNode); WINGET_DEFINE_RESOURCE_STRINGID(MissingManifestDependenciesNode); WINGET_DEFINE_RESOURCE_STRINGID(MsixSignatureHashFailed); WINGET_DEFINE_RESOURCE_STRINGID(MultiManifestPackageHasDependencies); WINGET_DEFINE_RESOURCE_STRINGID(NoSuitableMinVersionDependency); WINGET_DEFINE_RESOURCE_STRINGID(NoSupportedPlatforms); WINGET_DEFINE_RESOURCE_STRINGID(OptionalFieldMissing); WINGET_DEFINE_RESOURCE_STRINGID(RelativeFilePathEscapesDirectory); WINGET_DEFINE_RESOURCE_STRINGID(RequiredFieldEmpty); WINGET_DEFINE_RESOURCE_STRINGID(RequiredFieldMissing); WINGET_DEFINE_RESOURCE_STRINGID(SchemaError); WINGET_DEFINE_RESOURCE_STRINGID(ScopeNotSupported); WINGET_DEFINE_RESOURCE_STRINGID(ShadowManifestNotAllowed); WINGET_DEFINE_RESOURCE_STRINGID(SingleManifestPackageHasDependencies); WINGET_DEFINE_RESOURCE_STRINGID(UnsupportedMultiFileManifestType); WINGET_DEFINE_RESOURCE_STRINGID(SchemaHeaderNotFound); WINGET_DEFINE_RESOURCE_STRINGID(InvalidSchemaHeader); WINGET_DEFINE_RESOURCE_STRINGID(SchemaHeaderManifestTypeMismatch); WINGET_DEFINE_RESOURCE_STRINGID(SchemaHeaderManifestVersionMismatch); WINGET_DEFINE_RESOURCE_STRINGID(SchemaHeaderUrlPatternMismatch); WINGET_DEFINE_RESOURCE_STRINGID(InvalidPortableFiletype); WINGET_DEFINE_RESOURCE_STRINGID(InvalidFontFiletype); } struct ValidationError { enum class Level { Warning, Error }; AppInstaller::StringResource::StringId Message; std::string Context = {}; std::string Value = {}; // line and column are 1 based size_t Line = 0; size_t Column = 0; Level ErrorLevel = Level::Error; std::string FileName; ValidationError(AppInstaller::StringResource::StringId message) : Message(std::move(message)) {} ValidationError(AppInstaller::StringResource::StringId message, Level level) : Message(std::move(message)), ErrorLevel(level) {} ValidationError(AppInstaller::StringResource::StringId message, std::string context) : Message(std::move(message)), Context(std::move(context)) {} ValidationError(AppInstaller::StringResource::StringId message, std::string context, Level level) : Message(std::move(message)), Context(std::move(context)), ErrorLevel(level) {} ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string_view value) : Message(std::move(message)), Context(std::move(context)), Value(value) {} ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string value) : Message(std::move(message)), Context(std::move(context)), Value(std::move(value)) {} ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string value, Level level) : Message(std::move(message)), Context(std::move(context)), Value(std::move(value)), ErrorLevel(level) {} ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string value, size_t line, size_t column) : Message(std::move(message)), Context(std::move(context)), Value(std::move(value)), Line(line), Column(column) {} ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string value, size_t line, size_t column, Level level) : Message(std::move(message)), Context(std::move(context)), Value(std::move(value)), Line(line), Column(column), ErrorLevel(level) {} std::string GetErrorMessage() const; static ValidationError MessageWithFile(AppInstaller::StringResource::StringId message, std::string file) { ValidationError error{ message }; error.FileName = file; return error; } static ValidationError MessageContextWithFile(AppInstaller::StringResource::StringId message, std::string context, std::string file) { ValidationError error{ message, context }; error.FileName = file; return error; } static ValidationError MessageContextValueWithFile(AppInstaller::StringResource::StringId message, std::string context, std::string value, std::string file) { ValidationError error{ message, context, value }; error.FileName = file; return error; } static ValidationError MessageLevelWithFile(AppInstaller::StringResource::StringId message, Level level, std::string file) { ValidationError error{ message, level }; error.FileName = file; return error; } static ValidationError MessageContextValueLineLevelWithFile(AppInstaller::StringResource::StringId message, std::string context, std::string value, size_t line, size_t column , Level level , std::string file) { ValidationError error{ message, context, value, line, column, level }; error.FileName = file; return error; } }; struct ManifestException : public wil::ResultException { ManifestException(std::vector&& errors = {}, HRESULT hr = APPINSTALLER_CLI_ERROR_MANIFEST_FAILED) : wil::ResultException(hr), m_errors(std::move(errors)) { auto p = [&](ValidationError const& e) { return e.ErrorLevel == ValidationError::Level::Error; }; m_warningOnly = !m_errors.empty() && std::find_if(m_errors.begin(), m_errors.end(), p) == m_errors.end(); } ManifestException(HRESULT hr) : ManifestException({}, hr) {} // Error message without wil diagnostic info const std::string& GetManifestErrorMessage() const noexcept { if (m_manifestErrorMessage.empty()) { if (m_errors.empty()) { // Syntax error, yaml parser error is stored in FailureInfo if (GetFailureInfo().pszMessage) { m_manifestErrorMessage = Utility::ConvertToUTF8(GetFailureInfo().pszMessage); } } else { for (auto const& error : m_errors) { if (error.ErrorLevel == ValidationError::Level::Error) { m_manifestErrorMessage += ManifestError::ErrorMessagePrefix; } else if (error.ErrorLevel == ValidationError::Level::Warning) { m_manifestErrorMessage += ManifestError::WarningMessagePrefix; } m_manifestErrorMessage += error.GetErrorMessage(); if (!error.Context.empty()) { m_manifestErrorMessage += " [" + error.Context + "]"; } if (!error.Value.empty()) { m_manifestErrorMessage += " Value: " + error.Value; } if (error.Line > 0 && error.Column > 0) { m_manifestErrorMessage += " Line: " + std::to_string(error.Line) + ", Column: " + std::to_string(error.Column); } if (!error.FileName.empty()) { m_manifestErrorMessage += " File: " + error.FileName; } m_manifestErrorMessage += '\n'; } } } return m_manifestErrorMessage; } bool IsWarningOnly() const noexcept { return m_warningOnly; } const char* what() const noexcept override { if (m_whatMessage.empty()) { m_whatMessage = ResultException::what(); if (!m_errors.empty()) { m_whatMessage += GetManifestErrorMessage(); } } return m_whatMessage.c_str(); } const std::vector& Errors() const { return m_errors; } private: std::vector m_errors; mutable std::string m_whatMessage; mutable std::string m_manifestErrorMessage; bool m_warningOnly; }; // fullValidation: bool to set if manifest validation should perform extra validation that is not required for reading a manifest. std::vector ValidateManifest(const Manifest& manifest, bool fullValidation = true); std::vector ValidateManifestLocalization(const ManifestLocalization& localization, bool treatErrorAsWarning = false); std::vector ValidateManifestInstallers(const Manifest& manifest, bool treatErrorAsWarning = false); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestYamlParser.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Manifest::YamlParser { struct YamlManifestInfo { // Root node of a yaml manifest file YAML::Node Root; // File name of the manifest file if applicable for error reporting std::string FileName; // Schema header string found in the manifest file YAML::DocumentSchemaHeader DocumentSchemaHeader; // The SHA256 hash of the stream Utility::SHA256::HashBuffer StreamSha256; ManifestTypeEnum ManifestType = ManifestTypeEnum::Preview; }; Manifest CreateFromPath( const std::filesystem::path& inputPath, ManifestValidateOption validateOption = {}, const std::filesystem::path& mergedManifestPath = {}); Manifest Create( const std::string& input, ManifestValidateOption validateOption = {}, const std::filesystem::path& mergedManifestPath = {}); Manifest ParseManifest( std::vector& input, ManifestValidateOption validateOption = {}, const std::filesystem::path& mergedManifestPath = {}); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Manifest { // Add here new manifest pointer types. using VariantManifestPtr = std::variant< Agreement*, AppsAndFeaturesEntry*, Dependency*, DependencyList*, DesiredStateConfigurationContainerInfo*, DesiredStateConfigurationResourceInfo*, Documentation*, ExpectedReturnCode*, Icon*, InstallationMetadataInfo*, InstalledFile*, Manifest*, ManifestInstaller*, ManifestLocalization*, MarketsInfo*, NestedInstallerFile*, AppInstaller::Authentication::AuthenticationInfo*, AppInstaller::Authentication::MicrosoftEntraIdAuthenticationInfo*, std::map*, std::vector* >; struct ManifestYamlPopulator { static std::vector PopulateManifest( YAML::Node& rootNode, Manifest& manifest, const ManifestVer& manifestVersion, ManifestValidateOption validateOption, const std::optional& shadowNode); private: ManifestYamlPopulator(YAML::Node& rootNode, Manifest& manifest, const ManifestVer& manifestVersion, ManifestValidateOption validateOption); std::reference_wrapper m_rootNode; std::reference_wrapper m_manifest; std::reference_wrapper m_manifestVersion; bool m_isMergedManifest = false; ManifestValidateOption m_validateOption; // Struct mapping a manifest field to its population logic struct FieldProcessInfo { FieldProcessInfo(std::string name, std::function(const YAML::Node&, const VariantManifestPtr& v)> func, bool requireVerifiedPublisher = false) : Name(std::move(name)), ProcessFunc(func), RequireVerifiedPublisher(requireVerifiedPublisher) {} std::string Name; std::function(const YAML::Node&, const VariantManifestPtr& v)> ProcessFunc; bool RequireVerifiedPublisher = false; }; std::vector RootFieldInfos; std::vector InstallerFieldInfos; std::vector SwitchesFieldInfos; std::vector ExpectedReturnCodesFieldInfos; std::vector DependenciesFieldInfos; std::vector PackageDependenciesFieldInfos; std::vector LocalizationFieldInfos; std::vector AgreementFieldInfos; std::vector MarketsFieldInfos; std::vector AppsAndFeaturesEntryFieldInfos; std::vector DocumentationFieldInfos; std::vector IconFieldInfos; std::vector NestedInstallerFileFieldInfos; std::vector InstallationMetadataFieldInfos; std::vector InstallationMetadataFilesFieldInfos; std::vector AuthenticationFieldInfos; std::vector MicrosoftEntraIdAuthenticationInfoFieldInfos; std::vector DesiredStateConfigurationFieldInfos; std::vector DesiredStateConfigurationPowerShellModuleFieldInfos; std::vector DesiredStateConfigurationPowerShellResourceFieldInfos; std::vector DesiredStateConfigurationDSCv3FieldInfos; std::vector DesiredStateConfigurationDSCv3ResourceFieldInfos; // Cache of Installers node and Localization node YAML::Node const* m_p_installersNode = nullptr; YAML::Node const* m_p_localizationsNode = nullptr; std::vector GetRootFieldProcessInfo(); std::vector GetInstallerFieldProcessInfo(bool forRootFields = false); std::vector GetSwitchesFieldProcessInfo(); std::vector GetExpectedReturnCodesFieldProcessInfo(); std::vector GetDependenciesFieldProcessInfo(); std::vector GetPackageDependenciesFieldProcessInfo(); std::vector GetLocalizationFieldProcessInfo(bool forRootFields = false); std::vector GetAgreementFieldProcessInfo(); std::vector GetMarketsFieldProcessInfo(); std::vector GetAppsAndFeaturesEntryFieldProcessInfo(); std::vector GetDocumentationFieldProcessInfo(); std::vector GetIconFieldProcessInfo(); std::vector GetNestedInstallerFileFieldProcessInfo(); std::vector GetInstallationMetadataFieldProcessInfo(); std::vector GetInstallationMetadataFilesFieldProcessInfo(); std::vector GetAuthenticationFieldInfos(); std::vector GetMicrosoftEntraIdAuthenticationInfoFieldInfos(); std::vector GetDesiredStateConfigurationFieldInfos(); std::vector GetDesiredStateConfigurationPowerShellModuleFieldInfos(); std::vector GetDesiredStateConfigurationPowerShellResourceFieldInfos(); std::vector GetDesiredStateConfigurationDSCv3FieldInfos(); std::vector GetDesiredStateConfigurationDSCv3ResourceFieldInfos(); // Shadow std::vector GetShadowRootFieldProcessInfo(); std::vector GetShadowLocalizationFieldProcessInfo(); // This method takes YAML root node and list of manifest field info. // Yaml lib does not support case-insensitive search and it allows duplicate keys. If duplicate keys exist, // the value is undefined. So in this method, we will iterate through the node map and process each individual // pair ourselves. This also helps with generating aggregated error rather than throwing on first failure. std::vector ValidateAndProcessFields( const YAML::Node& rootNode, const std::vector& fieldInfos, const VariantManifestPtr& v); std::vector ProcessPackageDependenciesNode(const YAML::Node& rootNode, DependencyList* dependencyList); std::vector ProcessAgreementsNode(const YAML::Node& agreementsNode, ManifestLocalization* localization); std::vector ProcessMarketsNode(const YAML::Node& marketsNode, AppInstaller::Manifest::ManifestInstaller* installer); std::vector ProcessAppsAndFeaturesEntriesNode(const YAML::Node& appsAndFeaturesEntriesNode, AppInstaller::Manifest::ManifestInstaller* installer); std::vector ProcessExpectedReturnCodesNode(const YAML::Node& returnCodesNode, AppInstaller::Manifest::ManifestInstaller* installer); std::vector ProcessDocumentationsNode(const YAML::Node& documentationsNode, ManifestLocalization* localization); std::vector ProcessIconsNode(const YAML::Node& iconsNode, ManifestLocalization* localization); std::vector ProcessNestedInstallerFilesNode(const YAML::Node& nestedInstallerFilesNode, AppInstaller::Manifest::ManifestInstaller* installer); std::vector ProcessInstallationMetadataFilesNode(const YAML::Node& installedFilesNode, InstallationMetadataInfo* installationMetadata); std::vector ProcessShadowLocalizationNode(const YAML::Node& localizationNode, Manifest* manifest); std::vector ProcessDSC_PowerShellModuleNode(const YAML::Node& node, std::vector* containers); std::vector ProcessDSC_PowerShellResourcesNode(const YAML::Node& node, DesiredStateConfigurationContainerInfo* container); std::vector ProcessDSCv3ResourcesNode(const YAML::Node& node, DesiredStateConfigurationContainerInfo* container); std::vector PopulateManifestInternal(); std::vector InsertShadow(const YAML::Node& shadowNode); }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ManifestYamlWriter.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Manifest::YamlWriter { ///

/// Converts the manifest and a single installer to a yaml string. /// /// Manifest object. /// Manifest installer object. /// Yaml string. std::string ManifestToYamlString(const Manifest& manifest, const ManifestInstaller& installer); /// /// Exports the manifest and single manifest installer to a yaml file. /// /// Manifest object. /// Manifest installer object. /// Path of the yaml file. void OutputYamlFile(const Manifest& manifest, const ManifestInstaller& installer, const std::filesystem::path& out); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/MsiExecArguments.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include // This file defines parsing of the command line arguments passed to msiexec.exe. // // Some packages require the UAC prompt for installing even on silent installs. This // can be done with the MSI API using the INSTALLUILEVEL_UACONLY flag, but msiexec.exe // does not provide a way to use it. So, we use the MSI API directly instead of // through msiexec.exe. Since msiexec.exe does some parsing of command line arguments // before handing off to the API, we replicate that parsing here. // // Since we care only about installation, we simplify the parsing by assuming that // the command line has the form // msiexec.exe /i (MSI file) [Other args...] namespace AppInstaller::Msi { DEFINE_ENUM_FLAG_OPERATORS(INSTALLUILEVEL); DEFINE_ENUM_FLAG_OPERATORS(INSTALLLOGMODE); DEFINE_ENUM_FLAG_OPERATORS(INSTALLLOGATTRIBUTES); constexpr INSTALLLOGMODE DefaultLogMode = INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | INSTALLLOGMODE_INFO | INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA; // All but the four flags that always have to be set explicitly (Verbose, ExtraDebug, LogOnlyOnError, LogPerformance) constexpr INSTALLLOGMODE AllLogMode = INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO | INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_PROPERTYDUMP | INSTALLLOGMODE_COMMONDATA; // Arguments parsed from a command line string. // Arguments currently supported are: // - Logging options (/l) // - Quiet options (/q) // - Properties (PROPERTY=Value) struct MsiParsedArguments { // Logging options. See: MsiEnableLog() INSTALLLOGMODE LogMode = {}; std::optional LogFile; INSTALLLOGATTRIBUTES LogAttributes = {}; // UI options. See: MsiSetInternalUI() INSTALLUILEVEL UILevel = INSTALLUILEVEL_DEFAULT; // Properties string std::wstring Properties; }; // Parses a command line string for msiexec. // This function assumes that the full command line will have the form // msiexec.exe /i package.msi [arguments] // and that it is only parsing the [arguments] part. // // Note: This does not match msiexec exactly. It does not support options // unrelated to install, nor all options for install (e.g. /n). MsiParsedArguments ParseMSIArguments(std::string_view arguments); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/MsixManifest.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AppInstallerStrings.h" #include "AppInstallerVersions.h" #include #include namespace AppInstaller::Msix { using PackageVersion = Utility::UInt64Version; using OSVersion = Utility::UInt64Version; // Package identity for an MSIX manifest struct MsixPackageManifestIdentity { MsixPackageManifestIdentity(Microsoft::WRL::ComPtr packageId) : m_packageId(packageId) {} Utility::NormalizedString GetPackageFamilyName() const; PackageVersion GetVersion() const; private: Microsoft::WRL::ComPtr m_packageId; }; // Target device family for an MSIX manifest struct MsixPackageManifestTargetDeviceFamily { // Target device family names static constexpr std::string_view WindowsDesktopName = "Windows.Desktop"; static constexpr std::string_view WindowsUniversalName = "Windows.Universal"; enum Platform { WindowsDesktop, WindowsUniversal, Other, }; MsixPackageManifestTargetDeviceFamily(Microsoft::WRL::ComPtr targetDeviceFamily) : m_targetDeviceFamily(targetDeviceFamily) {} std::string GetName() const; OSVersion GetMinVersion() const; Platform GetPlatform() const; private: Microsoft::WRL::ComPtr m_targetDeviceFamily; }; // MSIX manifest struct MsixPackageManifest { MsixPackageManifest(Microsoft::WRL::ComPtr manifestReader) : m_manifestReader(manifestReader) {} std::vector GetTargetDeviceFamilies() const; MsixPackageManifestIdentity GetIdentity() const; std::optional GetMinimumOSVersionForSupportedPlatforms() const; private: Microsoft::WRL::ComPtr m_manifestReader; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/MsixManifestValidation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AppInstallerErrors.h" #include "AppInstallerMsixInfo.h" #include "winget/Manifest.h" #include "winget/ManifestValidation.h" namespace AppInstaller::Manifest { struct MsixManifestValidation { MsixManifestValidation(ValidationError::Level validationErrorLevel) : m_validationErrorLevel(validationErrorLevel) {} ~MsixManifestValidation(); // Validate manifest for Msix packages and Msix bundles. std::vector Validate( const Manifest& manifest, const ManifestInstaller& installer); private: std::map> m_msixInfoCache; std::vector m_downloadedInstallers; ValidationError::Level m_validationErrorLevel; // Get Msix info from url/local path, or load it from cache. // Return null pointer if operation failed. std::shared_ptr GetMsixInfo(std::string installerUrl); // Get Msix info from installer url. // Return null pointer if operation failed. std::shared_ptr GetMsixInfoFromUrl(std::string installerUrl); // Download and get msix info from installer local path. // Return null pointer if operation failed. std::shared_ptr GetMsixInfoFromLocalPath(std::string installerUrl); // Download the installer. // If the download was successful, return the destination path. std::optional DownloadInstaller(std::string installerUrl, int retryCount); // Get manifest installer minimum OS version or nullopt if failed to // parse input. std::optional GetManifestInstallerMinOSVersion( std::string minOSVersion, std::vector& errors); // Validate Msix package family name. void ValidateMsixManifestPackageFamilyName( Utility::NormalizedString msixPackageFamilyName, Utility::NormalizedString manifestPackageFamilyName, std::vector& errors); // Validate Msix package version. void ValidateMsixManifestPackageVersion( const Msix::PackageVersion& msixPackageVersion, const string_t& manifestPackageVersionStr, std::vector& errors); // Validate Msix minimum OS version for supported platforms. void ValidateMsixManifestMinOSVersion( const std::optional& msixMinOSVersion, const std::optional& manifestMinOSVersion, std::string installerUrl, std::vector& errors); // Validate Msix signature hash. void ValidateMsixManifestSignatureHash( const std::shared_ptr msixInfo, const Utility::SHA256::HashBuffer& manifestSignatureHash, std::vector& errors); }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/NameNormalization.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Utility { // The specific version of normalization being used. enum class NormalizationVersion { Initial, InitialPreserveWhiteSpace, }; // List of name normalization fields. Architecture, locale, etc. // Currently only architecture is used. enum class NormalizationField : uint32_t { None = 0x0, Architecture = 0x1, }; DEFINE_ENUM_FLAG_OPERATORS(NormalizationField); struct NameNormalizer; // A package publisher and name that has been normalized, allowing direct // comparison across versions and many other facet. Also allows use in // generating and Id for local packages. struct NormalizedName { NormalizedName() = default; const std::string& Name() const { return m_name; } void Name(std::string&& name) { m_name = std::move(name); } void Name(std::string_view name) { m_name = name; } Utility::Architecture Architecture() const { return m_arch; } void Architecture(Utility::Architecture arch) { m_arch = arch; } const std::string& Locale() const { return m_locale; } void Locale(std::string&& locale) { m_locale = std::move(locale); } const std::string& Publisher() const { return m_publisher; } void Publisher(std::string&& publisher) { m_publisher = std::move(publisher); } void Publisher(std::string_view publisher) { m_publisher = publisher; } // Gets normalized name with additional normalization fields included. std::string GetNormalizedName(NormalizationField fieldsToInclude) const; // Gets a flag indicating the list of fields detected in normalization. NormalizationField GetNormalizedFields() const; private: std::string m_name; Utility::Architecture m_arch = Utility::Architecture::Unknown; std::string m_locale; std::string m_publisher; }; namespace details { // NameNormalizer interface to allow different versions. struct INameNormalizer { virtual ~INameNormalizer() = default; // Normalize both the name and publisher at the same time. virtual NormalizedName Normalize(std::string_view name, std::string_view publisher) const = 0; // Normalize only the name. virtual NormalizedName NormalizeName(std::string_view name) const = 0; // Normalize only the publisher. virtual std::string NormalizePublisher(std::string_view publisher) const = 0; }; } // Helper that manages the lifetime of the internals required to // execute the name normalization. struct NameNormalizer { NameNormalizer(NormalizationVersion version); NormalizedName Normalize(std::string_view name, std::string_view publisher) const; NormalizedName NormalizeName(std::string_view name) const; std::string NormalizePublisher(std::string_view publisher) const; private: std::unique_ptr m_normalizer; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/NetworkSettings.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "winget/UserSettings.h" namespace AppInstaller::Settings { // Network related settings. // Merges information from user settings, admin settings, command line, and group policy. // TODO: This is currently a process global. It should be converted to a thread local // (like telemetry) once we expose configuring a proxy through the COM API struct NetworkSettings { static NetworkSettings& Instance(); const std::optional& GetProxyUri() const { return m_proxyUri; } // Sets the proxy URI; may do nothing depending on admin settings and group policy void SetProxyUri(const std::optional& proxyUri); InstallerDownloader GetInstallerDownloader() const; protected: NetworkSettings(); ~NetworkSettings() = default; std::optional m_proxyUri; }; NetworkSettings& Network(); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/OutputDebugStringLogger.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::Logging { // Sends logs to the OutputDebugString function. // Intended for use during initialization debugging. struct OutputDebugStringLogger : ILogger { OutputDebugStringLogger() = default; ~OutputDebugStringLogger() = default; // ILogger std::string GetName() const override; void Write(Channel channel, Level, std::string_view message) noexcept override; void WriteDirect(Channel channel, Level level, std::string_view message) noexcept override; // Adds OutputDebugStringLogger to the current Log static void Add(); // Removes OutputDebugStringLogger from the current Log static void Remove(); }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/PackageDependenciesValidationUtil.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::Manifest { enum WinGetManifestDependenciesErrorResult : uint32_t { None = 0x0, // Each validation step should have an enum for corresponding failure. SingleManifestPackageHasDependencies = 0x10000, MultiManifestPackageHasDependencies = 0x20000, MissingManifestDependenciesNode = 0x40000, NoSuitableMinVersionDependency = 0x80000, FoundDependencyLoop = 0x100000, }; DEFINE_ENUM_FLAG_OPERATORS(WinGetManifestDependenciesErrorResult); WinGetManifestDependenciesErrorResult GetDependenciesValidationResultFromException(const ManifestException& manifestException); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/PackageVersionDataManifest.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Manifest { // Contains the manifest that stores package version data for index v2 struct PackageVersionDataManifest { // The file name to use for the package version data manifest. static std::string_view VersionManifestFileName(); // The file name to use for the compressed package version data manifest. static std::string_view VersionManifestCompressedFileName(); // Gets the relative path to the package version manifest from the inputs. static std::filesystem::path GetRelativeDirectoryPath(std::string_view packageIdentifier, std::string_view manifestHash); // Creates the compressor used by the PackageVersionDataManifest. static Compression::Compressor CreateCompressor(); // Creates the decompressor used by the PackageVersionDataManifest. static Compression::Decompressor CreateDecompressor(); // Data on an individual version. struct VersionData { VersionData() = default; VersionData( const Utility::VersionAndChannel& versionAndChannel, std::optional arpMinVersion, std::optional arpMaxVersion, std::optional relativePath, std::optional manifestHash); Utility::Version Version; std::optional ArpMinVersion; std::optional ArpMaxVersion; std::string ManifestRelativePath; std::string ManifestHash; }; // Adds the given version data to the manifest. void AddVersion(VersionData&& versionData); // Gets the version data in this object. const std::vector& Versions() const; // Returns a serialized version of the current manifest data. std::string Serialize(); // Parses the input into this objects data. void Deserialize(std::string_view input); // Parses the input into this objects data. void Deserialize(const std::vector& input); private: std::vector m_versions; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/PathVariable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winget/Manifest.h" #include "winget/Registry.h" namespace AppInstaller::Registry::Environment { bool RefreshPathVariableForCurrentProcess(); struct PathVariable { PathVariable(Manifest::ScopeEnum scope, bool readOnly = false); // Returns the PATH variable as a string. std::string GetPathValue(); // Checks if the PATH variable contains the target path. bool Contains(const std::filesystem::path& target); // Returns a value indicating whether the target path was removed from the PATH variable. bool Remove(const std::filesystem::path& target); // Returns a value indicating whether the target path was appended to the PATH variable. bool Append(const std::filesystem::path& target); private: void SetPathValue(const std::string& value); Registry::Key m_key; Manifest::ScopeEnum m_scope; bool m_readOnly; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Pin.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winget/Manifest.h" #include "AppInstallerVersions.h" namespace AppInstaller::Pinning { // The pin types are ordered by how "strict" they are. // Meaning, the one that is more restrictive goes later. // This is used to decide which pin to report if there are multiple pins. enum class PinType : int64_t { // Unknown pin type or not pinned Unknown, // Pinned by the manifest using the RequiresExplicitUpgrade field. // Behaves the same as Pinning pins PinnedByManifest, // The package is excluded from 'upgrade --all', unless '--include-pinned' is added. // 'upgrade ' is not blocked. Pinning, // The package is pinned to a specific version range. Gating, // The package is blocked from 'upgrade --all' and 'upgrade '. // User has to unblock to allow update. Blocking, }; std::string_view ToString(PinType type); PinType ConvertToPinTypeEnum(std::string_view in); // Determines which of two pin types is more strict. bool IsStricter(PinType first, PinType second); // Returns the stricter of two pin types. PinType Stricter(PinType first, PinType second); // The set of values needed to uniquely identify a Pin. // A Pin can apply to an installed package or to an available package. // Pins on available packages can persist when an app is updated outside of winget, // but it's hard to have them work when there are multiple installed packages for the same available package. // Pins on installed packages work fine when there are multiple installed packages for the same available, // but they break when the package is updated outside of winget. struct PinKey { PinKey() = default; PinKey(const Manifest::Manifest::string_t& packageId, std::string_view sourceId) : PackageId(packageId), SourceId(sourceId) {} // Gets a pin key that refers to an installed package by its ProductCode or PackageFamilyName. // The sourceId used is a special string to distinguish from available packages. static PinKey GetPinKeyForInstalled(std::string_view systemReferenceString); bool IsForInstalled() const; bool operator==(const PinKey& other) const { return PackageId == other.PackageId && SourceId == other.SourceId; } bool operator!=(const PinKey& other) const { return !(*this == other); } bool operator<(const PinKey& other) const { // std::tie implements tuple comparison, wherein it checks the first item in the tuple, // iff the first elements are equal, then the second element is used for comparison, and so on return std::tie(PackageId, SourceId) < std::tie(other.PackageId, other.SourceId); } // Used for logging std::string ToString() const; std::string PackageId; std::string SourceId; }; struct Pin { Pin(const Pin&) = default; Pin& operator=(const Pin& other) = default; Pin(Pin&&) = default; Pin& operator=(Pin&&) = default; static Pin CreateBlockingPin(PinKey&& pinKey); static Pin CreatePinningPin(PinKey&& pinKey); static Pin CreateGatingPin(PinKey&& pinKey, Utility::GatedVersion&& gatedVersion); static Pin CreateBlockingPin(const PinKey& pinKey) { return CreateBlockingPin(PinKey{ pinKey }); } static Pin CreatePinningPin(const PinKey& pinKey) { return CreatePinningPin(PinKey{ pinKey }); } static Pin CreateGatingPin(const PinKey& pinKey, const Utility::GatedVersion& gatedVersion) { return CreateGatingPin(PinKey{ pinKey }, Utility::GatedVersion{ gatedVersion }); } PinType GetType() const { return m_type; } const PinKey& GetKey() const { return m_key; } const Utility::GatedVersion& GetGatedVersion() const { return m_gatedVersion; } bool operator==(const Pin& other) const; bool operator<(const Pin& other) const { return std::make_pair(m_type, m_key) < std::make_pair(other.m_type, other.m_key); } // Used for logging std::string ToString() const; private: Pin(PinType type, PinKey&& pinKey, Utility::GatedVersion&& gatedVersion = {}) : m_type(type), m_key(std::move(pinKey)), m_gatedVersion(std::move(gatedVersion)) {} PinType m_type = PinType::Unknown; PinKey m_key; Utility::GatedVersion m_gatedVersion; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/PortableARPEntry.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Manifest.h" namespace AppInstaller::Registry::Portable { enum class PortableValueName { DisplayName, DisplayVersion, HelpLink, InstallDate, InstallDirectoryCreated, InstallLocation, PortableSymlinkFullPath, PortableTargetFullPath, Publisher, SHA256, URLInfoAbout, UninstallString, WinGetInstallerType, WinGetPackageIdentifier, WinGetSourceIdentifier, InstallDirectoryAddedToPath, }; std::wstring_view ToString(PortableValueName valueName); struct PortableARPEntry : Registry::Key { PortableARPEntry(Manifest::ScopeEnum scope, Utility::Architecture arch, const std::string& productCode); std::optional operator[](PortableValueName valueName) const; bool Exists() { return m_exists; } void SetValue(PortableValueName valueName, const std::wstring& value); void SetValue(PortableValueName valueName, const std::string_view& value); void SetValue(PortableValueName valueName, bool& value); void Delete(); Registry::Key GetKey() { return m_key; }; Manifest::ScopeEnum GetScope() { return m_scope; }; Utility::Architecture GetArchitecture() { return m_arch; }; std::string GetProductCode() { return m_productCode; }; private: bool m_exists = false; std::string m_productCode; Key m_key; HKEY m_root; std::wstring m_subKey; DWORD m_samDesired; Manifest::ScopeEnum m_scope; Utility::Architecture m_arch; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/PortableFileEntry.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AppInstallerSHA256.h" #include #include namespace AppInstaller::Portable { // File type enum of the portable file enum class PortableFileType { Unknown, File, Directory, Symlink }; // Metadata representation of a portable file placed down during installation struct PortableFileEntry { // Version 1.0 PortableFileType FileType = PortableFileType::Unknown; std::string SHA256; std::string SymlinkTarget; std::filesystem::path CurrentPath; void SetFilePath(const std::filesystem::path& path) { if (FileType != PortableFileType::Symlink) { m_filePath = std::filesystem::weakly_canonical(path); } else { m_filePath = path; } }; std::filesystem::path GetFilePath() const { return m_filePath; }; static PortableFileEntry CreateFileEntry(const std::filesystem::path& currentPath, const std::filesystem::path& targetPath, const std::string& sha256) { PortableFileEntry fileEntry; fileEntry.FileType = PortableFileType::File; fileEntry.CurrentPath = currentPath; fileEntry.SetFilePath(targetPath); if (sha256.empty()) { fileEntry.SHA256 = Utility::SHA256::ConvertToString(Utility::SHA256::ComputeHashFromFile(currentPath)); } else { fileEntry.SHA256 = sha256; } return fileEntry; } static PortableFileEntry CreateSymlinkEntry(const std::filesystem::path& symlinkPath, const std::filesystem::path& targetPath) { PortableFileEntry symlinkEntry; symlinkEntry.FileType = PortableFileType::Symlink; symlinkEntry.SetFilePath(symlinkPath); symlinkEntry.SymlinkTarget = targetPath.u8string(); return symlinkEntry; } static PortableFileEntry CreateDirectoryEntry(const std::filesystem::path& currentPath, const std::filesystem::path& directoryPath) { PortableFileEntry directoryEntry; directoryEntry.FileType = PortableFileType::Directory; directoryEntry.CurrentPath = currentPath; directoryEntry.SetFilePath(directoryPath); return directoryEntry; } private: std::filesystem::path m_filePath; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Reboot.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once namespace AppInstaller::Reboot { bool InitiateReboot(); // Registers the application to be restarted by Windows Error Reporting (WER) in case of an unexpected shutdown. bool RegisterRestartForWER(const std::string& commandLineArgs); // Unregisters the application from being restarted by Windows Error Reporting (WER). bool UnregisterRestartForWER(); // Runs a program when a user logs on. void WriteToRunOnceRegistry(const std::string& resumeId, const std::string& commandLine); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Regex.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Regex { // Options for regular expression use. enum class Options { None = 0, CaseInsensitive, }; // Stores the compiled regular expression. // All pattern strings are considered UTF-8. // All input strings are considered UTF-16, as this is what ICU operates on internally. struct Expression { Expression(); Expression(std::string_view pattern, Options options = Options::None); Expression(const Expression&); Expression& operator=(const Expression&); Expression(Expression&&) noexcept; Expression& operator=(Expression&&) noexcept; ~Expression(); // Determines if the expression contains a value. operator bool() const; // Returns a value indicating whether the *entire* input matches the expression. bool IsMatch(std::wstring_view input) const; // Replaces all matches in the input with the replacement. std::wstring Replace(std::wstring_view input, std::wstring_view replacement) const; // For each section of the input, invoke the given functor. This allows the caller // to iterate over the entire string, taking action as appropriate for each part. // The parameters are: // bool :: indicates whether this section was a match // string_view :: the text for the section // The functor should return true to continue the loop, or false to break it. void ForEach(std::wstring_view input, const std::function& f) const; private: struct impl; std::unique_ptr pImpl; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Rest.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::Rest { utility::string_t GetRestAPIBaseUri(std::string restApiUri); bool IsValidUri(const utility::string_t& restApiUri); utility::string_t AppendPathToUri(const utility::string_t& restApiUri, const utility::string_t& path); utility::string_t MakeQueryParam(std::string_view queryName, const std::string& queryValue); utility::string_t AppendQueryParamsToUri(const utility::string_t& uri, const std::map& queryParameters); std::vector GetUniqueItems(const std::vector& list); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/SelfManagement.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::SelfManagement { // Gets the stub preference for the current package. // Returns true if the package is set to prefer stubs. // Returns false if the package is set to prefer the full package, // or the current process is not packaged. bool IsStubPreferred(); // Sets the stub preference for the current package. // It is an error to set the preference if the process is not packaged, // or the preference can otherwise not be set (older version of Windows). void SetStubPreferred(bool preferStub); // Gets a value indicating whether the current package is the stub package. bool IsStubPackage(); } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/Settings.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::Settings { using namespace std::string_view_literals; namespace details { // A settings container. struct ISettingsContainer { virtual ~ISettingsContainer() = default; // Gets a stream containing the setting's value, if present. // If the setting does not exist, returns an empty value. virtual std::unique_ptr Get() = 0; // Sets the setting to the given value. virtual bool Set(std::string_view value) = 0; // Deletes the setting. virtual void Remove() = 0; // Gets the path to the setting, if reasonable. virtual std::filesystem::path PathTo() = 0; }; } // Allows settings to be classified and treated differently base on any number of factors. // Names should still be unique, as there is no guarantee made about types mapping to unique roots. enum class Type { // A Standard setting stream has no special requirements (limited to 8K contents and no embedded null characters). Standard, // A UserFile setting stream should be located in a file that is easily editable by the user. UserFile, // A settings stream that should not be modified except by admin privileges. Secure, // A settings stream that is encrypted. It does not require admin privileges to write to. Encrypted, // A setting stream has should be stored in a file, removing the limitations of the Standard type. StandardFile, }; // Converts the Type enum to a string. std::string_view ToString(Type type); // A stream definition, combining both type and path. // The well known values in Streams should be used by product code, while tests may directly create them. struct StreamDefinition { constexpr StreamDefinition(Type type, std::string_view name) : Type(type), Name(name) {} // The type of stream. Type Type; // The name is used as a file name in some situations. std::string_view Name; }; // A setting stream; provides access to functionality on the stream. struct Stream { // The set of well known settings streams. // Changing these values can result in data loss. // The set of sources as defined by the user. constexpr static StreamDefinition UserSources{ Type::Secure, "user_sources"sv }; // The metadata about all sources. constexpr static StreamDefinition SourcesMetadata{ Type::Standard, "sources_metadata"sv }; // The primary user settings file. constexpr static StreamDefinition PrimaryUserSettings{ Type::UserFile, "settings.json"sv }; // The backup user settings file. constexpr static StreamDefinition BackupUserSettings{ Type::UserFile, "settings.json.backup"sv }; // The admin settings. constexpr static StreamDefinition AdminSettings{ Type::Secure, "admin_settings"sv }; // The REST information cache. constexpr static StreamDefinition RestInformationCache{ Type::Encrypted, "rest_information"sv }; // Gets a Stream for the StreamDefinition. // If the stream is synchronized, attempts to Set the value can fail due to another writer // having changed the underlying stream. Stream(const StreamDefinition& streamDefinition); const StreamDefinition& Definition() const { return m_streamDefinition; } // Gets the stream if present. // If the setting stream does not exist, returns an empty value (see operator bool). std::unique_ptr Get(); // Sets the stream to the given value. // Returns true if successful; false if the underlying stream has changed. [[nodiscard]] bool Set(std::string_view value); // Deletes the setting stream. void Remove(); // Gets the name of the stream. std::string_view GetName() const; // Gets the path to the stream. std::filesystem::path GetPath() const; private: const StreamDefinition m_streamDefinition; std::unique_ptr m_container; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/StdErrLogger.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::Logging { // Sends logs to the stderr stream. struct StdErrLogger : ILogger { StdErrLogger() = default; ~StdErrLogger() = default; // ILogger std::string GetName() const override; void Write(Channel channel, Level level, std::string_view message) noexcept override; void WriteDirect(Channel channel, Level level, std::string_view message) noexcept override; // Adds OutputDebugStringLogger to the current Log static void Add(); // Removes OutputDebugStringLogger from the current Log static void Remove(); private: Level m_level = Level::Error; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/ThreadGlobals.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::ThreadLocalStorage { struct WingetThreadGlobals : public ThreadGlobals { WingetThreadGlobals() = default; virtual ~WingetThreadGlobals() = default; // Request that a sub ThreadGlobals be constructed from the given parent. struct create_sub_thread_globals_t {}; WingetThreadGlobals(WingetThreadGlobals& parent, create_sub_thread_globals_t); AppInstaller::Logging::DiagnosticLogger& GetDiagnosticLogger() override; void* GetTelemetryObject() override; AppInstaller::Logging::TelemetryTraceLogger& GetTelemetryLogger(); // Set Globals for Current Thread // Return RAII object with its ownership to set the AppInstaller ThreadLocalStorage back to previous state std::unique_ptr SetForCurrentThread() override; private: void Initialize(); std::shared_ptr m_pDiagnosticLogger; std::unique_ptr m_pTelemetryLogger; std::once_flag m_loggerInitOnceFlag; }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/TraceLogger.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::Logging { // Log ETW events for tracing. // Doesn't save events to a file on disk. struct TraceLogger : ILogger { TraceLogger() = default; ~TraceLogger() = default; // ILogger std::string GetName() const override; void Write(Channel channel, Level, std::string_view message) noexcept override; void WriteDirect(Channel channel, Level level, std::string_view message) noexcept override; // Adds a TraceLogger to the current Log static void Add(); }; } ================================================ FILE: src/AppInstallerCommonCore/Public/winget/UserSettings.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AppInstallerStrings.h" #include "AppInstallerLogging.h" #include "winget/Archive.h" #include "winget/GroupPolicy.h" #include "winget/Resources.h" #include "winget/ManifestCommon.h" #include #include #include #include #include #include #include #include "AppInstallerArchitecture.h" using namespace std::chrono_literals; using namespace std::string_view_literals; namespace AppInstaller::Settings { // The type of argument. enum class UserSettingsType { // Settings files don't exist. A file is created on the first call to the settings command. Default, // Loaded settings.json Standard, // Loaded settings.json.backup Backup, // Loaded from custom settings content Custom, }; // The visual style of the progress bar. enum class VisualStyle { NoVT, Retro, Accent, Rainbow, Sixel, Disabled, }; // The download code to use for *installers*. enum class InstallerDownloader { Default, WinInet, DeliveryOptimization, }; // Enum of settings. // Must start at 0 to enable direct access to variant in UserSettings. // Max must be last and unused. // How to add a setting // 1 - Add to enum. // 2 - Implement SettingMap specialization via SETTINGMAPPING_SPECIALIZATION // Validate will be called by ValidateAll without any more changes. enum class Setting : size_t { // Visual ProgressBarVisualStyle, AnonymizePathForDisplay, EnableSixelDisplay, // Source AutoUpdateTimeInMinutes, // Experimental EFExperimentalCmd, EFExperimentalArg, EFDirectMSI, EFResume, EFFonts, EFSourcePriority, // Telemetry TelemetryDisable, // Install behavior InstallScopePreference, InstallScopeRequirement, InstallArchitecturePreference, InstallArchitectureRequirement, InstallLocalePreference, InstallLocaleRequirement, InstallerTypePreference, InstallerTypeRequirement, InstallDefaultRoot, InstallSkipDependencies, ArchiveExtractionMethod, DisableInstallNotes, PortablePackageUserRoot, PortablePackageMachineRoot, MaxResumes, // Network NetworkDownloader, NetworkDOProgressTimeoutInSeconds, NetworkWingetAlternateSourceURL, // Logging LoggingLevelPreference, LoggingChannelPreference, LoggingFileAgeLimitInDays, LoggingFileTotalSizeLimitInMB, LoggingFileIndividualSizeLimitInMB, LoggingFileCountLimit, // Uninstall behavior UninstallPurgePortablePackage, // Download behavior DownloadDefaultDirectory, // Configure behavior ConfigureDefaultModuleRoot, // Interactivity InteractivityDisable, #ifndef AICLI_DISABLE_TEST_HOOKS // Debug EnableSelfInitiatedMinidump, KeepAllLogFiles, #endif Max }; namespace details { template struct SettingMapping { // json_t - type the setting in json. // value_t - the type of this setting. // DefaultValue - the value_t default value when setting is absent or semantically wrong. // Path - json path to the property. See Json::Path in json.h for syntax. So far, this is sufficient // but since is "brief" and "untested" we might implement our own if needed. // Validate - Function that does semantic validation. }; #define SETTINGMAPPING_SPECIALIZATION_POLICY(_setting_, _json_, _value_, _default_, _path_, _valuePolicy_) \ template <> \ struct SettingMapping<_setting_> \ { \ using json_t = _json_; \ using value_t = _value_; \ inline static const value_t DefaultValue = _default_; \ static constexpr std::string_view Path = _path_; \ static std::optional Validate(const json_t& value); \ static constexpr ValuePolicy Policy = _valuePolicy_; \ using policy_t = GroupPolicy::ValueType; \ static_assert(Policy == ValuePolicy::None || std::is_same::value); \ } #define SETTINGMAPPING_SPECIALIZATION(_setting_, _json_, _value_, _default_, _path_) \ SETTINGMAPPING_SPECIALIZATION_POLICY(_setting_, _json_, _value_, _default_, _path_, ValuePolicy::None) // Visual SETTINGMAPPING_SPECIALIZATION(Setting::ProgressBarVisualStyle, std::string, VisualStyle, VisualStyle::Accent, ".visual.progressBar"sv); SETTINGMAPPING_SPECIALIZATION(Setting::AnonymizePathForDisplay, bool, bool, true, ".visual.anonymizeDisplayedPaths"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EnableSixelDisplay, bool, bool, false, ".visual.enableSixels"sv); // Source SETTINGMAPPING_SPECIALIZATION_POLICY(Setting::AutoUpdateTimeInMinutes, uint32_t, std::chrono::minutes, 15min, ".source.autoUpdateIntervalInMinutes"sv, ValuePolicy::SourceAutoUpdateIntervalInMinutes); // Experimental SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalCmd, bool, bool, false, ".experimentalFeatures.experimentalCmd"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalArg, bool, bool, false, ".experimentalFeatures.experimentalArg"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFResume, bool, bool, false, ".experimentalFeatures.resume"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFFonts, bool, bool, false, ".experimentalFeatures.fonts"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFSourcePriority, bool, bool, false, ".experimentalFeatures.sourcePriority"sv); // Telemetry SETTINGMAPPING_SPECIALIZATION(Setting::TelemetryDisable, bool, bool, false, ".telemetry.disable"sv); // Install behavior SETTINGMAPPING_SPECIALIZATION(Setting::InstallArchitecturePreference, std::vector, std::vector, {}, ".installBehavior.preferences.architectures"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallArchitectureRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.architectures"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallScopePreference, std::string, Manifest::ScopeEnum, Manifest::ScopeEnum::User, ".installBehavior.preferences.scope"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallScopeRequirement, std::string, Manifest::ScopeEnum, Manifest::ScopeEnum::Unknown, ".installBehavior.requirements.scope"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocalePreference, std::vector, std::vector, {}, ".installBehavior.preferences.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocaleRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallerTypePreference, std::vector, std::vector, {}, ".installBehavior.preferences.installerTypes"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallerTypeRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.installerTypes"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallSkipDependencies, bool, bool, false, ".installBehavior.skipDependencies"sv); SETTINGMAPPING_SPECIALIZATION(Setting::ArchiveExtractionMethod, std::string, Archive::ExtractionMethod, Archive::ExtractionMethod::ShellApi, ".installBehavior.archiveExtractionMethod"sv); SETTINGMAPPING_SPECIALIZATION(Setting::DisableInstallNotes, bool, bool, false, ".installBehavior.disableInstallNotes"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortablePackageUserRoot, std::string, std::filesystem::path, {}, ".installBehavior.portablePackageUserRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortablePackageMachineRoot, std::string, std::filesystem::path, {}, ".installBehavior.portablePackageMachineRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallDefaultRoot, std::string, std::filesystem::path, {}, ".installBehavior.defaultInstallRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::MaxResumes, uint32_t, int, 3, ".installBehavior.maxResumes"sv); // Uninstall behavior SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurgePortablePackage, bool, bool, false, ".uninstallBehavior.purgePortablePackage"sv); // Download behavior SETTINGMAPPING_SPECIALIZATION(Setting::DownloadDefaultDirectory, std::string, std::filesystem::path, {}, ".downloadBehavior.defaultDownloadDirectory"sv); // Configure behavior SETTINGMAPPING_SPECIALIZATION(Setting::ConfigureDefaultModuleRoot, std::string, std::filesystem::path, {}, ".configureBehavior.defaultModuleRoot"sv); // Network SETTINGMAPPING_SPECIALIZATION(Setting::NetworkDownloader, std::string, InstallerDownloader, InstallerDownloader::Default, ".network.downloader"sv); SETTINGMAPPING_SPECIALIZATION(Setting::NetworkDOProgressTimeoutInSeconds, uint32_t, std::chrono::seconds, 60s, ".network.doProgressTimeoutInSeconds"sv); SETTINGMAPPING_SPECIALIZATION(Setting::NetworkWingetAlternateSourceURL, bool, bool, true, ".network.enableWingetAlternateSourceURL"sv); #ifndef AICLI_DISABLE_TEST_HOOKS // Debug SETTINGMAPPING_SPECIALIZATION(Setting::EnableSelfInitiatedMinidump, bool, bool, false, ".debugging.enableSelfInitiatedMinidump"sv); SETTINGMAPPING_SPECIALIZATION(Setting::KeepAllLogFiles, bool, bool, false, ".debugging.keepAllLogFiles"sv); #endif // Logging SETTINGMAPPING_SPECIALIZATION(Setting::LoggingLevelPreference, std::string, Logging::Level, Logging::Level::Info, ".logging.level"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingChannelPreference, std::vector, Logging::Channel, Logging::Channel::Defaults, ".logging.channels"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileAgeLimitInDays, uint32_t, std::chrono::hours, (7 * 24h), ".logging.file.ageLimitInDays"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileTotalSizeLimitInMB, uint32_t, uint32_t, 128, ".logging.file.totalSizeLimitInMB"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileIndividualSizeLimitInMB, uint32_t, uint32_t, 16, ".logging.file.individualSizeLimitInMB"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingFileCountLimit, uint32_t, uint32_t, 0, ".logging.file.countLimit"sv); // Interactivity SETTINGMAPPING_SPECIALIZATION(Setting::InteractivityDisable, bool, bool, false, ".interactivity.disable"sv); // Used to deduce the SettingVariant type; making a variant that includes std::monostate and all SettingMapping types. template inline auto Deduce(std::index_sequence) { return std::variant(I)>::value_t...>{}; } // Holds data of any type listed in a SettingMapping. using SettingVariant = decltype(Deduce(std::make_index_sequence(Setting::Max)>())); // Gets the index into the variant for the given Setting. constexpr inline size_t SettingIndex(Setting s) { return static_cast(s) + 1; } } // Representation of the parsed settings file. struct UserSettings { // Jsoncpp doesn't provide line number and column for an individual Json::Value node. struct Warning { Warning(StringResource::StringId message) : Message(message) {} Warning(StringResource::StringId message, std::string_view settingPath) : Message(message), Path(settingPath) {} Warning(StringResource::StringId message, std::string_view settingPath, std::string_view settingValue, bool isField = true) : Message(message), Path(settingPath), Data(settingValue), IsFieldWarning(isField) {} StringResource::StringId Message; Utility::LocIndString Path; Utility::LocIndString Data; bool IsFieldWarning = true; }; static UserSettings const& Instance(const std::optional& content = std::nullopt); static std::filesystem::path SettingsFilePath(bool forDisplay = false); UserSettings(const UserSettings&) = delete; UserSettings& operator=(const UserSettings&) = delete; UserSettings(UserSettings&&) = delete; UserSettings& operator=(UserSettings&&) = delete; UserSettingsType GetType() const { return m_type; } std::vector const& GetWarnings() const { return m_warnings; } void PrepareToShellExecuteFile() const; // Gets setting value, if its not in the map it returns the default value. template typename details::SettingMapping::value_t Get() const { auto itr = m_settings.find(S); if (itr == m_settings.end()) { return details::SettingMapping::DefaultValue; } return std::get(itr->second); } protected: UserSettingsType m_type = UserSettingsType::Default; std::vector m_warnings; std::map m_settings; UserSettings(const std::optional& content = std::nullopt); ~UserSettings() = default; }; const UserSettings* TryGetUser(); UserSettings const& User(); bool TryInitializeCustomUserSettings(std::string content); } ================================================ FILE: src/AppInstallerCommonCore/Reboot.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerLogging.h" #include "AppInstallerStrings.h" #include "Public/winget/Reboot.h" #include "Public/winget/Registry.h" #include #include using namespace AppInstaller::Registry; namespace AppInstaller::Reboot { namespace { constexpr std::wstring_view s_RunOnceRegistry = L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; } #ifndef AICLI_DISABLE_TEST_HOOKS static bool* s_InitiateRebootResult_TestHook_Override = nullptr; void TestHook_SetInitiateRebootResult_Override(bool* status) { s_InitiateRebootResult_TestHook_Override = status; } static bool* s_RegisterForRestartResult_TestHook_Override = nullptr; void TestHook_SetRegisterForRestartResult_Override(bool* status) { s_RegisterForRestartResult_TestHook_Override = status; } #endif bool InitiateReboot() { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_InitiateRebootResult_TestHook_Override) { return *s_InitiateRebootResult_TestHook_Override; } #endif wil::unique_handle hToken; TOKEN_PRIVILEGES pTokenPrivileges; // Get a token for this process. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { AICLI_LOG(Core, Error, << "OpenProcessToken error: " << GetLastError()); return false; } // Shutdown privilege must be enabled for this process. if (!LookupPrivilegeValueW(NULL, SE_SHUTDOWN_NAME, &pTokenPrivileges.Privileges[0].Luid)) { AICLI_LOG(Core, Error, << "LookupPrivilegeValue error: " << GetLastError()); return false; } pTokenPrivileges.PrivilegeCount = 1; pTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken.get(), FALSE, &pTokenPrivileges, 0, (PTOKEN_PRIVILEGES)NULL, 0)) { AICLI_LOG(Core, Error, << "AdjustTokenPrivilege error: " << GetLastError()); return false; } AICLI_LOG(Core, Info, << "Initiating reboot."); return ExitWindowsEx(EWX_RESTARTAPPS, SHTDN_REASON_MINOR_INSTALLATION); } bool RegisterRestartForWER(const std::string& commandLineArgs) { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_RegisterForRestartResult_TestHook_Override) { return *s_RegisterForRestartResult_TestHook_Override; } #endif HRESULT result = RegisterApplicationRestart(AppInstaller::Utility::ConvertToUTF16(commandLineArgs).c_str(), 0 /* Always restart process */); if (FAILED(result)) { AICLI_LOG(Core, Error, << "RegisterApplicationRestart failed with hr: " << result); return false; } else { AICLI_LOG(CLI, Info, << "Register for restart with command line args: " << commandLineArgs); return true; } } bool UnregisterRestartForWER() { HRESULT result = UnregisterApplicationRestart(); AICLI_LOG(CLI, Info, << "Application unregistered for restart."); if (FAILED(result)) { AICLI_LOG(Core, Error, << "RegisterApplicationRestart failed with hr: " << result); return false; } else { return true; } } void WriteToRunOnceRegistry(const std::string& resumeId, const std::string& commandLine) { THROW_HR_IF(E_UNEXPECTED, commandLine.size() > MAX_PATH); HKEY root = AppInstaller::Runtime::IsRunningAsAdmin() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; std::wstring subKey = std::wstring{ s_RunOnceRegistry }; Key key = Key::OpenIfExists(root, subKey, 0, KEY_ALL_ACCESS); if (!key) { key = Key::Create(root, subKey); } key.SetValue(Utility::ConvertToUTF16("WingetResume-" + resumeId), Utility::ConvertToUTF16(commandLine), REG_SZ); AICLI_LOG(CLI, Info, << "Set RunOnce registry with value: " << commandLine); } } ================================================ FILE: src/AppInstallerCommonCore/Regex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/Regex.h" #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerLanguageUtilities.h" #define WINGET_THROW_REGEX_ERROR_IF_FAILED(_err_,_func_) \ if (U_FAILURE(_err_)) \ { \ AICLI_LOG(Core, Error, << #_func_ " returned " << _err_); \ THROW_HR(APPINSTALLER_CLI_ERROR_ICU_REGEX_ERROR); \ } namespace AppInstaller::Regex { struct Expression::impl { using uregex_ptr = wil::unique_any; using utext_ptr = wil::unique_any; // Create caches the original ICU regex objects in a static map and hands out copies of them // when requested. Since we have a limited set, this is a very simple cache-all-forever pattern. static std::unique_ptr Create(std::string_view pattern, Options options) { struct key { std::string pattern; Options options = Options::None; bool operator<(const key& other) const { if (pattern < other.pattern) { return true; } else if (pattern == other.pattern) { return ToIntegral(options) < ToIntegral(other.options); } else { return false; } } }; struct statics { std::map map; wil::srwlock lock; }; static statics s_regex_cache; key requested; requested.pattern = pattern; requested.options = options; { // Attempt to find in the cache auto sharedLock = s_regex_cache.lock.lock_shared(); auto itr = s_regex_cache.map.find(requested); if (itr != s_regex_cache.map.end()) { return std::make_unique(itr->second); } } auto exclusiveLock = s_regex_cache.lock.lock_exclusive(); // Check if another thread created it while we waited for the lock. auto itr = s_regex_cache.map.find(requested); if (itr != s_regex_cache.map.end()) { return std::make_unique(itr->second); } else { return std::make_unique(s_regex_cache.map.emplace(std::move(requested), impl{ pattern, options }).first->second); } } impl(std::string_view pattern, Options options) { UErrorCode uec = U_ZERO_ERROR; utext_ptr patternUtext{ utext_openUTF8(nullptr, pattern.data(), pattern.length(), &uec) }; WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, utext_openUTF8); // For now, just handle the one option uint32_t flags = 0; if (options == Options::CaseInsensitive) { flags = UREGEX_CASE_INSENSITIVE; } UParseError parseError{}; m_regex.reset(uregex_openUText(patternUtext.get(), flags, &parseError, &uec)); if (U_FAILURE(uec)) { AICLI_LOG(Core, Error, << "uregex_openUText failed with error [" << uec << "] at line " << parseError.line << ", position " << parseError.offset << '\n' << pattern); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_REGEX_ERROR); } } impl(const impl& other) { UErrorCode uec = U_ZERO_ERROR; m_regex.reset(uregex_clone(other.m_regex.get(), &uec)); WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, uregex_clone); } impl& operator=(const impl& other) { *this = impl{ other }; return *this; } impl(impl&&) = default; impl& operator=(impl&&) = default; ~impl() = default; bool IsMatch(std::wstring_view input) const { UErrorCode uec = U_ZERO_ERROR; SetText(input); UBool result = uregex_matches(m_regex.get(), -1, &uec); WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, uregex_matches); return !!result; } std::wstring Replace(std::wstring_view input, std::wstring_view replacement) const { UErrorCode uec = U_ZERO_ERROR; SetText(input); std::u16string_view u16replacement = Convert(replacement); utext_ptr replacementUtext{ utext_openUChars(nullptr, u16replacement.data(), u16replacement.length(), &uec) }; WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, utext_openUTF8); utext_ptr resultUText{ uregex_replaceAllUText(m_regex.get(), replacementUtext.get(), nullptr, &uec) }; WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, uregex_replaceAllUText); int64_t cch = utext_nativeLength(resultUText.get()); std::wstring result(static_cast(cch), '\0'); utext_extract(resultUText.get(), 0, std::numeric_limits::max(), reinterpret_cast(&result[0]), static_cast(result.size()), &uec); WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, utext_extract); return result; } void ForEach(std::wstring_view input, const std::function&f) const { UErrorCode uec = U_ZERO_ERROR; SetText(input); int32_t startPos = 0; while (uregex_findNext(m_regex.get(), &uec)) { WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, uregex_findNext); int32_t pos = uregex_start(m_regex.get(), 0, &uec); WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, uregex_start); THROW_HR_IF(E_UNEXPECTED, pos == -1); // First, send off the unmatched part before the match if (pos > startPos) { if (!f(false, input.substr(startPos, static_cast(pos) - startPos))) { return; } } // Now send the matched part int32_t end = uregex_end(m_regex.get(), 0, &uec); WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, uregex_end); THROW_HR_IF(E_UNEXPECTED, end == -1); if (!f(true, input.substr(pos, static_cast(end) - pos))) { return; } startPos = end; } WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, uregex_findNext); // Finally, send any remaining part if (input.length() > static_cast(startPos)) { f(false, input.substr(startPos)); } } private: static std::u16string_view Convert(std::wstring_view input) { static_assert(sizeof(wchar_t) == sizeof(char16_t), "wchar_t and char16_t must be the same size"); return { reinterpret_cast(input.data()), input.size() }; } void SetText(std::wstring_view input) const { UErrorCode uec = U_ZERO_ERROR; std::u16string_view u16 = Convert(input); uregex_setText(m_regex.get(), u16.data(), static_cast(u16.length()), &uec); WINGET_THROW_REGEX_ERROR_IF_FAILED(uec, uregex_setText); } uregex_ptr m_regex; }; Expression::Expression() = default; Expression::Expression(std::string_view pattern, Options options) : pImpl(impl::Create(pattern, options)) {} Expression::Expression(const Expression& other) { if (other.pImpl) { pImpl = std::make_unique(*other.pImpl); } } Expression& Expression::operator=(const Expression& other) { return *this = Expression{ other }; } Expression::Expression(Expression&&) noexcept = default; Expression& Expression::operator=(Expression&&) noexcept = default; Expression::~Expression() = default; Expression::operator bool() const { return static_cast(pImpl); } bool Expression::IsMatch(std::wstring_view input) const { THROW_HR_IF(E_NOT_VALID_STATE, !pImpl); return pImpl->IsMatch(input); } std::wstring Expression::Replace(std::wstring_view input, std::wstring_view replacement) const { THROW_HR_IF(E_NOT_VALID_STATE, !pImpl); return pImpl->Replace(input, replacement); } void Expression::ForEach(std::wstring_view input, const std::function& f) const { THROW_HR_IF(E_NOT_VALID_STATE, !pImpl); return pImpl->ForEach(input, f); } } ================================================ FILE: src/AppInstallerCommonCore/Rest.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerStrings.h" #include "winget/Rest.h" #include namespace AppInstaller::Rest { utility::string_t GetRestAPIBaseUri(std::string uri) { // Trim if (!uri.empty()) { uri = AppInstaller::Utility::Trim(uri); // Remove trailing forward slash if (uri.back() == '/') { uri.pop_back(); } } // Encode the Uri return web::uri::encode_uri(JSON::GetUtilityString(uri)); } bool IsValidUri(const utility::string_t& restApiUri) { return web::uri::validate(restApiUri); } utility::string_t AppendPathToUri(const utility::string_t& restApiUri, const utility::string_t& path) { web::uri_builder builder(restApiUri); builder.append_path(path, true); return builder.to_string(); } utility::string_t MakeQueryParam(std::string_view queryName, const std::string& queryValue) { std::string queryParam; queryParam.append(queryName).append("=").append(queryValue); return utility::conversions::to_string_t(queryParam); } utility::string_t AppendQueryParamsToUri(const utility::string_t& uri, const std::map& queryParameters) { web::http::uri_builder builder{ uri }; for (auto& pair : queryParameters) { builder.append_query(MakeQueryParam(pair.first, pair.second), true); } return builder.to_string(); } std::vector GetUniqueItems(const std::vector& list) { std::set set; for (const auto& item : list) { set.emplace(item); } std::vector result{ set.begin(), set.end() }; return result; } } ================================================ FILE: src/AppInstallerCommonCore/Runtime.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerStrings.h" #include "Public/winget/UserSettings.h" #include "Public/winget/Registry.h" #include #define WINGET_DEFAULT_LOG_DIRECTORY "DiagOutputDir" namespace AppInstaller::Runtime { using namespace Utility; using namespace Settings; using namespace Filesystem; namespace { using namespace std::string_view_literals; constexpr std::string_view s_DefaultTempDirectory = "WinGet"sv; constexpr std::string_view s_SettingsFile_Relative = "Settings"sv; constexpr std::string_view s_SecureSettings_Base = "Microsoft\\WinGet"sv; constexpr std::string_view s_SecureSettings_UserRelative = "settings"sv; constexpr std::string_view s_SecureSettings_Relative_Unpackaged = "win"sv; constexpr std::string_view s_PortablePackageUserRoot_Base = "Microsoft"sv; constexpr std::string_view s_PortablePackageRoot = "WinGet"sv; constexpr std::string_view s_PortablePackagesDirectory = "Packages"sv; constexpr std::string_view s_LinksDirectory = "Links"sv; constexpr std::string_view s_FontsInstallDirectory = "Microsoft\\Windows\\Fonts"sv; constexpr std::string_view s_ConfigurationModulesDirectory = "Configuration\\Modules"sv; // Use production CLSIDs as a surrogate for repository location. #if USE_PROD_CLSIDS constexpr std::string_view s_ImageAssetsDirectoryRelative = "Assets\\WinGet"sv; #else constexpr std::string_view s_ImageAssetsDirectoryRelative = "Images"sv; #endif constexpr std::string_view s_CheckpointsDirectory = "Checkpoints"sv; constexpr std::string_view s_DevModeSubkey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock"sv; constexpr std::string_view s_AllowDevelopmentWithoutDevLicense = "AllowDevelopmentWithoutDevLicense"sv; #ifndef WINGET_DISABLE_FOR_FUZZING constexpr std::string_view s_SecureSettings_Relative_Packaged = "pkg"sv; #endif constexpr std::string_view s_RuntimePath_Unpackaged_DefaultState = "defaultState"sv; constexpr std::string_view s_UserProfileEnvironmentVariable = "%USERPROFILE%"; constexpr std::string_view s_LocalAppDataEnvironmentVariable = "%LOCALAPPDATA%"; constexpr std::string_view s_WindowsApps_Base = "Microsoft\\WindowsApps"sv; constexpr std::string_view s_WinGetDev_Exe = "wingetdev.exe"; constexpr std::string_view s_WinGet_Exe = "winget.exe"; constexpr std::string_view s_WinGetMCPDev_Exe = "WindowsPackageManagerMCPServerDev.exe"; constexpr std::string_view s_WinGetMCP_Exe = "WindowsPackageManagerMCPServer.exe"; static std::optional s_runtimePathStateName; static wil::srwlock s_runtimePathStateNameLock; // Gets the path to the root of the package containing the current process. std::filesystem::path GetPackagePath() { wchar_t packageFullName[PACKAGE_FULL_NAME_MAX_LENGTH + 1]; UINT32 nameLength = ARRAYSIZE(packageFullName); THROW_IF_WIN32_ERROR(GetPackageFullName(GetCurrentProcess(), &nameLength, packageFullName)); UINT32 pathLength = 0; LONG result = GetPackagePathByFullName(packageFullName, &pathLength, nullptr); THROW_HR_IF(HRESULT_FROM_WIN32(result), result != ERROR_INSUFFICIENT_BUFFER); std::unique_ptr buffer = std::make_unique(pathLength); THROW_IF_WIN32_ERROR(GetPackagePathByFullName(packageFullName, &pathLength, buffer.get())); return { buffer.get() }; } // Gets the path to the directory containing the currently executing binary file. std::filesystem::path GetBinaryDirectoryPath() { HMODULE moduleHandle = NULL; THROW_IF_WIN32_BOOL_FALSE(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCWSTR)&GetBinaryDirectoryPath, &moduleHandle)); // Get the path for this module. wil::unique_process_heap_string binaryPath; THROW_IF_FAILED(wil::GetModuleFileNameW(moduleHandle, binaryPath)); std::filesystem::path resultFilePath{ binaryPath.get() }; return resultFilePath.parent_path(); } std::unique_ptr GetPACKAGE_ID() { UINT32 bufferLength = 0; LONG gcpiResult = GetCurrentPackageId(&bufferLength, nullptr); THROW_HR_IF(E_UNEXPECTED, gcpiResult != ERROR_INSUFFICIENT_BUFFER); std::unique_ptr buffer = std::make_unique(bufferLength); gcpiResult = GetCurrentPackageId(&bufferLength, buffer.get()); if (FAILED_WIN32_LOG(gcpiResult)) { return {}; } return buffer; } // Gets the package name; only succeeds if running in a packaged context. std::string GetPackageName() { std::unique_ptr buffer = GetPACKAGE_ID(); if (!buffer) { return {}; } PACKAGE_ID* packageId = reinterpret_cast(buffer.get()); return Utility::ConvertToUTF8(packageId->name); } #ifndef AICLI_DISABLE_TEST_HOOKS static std::map s_Path_TestHook_Overrides; #endif // Gets the user's temp path std::filesystem::path GetPathToUserTemp(bool forDisplay) { if (forDisplay && Settings::User().Get()) { return "%TEMP%"; } else { wchar_t tempPath[MAX_PATH + 1]; DWORD tempChars = GetTempPathW(ARRAYSIZE(tempPath), tempPath); THROW_LAST_ERROR_IF(!tempChars); THROW_HR_IF(E_UNEXPECTED, tempChars > ARRAYSIZE(tempPath)); return { std::wstring_view{ tempPath, static_cast(tempChars) } }; } } // Gets the current user's SID for use in paths. std::filesystem::path GetUserSID() { auto userToken = wil::get_token_information(); wil::unique_hlocal_string sidString; THROW_IF_WIN32_BOOL_FALSE(ConvertSidToStringSidW(userToken->User.Sid, &sidString)); return { sidString.get() }; } std::string GetRuntimePathStateName() { std::string result; auto lock = s_runtimePathStateNameLock.lock_shared(); if (s_runtimePathStateName.has_value()) { result = s_runtimePathStateName.value(); } if (Utility::IsEmptyOrWhitespace(result)) { result = s_RuntimePath_Unpackaged_DefaultState; } return result; } } void SetRuntimePathStateName(std::string name) { auto suitablePathPart = MakeSuitablePathPart(name); auto lock = s_runtimePathStateNameLock.lock_exclusive(); s_runtimePathStateName.emplace(std::move(suitablePathPart)); } // Contains all of the paths that are common between the runtime contexts. PathDetails GetPathDetailsCommon(PathName path, bool forDisplay) { PathDetails result; // We should not create directories by default when they are retrieved for display purposes. result.Create = !forDisplay; bool mayBeInProfilePath = false; switch (path) { case PathName::UserProfile: result.Path = (forDisplay && Settings::User().Get()) ? s_UserProfileEnvironmentVariable : GetKnownFolderPath(FOLDERID_Profile); result.Create = false; break; case PathName::PortablePackageUserRoot: result.Path = Settings::User().Get(); if (result.Path.empty()) { result.Path = GetKnownFolderPath(FOLDERID_LocalAppData); result.Path /= s_PortablePackageUserRoot_Base; result.Path /= s_PortablePackageRoot; result.Path /= s_PortablePackagesDirectory; } mayBeInProfilePath = true; break; case PathName::PortablePackageMachineRoot: result.Path = Settings::User().Get(); if (result.Path.empty()) { result.Path = GetKnownFolderPath(FOLDERID_ProgramFiles); result.Path /= s_PortablePackageRoot; result.Path /= s_PortablePackagesDirectory; } break; case PathName::PortablePackageMachineRootX86: result.Path = Settings::User().Get(); if (result.Path.empty()) { result.Path = GetKnownFolderPath(FOLDERID_ProgramFilesX86); result.Path /= s_PortablePackageRoot; result.Path /= s_PortablePackagesDirectory; } break; case PathName::PortableLinksUserLocation: result.Path = GetKnownFolderPath(FOLDERID_LocalAppData); result.Path /= s_PortablePackageUserRoot_Base; result.Path /= s_PortablePackageRoot; result.Path /= s_LinksDirectory; mayBeInProfilePath = true; break; case PathName::PortableLinksMachineLocation: result.Path = GetKnownFolderPath(FOLDERID_ProgramFiles); result.Path /= s_PortablePackageRoot; result.Path /= s_LinksDirectory; break; case PathName::UserProfileDownloads: result.Path = GetKnownFolderPath(FOLDERID_Downloads); mayBeInProfilePath = true; break; case PathName::FontsUserInstallLocation: result.Path = GetKnownFolderPath(FOLDERID_LocalAppData); result.Path /= s_FontsInstallDirectory; mayBeInProfilePath = true; break; case PathName::FontsMachineInstallLocation: result.Path = GetKnownFolderPath(FOLDERID_Fonts); break; case PathName::ConfigurationModules: result.Path = Settings::User().Get(); if (result.Path.empty()) { result.Path = GetKnownFolderPath(FOLDERID_LocalAppData); result.Path /= s_SecureSettings_Base; result.Path /= s_ConfigurationModulesDirectory; } mayBeInProfilePath = true; break; default: THROW_HR(E_UNEXPECTED); } if (mayBeInProfilePath && forDisplay && Settings::User().Get()) { ReplaceProfilePathsWithEnvironmentVariable(result.Path); } return result; } #ifndef WINGET_DISABLE_FOR_FUZZING PathDetails GetPathDetailsForPackagedContext(PathName path, bool forDisplay) { PathDetails result; // We should not create directories by default when they are retrieved for display purposes. result.Create = !forDisplay; auto appStorage = winrt::Windows::Storage::ApplicationData::Current(); bool mayBeInProfilePath = false; switch (path) { case PathName::Temp: result.Path = GetPathToUserTemp(forDisplay) / s_DefaultTempDirectory; result.SetOwner(ACEPrincipal::CurrentUser); result.ACL[ACEPrincipal::System] = ACEPermissions::All; result.ACL[ACEPrincipal::Admins] = ACEPermissions::All; break; case PathName::LocalState: case PathName::UserFileSettings: result.Path.assign(appStorage.LocalFolder().Path().c_str()); mayBeInProfilePath = true; break; case PathName::StandardFileSettings: result.Path.assign(appStorage.LocalFolder().Path().c_str()); result.Path /= s_SettingsFile_Relative; mayBeInProfilePath = true; break; case PathName::DefaultLogLocation: // To enable UIF collection through Feedback hub, we must put our logs here. result.Path.assign(appStorage.LocalFolder().Path().c_str()); result.Path /= WINGET_DEFAULT_LOG_DIRECTORY; mayBeInProfilePath = true; break; case PathName::StandardSettings: result.Create = false; break; case PathName::SecureSettingsForRead: case PathName::SecureSettingsForWrite: result.Path = GetKnownFolderPath(FOLDERID_ProgramData); result.Path /= s_SecureSettings_Base; result.Path /= GetUserSID(); result.Path /= s_SecureSettings_UserRelative; result.Path /= s_SecureSettings_Relative_Packaged; result.Path /= GetPackageName(); if (path == PathName::SecureSettingsForWrite) { result.SetOwner(ACEPrincipal::Admins); // When running as system, we do not set current user permissions to avoid permission conflicts. if (!IsRunningAsSystem()) { result.ACL[ACEPrincipal::CurrentUser] = ACEPermissions::ReadExecute; } result.ACL[ACEPrincipal::System] = ACEPermissions::All; } else { result.Create = false; } break; case PathName::UserProfile: case PathName::PortablePackageMachineRoot: case PathName::PortablePackageMachineRootX86: case PathName::PortableLinksMachineLocation: case PathName::PortableLinksUserLocation: case PathName::PortablePackageUserRoot: case PathName::UserProfileDownloads: case PathName::FontsUserInstallLocation: case PathName::FontsMachineInstallLocation: case PathName::ConfigurationModules: result = GetPathDetailsCommon(path, forDisplay); break; case PathName::SelfPackageRoot: case PathName::ImageAssets: result.Path = GetPackagePath(); result.Create = false; if (path == PathName::ImageAssets) { result.Path /= s_ImageAssetsDirectoryRelative; } break; case PathName::CheckpointsLocation: result = GetPathDetailsForPackagedContext(PathName::LocalState, forDisplay); result.Path /= s_CheckpointsDirectory; break; case PathName::CLIExecutable: case PathName::MCPExecutable: result.Path = GetKnownFolderPath(FOLDERID_LocalAppData); result.Path /= s_WindowsApps_Base; result.Path /= GetPackageFamilyName(); if (path == PathName::CLIExecutable) { #if USE_PROD_CLSIDS result.Path /= s_WinGet_Exe; #else result.Path /= s_WinGetDev_Exe; #endif } else if (path == PathName::MCPExecutable) { #if USE_PROD_CLSIDS result.Path /= s_WinGetMCP_Exe; #else result.Path /= s_WinGetMCPDev_Exe; #endif } result.Create = false; mayBeInProfilePath = true; break; default: THROW_HR(E_UNEXPECTED); } if (mayBeInProfilePath && forDisplay && Settings::User().Get()) { ReplaceProfilePathsWithEnvironmentVariable(result.Path); } return result; } #endif PathDetails GetPathDetailsForUnpackagedContext(PathName path, bool forDisplay) { PathDetails result; // We should not create directories by default when they are retrieved for display purposes. result.Create = !forDisplay; bool anonymize = forDisplay && Settings::User().Get(); switch (path) { case PathName::Temp: case PathName::DefaultLogLocation: { result.Path = GetPathToUserTemp(forDisplay); result.Path /= s_DefaultTempDirectory; result.Path /= GetRuntimePathStateName(); if (path == PathName::Temp) { result.SetOwner(ACEPrincipal::CurrentUser); result.ACL[ACEPrincipal::System] = ACEPermissions::All; result.ACL[ACEPrincipal::Admins] = ACEPermissions::All; } } break; case PathName::LocalState: result = Filesystem::GetPathDetailsFor(Filesystem::PathName::UnpackagedLocalStateRoot, anonymize); result.Create = !forDisplay; result.Path /= GetRuntimePathStateName(); break; case PathName::StandardSettings: case PathName::StandardFileSettings: case PathName::UserFileSettings: result = Filesystem::GetPathDetailsFor(Filesystem::PathName::UnpackagedSettingsRoot, anonymize); result.Create = !forDisplay; result.Path /= GetRuntimePathStateName(); break; case PathName::SecureSettingsForRead: case PathName::SecureSettingsForWrite: result.Path = GetKnownFolderPath(FOLDERID_ProgramData); result.Path /= s_SecureSettings_Base; result.Path /= GetUserSID(); result.Path /= s_SecureSettings_UserRelative; result.Path /= s_SecureSettings_Relative_Unpackaged; result.Path /= GetRuntimePathStateName(); if (path == PathName::SecureSettingsForWrite) { result.SetOwner(ACEPrincipal::Admins); // When running as system, we do not set current user permissions to avoid permission conflicts. if (!IsRunningAsSystem()) { result.ACL[ACEPrincipal::CurrentUser] = ACEPermissions::ReadExecute; } result.ACL[ACEPrincipal::System] = ACEPermissions::All; } else { result.Create = false; } break; case PathName::UserProfile: case PathName::PortablePackageMachineRoot: case PathName::PortablePackageMachineRootX86: case PathName::PortableLinksMachineLocation: case PathName::PortableLinksUserLocation: case PathName::PortablePackageUserRoot: case PathName::UserProfileDownloads: case PathName::FontsUserInstallLocation: case PathName::FontsMachineInstallLocation: case PathName::ConfigurationModules: result = GetPathDetailsCommon(path, forDisplay); break; case PathName::SelfPackageRoot: case PathName::CLIExecutable: case PathName::MCPExecutable: case PathName::ImageAssets: result.Path = GetBinaryDirectoryPath(); result.Create = false; if (path == PathName::CLIExecutable) { result.Path /= s_WinGet_Exe; } else if (path == PathName::MCPExecutable) { result.Path /= s_WinGetMCP_Exe; } else if (path == PathName::ImageAssets) { result.Path /= s_ImageAssetsDirectoryRelative; if (!std::filesystem::is_directory(result.Path)) { result.Path.clear(); } } break; case PathName::CheckpointsLocation: result = GetPathDetailsForUnpackagedContext(PathName::LocalState, forDisplay); result.Path /= s_CheckpointsDirectory; break; default: THROW_HR(E_UNEXPECTED); } return result; } PathDetails GetPathDetailsFor(PathName path, bool forDisplay) { PathDetails result; #ifndef WINGET_DISABLE_FOR_FUZZING if (IsRunningInPackagedContext()) { result = GetPathDetailsForPackagedContext(path, forDisplay); } else #endif { result = GetPathDetailsForUnpackagedContext(path, forDisplay); } #ifndef AICLI_DISABLE_TEST_HOOKS // Override the value after letting the normal code path run auto itr = s_Path_TestHook_Overrides.find(path); if (itr != s_Path_TestHook_Overrides.end()) { result = itr->second; } #endif return result; } // Try to replace LOCALAPPDATA first as it is the likely location, fall back to trying USERPROFILE. void ReplaceProfilePathsWithEnvironmentVariable(std::filesystem::path& path) { if (!ReplaceCommonPathPrefix(path, GetKnownFolderPath(FOLDERID_LocalAppData), s_LocalAppDataEnvironmentVariable)) { ReplaceCommonPathPrefix(path, GetKnownFolderPath(FOLDERID_Profile), s_UserProfileEnvironmentVariable); } } std::filesystem::path GetNewTempFilePath() { GUID guid; THROW_IF_FAILED(CoCreateGuid(&guid)); WCHAR tempFileName[256]; THROW_HR_IF(E_UNEXPECTED, StringFromGUID2(guid, tempFileName, ARRAYSIZE(tempFileName)) == 0); auto tempFilePath = Runtime::GetPathTo(Runtime::PathName::Temp); tempFilePath /= tempFileName; return tempFilePath; } // Determines whether developer mode is enabled. // Does not account for the group policy value which takes precedence over this registry value. bool IsDevModeEnabled() { const auto& devModeSubKey = Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, s_DevModeSubkey, 0, KEY_READ|KEY_WOW64_64KEY); const auto& devModeEnabled = devModeSubKey[s_AllowDevelopmentWithoutDevLicense]; if (devModeEnabled.has_value()) { return devModeEnabled->GetValue() == 1; } else { return false; } } // Using "standard" user agent format // Keeping `winget-cli` for historical reasons Utility::LocIndString GetDefaultUserAgent() { std::ostringstream strstr; strstr << "winget-cli" << " WindowsPackageManager/" << GetClientVersion() << " DesktopAppInstaller/" << GetPackageVersion(); return Utility::LocIndString{ strstr.str() }; } Utility::LocIndString GetUserAgent(std::string_view caller) { auto escapedCaller = winrt::Windows::Foundation::Uri::EscapeComponent(Utility::ConvertToUTF16(caller)); std::ostringstream strstr; strstr << Utility::ConvertToUTF8(escapedCaller) << " WindowsPackageManager/" << GetClientVersion() << " DesktopAppInstaller/" << GetPackageVersion(); return Utility::LocIndString{ strstr.str() }; } #ifndef AICLI_DISABLE_TEST_HOOKS void TestHook_SetPathOverride(PathName target, const std::filesystem::path& path) { if (s_Path_TestHook_Overrides.count(target)) { s_Path_TestHook_Overrides[target].Path = path; } else { PathDetails details = GetPathDetailsFor(target); details.Path = path; s_Path_TestHook_Overrides[target] = std::move(details); } } void TestHook_SetPathOverride(PathName target, const PathDetails& details) { s_Path_TestHook_Overrides[target] = details; } void TestHook_ClearPathOverrides() { s_Path_TestHook_Overrides.clear(); } #endif } ================================================ FILE: src/AppInstallerCommonCore/SelfManagement.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/ExperimentalFeature.h" #include "winget/SelfManagement.h" #include "AppInstallerRuntime.h" namespace AppInstaller::SelfManagement { using namespace AppInstaller::Settings; using namespace std::string_view_literals; using namespace winrt::Windows::ApplicationModel; using namespace winrt::Windows::Management::Deployment; using namespace winrt::Windows::Services::Store; // Always use AppInstaller's package family name for wingetdev static constexpr std::wstring_view s_AppInstallerPfn = L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"sv; constexpr std::wstring_view s_RemoteServerFileName = L"DotNet\\ConfigurationRemotingServer.exe"; bool IsStubPreferred() { winrt::hstring packageFamilyName{ s_AppInstallerPfn }; PackageManager packageManager; auto packageManager9 = packageManager.try_as(); if (!packageManager9) { // If the API isn't present, then the only option is full package. return false; } auto preference = packageManager9.GetPackageStubPreference(packageFamilyName); return preference == PackageStubPreference::Stub; } void SetStubPreferred(bool preferStub) { winrt::hstring packageFamilyName{ s_AppInstallerPfn }; PackageManager packageManager; auto packageManager9 = packageManager.try_as(); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION), !packageManager9); packageManager9.SetPackageStubPreference(packageFamilyName, preferStub ? PackageStubPreference::Stub : PackageStubPreference::Full); } bool IsStubPackage() { // The right way to do it is to call FindPackage APIs from PackageManager, but that requires admin. std::filesystem::path serverPath = Runtime::GetPathTo(Runtime::PathName::SelfPackageRoot) / s_RemoteServerFileName; return !std::filesystem::exists(serverPath); } } ================================================ FILE: src/AppInstallerCommonCore/Settings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/Settings.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerSHA256.h" #include "Public/winget/Yaml.h" namespace AppInstaller::Settings { using namespace std::string_view_literals; using namespace Runtime; using namespace Utility; namespace { void ValidateSettingNamePath(const std::filesystem::path& name) { THROW_HR_IF(E_INVALIDARG, !name.has_relative_path()); THROW_HR_IF(E_INVALIDARG, name.has_root_path()); THROW_HR_IF(E_INVALIDARG, !name.has_filename()); } void LogSettingAction(std::string_view action, const StreamDefinition& def) { AICLI_LOG(Core, Verbose, << "Setting action: " << action << ", Type: " << ToString(def.Type) << ", Name: " << def.Name); } #ifndef WINGET_DISABLE_FOR_FUZZING // A settings container backed by the ApplicationDataContainer functionality. struct ApplicationDataSettingsContainer : public details::ISettingsContainer { using Container = winrt::Windows::Storage::ApplicationDataContainer; ApplicationDataSettingsContainer(const Container& container, const std::filesystem::path& name) { m_parentContainer = GetRelativeContainer(container, name.parent_path()); m_settingName = winrt::to_hstring(name.filename().c_str()); } static Container GetRelativeContainer(const Container& container, const std::filesystem::path& offset) { auto result = container; for (const auto& part : offset) { auto partHstring = winrt::to_hstring(part.c_str()); result = result.CreateContainer(partHstring, winrt::Windows::Storage::ApplicationDataCreateDisposition::Always); } return result; } std::unique_ptr Get() override { auto settingsValues = m_parentContainer.Values(); if (settingsValues.HasKey(m_settingName)) { auto value = winrt::unbox_value(settingsValues.Lookup(m_settingName)); return std::make_unique(Utility::ConvertToUTF8(value.c_str())); } else { return {}; } } bool Set(std::string_view value) override { m_parentContainer.Values().Insert(m_settingName, winrt::box_value(winrt::to_hstring(value))); return true; } void Remove() override { m_parentContainer.Values().Remove(m_settingName); } std::filesystem::path PathTo() override { THROW_HR(E_UNEXPECTED); } private: Container m_parentContainer = nullptr; winrt::hstring m_settingName; }; #endif // A settings container backed by the filesystem. struct FileSettingsContainer : public details::ISettingsContainer { FileSettingsContainer(std::filesystem::path root, const std::filesystem::path& name) : m_settingFile(std::move(root)) { m_settingFile /= name; } std::unique_ptr Get() override { if (std::filesystem::exists(m_settingFile)) { auto result = std::make_unique(m_settingFile, std::ios_base::in | std::ios_base::binary); THROW_LAST_ERROR_IF(result->fail()); return result; } else { return {}; } } bool Set(std::string_view value) override { EnsureParentPath(); std::ofstream stream(m_settingFile, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); THROW_LAST_ERROR_IF(stream.fail()); stream << value << std::flush; THROW_LAST_ERROR_IF(stream.fail()); return true; } void Remove() override { std::filesystem::remove(m_settingFile); } std::filesystem::path PathTo() override { return m_settingFile; } private: void EnsureParentPath() { std::filesystem::create_directories(m_settingFile.parent_path()); } std::filesystem::path m_settingFile; }; // A settings container that manages safely writing to its value with exchange semantics. // Only allows Set to succeed if the hash value of the setting is the same as the last time it was read. struct ExchangeSettingsContainer : public details::ISettingsContainer { ExchangeSettingsContainer(std::unique_ptr&& container, const std::string_view& name) : m_container(std::move(container)), m_name(name) {} std::unique_ptr Get() override { return GetInternal(m_hash); } bool Set(std::string_view value) override { THROW_HR_IF(E_UNEXPECTED, value.size() > std::numeric_limits::max()); // If Set is called without ever reading the value, then we can assume that caller wants // to overwrite it regardless. Also, we don't have any previous value to compare against // anyway so the only other option would be to always reject it. if (m_hash) { std::optional currentHash; std::ignore = GetInternal(currentHash); if (currentHash && !SHA256::AreEqual(m_hash.value(), currentHash.value())) { AICLI_LOG(Core, Verbose, << "Setting value for '" << m_name << "' has changed since last read; rejecting Set"); return false; } } SHA256::HashBuffer newHash = SHA256::ComputeHash(reinterpret_cast(value.data()), static_cast(value.size())); if (m_container->Set(value)) { m_hash = std::move(newHash); return true; } else { return false; } } void Remove() override { m_container->Remove(); m_hash.reset(); } std::filesystem::path PathTo() override { return m_container->PathTo(); } protected: std::optional GetAsString() { return GetAsStringInternal(m_hash); } std::string_view m_name; std::optional m_hash; private: std::optional GetAsStringInternal(std::optional& hashStorage) { std::unique_ptr stream = m_container->Get(); if (!stream) { // If no stream exists, then no hashing needs to be done. // Return an empty hash vector to indicate the attempted read but no result. hashStorage.emplace(); return std::nullopt; } std::string streamContents = Utility::ReadEntireStream(*stream); THROW_HR_IF(E_UNEXPECTED, streamContents.size() > std::numeric_limits::max()); hashStorage = SHA256::ComputeHash(reinterpret_cast(streamContents.c_str()), static_cast(streamContents.size())); return streamContents; } std::unique_ptr GetInternal(std::optional& hashStorage) { auto string = GetAsStringInternal(hashStorage); // Return a stream over the contents that we read in and hashed, to prevent a race. return string ? std::make_unique(string.value()) : nullptr; } std::unique_ptr m_container; }; // A settings container wrapper that enforces security. struct SecureSettingsContainer : public ExchangeSettingsContainer { constexpr static std::string_view NodeName_Sha256 = "SHA256"sv; SecureSettingsContainer(std::unique_ptr&& container, const std::string_view& name) : ExchangeSettingsContainer(std::move(container), name), m_secure(GetPathTo(PathName::SecureSettingsForRead), name) {} private: struct VerificationData { bool Found = false; SHA256::HashBuffer Hash; }; VerificationData GetVerificationData() { std::unique_ptr stream = m_secure.Get(); if (!stream) { return {}; } std::string streamContents = Utility::ReadEntireStream(*stream); YAML::Node document; try { document = YAML::Load(streamContents); } catch (const std::runtime_error& e) { AICLI_LOG(Core, Error, << "Secure setting metadata for '" << m_name << "' contained invalid YAML (" << e.what() << "):\n" << streamContents); return {}; } std::string hashString; try { hashString = document[NodeName_Sha256].as(); } catch (const std::runtime_error& e) { AICLI_LOG(Core, Error, << "Secure setting metadata for '" << m_name << "' contained invalid YAML (" << e.what() << "):\n" << streamContents); return {}; } VerificationData result; result.Found = true; result.Hash = SHA256::ConvertToBytes(hashString); return result; } void SetVerificationData(VerificationData data) { YAML::Emitter out; out << YAML::BeginMap; out << YAML::Key << NodeName_Sha256 << YAML::Value << SHA256::ConvertToString(data.Hash); out << YAML::EndMap; m_secure.Set(out.str()); } public: std::unique_ptr Get() override { std::unique_ptr stream = ExchangeSettingsContainer::Get(); if (!stream) { // If no stream exists, then no verification needs to be done. return stream; } VerificationData verData = GetVerificationData(); // This case should be very rare, so a very identifiable error is helpful. // Plus the text for this one is fairly on point for what has happened. THROW_HR_IF(SPAPI_E_FILE_HASH_NOT_IN_CATALOG, !verData.Found); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_DATA_CHECKSUM_ERROR), !SHA256::AreEqual(m_hash.value(), verData.Hash)); // ExchangeSettingsContainer already produces an in memory stream that we can use. return stream; } bool Set(std::string_view value) override { // Force the creation of the secure settings location with appropriate ACLs GetPathTo(PathName::SecureSettingsForWrite); bool exchangeResult = ExchangeSettingsContainer::Set(value); if (exchangeResult) { VerificationData verData; verData.Hash = m_hash.value(); SetVerificationData(verData); } return exchangeResult; } void Remove() override { ExchangeSettingsContainer::Remove(); m_secure.Remove(); } std::filesystem::path PathTo() override { THROW_HR(E_UNEXPECTED); } private: FileSettingsContainer m_secure; }; // A settings container wrapper that enforces privacy. struct EncryptedSettingsContainer : public ExchangeSettingsContainer { EncryptedSettingsContainer(std::unique_ptr&& container, const std::string_view& name) : ExchangeSettingsContainer(std::move(container), name) { } std::unique_ptr Get() override { std::optional stream = ExchangeSettingsContainer::GetAsString(); if (!stream) { // If no stream exists, then nothing needs to be done. return nullptr; } DATA_BLOB data{}; data.pbData = reinterpret_cast(stream->data()); data.cbData = static_cast(stream->size()); DATA_BLOB out{}; auto freeOut = wil::scope_exit([&]() { LocalFree(out.pbData); }); THROW_IF_WIN32_BOOL_FALSE(CryptUnprotectData(&data, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &out)); return std::make_unique(std::string{ reinterpret_cast(out.pbData), out.cbData }); } bool Set(std::string_view value) override { DATA_BLOB data{}; data.pbData = reinterpret_cast(const_cast(value.data())); data.cbData = static_cast(value.size()); DATA_BLOB out{}; auto freeOut = wil::scope_exit([&]() { LocalFree(out.pbData); }); THROW_IF_WIN32_BOOL_FALSE(CryptProtectData(&data, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &out)); return ExchangeSettingsContainer::Set(std::string_view{ reinterpret_cast(out.pbData), out.cbData }); } std::filesystem::path PathTo() override { THROW_HR(E_UNEXPECTED); } }; std::unique_ptr GetRawSettingsContainer(Type type, const std::string_view& name) { #ifndef WINGET_DISABLE_FOR_FUZZING if (IsRunningInPackagedContext()) { switch (type) { case Type::Standard: return std::make_unique( ApplicationDataSettingsContainer::GetRelativeContainer( winrt::Windows::Storage::ApplicationData::Current().LocalSettings(), GetPathTo(PathName::StandardSettings)), name); case Type::StandardFile: return std::make_unique(GetPathTo(PathName::StandardFileSettings), name); default: THROW_HR(E_UNEXPECTED); } } else #endif { switch (type) { case Type::Standard: case Type::StandardFile: return std::make_unique(GetPathTo(PathName::StandardSettings), name); default: THROW_HR(E_UNEXPECTED); } } } // The default is not a raw container, so we wrap some of the underlying containers to enable higher order behaviors. std::unique_ptr GetSettingsContainer(Type type, const std::string_view& name) { switch (type) { case Type::Standard: // Standard settings should use exchange semantics to prevent overwrites return std::make_unique(GetRawSettingsContainer(type, name), name); case Type::UserFile: // User file settings are not typically modified by us, so there is no need for exchange return std::make_unique(GetPathTo(PathName::UserFileSettings), name); case Type::Secure: // Secure settings add hash verification on reads on top of exchange semantics return std::make_unique(GetRawSettingsContainer(Type::Standard, name), name); case Type::Encrypted: // Encrypted settings add encryption on top of exchange semantics return std::make_unique(GetRawSettingsContainer(Type::StandardFile, name), name); case Type::StandardFile: // Standard settings should use exchange semantics to prevent overwrites return std::make_unique(GetRawSettingsContainer(type, name), name); default: THROW_HR(E_UNEXPECTED); } } std::unique_ptr GetSettingsContainer(const StreamDefinition& streamDefinition) { return GetSettingsContainer(streamDefinition.Type, streamDefinition.Name); } } std::string_view ToString(Type type) { switch (type) { case Type::Standard: return "Standard"sv; case Type::UserFile: return "UserFile"sv; case Type::Secure: return "Secure"sv; case Type::Encrypted: return "Encrypted"sv; case Type::StandardFile: return "StandardFile"sv; default: THROW_HR(E_UNEXPECTED); } } Stream::Stream(const StreamDefinition& streamDefinition) : m_streamDefinition(streamDefinition), m_container(GetSettingsContainer(streamDefinition)) { ValidateSettingNamePath(m_streamDefinition.Name); } std::unique_ptr Stream::Get() { LogSettingAction("Get", m_streamDefinition); return m_container->Get(); } [[nodiscard]] bool Stream::Set(std::string_view value) { LogSettingAction("Set", m_streamDefinition); return m_container->Set(value); } void Stream::Remove() { LogSettingAction("Remove", m_streamDefinition); m_container->Remove(); } std::string_view Stream::GetName() const { return m_streamDefinition.Name; } std::filesystem::path Stream::GetPath() const { return m_container->PathTo(); } } ================================================ FILE: src/AppInstallerCommonCore/StdErrLogger.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/StdErrLogger.h" namespace AppInstaller::Logging { namespace { static constexpr std::string_view s_StdErrLoggerName = "StdErrLogger"; } std::string StdErrLogger::GetName() const { return std::string{ s_StdErrLoggerName }; } void StdErrLogger::Write(Channel channel, Level level, std::string_view message) noexcept try { if (level >= m_level) { std::cerr << "[" << std::setw(GetMaxChannelNameLength()) << std::left << std::setfill(' ') << GetChannelName(channel) << "] " << message << std::endl; } } catch (...) { // Just eat any exceptions here; better than losing logs } void StdErrLogger::WriteDirect(Channel, Level level, std::string_view message) noexcept try { if (level >= m_level) { std::cerr.write(message.data(), static_cast(message.size())); } } catch (...) { // Just eat any exceptions here; better than losing logs } void StdErrLogger::Add() { Log().AddLogger(std::make_unique()); } void StdErrLogger::Remove() { Log().RemoveLogger(std::string{ s_StdErrLoggerName }); } } ================================================ FILE: src/AppInstallerCommonCore/Synchronization.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include namespace AppInstaller::Synchronization { // The amount of time that we wait in between checking for cancellation constexpr std::chrono::milliseconds s_CrossProcessInstallLock_WaitLoopTime = 250ms; CrossProcessLock::CrossProcessLock(std::string_view name) : CrossProcessLock(Utility::ConvertToUTF16(name)) { } CrossProcessLock::CrossProcessLock(const std::wstring& name) { m_mutex.create(name.c_str(), 0, SYNCHRONIZE); } CrossProcessLock::~CrossProcessLock() { Release(); } bool CrossProcessLock::Acquire(IProgressCallback& progress) { while (!progress.IsCancelledBy(CancelReason::Any)) { auto lock = m_mutex.acquire(nullptr, static_cast(std::chrono::duration_cast(s_CrossProcessInstallLock_WaitLoopTime).count())); if (lock) { m_lockThreadId = GetCurrentThreadId(); m_lock = std::move(lock); return true; } } return false; } void CrossProcessLock::Release() { if (m_lock) { // Ensure that we are in fact always releasing on the same thread that acquired the lock. // This is to force crashes rather than deadlocks in the event that we make a design error that leads to that. FAIL_FAST_IF(m_lockThreadId != GetCurrentThreadId()); m_lock.reset(); } } bool CrossProcessLock::TryAcquireNoWait() { auto lock = m_mutex.acquire(nullptr, 0); if (lock) { m_lockThreadId = GetCurrentThreadId(); m_lock = std::move(lock); return true; } return false; } CrossProcessLock::operator bool() const { return static_cast(m_lock); } } ================================================ FILE: src/AppInstallerCommonCore/Telemetry/TraceLogging.cpp ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. #include "pch.h" #include "TraceLogging.h" // GUID for Microsoft.PackageManager.Client : {c0cf606f-569b-5c20-27d9-88a745fa2175} TRACELOGGING_DEFINE_PROVIDER( g_hTraceProvider, "Microsoft.PackageManager.Client", (0xc0cf606f, 0x569b, 0x5c20, 0x27, 0xd9, 0x88, 0xa7, 0x45, 0xfa, 0x21, 0x75), TraceLoggingOptionMicrosoftTelemetry()); bool g_IsTelemetryProviderEnabled{}; UCHAR g_TelemetryProviderLevel{}; ULONGLONG g_TelemetryProviderMatchAnyKeyword{}; struct TraceProvider { TraceProvider(); ~TraceProvider(); }; TraceProvider g_TraceProvider{}; void WINAPI TelemetryProviderEnabledCallback( _In_ LPCGUID /*sourceId*/, _In_ ULONG isEnabled, _In_ UCHAR level, _In_ ULONGLONG matchAnyKeyword, _In_ ULONGLONG /*matchAllKeywords*/, _In_opt_ PEVENT_FILTER_DESCRIPTOR /*filterData*/, _In_opt_ PVOID /*callbackContext*/) { g_IsTelemetryProviderEnabled = !!isEnabled; g_TelemetryProviderLevel = level; g_TelemetryProviderMatchAnyKeyword = matchAnyKeyword; } TraceProvider::TraceProvider() { TraceLoggingRegisterEx(g_hTraceProvider, TelemetryProviderEnabledCallback, nullptr); } TraceProvider::~TraceProvider() { TraceLoggingUnregister(g_hTraceProvider); } ================================================ FILE: src/AppInstallerCommonCore/Telemetry/TraceLogging.h ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. #pragma once #include #include #include // Keywords #define KEYWORD_REPEATER 0x0000000000000001 #define KEYWORD_SCROLLER 0x0000000000000002 #define KEYWORD_PTR 0x0000000000000004 #define KEYWORD_SCROLLVIEWER 0x0000000000000008 #define KEYWORD_SWIPECONTROL 0x0000000000000010 #define KEYWORD_COMMANDBARFLYOUT 0x0000000000000020 // Common output formats #define TRACE_MSG_METH L"%s[0x%p]()\n" #define TRACE_MSG_METH_DBL L"%s[0x%p](%lf)\n" #define TRACE_MSG_METH_DBL_DBL L"%s[0x%p](%lf, %lf)\n" #define TRACE_MSG_METH_DBL_INT L"%s[0x%p](%lf, %d)\n" #define TRACE_MSG_METH_DBL_DBL_INT L"%s[0x%p](%lf, %lf, %d)\n" #define TRACE_MSG_METH_DBL_DBL_FLT L"%s[0x%p](%lf, %lf, %f)\n" #define TRACE_MSG_METH_DBL_DBL_STR L"%s[0x%p](%lf, %lf, %s)\n" #define TRACE_MSG_METH_FLT L"%s[0x%p](%f)\n" #define TRACE_MSG_METH_FLT_FLT L"%s[0x%p](%f, %f)\n" #define TRACE_MSG_METH_FLT_FLT_FLT L"%s[0x%p](%f, %f, %f)\n" #define TRACE_MSG_METH_FLT_FLT_FLT_FLT L"%s[0x%p](%f, %f, %f, %f)\n" #define TRACE_MSG_METH_FLT_FLT_STR_INT L"%s[0x%p](%f, %f, %s, %d)\n" #define TRACE_MSG_METH_INT L"%s[0x%p](%d)\n" #define TRACE_MSG_METH_INT_INT L"%s[0x%p](%d, %d)\n" #define TRACE_MSG_METH_PTR L"%s[0x%p](0x%p)\n" #define TRACE_MSG_METH_PTR_PTR L"%s[0x%p](0x%p, 0x%p)\n" #define TRACE_MSG_METH_PTR_DBL L"%s[0x%p](0x%p, %lf)\n" #define TRACE_MSG_METH_PTR_INT L"%s[0x%p](0x%p, %d)\n" #define TRACE_MSG_METH_PTR_STR L"%s[0x%p](0x%p, %s)\n" #define TRACE_MSG_METH_STR L"%s[0x%p](%s)\n" #define TRACE_MSG_METH_STR_STR L"%s[0x%p](%s, %s)\n" #define TRACE_MSG_METH_STR_DBL L"%s[0x%p](%s, %lf)\n" #define TRACE_MSG_METH_STR_FLT L"%s[0x%p](%s, %f)\n" #define TRACE_MSG_METH_STR_INT L"%s[0x%p](%s, %d)\n" #define TRACE_MSG_METH_STR_STR_STR L"%s[0x%p](%s, %s, %s)\n" #define TRACE_MSG_METH_STR_INT_INT L"%s[0x%p](%s, %d, %d)\n" #define TRACE_MSG_METH_STR_FLT_FLT L"%s[0x%p](%s, %f, %f)\n" #define TRACE_MSG_METH_STR_STR_FLT L"%s[0x%p](%s, %s, %f)\n" #define TRACE_MSG_METH_STR_STR_INT_INT L"%s[0x%p](%s, %s, %d, %d)\n" #define TRACE_MSG_METH_METH L"%s[0x%p] - calls %s()\n" #define TRACE_MSG_METH_METH_INT L"%s[0x%p] - calls %s(%d)\n" #define TRACE_MSG_METH_METH_STR L"%s[0x%p] - calls %s(%s)\n" #define TRACE_MSG_METH_METH_STR_STR L"%s[0x%p] - calls %s(%s, %s)\n" #define TRACE_MSG_METH_METH_FLT_STR L"%s[0x%p] - calls %s(%f, %s)\n" #define TRACE_MSG_METH_METH_FLT_FLT_FLT L"%s[0x%p] - calls %s(%f, %f, %f)\n" // Current method name #define METH_NAME StringUtil::Utf8ToUtf16(__FUNCTION__).c_str() // TraceLogging provider name for telemetry. #define TELEMETRY_PROVIDER_NAME "Microsoft.PackageManager.Client" TRACELOGGING_DECLARE_PROVIDER(g_hTraceProvider); extern bool g_IsTelemetryProviderEnabled; extern UCHAR g_TelemetryProviderLevel; extern ULONGLONG g_TelemetryProviderMatchAnyKeyword; ================================================ FILE: src/AppInstallerCommonCore/ThreadGlobals.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/ThreadGlobals.h" namespace AppInstaller::ThreadLocalStorage { using namespace AppInstaller::Logging; WingetThreadGlobals::WingetThreadGlobals(WingetThreadGlobals& parent, create_sub_thread_globals_t) { parent.Initialize(); m_pDiagnosticLogger = parent.m_pDiagnosticLogger; m_pTelemetryLogger = parent.m_pTelemetryLogger->CreateSubTraceLogger(); // Flip the initialization flag std::call_once(m_loggerInitOnceFlag, []() {}); } DiagnosticLogger& WingetThreadGlobals::GetDiagnosticLogger() { return *(m_pDiagnosticLogger); } void* WingetThreadGlobals::GetTelemetryObject() { return m_pTelemetryLogger.get(); } TelemetryTraceLogger& WingetThreadGlobals::GetTelemetryLogger() { return *(m_pTelemetryLogger); } std::unique_ptr WingetThreadGlobals::SetForCurrentThread() { Initialize(); return ThreadGlobals::SetForCurrentThread(); } void WingetThreadGlobals::Initialize() { try { std::call_once(m_loggerInitOnceFlag, [this]() { m_pDiagnosticLogger = std::make_unique(); m_pTelemetryLogger = std::make_unique(); // The above make_unique for TelemetryTraceLogger will either create an object or will throw which is caught below. m_pTelemetryLogger->Initialize(); }); } catch (...) { // May throw std::system_error if any condition prevents calls to call_once from executing as specified // May throw std::bad_alloc or any exception thrown by the constructor of TelemetryTraceLogger // Loggers are best effort and shouldn't block core functionality. So eat up the exceptions here } } } ================================================ FILE: src/AppInstallerCommonCore/TraceLogger.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/TraceLogger.h" #include "Public/AppInstallerTelemetry.h" #include "Public/winget/ThreadGlobals.h" namespace AppInstaller::Logging { void TraceLogger::Write(Channel channel, Level, std::string_view message) noexcept try { // Send to a string first to create a single block to log to a trace. std::stringstream strstr; strstr << std::chrono::system_clock::now() << " [" << std::setw(GetMaxChannelNameLength()) << std::left << std::setfill(' ') << GetChannelName(channel) << "] " << message << std::endl; TraceLoggingWriteActivity(g_hTraceProvider, "Diagnostics", Telemetry().GetActivityId(), Telemetry().GetParentActivityId(), TraceLoggingString(strstr.str().c_str(), "LogMessage")); } catch (...) { // Just eat any exceptions here; better to lose logs than functionality } void TraceLogger::WriteDirect(Channel, Level, std::string_view message) noexcept try { TraceLoggingWriteActivity(g_hTraceProvider, "Diagnostics", Telemetry().GetActivityId(), Telemetry().GetParentActivityId(), TraceLoggingCountedUtf8String(message.data(), static_cast(message.size()), "LogMessage")); } catch (...) { // Just eat any exceptions here; better to lose logs than functionality } std::string TraceLogger::GetName() const { return "Trace"; } void TraceLogger::Add() { Log().AddLogger(std::make_unique()); } } ================================================ FILE: src/AppInstallerCommonCore/UserSettings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AppInstallerRuntime.h" #include "AppInstallerLanguageUtilities.h" #include "AppInstallerLogging.h" #include "winget/JsonUtil.h" #include "winget/Settings.h" #include "winget/UserSettings.h" #include "winget/filesystem.h" #include "AppInstallerArchitecture.h" #include "winget/Locale.h" namespace AppInstaller::Settings { using namespace std::string_view_literals; using namespace Runtime; using namespace Utility; using namespace Logging; using namespace JSON; using namespace Filesystem; static constexpr std::string_view s_SettingEmpty = R"({ "$schema": "https://aka.ms/winget-settings.schema.json", // For documentation on these settings, see: https://aka.ms/winget-settings // "source": { // "autoUpdateIntervalInMinutes": 5 // }, })"sv; namespace { template inline std::string GetValueString(T value) { std::string convertedValue; if constexpr (std::is_arithmetic_v) { convertedValue = std::to_string(value); } else { convertedValue = value; } return convertedValue; } template<> inline std::string GetValueString(std::vector value) { std::string convertedValue = "["; bool first = true; for (auto const& entry : value) { if (first) { first = false; } else { convertedValue += ", "; } convertedValue += entry; } convertedValue += ']'; return convertedValue; } std::optional ParseSettingsContent(const std::string& content, std::string_view settingName, std::vector& warnings) { Json::Value root; Json::CharReaderBuilder builder; const std::unique_ptr reader(builder.newCharReader()); std::string error; if (reader->parse(content.c_str(), content.c_str() + content.size(), &root, &error)) { return root; } AICLI_LOG(Core, Error, << "Error parsing " << settingName << ": " << error); warnings.emplace_back(StringResource::String::SettingsWarningParseError, settingName, error, false); return {}; } std::optional ParseFile(const StreamDefinition& setting, std::vector& warnings) { try { auto stream = Stream{ setting }.Get(); if (stream) { std::string settingsContentStr = Utility::ReadEntireStream(*stream); return ParseSettingsContent(settingsContentStr, setting.Name, warnings); } } catch (const std::exception& e) { AICLI_LOG(Core, Error, << "Failed to read " << setting.Name << "; Reason: " << e.what()); } catch (...) { AICLI_LOG(Core, Error, << "Failed to read " << setting.Name << "; Reason unknown."); } return {}; } template std::optional::json_t> GetValueFromPolicy() { return GroupPolicies().GetValue::Policy>(); } template void Validate( Json::Value& root, std::map& settings, std::vector& warnings) { // jsoncpp doesn't support std::string_view yet. auto path = std::string(details::SettingMapping::Path); // Settings set by Group Policy override anything else. See if there is one. auto policyValue = GetValueFromPolicy(); if (policyValue.has_value()) { // If the value is valid, use it. // Otherwise, fall back to default. // In any case, we do not need to read the setting from the JSON. auto validatedValue = details::SettingMapping::Validate(policyValue.value()); if (validatedValue.has_value()) { // Add it to the map settings[S].emplace( std::forward::value_t>(validatedValue.value())); AICLI_LOG(Core, Verbose, << "Valid setting from Group Policy. Field: " << path << " Value: " << GetValueString(policyValue.value())); } else { auto valueAsString = GetValueString(policyValue.value()); AICLI_LOG(Core, Error, << "Invalid setting from Group Policy. Field: " << path << " Value: " << valueAsString); warnings.emplace_back(StringResource::String::SettingsWarningInvalidValueFromPolicy, path, valueAsString); } return; } const Json::Path jsonPath(path); Json::Value result = jsonPath.resolve(root); if (!result.isNull()) { auto jsonValue = GetValue::json_t>(result); if (jsonValue.has_value()) { auto validatedValue = details::SettingMapping::Validate(jsonValue.value()); if (validatedValue.has_value()) { // Finally add it to the map settings[S].emplace( std::forward::value_t>(validatedValue.value())); AICLI_LOG(Core, Verbose, << "Valid setting. Field: " << path << " Value: " << GetValueString(jsonValue.value())); } else { auto valueAsString = GetValueString(jsonValue.value()); AICLI_LOG(Core, Error, << "Invalid field value. Field: " << path << " Value: " << valueAsString); warnings.emplace_back(StringResource::String::SettingsWarningInvalidFieldValue, path, valueAsString); } } else { AICLI_LOG(Core, Error, << "Invalid field format. Field: " << path << " Using default"); warnings.emplace_back(StringResource::String::SettingsWarningInvalidFieldFormat, path); } } else { AICLI_LOG(Core, Verbose, << "Setting " << path << " not found. Using default"); } } template void ValidateAll( Json::Value& root, std::map& settings, std::vector& warnings, std::index_sequence) { // Use folding to call each setting validate function. (FoldHelper{}, ..., Validate(S)>(root, settings, warnings)); } std::optional ValidatePathValue(std::string_view value) { std::filesystem::path path = ConvertToUTF16(value); if (!path.is_absolute()) { return {}; } return path; } } namespace details { #define WINGET_VALIDATE_SIGNATURE(_setting_) \ std::optional::value_t> \ SettingMapping::Validate(const SettingMapping::json_t& value) // Stamps out a validate function that simply returns the input value. #define WINGET_VALIDATE_PASS_THROUGH(_setting_) \ WINGET_VALIDATE_SIGNATURE(_setting_) \ { \ return value; \ } WINGET_VALIDATE_SIGNATURE(AutoUpdateTimeInMinutes) { return std::chrono::minutes(value); } WINGET_VALIDATE_SIGNATURE(ProgressBarVisualStyle) { std::string lowerValue = ToLower(value); if (value == "accent") { return VisualStyle::Accent; } else if (value == "rainbow") { return VisualStyle::Rainbow; } else if (value == "retro") { return VisualStyle::Retro; } else if (value == "sixel") { return VisualStyle::Sixel; } else if (value == "disabled") { return VisualStyle::Disabled; } return {}; } WINGET_VALIDATE_PASS_THROUGH(EnableSixelDisplay) WINGET_VALIDATE_PASS_THROUGH(EFExperimentalCmd) WINGET_VALIDATE_PASS_THROUGH(EFExperimentalArg) WINGET_VALIDATE_PASS_THROUGH(EFDirectMSI) WINGET_VALIDATE_PASS_THROUGH(EFResume) WINGET_VALIDATE_PASS_THROUGH(EFFonts) WINGET_VALIDATE_PASS_THROUGH(EFSourcePriority) WINGET_VALIDATE_PASS_THROUGH(AnonymizePathForDisplay) WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable) WINGET_VALIDATE_PASS_THROUGH(InteractivityDisable) WINGET_VALIDATE_PASS_THROUGH(InstallSkipDependencies) WINGET_VALIDATE_PASS_THROUGH(DisableInstallNotes) WINGET_VALIDATE_PASS_THROUGH(UninstallPurgePortablePackage) WINGET_VALIDATE_PASS_THROUGH(NetworkWingetAlternateSourceURL) WINGET_VALIDATE_PASS_THROUGH(MaxResumes) WINGET_VALIDATE_PASS_THROUGH(LoggingFileTotalSizeLimitInMB) WINGET_VALIDATE_PASS_THROUGH(LoggingFileIndividualSizeLimitInMB) WINGET_VALIDATE_PASS_THROUGH(LoggingFileCountLimit) #ifndef AICLI_DISABLE_TEST_HOOKS WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) WINGET_VALIDATE_PASS_THROUGH(KeepAllLogFiles) #endif WINGET_VALIDATE_SIGNATURE(PortablePackageUserRoot) { return ValidatePathValue(value); } WINGET_VALIDATE_SIGNATURE(PortablePackageMachineRoot) { return ValidatePathValue(value); } WINGET_VALIDATE_SIGNATURE(ArchiveExtractionMethod) { static constexpr std::string_view s_archiveExtractionMethod_shellApi = "shellApi"; static constexpr std::string_view s_archiveExtractionMethod_tar = "tar"; if (Utility::CaseInsensitiveEquals(value, s_archiveExtractionMethod_tar)) { return Archive::ExtractionMethod::Tar; } else if (Utility::CaseInsensitiveEquals(value, s_archiveExtractionMethod_shellApi)) { return Archive::ExtractionMethod::ShellApi; } return {}; } WINGET_VALIDATE_SIGNATURE(InstallArchitecturePreference) { std::vector archs; for (auto const& i : value) { Utility::Architecture arch = Utility::ConvertToArchitectureEnum(i); if (Utility::IsApplicableArchitecture(arch) == Utility::InapplicableArchitecture) { return {}; } archs.emplace_back(arch); } return archs; } WINGET_VALIDATE_SIGNATURE(InstallArchitectureRequirement) { return SettingMapping::Validate(value); } WINGET_VALIDATE_SIGNATURE(InstallScopePreference) { static constexpr std::string_view s_scope_user = "user"; static constexpr std::string_view s_scope_machine = "machine"; if (Utility::CaseInsensitiveEquals(value, s_scope_user)) { return Manifest::ScopeEnum::User; } else if (Utility::CaseInsensitiveEquals(value, s_scope_machine)) { return Manifest::ScopeEnum::Machine; } return {}; } WINGET_VALIDATE_SIGNATURE(InstallScopeRequirement) { return SettingMapping::Validate(value); } WINGET_VALIDATE_SIGNATURE(InstallLocalePreference) { for (auto const& entry : value) { if (!Locale::IsWellFormedBcp47Tag(entry)) { return {}; } } return value; } WINGET_VALIDATE_SIGNATURE(InstallLocaleRequirement) { return SettingMapping::Validate(value); } WINGET_VALIDATE_SIGNATURE(InstallerTypePreference) { std::vector installerTypes; for (auto const& i : value) { Manifest::InstallerTypeEnum installerType = Manifest::ConvertToInstallerTypeEnum(i); if (installerType == Manifest::InstallerTypeEnum::Unknown) { return {}; } installerTypes.emplace_back(installerType); } return installerTypes; } WINGET_VALIDATE_SIGNATURE(InstallerTypeRequirement) { return SettingMapping::Validate(value); } WINGET_VALIDATE_SIGNATURE(InstallDefaultRoot) { return ValidatePathValue(value); } WINGET_VALIDATE_SIGNATURE(DownloadDefaultDirectory) { return ValidatePathValue(value); } WINGET_VALIDATE_SIGNATURE(ConfigureDefaultModuleRoot) { return ValidatePathValue(value); } WINGET_VALIDATE_SIGNATURE(NetworkDownloader) { static constexpr std::string_view s_downloader_default = "default"; static constexpr std::string_view s_downloader_wininet = "wininet"; static constexpr std::string_view s_downloader_do = "do"; if (Utility::CaseInsensitiveEquals(value, s_downloader_default)) { return InstallerDownloader::Default; } else if (Utility::CaseInsensitiveEquals(value, s_downloader_wininet)) { return InstallerDownloader::WinInet; } else if (Utility::CaseInsensitiveEquals(value, s_downloader_do)) { return InstallerDownloader::DeliveryOptimization; } return {}; } WINGET_VALIDATE_SIGNATURE(NetworkDOProgressTimeoutInSeconds) { return std::chrono::seconds(value); } WINGET_VALIDATE_SIGNATURE(LoggingLevelPreference) { // logging preference possible values static constexpr std::string_view s_logging_verbose = "verbose"; static constexpr std::string_view s_logging_info = "info"; static constexpr std::string_view s_logging_warning = "warning"; static constexpr std::string_view s_logging_error = "error"; static constexpr std::string_view s_logging_critical = "critical"; if (Utility::CaseInsensitiveEquals(value, s_logging_verbose)) { return Level::Verbose; } else if (Utility::CaseInsensitiveEquals(value, s_logging_info)) { return Level::Info; } else if (Utility::CaseInsensitiveEquals(value, s_logging_warning)) { return Level::Warning; } else if (Utility::CaseInsensitiveEquals(value, s_logging_error)) { return Level::Error; } else if (Utility::CaseInsensitiveEquals(value, s_logging_critical)) { return Level::Crit; } return {}; } WINGET_VALIDATE_SIGNATURE(LoggingChannelPreference) { Logging::Channel result = Logging::Channel::None; for (auto const& entry : value) { result |= GetChannelFromName(entry); } return result; } WINGET_VALIDATE_SIGNATURE(LoggingFileAgeLimitInDays) { return value * 24h; } } #ifndef AICLI_DISABLE_TEST_HOOKS static UserSettings* s_UserSettings_Override = nullptr; void SetUserSettingsOverride(UserSettings* value) { s_UserSettings_Override = value; } #endif static std::atomic_bool s_userSettingsInitialized{ false }; static std::atomic_bool s_userSettingsInInitialization{ false }; UserSettings const& UserSettings::Instance(const std::optional& content) { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_UserSettings_Override) { return *s_UserSettings_Override; } #endif if (!s_userSettingsInitialized) { s_userSettingsInInitialization = true; } static UserSettings userSettings(content); s_userSettingsInitialized = true; s_userSettingsInInitialization = false; return userSettings; } const UserSettings* TryGetUser() { if (s_userSettingsInitialized) { return &UserSettings::Instance(); } // Try to initialize UserSettings, return nullptr if it's already in initialization. if (s_userSettingsInInitialization) { return nullptr; } return &UserSettings::Instance(); } UserSettings const& User() { return UserSettings::Instance(); } bool TryInitializeCustomUserSettings(std::string content) { if (s_userSettingsInitialized || s_userSettingsInInitialization) { return false; } return UserSettings::Instance(std::move(content)).GetType() == UserSettingsType::Custom; } UserSettings::UserSettings(const std::optional& content) : m_type(UserSettingsType::Default) { Json::Value settingsRoot = Json::Value::nullSingleton(); // Settings can be loaded from settings.json or settings.json.backup files. // 0 - Use default (empty) settings if disabled by group policy. // if // 1 - Use passed in settings content if available. // else // 2 - Use settings.json if exists and passes parsing. // 3 - Use settings.backup.json if settings.json fails to parse. // finally // 4 - Use default (empty) if both settings files fail to load. if (!GroupPolicies().IsEnabled(TogglePolicy::Policy::Settings)) { AICLI_LOG(Core, Info, << "Ignoring settings file due to group policy. Using default values."); return; } if (content.has_value()) { auto settingsJson = ParseSettingsContent(content.value(), "CustomSettings", m_warnings); if (settingsJson.has_value()) { AICLI_LOG(Core, Info, << "Settings loaded from custom settings"); m_type = UserSettingsType::Custom; settingsRoot = settingsJson.value(); } } else { auto settingsJson = ParseFile(Stream::PrimaryUserSettings, m_warnings); if (settingsJson.has_value()) { AICLI_LOG(Core, Info, << "Settings loaded from " << Stream::PrimaryUserSettings.Name); m_type = UserSettingsType::Standard; settingsRoot = settingsJson.value(); } // Settings didn't parse or doesn't exist, try with backup. if (settingsRoot.isNull()) { auto settingsBackupJson = ParseFile(Stream::BackupUserSettings, m_warnings); if (settingsBackupJson.has_value()) { AICLI_LOG(Core, Info, << "Settings loaded from " << Stream::BackupUserSettings.Name); m_warnings.emplace_back(StringResource::String::SettingsWarningLoadedBackupSettings); m_type = UserSettingsType::Backup; settingsRoot = settingsBackupJson.value(); } else { // Settings and back up didn't parse or exist. If they exist then warn the user. auto settingsPath = Stream{ Stream::PrimaryUserSettings }.GetPath(); auto backupPath = Stream{ Stream::BackupUserSettings }.GetPath(); if (std::filesystem::exists(settingsPath) || std::filesystem::exists(backupPath)) { m_warnings.emplace_back(StringResource::String::SettingsWarningUsingDefault); } } } } if (!settingsRoot.isNull()) { ValidateAll(settingsRoot, m_settings, m_warnings, std::make_index_sequence(Setting::Max)>()); } else { AICLI_LOG(Core, Info, << "Valid settings file not found. Using default values."); } } void UserSettings::PrepareToShellExecuteFile() const { UserSettingsType userSettingType = GetType(); if (userSettingType == UserSettingsType::Default) { Stream primarySettings{ Stream::PrimaryUserSettings }; // Create settings file if it doesn't exist. if (!std::filesystem::exists(primarySettings.GetPath())) { std::ignore = primarySettings.Set(s_SettingEmpty); AICLI_LOG(Core, Info, << "Created new settings file"); } } else if (userSettingType == UserSettingsType::Standard) { // Settings file was loaded correctly, create backup. auto from = SettingsFilePath(); auto to = Stream{ Stream::BackupUserSettings }.GetPath(); std::filesystem::copy_file(from, to, std::filesystem::copy_options::overwrite_existing); AICLI_LOG(Core, Info, << "Copied settings to backup file"); } } std::filesystem::path UserSettings::SettingsFilePath(bool forDisplay) { auto path = Stream{ Stream::PrimaryUserSettings }.GetPath(); if (forDisplay && Settings::User().Get()) { ReplaceCommonPathPrefix(path, GetKnownFolderPath(FOLDERID_LocalAppData), "%LOCALAPPDATA%"); } return path; } } ================================================ FILE: src/AppInstallerCommonCore/packages.config ================================================  ================================================ FILE: src/AppInstallerCommonCore/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/AppInstallerCommonCore/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "TraceLogging.h" #define YAML_DECLARE_STATIC #include // TODO: See if we can get down to having just one JSON parser... #include #pragma warning( push ) #pragma warning ( disable : 4458 4100 4702 6031 26439 ) #include #include #include #include #pragma warning( pop ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WINGET_DISABLE_FOR_FUZZING #pragma warning( push ) #pragma warning ( disable : 26495 26439 ) #include #include #include #pragma warning( pop ) #endif #pragma warning( push ) #pragma warning ( disable : 6001 6285 6287 6340 6387 6388 26451 26495 28196 ) #include #include #include #include #include #include #include #include #include #pragma warning( pop ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Stream/buffer helper APIs #include #include #include #include ================================================ FILE: src/AppInstallerRepositoryCore/ARPCorrelation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/ARPCorrelation.h" #include "winget/ARPCorrelationAlgorithms.h" #include "winget/Manifest.h" #include "winget/NameNormalization.h" #include "winget/RepositorySearch.h" #include "winget/RepositorySource.h" using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Correlation { namespace { constexpr double MatchingThreshold = 0.5; constexpr double MinimumDifferentiationThreshold = 0.05; IARPMatchConfidenceAlgorithm& InstanceInternal(std::optional algorithmOverride = {}) { static WordsEditDistanceMatchConfidenceAlgorithm s_algorithm; static IARPMatchConfidenceAlgorithm* s_override = nullptr; if (algorithmOverride.has_value()) { s_override = algorithmOverride.value(); } if (s_override) { return *s_override; } else { return s_algorithm; } } } IARPMatchConfidenceAlgorithm& IARPMatchConfidenceAlgorithm::Instance() { return InstanceInternal(); } #ifndef AICLI_DISABLE_TEST_HOOKS void IARPMatchConfidenceAlgorithm::OverrideInstance(IARPMatchConfidenceAlgorithm* algorithmOverride) { InstanceInternal(algorithmOverride); } void IARPMatchConfidenceAlgorithm::ResetInstance() { InstanceInternal(nullptr); } #endif // Find the best match using heuristics ARPHeuristicsCorrelationResult FindARPEntryForNewlyInstalledPackageWithHeuristics( const Manifest::Manifest& manifest, const std::vector& arpEntries) { // TODO: In the future we can make different passes with different algorithms until we find a match return FindARPEntryForNewlyInstalledPackageWithHeuristics(manifest, arpEntries, IARPMatchConfidenceAlgorithm::Instance()); } ARPHeuristicsCorrelationResult FindARPEntryForNewlyInstalledPackageWithHeuristics( const AppInstaller::Manifest::Manifest& manifest, const std::vector& arpEntries, IARPMatchConfidenceAlgorithm& algorithm) { if (arpEntries.empty()) { AICLI_LOG(Repo, Warning, << "Empty ARP entries given"); return {}; } AICLI_LOG(Repo, Verbose, << "Looking for best match in ARP for manifest " << manifest.Id); algorithm.Init(manifest); ARPHeuristicsCorrelationResult result; result.Measures.reserve(arpEntries.size()); for (const auto& arpEntry : arpEntries) { auto score = algorithm.ComputeConfidence(arpEntry); AICLI_LOG(Repo, Verbose, << "Match confidence for " << arpEntry.Entry->GetProperty(PackageProperty::Id) << ": " << score); result.Measures.emplace_back(CorrelationMeasure{ score, arpEntry.Entry->GetLatestVersion() }); } std::sort(result.Measures.begin(), result.Measures.end(), [](const CorrelationMeasure& a, const CorrelationMeasure& b) { return a.Measure > b.Measure; }); if (result.Measures[0].Measure < MatchingThreshold) { AICLI_LOG(Repo, Verbose, << "Maximum score [" << result.Measures[0].Measure << "] is lower than threshold [" << MatchingThreshold << "]"); result.Reason = "maximum score below threshold"; } else if (result.Measures.size() >= 2 && (result.Measures[0].Measure - result.Measures[1].Measure) < MinimumDifferentiationThreshold) { AICLI_LOG(Repo, Verbose, << "Top two scores, [" << result.Measures[0].Measure << "] and [" << result.Measures[1].Measure << "] are not significantly different [" << MinimumDifferentiationThreshold << "]"); result.Reason = "top two scores are not significantly different"; } else { AICLI_LOG(Repo, Verbose, << "Best match is " << result.Measures[0].Package->GetProperty(PackageVersionProperty::Id)); result.Package = result.Measures[0].Package; result.Reason = "heuristics match"; } return result; } void ARPCorrelationData::CapturePreInstallSnapshot() { ProgressCallback empty; Repository::Source preInstallARP = Repository::Source(PredefinedSource::ARP); preInstallARP.Open(empty); for (const auto& entry : preInstallARP.Search({}).Matches) { auto installed = entry.Package->GetInstalled()->GetLatestVersion(); if (installed) { m_preInstallSnapshot.emplace_back(std::make_tuple( installed->GetProperty(PackageVersionProperty::Id), installed->GetProperty(PackageVersionProperty::Version), installed->GetProperty(PackageVersionProperty::Channel))); } } std::sort(m_preInstallSnapshot.begin(), m_preInstallSnapshot.end()); } void ARPCorrelationData::CapturePostInstallSnapshot() { ProgressCallback empty; m_postInstallSnapshotSource = Repository::Source(PredefinedSource::ARP); m_postInstallSnapshotSource.Open(empty); for (auto& entry : m_postInstallSnapshotSource.Search({}).Matches) { auto installed = entry.Package->GetInstalled()->GetLatestVersion(); if (installed) { auto entryKey = std::make_tuple( installed->GetProperty(PackageVersionProperty::Id), installed->GetProperty(PackageVersionProperty::Version), installed->GetProperty(PackageVersionProperty::Channel)); auto itr = std::lower_bound(m_preInstallSnapshot.begin(), m_preInstallSnapshot.end(), entryKey); m_postInstallSnapshot.emplace_back(entry.Package->GetInstalled(), itr == m_preInstallSnapshot.end() || *itr != entryKey); } } } ARPCorrelationResult ARPCorrelationData::CorrelateForNewlyInstalled(const Manifest::Manifest& manifest, const ARPCorrelationSettings& settings) { AICLI_LOG(Repo, Verbose, << "Finding ARP entry matching newly installed package"); // Also attempt to find the entry based on the manifest data SearchRequest manifestSearchRequest; AppInstaller::Manifest::Manifest::string_t defaultPublisher; if (manifest.DefaultLocalization.Contains(Localization::Publisher)) { defaultPublisher = manifest.DefaultLocalization.Get(); } // The default localization must contain the name or we cannot do this lookup if (manifest.DefaultLocalization.Contains(Localization::PackageName)) { AppInstaller::Manifest::Manifest::string_t defaultName = manifest.DefaultLocalization.Get(); manifestSearchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, defaultName, defaultPublisher)); for (const auto& loc : manifest.Localizations) { if (loc.Contains(Localization::PackageName) || loc.Contains(Localization::Publisher)) { manifestSearchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, loc.Contains(Localization::PackageName) ? loc.Get() : defaultName, loc.Contains(Localization::Publisher) ? loc.Get() : defaultPublisher)); } } } std::set productCodes; std::set upgradeCodes; for (const auto& installer : manifest.Installers) { if (!installer.ProductCode.empty()) { // Add each ProductCode only once if (productCodes.insert(installer.ProductCode).second) { manifestSearchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, installer.ProductCode)); } } for (const auto& appsAndFeaturesEntry : installer.AppsAndFeaturesEntries) { if (!appsAndFeaturesEntry.DisplayName.empty()) { manifestSearchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, appsAndFeaturesEntry.DisplayName, appsAndFeaturesEntry.Publisher.empty() ? defaultPublisher : appsAndFeaturesEntry.Publisher)); } // Add each ProductCode and UpgradeCode only once; if (!appsAndFeaturesEntry.ProductCode.empty() && productCodes.insert(appsAndFeaturesEntry.ProductCode).second) { manifestSearchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, appsAndFeaturesEntry.ProductCode)); } if (!appsAndFeaturesEntry.UpgradeCode.empty() && upgradeCodes.insert(appsAndFeaturesEntry.UpgradeCode).second) { manifestSearchRequest.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::UpgradeCode, MatchType::Exact, appsAndFeaturesEntry.UpgradeCode)); } } } SearchResult findByManifest; // Don't execute this search if it would just find everything if (!manifestSearchRequest.IsForEverything()) { findByManifest = m_postInstallSnapshotSource.Search(manifestSearchRequest); } // Cross reference the changes with the search results std::vector> packagesInBoth; for (const auto& change : m_postInstallSnapshot) { if (change.IsNewOrUpdated) { for (const auto& byManifest : findByManifest.Matches) { if (change.Entry->IsSame(byManifest.Package->GetInstalled().get())) { packagesInBoth.emplace_back(change.Entry); break; } } } } // We now have all of the package changes; time to report them. // // The set of cases we could have for finding packages based on the manifest: // 0 packages :: The manifest data does not match the ARP information. // 1 package :: Golden path; this should be what we installed. // 2+ packages :: The data in the manifest is either too broad or we have // a problem with our name normalization. // Find the package that we are going to log ARPCorrelationResult result; // TODO: Find a good way to consider the other heuristics in these stats. result.ChangesToARP = std::count_if(m_postInstallSnapshot.begin(), m_postInstallSnapshot.end(), [](const ARPEntry& e) { return e.IsNewOrUpdated; }); result.MatchesInARP = findByManifest.Matches.size(); result.CountOfIntersectionOfChangesAndMatches = packagesInBoth.size(); // If there is only a single common package (changed and matches), it is almost certainly the correct one. if (settings.AllowNormalization && packagesInBoth.size() == 1) { result.Package = packagesInBoth[0]->GetLatestVersion(); result.Reason = "normalization match and new/changed"; } // If it wasn't changed but we still find a match, that is the best thing to report. else if (settings.AllowNormalization && findByManifest.Matches.size() == 1) { result.Package = findByManifest.Matches[0].Package->GetInstalled()->GetLatestVersion(); result.Reason = "normalization match (not new/changed)"; } else if (settings.AllowSingleChange && result.ChangesToARP == 1) { result.Package = std::find_if(m_postInstallSnapshot.begin(), m_postInstallSnapshot.end(), [](const ARPEntry& e) { return e.IsNewOrUpdated; })->Entry->GetLatestVersion(); result.Reason = "only new/changed value"; } else { // We were not able to find an exact match, so we now run some heuristics // to try and match the package with some ARP entry by assigning them scores. AICLI_LOG(Repo, Verbose, << "No exact ARP match found. Trying to find one with heuristics"); result = FindARPEntryForNewlyInstalledPackageWithHeuristics(manifest, m_postInstallSnapshot); } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/ARPCorrelationAlgorithms.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/ARPCorrelationAlgorithms.h" using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Correlation { using WordSequence = WordsEditDistanceMatchConfidenceAlgorithm::WordSequence; namespace { // A simple matrix class to hold score tables without having to allocate multiple arrays. struct Matrix { Matrix(size_t rows, size_t columns) : m_rows(rows), m_columns(columns), m_data(rows* columns) {} double& At(size_t i, size_t j) { return m_data[i * m_columns + j]; } private: size_t m_rows; size_t m_columns; std::vector m_data; }; double EditDistanceScore(const std::vector& s1, const std::vector& s2) { // Naive implementation of edit distance (scaled over the sequence size). // This considers only the operations of adding and removing elements. if (s1.empty() || s2.empty()) { return 0; } // distance[i, j] = distance between s1[0:i] and s2[0:j] // We don't need to hold more than two rows at a time, but it's simpler to keep the whole table. Matrix distance(s1.size() + 1, s2.size() + 1); for (size_t i = 0; i < s1.size(); ++i) { for (size_t j = 0; j < s2.size(); ++j) { double& d = distance.At(i, j); if (s1[i] == s2[j]) { // If the two elements are equal, the distance is the same as from one element before. // In case we are on the first element of one of the two sequences, the distance is // equal to the cost of adding all the previous elements in the other if (i == 0) { d = static_cast(j); } else if (j == 0) { d = static_cast(i); } else { d = distance.At(i - 1, j - 1); } } else { // If the two elements are distinct, the score is the cost of removing the last element // in one sequence plus the cost of editing the remainder of both. if (i > 0 && j > 0) { d = 1 + std::min(distance.At(i - 1, j), distance.At(i, j - 1)); } else if (i > 0) { d = 1 + distance.At(i - 1, j); } else if (j > 0) { d = 1 + distance.At(i, j - 1); } else { // Remove one and add the other d = 2; } } } } // Maximum distance is equal to the sum of both lengths (removing all elements from one and adding all the elements from the other). // We use that to scale to [0,1]. // A smaller distance represents a higher match, so we subtract from 1 for the final score double editDistance = distance.At(s1.size() - 1, s2.size() - 1); return 1 - editDistance / (static_cast(s1.size()) + static_cast(s2.size())); } } WordsEditDistanceMatchConfidenceAlgorithm::NameAndPublisher::NameAndPublisher(const WordSequence& name, const WordSequence& publisher) : Name(name), Publisher(publisher) { NamePublisher.insert(NamePublisher.end(), publisher.begin(), publisher.end()); NamePublisher.insert(NamePublisher.end(), name.begin(), name.end()); } WordsEditDistanceMatchConfidenceAlgorithm::NameAndPublisher::NameAndPublisher(WordSequence&& name, WordSequence&& publisher) : Name(std::move(name)), Publisher(std::move(publisher)) { NamePublisher.insert(NamePublisher.end(), publisher.begin(), publisher.end()); NamePublisher.insert(NamePublisher.end(), name.begin(), name.end()); } void WordsEditDistanceMatchConfidenceAlgorithm::Init(const AppInstaller::Manifest::Manifest& manifest) { // We will use the name and publisher from each localization. m_namesAndPublishers.clear(); WordSequence defaultPublisher; if (manifest.DefaultLocalization.Contains(Manifest::Localization::Publisher)) { defaultPublisher = NormalizeAndPreparePublisher(manifest.DefaultLocalization.Get()); } if (manifest.DefaultLocalization.Contains(Manifest::Localization::PackageName)) { WordSequence defaultName = NormalizeAndPrepareName(manifest.DefaultLocalization.Get()); m_namesAndPublishers.emplace_back(defaultName, defaultPublisher); for (const auto& loc : manifest.Localizations) { if (loc.Contains(Manifest::Localization::PackageName) || loc.Contains(Manifest::Localization::Publisher)) { auto name = loc.Contains(Manifest::Localization::PackageName) ? NormalizeAndPrepareName(loc.Get()) : defaultName; auto publisher = loc.Contains(Manifest::Localization::Publisher) ? NormalizeAndPreparePublisher(loc.Get()) : defaultPublisher; m_namesAndPublishers.emplace_back(std::move(name), std::move(publisher)); } } } } double WordsEditDistanceMatchConfidenceAlgorithm::ComputeConfidence(const ARPEntry& arpEntry) const { // Name and Publisher are available as multi properties, but for ARP entries there will only be 0 or 1 values. NameAndPublisher arpNameAndPublisher( NormalizeAndPrepareName(arpEntry.Entry->GetLatestVersion()->GetProperty(PackageVersionProperty::Name).get()), NormalizeAndPreparePublisher(arpEntry.Entry->GetLatestVersion()->GetProperty(PackageVersionProperty::Publisher).get())); // Get the best score across all localizations double bestMatchingScore = 0; for (const auto& manifestNameAndPublisher : m_namesAndPublishers) { // Sometimes the publisher may be included in the name, for example Microsoft PowerToys as opposed to simply PowerToys. // This may happen both in the ARP entry and the manifest. We try adding it in case it is in one but not in both. auto nameScore = EditDistanceScore(manifestNameAndPublisher.Name, arpNameAndPublisher.Name); // Ignore cases where the name is not at all similar to avoid matching due to publisher only if (nameScore < m_nameMatchingScoreMinThreshold) { continue; } auto publisherScore = EditDistanceScore(manifestNameAndPublisher.Publisher, arpNameAndPublisher.Publisher); auto namePublisherScore = std::max( EditDistanceScore(manifestNameAndPublisher.NamePublisher, arpNameAndPublisher.Name), EditDistanceScore(manifestNameAndPublisher.Name, arpNameAndPublisher.NamePublisher)); // Use the best between considering name and publisher as a single string or separately. auto score = std::max( nameScore * m_nameMatchingScoreWeight + publisherScore * (1 - m_nameMatchingScoreWeight), namePublisherScore); bestMatchingScore = std::max(bestMatchingScore, score); } // Factor in whether this entry is new auto result = bestMatchingScore * m_stringMatchingWeight + (arpEntry.IsNewOrUpdated ? 1 : 0) * (1 - m_stringMatchingWeight); return result; } WordSequence WordsEditDistanceMatchConfidenceAlgorithm::PrepareString(std::string_view s) const { return Utility::SplitIntoWords(Utility::FoldCase(s)); } WordSequence WordsEditDistanceMatchConfidenceAlgorithm::NormalizeAndPrepareName(std::string_view name) const { return PrepareString(m_normalizer.NormalizeName(name).Name()); } WordSequence WordsEditDistanceMatchConfidenceAlgorithm::NormalizeAndPreparePublisher(std::string_view publisher) const { return PrepareString(m_normalizer.NormalizePublisher(publisher)); } } ================================================ FILE: src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj ================================================ true true true 15.0 {5eb88068-5fb9-4e69-89b2-72dbc5e068f9} Win32Proj AppInstallerRepositoryCore 10.0.26100.0 10.0.17763.0 true Debug ARM64 Debug Win32 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Debug x64 Release x64 StaticLibrary true true false true false false true false Spectre Spectre Spectre Spectre Spectre Spectre true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING Disabled _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;_DEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) true true true true true true false false false Windows Windows _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;WIN32;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) true true true false Windows MaxSpeed true true _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) true true true true true true false false false false false false true true false Windows Windows Windows MaxSpeed true true _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) true true true true true true false false false MultiThreaded MultiThreaded MultiThreaded false false false true true false Windows Windows Windows Create This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters ================================================ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {2d97761a-4967-4ece-b5fd-d96f346731ed} {7996cf09-4e36-4009-b48d-53ffc3bfa956} {cff561cd-211b-4cab-87f5-39359eef1e8d} {69ce2e35-fe7f-41af-bd47-91a70131d167} {aef8989c-44fd-4848-ae2c-c81d3f2e2c72} {dc8b6163-e9b5-4d05-87bc-d6098afe0f92} {7f16f6e0-12c6-4bb9-885e-3f8b666d5f74} {15a1ee83-4118-40fe-ba43-c54d7185d505} {8a93b62f-d065-4048-b43a-a2b14b70c330} {6bcbaf7a-289f-4d0b-b128-67bef903745c} {15639b2c-ce61-4a18-995a-a73cf1a5817e} {9d8095ed-07de-4bc9-bfe7-630b781586d0} {2cc20cdb-dcb2-4e0e-b04f-e2d838146100} {aa7315bc-4eb0-4280-9572-f5a25f6e73ad} {dcae9c55-cdd7-4381-8acd-3554896608a5} {e31c8e5b-ed2c-43c8-b91b-db8ec4c52f71} {84a55def-9fb8-4c90-8d5a-2cedc171940b} {edef5ff7-9bfe-48f8-a179-e343d1a8b57f} {4f5950e2-1713-4c1e-84ce-f868cfcb3a68} {d9d70cf5-ce04-4db2-a0ec-970dd0ad22b6} {f131c993-1136-4f4c-85a9-8606c6d297a8} {78cf34a8-b868-4393-ad9c-630940569186} {f05e19bb-2161-4ab0-9d04-2dfa2d3eb3c6} {21da32f5-b918-436e-96a9-465525f90259} {b2e78f3d-931e-432c-8485-255b1dbc9db7} {f610927a-6f1d-42c5-9ad9-b59790091944} {a3f9c7ed-f487-40d6-9ee7-e9a052e55c29} {f42acfce-4fc0-435b-a9a2-bf5528aecbcc} {7fd6c265-81c0-4c6e-87b2-24bef117e21d} {34442899-29e5-4183-96ba-a1e8740146be} {8edd7018-8836-4b15-84c1-998391e19038} {7464e3ff-7a60-4bb6-8806-70562382043b} {da801426-6d3b-40ca-b204-152e7e18067b} {1a1efb9f-7332-4094-bb98-a4c51ea68b24} {c29ae113-5eb6-41af-b64d-fac925dcf2c2} {2075b51e-aa11-473a-bae0-e0d4366f926b} Header Files Microsoft Microsoft\Schema Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Header Files Microsoft Microsoft Microsoft\Schema\1_0 Microsoft\Schema\1_1 Microsoft\Schema\1_1 Microsoft\Schema\1_1 Microsoft\Schema\1_1 Microsoft Header Files Microsoft\Schema\1_1 Microsoft Microsoft Microsoft\Schema\1_2 Microsoft\Schema\1_2 Microsoft\Schema\1_2 Microsoft\Schema\1_2 Rest\Schema\1_0 Rest\Schema Rest Rest Rest Rest\Schema\1_0\Json Rest\Schema\1_0\Json Rest\Schema\1_0\Json Rest\Schema Microsoft\Schema\1_3 Microsoft\Schema\1_3 Header Files Header Files Microsoft Rest\Schema Rest\Schema\1_1 Rest\Schema\1_1\Json Rest\Schema\1_1\Json Microsoft Public\winget Public\winget Public\winget Header Files Microsoft\Schema\1_4 Microsoft\Schema\1_4 Header Files Header Files Public\winget Public\winget Public\winget Public\winget Microsoft\Schema\1_5 Microsoft\Schema\1_5 Header Files Microsoft\Schema\1_0 Microsoft\Schema\1_6 Microsoft\Schema\1_6 Microsoft\Schema\1_6 Microsoft\Schema\Portable_1_0 Microsoft\Schema\Portable_1_0 Microsoft\Schema Rest\Schema\1_4\Json Rest\Schema\1_4 Rest\Schema\1_4\Json Rest\Schema\1_5\Json Rest\Schema\1_5 Rest\Schema Rest\Schema Microsoft\Schema Microsoft Microsoft\Schema\Pinning_1_0 Microsoft\Schema\Pinning_1_0 Public\winget Header Files Public\winget Rest\Schema\1_6\Json Rest\Schema\1_6 Microsoft\Schema\1_7 Microsoft\Schema Microsoft\Schema\Checkpoint_1_0 Microsoft\Schema\Checkpoint_1_0 Microsoft\Schema\Checkpoint_1_0 Public\winget Header Files Rest\Schema\1_7 Rest\Schema Public\winget Public\winget Public\winget Public\winget Public\winget Rest\Schema\1_7\Json Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema Microsoft Microsoft Rest\Schema\1_9\Json Rest\Schema\1_9 Rest\Schema\1_10 Rest\Schema\1_10\Json Rest\Schema\1_12 Rest\Schema\1_12\Json Rest Header Files Source Files Microsoft Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Microsoft\Schema\1_0 Source Files Microsoft Microsoft Microsoft\Schema\1_0 Microsoft\Schema\1_1 Microsoft\Schema\1_0 Microsoft\Schema\1_1 Microsoft Source Files Microsoft\Schema\1_1 Microsoft Microsoft Microsoft\Schema\1_2 Microsoft\Schema\1_2 Rest\Schema\1_0 Rest Rest Rest Rest\Schema\1_0\Json Rest\Schema\1_0\Json Rest\Schema\1_0\Json Microsoft\Schema\1_3 Source Files Source Files Rest\Schema Microsoft Rest\Schema\1_1 Rest\Schema\1_1\Json Rest\Schema\1_1\Json Microsoft Source Files Source Files Microsoft\Schema\1_4 Microsoft\Schema\1_4 Source Files Source Files Source Files Source Files Source Files Microsoft\Schema\1_5 Source Files Microsoft\Schema\1_6 Microsoft\Schema\1_6 Microsoft Microsoft\Schema\Portable_1_0 Microsoft\Schema\Portable_1_0 Rest\Schema\1_4\Json Rest\Schema\1_4 Rest\Schema\1_4\Json Rest\Schema\1_5\Json Rest\Schema\1_5 Rest\Schema Rest\Schema Source Files Source Files Microsoft\Schema\Pinning_1_0 Source Files Source Files Rest\Schema\1_6\Json Rest\Schema\1_6 Microsoft\Schema\1_7 Source Files Microsoft\Schema\Checkpoint_1_0 Microsoft\Schema\Checkpoint_1_0 Source Files Microsoft\Schema\Checkpoint_1_0 Microsoft\Schema\Pinning_1_0 Rest\Schema\1_7 Rest\Schema Source Files Source Files Rest\Schema\1_7\Json Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft\Schema Microsoft\Schema\2_0 Microsoft\Schema\2_0 Microsoft Microsoft Rest\Schema\1_9\Json Rest\Schema\1_9 Rest\Schema\1_10 Rest\Schema\1_10\Json Rest\Schema\1_12 Rest\Schema\1_12\Json Rest Source Files Microsoft ================================================ FILE: src/AppInstallerRepositoryCore/ArpVersionValidation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "pch.h" #include "ArpVersionValidation.h" #include namespace AppInstaller::Repository { namespace { std::vector GetArpVersionRangesByPackageRowId(const Microsoft::SQLiteIndex* index, Microsoft::SQLiteIndex::IdType packageRowId, const Utility::VersionAndChannel& excludeVersionAndChannel = {}) { std::vector result; auto versionKeys = index->GetVersionKeysById(packageRowId); for (auto const& versionKey : versionKeys) { // For manifest update, the manifest to be updated does not need to be checked. // In unlikely cases if both version 1.0.0 and 1.0 of the same package exist, we compare raw values here as what sqlite index does. if (versionKey.VersionAndChannel.GetVersion().ToString() == excludeVersionAndChannel.GetVersion().ToString() && versionKey.VersionAndChannel.GetChannel().ToString() == excludeVersionAndChannel.GetChannel().ToString()) { continue; } auto arpMinVersion = index->GetPropertyByPrimaryId(versionKey.ManifestId, PackageVersionProperty::ArpMinVersion).value_or(""); auto arpMaxVersion = index->GetPropertyByPrimaryId(versionKey.ManifestId, PackageVersionProperty::ArpMaxVersion).value_or(""); // Either both empty or both not empty THROW_HR_IF(E_UNEXPECTED, arpMinVersion.empty() != arpMaxVersion.empty()); if (!arpMinVersion.empty() && !arpMaxVersion.empty()) { result.emplace_back(Utility::VersionRange{ Utility::Version{ std::move(arpMinVersion) }, Utility::Version{ std::move(arpMaxVersion) } }); } } return result; } } void ValidateManifestArpVersion(const Microsoft::SQLiteIndex* index, const Manifest::Manifest& manifest) { try { auto manifestArpVersionRange = manifest.GetArpVersionRange(); if (manifestArpVersionRange.IsEmpty()) { return; } SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, manifest.Id); auto searchResult = index->Search(request); if (searchResult.Matches.empty()) { return; } auto arpVersionRangesInIndex = GetArpVersionRangesByPackageRowId(index, searchResult.Matches[0].first, { Utility::Version{ manifest.Version }, Utility::Channel{ manifest.Channel } }); for (auto const& arpInIndex : arpVersionRangesInIndex) { if (manifestArpVersionRange.Overlaps(arpInIndex)) { std::string context = (" [" + arpInIndex.GetMinVersion().ToString() + ", " + arpInIndex.GetMaxVersion().ToString() + "]"); auto validationError = Manifest::ValidationError(Manifest::ManifestError::ArpVersionOverlapWithIndex, context); AICLI_LOG(Repo, Error, << validationError.GetErrorMessage() << context); THROW_EXCEPTION(Manifest::ManifestException( { validationError }, APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED)); } } } catch (const Manifest::ManifestException&) { // Prevent ManifestException from being wrapped in another ManifestException throw; } catch (...) { AICLI_LOG(Repo, Error, << "ValidateManifestArpVersion() encountered internal error."); THROW_EXCEPTION(Manifest::ManifestException( { Manifest::ValidationError(Manifest::ManifestError::ArpVersionValidationInternalError) }, APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED)); } } } ================================================ FILE: src/AppInstallerRepositoryCore/ArpVersionValidation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/SQLiteIndex.h" #include namespace AppInstaller::Repository { // Validate the manifest arp version range against index. Any validation failures will be thrown as ManifestException for better message back to caller. void ValidateManifestArpVersion(const Microsoft::SQLiteIndex* index, const Manifest::Manifest& manifest); } ================================================ FILE: src/AppInstallerRepositoryCore/CompositeSource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "CompositeSource.h" #include using namespace AppInstaller::Settings; namespace AppInstaller::Repository { using namespace std::string_view_literals; namespace anon { Utility::VersionAndChannel GetVACFromVersion(IPackageVersion* packageVersion) { return { Utility::Version(packageVersion->GetProperty(PackageVersionProperty::Version)), Utility::Channel(packageVersion->GetProperty(PackageVersionProperty::Channel)) }; } // Returns true for fields that provide a strong match; one that is not based on a heuristic. bool IsStrongMatchField(PackageMatchField field) { switch (field) { case AppInstaller::Repository::PackageMatchField::PackageFamilyName: case AppInstaller::Repository::PackageMatchField::ProductCode: case AppInstaller::Repository::PackageMatchField::UpgradeCode: return true; } return false; } // Gets the only available package from the composite, ensuring this fact in test contexts. std::shared_ptr OnlyAvailable(const std::shared_ptr& composite) { std::vector> availablePackages = composite->GetAvailable(); #ifndef AICLI_DISABLE_TEST_HOOKS THROW_HR_IF(E_UNEXPECTED, availablePackages.size() != 1); #endif return std::move(availablePackages.front()); } // Move returns if there is only one package in the matches that is strong; otherwise returns an empty value. std::shared_ptr FindOnlyStrongMatchFieldResult(std::vector& matches) { std::shared_ptr result; for (auto&& match : matches) { AICLI_LOG(Repo, Info, << " Checking match with package id: " << match.Package->GetProperty(PackageProperty::Id)); if (IsStrongMatchField(match.MatchCriteria.Field)) { if (!result) { result = std::move(match.Package); } else { AICLI_LOG(Repo, Info, << " Found multiple packages with strong match fields"); result.reset(); break; } } } return result; } // Gets a single matching package from the results template std::shared_ptr GetMatchingPackage(std::vector& matches, MultipleIntro&& multipleIntro, Indeterminate&& indeterminate) { if (matches.empty()) { return {}; } else if (matches.size() == 1) { return std::move(matches[0].Package); } else { multipleIntro(); auto result = FindOnlyStrongMatchFieldResult(matches); if (!result) { indeterminate(); } return result; } } // For a given package from a tracking catalog, get the latest write time. // Look at all versions rather than just the latest to account for the potential of downgrading. std::chrono::system_clock::time_point GetLatestTrackingWriteTime( const std::shared_ptr& trackingPackage) { std::chrono::system_clock::time_point result{}; for (const auto& key : trackingPackage->GetVersionKeys()) { auto version = trackingPackage->GetVersion(key); if (version) { auto metadata = version->GetMetadata(); auto itr = metadata.find(PackageVersionMetadata::TrackingWriteTime); if (itr != metadata.end()) { std::int64_t unixEpoch = 0; try { unixEpoch = std::stoll(itr->second); } CATCH_LOG(); std::chrono::system_clock::time_point versionTime = Utility::ConvertUnixEpochToSystemClock(unixEpoch); if (versionTime > result) { result = versionTime; } } } } return result; } // An installed package's version reported in ARP does not necessarily match the versions used for the manifest. // This function uses the data in the manifest to map the installed version string to the version used by the manifest. // // TODO: Note: Currently this function assumes the all versions in the available package is from one source. // Even though a composite package can have available packages from multiple sources, we only call this function // for the default (first) available package. If we ever need to consider other sources, this function needs to be revisited. std::string GetMappedInstalledVersion(const std::string& installedVersion, const std::shared_ptr& availablePackage) { // Perform an initial check to see if the latest version has a mapping; if it does not, do not attempt any more. auto latestVersion = availablePackage->GetLatestVersion(); if (latestVersion) { auto version = latestVersion->GetProperty(PackageVersionProperty::Version); auto arpMinVersion = latestVersion->GetProperty(PackageVersionProperty::ArpMinVersion); auto arpMaxVersion = latestVersion->GetProperty(PackageVersionProperty::ArpMaxVersion); if ((arpMinVersion.empty() || arpMinVersion == version) && (arpMaxVersion.empty() || arpMaxVersion == version)) { return installedVersion; } } // Stores raw versions value strings to run a preliminary check whether version mapping is needed. std::vector> rawVersionValues; auto versionKeys = availablePackage->GetVersionKeys(); bool shouldTryPerformMapping = false; for (auto const& versionKey : versionKeys) { auto availableVersion = availablePackage->GetVersion(versionKey); std::string arpMinVersion = availableVersion->GetProperty(PackageVersionProperty::ArpMinVersion); std::string arpMaxVersion = availableVersion->GetProperty(PackageVersionProperty::ArpMaxVersion); if (!arpMinVersion.empty() && !arpMaxVersion.empty()) { std::string manifestVersion = versionKey.Version; if (!shouldTryPerformMapping && (arpMinVersion != manifestVersion || arpMaxVersion != manifestVersion)) { shouldTryPerformMapping = true; } rawVersionValues.emplace_back(std::make_tuple(std::move(manifestVersion), std::move(arpMinVersion), std::move(arpMaxVersion))); } } if (!shouldTryPerformMapping) { return installedVersion; } // Construct a map between manifest version and arp version range. The map is ordered in descending by package version. std::vector> arpVersionMap; for (auto& tuple : rawVersionValues) { auto&& [manifestVersion, arpMinVersion, arpMaxVersion] = std::move(tuple); Utility::VersionRange arpVersionRange{ Utility::Version(std::move(arpMinVersion)), Utility::Version(std::move(arpMaxVersion)) }; Utility::Version manifestVer{ std::move(manifestVersion) }; // Skip mapping to unknown version if (!manifestVer.IsUnknown()) { arpVersionMap.emplace_back(std::make_pair(std::move(manifestVer), std::move(arpVersionRange))); } } // Go through the arp version map and determine what mapping should be performed. // shouldPerformMapping is true when at least 1 arp version range is different from the package version. bool shouldPerformMapping = false; bool isArpVersionRangeInDescendingOrder = true; const Utility::VersionRange* previousVersionRange = nullptr; for (auto const& pair : arpVersionMap) { // If arp version range is not same as package version, should perform mapping // This check is still needed to account for 1.0 == 1.0.0 cases if (!shouldPerformMapping && !pair.second.IsSameAsSingleVersion(pair.first)) { shouldPerformMapping = true; } if (!previousVersionRange) { // This is the first non empty arp version range previousVersionRange = &pair.second; } else if (isArpVersionRangeInDescendingOrder) { // The arp version range should be less than previous range if (pair.second < *previousVersionRange) { previousVersionRange = &pair.second; } else { isArpVersionRangeInDescendingOrder = false; } } } // Now perform arp version mapping if (shouldPerformMapping) { Utility::Version installed{ installedVersion }; for (auto const& pair : arpVersionMap) { // If the installed version is in the arp version range if (pair.second.ContainsVersion(installed)) { return pair.first.ToString(); } } // At this point, no mapping found. Perform approximate mapping if applicable. // We'll start from end of the vector because we try to find closest less than version if possible. if (isArpVersionRangeInDescendingOrder) { const Utility::Version* lastGreaterThanVersion = nullptr; auto it = arpVersionMap.rbegin(); while (it != arpVersionMap.rend()) { const auto& pair = *it; if (installed < pair.second.GetMinVersion()) { return Utility::Version{ pair.first, Utility::Version::ApproximateComparator::LessThan }.ToString(); } else { lastGreaterThanVersion = &pair.first; } it++; } // No approximate less than version found, approximate greater than version will be returned. if (lastGreaterThanVersion) { return Utility::Version{ *lastGreaterThanVersion, Utility::Version::ApproximateComparator::GreaterThan }.ToString(); } } } // return the input installed version if no mapping is performed or found. return installedVersion; } // A composite package installed version that allows us to override the source or the version. struct CompositeInstalledVersion : public IPackageVersion { CompositeInstalledVersion(std::shared_ptr baseInstalledVersion, Source trackingSource, std::shared_ptr trackingPackageVersion, std::string overrideVersion = {}) : m_baseInstalledVersion(std::move(baseInstalledVersion)), m_trackingSource(std::move(trackingSource)), m_trackingPackageVersion(std::move(trackingPackageVersion)), m_overrideVersion(std::move(overrideVersion)) {} Utility::LocIndString GetProperty(PackageVersionProperty property) const override { // If there is an override version, use it. if (property == PackageVersionProperty::Version && !m_overrideVersion.empty()) { return Utility::LocIndString{ m_overrideVersion }; } return m_baseInstalledVersion->GetProperty(property); } std::vector GetMultiProperty(PackageVersionMultiProperty property) const override { return m_baseInstalledVersion->GetMultiProperty(property); } Manifest::Manifest GetManifest() override { return m_baseInstalledVersion->GetManifest(); } Source GetSource() const override { // If there is a tracking source, use it instead to indicate that it came from there. if (m_trackingSource) { return m_trackingSource; } return m_baseInstalledVersion->GetSource(); } Metadata GetMetadata() const override { auto result = m_baseInstalledVersion->GetMetadata(); // Populate metadata from tracking package version if not present in base installed version. if (m_trackingPackageVersion) { auto trackingMetadata = m_trackingPackageVersion->GetMetadata(); for (auto metadataItem : { PackageVersionMetadata::InstalledArchitecture, PackageVersionMetadata::InstalledLocale, PackageVersionMetadata::UserIntentArchitecture, PackageVersionMetadata::UserIntentLocale, PackageVersionMetadata::PinnedState }) { auto itr = trackingMetadata.find(metadataItem); auto existingItr = result.find(metadataItem); if (itr != trackingMetadata.end() && existingItr == result.end()) { result[metadataItem] = itr->second; } } } return result; } private: std::shared_ptr m_baseInstalledVersion; Source m_trackingSource; std::string m_overrideVersion; std::shared_ptr m_trackingPackageVersion; }; // An IPackage for the installed package of a CompositePackage. struct CompositeInstalledPackage : public IPackage { static constexpr IPackageType PackageType = IPackageType::CompositeInstalledPackage; CompositeInstalledPackage(std::shared_ptr package) { AddPackageAndVersionKeyData(std::move(package)); } Utility::LocIndString GetProperty(PackageProperty property) const override { THROW_HR_IF(E_UNEXPECTED, m_packages.empty() || m_versionKeyData.empty()); // Use the highest version for package properties return m_packages[m_versionKeyData[0].PackageIndex]->GetProperty(property); } std::vector GetMultiProperty(PackageMultiProperty property) const override { std::vector result; for (const auto& package : m_packages) { for (auto&& string : package->GetMultiProperty(property)) { auto itr = std::lower_bound(result.begin(), result.end(), string); if (itr == result.end() || *itr != string) { result.emplace(itr, std::move(string)); } } } return result; } std::vector GetVersionKeys() const override { return { m_versionKeyData.begin(), m_versionKeyData.end() }; } std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const override { std::shared_ptr installedVersion; std::string overrideVersion; for (const VersionKeyData& key : m_versionKeyData) { if (key.IsMatch(versionKey)) { installedVersion = key.InstalledVersion; overrideVersion = key.Version; break; } } if (installedVersion) { // Get the appropriate tracking version or latest if it is not found. // The tracking package uses the mapped version. std::shared_ptr trackingPackageVersion; if (m_trackingPackage) { // Remove our use of the package id as source PackageVersionKey versionKey_NoSource = versionKey; versionKey_NoSource.SourceId.clear(); trackingPackageVersion = m_trackingPackage->GetVersion(versionKey_NoSource); if (!trackingPackageVersion) { trackingPackageVersion = m_trackingPackage->GetLatestVersion(); } } return std::make_shared(std::move(installedVersion), m_trackingSource, std::move(trackingPackageVersion), std::move(overrideVersion)); } return nullptr; } std::shared_ptr GetLatestVersion() const override { return GetVersion({}); } Source GetSource() const override { // If there is a tracking source, use it instead to indicate that it came from there. // Otherwise, all of the installed packages should be from the same source. return m_trackingSource ? m_trackingSource : m_packages[0]->GetSource(); } bool IsSame(const IPackage* other) const override { const CompositeInstalledPackage* otherPackage = PackageCast(other); if (otherPackage) { if (m_packages.size() != otherPackage->m_packages.size()) { return false; } for (const auto& subPackage : m_packages) { bool foundSame = false; for (const auto& otherSubPackage : otherPackage->m_packages) { if (subPackage->IsSame(otherSubPackage.get())) { foundSame = true; break; } } if (!foundSame) { return false; } } return true; } return false; } const void* CastTo(IPackageType type) const override { if (type == PackageType) { return this; } return nullptr; } void SetTracking( Source trackingSource, std::shared_ptr trackingPackage, std::chrono::system_clock::time_point trackingWriteTime) { m_trackingSource = std::move(trackingSource); m_trackingPackage = std::move(trackingPackage); m_trackingWriteTime = trackingWriteTime; } Source GetTrackingSource() const { return m_trackingSource; } const std::shared_ptr& GetTrackingPackage() const { return m_trackingPackage; } std::chrono::system_clock::time_point GetTrackingPackageWriteTime() const { return m_trackingWriteTime; } bool ContainsInstalledPackage(const IPackage* installedPackage) const { for (const auto& package : m_packages) { if (package->IsSame(installedPackage)) { return true; } } return false; } void FoldInstalledIn(const std::shared_ptr& other) { for (const auto& package : other->m_packages) { AddPackageAndVersionKeyData(package); } } // Set a version that will override the version string from the installed package void SetOverrideInstalledVersion(const std::shared_ptr& availablePackage) { if (availablePackage) { m_availablePackageVersionOverride = availablePackage; for (auto& key : m_versionKeyData) { if (Manifest::DoesInstallerTypeSupportArpVersionRange(key.InstalledType)) { key.Version = GetMappedInstalledVersion(key.InstalledVersion->GetProperty(PackageVersionProperty::Version), availablePackage); key.VersionAndChannel = Utility::VersionAndChannel{ key.Version, key.Channel }; } } } } bool IsEmpty() const { return m_versionKeyData.empty(); } private: // Contains information about all of the version keys. // We use the `SourceId` field to store the installed package identifier so that we can disambiguate keys is they have the same version. struct VersionKeyData : public PackageVersionKey { size_t PackageIndex; std::shared_ptr InstalledVersion; Manifest::InstallerTypeEnum InstalledType; Utility::VersionAndChannel VersionAndChannel; bool operator<(const VersionKeyData& other) const { return VersionAndChannel < other.VersionAndChannel; } }; // Adds the package and version key data to the composite. // The version keys are then sorted so that the first (index 0) in the vector has the highest version. // Note that it may tied for highest version if, for instance, the same version is installed for different architectures. void AddPackageAndVersionKeyData(std::shared_ptr package) { // We don't want this to happen, but it could. Rather than a crash, we will log it and move on. if (!package) { AICLI_LOG(Repo, Verbose, << "AddPackageAndVersionKeyData called with an empty package"); return; } size_t packageIndex = m_packages.size(); std::string packageIdentifier = package->GetProperty(PackageProperty::Id); bool versionAdded = false; for (const auto& versionKey : package->GetVersionKeys()) { VersionKeyData keyData{ versionKey }; keyData.PackageIndex = packageIndex; keyData.InstalledVersion = package->GetVersion(versionKey); if (!keyData.InstalledVersion) { AICLI_LOG(Repo, Verbose, << "AddPackageAndVersionKeyData: Package [" << packageIdentifier << "] did not return a version for [" << versionKey.Version << "]"); continue; } // We use the `SourceId` field to store the installed package identifier so that we can disambiguate keys if they have the same version. keyData.SourceId = packageIdentifier; keyData.InstalledType = Manifest::ConvertToInstallerTypeEnum(keyData.InstalledVersion->GetMetadata()[PackageVersionMetadata::InstalledType]); if (m_availablePackageVersionOverride && Manifest::DoesInstallerTypeSupportArpVersionRange(keyData.InstalledType)) { keyData.Version = GetMappedInstalledVersion(keyData.InstalledVersion->GetProperty(PackageVersionProperty::Version), m_availablePackageVersionOverride); } keyData.VersionAndChannel = Utility::VersionAndChannel{ keyData.Version, keyData.Channel }; m_versionKeyData.emplace_back(std::move(keyData)); versionAdded = true; } if (versionAdded) { m_packages.emplace_back(std::move(package)); std::sort(m_versionKeyData.begin(), m_versionKeyData.end()); } } std::vector> m_packages; std::vector m_versionKeyData; Source m_trackingSource; std::shared_ptr m_trackingPackage; std::chrono::system_clock::time_point m_trackingWriteTime = std::chrono::system_clock::time_point::min(); std::shared_ptr m_availablePackageVersionOverride; }; // An ICompositePackage for the CompositeSource. struct CompositePackage : public ICompositePackage { // The availablePackage may only contain one available package within it, as it is expected to be the output of a search on a single source. CompositePackage(const std::shared_ptr& installedPackage, const std::shared_ptr& availablePackage = {}, bool setPrimary = false) { if (installedPackage) { m_installedPackage = std::make_shared(installedPackage->GetInstalled()); // If the installed package result existed, but didn't actually create any installed versions, drop it. if (m_installedPackage->IsEmpty()) { m_installedPackage.reset(); } } AddAvailablePackage(availablePackage, setPrimary); } Utility::LocIndString GetProperty(PackageProperty property) const override { IPackage* truth = nullptr; if (m_primaryAvailablePackage) { truth = m_primaryAvailablePackage.get(); } if (!truth && !m_availablePackages.empty()) { truth = m_availablePackages[0].get(); } if (!truth) { truth = m_installedPackage.get(); } THROW_HR_IF(E_UNEXPECTED, !truth); return truth->GetProperty(property); } std::shared_ptr GetInstalled() override { return m_installedPackage; } std::vector> GetAvailable() override { return m_availablePackages; } const std::vector>& GetAvailablePackages() { return m_availablePackages; } bool IsSameAsAnyAvailable(const IPackage* other) const { if (other) { for (const auto& availablePackage : m_availablePackages) { if (other->IsSame(availablePackage.get())) { return true; } } } return false; } const std::shared_ptr& GetInstalledPackage() const { return m_installedPackage; } bool ContainsInstalledPackage(const IPackage* installedPackage) const { return m_installedPackage ? m_installedPackage->ContainsInstalledPackage(installedPackage) : false; } void AddAvailablePackage(const std::shared_ptr& availablePackage, bool setPrimary = false) { if (availablePackage) { m_availablePackages.emplace_back(OnlyAvailable(availablePackage)); if (setPrimary) { m_primaryAvailablePackage = m_availablePackages.back(); } // Set override for primary or with the first available version found if (setPrimary || m_availablePackages.size() == 1) { TrySetOverrideInstalledVersion(m_availablePackages.back()); } } } std::shared_ptr& GetPrimaryAvailablePackage() { return m_primaryAvailablePackage; } Source GetTrackingSource() const { return m_installedPackage ? m_installedPackage->GetTrackingSource() : Source{}; } std::shared_ptr GetTrackingPackage() const { return m_installedPackage ? m_installedPackage->GetTrackingPackage() : std::shared_ptr{}; } std::chrono::system_clock::time_point GetTrackingPackageWriteTime() const { return m_installedPackage ? m_installedPackage->GetTrackingPackageWriteTime() : std::chrono::system_clock::time_point::min(); } void SetTracking( Source trackingSource, std::shared_ptr trackingPackage, std::chrono::system_clock::time_point trackingWriteTime) { if (m_installedPackage) { m_installedPackage->SetTracking(std::move(trackingSource), std::move(trackingPackage), trackingWriteTime); } } void FoldInstalledIn(const std::shared_ptr& other) { if (other->m_installedPackage) { if (m_installedPackage) { m_installedPackage->FoldInstalledIn(other->m_installedPackage); } else { m_installedPackage = other->m_installedPackage; } } } private: // Try to set a version that will override the version string from the installed package void TrySetOverrideInstalledVersion(const std::shared_ptr& availablePackage) { if (m_installedPackage && availablePackage) { m_installedPackage->SetOverrideInstalledVersion(availablePackage); } } std::shared_ptr m_installedPackage; std::shared_ptr m_primaryAvailablePackage; std::vector> m_availablePackages; }; // The comparator compares the ResultMatch by MatchType first, then Field in a predefined order. struct ResultMatchComparator { template bool operator() ( const U& match1, const V& match2) { if (match1.MatchCriteria.Type != match2.MatchCriteria.Type) { return match1.MatchCriteria.Type < match2.MatchCriteria.Type; } if (match1.MatchCriteria.Field != match2.MatchCriteria.Field) { return match1.MatchCriteria.Field < match2.MatchCriteria.Field; } return false; } }; template void SortResultMatches(std::vector& matches) { std::stable_sort(matches.begin(), matches.end(), ResultMatchComparator()); } // A copy of the standard match that holds a CompositePackage instead. struct CompositeResultMatch { std::shared_ptr Package; PackageMatchFilter MatchCriteria; CompositeResultMatch(std::shared_ptr p, PackageMatchFilter f) : Package(std::move(p)), MatchCriteria(std::move(f)) {} }; // Stores data to enable correlation between installed and available packages. struct CompositeResult { // A system reference string. struct SystemReferenceString { SystemReferenceString(PackageMatchField field, Utility::LocIndString string) : Field(field), String1(Utility::FoldCase(string)) {} SystemReferenceString(PackageMatchField field, Utility::LocIndString string1, Utility::LocIndString string2) : Field(field), String1(Utility::FoldCase(string1)), String2(Utility::FoldCase(string2)) {} bool operator<(const SystemReferenceString& other) const { if (Field != other.Field) { return Field < other.Field; } if (String1 != other.String1) { return String1 < other.String1; } return String2 < other.String2; } bool operator==(const SystemReferenceString& other) const { return Field == other.Field && String1 == other.String1 && String2 == other.String2; } void AddToFilters( std::vector& filters) const { switch (Field) { case PackageMatchField::NormalizedNameAndPublisher: filters.emplace_back(PackageMatchFilter(Field, MatchType::Exact, String1.get(), String2.get())); break; default: filters.emplace_back(PackageMatchFilter(Field, MatchType::Exact, String1.get())); } } private: PackageMatchField Field; Utility::LocIndString String1; Utility::LocIndString String2; }; // Data relevant to correlation for a package. struct PackageData { std::set SystemReferenceStrings; void AddIfNotPresent(SystemReferenceString&& srs) { if (SystemReferenceStrings.find(srs) == SystemReferenceStrings.end()) { SystemReferenceStrings.emplace(std::move(srs)); } } SearchRequest CreateInclusionsSearchRequest(SearchPurpose searchPurpose) const { SearchRequest result; for (const auto& srs : SystemReferenceStrings) { srs.AddToFilters(result.Inclusions); } result.Purpose = searchPurpose; return result; } std::shared_ptr AddSystemReferenceStringsFromTrackingPackage(const PackageTrackingCatalog& trackingCatalog, const Utility::LocIndString& identifier, std::string_view sourceIdentifier) { SearchRequest trackingRequest; trackingRequest.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, identifier.get()); SearchResult trackingResult = trackingCatalog.Search(trackingRequest); std::shared_ptr result; if (trackingResult.Matches.size() == 1) { result = OnlyAvailable(trackingResult.Matches[0].Package); AddSystemReferenceStrings(result.get()); } else if (trackingResult.Matches.size() > 1) { AICLI_LOG(Repo, Warning, << "Found " << trackingResult.Matches.size() << " results for Id [" << identifier << "] in tracking catalog for: " << sourceIdentifier); } return result; } void AddSystemReferenceStrings(IPackage* package) { GetSystemReferenceStrings( package, PackageMultiProperty::PackageFamilyName, PackageMatchField::PackageFamilyName); GetSystemReferenceStrings( package, PackageMultiProperty::ProductCode, PackageMatchField::ProductCode); GetSystemReferenceStrings( package, PackageMultiProperty::UpgradeCode, PackageMatchField::UpgradeCode); GetNameAndPublisher( package); } void AddSystemReferenceStringsFromManifest(const Manifest::Manifest& manifest) { for (const auto& pfn : manifest.GetPackageFamilyNames()) { AddIfNotPresent(SystemReferenceString{ PackageMatchField::PackageFamilyName, Utility::LocIndString{ pfn } }); } for (const auto& productCode : manifest.GetProductCodes()) { AddIfNotPresent(SystemReferenceString{ PackageMatchField::ProductCode, Utility::LocIndString{ productCode } }); } for (const auto& upgradeCode : manifest.GetUpgradeCodes()) { AddIfNotPresent(SystemReferenceString{ PackageMatchField::UpgradeCode, Utility::LocIndString{ upgradeCode } }); } for (const auto& name : manifest.GetPackageNames()) { for (const auto& publisher : manifest.GetPublishers()) { AddIfNotPresent(SystemReferenceString{ PackageMatchField::NormalizedNameAndPublisher, Utility::LocIndString{ name }, Utility::LocIndString{ publisher } }); } } } private: void GetSystemReferenceStrings( IPackage* package, PackageMultiProperty prop, PackageMatchField field) { for (auto&& string : package->GetMultiProperty(prop)) { AddIfNotPresent(SystemReferenceString{ field, std::move(string) }); } } void GetNameAndPublisher( IPackage* package) { // Unfortunately the names and publishers are unique and not tied to each other strictly, so we need // to go broad on the matches. Future work can hopefully make name and publisher operate more as a unit, // but for now we have to search for the cartesian of these... auto names = package->GetMultiProperty(PackageMultiProperty::NormalizedName); auto publishers = package->GetMultiProperty(PackageMultiProperty::NormalizedPublisher); for (const auto& name : names) { for (const auto& publisher : publishers) { AddIfNotPresent(SystemReferenceString{ PackageMatchField::NormalizedNameAndPublisher, name, publisher }); } } } }; // For a given package, prepares the results for it. PackageData GetSystemReferenceStrings(IPackage* package) { PackageData result; result.AddSystemReferenceStrings(package); return result; } // Check for a package already in the result that should have been correlated already. // If we find one, see if we should upgrade it's match criteria. // If we don't, return package data for further use. // downloadManifests: when creating system reference strings, also download manifests to get more data. std::optional CheckForExistingResultFromAvailablePackageMatch(const ResultMatch& availableMatch, bool downloadManifests) { std::shared_ptr availablePackage = OnlyAvailable(availableMatch.Package); for (auto& match : Matches) { if (match.Package->IsSameAsAnyAvailable(availablePackage.get())) { if (ResultMatchComparator{}(availableMatch, match)) { match.MatchCriteria = availableMatch.MatchCriteria; } return {}; } } PackageData result; result.AddSystemReferenceStrings(availablePackage.get()); if (downloadManifests) { constexpr int c_downloadManifestsLimit = 3; int manifestsDownloaded = 0; for (auto const& versionKey : availablePackage->GetVersionKeys()) { auto packageVersion = availablePackage->GetVersion(versionKey); auto manifest = packageVersion->GetManifest(); result.AddSystemReferenceStringsFromManifest(manifest); manifestsDownloaded++; if (manifestsDownloaded >= c_downloadManifestsLimit) { break; } } } return result; } // Determines if the results contain the given installed package. bool ContainsInstalledPackage(const IPackage* installedPackage) const { for (auto& match : Matches) { if (match.Package->ContainsInstalledPackage(installedPackage)) { return true; } } return false; } // Determines if the results contain the given installed package. std::shared_ptr FindInstalledPackage(const IPackage* installedPackage) const { for (auto& match : Matches) { if (match.Package->ContainsInstalledPackage(installedPackage)) { return match.Package; } } return {}; } // *Destructively* converts the result to the standard variant. SearchResult ConvertToSearchResult() { FoldResults(); SearchResult result; result.Matches.reserve(Matches.size()); for (auto& match : Matches) { result.Matches.emplace_back(std::move(match.Package), std::move(match.MatchCriteria)); } result.Truncated = Truncated; result.Failures = std::move(Failures); return result; } bool AddFailureIfSourceNotPresent(SearchResult::Failure&& failure) { auto itr = std::find_if(Failures.begin(), Failures.end(), [&failure](const SearchResult::Failure& present) { return present.SourceName == failure.SourceName; }); if (itr == Failures.end()) { Failures.emplace_back(std::move(failure)); return true; } return false; } SearchResult SearchAndHandleFailures(const Source& source, const SearchRequest& request) { SearchResult result; try { result = source.Search(request); } catch (...) { if (AddFailureIfSourceNotPresent({ source.GetDetails().Name, std::current_exception() })) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Repo, Warning, << "Failed to search source for correlation: " << source.GetDetails().Name); } } // Move failures into the result for (SearchResult::Failure& failure : result.Failures) { AddFailureIfSourceNotPresent(std::move(failure)); } return result; } // Group results in an attempt to have a single result that covers all installed versions. // This is expected to be called immediately after the installed search portion, // when each result will contain a single installed version and some number of available packages. // // The folds that happen are: // 1. When results have the same primary available package (the primary available package is set due to tracking data) // 2. When a result has no primary available package, but another result does have a primary that matches one of the available // a. Choose the latest primary if there are multiple // 3. When multiple results have no primary available package and share the same available package set // a. There are many potential additional rules that could be made here, but we will start with the simplest version. // // Potential improvements: // 1. Attempting correlation of non-primary available packages to allow folding in more complex cases // a. For example, if installed A has {source1:package1, source2:package2} and installed B has {source1:package1}, can we // make sure that source1:package1 and source2:package2 are in fact "the same" to confidently say that installed A and B // are side by side versions. // 2. Attempt correlation by installed data only // a. We can potentially detect multiple instances of the same installed item with the same correlation logic turned back on // the installed source. This would allow for folding even when the package is not in any available source. void FoldResults() { // The key to uniquely identify the package in the map struct InstalledResultFoldKey { InstalledResultFoldKey() = default; InstalledResultFoldKey(const std::shared_ptr& package) { std::shared_ptr latestAvailable = package->GetLatestVersion(); if (latestAvailable) { SourceIdentifier = latestAvailable->GetSource().GetIdentifier(); PackageIdentifier = latestAvailable->GetProperty(PackageVersionProperty::Id); } } // Hash operation size_t operator()(const InstalledResultFoldKey& value) const noexcept { std::hash hashString; return hashString(value.SourceIdentifier) ^ (hashString(value.PackageIdentifier) << 1); } bool operator==(const InstalledResultFoldKey& other) const noexcept { // Treat both empty as invalid and never equal if (SourceIdentifier.empty() && PackageIdentifier.empty()) { return false; } return SourceIdentifier == other.SourceIdentifier && PackageIdentifier == other.PackageIdentifier; } std::string SourceIdentifier; std::string PackageIdentifier; }; // The data for a package in the map struct InstalledResultFoldData { InstalledResultFoldData() = default; explicit InstalledResultFoldData(size_t primaryPackageIndex) : PrimaryPackageIndex(primaryPackageIndex) {} std::optional PrimaryPackageIndex; std::vector NonPrimaryPackageIndices; }; std::unordered_map foldData; // Attempt to fold all primary package matches first. // Packages without primaries will still be indexed into the hash table. for (size_t i = 0; i < Matches.size(); ++i) { CompositeResultMatch& currentMatch = Matches[i]; // Check current match for fold target if (currentMatch.Package->GetPrimaryAvailablePackage()) { InstalledResultFoldKey key{ currentMatch.Package->GetPrimaryAvailablePackage() }; auto itr = foldData.find(key); if (itr != foldData.end()) { if (itr->second.PrimaryPackageIndex) { Matches[itr->second.PrimaryPackageIndex.value()].Package->FoldInstalledIn(currentMatch.Package); currentMatch.Package.reset(); } else { itr->second.PrimaryPackageIndex = i; } } else { foldData[key] = InstalledResultFoldData{ i }; } } else { for (const auto& availablePackage : currentMatch.Package->GetAvailablePackages()) { InstalledResultFoldKey key{ availablePackage }; auto itr = foldData.find(key); if (itr == foldData.end()) { itr = foldData.insert({ key, {} }).first; } itr->second.NonPrimaryPackageIndices.emplace_back(i); } } } // After primary matches are folded, attempt to fold results without primary matches. // The latest primary match will be preferred. for (size_t i = 0; i < Matches.size(); ++i) { CompositeResultMatch& currentMatch = Matches[i]; // Skip any matches that we have already folded if (!currentMatch.Package) { continue; } if (!currentMatch.Package->GetPrimaryAvailablePackage()) { InstalledResultFoldData* latestPrimaryAvailable = nullptr; std::vector availableFoldData; for (const auto& availablePackage : currentMatch.Package->GetAvailablePackages()) { auto& packageFoldData = foldData.at(availablePackage); if (packageFoldData.PrimaryPackageIndex) { if (!latestPrimaryAvailable || Matches[latestPrimaryAvailable->PrimaryPackageIndex.value()].Package->GetTrackingPackageWriteTime() < Matches[packageFoldData.PrimaryPackageIndex.value()].Package->GetTrackingPackageWriteTime()) { latestPrimaryAvailable = &packageFoldData; } } else { availableFoldData.emplace_back(&packageFoldData); } } if (latestPrimaryAvailable) { Matches[latestPrimaryAvailable->PrimaryPackageIndex.value()].Package->FoldInstalledIn(currentMatch.Package); currentMatch.Package.reset(); // If the result with the primary is later, move it forward if (latestPrimaryAvailable->PrimaryPackageIndex.value() > i) { currentMatch.Package = std::move(Matches[latestPrimaryAvailable->PrimaryPackageIndex.value()].Package); Matches[latestPrimaryAvailable->PrimaryPackageIndex.value()].Package.reset(); latestPrimaryAvailable->PrimaryPackageIndex = i; } continue; } // First, find the intersection of all results that contain all of the packages from this result. std::vector candidateMatches; for (size_t j = 0; j < availableFoldData.size(); ++j) { InstalledResultFoldData* packageFoldData = availableFoldData[j]; if (j == 0) { candidateMatches = packageFoldData->NonPrimaryPackageIndices; } else { std::vector temp; std::set_intersection( candidateMatches.begin(), candidateMatches.end(), packageFoldData->NonPrimaryPackageIndices.begin(), packageFoldData->NonPrimaryPackageIndices.end(), std::back_inserter(temp)); candidateMatches = std::move(temp); } } // Now exclude both our own result and any that have a different (larger) number of available packages candidateMatches.erase(std::remove_if(candidateMatches.begin(), candidateMatches.end(), [&](size_t index) { return index == i || Matches[index].Package->GetAvailablePackages().size() != currentMatch.Package->GetAvailablePackages().size(); }), candidateMatches.end()); // All of these remaining values should be folded in to our result for (size_t foldTarget : candidateMatches) { currentMatch.Package->FoldInstalledIn(Matches[foldTarget].Package); Matches[foldTarget].Package.reset(); } } } // Get rid of the folded results; we reset the Package to indicate that it is no longer valid Matches.erase(std::remove_if(Matches.begin(), Matches.end(), [&](const CompositeResultMatch& match) { return !match.Package; }), Matches.end()); } std::vector Matches; bool Truncated = false; std::vector Failures; }; std::shared_ptr GetTrackedPackageFromAvailableSource(CompositeResult& result, const Source& source, const Utility::LocIndString& identifier) { SearchRequest directRequest; directRequest.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, identifier.get()); SearchResult directResult = result.SearchAndHandleFailures(source, directRequest); if (directResult.Matches.empty()) { AICLI_LOG(Repo, Warning, << "Did not find Id [" << identifier << "] in tracked source: " << source.GetDetails().Name); } else if (directResult.Matches.size() == 1) { return directResult.Matches[0].Package; } else { AICLI_LOG(Repo, Warning, << "Found multiple results for Id [" << identifier << "] in tracked source: " << source.GetDetails().Name); } return {}; } } using namespace anon; CompositeSource::CompositeSource(std::string identifier) { m_details.Identifier = std::move(identifier); } const SourceDetails& CompositeSource::GetDetails() const { return m_details; } const std::string& CompositeSource::GetIdentifier() const { return m_details.Identifier; } // The composite search needs to take several steps to get results, and due to the // potential for different information spread across multiple sources, base searches // need to be performed in both installed and available. // // If an installed source is present, then the searches should only return packages // that are installed. This means that the base searches against available sources // will only return results where a match is found in the installed source. SearchResult CompositeSource::Search(const SearchRequest& request) const { if (m_installedSource) { return SearchInstalled(request); } else { return SearchAvailable(request); } } void* CompositeSource::CastTo(ISourceType type) { if (type == SourceType) { return this; } return nullptr; } void CompositeSource::AddAvailableSource(const Source& source) { m_availableSources.emplace_back(source); } void CompositeSource::SetInstalledSource(Source source, CompositeSearchBehavior searchBehavior) { m_installedSource = std::move(source); m_searchBehavior = searchBehavior; } // An installed search first finds all installed packages that match the request, then correlates with available sources. // Next the search is performed against the available sources and correlated with the installed source. A result will only // be added if there exists an installed package that was not found by the initial search. // This allows for search terms to find installed packages by their available metadata, as well as the local values. // // Search flow: // Installed :: Search incoming request // For each result // For each available source // Tracking :: Search system references // If tracking found // Available :: Search tracking ID // If no available, for each available source // Available :: Search system references // // For each available source // Tracking :: Search incoming request // For each result // Installed :: Search system references // If found // Available :: Search tracking ID // Available :: Search incoming request // For each result // Installed :: Search system references SearchResult CompositeSource::SearchInstalled(const SearchRequest& request) const { CompositeResult result; // If the search behavior is for AllPackages or Installed then the result can contain packages that are // only in the Installed source, but do not have an AvailableVersion. if (m_searchBehavior == CompositeSearchBehavior::AllPackages || m_searchBehavior == CompositeSearchBehavior::Installed) { // Search installed source (allow exceptions out as we own the installed source) SearchResult installedResult = m_installedSource.Search(request); result.Truncated = installedResult.Truncated; for (auto&& match : installedResult.Matches) { if (!match.Package) { // Ensure that the crash from installedVersion below is not from the actual package being null. AICLI_LOG(Repo, Warning, << "CompositeSource: The match of the package (matched on " << ToString(match.MatchCriteria.Field) << " => '" << match.MatchCriteria.Value << "') was null and is being dropped from the results."); continue; } std::shared_ptr compositePackage = std::make_shared(match.Package); auto installedPackage = compositePackage->GetInstalled(); if (!installedPackage) { // One would think that the installed package coming directly from our own installed source // would never be null, but it is sometimes. Rather than making users suffer through crashes // that break their entire experience, lets log a few things and then ignore this match. AICLI_LOG(Repo, Warning, << "CompositeSource: The installed version of the package '" << match.Package->GetProperty(PackageProperty::Id) << "' was null and is being dropped from the results."); continue; } auto installedPackageData = result.GetSystemReferenceStrings(installedPackage.get()); // Create a search request to run against all available sources if (!installedPackageData.SystemReferenceStrings.empty()) { SearchRequest systemReferenceSearch = installedPackageData.CreateInclusionsSearchRequest(SearchPurpose::CorrelationToAvailable); AICLI_LOG(Repo, Verbose, << "Finding available package from installed package using system reference search: " << systemReferenceSearch.ToString()); // Search sources and add to result for (const auto& source : m_availableSources) { AICLI_LOG(Repo, Verbose, << " ... searching source: " << source.GetDetails().Name << " [" << source.GetIdentifier() << ']'); // Find the tracking result with the latest timestamp. auto trackingCatalog = source.GetTrackingCatalog(); SearchResult trackingResult = trackingCatalog.Search(systemReferenceSearch); std::shared_ptr trackingPackage; std::chrono::system_clock::time_point trackingPackageTime; bool trackingSet = false; for (const auto& trackingMatch : trackingResult.Matches) { auto candidateTime = GetLatestTrackingWriteTime(OnlyAvailable(trackingMatch.Package)); if (!trackingPackage || candidateTime > trackingPackageTime) { trackingPackage = OnlyAvailable(trackingMatch.Package); trackingPackageTime = candidateTime; } } if (trackingPackage && trackingPackageTime > compositePackage->GetTrackingPackageWriteTime()) { AICLI_LOG(Repo, Verbose, << " ... setting latest tracking package to: " << trackingPackage->GetProperty(PackageProperty::Id)); compositePackage->SetTracking(source, trackingPackage, trackingPackageTime); trackingSet = true; } // Attempt to correlate local packages against this source if supported. SearchResult availableResult; if (source.GetDetails().SupportInstalledSearchCorrelation) { availableResult = result.SearchAndHandleFailures(source, systemReferenceSearch); } auto availablePackage = GetMatchingPackage(availableResult.Matches, [&]() { AICLI_LOG(Repo, Info, << "Found multiple matches for installed package [" << installedPackage->GetProperty(PackageProperty::Id) << "] in source [" << source.GetIdentifier() << "] when searching for [" << systemReferenceSearch.ToString() << "]"); }, [&] { AICLI_LOG(Repo, Warning, << " Appropriate available package could not be determined"); }); if (trackingPackage) { auto trackingIdentifier = trackingPackage->GetProperty(PackageProperty::Id); // We always want to take the available search result if it exists as the package may have been updated. if (availablePackage) { auto availableIdentifier = availablePackage->GetProperty(PackageProperty::Id); if (!Utility::ICUCaseInsensitiveEquals(availableIdentifier, trackingIdentifier)) { AICLI_LOG(Repo, Verbose, << " ... overriding tracking package (" << trackingIdentifier << ") with available package (" << availableIdentifier << ")"); } } else { AICLI_LOG(Repo, Verbose, << " ... using tracking package: " << trackingIdentifier); availablePackage = GetTrackedPackageFromAvailableSource(result, source, trackingIdentifier); } } if (availablePackage) { AICLI_LOG(Repo, Verbose, << " ... adding available package: " << availablePackage->GetProperty(PackageProperty::Id)); compositePackage->AddAvailablePackage(availablePackage, trackingSet); } } } // Move the installed result into the composite result result.Matches.emplace_back(std::move(compositePackage), std::move(match.MatchCriteria)); } // Optimization for the "everything installed" case, no need to allow for reverse correlations if (request.IsForEverything() && m_searchBehavior == CompositeSearchBehavior::Installed) { return result.ConvertToSearchResult(); } } // Search available sources for (const auto& source : m_availableSources) { auto trackingCatalog = source.GetTrackingCatalog(); SearchResult availableResult = result.SearchAndHandleFailures(source, request); bool downloadManifests = source.QueryFeatureFlag(SourceFeatureFlag::ManifestMayContainAdditionalSystemReferenceStrings); for (auto&& match : availableResult.Matches) { // Check for the package already in the result. // In cases that PackageData will be created, also download manifests for system reference strings // when search result is small (currently limiting to 1). auto packageData = result.CheckForExistingResultFromAvailablePackageMatch(match, downloadManifests && availableResult.Matches.size() == 1); // If found existing package in the result, continue if (!packageData) { continue; } // Use data from the tracking catalog as it can potentially get better correlations auto trackingPackage = packageData->AddSystemReferenceStringsFromTrackingPackage(trackingCatalog, match.Package->GetProperty(PackageProperty::Id), source.GetDetails().Name); // If no package was found that was already in the results, do a correlation lookup with the installed // source to create a new composite package entry if we find any packages there. bool foundInstalledMatch = false; if (!packageData->SystemReferenceStrings.empty()) { // Create a search request to run against the installed source SearchRequest systemReferenceSearch = packageData->CreateInclusionsSearchRequest(SearchPurpose::CorrelationToInstalled); AICLI_LOG(Repo, Verbose, << "Finding installed package from available package using system reference search: " << systemReferenceSearch.ToString()); // Correlate against installed (allow exceptions out as we own the installed source) SearchResult installedCrossRef = m_installedSource.Search(systemReferenceSearch); for (const auto& installedMatch : installedCrossRef.Matches) { if (!IsStrongMatchField(installedMatch.MatchCriteria.Field)) { // For weak correlations, do an installed -> available check to ensure that there are no other strong correlations. SearchResult correlationConfirmation; if (source.GetDetails().SupportInstalledSearchCorrelation) { correlationConfirmation = result.SearchAndHandleFailures(source, result.GetSystemReferenceStrings(installedMatch.Package->GetInstalled().get()).CreateInclusionsSearchRequest(SearchPurpose::CorrelationToAvailable)); } if (correlationConfirmation.Matches.empty()) { // We probably made the correlation due to tracking data, keep it. } else if (correlationConfirmation.Matches.size() > 1) { // There is contention for the correlation. AICLI_LOG(Repo, Verbose, << " ... installed package [" << installedMatch.Package->GetProperty(PackageProperty::Id) << "] had multiple correlations and is being ignored as a match for [" << match.Package->GetProperty(PackageProperty::Id) << "]"); continue; } else if (!OnlyAvailable(correlationConfirmation.Matches[0].Package)->IsSame(OnlyAvailable(match.Package).get())) { // The only correlation is not to the current package. AICLI_LOG(Repo, Verbose, << " ... installed package [" << installedMatch.Package->GetProperty(PackageProperty::Id) << "] was found through available package [" << match.Package->GetProperty(PackageProperty::Id) << "], but only correlated to [" << correlationConfirmation.Matches[0].Package->GetProperty(PackageProperty::Id) << "] and is being ignored"); continue; } } // Now that we know we need to add this available package, determine how exactly std::shared_ptr resultPackage = result.FindInstalledPackage(installedMatch.Package->GetInstalled().get()); if (resultPackage) { // Check for a package from the same source already present on the result package. bool foundSameSource = false; for (const auto& availablePackage : resultPackage->GetAvailablePackages()) { if (availablePackage->GetSource() == source) { // TODO: May need to add more data so that we can choose the proper correlation, but it may also be very difficult to get through // the gauntlet of other checks and arrive in this situation. AICLI_LOG(Repo, Verbose, << " ... found [" << availablePackage->GetProperty(PackageProperty::Id) << "] already correlated to [" << installedMatch.Package->GetProperty(PackageProperty::Id) << "] from the same source [" << source.GetDetails().Name << "] as [" << match.Package->GetProperty(PackageProperty::Id) << "]; ignoring the second correlation"); foundSameSource = true; } } if (foundSameSource) { continue; } } else { result.Matches.emplace_back(std::make_shared(installedMatch.Package), match.MatchCriteria); resultPackage = result.Matches.back().Package; } bool setPrimary = false; if (trackingPackage) { auto trackingPackageTime = GetLatestTrackingWriteTime(trackingPackage); if (trackingPackageTime > resultPackage->GetTrackingPackageWriteTime()) { resultPackage->SetTracking(source, std::move(trackingPackage), trackingPackageTime); setPrimary = true; } } resultPackage->AddAvailablePackage(std::move(match.Package), setPrimary); foundInstalledMatch = true; } } // If there was no correlation for this package, add it without one. if ((m_searchBehavior == CompositeSearchBehavior::AllPackages || m_searchBehavior == CompositeSearchBehavior::AvailablePackages) && !foundInstalledMatch) { result.Matches.emplace_back(std::make_shared(std::shared_ptr{}, std::move(match.Package)), match.MatchCriteria); } } } SortResultMatches(result.Matches); if (request.MaximumResults > 0 && result.Matches.size() > request.MaximumResults) { result.Truncated = true; result.Matches.erase(result.Matches.begin() + request.MaximumResults, result.Matches.end()); } return result.ConvertToSearchResult(); } // An available search goes through each source, searching individually and then sorting the full result set. SearchResult CompositeSource::SearchAvailable(const SearchRequest& request) const { SearchResult result; // Search available sources for (const auto& source : m_availableSources) { SearchResult oneSourceResult; try { oneSourceResult = source.Search(request); } catch (...) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Repo, Warning, << "Failed to search source: " << source.GetDetails().Name); result.Failures.emplace_back(SearchResult::Failure{ source.GetDetails().Name, std::current_exception() }); } // Move into the single result std::move(oneSourceResult.Matches.begin(), oneSourceResult.Matches.end(), std::back_inserter(result.Matches)); std::move(oneSourceResult.Failures.begin(), oneSourceResult.Failures.end(), std::back_inserter(result.Failures)); } SortResultMatches(result.Matches); if (request.MaximumResults > 0 && result.Matches.size() > request.MaximumResults) { result.Truncated = true; result.Matches.erase(result.Matches.begin() + request.MaximumResults, result.Matches.end()); } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/CompositeSource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" namespace AppInstaller::Repository { struct CompositeSource : public ISource { static constexpr ISourceType SourceType = ISourceType::CompositeSource; explicit CompositeSource(std::string identifier); CompositeSource(const CompositeSource&) = delete; CompositeSource& operator=(const CompositeSource&) = delete; CompositeSource(CompositeSource&&) = default; CompositeSource& operator=(CompositeSource&&) = default; ~CompositeSource() = default; // ISource // Get the source's details. const SourceDetails& GetDetails() const override; // Gets the source's identifier; a unique identifier independent of the name // that will not change between a remove/add or between additional adds. // Must be suitable for filesystem names. const std::string& GetIdentifier() const override; // Execute a search on the source. SearchResult Search(const SearchRequest& request) const override; // Casts to the requested type. void* CastTo(ISourceType type) override; // ~ISource // Adds an available source to be aggregated. void AddAvailableSource(const Source& source); // Gets the available sources if the source is composite. std::vector GetAvailableSources() const { return m_availableSources; } // Checks if any available sources are present bool HasAvailableSource() const { return !m_availableSources.empty(); } // Sets the installed source to be composited. void SetInstalledSource(Source source, CompositeSearchBehavior searchBehavior = CompositeSearchBehavior::Installed); private: // Performs a search when an installed source is present. SearchResult SearchInstalled(const SearchRequest& request) const; // Performs a search when no installed source is present. SearchResult SearchAvailable(const SearchRequest& request) const; Source m_installedSource; std::vector m_availableSources; SourceDetails m_details; CompositeSearchBehavior m_searchBehavior = CompositeSearchBehavior::Installed; }; } ================================================ FILE: src/AppInstallerRepositoryCore/ISource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Public/winget/RepositorySource.h" #include #include namespace AppInstaller::Repository { // To allow for runtime casting from ISource to the specific types, this enum contains all of the ISource implementations. enum class ISourceType { TestSource, ConfigurableTestSource, RestSource, SQLiteIndexSource, CompositeSource, IMutablePackageSource, OpenExceptionProxy, }; // Internal interface for interacting with a source from outside of the repository lib. struct ISource { virtual ~ISource() = default; // Gets the source's identifier; a unique identifier independent of the name // that will not change between a remove/add or between additional adds. // Must be suitable for filesystem names unless the source is internal to winget, // in which case the identifier should begin with a '*' character. virtual const std::string& GetIdentifier() const = 0; // Get the source's configuration from settings. Source details can be used during opening the source. virtual const SourceDetails& GetDetails() const = 0; // Get the source's information after the source is opened. virtual SourceInformation GetInformation() const { return {}; }; // Query the value of the given feature flag. // The default state of any new flag is false. virtual bool QueryFeatureFlag(SourceFeatureFlag) const { return false; } // Execute a search on the source. virtual SearchResult Search(const SearchRequest& request) const = 0; // Gets this object as the requested type, or null if it is not the requested type. virtual void* CastTo(ISourceType type) = 0; }; // Does the equivalent of a dynamic_pointer_cast, but without it to allow RTTI to be disabled. template std::shared_ptr SourceCast(const std::shared_ptr& source) { if (!source) { return {}; } void* castResult = source->CastTo(SourceType::SourceType); if (!castResult) { return {}; } return std::shared_ptr(source, reinterpret_cast(castResult)); } // Internal interface to represent source information; basically SourceDetails but with methods to enable differential behaviors. struct ISourceReference { virtual ~ISourceReference() = default; // Gets the source's identifier; a unique identifier independent of the name // that will not change between a remove/add or between additional adds. // Must be suitable for filesystem names unless the source is internal to winget, // in which case the identifier should begin with a '*' character. virtual std::string GetIdentifier() = 0; // Get the source's configuration details from settings. virtual SourceDetails& GetDetails() = 0; // Get the source's information. virtual SourceInformation GetInformation() { return {}; } // Set custom header. Returns false if custom header is not supported. virtual bool SetCustomHeader(std::optional) { return false; } // Set caller. virtual void SetCaller(std::string) {} // Set authentication arguments. virtual void SetAuthenticationArguments(Authentication::AuthenticationArguments) {} // Determine if the source needs to be updated before being opened. virtual bool ShouldUpdateBeforeOpen(const std::optional&) { return false; } // Opens the source. This function should throw upon open failure rather than returning an empty pointer. virtual std::shared_ptr Open(IProgressCallback& progress) = 0; // Sets thread globals for the source. This allows the option for sources that operate on other threads to log properly. virtual void SetThreadGlobals(const std::shared_ptr&) {} }; // Internal interface extension to ISource for databases that can be updated after creation, like InstallingPackages struct IMutablePackageSource { static constexpr AppInstaller::Repository::ISourceType SourceType = AppInstaller::Repository::ISourceType::IMutablePackageSource; virtual ~IMutablePackageSource() = default; // Adds a package version to the source. virtual void AddPackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) = 0; // Removes a package version from the source. virtual void RemovePackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) = 0; }; } ================================================ FILE: src/AppInstallerRepositoryCore/IconDefs.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once // Icon file structure and definitions: // https://msdn.microsoft.com/en-us/library/ms997538.aspx // .ico icon structures // Icon entry struct typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved ( must be 0) WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // How many bytes in this resource? DWORD dwImageOffset; // Where in the file is this image? } ICONDIRENTRY, *LPICONDIRENTRY; // Icon directory struct typedef struct { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource Type (1 for icons) WORD idCount; // How many images? ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) } ICONDIR, *LPICONDIR; // Image struct typedef struct { BITMAPINFOHEADER icHeader; // DIB header RGBQUAD icColors[1]; // Color table BYTE icXOR[1]; // DIB bits for XOR mask BYTE icAND[1]; // DIB bits for AND mask } ICONIMAGE, * LPICONIMAGE; // .exe and .dll icon structures // #pragmas are used here to insure that the structure's // packing in memory matches the packing of the EXE or DLL. #pragma pack( push ) #pragma pack( 2 ) typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // how many bytes in this resource? WORD nID; // the ID } GRPICONDIRENTRY, *LPGRPICONDIRENTRY; typedef struct { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource type (1 for icons) WORD idCount; // How many images? GRPICONDIRENTRY idEntries[1]; // The entries for each image } GRPICONDIR, *LPGRPICONDIR; #pragma pack( pop ) ================================================ FILE: src/AppInstallerRepositoryCore/IconExtraction.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "IconDefs.h" #include "winget/IconExtraction.h" #include "Microsoft/ARPHelper.h" #include #include namespace AppInstaller::Repository { using namespace AppInstaller::Repository::Microsoft; namespace { // Struct used as data object passed to Enumerate callback function of EnumResourceNamesEx struct EnumGroupIconProcParameter { // Input to specify icon index int IconIndex = 0; // The result Resource handle of the group icon HRSRC ResourceHandle = nullptr; // How many icons were already found int IconsFound = 0; }; BOOL CALLBACK EnumGroupIconProc(HMODULE hModule, LPCWSTR lpType, LPWSTR lpName, LONG_PTR lParam) { EnumGroupIconProcParameter* parameter = reinterpret_cast(lParam); bool foundRequestedIcon = false; // Find icon by resource name if (parameter->IconIndex < 0) { if (IS_INTRESOURCE(lpName)) { if (-parameter->IconIndex == LOWORD(lpName)) { // Found icon by MAKEINTRESOURCE name foundRequestedIcon = true; } } else if (lpName[0] == TEXT('#')) { std::wstring resourceIdString = lpName + 1; // skip the # try { auto resourceId = std::stoi(resourceIdString.c_str(), nullptr, 0); if (-parameter->IconIndex == resourceId) { // Found icon by number as string #12 foundRequestedIcon = true; } } catch (...) { // Error occurred, stop enumerating return FALSE; } } } else if (parameter->IconIndex == parameter->IconsFound) { // Found icon by index foundRequestedIcon = TRUE; } if (foundRequestedIcon) { parameter->ResourceHandle = FindResourceExW(hModule, lpType, lpName, 0); return FALSE; } // Continue enumerating parameter->IconsFound++; return TRUE; }; void WriteIconDirHeaderToByteArray(std::vector& data, const ICONDIR& iconDir) { data.clear(); BYTE const* toBeWritten = reinterpret_cast(&(iconDir.idReserved)); data.insert(data.end(), toBeWritten, toBeWritten + sizeof(iconDir.idReserved)); toBeWritten = reinterpret_cast(&(iconDir.idType)); data.insert(data.end(), toBeWritten, toBeWritten + sizeof(iconDir.idType)); toBeWritten = reinterpret_cast(&(iconDir.idCount)); data.insert(data.end(), toBeWritten, toBeWritten + sizeof(iconDir.idCount)); } void AppendIconDirEntryToByteArray(std::vector& data, const ICONDIRENTRY& iconDirEntry) { data.insert(data.end(), iconDirEntry.bWidth); data.insert(data.end(), iconDirEntry.bHeight); data.insert(data.end(), iconDirEntry.bColorCount); data.insert(data.end(), iconDirEntry.bReserved); BYTE const* toBeWritten = reinterpret_cast(&(iconDirEntry.wPlanes)); data.insert(data.end(), toBeWritten, toBeWritten + sizeof(iconDirEntry.wPlanes)); toBeWritten = reinterpret_cast(&(iconDirEntry.wBitCount)); data.insert(data.end(), toBeWritten, toBeWritten + sizeof(iconDirEntry.wBitCount)); toBeWritten = reinterpret_cast(&(iconDirEntry.dwBytesInRes)); data.insert(data.end(), toBeWritten, toBeWritten + sizeof(iconDirEntry.dwBytesInRes)); toBeWritten = reinterpret_cast(&(iconDirEntry.dwImageOffset)); data.insert(data.end(), toBeWritten, toBeWritten + sizeof(iconDirEntry.dwImageOffset)); } } std::vector ExtractIconFromBinaryFile(const std::filesystem::path binaryPath, int iconIndex) { try { wil::unique_hmodule module; module.reset(LoadLibraryEx(binaryPath.c_str(), nullptr, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE)); THROW_LAST_ERROR_IF_NULL(module); EnumGroupIconProcParameter param; param.IconIndex = iconIndex; #pragma warning( push ) #pragma warning ( disable : 4302 ) // First find the requested group icon EnumResourceNamesExW( module.get(), MAKEINTRESOURCE(RT_GROUP_ICON), EnumGroupIconProc, reinterpret_cast(¶m), (RESOURCE_ENUM_MUI | RESOURCE_ENUM_LN | RESOURCE_ENUM_VALIDATE), 0); #pragma warning( pop ) if (param.ResourceHandle) { // Load and Lock to get a pointer to a GRPICONDIR HGLOBAL groupIconResourceHandle = LoadResource(module.get(), param.ResourceHandle); THROW_LAST_ERROR_IF_NULL(groupIconResourceHandle); LPGRPICONDIR groupIconDir = reinterpret_cast(LockResource(groupIconResourceHandle)); THROW_LAST_ERROR_IF_NULL(groupIconDir); // Basic validation if (groupIconDir->idReserved != 0 || groupIconDir->idType != 1 || groupIconDir->idCount == 0) { return {}; } struct SingleIconImage { ICONDIRENTRY DirEntry = { 0 }; // pointer to byte contents with size std::pair Content; }; // Read all individual icon image contents std::vector iconContents; // The first image's offset. DWORD imageOffset = 6 /* ICONDIR size */ + groupIconDir->idCount * 16 /* each ICONDIRENTRY size */; for (int i = 0; i < groupIconDir->idCount; i++) { SingleIconImage iconEntry; // Populate ICONDIRENTRY iconEntry.DirEntry.bWidth = groupIconDir->idEntries[i].bWidth; iconEntry.DirEntry.bHeight = groupIconDir->idEntries[i].bHeight; iconEntry.DirEntry.bColorCount = groupIconDir->idEntries[i].bColorCount; iconEntry.DirEntry.bReserved = groupIconDir->idEntries[i].bReserved; iconEntry.DirEntry.wPlanes = groupIconDir->idEntries[i].wPlanes; iconEntry.DirEntry.wBitCount = groupIconDir->idEntries[i].wBitCount; iconEntry.DirEntry.dwBytesInRes = groupIconDir->idEntries[i].dwBytesInRes; iconEntry.DirEntry.dwImageOffset = imageOffset; // Load individual icon content HRSRC iconResourceHandle = FindResourceExW(module.get(), RT_ICON, MAKEINTRESOURCE(groupIconDir->idEntries[i].nID), 0); THROW_LAST_ERROR_IF_NULL(iconResourceHandle); HGLOBAL iconResourceContentHandle = LoadResource(module.get(), iconResourceHandle); THROW_LAST_ERROR_IF_NULL(iconResourceContentHandle); iconEntry.Content.second = SizeofResource(module.get(), iconResourceHandle); THROW_LAST_ERROR_IF(iconEntry.Content.second == 0); iconEntry.Content.first = reinterpret_cast(LockResource(iconResourceContentHandle)); THROW_LAST_ERROR_IF_NULL(iconEntry.Content.first); // This will be the next image offset. imageOffset += iconEntry.Content.second; iconContents.emplace_back(std::move(iconEntry)); } // Construct ico file icon dir header ICONDIR iconDir; iconDir.idReserved = groupIconDir->idReserved; iconDir.idType = groupIconDir->idType; iconDir.idCount = groupIconDir->idCount; std::vector result; // Write Icon Dir header WriteIconDirHeaderToByteArray(result, iconDir); // Write Icon Dir entries for (auto const& singleIconEntry : iconContents) { AppendIconDirEntryToByteArray(result, singleIconEntry.DirEntry); } // Write Icon contents for (auto const& singleIconEntry : iconContents) { result.insert(result.end(), singleIconEntry.Content.first, singleIconEntry.Content.first + singleIconEntry.Content.second); } return result; } } CATCH_LOG(); return {}; } #ifndef AICLI_DISABLE_TEST_HOOKS static std::vector* s_ExtractIconFromArpEntry_TestHook_Override = nullptr; void TestHook_SetExtractIconFromArpEntryResult_Override(std::vector* result) { s_ExtractIconFromArpEntry_TestHook_Override = result; } #endif std::vector ExtractIconFromArpEntry(const std::string& productCode, Manifest::ScopeEnum scope) { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_ExtractIconFromArpEntry_TestHook_Override) { return *s_ExtractIconFromArpEntry_TestHook_Override; } #endif ARPHelper arpHelper; Registry::Key arpEntry = arpHelper.FindARPEntry(productCode, scope); if (arpEntry) { std::wstring iconPathRaw; if (arpHelper.GetBoolValue(arpEntry, arpHelper.WindowsInstaller)) { // For msi, get icon from ProductInfo auto productCodeWide = Utility::ConvertToUTF16(productCode); DWORD iconPathSize = 0; if (ERROR_MORE_DATA == MsiGetProductInfoW(productCodeWide.c_str(), INSTALLPROPERTY_PRODUCTICON, nullptr, &iconPathSize)) { std::wstring iconPathBuffer; // The iconPathSize returned in previous call does not count the null terminator. iconPathSize++; iconPathBuffer.resize(iconPathSize); if (ERROR_SUCCESS == MsiGetProductInfoW(productCodeWide.c_str(), INSTALLPROPERTY_PRODUCTICON, iconPathBuffer.data(), &iconPathSize)) { iconPathBuffer.resize(iconPathSize); iconPathRaw = iconPathBuffer; } } } else { // For other win32 apps, try DisplayIcon. iconPathRaw = Utility::ConvertToUTF16(arpHelper.GetStringValue(arpEntry, arpHelper.DisplayIcon)); } if (!iconPathRaw.empty()) { PathUnquoteSpacesW(iconPathRaw.data()); // For paths like C:\test\test.exe,-3 int iconIndex = 0; iconIndex = PathParseIconLocationW(iconPathRaw.data()); // Above operations will modify the input string with null terminator in the middle. iconPathRaw = iconPathRaw.c_str(); auto iconPath = Filesystem::GetExpandedPath(Utility::ConvertToUTF8(iconPathRaw)); if (std::filesystem::exists(iconPath)) { auto extension = iconPath.extension().u8string(); std::vector iconContent; if (Utility::CaseInsensitiveEquals(extension, ".ico")) { std::ifstream iconFile{ iconPath, std::ios::in | std::ios::binary }; iconContent = Utility::ReadEntireStreamAsByteArray(iconFile); } else if (Utility::CaseInsensitiveEquals(extension, ".exe") || Utility::CaseInsensitiveEquals(extension, ".dll")) { iconContent = ExtractIconFromBinaryFile(iconPath, iconIndex); } // Construct ExtractedIconInfo return result if (!iconContent.empty()) { ExtractedIconInfo iconInfo; iconInfo.IconFileType = Manifest::IconFileTypeEnum::Ico; iconInfo.IconTheme = Manifest::IconThemeEnum::Default; iconInfo.IconResolution = Manifest::IconResolutionEnum::Custom; iconInfo.IconSha256 = Utility::SHA256::ComputeHash(iconContent.data(), static_cast(iconContent.size())); iconInfo.IconContent = std::move(iconContent); return { std::move(iconInfo) }; } } } } return {}; } } ================================================ FILE: src/AppInstallerRepositoryCore/InstalledFilesCorrelation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/InstalledFilesCorrelation.h" #include #include using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Correlation { namespace { constexpr std::string_view s_ShellLinkFileExtension = ".lnk"sv; const std::vector> s_CandidateInstallLocationRoots = { { Filesystem::GetKnownFolderPath(FOLDERID_LocalAppData), "%LOCALAPPDATA%" }, { Filesystem::GetKnownFolderPath(FOLDERID_ProgramFiles), "%PROGRAMFILES%" }, { Filesystem::GetKnownFolderPath(FOLDERID_ProgramFilesX86), "%PROGRAMFILES(X86)%" }, }; // Contains shell link info struct ShellLinkFileInfo { std::filesystem::path Path; std::string Args; std::string DisplayName; }; std::optional ParseShellLinkFile(const std::filesystem::path& linkFile) { try { AICLI_LOG(Repo, Info, << "Parsing link file at " << linkFile); ShellLinkFileInfo result; Microsoft::WRL::ComPtr shellLink; THROW_IF_FAILED(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink))); Microsoft::WRL::ComPtr persistFile; THROW_IF_FAILED(shellLink.As(&persistFile)); THROW_IF_FAILED(persistFile->Load(linkFile.wstring().c_str(), STGM_READ)); THROW_IF_FAILED(shellLink->Resolve(nullptr, SLR_NO_UI | SLR_NOUPDATE | SLR_NOSEARCH | SLR_NOTRACK | SLR_NOLINKINFO)); { // Parse Path from shell link std::wstring buffer; buffer.resize(MAX_PATH); HRESULT hr = S_OK; for (int retry = 0; retry < 5; retry++) { hr = shellLink->GetPath( &buffer[0], static_cast(buffer.size()), nullptr, 0 ); if (SUCCEEDED(hr)) { buffer.erase(std::find(buffer.begin(), buffer.end(), L'\0'), buffer.end()); result.Path = buffer; break; } else if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { buffer.resize(buffer.size() * 2); } else { THROW_IF_FAILED(hr); } } } { // Parse arguments from shell link std::wstring buffer; buffer.resize(MAX_PATH); HRESULT hr = S_OK; for (int retry = 0; retry < 5; retry++) { hr = shellLink->GetArguments( &buffer[0], static_cast(buffer.size())); if (SUCCEEDED(hr)) { buffer.erase(std::find(buffer.begin(), buffer.end(), L'\0'), buffer.end()); result.Args = Utility::ConvertToUTF8(buffer); break; } else if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { buffer.resize(buffer.size() * 2); } else { THROW_IF_FAILED(hr); } } } // Use shell link file name (minus extension) as display name. result.DisplayName = linkFile.stem().u8string(); AICLI_LOG(Repo, Info, << "Link file parsed. Path: " << result.Path << " Args: " << result.Args << " DisplayName: " << result.DisplayName); return result; } catch (...) { AICLI_LOG(Repo, Error, << "Failed to parse link file at " << linkFile); return {}; } } // Returns nullopt if path is not under base. std::optional GetRelativePath(const std::filesystem::path& path, const std::filesystem::path& base) { auto canonicalPath = std::filesystem::weakly_canonical(path); auto canonicalBase = std::filesystem::weakly_canonical(base); auto relativePath = std::filesystem::relative(canonicalPath, canonicalBase); if (!relativePath.empty() && *relativePath.begin() != "." && *relativePath.begin() != "..") { return relativePath; } else { return {}; } } std::optional CheckOneInstallLocation(const std::filesystem::path& childFile, const std::filesystem::path& baseFolder) { auto relativePath = GetRelativePath(childFile, baseFolder); if (relativePath) { // TODO: Here we assume the install location is the top directory of relative path. auto installLocation = baseFolder / *relativePath->begin(); if (std::filesystem::exists(installLocation) && std::filesystem::is_directory(installLocation)) { return installLocation; } } return {}; } // If install location is not provided in arp entry, try LocalAppData folder and Program Files folders. std::optional CheckInstallLocation(const std::filesystem::path& path) { for (auto const& entry : s_CandidateInstallLocationRoots) { auto installLocation = CheckOneInstallLocation(path, entry.first); if (installLocation) { return installLocation; } } return {}; } // TODO: basic heuristics to determine file type. AppInstaller::Manifest::InstalledFileTypeEnum GetInstalledFileType(const ShellLinkFileInfo& linkInfo) { Manifest::InstalledFileTypeEnum result = Manifest::InstalledFileTypeEnum::Other; if (Utility::CaseInsensitiveContainsSubstring(linkInfo.Path.u8string(), "uninstall") || Utility::CaseInsensitiveContainsSubstring(linkInfo.Path.u8string(), "unins000") || Utility::CaseInsensitiveContainsSubstring(linkInfo.Args, "uninstall") || Utility::CaseInsensitiveContainsSubstring(linkInfo.DisplayName, "uninstall")) { result = Manifest::InstalledFileTypeEnum::Uninstall; } else if (Utility::CaseInsensitiveEquals(linkInfo.Path.extension().u8string(), ".exe")) { result = Manifest::InstalledFileTypeEnum::Launch; } return result; } std::string GetUnexpandedInstallLocation(const std::filesystem::path& installLocation) { // Try to match the candidate install location roots first. std::filesystem::path resultInstallLocation = installLocation; for (auto const& entry : s_CandidateInstallLocationRoots) { if (Filesystem::ReplaceCommonPathPrefix(resultInstallLocation, entry.first, entry.second)) { return resultInstallLocation.u8string(); } } // Then try PathUnExpandEnvStrings OS api std::wstring installLocationWString = installLocation.wstring(); std::wstring buffer; buffer.resize(installLocationWString.size() + 20); if (PathUnExpandEnvStrings( installLocationWString.c_str(), &buffer[0], static_cast(buffer.size()))) { buffer.resize(buffer.find(L'\0')); return Utility::ConvertToUTF8(buffer); } return resultInstallLocation.u8string(); } } InstalledFilesCorrelation::InstalledFilesCorrelation() { m_fileWatchers.emplace_back(Filesystem::GetKnownFolderPath(FOLDERID_CommonStartMenu), std::string{ s_ShellLinkFileExtension }); m_fileWatchers.emplace_back(Filesystem::GetKnownFolderPath(FOLDERID_StartMenu), std::string{ s_ShellLinkFileExtension }); } void InstalledFilesCorrelation::StartFileWatcher() { m_files.clear(); for (auto& watcher : m_fileWatchers) { watcher.Start(); } } void InstalledFilesCorrelation::StopFileWatcher() { for (auto& watcher : m_fileWatchers) { watcher.Stop(); } for (auto& watcher : m_fileWatchers) { FileWatcherFiles files; files.Folder = watcher.FolderPath(); for (auto const& file : watcher.Files()) { files.Files.emplace_back(file); } m_files.emplace_back(std::move(files)); } } InstallationMetadata InstalledFilesCorrelation::CorrelateForNewlyInstalled( const Manifest::Manifest&, const std::string& arpInstallLocation) { InstallationMetadata result; std::filesystem::path installLocation; // Use arp install location if provided if (!arpInstallLocation.empty()) { installLocation = Filesystem::GetExpandedPath(arpInstallLocation); } for (auto const& files : m_files) { for (auto const& file : files.Files) { // TODO: we only watch shell link files at the moment. auto linkInfo = ParseShellLinkFile(files.Folder / file); if (linkInfo) { auto installedFileType = GetInstalledFileType(linkInfo.value()); // Collect installed files metadata if exist if (std::filesystem::exists(linkInfo->Path) && std::filesystem::is_regular_file(linkInfo->Path)) { if (installLocation.empty()) { // TODO: In most cases, installed files are under same folder, so use the first file to determine install location at the moment. auto location = CheckInstallLocation(linkInfo->Path); if (!location) { continue; } installLocation = location.value(); } auto relativePath = GetRelativePath(linkInfo->Path, installLocation); if (relativePath) { AppInstaller::Manifest::InstalledFile fileEntry; fileEntry.RelativeFilePath = relativePath->string(); std::ifstream in{ linkInfo->Path, std::ifstream::binary }; fileEntry.FileSha256 = Utility::SHA256::ComputeHash(in); fileEntry.InvocationParameter = linkInfo->Args; fileEntry.DisplayName = linkInfo->DisplayName; fileEntry.FileType = installedFileType; result.InstalledFiles.Files.emplace_back(std::move(fileEntry)); } } // Collect short cut paths InstalledStartupLinkFile linkFile; linkFile.RelativeFilePath = file.u8string(); linkFile.FileType = installedFileType; result.StartupLinkFiles.emplace_back(linkFile); } } } if (!installLocation.empty()) { result.InstalledFiles.DefaultInstallLocation = GetUnexpandedInstallLocation(installLocation); } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/InstallerMetadataCollectionContext.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/InstallerMetadataCollectionContext.h" #include #include #include #include #include #include #include #include using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Metadata { namespace { struct ProductMetadataFields_1_N { ProductMetadataFields_1_N(const Version& version) { if (::AppInstaller::Utility::Version{ "1.1" } <= version) { SchemaVersion = L"1.1"; Scope = L"scope"; } if (::AppInstaller::Utility::Version{ "1.2" } <= version) { SchemaVersion = L"1.2"; InstalledFiles = L"installedFiles"; DefaultInstallLocation = L"DefaultInstallLocation"; InstallationMetadataFiles = L"Files"; InstalledFileRelativeFilePath = L"RelativeFilePath"; InstalledFileSha256 = L"FileSha256"; InstalledFileType = L"FileType"; InstalledFileInvocationParameter = L"InvocationParameter"; InstalledFileDisplayName = L"DisplayName"; InstalledStartupLinks = L"startupLinks"; InstalledStartupLinkPath = L"RelativeFilePath"; InstalledStartupLinkType = L"FileType"; Icons = L"icons"; IconContent = L"IconContent"; IconSha256 = L"IconSha256"; IconFileType = L"IconFileType"; IconResolution = L"IconResolution"; IconTheme = L"IconTheme"; } } utility::string_t SchemaVersion = L"1.0"; // 1.0 utility::string_t ProductVersionMin = L"productVersionMin"; utility::string_t ProductVersionMax = L"productVersionMax"; utility::string_t Metadata = L"metadata"; utility::string_t InstallerHash = L"installerHash"; utility::string_t SubmissionIdentifier = L"submissionIdentifier"; utility::string_t Version = L"version"; utility::string_t AppsAndFeaturesEntries = L"AppsAndFeaturesEntries"; utility::string_t Historical = L"historical"; // AppsAndFeaturesEntry fields. utility::string_t DisplayName = L"DisplayName"; utility::string_t Publisher = L"Publisher"; utility::string_t DisplayVersion = L"DisplayVersion"; utility::string_t ProductCode = L"ProductCode"; utility::string_t UpgradeCode = L"UpgradeCode"; utility::string_t InstallerType = L"InstallerType"; utility::string_t VersionMin = L"versionMin"; utility::string_t VersionMax = L"versionMax"; utility::string_t Names = L"names"; utility::string_t Publishers = L"publishers"; utility::string_t ProductCodes = L"productCodes"; utility::string_t UpgradeCodes = L"upgradeCodes"; // 1.1 utility::string_t Scope; // 1.2 // Installed files utility::string_t InstalledFiles; utility::string_t DefaultInstallLocation; utility::string_t InstallationMetadataFiles; utility::string_t InstalledFileRelativeFilePath; utility::string_t InstalledFileSha256; utility::string_t InstalledFileType; utility::string_t InstalledFileInvocationParameter; utility::string_t InstalledFileDisplayName; // Startup links utility::string_t InstalledStartupLinks; utility::string_t InstalledStartupLinkPath; utility::string_t InstalledStartupLinkType; // Icons utility::string_t Icons; utility::string_t IconContent; utility::string_t IconSha256; utility::string_t IconFileType; utility::string_t IconResolution; utility::string_t IconTheme; }; struct OutputFields_1_0 { utility::string_t Version = L"version"; utility::string_t SubmissionData = L"submissionData"; utility::string_t InstallerHash = L"installerHash"; utility::string_t Status = L"status"; utility::string_t Metadata = L"metadata"; utility::string_t Diagnostics = L"diagnostics"; }; struct DiagnosticFields { // Error case utility::string_t ErrorHR = L"errorHR"; utility::string_t ErrorText = L"errorText"; // Non-error case utility::string_t Reason = L"reason"; utility::string_t ChangedEntryCount = L"changedEntryCount"; utility::string_t MatchedEntryCount = L"matchedEntryCount"; utility::string_t IntersectionCount = L"intersectionCount"; utility::string_t CorrelationMeasures = L"correlationMeasures"; utility::string_t Value = L"value"; utility::string_t Name = L"name"; utility::string_t Publisher = L"publisher"; }; std::string GetRequiredString(const web::json::value& value, const utility::string_t& field) { auto optString = AppInstaller::JSON::GetRawStringValueFromJsonNode(value, field); if (!optString) { AICLI_LOG(Repo, Error, << "Required field '" << Utility::ConvertToUTF8(field) << "' was not present"); THROW_HR(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE); } return std::move(optString).value(); } void AddFieldIfNotEmpty(web::json::value& value, const utility::string_t& field, std::string_view string) { if (!string.empty()) { value[field] = AppInstaller::JSON::GetStringValue(string); } } web::json::value CreateStringArray(const std::set& values) { web::json::value result; size_t index = 0; for (const std::string& value : values) { result[index++] = AppInstaller::JSON::GetStringValue(value); } return result; } bool AddIfNotPresentAndNotEmpty(std::set& strings, const std::set& filter, const std::string& string) { if (string.empty() || filter.find(string) != filter.end()) { return false; } strings.emplace(string); return true; } bool AddIfNotPresentAndNotEmpty(std::set& strings, const std::string& string) { return AddIfNotPresentAndNotEmpty(strings, strings, string); } void AddIfNotPresent(std::set& strings, std::set& filter, const std::set& inputs) { for (const std::string& input : inputs) { if (AddIfNotPresentAndNotEmpty(strings, filter, input)) { filter.emplace(input); } } } void FilterAndAddToEntries(Manifest::AppsAndFeaturesEntry&& newEntry, std::vector& entries) { // Erase all duplicated data from the new entry for (const auto& entry : entries) { #define WINGET_ERASE_IF_SAME(_value_) if (entry._value_ == newEntry._value_) { newEntry._value_.clear(); } WINGET_ERASE_IF_SAME(DisplayName); WINGET_ERASE_IF_SAME(DisplayVersion); WINGET_ERASE_IF_SAME(ProductCode); WINGET_ERASE_IF_SAME(Publisher); WINGET_ERASE_IF_SAME(UpgradeCode); #undef WINGET_ERASE_IF_SAME if (entry.InstallerType == newEntry.InstallerType) { newEntry.InstallerType = Manifest::InstallerTypeEnum::Unknown; } } // If anything remains, add it if (!newEntry.DisplayName.empty() || !newEntry.DisplayVersion.empty() || !newEntry.ProductCode.empty() || !newEntry.Publisher.empty() || !newEntry.UpgradeCode.empty() || newEntry.InstallerType != Manifest::InstallerTypeEnum::Unknown) { entries.emplace_back(std::move(newEntry)); } } std::optional GetStringFromFutureSchema(const web::json::value& value, const utility::string_t& field) { if (field.empty()) { return {}; } return AppInstaller::JSON::GetRawStringValueFromJsonNode(value, field); } void SetStringFromFutureSchema(web::json::value& json, const utility::string_t& field, std::string_view value) { if (!field.empty()) { json[field] = AppInstaller::JSON::GetStringValue(value); } } // For installed files merging, we remove conflicting entries, like scope. Indicating we are not certain some files will always be installed. void MergeInstalledFilesMetadata(Manifest::InstallationMetadataInfo& existing, const Manifest::InstallationMetadataInfo& incoming) { if (!Utility::CaseInsensitiveEquals(existing.DefaultInstallLocation, incoming.DefaultInstallLocation)) { existing.Clear(); return; } auto existingItr = existing.Files.begin(); while (existingItr != existing.Files.end()) { auto itr = std::find_if(incoming.Files.begin(), incoming.Files.end(), [&](const Manifest::InstalledFile& entry) { return Utility::CaseInsensitiveEquals(existingItr->RelativeFilePath, entry.RelativeFilePath); }); if (itr == incoming.Files.end()) { existingItr = existing.Files.erase(existingItr); } else { if (existingItr->InvocationParameter != itr->InvocationParameter) { existingItr->InvocationParameter.clear(); } if (!Utility::CaseInsensitiveEquals(existingItr->DisplayName, itr->DisplayName)) { existingItr->DisplayName.clear(); } if (!Utility::SHA256::AreEqual(existingItr->FileSha256, itr->FileSha256)) { existingItr->FileSha256.clear(); } if (existingItr->FileType != itr->FileType) { existingItr->FileType = Manifest::InstalledFileTypeEnum::Unknown; } ++existingItr; } } } // For startup link files merging, we add non duplicate entries, like ProductCodes. Indicating possible startup links an installer could potentially add. void MergeStartupLinkFilesMetadata(std::vector& existing, const std::vector& incoming) { for (auto const& incomingEntry : incoming) { auto itr = std::find_if(existing.begin(), existing.end(), [&](const Correlation::InstalledStartupLinkFile& entry) { return Utility::CaseInsensitiveEquals(incomingEntry.RelativeFilePath, entry.RelativeFilePath); }); if (itr == existing.end()) { existing.emplace_back(incomingEntry); } else if (itr->FileType != incomingEntry.FileType) { // Set conflicting file type to Unknown. itr->FileType = AppInstaller::Manifest::InstalledFileTypeEnum::Unknown; } } } // TODO: This method could be moved to rest response parser and reused when winget supports launch // scenarios (i.e. when startup links info are exposed in winget manifest). std::optional> DeserializeInstalledStartupLinks( const web::json::value& startupLinkFiles, const ProductMetadataFields_1_N& fields) { if (startupLinkFiles.is_null() || !startupLinkFiles.is_array()) { return {}; } std::vector startupLinks; for (auto const& startupLink : startupLinkFiles.as_array()) { Correlation::InstalledStartupLinkFile fileEntry; std::optional relativeFilePath = AppInstaller::JSON::GetRawStringValueFromJsonNode(startupLink, fields.InstalledStartupLinkPath); if (!AppInstaller::JSON::IsValidNonEmptyStringValue(relativeFilePath)) { AICLI_LOG(Repo, Error, << "Missing RelativeFilePath in Installed Startup Link Files."); return {}; } fileEntry.RelativeFilePath = std::move(*relativeFilePath); std::optional fileType = AppInstaller::JSON::GetRawStringValueFromJsonNode(startupLink, fields.InstalledStartupLinkType); if (AppInstaller::JSON::IsValidNonEmptyStringValue(fileType)) { fileEntry.FileType = Manifest::ConvertToInstalledFileTypeEnum(*fileType); } startupLinks.emplace_back(std::move(fileEntry)); } return startupLinks; } std::vector DeserializeExtractedIcons( const web::json::value& icons, const ProductMetadataFields_1_N& fields) { if (icons.is_null() || !icons.is_array()) { return {}; } std::vector result; for (auto const& iconInfo : icons.as_array()) { ExtractedIconInfo iconInfoEntry; auto content = AppInstaller::JSON::GetRawStringValueFromJsonNode(iconInfo, fields.IconContent); if (!AppInstaller::JSON::IsValidNonEmptyStringValue(content)) { AICLI_LOG(Repo, Error, << "Missing IconContent in Extracted Icons."); return {}; } iconInfoEntry.IconContent = AppInstaller::JSON::Base64Decode(*content); std::optional sha256 = AppInstaller::JSON::GetRawStringValueFromJsonNode(iconInfo, fields.IconSha256); if (AppInstaller::JSON::IsValidNonEmptyStringValue(sha256)) { iconInfoEntry.IconSha256 = Utility::SHA256::ConvertToBytes(*sha256); } std::optional fileType = AppInstaller::JSON::GetRawStringValueFromJsonNode(iconInfo, fields.IconFileType); if (AppInstaller::JSON::IsValidNonEmptyStringValue(fileType)) { iconInfoEntry.IconFileType = Manifest::ConvertToIconFileTypeEnum(*fileType); } std::optional theme = AppInstaller::JSON::GetRawStringValueFromJsonNode(iconInfo, fields.IconTheme); if (AppInstaller::JSON::IsValidNonEmptyStringValue(theme)) { iconInfoEntry.IconTheme = Manifest::ConvertToIconThemeEnum(*theme); } std::optional resolution = AppInstaller::JSON::GetRawStringValueFromJsonNode(iconInfo, fields.IconResolution); if (AppInstaller::JSON::IsValidNonEmptyStringValue(resolution)) { iconInfoEntry.IconResolution = Manifest::ConvertToIconResolutionEnum(*resolution); } result.emplace_back(std::move(iconInfoEntry)); } return result; } } void ProductMetadata::Clear() { SchemaVersion = {}; ProductVersionMin = {}; ProductVersionMax = {}; InstallerMetadataMap.clear(); HistoricalMetadataList.clear(); } void ProductMetadata::FromJson(const web::json::value& json) { Clear(); utility::string_t versionFieldName = L"version"; THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, json.is_null()); SchemaVersion = Version{ GetRequiredString(json, versionFieldName) }; AICLI_LOG(Repo, Info, << "Parsing metadata JSON version " << SchemaVersion.ToString()); if (SchemaVersion.PartAt(0).Integer == 1) { FromJson_1_N(json); } else { AICLI_LOG(Repo, Error, << "Don't know how to handle metadata version " << SchemaVersion.ToString()); THROW_HR(HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)); } // Sort the historical data with oldest last (thus b < a) std::sort(HistoricalMetadataList.begin(), HistoricalMetadataList.end(), [](const HistoricalMetadata& a, const HistoricalMetadata& b) { return b.ProductVersionMin < a.ProductVersionMin; }); } web::json::value ProductMetadata::ToJson(const Utility::Version& schemaVersion, size_t maximumSizeInBytes) { SchemaVersion = schemaVersion; AICLI_LOG(Repo, Info, << "Creating metadata JSON version " << SchemaVersion.ToString()); using ToJsonFunctionPointer = web::json::value(ProductMetadata::*)(); ToJsonFunctionPointer toJsonFunction = nullptr; if (SchemaVersion.PartAt(0).Integer == 1) { toJsonFunction = &ProductMetadata::ToJson_1_N; } else { AICLI_LOG(Repo, Error, << "Don't know how to handle metadata version " << SchemaVersion.ToString()); THROW_HR(HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)); } // Constrain the result based on maximum size given web::json::value result = (this->*toJsonFunction)(); while (maximumSizeInBytes) { // Determine current size std::ostringstream temp; result.serialize(temp); std::string tempStr = temp.str(); if (tempStr.length() > maximumSizeInBytes) { if (!DropOldestHistoricalData()) { AICLI_LOG(Repo, Error, << "Could not remove any more historical data to get under " << maximumSizeInBytes << " bytes"); AICLI_LOG(Repo, Info, << " Smallest size was " << tempStr.length() << " bytes with value:\n" << tempStr); THROW_HR(HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE)); } result = (this->*toJsonFunction)(); } else { break; } } return result; } void ProductMetadata::CopyFrom(const ProductMetadata& source, std::string_view submissionIdentifier) { // If the source has no installer metadata, consider it empty if (source.InstallerMetadataMap.empty()) { return; } // With the same submission, just copy over all of the data if (source.InstallerMetadataMap.begin()->second.SubmissionIdentifier == submissionIdentifier) { *this = source; return; } // This is a new submission, so we must move all of the data to historical and update the older historical data // First, create a new historical entry for the current metadata HistoricalMetadata currentHistory; currentHistory.ProductVersionMin = source.ProductVersionMin; currentHistory.ProductVersionMax = source.ProductVersionMax; for (const auto& metadataItem : source.InstallerMetadataMap) { for (const auto& entry : metadataItem.second.AppsAndFeaturesEntries) { AddIfNotPresentAndNotEmpty(currentHistory.Names, entry.DisplayName); AddIfNotPresentAndNotEmpty(currentHistory.Publishers, entry.Publisher); AddIfNotPresentAndNotEmpty(currentHistory.ProductCodes, entry.ProductCode); AddIfNotPresentAndNotEmpty(currentHistory.UpgradeCodes, entry.UpgradeCode); } } // Copy the data in so that we can continue using currentHistory to track all strings HistoricalMetadataList.emplace_back(currentHistory); // Now, copy over the other historical data, filtering out anything we have seen for (const auto& historical : source.HistoricalMetadataList) { HistoricalMetadata copied; copied.ProductVersionMin = historical.ProductVersionMin; copied.ProductVersionMax = historical.ProductVersionMax; AddIfNotPresent(copied.Names, currentHistory.Names, historical.Names); AddIfNotPresent(copied.Publishers, currentHistory.Publishers, historical.Publishers); AddIfNotPresent(copied.ProductCodes, currentHistory.ProductCodes, historical.ProductCodes); AddIfNotPresent(copied.UpgradeCodes, currentHistory.UpgradeCodes, historical.UpgradeCodes); if (!copied.Names.empty() || !copied.Publishers.empty() || !copied.ProductCodes.empty() || !copied.UpgradeCodes.empty()) { HistoricalMetadataList.emplace_back(std::move(copied)); } } } void ProductMetadata::FromJson_1_N(const web::json::value& json) { AICLI_LOG(Repo, Info, << "Parsing metadata JSON " << SchemaVersion.ToString() << " fields"); ProductMetadataFields_1_N fields{ SchemaVersion }; auto productVersionMinString = AppInstaller::JSON::GetRawStringValueFromJsonNode(json, fields.ProductVersionMin); if (productVersionMinString) { ProductVersionMin = Version{ std::move(productVersionMinString).value() }; } auto productVersionMaxString = AppInstaller::JSON::GetRawStringValueFromJsonNode(json, fields.ProductVersionMax); if (productVersionMaxString) { ProductVersionMax = Version{ std::move(productVersionMaxString).value() }; } // The 1.0 version of metadata uses the 1.5 version of REST JSON::ManifestJSONParser parser{ Version{ "1.5" } }; std::string submissionIdentifierVerification; auto metadataArray = AppInstaller::JSON::GetRawJsonArrayFromJsonNode(json, fields.Metadata); if (metadataArray) { for (const auto& item : metadataArray->get()) { std::string installerHashString = GetRequiredString(item, fields.InstallerHash); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, InstallerMetadataMap.find(installerHashString) != InstallerMetadataMap.end()); InstallerMetadata installerMetadata; installerMetadata.SubmissionIdentifier = GetRequiredString(item, fields.SubmissionIdentifier); if (submissionIdentifierVerification.empty()) { submissionIdentifierVerification = installerMetadata.SubmissionIdentifier; } else if (submissionIdentifierVerification != installerMetadata.SubmissionIdentifier) { AICLI_LOG(Repo, Error, << "Different submission identifiers found in metadata: '" << submissionIdentifierVerification << "' and '" << installerMetadata.SubmissionIdentifier << "'"); THROW_HR(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE); } auto appsAndFeatures = AppInstaller::JSON::GetRawJsonArrayFromJsonNode(item, fields.AppsAndFeaturesEntries); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, !appsAndFeatures); installerMetadata.AppsAndFeaturesEntries = parser.DeserializeAppsAndFeaturesEntries(appsAndFeatures.value()); installerMetadata.Scope = GetStringFromFutureSchema(item, fields.Scope).value_or(std::string{}); if (!fields.InstalledFiles.empty()) { auto installedFiles = AppInstaller::JSON::GetJsonValueFromNode(item, fields.InstalledFiles); if (installedFiles) { installerMetadata.InstalledFiles = parser.DeserializeInstallationMetadata(installedFiles->get()); } } if (!fields.InstalledStartupLinks.empty()) { auto startupLinks = AppInstaller::JSON::GetJsonValueFromNode(item, fields.InstalledStartupLinks); if (startupLinks) { installerMetadata.StartupLinkFiles = DeserializeInstalledStartupLinks(startupLinks->get(), fields); } } if (!fields.Icons.empty()) { auto icons = AppInstaller::JSON::GetJsonValueFromNode(item, fields.Icons); if (icons) { installerMetadata.Icons = DeserializeExtractedIcons(icons->get(), fields); } } InstallerMetadataMap[installerHashString] = std::move(installerMetadata); } } auto historicalArray = AppInstaller::JSON::GetRawJsonArrayFromJsonNode(json, fields.Historical); if (historicalArray) { for (const auto& item : historicalArray->get()) { HistoricalMetadata historicalMetadata; historicalMetadata.ProductVersionMin = Version{ GetRequiredString(item, fields.VersionMin) }; historicalMetadata.ProductVersionMax = Version{ GetRequiredString(item, fields.VersionMax) }; historicalMetadata.Names = AppInstaller::JSON::GetRawStringSetFromJsonNode(item, fields.Names); historicalMetadata.Publishers = AppInstaller::JSON::GetRawStringSetFromJsonNode(item, fields.Publishers); historicalMetadata.ProductCodes = AppInstaller::JSON::GetRawStringSetFromJsonNode(item, fields.ProductCodes); historicalMetadata.UpgradeCodes = AppInstaller::JSON::GetRawStringSetFromJsonNode(item, fields.UpgradeCodes); HistoricalMetadataList.emplace_back(std::move(historicalMetadata)); } } } web::json::value ProductMetadata::ToJson_1_N() { AICLI_LOG(Repo, Info, << "Creating metadata JSON " << SchemaVersion.ToString() << " fields"); ProductMetadataFields_1_N fields{ SchemaVersion }; web::json::value result; result[fields.Version] = web::json::value::string(fields.SchemaVersion); result[fields.ProductVersionMin] = AppInstaller::JSON::GetStringValue(ProductVersionMin.ToString()); result[fields.ProductVersionMax] = AppInstaller::JSON::GetStringValue(ProductVersionMax.ToString()); web::json::value metadataArray = web::json::value::array(); size_t metadataItemIndex = 0; for (const auto& item : InstallerMetadataMap) { web::json::value itemValue; itemValue[fields.InstallerHash] = AppInstaller::JSON::GetStringValue(item.first); itemValue[fields.SubmissionIdentifier] = AppInstaller::JSON::GetStringValue(item.second.SubmissionIdentifier); SetStringFromFutureSchema(itemValue, fields.Scope, item.second.Scope); if (!fields.InstalledFiles.empty() && item.second.InstalledFiles.has_value()) { web::json::value installationMetadata; installationMetadata[fields.DefaultInstallLocation] = AppInstaller::JSON::GetStringValue(item.second.InstalledFiles->DefaultInstallLocation); web::json::value installedFilesArray = web::json::value::array(); size_t installedFileIndex = 0; for (const auto& entry : item.second.InstalledFiles->Files) { web::json::value entryValue; AddFieldIfNotEmpty(entryValue, fields.InstalledFileRelativeFilePath, entry.RelativeFilePath); AddFieldIfNotEmpty(entryValue, fields.InstalledFileInvocationParameter, entry.InvocationParameter); AddFieldIfNotEmpty(entryValue, fields.InstalledFileDisplayName, entry.DisplayName); entryValue[fields.InstalledFileType] = AppInstaller::JSON::GetStringValue(Manifest::InstalledFileTypeToString(entry.FileType)); if (!entry.FileSha256.empty()) { entryValue[fields.InstalledFileSha256] = AppInstaller::JSON::GetStringValue(SHA256::ConvertToString(entry.FileSha256)); } installedFilesArray[installedFileIndex++] = std::move(entryValue); } installationMetadata[fields.InstallationMetadataFiles] = std::move(installedFilesArray); itemValue[fields.InstalledFiles] = std::move(installationMetadata); } if (!fields.InstalledStartupLinks.empty() && item.second.StartupLinkFiles.has_value()) { web::json::value startupLinkFilesArray = web::json::value::array(); size_t startupLinkFileIndex = 0; for (const auto& entry : item.second.StartupLinkFiles.value()) { web::json::value entryValue; entryValue[fields.InstalledStartupLinkPath] = AppInstaller::JSON::GetStringValue(entry.RelativeFilePath); entryValue[fields.InstalledStartupLinkType] = AppInstaller::JSON::GetStringValue(Manifest::InstalledFileTypeToString(entry.FileType)); startupLinkFilesArray[startupLinkFileIndex++] = std::move(entryValue); } itemValue[fields.InstalledStartupLinks] = std::move(startupLinkFilesArray); } if (!fields.Icons.empty() && !item.second.Icons.empty()) { web::json::value iconsArray = web::json::value::array(); size_t iconIndex = 0; for (const auto& entry : item.second.Icons) { web::json::value entryValue; entryValue[fields.IconContent] = AppInstaller::JSON::GetStringValue(AppInstaller::JSON::Base64Encode(entry.IconContent)); if (!entry.IconSha256.empty()) { entryValue[fields.IconSha256] = AppInstaller::JSON::GetStringValue(SHA256::ConvertToString(entry.IconSha256)); } entryValue[fields.IconFileType] = AppInstaller::JSON::GetStringValue(Manifest::IconFileTypeToString(entry.IconFileType)); entryValue[fields.IconTheme] = AppInstaller::JSON::GetStringValue(Manifest::IconThemeToString(entry.IconTheme)); entryValue[fields.IconResolution] = AppInstaller::JSON::GetStringValue(Manifest::IconResolutionToString(entry.IconResolution)); iconsArray[iconIndex++] = std::move(entryValue); } itemValue[fields.Icons] = std::move(iconsArray); } web::json::value appsAndFeaturesArray = web::json::value::array(); size_t appsAndFeaturesEntryIndex = 0; for (const auto& entry : item.second.AppsAndFeaturesEntries) { web::json::value entryValue; AddFieldIfNotEmpty(entryValue, fields.DisplayName, entry.DisplayName); AddFieldIfNotEmpty(entryValue, fields.Publisher, entry.Publisher); AddFieldIfNotEmpty(entryValue, fields.DisplayVersion, entry.DisplayVersion); AddFieldIfNotEmpty(entryValue, fields.ProductCode, entry.ProductCode); AddFieldIfNotEmpty(entryValue, fields.UpgradeCode, entry.UpgradeCode); if (entry.InstallerType != Manifest::InstallerTypeEnum::Unknown) { entryValue[fields.InstallerType] = AppInstaller::JSON::GetStringValue(Manifest::InstallerTypeToString(entry.InstallerType)); } appsAndFeaturesArray[appsAndFeaturesEntryIndex++] = std::move(entryValue); } itemValue[fields.AppsAndFeaturesEntries] = std::move(appsAndFeaturesArray); metadataArray[metadataItemIndex++] = std::move(itemValue); } result[fields.Metadata] = std::move(metadataArray); web::json::value historicalArray = web::json::value::array(); size_t historicalItemIndex = 0; for (const auto& item : HistoricalMetadataList) { web::json::value itemValue; itemValue[fields.VersionMin] = AppInstaller::JSON::GetStringValue(item.ProductVersionMin.ToString()); itemValue[fields.VersionMax] = AppInstaller::JSON::GetStringValue(item.ProductVersionMax.ToString()); itemValue[fields.Names] = CreateStringArray(item.Names); itemValue[fields.Publishers] = CreateStringArray(item.Publishers); itemValue[fields.ProductCodes] = CreateStringArray(item.ProductCodes); itemValue[fields.UpgradeCodes] = CreateStringArray(item.UpgradeCodes); historicalArray[historicalItemIndex++] = std::move(itemValue); } result[fields.Historical] = std::move(historicalArray); return result; } bool ProductMetadata::DropOldestHistoricalData() { if (HistoricalMetadataList.empty()) { return false; } HistoricalMetadataList.pop_back(); return true; } InstallerMetadataCollectionContext::InstallerMetadataCollectionContext() : m_correlationData(std::make_unique()), m_installedFilesCorrelation(std::make_unique()) {} InstallerMetadataCollectionContext::InstallerMetadataCollectionContext( std::unique_ptr correlationData, std::unique_ptr installedFilesCorrelation, const std::wstring& json) : m_correlationData(std::move(correlationData)), m_installedFilesCorrelation(std::move(installedFilesCorrelation)) { auto threadGlobalsLifetime = InitializeLogging({}); InitializePreinstallState(json); } std::unique_ptr InstallerMetadataCollectionContext::FromFile(const std::filesystem::path& file, const std::filesystem::path& logFile) { THROW_HR_IF(E_INVALIDARG, file.empty()); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), !std::filesystem::exists(file)); std::unique_ptr result = std::make_unique(); auto threadGlobalsLifetime = result->InitializeLogging(logFile); AICLI_LOG(Repo, Info, << "Opening InstallerMetadataCollectionContext input file: " << file); std::ifstream fileStream{ file }; auto content = ReadEntireStream(fileStream); // CppRestSdk's implementation of json parsing does not work with '\0', so trimming them here content.erase(std::find(content.begin(), content.end(), '\0'), content.end()); result->InitializePreinstallState(ConvertToUTF16(content)); return result; } std::unique_ptr InstallerMetadataCollectionContext::FromURI(std::wstring_view uri, const std::filesystem::path& logFile) { THROW_HR_IF(E_INVALIDARG, uri.empty()); std::unique_ptr result = std::make_unique(); auto threadGlobalsLifetime = result->InitializeLogging(logFile); std::string utf8Uri = ConvertToUTF8(uri); THROW_HR_IF(E_INVALIDARG, !IsUrlRemote(utf8Uri)); AICLI_LOG(Repo, Info, << "Downloading InstallerMetadataCollectionContext input file: " << utf8Uri); std::ostringstream jsonStream; ProgressCallback emptyCallback; const int MaxRetryCount = 2; for (int retryCount = 0; retryCount < MaxRetryCount; ++retryCount) { try { auto downloadHash = DownloadToStream(utf8Uri, jsonStream, DownloadType::InstallerMetadataCollectionInput, emptyCallback); break; } catch (...) { if (retryCount < MaxRetryCount - 1) { AICLI_LOG(Repo, Info, << " Downloading InstallerMetadataCollectionContext input failed, waiting a bit and retrying..."); Sleep(500); } else { throw; } } } result->InitializePreinstallState(ConvertToUTF16(jsonStream.str())); return result; } std::unique_ptr InstallerMetadataCollectionContext::FromJSON(const std::wstring& json, const std::filesystem::path& logFile) { THROW_HR_IF(E_INVALIDARG, json.empty()); std::unique_ptr result = std::make_unique(); auto threadGlobalsLifetime = result->InitializeLogging(logFile); result->InitializePreinstallState(json); return result; } void InstallerMetadataCollectionContext::Complete(const std::filesystem::path& output) { auto threadGlobalsLifetime = m_threadGlobals.SetForCurrentThread(); THROW_HR_IF(E_INVALIDARG, !output.has_filename()); if (output.has_parent_path()) { std::filesystem::create_directories(output.parent_path()); } std::ofstream outputStream{ output }; THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_OPEN_FAILED), !outputStream); CompleteWithThreadGlobalsSet(outputStream); } void InstallerMetadataCollectionContext::Complete(std::ostream& output) { auto threadGlobalsLifetime = m_threadGlobals.SetForCurrentThread(); CompleteWithThreadGlobalsSet(output); } std::wstring InstallerMetadataCollectionContext::Merge(const std::wstring& json, size_t maximumSizeInBytes, const std::filesystem::path& logFile) { ThreadLocalStorage::WingetThreadGlobals threadGlobals; auto globalsLifetime = InitializeLogging(threadGlobals, logFile); AICLI_LOG(Repo, Info, << "Parsing input JSON:\n" << ConvertToUTF8(json)); // Parse and validate JSON try { utility::string_t versionFieldName = L"version"; web::json::value inputValue = web::json::value::parse(json); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, inputValue.is_null()); Version inputVersion = Version{ GetRequiredString(inputValue, versionFieldName) }; AICLI_LOG(Repo, Info, << "Parsing input JSON version " << inputVersion.ToString()); web::json::value mergedResult; if (inputVersion.PartAt(0).Integer == 1) { mergedResult = Merge_1_0(inputValue, maximumSizeInBytes); } else { AICLI_LOG(Repo, Error, << "Don't know how to handle version " << inputVersion.ToString()); THROW_HR(HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)); } std::wostringstream outputStream; mergedResult.serialize(outputStream); return std::move(outputStream).str(); } catch (const web::json::json_exception& exc) { AICLI_LOG(Repo, Error, << "Exception parsing input JSON: " << exc.what()); } // We will return within the try or throw a non-json exception, so if we get here it was a json exception. THROW_HR(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE); } void InstallerMetadataCollectionContext::CompleteWithThreadGlobalsSet(std::ostream& output) { web::json::value outputJSON; if (!ContainsError()) { try { // Collect post-install system state m_correlationData->CapturePostInstallSnapshot(); m_installedFilesCorrelation->StopFileWatcher(); ComputeOutputData(); // Construct output JSON AICLI_LOG(Repo, Info, << "Creating output JSON version for input version " << m_inputVersion.ToString()); if (m_inputVersion.PartAt(0).Integer == 1) { // We only have one version currently, so use that as long as the major version is 1 outputJSON = CreateOutputJson_1_0(); } else { AICLI_LOG(Repo, Error, << "Don't know how to output for version " << m_inputVersion.ToString()); THROW_HR(HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)); } } catch (...) { CollectErrorDataFromException(std::current_exception()); } } if (ContainsError()) { // We only have one version currently outputJSON = CreateErrorJson_1_0(); } // Write output outputJSON.serialize(output); } std::unique_ptr InstallerMetadataCollectionContext::InitializeLogging(ThreadLocalStorage::WingetThreadGlobals& threadGlobals, const std::filesystem::path& logFile) { auto threadGlobalsLifetime = threadGlobals.SetForCurrentThread(); Logging::Log().SetLevel(Logging::Level::Info); Logging::Log().SetEnabledChannels(Logging::Channel::All); Logging::EnableWilFailureTelemetry(); Logging::TraceLogger::Add(); if (!logFile.empty()) { Logging::FileLogger::Add(logFile); } Logging::Telemetry().SetCaller("installer-metadata-collection"); Logging::Telemetry().LogStartup(); return threadGlobalsLifetime; } std::unique_ptr InstallerMetadataCollectionContext::InitializeLogging(const std::filesystem::path& logFile) { return InitializeLogging(m_threadGlobals, logFile); } void InstallerMetadataCollectionContext::InitializePreinstallState(const std::wstring& json) { try { AICLI_LOG(Repo, Info, << "Parsing input JSON:\n" << ConvertToUTF8(json)); // Parse and validate JSON try { utility::string_t versionFieldName = L"version"; web::json::value inputValue = web::json::value::parse(json); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, inputValue.is_null()); m_inputVersion = Version{ GetRequiredString(inputValue, versionFieldName) }; AICLI_LOG(Repo, Info, << "Parsing input JSON version " << m_inputVersion.ToString()); if (m_inputVersion.PartAt(0).Integer == 1) { // We only have one version currently, so use that as long as the major version is 1 ParseInputJson_1_0(inputValue); } else { AICLI_LOG(Repo, Error, << "Don't know how to handle version " << m_inputVersion.ToString()); THROW_HR(HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)); } } catch (const web::json::json_exception& exc) { AICLI_LOG(Repo, Error, << "Exception parsing input JSON: " << exc.what()); throw; } // Collect pre-install system state m_correlationData->CapturePreInstallSnapshot(); m_installedFilesCorrelation->StartFileWatcher(); } catch (...) { CollectErrorDataFromException(std::current_exception()); } } void InstallerMetadataCollectionContext::ComputeOutputData() { // Copy the metadata from the current; this function takes care of moving data to historical if the submission is new. m_outputMetadata.CopyFrom(m_currentMetadata, m_submissionIdentifier); Correlation::ARPCorrelationSettings settings; std::string arpInstallLocation; // As this code is typically run in a controlled environment, we can assume that a single value change is very likely the correct value. settings.AllowSingleChange = true; // ARP entry correlation Correlation::ARPCorrelationResult correlationResult = m_correlationData->CorrelateForNewlyInstalled(m_incomingManifest, settings); if (correlationResult.Package) { auto& package = correlationResult.Package; // Update min and max versions based on the version of the correlated package Version packageVersion{ package->GetProperty(PackageVersionProperty::Version) }; if (m_outputMetadata.ProductVersionMin.IsEmpty() || packageVersion < m_outputMetadata.ProductVersionMin) { m_outputMetadata.ProductVersionMin = packageVersion; } if (m_outputMetadata.ProductVersionMax.IsEmpty() || m_outputMetadata.ProductVersionMax < packageVersion) { m_outputMetadata.ProductVersionMax = packageVersion; } // Create the AppsAndFeaturesEntry that we need to add Manifest::AppsAndFeaturesEntry newEntry; auto packageMetadata = package->GetMetadata(); // Arp installed location will be used in later installed files correlation. arpInstallLocation = packageMetadata[PackageVersionMetadata::InstalledLocation]; // TODO: Use some amount of normalization here to prevent things like versions being in the name from bloating the data newEntry.DisplayName = package->GetProperty(PackageVersionProperty::Name).get(); newEntry.DisplayVersion = packageVersion.ToString(); if (packageMetadata.count(PackageVersionMetadata::InstalledType)) { newEntry.InstallerType = Manifest::ConvertToInstallerTypeEnum(packageMetadata[PackageVersionMetadata::InstalledType]); } auto productCodes = package->GetMultiProperty(PackageVersionMultiProperty::ProductCode); if (!productCodes.empty()) { newEntry.ProductCode = std::move(productCodes[0]).get(); } newEntry.Publisher = package->GetProperty(PackageVersionProperty::Publisher).get(); // TODO: Support upgrade code throughout the code base... Manifest::ScopeEnum scope = Manifest::ConvertToScopeEnum(packageMetadata[PackageVersionMetadata::InstalledScope]); // ARP entry icon extraction upon ARP correlation success auto icons = ExtractIconFromArpEntry(newEntry.ProductCode, scope); // Add or update the metadata for the installer hash auto itr = m_outputMetadata.InstallerMetadataMap.find(m_installerHash); if (itr == m_outputMetadata.InstallerMetadataMap.end()) { // New entry needed ProductMetadata::InstallerMetadata newMetadata; newMetadata.SubmissionIdentifier = m_submissionIdentifier; newMetadata.AppsAndFeaturesEntries.emplace_back(std::move(newEntry)); if (scope != Manifest::ScopeEnum::Unknown) { newMetadata.Scope = Manifest::ScopeToString(scope); } if (!icons.empty()) { newMetadata.Icons = std::move(icons); } m_outputMetadata.InstallerMetadataMap[m_installerHash] = std::move(newMetadata); } else { if (itr->second.Scope.empty()) { itr->second.Scope = Manifest::ScopeToString(scope); } // If there is a conflicting scope already present, force it to Unknown else if (scope != Manifest::ScopeEnum::Unknown && Manifest::ConvertToScopeEnum(itr->second.Scope) != scope) { itr->second.Scope = Manifest::ScopeToString(Manifest::ScopeEnum::Unknown); } // We will always use the latest extracted icons upon confliction. if (!icons.empty()) { itr->second.Icons = std::move(icons); } // Existing entry for installer hash, add/update the entry FilterAndAddToEntries(std::move(newEntry), itr->second.AppsAndFeaturesEntries); } } // Installation files correlation auto installationMetadata = m_installedFilesCorrelation->CorrelateForNewlyInstalled(m_incomingManifest, arpInstallLocation); if (installationMetadata.InstalledFiles.HasData() || !installationMetadata.StartupLinkFiles.empty()) { // Add or update the metadata for the installer hash auto itr = m_outputMetadata.InstallerMetadataMap.find(m_installerHash); if (itr == m_outputMetadata.InstallerMetadataMap.end()) { // New entry needed ProductMetadata::InstallerMetadata newMetadata; newMetadata.SubmissionIdentifier = m_submissionIdentifier; if (installationMetadata.InstalledFiles.HasData()) { newMetadata.InstalledFiles = std::move(installationMetadata.InstalledFiles); } if (!installationMetadata.StartupLinkFiles.empty()) { newMetadata.StartupLinkFiles = std::move(installationMetadata.StartupLinkFiles); } m_outputMetadata.InstallerMetadataMap[m_installerHash] = std::move(newMetadata); } else { // Add new or merge with existing entry if (installationMetadata.InstalledFiles.HasData()) { if (!itr->second.InstalledFiles.has_value()) { itr->second.InstalledFiles = std::move(installationMetadata.InstalledFiles); } else { MergeInstalledFilesMetadata(*(itr->second.InstalledFiles), installationMetadata.InstalledFiles); } } if (!installationMetadata.StartupLinkFiles.empty()) { if (!itr->second.StartupLinkFiles.has_value()) { itr->second.StartupLinkFiles = std::move(installationMetadata.StartupLinkFiles); } else { MergeStartupLinkFilesMetadata(*(itr->second.StartupLinkFiles), installationMetadata.StartupLinkFiles); } } } } if (correlationResult.Package) { m_outputStatus = OutputStatus::Success; } else { m_outputStatus = OutputStatus::LowConfidence; } // Create the diagnostics data, based on the other values from the correlation result. DiagnosticFields fields; m_outputDiagnostics[fields.Reason] = AppInstaller::JSON::GetStringValue(correlationResult.Reason); m_outputDiagnostics[fields.ChangedEntryCount] = web::json::value::number(static_cast(correlationResult.ChangesToARP)); m_outputDiagnostics[fields.MatchedEntryCount] = web::json::value::number(static_cast(correlationResult.MatchesInARP)); m_outputDiagnostics[fields.IntersectionCount] = web::json::value::number(static_cast(correlationResult.CountOfIntersectionOfChangesAndMatches)); constexpr size_t MaximumDiagnosticMeasures = 10; web::json::value measuresArray = web::json::value::array(); for (size_t i = 0; i < correlationResult.Measures.size() && i < MaximumDiagnosticMeasures; ++i) { web::json::value measureValue; const auto& measure = correlationResult.Measures[i]; measureValue[fields.Value] = web::json::value::number(measure.Measure); measureValue[fields.Name] = AppInstaller::JSON::GetStringValue(measure.Package->GetProperty(PackageVersionProperty::Name)); measureValue[fields.Publisher] = AppInstaller::JSON::GetStringValue(measure.Package->GetProperty(PackageVersionProperty::Publisher)); measuresArray[i] = std::move(measureValue); } m_outputDiagnostics[fields.CorrelationMeasures] = std::move(measuresArray); } void InstallerMetadataCollectionContext::ParseInputJson_1_0(web::json::value& input) { AICLI_LOG(Repo, Info, << "Parsing input JSON 1.0 fields"); // Field names utility::string_t metadataVersionFieldName = L"supportedMetadataVersion"; utility::string_t metadataFieldName = L"currentMetadata"; utility::string_t submissionDataFieldName = L"submissionData"; utility::string_t submissionIdentifierFieldName = L"submissionIdentifier"; utility::string_t packageDataFieldName = L"packageData"; utility::string_t installerHashFieldName = L"installerHash"; utility::string_t defaultLocaleFieldName = L"DefaultLocale"; utility::string_t localesFieldName = L"Locales"; // root fields m_supportedMetadataVersion = Version{ GetRequiredString(input, metadataVersionFieldName) }; auto currentMetadataValue = AppInstaller::JSON::GetJsonValueFromNode(input, metadataFieldName); if (currentMetadataValue) { m_currentMetadata.FromJson(currentMetadataValue.value()); } // submissionData fields auto submissionDataValue = AppInstaller::JSON::GetJsonValueFromNode(input, submissionDataFieldName); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, !submissionDataValue); m_submissionData = submissionDataValue.value(); m_submissionIdentifier = GetRequiredString(m_submissionData, submissionIdentifierFieldName); // packageData fields auto packageDataValue = AppInstaller::JSON::GetJsonValueFromNode(input, packageDataFieldName); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, !packageDataValue); m_installerHash = GetRequiredString(packageDataValue.value(), installerHashFieldName); // The 1.0 version of input uses the 1.5 version of REST JSON::ManifestJSONParser parser{ Version{ "1.5" }}; { auto defaultLocaleValue = AppInstaller::JSON::GetJsonValueFromNode(packageDataValue.value(), defaultLocaleFieldName); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, !defaultLocaleValue); auto defaultLocale = parser.DeserializeLocale(defaultLocaleValue.value()); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, !defaultLocale || !defaultLocale->Contains(Manifest::Localization::PackageName) || !defaultLocale->Contains(Manifest::Localization::Publisher)); m_incomingManifest.DefaultLocalization = std::move(defaultLocale).value(); auto localesArray = AppInstaller::JSON::GetRawJsonArrayFromJsonNode(packageDataValue.value(), localesFieldName); if (localesArray) { for (const auto& locale : localesArray->get()) { auto localization = parser.DeserializeLocale(locale); if (localization) { m_incomingManifest.Localizations.emplace_back(std::move(localization).value()); } } } } } web::json::value InstallerMetadataCollectionContext::CreateOutputJson_1_0() { AICLI_LOG(Repo, Info, << "Setting output JSON 1.0 fields"); OutputFields_1_0 fields; web::json::value result; result[fields.Version] = web::json::value::string(L"1.0"); result[fields.SubmissionData] = m_submissionData; result[fields.InstallerHash] = AppInstaller::JSON::GetStringValue(m_installerHash); // Limit output status to 1.0 known values OutputStatus statusToUse = OutputStatus::Unknown; if (m_outputStatus == OutputStatus::Success || m_outputStatus == OutputStatus::Error || m_outputStatus == OutputStatus::LowConfidence) { statusToUse = m_outputStatus; } result[fields.Status] = web::json::value::string(ToString(statusToUse)); if (m_outputStatus == OutputStatus::Success) { result[fields.Metadata] = m_outputMetadata.ToJson(m_supportedMetadataVersion, 0); } result[fields.Diagnostics] = m_outputDiagnostics; return result; } utility::string_t InstallerMetadataCollectionContext::ToString(OutputStatus status) { switch (status) { case OutputStatus::Success: return L"Success"; case OutputStatus::Error: return L"Error"; case OutputStatus::LowConfidence: return L"LowConfidence"; } // For both the status value of Unknown and anything else return L"Unknown"; } bool InstallerMetadataCollectionContext::ContainsError() const { return m_outputStatus == OutputStatus::Error; } void InstallerMetadataCollectionContext::CollectErrorDataFromException(std::exception_ptr exception) { m_outputStatus = OutputStatus::Error; try { std::rethrow_exception(exception); } catch (const wil::ResultException& re) { m_errorHR = re.GetErrorCode(); m_errorText = GetUserPresentableMessage(re); } catch (const winrt::hresult_error& hre) { m_errorHR = hre.code(); m_errorText = GetUserPresentableMessage(hre); } catch (const std::exception& e) { m_errorHR = E_FAIL; m_errorText = GetUserPresentableMessage(e); } catch (...) { m_errorHR = E_UNEXPECTED; m_errorText = "An unexpected exception type was thrown."; } } web::json::value InstallerMetadataCollectionContext::CreateErrorJson_1_0() { AICLI_LOG(Repo, Info, << "Setting error JSON 1.0 fields"); OutputFields_1_0 fields; DiagnosticFields diagnosticFields; web::json::value result; result[fields.Version] = web::json::value::string(L"1.0"); result[fields.SubmissionData] = m_submissionData; result[fields.InstallerHash] = AppInstaller::JSON::GetStringValue(m_installerHash); result[fields.Status] = web::json::value::string(ToString(OutputStatus::Error)); result[fields.Metadata] = web::json::value::null(); web::json::value error; error[diagnosticFields.ErrorHR] = web::json::value::number(static_cast(m_errorHR)); error[diagnosticFields.ErrorText] = AppInstaller::JSON::GetStringValue(m_errorText); result[fields.Diagnostics] = std::move(error); return result; } web::json::value InstallerMetadataCollectionContext::Merge_1_0(web::json::value& input, size_t maximumSizeInBytes) { AICLI_LOG(Repo, Info, << "Merging 1.0 input metadatas"); utility::string_t metadatasFieldName = L"metadatas"; auto metadatasValue = AppInstaller::JSON::GetRawJsonArrayFromJsonNode(input, metadatasFieldName); THROW_HR_IF(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, !metadatasValue); std::vector metadatas; for (const auto& value : metadatasValue->get()) { ProductMetadata current; current.FromJson(value); metadatas.emplace_back(std::move(current)); } THROW_HR_IF(E_NOT_SET, metadatas.empty()); // Require that all merging values use the same submission for (const ProductMetadata& metadata : metadatas) { const std::string& firstSubmission = metadatas[0].InstallerMetadataMap.begin()->second.SubmissionIdentifier; const std::string& metadataSubmission = metadata.InstallerMetadataMap.begin()->second.SubmissionIdentifier; if (firstSubmission != metadataSubmission) { AICLI_LOG(Repo, Info, << "Found submission identifier mismatch: " << firstSubmission << " != " << metadataSubmission); THROW_HR(E_NOT_VALID_STATE); } } // Do the actual merging ProductMetadata resultMetadata; // The historical data should be the same across the board, so we can just copy the first one. resultMetadata.HistoricalMetadataList = metadatas[0].HistoricalMetadataList; for (const ProductMetadata& metadata : metadatas) { // Get the minimum and maximum versions from the individual values if (resultMetadata.ProductVersionMin.IsEmpty() || metadata.ProductVersionMin < resultMetadata.ProductVersionMin) { resultMetadata.ProductVersionMin = metadata.ProductVersionMin; } if (resultMetadata.ProductVersionMax < metadata.ProductVersionMax) { resultMetadata.ProductVersionMax = metadata.ProductVersionMax; } if (resultMetadata.SchemaVersion < metadata.SchemaVersion) { resultMetadata.SchemaVersion = metadata.SchemaVersion; } for (const auto& installerMetadata : metadata.InstallerMetadataMap) { auto itr = resultMetadata.InstallerMetadataMap.find(installerMetadata.first); if (itr == resultMetadata.InstallerMetadataMap.end()) { // Installer hash not in the result, so just copy it resultMetadata.InstallerMetadataMap.emplace(installerMetadata); } else { if (itr->second.Scope.empty()) { itr->second.Scope = installerMetadata.second.Scope; } else if (!installerMetadata.second.Scope.empty()) { // If there is a conflicting scope already present, force it to Unknown if (Manifest::ConvertToScopeEnum(itr->second.Scope) != Manifest::ConvertToScopeEnum(installerMetadata.second.Scope)) { itr->second.Scope = Manifest::ScopeToString(Manifest::ScopeEnum::Unknown); } } // We will always use the latest extracted icons upon confliction. if (!installerMetadata.second.Icons.empty()) { itr->second.Icons = installerMetadata.second.Icons; } if (!itr->second.InstalledFiles.has_value()) { itr->second.InstalledFiles = installerMetadata.second.InstalledFiles; } else if (installerMetadata.second.InstalledFiles.has_value()) { MergeInstalledFilesMetadata(*(itr->second.InstalledFiles), *(installerMetadata.second.InstalledFiles)); } if (!itr->second.StartupLinkFiles.has_value()) { itr->second.StartupLinkFiles = installerMetadata.second.StartupLinkFiles; } else if (installerMetadata.second.StartupLinkFiles.has_value()) { MergeStartupLinkFilesMetadata(*(itr->second.StartupLinkFiles), *(installerMetadata.second.StartupLinkFiles)); } // Merge into existing installer data for (const auto& targetEntry : installerMetadata.second.AppsAndFeaturesEntries) { FilterAndAddToEntries(Manifest::AppsAndFeaturesEntry{ targetEntry }, itr->second.AppsAndFeaturesEntries); } } } } // Convert to JSON return resultMetadata.ToJson(resultMetadata.SchemaVersion, maximumSizeInBytes); } } ================================================ FILE: src/AppInstallerRepositoryCore/ManifestJSONParser.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/ManifestJSONParser.h" #include "Rest/Schema/1_0/Json/ManifestDeserializer.h" #include "Rest/Schema/1_1/Json/ManifestDeserializer.h" #include "Rest/Schema/1_4/Json/ManifestDeserializer.h" #include "Rest/Schema/1_5/Json/ManifestDeserializer.h" #include "Rest/Schema/1_6/Json/ManifestDeserializer.h" #include "Rest/Schema/1_7/Json/ManifestDeserializer.h" #include "Rest/Schema/1_9/Json/ManifestDeserializer.h" #include "Rest/Schema/1_10/Json/ManifestDeserializer.h" #include "Rest/Schema/1_12/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::JSON { struct ManifestJSONParser::impl { // The deserializer. We only have one lineage (1.0+) right now. std::unique_ptr m_deserializer; }; ManifestJSONParser::ManifestJSONParser(const Utility::Version& responseSchemaVersion) { const auto& parts = responseSchemaVersion.GetParts(); THROW_HR_IF(E_INVALIDARG, parts.empty()); m_pImpl = std::make_unique(); if (parts[0].Integer == 1) { if (parts.size() == 1 || parts[1].Integer == 0) { m_pImpl->m_deserializer = std::make_unique(); } else if (parts.size() > 1 && parts[1].Integer < 4) { m_pImpl->m_deserializer = std::make_unique(); } else if (parts.size() > 1 && parts[1].Integer < 5) { m_pImpl->m_deserializer = std::make_unique(); } else if (parts.size() > 1 && parts[1].Integer < 6) { m_pImpl->m_deserializer = std::make_unique(); } else if (parts.size() > 1 && parts[1].Integer < 7) { m_pImpl->m_deserializer = std::make_unique(); } else if (parts.size() > 1 && parts[1].Integer < 9) { m_pImpl->m_deserializer = std::make_unique(); } else if (parts.size() > 1 && parts[1].Integer < 10) { m_pImpl->m_deserializer = std::make_unique(); } else if (parts.size() > 1 && parts[1].Integer < 12) { m_pImpl->m_deserializer = std::make_unique(); } else { m_pImpl->m_deserializer = std::make_unique(); } } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)); } } ManifestJSONParser::ManifestJSONParser(ManifestJSONParser&&) noexcept = default; ManifestJSONParser& ManifestJSONParser::operator=(ManifestJSONParser&&) noexcept = default; ManifestJSONParser::~ManifestJSONParser() = default; std::vector ManifestJSONParser::Deserialize(const web::json::value& response) const { return m_pImpl->m_deserializer->Deserialize(response); } std::vector ManifestJSONParser::DeserializeData(const web::json::value& data) const { return m_pImpl->m_deserializer->DeserializeData(data); } std::vector ManifestJSONParser::DeserializeAppsAndFeaturesEntries(const web::json::array& data) const { return m_pImpl->m_deserializer->DeserializeAppsAndFeaturesEntries(data); } std::optional ManifestJSONParser::DeserializeLocale(const web::json::value& locale) const { return m_pImpl->m_deserializer->DeserializeLocale(locale); } std::optional ManifestJSONParser::DeserializeInstallationMetadata(const web::json::value& installationMetadata) const { return m_pImpl->m_deserializer->DeserializeInstallationMetadata(installationMetadata); } } ================================================ FILE: src/AppInstallerRepositoryCore/MatchCriteriaResolver.cpp ================================================ // Copyright (c) Microsoft Corporation. #include "pch.h" #include "MatchCriteriaResolver.h" #include namespace AppInstaller::Repository { namespace { using ValueMatchFunction = bool (*)(const Utility::NormalizedString&, const Utility::NormalizedString&); bool ValueMatchFunction_AlwaysFalse(const Utility::NormalizedString&, const Utility::NormalizedString&) { return false; } bool ValueMatchFunction_Exact(const Utility::NormalizedString& a, const Utility::NormalizedString& b) { return a == b; } bool ValueMatchFunction_CaseInsensitive(const Utility::NormalizedString& a, const Utility::NormalizedString& b) { return Utility::ICUCaseInsensitiveEquals(a, b); } bool ValueMatchFunction_StartsWith(const Utility::NormalizedString& a, const Utility::NormalizedString& b) { return Utility::ICUCaseInsensitiveStartsWith(a, b); } bool ValueMatchFunction_Substring(const Utility::NormalizedString& a, const Utility::NormalizedString& b) { return Utility::ContainsSubstring(FoldCase(a), FoldCase(b)); } ValueMatchFunction GetMatchTypeFunction(MatchType matchType) { switch (matchType) { case MatchType::Exact: return ValueMatchFunction_Exact; case MatchType::CaseInsensitive: return ValueMatchFunction_CaseInsensitive; case MatchType::StartsWith: return ValueMatchFunction_StartsWith; case MatchType::Substring: return ValueMatchFunction_Substring; case MatchType::Fuzzy: case MatchType::FuzzySubstring: case MatchType::Wildcard: default: return ValueMatchFunction_AlwaysFalse; } } PackageVersionProperty GetPackageVersionPropertyFor(PackageMatchField field) { switch (field) { case PackageMatchField::Id: return PackageVersionProperty::Id; case PackageMatchField::Name: return PackageVersionProperty::Name; case PackageMatchField::Moniker: return PackageVersionProperty::Moniker; default: THROW_HR(E_UNEXPECTED); } } PackageVersionMultiProperty GetPackageVersionMultiPropertyFor(PackageMatchField field) { switch (field) { case PackageMatchField::Command: return PackageVersionMultiProperty::Command; case PackageMatchField::Tag: return PackageVersionMultiProperty::Tag; case PackageMatchField::PackageFamilyName: return PackageVersionMultiProperty::PackageFamilyName; case PackageMatchField::ProductCode: return PackageVersionMultiProperty::ProductCode; case PackageMatchField::UpgradeCode: return PackageVersionMultiProperty::UpgradeCode; default: THROW_HR(E_UNEXPECTED); } } // Gets the best match type for the given field value and required minimum match type. std::optional GetBestMatchType(const RequestMatch& request, MatchType mustBeBetterThanMatchType, const Utility::NormalizedString& value) { if (request.Value.empty()) { return std::nullopt; } for (auto matchType : { MatchType::Exact, MatchType::CaseInsensitive, MatchType::StartsWith, MatchType::Substring }) { if (matchType >= mustBeBetterThanMatchType) { break; } auto matchFunction = GetMatchTypeFunction(matchType); if (matchFunction(value, request.Value)) { return matchType; } } return std::nullopt; } // Gets the best match type for the given field value and required minimum match type. std::optional GetBestMatchType(const SearchRequest& request, PackageMatchField field, MatchType mustBeBetterThanMatchType, const Utility::NormalizedString& value) { std::optional result; if (request.Query) { result = GetBestMatchType(request.Query.value(), mustBeBetterThanMatchType, value); if (result) { mustBeBetterThanMatchType = result.value(); } } for (const auto& filter : request.Filters) { if (result.value_or(MatchType::Wildcard) == MatchType::Exact) { break; } if (filter.Field == field) { std::optional filterResult = GetBestMatchType(filter, mustBeBetterThanMatchType, value); if (filterResult) { result = std::move(filterResult); mustBeBetterThanMatchType = result.value(); } } } return result; } // Gets the best match and updates the result if it should be updated. // Returns true to indicate that an exact match has been found. bool UpdatePackageMatchFilterCheck(const SearchRequest& request, PackageMatchField field, PackageMatchFilter& result, const Utility::LocIndString& propertyValue) { Utility::NormalizedString normalizedValue = propertyValue.get(); auto bestMatch = GetBestMatchType(request, field, result.Type, normalizedValue); if (bestMatch && bestMatch.value() < result.Type) { result.Type = bestMatch.value(); result.Field = field; result.Value = std::move(normalizedValue); } return MatchType::Exact == result.Type; } } PackageMatchFilter FindBestMatchCriteria(const SearchRequest& request, const IPackageVersion* packageVersion) { PackageMatchFilter result{ PackageMatchField::Unknown, MatchType::Wildcard }; // Single value fields for (auto field : { PackageMatchField::Id, PackageMatchField::Name, PackageMatchField::Moniker }) { auto propertyValue = packageVersion->GetProperty(GetPackageVersionPropertyFor(field)); if (propertyValue.empty()) { continue; } if (UpdatePackageMatchFilterCheck(request, field, result, propertyValue)) { break; } } // Multi-value fields for (auto field : { PackageMatchField::Command, PackageMatchField::Tag, PackageMatchField::PackageFamilyName, PackageMatchField::ProductCode, PackageMatchField::UpgradeCode }) { if (MatchType::Exact == result.Type) { break; } auto propertyValues = packageVersion->GetMultiProperty(GetPackageVersionMultiPropertyFor(field)); if (propertyValues.empty()) { continue; } for (const auto& propertyValue : propertyValues) { if (UpdatePackageMatchFilterCheck(request, field, result, propertyValue)) { break; } } } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/MatchCriteriaResolver.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Public/winget/RepositorySearch.h" namespace AppInstaller::Repository { // Finds the highest rated match criteria for the package based on the search request, PackageMatchFilter FindBestMatchCriteria(const SearchRequest& request, const IPackageVersion* packageVersion); } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ARPHelper.h" #include "winget/PortableARPEntry.h" namespace AppInstaller::Repository::Microsoft { using namespace AppInstaller::Registry::Portable; namespace { // "Unpacks" a GUID in the format used by the UpgradesCode registry key into the usual format. // Returns empty if it is not a valid GUID std::optional TryUnpackUpgradeCodeGuid(std::string_view packed) { // A GUID is made up of 4 parts: // - Part 1 is made up of one 4 byte block // - Parts 2 and 3 are made up of one 2 byte block // - Part 4 is made up of eight 1 byte blocks // // The GUID strings we have in the manifests represent all of this in hex in order, // with dashes between each part, and after the second byte of Part 4. // The "packed" GUIDs in the registry place the blocks in the same order, // without dashes and with opposite endian-ness. // // For example // ARP: {FECAFEB5-8D0E-4AE4-8FA0-745BAA835C35} // FECAFEB5 8D0E 4AE4 8F A0 74 5B AA 83 5C 35 // Part 1 P2 P3 <------ Part 4 -------> // 5BEFACEF E0D8 4EA4 F8 0A 47 B5 AA 38 C5 53 // UpgradeCode: 5BEFACEFE0D84EA4F80A47B5AA38C553 // // The conversion can be done by mapping each location in the packed string // to the appropriate location in the unpacked string. constexpr size_t PackedLength = 32; if (packed.length() != PackedLength || !std::all_of(packed.begin(), packed.end(), isxdigit)) { return {}; } // PositionMapping[i] is the position to which the i-th char is mapped // I.e., unpacked[ PositionMapping[i] ] = packed[i] constexpr size_t PositionMapping[PackedLength] = { 8,7,6,5,4,3,2,1, 13,12,11,10, 18,17,16,15, 21,20, 23,22, 26,25, 28,27, 30,29, 32,31, 34,33, 36,35, }; std::string unpacked("{00000000-0000-0000-0000-000000000000}"); for (size_t i = 0; i < PackedLength; ++i) { unpacked[PositionMapping[i]] = packed[i]; } return unpacked; } // Gets a mapping from ProductCode to UpgradeCode for MSI packages. std::map GetUpgradeCodes() { // The UpgradeCode is not stored in the ARP registry keys, so we have to get it separately. // We could use MsiGetProductProperty or MsiGetProperty from the MSI API to query it, // but it is very slow. // // The UpgradeCode is also stored in the registry under // HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes // (Note that this key is not documented, so it is possible that it will change but very unlikely...) // // Under 'UpgradeCodes' there is one key for each upgrade code, and each upgrade code key // contains the product code as a value. All the upgrade codes and product codes are GUIDs, // but represented in an unusual way - see TryUnpackUpgradeCodeGuid() AICLI_LOG(Repo, Info, << "Reading MSI UpgradeCodes"); std::map upgradeCodes; try { // There is no UpgradeCodes key on the x86 view of the registry Registry::Key upgradeCodesKey = Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UpgradeCodes", 0, KEY_READ | KEY_WOW64_64KEY); if (upgradeCodesKey) { for (const auto& upgradeCodeKeyRef : upgradeCodesKey) { std::string keyName; try { keyName = upgradeCodeKeyRef.Name(); auto upgradeCode = TryUnpackUpgradeCodeGuid(keyName); if (upgradeCode) { auto upgradeCodeKey = upgradeCodeKeyRef.Open(); for (const auto& productCodeValue : upgradeCodeKey.Values()) { auto productCode = TryUnpackUpgradeCodeGuid(productCodeValue.Name()); if (productCode) { upgradeCodes[*productCode] = *upgradeCode; } } } } CATCH_LOG_MSG("Failed to read upgrade code: %hs", keyName.c_str()); } } } CATCH_LOG_MSG("Failed to read upgrade codes."); return upgradeCodes; } } #ifndef AICLI_DISABLE_TEST_HOOKS using GetARPKeyFunc = std::function; static GetARPKeyFunc s_GetARPKey_Override; void SetGetARPKeyOverride(GetARPKeyFunc value) { s_GetARPKey_Override = value; } #endif Registry::Key ARPHelper::GetARPKey(Manifest::ScopeEnum scope, Utility::Architecture architecture) const { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_GetARPKey_Override) { return s_GetARPKey_Override(scope, architecture); } #endif HKEY rootKey = NULL; switch (scope) { case Manifest::ScopeEnum::User: rootKey = HKEY_CURRENT_USER; break; case Manifest::ScopeEnum::Machine: rootKey = HKEY_LOCAL_MACHINE; break; default: THROW_HR(E_UNEXPECTED); } bool isValid = false; REGSAM access = KEY_READ; switch (Utility::GetSystemArchitecture()) { case Utility::Architecture::X86: switch (architecture) { case Utility::Architecture::X86: isValid = true; break; } break; case Utility::Architecture::X64: switch (architecture) { case Utility::Architecture::X86: if (scope == Manifest::ScopeEnum::Machine) { access |= KEY_WOW64_32KEY; isValid = true; } break; case Utility::Architecture::X64: access |= KEY_WOW64_64KEY; isValid = true; break; } break; case Utility::Architecture::Arm: switch (architecture) { case Utility::Architecture::Arm: isValid = true; break; } break; case Utility::Architecture::Arm64: switch (architecture) { case Utility::Architecture::X86: if (scope == Manifest::ScopeEnum::Machine) { #ifdef _ARM_ // Not accessible if this is an ARM process AICLI_LOG(Repo, Warning, << "Cannot enumerate x86 machine ARP entries when current process is ARM"); #else access |= KEY_WOW64_32KEY; isValid = true; #endif } break; case Utility::Architecture::Arm64: access |= KEY_WOW64_64KEY; isValid = true; break; } break; } if (isValid) { return Registry::Key::OpenIfExists(rootKey, SubKeyPath, 0, access); } else { return {}; } } Registry::Key ARPHelper::FindARPEntry(const std::string& productCode, Manifest::ScopeEnum scope) const { if (productCode.empty()) { return {}; } std::vector scopesToSearch; if (scope == Manifest::ScopeEnum::Unknown) { scopesToSearch = { Manifest::ScopeEnum::User, Manifest::ScopeEnum::Machine }; } else { scopesToSearch = { scope }; } for (auto scopeToSearch : scopesToSearch) { for (auto architecture : Utility::GetApplicableArchitectures()) { Registry::Key arpRootKey = GetARPKey(scopeToSearch, architecture); if (arpRootKey) { for (const auto& entry : arpRootKey) { if (Utility::CaseInsensitiveEquals(productCode, entry.Name())) { return entry.Open(); } } } } } return {}; } bool ARPHelper::GetBoolValue(const Registry::Key& arpKey, const std::wstring& name) { auto value = arpKey[name]; return (value && value->GetType() == Registry::Value::Type::DWord && value->GetValue()); } std::string ARPHelper::GetStringValue(const Registry::Key& arpKey, const std::wstring& name) { auto value = arpKey[name]; if (value && value->GetType() == Registry::Value::Type::String) { return value->GetValue(); } return {}; } std::string ARPHelper::DetermineVersion(const Registry::Key& arpKey) const { // First check DisplayVersion for a complete version string auto displayVersion = arpKey[DisplayVersion]; if (displayVersion && displayVersion->GetType() == Registry::Value::Type::String) { std::string result = displayVersion->GetValue(); if (!result.empty()) { return result; } } // Next attempt VersionMajor.VersionMinor, then MajorVersion.MinorVersion for (const auto& names : { std::make_pair(std::ref(VersionMajor), std::ref(VersionMinor)), std::make_pair(std::ref(MajorVersion), std::ref(MinorVersion)) }) { auto majorVersion = arpKey[names.first]; auto minorVersion = arpKey[names.second]; if (majorVersion || minorVersion) { uint32_t majorVersionInt = 0; uint32_t minorVersionInt = 0; if (majorVersion && majorVersion->GetType() == Registry::Value::Type::DWord) { majorVersionInt = majorVersion->GetValue(); } if (minorVersion && minorVersion->GetType() == Registry::Value::Type::DWord) { minorVersionInt = minorVersion->GetValue(); } if (majorVersionInt || minorVersionInt) { std::ostringstream strstr; strstr << majorVersionInt << '.' << minorVersionInt; return strstr.str(); } } } // Finally attempt to turn the Version DWORD into a version string auto version = arpKey[Version]; if (version && version->GetType() == Registry::Value::Type::DWord) { uint32_t versionInt = version->GetValue(); if (versionInt) { std::ostringstream strstr; strstr << ((versionInt & 0xFF000000) >> 24) << '.' << ((versionInt & 0x00FF0000) >> 16) << '.' << (versionInt & 0x0000FFFF); return strstr.str(); } } return Utility::Version::CreateUnknown().ToString(); } void ARPHelper::AddMetadataIfPresent(const Registry::Key& key, const std::wstring& name, SQLiteIndex& index, SQLiteIndex::IdType manifestId, PackageVersionMetadata metadata) const { auto value = key[name]; if (value) { std::string valueString; if (value->GetType() == Registry::Value::Type::String) { valueString = value->GetValue(); } else if (value->GetType() == Registry::Value::Type::ExpandString) { valueString = value->GetValue(); } else if (value->GetType() == Registry::Value::Type::DWord) { DWORD dwordValue = value->GetValue(); if (name == Language) { valueString = Locale::LocaleIdToBcp47Tag(dwordValue); } else { std::ostringstream strstr; strstr << dwordValue; valueString = strstr.str(); } } if (!valueString.empty()) { index.SetMetadataByManifestId(manifestId, metadata, valueString); } } } void ARPHelper::PopulateIndexFromARP(SQLiteIndex& index, Manifest::ScopeEnum scope) const { auto upgradeCodes = GetUpgradeCodes(); for (auto architecture : Utility::GetApplicableArchitectures()) { Registry::Key arpRootKey = GetARPKey(scope, architecture); if (arpRootKey) { PopulateIndexFromKey(index, arpRootKey, Manifest::ScopeToString(scope), Utility::ToString(architecture), upgradeCodes); } } } void ARPHelper::PopulateIndexFromKey(SQLiteIndex& index, const Registry::Key& key, std::string_view scope, std::string_view architecture, const std::map& upgradeCodes) const { AICLI_LOG(Repo, Verbose, << "Examining ARP entries for " << scope << " | " << architecture); for (const auto& arpEntry : key) { std::string productCode; try { productCode = arpEntry.Name(); Manifest::Manifest manifest; manifest.DefaultLocalization.Add({ "ARP" }); // Construct a unique name for this entry const char separator = '\\'; std::ostringstream stream; stream << "ARP" << separator << scope << separator << architecture << separator << productCode; manifest.Id = stream.str(); manifest.Installers.emplace_back(); // TODO: This likely needs some cleanup applied, as it looks like INNO tends to append an "_is#" // that might vary across machines/installs. There may be other things we want to clean up as well, // like trimming spaces at the ends, or removing the version string from the product code // if it is present. manifest.Installers[0].ProductCode = productCode; Registry::Key arpKey = arpEntry.Open(); // Ignore entries that are listed as SystemComponent if (GetBoolValue(arpKey, SystemComponent)) { AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because it is a SystemComponent"); continue; } // If no name is provided, ignore this entry auto displayName = arpKey[DisplayName]; if (!displayName || displayName->GetType() != Registry::Value::Type::String) { AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because DisplayName is not a REG_SZ value"); continue; } auto displayNameValue = displayName->GetValue(); if (displayNameValue.empty()) { AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because DisplayName is empty"); continue; } manifest.DefaultLocalization.Add(displayNameValue); // Add DisplayName to ARP entries too // This is to help normalized publisher and name correlation where ARP DisplayName matching // will be getting improved in future iterations. manifest.Installers[0].AppsAndFeaturesEntries.emplace_back(); manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayName = displayNameValue; // If no version can be determined, ignore this entry manifest.Version = DetermineVersion(arpKey); if (manifest.Version.empty()) { AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because a version could not be determined"); continue; } auto publisher = arpKey[Publisher]; if (publisher && publisher->GetType() == Registry::Value::Type::String) { manifest.DefaultLocalization.Add(publisher->GetValue()); // If Publisher is set, change the Id using name normalization // TODO: Figure out how to actually make this work since there are often instances of the same // data in x64 and x86 entries that will collide. //auto normalizedName = index.NormalizeName( // manifest.DefaultLocalization.Get(), // manifest.DefaultLocalization.Get()); //manifest.Id = normalizedName.Publisher() + '.' + normalizedName.Name(); } // Pick up WindowsInstaller to determine if this is an MSI install. // TODO: Could also determine Inno (and maybe other types) through detecting other keys here. auto installedType = Manifest::InstallerTypeEnum::Exe; if (GetBoolValue(arpKey, WindowsInstaller)) { installedType = Manifest::InstallerTypeEnum::Msi; // If this is an MSI, look up the UpgradeCode auto upgradeCodeItr = upgradeCodes.find(productCode); if (upgradeCodeItr != upgradeCodes.end()) { manifest.Installers[0].AppsAndFeaturesEntries[0].UpgradeCode = upgradeCodeItr->second; } } // TODO: If we want to keep the constructed manifest around to allow for `show` type commands // against installed packages, we should use URLInfoAbout/HelpLink for the Homepage. // TODO: Determine the best way to handle duplicates; sometimes the same package will be listed under // both x64 and x86 locations for ARP. // For now, we will attempt to insert and catch. std::optional manifestIdOpt; try { // Use the ProductCode as a unique key for the path manifestIdOpt = index.AddManifest(manifest); } catch (...) { // Ignore errors if they occur, they are most likely a duplicate value } if (!manifestIdOpt) { AICLI_LOG(Repo, Warning, << "Ignoring duplicate ARP entry " << scope << '|' << architecture << '|' << productCode << " [" << manifest.DefaultLocalization.Get() << "]"); continue; } SQLiteIndex::IdType manifestId = manifestIdOpt.value(); // Pass scope along to metadata. index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledScope, scope); // TODO: Pass along architecture, although there are cases where it is not clear what architecture the package // is from it's ARP location, despite it very clearly being a specific architecture. And note that user // scope does not have separate ARP locations, so every architecture would appear as native. // Publisher is needed for certain scenarios but we don't store it from the manifest if (manifest.DefaultLocalization.Contains(Manifest::Localization::Publisher)) { index.SetMetadataByManifestId( manifestId, PackageVersionMetadata::Publisher, manifest.DefaultLocalization.Get()); } // Pick up InstallLocation when upgrade supports remove/install to enable this location // to survive across the removal. AddMetadataIfPresent(arpKey, InstallLocation, index, manifestId, PackageVersionMetadata::InstalledLocation); // Pick up UninstallString and QuietUninstallString for uninstall. AddMetadataIfPresent(arpKey, UninstallString, index, manifestId, PackageVersionMetadata::StandardUninstallCommand); AddMetadataIfPresent(arpKey, QuietUninstallString, index, manifestId, PackageVersionMetadata::SilentUninstallCommand); // Pick up ModifyPath for repair. AddMetadataIfPresent(arpKey, ModifyPath, index, manifestId, PackageVersionMetadata::StandardModifyCommand); AddMetadataIfPresent(arpKey, NoModify, index, manifestId, PackageVersionMetadata::NoModify); AddMetadataIfPresent(arpKey, NoRepair, index, manifestId, PackageVersionMetadata::NoRepair); // Pick up Language to enable proper selection of language for upgrade. AddMetadataIfPresent(arpKey, Language, index, manifestId, PackageVersionMetadata::InstalledLocale); if (Manifest::ConvertToInstallerTypeEnum(GetStringValue(arpKey, std::wstring{ ToString(PortableValueName::WinGetInstallerType) })) == Manifest::InstallerTypeEnum::Portable) { // Portable uninstall requires the installed architecture for locating the entry in the registry. index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledArchitecture, architecture); installedType = Manifest::InstallerTypeEnum::Portable; } index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledType, Manifest::InstallerTypeToString(installedType)); } catch (...) { AICLI_LOG(Repo, Warning, << "Failed to read ARP entry, ignoring it: " << scope << '|' << architecture << '|' << productCode); LOG_CAUGHT_EXCEPTION(); } } } std::vector ARPHelper::CreateRegistryWatchers(Manifest::ScopeEnum scope, std::function callback) { std::vector result; auto addToResult = [&](Manifest::ScopeEnum scopeToUse) { for (auto architecture : Utility::GetApplicableArchitectures()) { Registry::Key arpRootKey = GetARPKey(scopeToUse, architecture); if (arpRootKey) { result.emplace_back(wil::make_registry_watcher(arpRootKey, L"", true, [scopeToUse, architecture, callback](wil::RegistryChangeKind change) { callback(scopeToUse, architecture, change); })); } } }; if (scope == Manifest::ScopeEnum::Unknown) { addToResult(Manifest::ScopeEnum::User); addToResult(Manifest::ScopeEnum::Machine); } else { addToResult(scope); } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/ARPHelper.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/SQLiteIndex.h" #include #include #include #include #include #include #include namespace AppInstaller::Repository::Microsoft { // A helper to find the various locations that contain ARP (Add/Remove Programs) entries. struct ARPHelper { // See https://docs.microsoft.com/en-us/windows/win32/msi/uninstall-registry-key for details. const std::wstring SubKeyPath{ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" }; // REG_SZ const std::wstring DisplayName{ L"DisplayName" }; // REG_SZ const std::wstring Publisher{ L"Publisher" }; // REG_SZ const std::wstring DisplayVersion{ L"DisplayVersion" }; // REG_DWORD (ex. 0xMMmmbbbb, M[ajor], m[inor], b[uild]) const std::wstring Version{ L"Version" }; // REG_DWORD const std::wstring VersionMajor{ L"VersionMajor" }; // REG_DWORD const std::wstring VersionMinor{ L"VersionMinor" }; // REG_DWORD const std::wstring MajorVersion{ L"MajorVersion" }; // REG_DWORD const std::wstring MinorVersion{ L"MinorVersion" }; // REG_SZ const std::wstring URLInfoAbout{ L"URLInfoAbout" }; // REG_SZ const std::wstring HelpLink{ L"HelpLink" }; // REG_SZ const std::wstring InstallLocation{ L"InstallLocation" }; // REG_DWORD (ex. 1033 [en-us]) const std::wstring Language{ L"Language" }; // REG_SZ (ex. "english") const std::wstring InnoSetupLanguage{ L"Inno Setup: Language" }; // REG_EXPAND_SZ const std::wstring UninstallString{ L"UninstallString" }; // REG_EXPAND_SZ const std::wstring QuietUninstallString{ L"QuietUninstallString" }; // REG_DWORD (bool, true indicates MSI) const std::wstring WindowsInstaller{ L"WindowsInstaller" }; // REG_DWORD (bool) const std::wstring SystemComponent{ L"SystemComponent" }; // REG_SZ const std::wstring DisplayIcon{ L"DisplayIcon" }; // REG_DWORD const std::wstring NoModify{ L"NoModify" }; // REG_DWORD const std::wstring NoRepair{ L"NoRepair" }; // REG_SZ const std::wstring ModifyPath{ L"ModifyPath" }; // Gets the registry key associated with the given scope and architecture on this platform. // May return an empty key if there is no valid location (bad combination or not found). Registry::Key GetARPKey(Manifest::ScopeEnum scope, Utility::Architecture architecture) const; // Gets the arp registry key associated with the given scope and product code. // May return an empty key if not found. Registry::Key FindARPEntry(const std::string& productCode, AppInstaller::Manifest::ScopeEnum scope = AppInstaller::Manifest::ScopeEnum::Unknown) const; // Returns true IFF the value exists and contains a non-zero DWORD. static bool GetBoolValue(const Registry::Key& arpKey, const std::wstring& name); // Returns the string value if it exists. static std::string GetStringValue(const Registry::Key& arpKey, const std::wstring& name); // Determines the version from an ARP entry. // The priority is: // DisplayVersion // Version // MajorVersion, MinorVersion std::string DetermineVersion(const Registry::Key& arpKey) const; // Reads a value and adds it to the metadata if it exists. void AddMetadataIfPresent(const Registry::Key& key, const std::wstring& name, SQLiteIndex& index, SQLiteIndex::IdType manifestId, PackageVersionMetadata metadata) const; // Populates the index with the ARP entries from the given scope (machine/user). // Handles all of the architectures for the given scope. void PopulateIndexFromARP(SQLiteIndex& index, Manifest::ScopeEnum scope) const; // Populates the index with the ARP entries from the given key. // This entry point is primarily to allow unit tests to operate of arbitrary keys; // product code should use PopulateIndexFromARP. void PopulateIndexFromKey(SQLiteIndex& index, const Registry::Key& key, std::string_view scope, std::string_view architecture, const std::map& upgradeCodes = {}) const; // Creates registry watchers for the given scope std::vector CreateRegistryWatchers(Manifest::ScopeEnum scope, std::function callback); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/CheckpointDatabase.h" #include "Microsoft/Schema/ICheckpointDatabase.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h" namespace AppInstaller::Repository::Microsoft { namespace { // Creates the ICheckpointDatabase interface object for the given version. std::unique_ptr CreateICheckpointDatabase(const SQLite::Version& version) { if (version == SQLite::Version{ 1, 0 } || version.MajorVersion == 1 || version.IsLatest()) { return std::make_unique(); } THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } CheckpointDatabase::CheckpointDatabase(CheckpointDatabase&&) = default; CheckpointDatabase& CheckpointDatabase::operator=(CheckpointDatabase&&) = default; std::shared_ptr CheckpointDatabase::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Checkpoint database with version [" << version << "] at '" << filePath << "'"); CheckpointDatabase result{ filePath, version }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "CheckpointDatabase_CreateNew"); // Use calculated version, as incoming version could be 'latest' result.m_version.SetSchemaVersion(result.m_dbconn); result.m_interface->CreateTables(result.m_dbconn); result.SetLastWriteTime(); savepoint.Commit(); return std::make_shared(std::move(result)); } std::shared_ptr CheckpointDatabase::Open(const std::string& filePath, SQLite::SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) { return std::make_shared(CheckpointDatabase{ filePath, disposition, std::move(indexFile) }); } bool CheckpointDatabase::IsEmpty() { return m_interface->IsEmpty(m_dbconn); } CheckpointDatabase::IdType CheckpointDatabase::AddCheckpoint(std::string_view checkpointName) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Adding checkpoint [" << checkpointName << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "CheckpointDatabase_addCheckpoint"); IdType result = m_interface->AddCheckpoint(m_dbconn, checkpointName); SetLastWriteTime(); savepoint.Commit(); return result; } std::vector CheckpointDatabase::GetCheckpointIds() { return m_interface->GetCheckpointIds(m_dbconn); } bool CheckpointDatabase::HasDataField(IdType checkpointId, int type, const std::string& name) { return m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, type, name).has_value(); } std::vector CheckpointDatabase::GetDataTypes(IdType checkpointId) { return m_interface->GetCheckpointDataTypes(m_dbconn, checkpointId); } std::vector CheckpointDatabase::GetDataFieldNames(IdType checkpointId, int dataType) { return m_interface->GetCheckpointDataFields(m_dbconn, checkpointId, dataType); } void CheckpointDatabase::SetDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector& values) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Setting checkpoint data [" << dataType << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "CheckpointDatabase_setDataValue"); m_interface->SetCheckpointDataValues(m_dbconn, checkpointId, dataType, field, values); SetLastWriteTime(); savepoint.Commit(); } void CheckpointDatabase::UpdateDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector& values) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Updating checkpoint data [" << dataType << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "CheckpointDatabase_updateDataValue"); m_interface->RemoveCheckpointDataType(m_dbconn, checkpointId, dataType); m_interface->SetCheckpointDataValues(m_dbconn, checkpointId, dataType, field, values); SetLastWriteTime(); savepoint.Commit(); } void CheckpointDatabase::RemoveDataType(IdType checkpointId, int dataType) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Removing checkpoint data [" << dataType << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "CheckpointDatabase_removeDataValue"); m_interface->RemoveCheckpointDataType(m_dbconn, checkpointId, dataType); SetLastWriteTime(); savepoint.Commit(); } std::string CheckpointDatabase::GetDataFieldSingleValue(IdType checkpointId, int dataType, const std::string& field) { const auto& values = m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); if (!values.has_value()) { THROW_HR(E_UNEXPECTED); } return values.value()[0]; } std::vector CheckpointDatabase::GetDataFieldMultiValue(IdType checkpointId, int dataType, const std::string& field) { const auto& values = m_interface->GetCheckpointDataFieldValues(m_dbconn, checkpointId, dataType, field); if (!values.has_value()) { THROW_HR(E_UNEXPECTED); } return values.value(); } CheckpointDatabase::CheckpointDatabase(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : SQLiteStorageBase(target, disposition, std::move(indexFile)) { AICLI_LOG(Repo, Info, << "Opened Checkpoint Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); m_interface = CreateICheckpointDatabase(m_version); THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); } CheckpointDatabase::CheckpointDatabase(const std::string& target, SQLite::Version version) : SQLiteStorageBase(target, version) { m_interface = CreateICheckpointDatabase(m_version); m_version = m_interface->GetVersion(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/ConfigurableTestSourceFactory.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/ConfigurableTestSourceFactory.h" #include using namespace std::string_literals; using namespace std::string_view_literals; using namespace AppInstaller::Utility::literals; namespace AppInstaller::Repository::Microsoft { namespace { // The configuration defined for a source. // This can be added to as new scenarios are needed for testing. struct TestSourceConfiguration { TestSourceConfiguration(const std::string& config) { Json::Value root; Json::CharReaderBuilder builder; const std::unique_ptr reader(builder.newCharReader()); std::string error; if (reader->parse(config.c_str(), config.c_str() + config.size(), &root, &error)) { // TODO: If this becomes more dynamic, refactor the UserSettings code to make it easier to leverage here ParseHR(root, ".OpenHR", OpenHR); ParseHR(root, ".SearchHR", SearchHR); ParseBool(root, ".ContainsPackage", ContainsPackage); } else { AICLI_LOG(Repo, Error, << "Error parsing test source config: " << error); THROW_HR_MSG(E_INVALIDARG, "%hs", error.c_str()); } } // The HR to throw on Factory::Create (if FAILED) HRESULT OpenHR = S_OK; // The HR to throw on Source::Search (if FAILED) HRESULT SearchHR = S_OK; // If a result should be returned by search. bool ContainsPackage = false; private: static void ParseHR(const Json::Value& root, const std::string& path, HRESULT& hr) { const Json::Path jsonPath(path); Json::Value node = jsonPath.resolve(root); if (!node.isNull()) { if (node.isIntegral()) { hr = node.asInt(); } else if (node.isString()) { hr = static_cast(std::strtoll(node.asString().c_str(), nullptr, 0)); } } } static void ParseBool(const Json::Value& root, const std::string& path, bool& value) { const Json::Path jsonPath(path); Json::Value node = jsonPath.resolve(root); if (!node.isNull()) { if (node.isBool()) { value = node.asBool(); } } } }; // A test package that contains test data. struct TestPackage : public std::enable_shared_from_this, public ICompositePackage, public IPackage, public IPackageVersion { TestPackage(std::shared_ptr source) : m_source(std::move(source)) {} Utility::LocIndString GetProperty(PackageProperty property) const override { switch (property) { case PackageProperty::Id: return "ConfigurableTestSource Package Identifier"_lis; case PackageProperty::Name: return "ConfigurableTestSource Package Name"_lis; } return {}; } std::shared_ptr GetInstalled() override { return nullptr; } std::vector> GetAvailable() override { return { shared_from_this() }; } std::vector GetMultiProperty(PackageMultiProperty) const override { return {}; } Source GetSource() const override { return { m_source }; } bool IsSame(const IPackage*) const override { return false; } const void* CastTo(IPackageType) const override { return nullptr; } std::vector GetVersionKeys() const override { return { {} }; } std::shared_ptr GetVersion(const PackageVersionKey&) const override { return std::static_pointer_cast(NonConstSharedFromThis()); } std::shared_ptr GetLatestVersion() const override { return std::static_pointer_cast(NonConstSharedFromThis()); } Utility::LocIndString GetProperty(PackageVersionProperty property) const override { switch (property) { case PackageVersionProperty::Id: return "ConfigurableTestSource Package Version Identifier"_lis; case PackageVersionProperty::Name: return "ConfigurableTestSource Package Version Name"_lis; case PackageVersionProperty::SourceIdentifier: return "ConfigurableTestSource Package Version Source Identifier"_lis; case PackageVersionProperty::SourceName: return "ConfigurableTestSource Package Version Source Name"_lis; case PackageVersionProperty::Version: return "ConfigurableTestSource Package Version Version"_lis; case PackageVersionProperty::Channel: return "ConfigurableTestSource Package Version Channel"_lis; case PackageVersionProperty::RelativePath: return "ConfigurableTestSource Package Version Relative Path"_lis; case PackageVersionProperty::ManifestSHA256Hash: return "ConfigurableTestSource Package Version Manifest SHA 256 Hash"_lis; case PackageVersionProperty::Publisher: return "ConfigurableTestSource Package Version Publisher"_lis; case PackageVersionProperty::ArpMinVersion: return "ConfigurableTestSource Package Version Arp Min Version"_lis; case PackageVersionProperty::ArpMaxVersion: return "ConfigurableTestSource Package Version Arp Max Version"_lis; case PackageVersionProperty::Moniker: return "ConfigurableTestSource Package Version Moniker"_lis; } return {}; } std::vector GetMultiProperty(PackageVersionMultiProperty) const override { return {}; } Manifest::Manifest GetManifest() override { Manifest::Manifest result; result.Id = "ConfigurableTestSource Manifest Identifier"; result.CurrentLocalization.Add("ConfigurableTestSource Manifest Name"); return result; } Metadata GetMetadata() const override { return {}; } private: std::shared_ptr NonConstSharedFromThis() const { return const_cast(this)->shared_from_this(); } std::shared_ptr m_source; }; // The configurable source itself. struct ConfigurableTestSource : public std::enable_shared_from_this, public ISource { static constexpr ISourceType SourceType = ISourceType::ConfigurableTestSource; ConfigurableTestSource(const SourceDetails& details, const TestSourceConfiguration& config) : m_details(details), m_config(config) {} const SourceDetails& GetDetails() const override { return m_details; } const std::string& GetIdentifier() const override { return m_details.Identifier; } SearchResult Search(const SearchRequest&) const override { THROW_IF_FAILED(m_config.SearchHR); SearchResult result; if (m_config.ContainsPackage) { std::shared_ptr package = std::make_shared(NonConstSharedFromThis()); PackageMatchFilter packageFilter{ {}, {} }; result.Matches.emplace_back(std::move(package), std::move(packageFilter)); } return result; } void* CastTo(ISourceType type) override { if (type == SourceType) { return this; } return nullptr; } private: std::shared_ptr NonConstSharedFromThis() const { return const_cast(this)->shared_from_this(); } SourceDetails m_details; TestSourceConfiguration m_config; }; struct ConfigurableTestSourceReference : public ISourceReference { ConfigurableTestSourceReference(const SourceDetails& details) : m_details(details) { m_details.Identifier = "*ConfigurableTestSource"; } std::string GetIdentifier() override { return m_details.Identifier; } SourceDetails& GetDetails() override { return m_details; }; bool SetCustomHeader(std::optional) override { return true; } std::shared_ptr Open(IProgressCallback&) override { // enables `source add` with FAILED(OpenHR) TestSourceConfiguration config{ m_details.Arg }; THROW_IF_FAILED(config.OpenHR); return std::make_shared(m_details, config); } private: SourceDetails m_details; }; // The actual factory implementation. struct ConfigurableTestSourceFactoryImpl : public ISourceFactory { std::string_view TypeName() const override final { return ConfigurableTestSourceFactory::Type(); } std::shared_ptr Create(const SourceDetails& details) override final { return std::make_shared(details); } bool Add(SourceDetails& details, IProgressCallback&) override final { // Attempt to parse the configuration so that we can fail at the appropriate point TestSourceConfiguration config{ details.Arg }; return true; } bool Update(const SourceDetails&, IProgressCallback&) override final { return true; } bool BackgroundUpdate(const SourceDetails&, IProgressCallback&) override final { return true; } bool Remove(const SourceDetails&, IProgressCallback&) override final { return true; } }; } std::unique_ptr ConfigurableTestSourceFactory::Create() { return std::make_unique(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/ConfigurableTestSourceFactory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" #include "SourceFactory.h" #include namespace AppInstaller::Repository::Microsoft { using namespace std::string_view_literals; // A source for use in manual or E2E tests that can be configured to fail as needed. struct ConfigurableTestSourceFactory { // Get the type string for this source. static constexpr std::string_view Type() { using namespace std::string_view_literals; return "Microsoft.Test.Configurable"sv; } // Creates a source factory for this type. static std::unique_ptr Create(); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/FontHelper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "FontHelper.h" #include using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Microsoft { #ifndef AICLI_DISABLE_TEST_HOOKS using GetFontRegistryRootFunc = std::function; static GetFontRegistryRootFunc s_FontRegistryRoot_Override; void TestHook_SetGetFontRegistryRootFunc(GetFontRegistryRootFunc value) { s_FontRegistryRoot_Override = value; } #endif void FontHelper::PopulateIndex(SQLiteIndex& index, Manifest::ScopeEnum scope) const { const auto& fontPackages = AppInstaller::Fonts::GetInstalledFontPackages(scope); for (const auto& fontPackage : fontPackages) { try { // Font packages are collections of fonts; the PackageId is treated as the productCode. std::string productCode = ConvertToUTF8(fontPackage.PackageId); Manifest::Manifest manifest; manifest.DefaultLocalization.Add({ "Font" }); manifest.Id = ConvertToUTF8(fontPackage.PackageIdentifier); manifest.Version = ConvertToUTF8(fontPackage.PackageVersion); manifest.Moniker = productCode; manifest.Installers.emplace_back(); manifest.Installers[0].ProductCode = productCode; // We don't know the package name from the install information. The best we can do is reuse the id. manifest.DefaultLocalization.Add(productCode); std::optional manifestIdOpt; try { // Use the ProductCode as a unique key for the path manifestIdOpt = index.AddManifest(manifest); } catch (...) { // Same package may show up under User and Machine install. // Ignore errors if they occur, they are most likely a duplicate value } if (!manifestIdOpt) { AICLI_LOG(Repo, Warning, << "Ignoring duplicate font entry " << scope << '|' << manifest.Id << '|' << manifest.Version); continue; } SQLiteIndex::IdType manifestId = manifestIdOpt.value(); index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledScope, Manifest::ScopeToString(scope)); index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledType, Manifest::InstallerTypeToString(Manifest::InstallerTypeEnum::Font)); } catch (...) { AICLI_LOG(Repo, Warning, << "Failed to process font package entry, ignoring it: " << scope << '|' << ConvertToUTF8(fontPackage.PackageId)); LOG_CAUGHT_EXCEPTION(); } } } void FontHelper::AddRegistryWatchers(Manifest::ScopeEnum scope, std::function callback, std::vector& watchers) { auto addToWatchers = [&](Manifest::ScopeEnum scopeToUse) { auto hive = scopeToUse == Manifest::ScopeEnum::Machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; auto root = Registry::Key::OpenIfExists(hive, AppInstaller::Fonts::GetFontRegistryRoot(), 0UL, KEY_READ); #ifndef AICLI_DISABLE_TEST_HOOKS if (s_FontRegistryRoot_Override) { root = s_FontRegistryRoot_Override(scopeToUse); } #endif if (root) { watchers.emplace_back(wil::make_registry_watcher(root, L"", true, [scopeToUse, callback](wil::RegistryChangeKind change) { callback(scopeToUse, change); })); } }; if (scope == Manifest::ScopeEnum::Unknown) { addToWatchers(Manifest::ScopeEnum::User); addToWatchers(Manifest::ScopeEnum::Machine); } else { addToWatchers(scope); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/FontHelper.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/SQLiteIndex.h" #include #include #include #include #include #include #include namespace AppInstaller::Repository::Microsoft { // A helper to find the various locations that find installed font information. struct FontHelper { void PopulateIndex(SQLiteIndex& index, Manifest::ScopeEnum scope) const; void AddRegistryWatchers(Manifest::ScopeEnum scope, std::function callback, std::vector& watchers); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PinningIndex.h" #include #include "Schema/Pinning_1_0/PinningIndexInterface.h" namespace AppInstaller::Repository::Microsoft { #ifndef AICLI_DISABLE_TEST_HOOKS std::optional s_PinningIndexOverride{}; void TestHook_SetPinningIndex_Override(std::optional&& indexPath) { s_PinningIndexOverride = std::move(indexPath); } #endif namespace { std::filesystem::path GetPinningDatabasePath() { const auto DefaultPath = Runtime::GetPathTo(Runtime::PathName::LocalState) / "pinning.db"; return #ifndef AICLI_DISABLE_TEST_HOOKS s_PinningIndexOverride.has_value() ? s_PinningIndexOverride.value() : #endif DefaultPath; } std::shared_ptr OpenDatabaseIfExists(const std::filesystem::path& path, SQLite::SQLiteStorageBase::OpenDisposition openDisposition) { AICLI_LOG(Repo, Info, << "Attempting to open pinning database: " << path); try { if (std::filesystem::exists(path)) { if (std::filesystem::is_regular_file(path)) { try { AICLI_LOG(Repo, Info, << "... opening existing pinning database"); return std::make_shared(PinningIndex::Open(path.u8string(), openDisposition)); } CATCH_LOG(); AICLI_LOG(Repo, Info, << "... deleting bad pinning database file"); std::filesystem::remove_all(path); } else { AICLI_LOG(Repo, Info, << "... deleting pinning database path that is a directory"); std::filesystem::remove_all(path); } } } CATCH_LOG(); return {}; } } PinningIndex PinningIndex::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Pinning Index with version [" << version << "] at '" << filePath << "'"); PinningIndex result{ filePath, version }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "pinningindex_createnew"); // Use calculated version, as incoming version could be 'latest' result.m_version.SetSchemaVersion(result.m_dbconn); result.m_interface->CreateTables(result.m_dbconn); result.SetLastWriteTime(); savepoint.Commit(); return result; } std::shared_ptr PinningIndex::OpenIfExists(OpenDisposition openDisposition) { return OpenDatabaseIfExists(GetPinningDatabasePath(), openDisposition); } std::shared_ptr PinningIndex::OpenOrCreateDefault(OpenDisposition openDisposition) { const auto databasePath = GetPinningDatabasePath(); std::shared_ptr result = OpenDatabaseIfExists(databasePath, openDisposition); if (!result) { AICLI_LOG(Repo, Info, << "... creating pinning database"); try { result = std::make_shared(PinningIndex::CreateNew(databasePath.u8string())); } CATCH_LOG(); } return result; } PinningIndex::IdType PinningIndex::AddPin(const Pinning::Pin& pin) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Adding Pin " << pin.ToString()); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "pinningindex_addpin"); IdType result = m_interface->AddPin(m_dbconn, pin); SetLastWriteTime(); savepoint.Commit(); return result; } bool PinningIndex::UpdatePin(const Pinning::Pin& pin) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Updating Pin " << pin.ToString()); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "pinningindex_updatepin"); bool result = m_interface->UpdatePin(m_dbconn, pin).first; if (result) { SetLastWriteTime(); savepoint.Commit(); } return result; } void PinningIndex::AddOrUpdatePin(const Pinning::Pin& pin) { auto existingPin = GetPin(pin.GetKey()); if (existingPin.has_value()) { UpdatePin(pin); } else { AddPin(pin); } } void PinningIndex::RemovePin(const Pinning::PinKey& pinKey) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Removing Pin " << pinKey.ToString()); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "pinningIndex_removePin"); m_interface->RemovePin(m_dbconn, pinKey); SetLastWriteTime(); savepoint.Commit(); } std::optional PinningIndex::GetPin(const Pinning::PinKey& pinKey) { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->GetPin(m_dbconn, pinKey); } std::vector PinningIndex::GetAllPins() { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->GetAllPins(m_dbconn); } bool PinningIndex::ResetAllPins(std::string_view sourceId) { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->ResetAllPins(m_dbconn, sourceId); } std::unique_ptr PinningIndex::CreateIPinningIndex() const { if (m_version == SQLite::Version{ 1, 0 } || m_version.MajorVersion == 1 || m_version.IsLatest()) { return std::make_unique(); } THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } PinningIndex::PinningIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : SQLiteStorageBase(target, disposition, std::move(indexFile)) { AICLI_LOG(Repo, Info, << "Opened Pinning Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); m_interface = CreateIPinningIndex(); THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); } PinningIndex::PinningIndex(const std::string& target, SQLite::Version version) : SQLiteStorageBase(target, version) { m_interface = CreateIPinningIndex(); m_version = m_interface->GetVersion(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PinningIndex.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Microsoft/Schema/IPinningIndex.h" #include #include #include namespace AppInstaller::Repository::Microsoft { struct PinningIndex : SQLite::SQLiteStorageBase { // An id that refers to a specific Pinning file. using IdType = SQLite::rowid_t; PinningIndex(const PinningIndex&) = delete; PinningIndex& operator=(const PinningIndex&) = delete; PinningIndex(PinningIndex&&) = default; PinningIndex& operator=(PinningIndex&&) = default; // Creates a new PinningIndex database of the given version. static PinningIndex CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); // Opens an existing PinningIndex database. static PinningIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) { return { filePath, disposition, std::move(indexFile) }; } // Opens the PinningIndex database on the default path if it exists. // Returns nullptr in case of error. static std::shared_ptr OpenIfExists(OpenDisposition openDisposition = OpenDisposition::Read); // Opens or creates a PinningIndex database on the default path. // openDisposition is only used when opening an existing database. // Returns nullptr in case of error. static std::shared_ptr OpenOrCreateDefault(OpenDisposition openDisposition = OpenDisposition::ReadWrite); // Adds a pin to the index. IdType AddPin(const Pinning::Pin& pin); // Updates a pin type, and gated version if needed. // Return value indicates whether there were any changes. bool UpdatePin(const Pinning::Pin& pin); // Adds a pin or updates it if it already exists. void AddOrUpdatePin(const Pinning::Pin& pin); // Removes a pin from the index. void RemovePin(const Pinning::PinKey& pinKey); // Returns the current pin for a given package if it exists. std::optional GetPin(const Pinning::PinKey& pinKey); // Returns a vector containing all the existing pins. std::vector GetAllPins(); // Deletes all pins from a given source, or from all sources if none is specified bool ResetAllPins(std::string_view sourceId = {}); private: // Constructor used to open an existing index. PinningIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); // Constructor used to create a new index. PinningIndex(const std::string& target, SQLite::Version version); // Creates the IPinningIndex interface object for this version. std::unique_ptr CreateIPinningIndex() const; std::unique_ptr m_interface; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PortableIndex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/PortableIndex.h" #include "Microsoft/Schema/IPortableIndex.h" #include "Microsoft/Schema/Portable_1_0/PortableTable.h" #include #include "Schema/Portable_1_0/PortableIndexInterface.h" #include namespace AppInstaller::Repository::Microsoft { PortableIndex::PortableIndex(PortableIndex&&) = default; PortableIndex& PortableIndex::operator=(PortableIndex&&) = default; PortableIndex::~PortableIndex() = default; PortableIndex PortableIndex::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Portable Index with version [" << version << "] at '" << filePath << "'"); PortableIndex result{ filePath, version }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "portableindex_createnew"); // Use calculated version, as incoming version could be 'latest' result.m_version.SetSchemaVersion(result.m_dbconn); result.m_interface->CreateTable(result.m_dbconn); const auto& filePathUTF16 = Utility::ConvertToUTF16(filePath); SetFileAttributes(filePathUTF16.c_str(), GetFileAttributes(filePathUTF16.c_str()) | FILE_ATTRIBUTE_HIDDEN); result.SetLastWriteTime(); savepoint.Commit(); return result; } PortableIndex PortableIndex::Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile) { return { filePath, disposition, std::move(indexFile) }; } PortableIndex::IdType PortableIndex::AddPortableFile(const Portable::PortableFileEntry& file) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Adding portable file for [" << file.GetFilePath() << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "portableindex_addfile"); IdType result = m_interface->AddPortableFile(m_dbconn, file); SetLastWriteTime(); savepoint.Commit(); return result; } void PortableIndex::RemovePortableFile(const Portable::PortableFileEntry& file) { AICLI_LOG(Repo, Verbose, << "Removing portable file [" << file.GetFilePath() << "]"); std::lock_guard lockInterface{ *m_interfaceLock }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "portableindex_removefile"); m_interface->RemovePortableFile(m_dbconn, file); SetLastWriteTime(); savepoint.Commit(); } bool PortableIndex::UpdatePortableFile(const Portable::PortableFileEntry& file) { AICLI_LOG(Repo, Verbose, << "Updating portable file [" << file.GetFilePath() << "]"); std::lock_guard lockInterface{ *m_interfaceLock }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "portableindex_updatefile"); bool result = m_interface->UpdatePortableFile(m_dbconn, file).first; if (result) { SetLastWriteTime(); savepoint.Commit(); } return result; } bool PortableIndex::Exists(const Portable::PortableFileEntry& file) { AICLI_LOG(Repo, Verbose, << "Checking if portable file exists [" << file.GetFilePath() << "]"); return m_interface->Exists(m_dbconn, file); } bool PortableIndex::IsEmpty() { return m_interface->IsEmpty(m_dbconn); } void PortableIndex::AddOrUpdatePortableFile(const Portable::PortableFileEntry& file) { if (Exists(file)) { UpdatePortableFile(file); } else { AddPortableFile(file); } } std::vector PortableIndex::GetAllPortableFiles() { return m_interface->GetAllPortableFiles(m_dbconn); } std::unique_ptr PortableIndex::CreateIPortableIndex() const { if (m_version == SQLite::Version{ 1, 0 } || m_version.MajorVersion == 1 || m_version.IsLatest()) { return std::make_unique(); } THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } PortableIndex::PortableIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : SQLiteStorageBase(target, disposition, std::move(indexFile)) { AICLI_LOG(Repo, Info, << "Opened Portable Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); m_interface = CreateIPortableIndex(); THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); } PortableIndex::PortableIndex(const std::string& target, SQLite::Version version) : SQLiteStorageBase(target, version) { m_interface = CreateIPortableIndex(); m_version = m_interface->GetVersion(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/PreIndexedPackageSourceFactory.h" #include "Microsoft/SQLiteIndex.h" #include "Microsoft/SQLiteIndexSource.h" #include "SourceUpdateChecks.h" #include #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; namespace AppInstaller::Repository::Microsoft { namespace { static constexpr std::string_view s_PreIndexedPackageSourceFactory_PackageFileName = "source.msix"sv; static constexpr std::string_view s_PreIndexedPackageSourceFactory_V2_PackageFileName = "source2.msix"sv; static constexpr std::string_view s_PreIndexedPackageSourceFactory_PackageVersionHeader = "x-ms-meta-sourceversion"sv; static constexpr std::string_view s_PreIndexedPackageSourceFactory_IndexFileName = "index.db"sv; // TODO: This being hard coded to force using the Public directory name is not ideal. static constexpr std::string_view s_PreIndexedPackageSourceFactory_IndexFilePath = "Public\\index.db"sv; // Construct the package location from the given details. // Currently expects that the arg is an https uri pointing to the root of the data. std::string GetPackageLocation(const std::string& basePath, std::string_view fileName) { std::string result = basePath; if (result.back() != '/') { result += '/'; } result += fileName; return result; } // Gets the set of package locations that should be tried, in order. std::vector GetPackageLocations(const SourceDetails& details) { THROW_HR_IF(E_INVALIDARG, details.Arg.empty()); std::vector result; result.emplace_back(GetPackageLocation(details.Arg, s_PreIndexedPackageSourceFactory_V2_PackageFileName)); result.emplace_back(GetPackageLocation(details.Arg, s_PreIndexedPackageSourceFactory_PackageFileName)); if (!details.AlternateArg.empty()) { result.emplace_back(GetPackageLocation(details.AlternateArg, s_PreIndexedPackageSourceFactory_V2_PackageFileName)); result.emplace_back(GetPackageLocation(details.AlternateArg, s_PreIndexedPackageSourceFactory_PackageFileName)); } return result; } // Abstracts the fallback for package location when the MsixInfo is needed. struct PreIndexedPackageInfo { template PreIndexedPackageInfo(const SourceDetails& details, LocationCheck&& locationCheck) { std::vector potentialLocations = GetPackageLocations(details); for (const auto& location : potentialLocations) { locationCheck(location); } std::exception_ptr primaryException; for (const auto& location : potentialLocations) { try { m_msixInfo = std::make_unique(location); m_packageLocation = location; return; } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("PreIndexedPackageInfo failed on location: %hs", location.c_str()); if (!primaryException) { primaryException = std::current_exception(); } } } std::rethrow_exception(primaryException); } const std::string& PackageLocation() const { return m_packageLocation; } Msix::MsixInfo& MsixInfo() { return *m_msixInfo; } private: std::string m_packageLocation; std::unique_ptr m_msixInfo; }; // Abstracts the fallback for package location when an update is being done. struct PreIndexedPackageUpdateCheck { PreIndexedPackageUpdateCheck(const SourceDetails& details) { std::vector potentialLocations = GetPackageLocations(details); std::exception_ptr primaryException; for (const auto& location : potentialLocations) { try { m_availableVersion = GetAvailableVersionFrom(location); m_packageLocation = location; return; } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("PreIndexedPackageUpdateCheck failed on location: %hs", location.c_str()); if (!primaryException) { primaryException = std::current_exception(); } } } std::rethrow_exception(primaryException); } const std::string& PackageLocation() const { return m_packageLocation; } const Msix::PackageVersion& AvailableVersion() const { return m_availableVersion; } private: std::string m_packageLocation; Msix::PackageVersion m_availableVersion; Msix::PackageVersion GetAvailableVersionFrom(const std::string& packageLocation) { if (Utility::IsUrlRemote(packageLocation)) { std::map headers = Utility::GetHeaders(packageLocation); auto itr = headers.find(std::string{ s_PreIndexedPackageSourceFactory_PackageVersionHeader }); if (itr != headers.end()) { AICLI_LOG(Repo, Verbose, << "Header indicates version is: " << itr->second); return { itr->second }; } // We did not find the header we were looking for, log the ones we did find AICLI_LOG(Repo, Verbose, << "Did not find " << s_PreIndexedPackageSourceFactory_PackageVersionHeader << " in:\n" << [&]() { std::ostringstream headerLog; for (const auto& header : headers) { headerLog << " " << header.first << " : " << header.second << '\n'; } return std::move(headerLog).str(); }()); } AICLI_LOG(Repo, Verbose, << "Reading package data to determine version"); Msix::MsixInfo info{ packageLocation }; auto manifest = info.GetAppPackageManifests(); THROW_HR_IF(APPINSTALLER_CLI_ERROR_PACKAGE_IS_BUNDLE, manifest.size() > 1); THROW_HR_IF(E_UNEXPECTED, manifest.size() == 0); return manifest[0].GetIdentity().GetVersion(); } }; // Gets the package family name from the details. std::string GetPackageFamilyNameFromDetails(const SourceDetails& details) { THROW_HR_IF(E_UNEXPECTED, details.Data.empty()); return details.Data; } // Creates a name for the cross process reader-writer lock given the details. std::string CreateNameForCPL(const SourceDetails& details) { // The only relevant data is the package family name return "PreIndexedSourceCPL_"s + GetPackageFamilyNameFromDetails(details); } // The base class for a package that comes from a preindexed packaged source. struct PreIndexedFactoryBase : public ISourceFactory { std::string_view TypeName() const override final { return PreIndexedPackageSourceFactory::Type(); } std::shared_ptr Create(const SourceDetails& details) override final { // With more than one source implementation, we will probably need to probe first THROW_HR_IF(E_INVALIDARG, !details.Type.empty() && details.Type != PreIndexedPackageSourceFactory::Type()); return CreateInternal(details); } virtual std::shared_ptr CreateInternal(const SourceDetails& details) = 0; bool Add(SourceDetails& details, IProgressCallback& progress) override final { if (details.Type.empty()) { // With more than one source implementation, we will probably need to probe first details.Type = PreIndexedPackageSourceFactory::Type(); AICLI_LOG(Repo, Info, << "Initializing source type: " << details.Name << " => " << details.Type); } else { THROW_HR_IF(E_INVALIDARG, details.Type != PreIndexedPackageSourceFactory::Type()); } PreIndexedPackageInfo packageInfo(details, [](const std::string& packageLocation) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE, Utility::IsUrlRemote(packageLocation) && !Utility::IsUrlSecure(packageLocation)); }); AICLI_LOG(Repo, Info, << "Initializing source from: " << details.Name << " => " << packageInfo.PackageLocation()); THROW_HR_IF(APPINSTALLER_CLI_ERROR_PACKAGE_IS_BUNDLE, packageInfo.MsixInfo().GetIsBundle()); auto fullName = packageInfo.MsixInfo().GetPackageFullName(); AICLI_LOG(Repo, Info, << "Found package full name: " << details.Name << " => " << fullName); details.Data = Msix::GetPackageFamilyNameFromFullName(fullName); details.Identifier = Msix::GetPackageFamilyNameFromFullName(fullName); auto lock = LockExclusive(details, progress); if (!lock) { return false; } return UpdateInternal(packageInfo.PackageLocation(), details, progress); } bool Update(const SourceDetails& details, IProgressCallback& progress) override final { return UpdateBase(details, false, progress); } bool BackgroundUpdate(const SourceDetails& details, IProgressCallback& progress) override final { return UpdateBase(details, true, progress); } // Retrieves the currently cached version of the package. virtual std::optional GetCurrentVersion(const SourceDetails& details) = 0; virtual bool UpdateInternal(const std::string& packageLocation, const SourceDetails& details, IProgressCallback& progress) = 0; bool Remove(const SourceDetails& details, IProgressCallback& progress) override final { THROW_HR_IF(E_INVALIDARG, details.Type != PreIndexedPackageSourceFactory::Type()); auto lock = LockExclusive(details, progress); if (!lock) { return false; } return RemoveInternal(details, progress); } virtual bool RemoveInternal(const SourceDetails& details, IProgressCallback&) = 0; private: Synchronization::CrossProcessLock LockExclusive(const SourceDetails& details, IProgressCallback& progress, bool isBackground = false) { Synchronization::CrossProcessLock result(CreateNameForCPL(details)); if (isBackground) { // If this is a background update, don't wait on the lock. result.TryAcquireNoWait(); } else { result.Acquire(progress); } return result; } bool UpdateBase(const SourceDetails& details, bool isBackground, IProgressCallback& progress) { THROW_HR_IF(E_INVALIDARG, details.Type != PreIndexedPackageSourceFactory::Type()); std::optional currentVersion = GetCurrentVersion(details); PreIndexedPackageUpdateCheck updateCheck(details); if (currentVersion) { if (currentVersion.value() >= updateCheck.AvailableVersion()) { AICLI_LOG(Repo, Verbose, << "Remote source data (" << updateCheck.AvailableVersion().ToString() << ") was not newer than existing (" << currentVersion.value().ToString() << "), no update needed"); return true; } else { AICLI_LOG(Repo, Verbose, << "Remote source data (" << updateCheck.AvailableVersion().ToString() << ") was newer than existing (" << currentVersion.value().ToString() << "), updating"); } } if (progress.IsCancelledBy(CancelReason::Any)) { AICLI_LOG(Repo, Info, << "Cancelling update upon request"); return false; } auto lock = LockExclusive(details, progress, isBackground); if (!lock) { return false; } return UpdateInternal(updateCheck.PackageLocation(), details, progress); } }; // *Should only be called when under a CrossProcessReaderWriteLock* std::optional GetExtensionFromDetails(const SourceDetails& details) { Deployment::ExtensionCatalog catalog(Deployment::SourceExtensionName); return catalog.FindByPackageFamilyAndId(GetPackageFamilyNameFromDetails(details), Deployment::IndexDBId); } std::optional PackagedContextGetCurrentVersion(const SourceDetails& details) { auto extension = GetExtensionFromDetails(details); if (extension) { auto version = extension->GetPackageVersion(); return Msix::PackageVersion{ version.Major, version.Minor, version.Build, version.Revision }; } else { return std::nullopt; } } // Constructs the location that we will write files to. std::filesystem::path GetStatePathFromDetails(const SourceDetails& details) { std::filesystem::path result = Runtime::GetPathTo(Runtime::PathName::LocalState); result /= PreIndexedPackageSourceFactory::Type(); result /= GetPackageFamilyNameFromDetails(details); return result; } std::optional DesktopContextGetCurrentVersion(const SourceDetails& details) { std::filesystem::path packageState = GetStatePathFromDetails(details); std::filesystem::path packagePath = packageState / s_PreIndexedPackageSourceFactory_PackageFileName; if (std::filesystem::exists(packagePath)) { // If we already have a trusted index package, use it to determine if we need to update or not. Msix::WriteLockedMsixFile indexPackage{ packagePath }; if (indexPackage.ValidateTrustInfo(WI_IsFlagSet(details.TrustLevel, SourceTrustLevel::StoreOrigin))) { Msix::MsixInfo msixInfo{ packagePath }; auto manifest = msixInfo.GetAppPackageManifests(); if (manifest.size() == 1) { return manifest[0].GetIdentity().GetVersion(); } } } return std::nullopt; } bool CheckForUpdateBeforeOpen(const SourceDetails& details, std::optional currentVersion, const std::optional& requestedUpdateInterval) { // If we can't find a good package, then we have to update to operate if (!currentVersion) { AICLI_LOG(Repo, Verbose, << "Source `" << details.Name << "` has no data"); return true; } using namespace std::chrono_literals; using clock = std::chrono::system_clock; // Attempt to convert the package version to a time_point clock::time_point versionTime = Utility::GetTimePointFromVersion(currentVersion.value()); // Since we expect that the version time indicates creation time, don't let it be far in the future. auto now = clock::now(); if (versionTime > now && versionTime - now > 24h) { versionTime = clock::time_point::min(); } // Use the later of the version and last update times clock::time_point timeToCheck = (versionTime > details.LastUpdateTime ? versionTime : details.LastUpdateTime); return IsAfterUpdateCheckTime(details.Name, timeToCheck, requestedUpdateInterval); } struct PackagedContextSourceReference : public ISourceReference { PackagedContextSourceReference(const SourceDetails& details) : m_details(details) { if (!m_details.Data.empty()) { m_details.Identifier = GetPackageFamilyNameFromDetails(details); } } std::string GetIdentifier() override { return m_details.Identifier; } SourceDetails& GetDetails() override { return m_details; }; bool ShouldUpdateBeforeOpen(const std::optional& requestedUpdateInterval) override { return CheckForUpdateBeforeOpen(m_details, PackagedContextGetCurrentVersion(m_details), requestedUpdateInterval); } std::shared_ptr Open(IProgressCallback& progress) override { Synchronization::CrossProcessLock lock(CreateNameForCPL(m_details)); if (!lock.Acquire(progress)) { return {}; } auto extension = GetExtensionFromDetails(m_details); if (!extension) { AICLI_LOG(Repo, Info, << "Package not found " << m_details.Data); THROW_HR(APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING); } THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NEEDS_REMEDIATION), !extension->VerifyContentIntegrity(progress)); // To work around an issue with accessing the public folder, we are temporarily // constructing the location ourself. This was already the case for the non-packaged // runtime, and we can fix both in the future. The only problem with this is that // the directory in the extension *must* be Public, rather than one set by the creator. std::filesystem::path indexLocation = extension->GetPackagePath(); indexLocation /= s_PreIndexedPackageSourceFactory_IndexFilePath; SQLiteIndex index = SQLiteIndex::Open(indexLocation.u8string(), SQLiteIndex::OpenDisposition::Immutable); // We didn't use to store the source identifier, so we compute it here in case it's // missing from the details. m_details.Identifier = GetPackageFamilyNameFromDetails(m_details); return std::make_shared(m_details, std::move(index), false, true); } private: SourceDetails m_details; }; // Source factory for running within a packaged context struct PackagedContextFactory : public PreIndexedFactoryBase { std::shared_ptr CreateInternal(const SourceDetails& details) override { return std::make_shared(details); } std::optional GetCurrentVersion(const SourceDetails& details) override { return PackagedContextGetCurrentVersion(details); } bool UpdateInternal(const std::string& packageLocation, const SourceDetails& details, IProgressCallback& progress) override { // Due to complications with deployment, download the file and deploy from // a local source while we investigate further. bool download = Utility::IsUrlRemote(packageLocation); std::filesystem::path localFile; if (download) { localFile = Runtime::GetPathTo(Runtime::PathName::Temp); localFile /= GetPackageFamilyNameFromDetails(details) + ".msix"; Utility::Download(packageLocation, localFile, Utility::DownloadType::Index, progress); } else { localFile = Utility::ConvertToUTF16(packageLocation); } // Verify the local file Msix::WriteLockedMsixFile fileLock{ localFile }; Msix::MsixInfo localMsixInfo{ localFile }; // The package should not be a bundle THROW_HR_IF(APPINSTALLER_CLI_ERROR_PACKAGE_IS_BUNDLE, localMsixInfo.GetIsBundle()); // Ensure that family name has not changed THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE, GetPackageFamilyNameFromDetails(details) != Msix::GetPackageFamilyNameFromFullName(localMsixInfo.GetPackageFullName())); if (!fileLock.ValidateTrustInfo(WI_IsFlagSet(details.TrustLevel, SourceTrustLevel::StoreOrigin))) { AICLI_LOG(Repo, Error, << "Source update failed. Source package failed trust validation."); THROW_HR(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE); } winrt::Windows::Foundation::Uri uri = winrt::Windows::Foundation::Uri(localFile.c_str()); Deployment::AddPackage( uri, Deployment::Options{ WI_IsFlagSet(details.TrustLevel, SourceTrustLevel::Trusted) }, progress); if (download) { try { // If successful, delete the file std::filesystem::remove(localFile); } CATCH_LOG(); } return true; } bool RemoveInternal(const SourceDetails& details, IProgressCallback& callback) override { auto fullName = Msix::GetPackageFullNameFromFamilyName(GetPackageFamilyNameFromDetails(details)); if (!fullName) { AICLI_LOG(Repo, Info, << "No full name found for family name: " << GetPackageFamilyNameFromDetails(details)); } else { AICLI_LOG(Repo, Info, << "Removing package: " << *fullName); Deployment::RemovePackage(*fullName, winrt::Windows::Management::Deployment::RemovalOptions::None, callback); } return true; } }; struct DesktopContextSourceReference : public ISourceReference { DesktopContextSourceReference(const SourceDetails& details) : m_details(details) { if (!m_details.Data.empty()) { m_details.Identifier = GetPackageFamilyNameFromDetails(details); } } std::string GetIdentifier() override { return m_details.Identifier; } SourceDetails& GetDetails() override { return m_details; }; bool ShouldUpdateBeforeOpen(const std::optional& requestedUpdateInterval) override { return CheckForUpdateBeforeOpen(m_details, DesktopContextGetCurrentVersion(m_details), requestedUpdateInterval); } std::shared_ptr Open(IProgressCallback& progress) override { Synchronization::CrossProcessLock lock(CreateNameForCPL(m_details)); if (!lock.Acquire(progress)) { return {}; } std::filesystem::path packageLocation = GetStatePathFromDetails(m_details); packageLocation /= s_PreIndexedPackageSourceFactory_PackageFileName; if (!std::filesystem::exists(packageLocation)) { AICLI_LOG(Repo, Info, << "Data not found at " << packageLocation); THROW_HR(APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING); } // Put a write exclusive lock on the index package. Msix::WriteLockedMsixFile indexPackage{ packageLocation }; // Validate index package trust info. THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE, !indexPackage.ValidateTrustInfo(WI_IsFlagSet(m_details.TrustLevel, SourceTrustLevel::StoreOrigin))); // Create a temp lock exclusive index file. auto tempIndexFilePath = Runtime::GetNewTempFilePath(); auto tempIndexFile = Utility::ManagedFile::CreateWriteLockedFile(tempIndexFilePath, GENERIC_WRITE, true); // Populate temp index file. Msix::MsixInfo packageInfo(packageLocation); packageInfo.WriteToFileHandle(s_PreIndexedPackageSourceFactory_IndexFilePath, tempIndexFile.GetFileHandle(), progress); if (progress.IsCancelledBy(CancelReason::Any)) { AICLI_LOG(Repo, Info, << "Cancelling open upon request"); return {}; } SQLiteIndex index = SQLiteIndex::Open(tempIndexFile.GetFilePath().u8string(), SQLiteIndex::OpenDisposition::Immutable, std::move(tempIndexFile)); // We didn't use to store the source identifier, so we compute it here in case it's // missing from the details. m_details.Identifier = GetPackageFamilyNameFromDetails(m_details); return std::make_shared(m_details, std::move(index), false, true); } private: SourceDetails m_details; }; // Source factory for running outside of a package. struct DesktopContextFactory : public PreIndexedFactoryBase { std::shared_ptr CreateInternal(const SourceDetails& details) override { return std::make_shared(details); } std::optional GetCurrentVersion(const SourceDetails& details) override { return DesktopContextGetCurrentVersion(details); } bool UpdateInternal(const std::string& packageLocation, const SourceDetails& details, IProgressCallback& progress) override { // We will extract the manifest and index files directly to this location std::filesystem::path packageState = GetStatePathFromDetails(details); std::filesystem::create_directories(packageState); std::filesystem::path packagePath = packageState / s_PreIndexedPackageSourceFactory_PackageFileName; std::filesystem::path tempPackagePath = packagePath.u8string() + ".dnld.msix"; auto removeTempFileOnExit = wil::scope_exit([&]() { try { std::filesystem::remove(tempPackagePath); } catch (...) { AICLI_LOG(Repo, Info, << "Failed to remove temp index file at: " << tempPackagePath); } }); if (Utility::IsUrlRemote(packageLocation)) { AppInstaller::Utility::Download(packageLocation, tempPackagePath, AppInstaller::Utility::DownloadType::Index, progress); } else { std::filesystem::copy(packageLocation, tempPackagePath); progress.OnProgress(100, 100, ProgressType::Percent); } if (progress.IsCancelledBy(CancelReason::Any)) { AICLI_LOG(Repo, Info, << "Cancelling update upon request"); return false; } { // Extra scope to release the file lock right after trust validation. Msix::WriteLockedMsixFile tempIndexPackage{ tempPackagePath }; Msix::MsixInfo tempMsixInfo{ tempPackagePath }; // The package should not be a bundle THROW_HR_IF(APPINSTALLER_CLI_ERROR_PACKAGE_IS_BUNDLE, tempMsixInfo.GetIsBundle()); // Ensure that family name has not changed THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE, GetPackageFamilyNameFromDetails(details) != Msix::GetPackageFamilyNameFromFullName(tempMsixInfo.GetPackageFullName())); if (!tempIndexPackage.ValidateTrustInfo(WI_IsFlagSet(details.TrustLevel, SourceTrustLevel::StoreOrigin))) { AICLI_LOG(Repo, Error, << "Source update failed. Source package failed trust validation."); THROW_HR(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE); } } std::filesystem::rename(tempPackagePath, packagePath); AICLI_LOG(Repo, Info, << "Source update success."); removeTempFileOnExit.release(); return true; } bool RemoveInternal(const SourceDetails& details, IProgressCallback&) override { std::filesystem::path packageState = GetStatePathFromDetails(details); if (!std::filesystem::exists(packageState)) { AICLI_LOG(Repo, Info, << "No state found for source: " << packageState.u8string()); } else { AICLI_LOG(Repo, Info, << "Removing state found for source: " << packageState.u8string()); std::filesystem::remove_all(packageState); } return true; } }; } std::unique_ptr PreIndexedPackageSourceFactory::Create() { if (Runtime::IsRunningInPackagedContext()) { return std::make_unique(); } else { return std::make_unique(); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PreIndexedPackageSourceFactory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" #include "SourceFactory.h" #include namespace AppInstaller::Repository::Microsoft { using namespace std::string_view_literals; // A source where the index is precomputed and stored on a server within an optional MSIX package. // In addition, the manifest files are also individually available on the server. // Arg :: Expected to be a fully qualified path to the root of the data. // This can be a web location such as https://somewhere/ or a local file share \\somewhere\ // Under this path there must exist an MSIX package called "index.msix". // This must have a file called "index.db" contained within, which is a SQLiteIndex. // The index's paths refer to relative locations under the Arg value. // Data :: The package family name of the package at Arg + /index.msix. struct PreIndexedPackageSourceFactory { // Get the type string for this source. static constexpr std::string_view Type() { using namespace std::string_view_literals; return "Microsoft.PreIndexed.Package"sv; } // Creates a source factory for this type. static std::unique_ptr Create(); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/ARPHelper.h" #include "Microsoft/FontHelper.h" #include "Microsoft/PredefinedInstalledSourceFactory.h" #include "Microsoft/SQLiteIndex.h" #include "Microsoft/SQLiteIndexSource.h" #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; namespace AppInstaller::Repository::Microsoft { namespace { std::optional GetCachedMSIXName(const Utility::NormalizedString& id, const Utility::Version& version, SQLiteIndex& cacheData) { SearchRequest searchRequest; searchRequest.Inclusions.emplace_back(PackageMatchField::Id, MatchType::Exact, id); SQLiteIndex::SearchResult searchResult = cacheData.Search(searchRequest); if (searchResult.Matches.empty()) { return std::nullopt; } if (searchResult.Matches.size() != 1) { // This is very unexpected, but just log it and carry on AICLI_LOG(Repo, Warning, << "Found multiple (" << searchResult.Matches.size() << ") cache entries for: " << id); return std::nullopt; } auto versionKeys = cacheData.GetVersionKeysById(searchResult.Matches[0].first); const SQLiteIndex::VersionKey* versionKey = nullptr; for (const auto& key : versionKeys) { if (key.VersionAndChannel.GetVersion() == version) { versionKey = &key; break; } } if (!versionKey) { return std::nullopt; } return cacheData.GetPropertyByPrimaryId(versionKey->ManifestId, PackageVersionProperty::Name); } // Populates the index with the entries from MSIX. void PopulateIndexFromMSIX(SQLiteIndex& index, Manifest::ScopeEnum scope, SQLiteIndex* cacheData = nullptr) { using namespace winrt::Windows::ApplicationModel; using namespace winrt::Windows::Management::Deployment; using namespace winrt::Windows::Foundation::Collections; AICLI_LOG(Repo, Verbose, << "Examining MSIX entries for " << ScopeToString(scope)); IIterable packages; PackageManager packageManager; if (scope == Manifest::ScopeEnum::Machine) { // May not be present on our oldest supported systems; simply ignore for the time being. IPackageManager9 packageManager9 = packageManager.try_as(); if (packageManager9) { try { packages = packageManager.FindProvisionedPackages(); } catch (const winrt::hresult_error& hre) { // Historically this API has not been accessible unelevated; if it fails, try to carry on AICLI_LOG(Repo, Warning, << "FindProvisionedPackages failed, bypassing provisioned packages: 0x" << Logging::SetHRFormat << hre.code()); } } else { AICLI_LOG(Repo, Warning, << "FindProvisionedPackages is not available on this version of Windows"); } } else { // TODO: Consider if Optional packages should also be enumerated for (PackageTypes types : { PackageTypes::Main | PackageTypes::Framework, PackageTypes::Main, PackageTypes::Framework }) { try { packages = packageManager.FindPackagesForUserWithPackageTypes({}, types); break; } catch (const winrt::hresult_error& hre) { if (hre.code() == E_NOT_SET) { // This OS issue occurs frequently enough that we will attempt to work around it by enumerating progressively fewer packages AICLI_LOG(Repo, Warning, << "FindPackagesForUserWithPackageTypes returned E_NOT_SET for types: " << ToIntegral(types)); } else { throw; } } } } // Failed to retrieve even an empty package list; make sure that these cases have a log to indicate why. if (!packages) { AICLI_LOG(Repo, Warning, << "MSIX package list not populated"); return; } // Reuse the same manifest object, as we will be setting the same values every time. Manifest::Manifest manifest; // Add one installer for storing the package family name. manifest.Installers.emplace_back(); // Every package will have the same tags currently. manifest.DefaultLocalization.Add({ "msix" }); // Fields in the index but not populated: // AppMoniker - Not sure what we would put. // Channel - We don't know this information here. // Commands - We could open the manifest and look for these eventually. // Tags - Not sure what else we could put in here. for (const auto& package : packages) { // System packages are part of the OS, and cannot be managed by the user. // Filter them out as there is no point in showing them in a package manager. auto signatureKind = package.SignatureKind(); if (signatureKind == PackageSignatureKind::System) { continue; } auto packageId = package.Id(); Utility::NormalizedString fullName = Utility::ConvertToUTF8(packageId.FullName()); Utility::NormalizedString familyName = Utility::ConvertToUTF8(packageId.FamilyName()); manifest.Id = "MSIX\\" + fullName; // Get version std::ostringstream strstr; auto packageVersion = packageId.Version(); strstr << packageVersion.Major << '.' << packageVersion.Minor << '.' << packageVersion.Build << '.' << packageVersion.Revision; manifest.Version = strstr.str(); // Determine package name bool isPackageNameSet = false; // Look for the name in the cache data first if (cacheData) { std::optional cachedName = GetCachedMSIXName(manifest.Id, manifest.Version, *cacheData); if (cachedName) { manifest.DefaultLocalization.Add(cachedName.value()); isPackageNameSet = true; } } // Attempt to get the DisplayName. Since this will retrieve the localized value, it has a chance to fail. // Rather than completely skip this package in that case, we will simply fall back to using the package name below. if (!isPackageNameSet && !Runtime::IsRunningAsSystem()) { try { auto displayName = Utility::ConvertToUTF8(package.DisplayName()); if (!displayName.empty()) { manifest.DefaultLocalization.Add(displayName); isPackageNameSet = true; } } catch (const winrt::hresult_error& hre) { AICLI_LOG(Repo, Warning, << "winrt::hresult_error[0x" << Logging::SetHRFormat << hre.code() << ": " << Utility::ConvertToUTF8(hre.message()) << "] exception thrown when getting DisplayName for " << fullName); } catch (...) { AICLI_LOG(Repo, Warning, << "Unknown exception thrown when getting DisplayName for " << fullName); } } if (!isPackageNameSet) { manifest.DefaultLocalization.Add(Utility::ConvertToUTF8(packageId.Name())); } manifest.Installers[0].PackageFamilyName = familyName; // Use the full name as a unique key for the path auto manifestId = index.AddManifest(manifest); index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledType, Manifest::InstallerTypeToString(Manifest::InstallerTypeEnum::Msix)); auto architecture = Utility::ConvertToArchitectureEnum(packageId.Architecture()); if (architecture) { index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledArchitecture, ToString(architecture.value())); } // May not be present on our oldest supported systems; simply ignore for the time being. IPackage8 package8 = package.try_as(); if (package8) { index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledLocation, Utility::ConvertToUTF8(package8.InstalledPath())); } else { AICLI_LOG(Repo, Warning, << "Windows::ApplicationModel::Package::InstalledPath is not available on this version of Windows"); } } } SQLiteIndex CreateAndPopulateIndex(PredefinedInstalledSourceFactory::Filter filter) { AICLI_LOG(Repo, Verbose, << "Creating PredefinedInstalledSource with filter [" << PredefinedInstalledSourceFactory::FilterToString(filter) << ']'); // Create an in memory index SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless); // Put installed packages into the index if (filter == PredefinedInstalledSourceFactory::Filter::None || filter == PredefinedInstalledSourceFactory::Filter::ARP || filter == PredefinedInstalledSourceFactory::Filter::User || filter == PredefinedInstalledSourceFactory::Filter::Machine) { ARPHelper arpHelper; if (filter != PredefinedInstalledSourceFactory::Filter::User) { arpHelper.PopulateIndexFromARP(index, Manifest::ScopeEnum::Machine); } if (filter != PredefinedInstalledSourceFactory::Filter::Machine) { arpHelper.PopulateIndexFromARP(index, Manifest::ScopeEnum::User); } } if (filter == PredefinedInstalledSourceFactory::Filter::None || filter == PredefinedInstalledSourceFactory::Filter::MSIX || filter == PredefinedInstalledSourceFactory::Filter::User) { PopulateIndexFromMSIX(index, Manifest::ScopeEnum::User); } else if (filter == PredefinedInstalledSourceFactory::Filter::Machine) { PopulateIndexFromMSIX(index, Manifest::ScopeEnum::Machine); } // Put Installed Fonts into the index. if (filter == PredefinedInstalledSourceFactory::Filter::None || filter == PredefinedInstalledSourceFactory::Filter::ARP || filter == PredefinedInstalledSourceFactory::Filter::User || filter == PredefinedInstalledSourceFactory::Filter::Machine) { FontHelper fontHelper; if (filter != PredefinedInstalledSourceFactory::Filter::User) { fontHelper.PopulateIndex(index, Manifest::ScopeEnum::Machine); } if (filter != PredefinedInstalledSourceFactory::Filter::Machine) { fontHelper.PopulateIndex(index, Manifest::ScopeEnum::User); } } AICLI_LOG(Repo, Verbose, << " ... finished creating PredefinedInstalledSource"); return index; } struct CachedInstalledIndex { struct Singleton : public WinRT::COMStaticStorageBase { Singleton() : COMStaticStorageBase(L"WindowsPackageManager.CachedInstalledIndex") {} }; CachedInstalledIndex() { ARPHelper arpHelper; m_registryWatchers = arpHelper.CreateRegistryWatchers(Manifest::ScopeEnum::Unknown, [this](Manifest::ScopeEnum, Utility::Architecture, wil::RegistryChangeKind) { ForceNextUpdate(); }); FontHelper fontHelper; fontHelper.AddRegistryWatchers(Manifest::ScopeEnum::Unknown, [this](Manifest::ScopeEnum, wil::RegistryChangeKind) { ForceNextUpdate(); }, m_registryWatchers); m_catalog = winrt::Windows::ApplicationModel::PackageCatalog::OpenForCurrentUser(); m_eventRevoker = m_catalog.PackageStatusChanged(winrt::auto_revoke, [this](auto...) { ForceNextUpdate(); }); } void UpdateIndexIfNeeded() { auto sharedLock = m_lock.lock_shared(); if (CheckForUpdate()) { // Upgrade to exclusive sharedLock.reset(); auto exclusiveLock = m_lock.lock_exclusive(); if (CheckForUpdate()) { // TODO: To support servicing, the initial implementation of update will simply leverage // some data from the existing index to speed up the MSIX populate function. // In a larger update, we may want to make it possible to actually update the cache directly. // We may even persist the cache to disk to speed things up further. // Set the update indicator to false before we start reading so that an external change can // reindicate a need to update in the middle. But in the event that we error here, set it back to true // to prevent an error from blocking further attempts. m_forceNextUpdate = false; auto scopeExit = wil::scope_exit([&]() { m_forceNextUpdate = true; }); // Populate from ARP using standard mechanism. SQLiteIndex update = CreateAndPopulateIndex(PredefinedInstalledSourceFactory::Filter::ARP); // Populate from MSIX, using localization data from the existing index if applicable. PopulateIndexFromMSIX(update, Manifest::ScopeEnum::User, m_index.get()); m_index = std::make_unique(std::move(update)); scopeExit.release(); } } } SQLiteIndex GetCopy() { auto lock = m_lock.lock_shared(); THROW_HR_IF(E_POINTER, !m_index); return SQLiteIndex::CopyFrom(SQLITE_MEMORY_DB_CONNECTION_TARGET, *m_index); } void ForceNextUpdate() { m_forceNextUpdate = true; } private: bool CheckForUpdate() { return (!m_index || m_forceNextUpdate.load()); } wil::srwlock m_lock; std::atomic_bool m_forceNextUpdate{ false }; std::unique_ptr m_index; std::vector m_registryWatchers; winrt::Windows::ApplicationModel::PackageCatalog m_catalog = nullptr; decltype(winrt::Windows::ApplicationModel::PackageCatalog{ nullptr }.PackageStatusChanged(winrt::auto_revoke, nullptr)) m_eventRevoker; }; struct PredefinedInstalledSourceReference : public ISourceReference { PredefinedInstalledSourceReference(const SourceDetails& details) : m_details(details) { m_details.Identifier = "*PredefinedInstalledSource"; if (PredefinedInstalledSourceFactory::StringToFilter(m_details.Arg) == PredefinedInstalledSourceFactory::Filter::NoneWithForcedCacheUpdate) { GetCachedInstalledIndex()->ForceNextUpdate(); } } std::string GetIdentifier() override { return m_details.Identifier; } SourceDetails& GetDetails() override { return m_details; }; std::shared_ptr Open(IProgressCallback& progress) override { // TODO: Maybe we do need to use it? UNREFERENCED_PARAMETER(progress); // Determine the filter PredefinedInstalledSourceFactory::Filter filter = PredefinedInstalledSourceFactory::StringToFilter(m_details.Arg); // Only cache for the unfiltered install data if (filter == PredefinedInstalledSourceFactory::Filter::None || filter == PredefinedInstalledSourceFactory::Filter::NoneWithForcedCacheUpdate) { std::shared_ptr cachedIndex = GetCachedInstalledIndex(); cachedIndex->UpdateIndexIfNeeded(); return std::make_shared(m_details, cachedIndex->GetCopy(), true); } else { return std::make_shared(m_details, CreateAndPopulateIndex(filter), true); } } private: std::shared_ptr GetCachedInstalledIndex() { static CachedInstalledIndex::Singleton s_installedIndex; return s_installedIndex.Get(); } SourceDetails m_details; }; // The factory for the predefined installed source. struct Factory : public ISourceFactory { std::string_view TypeName() const override final { return PredefinedInstalledSourceFactory::Type(); } std::shared_ptr Create(const SourceDetails& details) override final { THROW_HR_IF(E_INVALIDARG, details.Type != PredefinedInstalledSourceFactory::Type()); return std::make_shared(details); } bool Add(SourceDetails&, IProgressCallback&) override final { // Add should never be needed, as this is predefined. THROW_HR(E_NOTIMPL); } bool Update(const SourceDetails&, IProgressCallback&) override final { // Update could be used later, but not for now. THROW_HR(E_NOTIMPL); } bool Remove(const SourceDetails&, IProgressCallback&) override final { // Similar to add, remove should never be needed. THROW_HR(E_NOTIMPL); } }; } std::string_view PredefinedInstalledSourceFactory::FilterToString(Filter filter) { switch (filter) { case AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory::Filter::None: return "None"sv; case AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory::Filter::ARP: return "ARP"sv; case AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory::Filter::MSIX: return "MSIX"sv; case AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory::Filter::User: return "User"sv; case AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory::Filter::Machine: return "Machine"sv; case AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory::Filter::NoneWithForcedCacheUpdate: return "NoneWithForcedCacheUpdate"sv; default: return "Unknown"sv; } } PredefinedInstalledSourceFactory::Filter PredefinedInstalledSourceFactory::StringToFilter(std::string_view filter) { if (filter == FilterToString(Filter::ARP)) { return Filter::ARP; } else if (filter == FilterToString(Filter::MSIX)) { return Filter::MSIX; } else if (filter == FilterToString(Filter::User)) { return Filter::User; } else if (filter == FilterToString(Filter::Machine)) { return Filter::Machine; } else if (filter == FilterToString(Filter::NoneWithForcedCacheUpdate)) { return Filter::NoneWithForcedCacheUpdate; } else { return Filter::None; } } std::unique_ptr PredefinedInstalledSourceFactory::Create() { return std::make_unique(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" #include "SourceFactory.h" #include namespace AppInstaller::Repository::Microsoft { using namespace std::string_view_literals; // A source of installed packages on the local system. // Arg :: A value indicating how the list is to be filtered. // Data :: Not used. struct PredefinedInstalledSourceFactory { // Get the type string for this source. static constexpr std::string_view Type() { return "Microsoft.Predefined.Installed"sv; } // The filtering level for the source. enum class Filter { // Contains user ARP, machine ARP, user MSIX, user Fonts and machine Fonts None, // Contains user ARP and machine ARP ARP, // Contains user MSIX MSIX, // Contains user ARP and user MSIX and user Fonts User, // Contains machine ARP and machine MSIX and machine Fonts Machine, // Same as None but creating the source reference causes the next Open to always update the cache NoneWithForcedCacheUpdate, }; // Converts a filter to its string. static std::string_view FilterToString(Filter filter); // Converts a string to its filter value. static Filter StringToFilter(std::string_view filter); // Creates a source factory for this type. static std::unique_ptr Create(); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/ARPHelper.h" #include "Microsoft/PredefinedWriteableSourceFactory.h" #include "Microsoft/SQLiteIndex.h" #include "Microsoft/SQLiteIndexSource.h" #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; namespace AppInstaller::Repository::Microsoft { namespace { // The factory for the predefined installing source. struct PredefinedWriteableSourceFactoryImpl : public ISourceFactory { std::string_view TypeName() const override final { return PredefinedWriteableSourceFactory::Type(); } std::shared_ptr Create(const SourceDetails& details) override final; bool Add(SourceDetails&, IProgressCallback&) override final { // Add should never be needed, as this is predefined. THROW_HR(E_NOTIMPL); } bool Update(const SourceDetails&, IProgressCallback&) override final { // Update could be used later, but not for now. THROW_HR(E_NOTIMPL); } bool Remove(const SourceDetails&, IProgressCallback&) override final { // Similar to add, remove should never be needed. THROW_HR(E_NOTIMPL); } }; struct PredefinedWriteableSourceReference : public ISourceReference { PredefinedWriteableSourceReference(const SourceDetails& details) : m_details(details) { m_details.Identifier = "*PredefinedWriteableSource"; } std::string GetIdentifier() override { return m_details.Identifier; } SourceDetails& GetDetails() override { return m_details; }; std::shared_ptr Open(IProgressCallback&) override { // Installing is the only type right now so just return the Installing source to all callers. // Since the source is writeable, it must be shared by all callers that try to open it // since queries on one instance would not see what was written on another instance. std::call_once(g_InstallingSourceOnceFlag, [&]() { // Create an in memory index without paths or dependencies SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); g_sharedSource = std::make_shared(m_details, std::move(index), true); }); return g_sharedSource; } private: SourceDetails m_details; static std::shared_ptr g_sharedSource; static std::once_flag g_InstallingSourceOnceFlag; }; std::shared_ptr PredefinedWriteableSourceReference::g_sharedSource = nullptr; std::once_flag PredefinedWriteableSourceReference::g_InstallingSourceOnceFlag; std::shared_ptr PredefinedWriteableSourceFactoryImpl::Create(const SourceDetails& details) { THROW_HR_IF(E_INVALIDARG, details.Type != PredefinedWriteableSourceFactory::Type()); return std::make_shared(details); } } std::string_view PredefinedWriteableSourceFactory::TypeToString(WriteableType type) { switch (type) { case AppInstaller::Repository::Microsoft::PredefinedWriteableSourceFactory::WriteableType::Installing: return "Installing"sv; default: return "Unknown"sv; } } std::unique_ptr PredefinedWriteableSourceFactory::Create() { return std::make_unique(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" #include "SourceFactory.h" #include namespace AppInstaller::Repository::Microsoft { using namespace std::string_view_literals; // A source of installing packages on the local system. // Arg :: A value indicating the type of writeable source // Data :: Not used. struct PredefinedWriteableSourceFactory { // Get the type string for this source. static constexpr std::string_view Type() { return "Microsoft.Predefined.Writeable"sv; } // The type for the source. enum class WriteableType { Installing }; // Converts a type to its string. static std::string_view TypeToString(WriteableType type); // Creates a source factory for this type. static std::unique_ptr Create(); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/README.md ================================================ ### Design The object stack is as such: - SQLiteIndex :: Houses the database connection and the proper interface with which to interact with it - ISQLiteIndex :: Interface that creates a uniform model to use against all schemas - Schema::V*::Interface :: Actual implementation of ISQLiteIndex for specific schema version The code that needs to interact with an index will create a SQLiteIndex object. That in turn will open the database, determine the schema version, then create the appropriate ISQLiteIndex providing object. All queries and changes to the index will go through the SQLiteIndex object. When a change to the schema is needed, a new schema directory should be created. This can pull code from the schemas before it, only updating the specific table(s) that are needed. Then Version code should be updated to create the new Interface as appropriate. Any new methods needed on ISQLiteIndex should be added, and SQLiteIndexBase to implement them in terms of the older functions. Only the new schema should need to implement the new functions. **Once shipped, one should never need to update code within an existing schema, save for bug fixes.** ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SQLiteIndex.h" #include #include "ArpVersionValidation.h" #include namespace AppInstaller::Repository::Microsoft { namespace { size_t GetPageSizeFromOptions(SQLiteIndex::CreateOptions options) { return WI_IsFlagSet(options, SQLiteIndex::CreateOptions::LargePageSize) ? 65536 : 0; } } SQLiteIndex SQLiteIndex::CreateNew(const std::string& filePath, SQLite::Version version, CreateOptions options) { AICLI_LOG(Repo, Info, << "Creating new SQLite Index with version [" << version << "] at '" << filePath << "'"); SQLiteIndex result{ filePath, version, options }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "sqliteindex_createnew"); // Use calculated version, as incoming version could be 'latest' result.m_version.SetSchemaVersion(result.m_dbconn); result.m_interface->CreateTables(result.m_dbconn, options); result.SetLastWriteTime(); savepoint.Commit(); return result; } SQLiteIndex SQLiteIndex::Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile) { return { filePath, disposition, std::move(indexFile) }; } SQLiteIndex SQLiteIndex::CopyFrom(const std::string& filePath, SQLiteIndex& source) { return { filePath, source }; } SQLiteIndex::SQLiteIndex(const std::string& target, const SQLite::Version& version, CreateOptions options) : SQLiteStorageBase(target, version, GetPageSizeFromOptions(options)) { m_dbconn.EnableICU(); m_interface = Schema::CreateISQLiteIndex(version); m_version = m_interface->GetVersion(); SetDatabaseFilePath(target); } SQLiteIndex::SQLiteIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : SQLiteStorageBase(target, disposition, std::move(indexFile)) { m_dbconn.EnableICU(); AICLI_LOG(Repo, Info, << "Opened SQLite Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); m_interface = Schema::CreateISQLiteIndex(m_version); THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); SetDatabaseFilePath(target); } SQLiteIndex::SQLiteIndex(const std::string& target, SQLiteIndex& source) : SQLiteStorageBase(target, source) { m_dbconn.EnableICU(); m_interface = Schema::CreateISQLiteIndex(m_version); SetDatabaseFilePath(target); } void SQLiteIndex::SetDatabaseFilePath(const std::string& target) { if (target != SQLITE_MEMORY_DB_CONNECTION_TARGET) { m_contextData.Add(Utility::ConvertToUTF16(target)); } } #ifndef AICLI_DISABLE_TEST_HOOKS void SQLiteIndex::ForceVersion(const SQLite::Version& version) { m_interface = Schema::CreateISQLiteIndex(version); } SQLite::Version SQLiteIndex::GetLatestVersion() { return Schema::CreateISQLiteIndex(SQLite::Version::Latest())->GetVersion(); } const Schema::SQLiteIndexContextData& SQLiteIndex::GetContextData() const { return m_contextData; } #endif SQLiteIndex::IdType SQLiteIndex::AddManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) { AICLI_LOG(Repo, Verbose, << "Adding manifest from file [" << manifestPath << "]"); Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); return AddManifestInternal(manifest, relativePath); } SQLiteIndex::IdType SQLiteIndex::AddManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { return AddManifestInternal(manifest, relativePath); } SQLiteIndex::IdType SQLiteIndex::AddManifest(const Manifest::Manifest& manifest) { return AddManifestInternal(manifest, {}); } SQLiteIndex::IdType SQLiteIndex::AddManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath) { std::lock_guard lockInterface{ *m_interfaceLock }; return AddManifestInternalHoldingLock(manifest, relativePath); } SQLiteIndex::IdType SQLiteIndex::AddManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional& relativePath) { AICLI_LOG(Repo, Verbose, << "Adding manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_addmanifest"); IdType result = m_interface->AddManifest(m_dbconn, manifest, relativePath); SetLastWriteTime(); savepoint.Commit(); return result; } bool SQLiteIndex::UpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) { AICLI_LOG(Repo, Verbose, << "Updating manifest from file [" << manifestPath << "]"); Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); return UpdateManifestInternal(manifest, relativePath); } bool SQLiteIndex::UpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { return UpdateManifestInternal(manifest, relativePath); } bool SQLiteIndex::UpdateManifest(const Manifest::Manifest& manifest) { return UpdateManifestInternal(manifest, {}); } bool SQLiteIndex::UpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath) { std::lock_guard lockInterface{ *m_interfaceLock }; return UpdateManifestInternalHoldingLock(manifest, relativePath); } bool SQLiteIndex::UpdateManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional& relativePath) { AICLI_LOG(Repo, Verbose, << "Updating manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_updatemanifest"); bool result = m_interface->UpdateManifest(m_dbconn, manifest, relativePath).first; if (result) { SetLastWriteTime(); savepoint.Commit(); } return result; } bool SQLiteIndex::AddOrUpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) { AICLI_LOG(Repo, Verbose, << "Adding or Updating manifest from file [" << manifestPath << "]"); Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); return AddOrUpdateManifestInternal(manifest, relativePath); } bool SQLiteIndex::AddOrUpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { return AddOrUpdateManifestInternal(manifest, relativePath); } bool SQLiteIndex::AddOrUpdateManifest(const Manifest::Manifest& manifest) { return AddOrUpdateManifestInternal(manifest, {}); } bool SQLiteIndex::AddOrUpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath) { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Adding or Updating manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); if (m_interface->GetManifestIdByManifest(m_dbconn, manifest)) { UpdateManifestInternalHoldingLock(manifest, relativePath); return false; } else { AddManifestInternalHoldingLock(manifest, relativePath); return true; } } void SQLiteIndex::RemoveManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) { AICLI_LOG(Repo, Verbose, << "Removing manifest from file [" << manifestPath << "]"); Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); RemoveManifest(manifest, relativePath); } void SQLiteIndex::RemoveManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { AICLI_LOG(Repo, Verbose, << "Removing manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath << "]"); RemoveManifest(manifest); } void SQLiteIndex::RemoveManifest(const Manifest::Manifest& manifest) { std::lock_guard lockInterface{ *m_interfaceLock }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_removemanifest"); m_interface->RemoveManifest(m_dbconn, manifest); SetLastWriteTime(); savepoint.Commit(); } void SQLiteIndex::RemoveManifestById(IdType manifestId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "SQLiteIndex_RemoveManifestById"); m_interface->RemoveManifestById(m_dbconn, manifestId); SetLastWriteTime(); savepoint.Commit(); } void SQLiteIndex::PrepareForPackaging() { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Info, << "Preparing index for packaging"); m_interface->PrepareForPackaging(Schema::SQLiteIndexContext{ m_dbconn, m_contextData }); } bool SQLiteIndex::CheckConsistency(bool log) const { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Info, << "Checking index consistency..."); bool result = m_interface->CheckConsistency(m_dbconn, log); AICLI_LOG(Repo, Info, << "...index *WAS" << (result ? "*" : " NOT*") << " consistent."); return result; } Schema::ISQLiteIndex::SearchResult SQLiteIndex::Search(const SearchRequest& request) const { std::lock_guard lockInterface{ *m_interfaceLock }; AICLI_LOG(Repo, Verbose, << "Performing search: " << request.ToString()); return m_interface->Search(m_dbconn, request); } std::optional SQLiteIndex::GetPropertyByPrimaryId(IdType primaryId, PackageVersionProperty property) const { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->GetPropertyByPrimaryId(m_dbconn, primaryId, property); } std::vector SQLiteIndex::GetMultiPropertyByPrimaryId(IdType primaryId, PackageVersionMultiProperty property) const { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->GetMultiPropertyByPrimaryId(m_dbconn, primaryId, property); } std::optional SQLiteIndex::GetManifestIdByKey(IdType id, std::string_view version, std::string_view channel) const { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->GetManifestIdByKey(m_dbconn, id, version, channel); } std::optional SQLiteIndex::GetManifestIdByManifest(const Manifest::Manifest& manifest) const { return m_interface->GetManifestIdByManifest(m_dbconn, manifest); } std::vector SQLiteIndex::GetVersionKeysById(IdType id) const { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->GetVersionKeysById(m_dbconn, id); } SQLiteIndex::MetadataResult SQLiteIndex::GetMetadataByManifestId(SQLite::rowid_t manifestId) const { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->GetMetadataByManifestId(m_dbconn, manifestId); } void SQLiteIndex::SetMetadataByManifestId(IdType manifestId, PackageVersionMetadata metadata, std::string_view value) { std::lock_guard lockInterface{ *m_interfaceLock }; m_interface->SetMetadataByManifestId(m_dbconn, manifestId, metadata, value); } Utility::NormalizedName SQLiteIndex::NormalizeName(std::string_view name, std::string_view publisher) const { std::lock_guard lockInterface{ *m_interfaceLock }; return m_interface->NormalizeName(name, publisher); } std::set> SQLiteIndex::GetDependenciesByManifestRowId(SQLite::rowid_t manifestRowId) const { return m_interface->GetDependenciesByManifestRowId(m_dbconn, manifestRowId); } std::vector> SQLiteIndex::GetDependentsById(AppInstaller::Manifest::string_t packageId) const { return m_interface->GetDependentsById(m_dbconn, packageId); } bool SQLiteIndex::MigrateTo(SQLite::Version version) { std::lock_guard lockInterface{ *m_interfaceLock }; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_migrate_to"); AICLI_LOG(Repo, Info, << "Attempting to migrate index from [" << m_interface->GetVersion() << "] to [" << version << "]..."); std::unique_ptr newInterface = Schema::CreateISQLiteIndex(version); bool result = newInterface->MigrateFrom(m_dbconn, m_interface.get()); AICLI_LOG(Repo, Info, << "...migration was " << (result ? "" : "NOT ") << "successful"); if (result) { version.SetSchemaVersion(m_dbconn); SetLastWriteTime(); savepoint.Commit(); m_version = version; m_interface = std::move(newInterface); } return result; } void SQLiteIndex::SetProperty(Property property, const std::string& value) { std::lock_guard lockInterface{ *m_interfaceLock }; switch (property) { case Property::PackageUpdateTrackingBaseTime: m_interface->SetProperty(m_dbconn, Schema::Property::PackageUpdateTrackingBaseTime, value); break; case Property::IntermediateFileOutputPath: { std::filesystem::path pathValue{ Utility::ConvertToUTF16(value) }; THROW_HR_IF(E_INVALIDARG, pathValue.empty() || pathValue.is_relative()); m_contextData.Add(std::move(pathValue)); } break; } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Microsoft/Schema/ISQLiteIndex.h" #include #include #include "ISource.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace AppInstaller::Repository::Microsoft { // Holds the connection to the database, as well as the appropriate functionality to interface with it. struct SQLiteIndex : SQLite::SQLiteStorageBase { // An id that refers to a specific application. using IdType = SQLite::rowid_t; // The return type of Search using SearchResult = Schema::ISQLiteIndex::SearchResult; // The return type of GetMetadataByManifestId using MetadataResult = Schema::ISQLiteIndex::MetadataResult; // Options for creating a new index. using CreateOptions = Schema::ISQLiteIndex::CreateOptions; // The type of version keys. using VersionKey = Schema::ISQLiteIndex::VersionKey; SQLiteIndex(const SQLiteIndex&) = delete; SQLiteIndex& operator=(const SQLiteIndex&) = delete; SQLiteIndex(SQLiteIndex&&) = default; SQLiteIndex& operator=(SQLiteIndex&&) = default; // Creates a new index database of the given version. static SQLiteIndex CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest(), CreateOptions options = CreateOptions::None); // Opens an existing SQLiteIndex database. static SQLiteIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}); // Creates a copy of the given index. static SQLiteIndex CopyFrom(const std::string& filePath, SQLiteIndex& source); #ifndef AICLI_DISABLE_TEST_HOOKS // Changes the version of the interface being used to operate on the database. // Should only be used for testing. void ForceVersion(const SQLite::Version& version); // Gets the latest version of the index schema (the actual numbers, not just the latest sentinel values). static SQLite::Version GetLatestVersion(); // Gets the context data for testing. const Schema::SQLiteIndexContextData& GetContextData() const; #endif // Adds the manifest at the repository relative path to the index. // If the function succeeds, the manifest has been added. // Returns the manifest id. IdType AddManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath); // Adds the manifest at the repository relative path to the index. // If the function succeeds, the manifest has been added. // Returns the manifest id. IdType AddManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); // Adds the manifest to the index. // If the function succeeds, the manifest has been added. // Returns the manifest id. IdType AddManifest(const Manifest::Manifest& manifest); // Updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the index was modified by the function. bool UpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath); // Updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the index was modified by the function. bool UpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); // Updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the index was modified by the function. bool UpdateManifest(const Manifest::Manifest& manifest); // Adds or updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the manifest was added (true) or updated (false). bool AddOrUpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath); // Updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the manifest was added (true) or updated (false). bool AddOrUpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); // Updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the manifest was added (true) or updated (false). bool AddOrUpdateManifest(const Manifest::Manifest& manifest); // Removes the manifest with matching { Id, Version, Channel } from the index. void RemoveManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath); // Removes the manifest with matching { Id, Version, Channel } from the index. void RemoveManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); // Removes the manifest with matching { Id, Version, Channel } from the index. void RemoveManifest(const Manifest::Manifest& manifest); // Removes the manifest with the given id. void RemoveManifestById(IdType manifestId); // Removes data that is no longer needed for an index that is to be published. void PrepareForPackaging(); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. bool CheckConsistency(bool log = false) const; // Performs a search based on the given criteria. SearchResult Search(const SearchRequest& request) const; // Gets the string for the given property and primary id, if present. std::optional GetPropertyByPrimaryId(IdType primaryId, PackageVersionProperty property) const; // Gets the string values for the given property and primary id, if present. std::vector GetMultiPropertyByPrimaryId(IdType primaryId, PackageVersionMultiProperty property) const; // Gets the manifest id for the given { id, version, channel }, if present. // If version is empty, gets the value for the 'latest' version. std::optional GetManifestIdByKey(IdType id, std::string_view version, std::string_view channel) const; // Gets the manifest id for the given manifest, if present. std::optional GetManifestIdByManifest(const Manifest::Manifest& manifest) const; // Gets all versions and channels for the given id. std::vector GetVersionKeysById(IdType id) const; // Gets the string for the given metadata and manifest id, if present. MetadataResult GetMetadataByManifestId(SQLite::rowid_t manifestId) const; // Sets the string for the given metadata and manifest id. void SetMetadataByManifestId(IdType manifestId, PackageVersionMetadata metadata, std::string_view value); // Normalizes a name using the internal rules used by the index. // Largely a utility function; should not be used to do work on behalf of the index by the caller. Utility::NormalizedName NormalizeName(std::string_view name, std::string_view publisher) const; // Get all the dependencies for a specific manifest. std::set> GetDependenciesByManifestRowId(SQLite::rowid_t manifestRowId) const; std::vector> GetDependentsById(AppInstaller::Manifest::string_t packageId) const; // Migrates the index to the target version. // Returns false to indicate that the requested migration is not supported. bool MigrateTo(SQLite::Version version); // The property values that can be set. enum class Property { PackageUpdateTrackingBaseTime, IntermediateFileOutputPath, }; // Sets the given property. // Some properties will persist into the database. void SetProperty(Property property, const std::string& value); private: // Constructor used to create a new index. SQLiteIndex(const std::string& target, const SQLite::Version& version, CreateOptions options); // Constructor used to open an existing index. SQLiteIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); // Constructor used to copy the given index. SQLiteIndex(const std::string& target, SQLiteIndex& source); // Sets the database file path in the context data if appropriate. void SetDatabaseFilePath(const std::string& target); // Internal functions to normalize on the relativePath being present. IdType AddManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath); IdType AddManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional& relativePath); bool UpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath); bool UpdateManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional& relativePath); bool AddOrUpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional& relativePath); std::unique_ptr m_interface; Schema::SQLiteIndexContextData m_contextData; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/SQLiteIndexSource.h" #include "Microsoft/SQLiteIndexSourceV1.h" #include "Microsoft/SQLiteIndexSourceV2.h" #include "Microsoft/PreIndexedPackageSourceFactory.h" #include #include using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Microsoft { namespace details { SourceReference::SourceReference(const std::shared_ptr& source) : m_source(source) {} std::shared_ptr SourceReference::GetReferenceSource() const { std::shared_ptr source = m_source.lock(); THROW_HR_IF(E_NOT_VALID_STATE, !source); return source; } } SQLiteIndexSource::SQLiteIndexSource( const SourceDetails& details, SQLiteIndex&& index, bool isInstalledSource, bool requireManifestHash) : m_details(details), m_isInstalled(isInstalledSource), m_index(std::move(index)), m_requireManifestHash(requireManifestHash) { std::vector cacheSources; cacheSources.push_back(m_details.Arg); if (!m_details.AlternateArg.empty()) { cacheSources.push_back(m_details.AlternateArg); } switch (m_index.GetVersion().MajorVersion) { case 1: m_manifestCache = std::make_shared(Caching::FileCache::Type::IndexV1_Manifest, m_details.Identifier, std::move(cacheSources)); break; case 2: m_manifestCache = std::make_shared(Caching::FileCache::Type::IndexV2_Manifest, m_details.Identifier, cacheSources); m_packageVersionDataCache = std::make_shared(Caching::FileCache::Type::IndexV2_PackageVersionData, m_details.Identifier, std::move(cacheSources)); break; default: THROW_WIN32(ERROR_NOT_SUPPORTED); } } const SourceDetails& SQLiteIndexSource::GetDetails() const { return m_details; } const std::string& SQLiteIndexSource::GetIdentifier() const { return m_details.Identifier; } SearchResult SQLiteIndexSource::Search(const SearchRequest& request) const { auto indexResults = m_index.Search(request); SearchResult result; std::shared_ptr sharedThis = NonConstSharedFromThis(); uint32_t majorVersion = m_index.GetVersion().MajorVersion; for (auto& indexResult : indexResults.Matches) { std::shared_ptr package; switch (majorVersion) { case 1: package = std::make_shared(sharedThis, indexResult.first, m_manifestCache, m_isInstalled); break; case 2: package = std::make_shared(sharedThis, indexResult.first, m_manifestCache, m_packageVersionDataCache, m_isInstalled); break; default: THROW_WIN32(ERROR_NOT_SUPPORTED); } result.Matches.emplace_back( std::move(package), std::move(indexResult.second)); } result.Truncated = indexResults.Truncated; return result; } void* SQLiteIndexSource::CastTo(ISourceType type) { if (type == SourceType) { return this; } return nullptr; } bool SQLiteIndexSource::IsSame(const SQLiteIndexSource* other) const { return (other && GetIdentifier() == other->GetIdentifier()); } std::shared_ptr SQLiteIndexSource::NonConstSharedFromThis() const { return const_cast(this)->shared_from_this(); } SQLiteIndexWriteableSource::SQLiteIndexWriteableSource(const SourceDetails& details, SQLiteIndex&& index, bool isInstalledSource) : SQLiteIndexSource(details, std::move(index), isInstalledSource) { } void* SQLiteIndexWriteableSource::CastTo(ISourceType type) { if (type == ISourceType::IMutablePackageSource) { return static_cast(this); } return SQLiteIndexSource::CastTo(type); } void SQLiteIndexWriteableSource::AddPackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { m_index.AddManifest(manifest, relativePath); } void SQLiteIndexWriteableSource::RemovePackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { m_index.RemoveManifest(manifest, relativePath); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/SQLiteIndex.h" #include "ISource.h" #include #include namespace AppInstaller::Repository::Microsoft { // A source that holds a SQLiteIndex and lock. struct SQLiteIndexSource : public std::enable_shared_from_this, public ISource { static constexpr ISourceType SourceType = ISourceType::SQLiteIndexSource; SQLiteIndexSource( const SourceDetails& details, SQLiteIndex&& index, bool isInstalledSource = false, bool requireManifestHash = false); SQLiteIndexSource(const SQLiteIndexSource&) = delete; SQLiteIndexSource& operator=(const SQLiteIndexSource&) = delete; SQLiteIndexSource(SQLiteIndexSource&&) = default; SQLiteIndexSource& operator=(SQLiteIndexSource&&) = default; ~SQLiteIndexSource() = default; // Get the source's details. const SourceDetails& GetDetails() const override; // Gets the source's identifier; a unique identifier independent of the name // that will not change between a remove/add or between additional adds. // Must be suitable for filesystem names. const std::string& GetIdentifier() const override; // Execute a search on the source. SearchResult Search(const SearchRequest& request) const override; // Casts to the requested type. void* CastTo(ISourceType type) override; // Gets the index. SQLiteIndex& GetIndex() { return m_index; } const SQLiteIndex& GetIndex() const { return m_index; } // Determines if the other source refers to the same as this. bool IsSame(const SQLiteIndexSource* other) const; bool RequireManifestHash() const { return m_requireManifestHash; } private: std::shared_ptr NonConstSharedFromThis() const; SourceDetails m_details; bool m_requireManifestHash; bool m_isInstalled; std::shared_ptr m_manifestCache; std::shared_ptr m_packageVersionDataCache; protected: SQLiteIndex m_index; }; // A source that holds a SQLiteIndex and lock. struct SQLiteIndexWriteableSource : public SQLiteIndexSource, public IMutablePackageSource { SQLiteIndexWriteableSource( const SourceDetails& details, SQLiteIndex&& index, bool isInstalledSource = false); // Casts to the requested type. void* CastTo(ISourceType type) override; // Adds a package version to the source. void AddPackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); // Removes a package version from the source. void RemovePackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); }; namespace details { // For the IPackage(Version) implementations that need to hold a weak reference to a SQLiteIndexSource. struct SourceReference { SourceReference(const std::shared_ptr& source); protected: std::shared_ptr GetReferenceSource() const; private: std::weak_ptr m_source; }; } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/SQLiteIndexSourceV1.h" #include using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Microsoft::details::V1 { // The IPackageVersion implementation for V1 index. struct PackageVersion : public SourceReference, public IPackageVersion { PackageVersion(const std::shared_ptr& source, SQLiteIndex::IdType manifestId, const std::shared_ptr& manifestCache) : SourceReference(source), m_manifestId(manifestId), m_manifestCache(manifestCache) {} // Inherited via IPackageVersion LocIndString GetProperty(PackageVersionProperty property) const override { switch (property) { case PackageVersionProperty::SourceIdentifier: return LocIndString{ GetReferenceSource()->GetIdentifier() }; case PackageVersionProperty::SourceName: return LocIndString{ GetReferenceSource()->GetDetails().Name }; default: // Values coming from the index will always be localized/independent. std::optional optValue = GetReferenceSource()->GetIndex().GetPropertyByPrimaryId(m_manifestId, property); return LocIndString{ optValue ? optValue.value() : std::string{} }; } } std::vector GetMultiProperty(PackageVersionMultiProperty property) const override { std::vector result; for (auto&& value : GetReferenceSource()->GetIndex().GetMultiPropertyByPrimaryId(m_manifestId, property)) { // Values coming from the index will always be localized/independent. result.emplace_back(std::move(value)); } return result; } Manifest::Manifest GetManifest() override { std::shared_ptr source = GetReferenceSource(); std::optional relativePathOpt = source->GetIndex().GetPropertyByPrimaryId(m_manifestId, PackageVersionProperty::RelativePath); THROW_HR_IF(E_NOT_SET, !relativePathOpt); std::optional manifestHashString = source->GetIndex().GetPropertyByPrimaryId(m_manifestId, PackageVersionProperty::ManifestSHA256Hash); THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE, source->RequireManifestHash() && !manifestHashString); SHA256::HashBuffer manifestSHA256; if (manifestHashString) { manifestSHA256 = SHA256::ConvertToBytes(manifestHashString.value()); } std::unique_ptr manifestStream = m_manifestCache->GetFile(ConvertToUTF16(relativePathOpt.value()), manifestSHA256); return Manifest::YamlParser::Create(ReadEntireStream(*manifestStream)); } Source GetSource() const override { return Source{ GetReferenceSource() }; } IPackageVersion::Metadata GetMetadata() const override { auto metadata = GetReferenceSource()->GetIndex().GetMetadataByManifestId(m_manifestId); IPackageVersion::Metadata result; for (auto&& data : metadata) { result.emplace(std::move(data)); } return result; } private: SQLiteIndex::IdType m_manifestId; std::shared_ptr m_manifestCache; }; SQLitePackage::SQLitePackage(const std::shared_ptr& source, SQLiteIndex::IdType idId, const std::shared_ptr& manifestCache, bool isInstalled) : SourceReference(source), m_idId(idId), m_manifestCache(manifestCache), m_isInstalled(isInstalled) {} LocIndString SQLitePackage::GetProperty(PackageProperty property) const { LocIndString result; std::shared_ptr truth = GetLatestVersion(); if (truth) { switch (property) { case PackageProperty::Id: return truth->GetProperty(PackageVersionProperty::Id); case PackageProperty::Name: return truth->GetProperty(PackageVersionProperty::Name); default: THROW_HR(E_UNEXPECTED); } } else { AICLI_LOG(Repo, Verbose, << "SQLitePackage: No manifest was found for the package with id# '" << m_idId << "'"); } return result; } std::vector SQLitePackage::GetMultiProperty(PackageMultiProperty property) const { std::shared_ptr source = GetReferenceSource(); std::vector result; PackageVersionMultiProperty mappedProperty = PackageMultiPropertyToPackageVersionMultiProperty(property); for (const auto& version : source->GetIndex().GetVersionKeysById(m_idId)) { for (auto&& string : source->GetIndex().GetMultiPropertyByPrimaryId(version.ManifestId, mappedProperty)) { auto itr = std::lower_bound(result.begin(), result.end(), string); if (itr == result.end() || itr->get() != string) { result.emplace(itr, std::move(string)); } } } return result; } std::vector SQLitePackage::GetVersionKeys() const { std::shared_ptr source = GetReferenceSource(); { auto sharedLock = m_versionKeysLock.lock_shared(); if (!m_versionKeys.empty()) { return m_versionKeys; } } auto exclusiveLock = m_versionKeysLock.lock_exclusive(); if (!m_versionKeys.empty()) { return m_versionKeys; } std::vector versions = source->GetIndex().GetVersionKeysById(m_idId); for (const auto& vk : versions) { std::string version = vk.VersionAndChannel.GetVersion().ToString(); std::string channel = vk.VersionAndChannel.GetChannel().ToString(); m_versionKeys.emplace_back(source->GetIdentifier(), version, channel); m_versionKeysMap.emplace(MapKey{ std::move(version), std::move(channel) }, vk.ManifestId); } return m_versionKeys; } std::shared_ptr SQLitePackage::GetLatestVersion() const { std::shared_ptr source = GetReferenceSource(); std::optional manifestId = source->GetIndex().GetManifestIdByKey(m_idId, {}, {}); if (manifestId) { return std::make_shared(source, manifestId.value(), m_manifestCache); } return {}; } std::shared_ptr SQLitePackage::GetVersion(const PackageVersionKey& versionKey) const { std::shared_ptr source = GetReferenceSource(); // Ensure that this key targets this (or any) source if (!versionKey.SourceId.empty() && versionKey.SourceId != source->GetIdentifier()) { return {}; } std::optional manifestId; { MapKey requested{ versionKey.Version, versionKey.Channel }; auto sharedLock = m_versionKeysLock.lock_shared(); auto itr = m_versionKeysMap.find(requested); if (itr != m_versionKeysMap.end()) { manifestId = itr->second; } } if (!manifestId) { manifestId = source->GetIndex().GetManifestIdByKey(m_idId, versionKey.Version, versionKey.Channel); } if (manifestId) { return std::make_shared(source, manifestId.value(), m_manifestCache); } return {}; } Source SQLitePackage::GetSource() const { return Source{ GetReferenceSource() }; } bool SQLitePackage::IsSame(const IPackage* other) const { const SQLitePackage* otherSQLite = PackageCast(other); if (otherSQLite) { return GetReferenceSource()->IsSame(otherSQLite->GetReferenceSource().get()) && m_idId == otherSQLite->m_idId; } return false; } const void* SQLitePackage::CastTo(IPackageType type) const { if (type == PackageType) { return this; } return nullptr; } std::shared_ptr SQLitePackage::GetInstalled() { return m_isInstalled ? shared_from_this() : std::shared_ptr{}; } std::vector> SQLitePackage::GetAvailable() { return m_isInstalled ? std::vector>{} : std::vector>{ shared_from_this() }; } bool SQLitePackage::MapKey::operator<(const MapKey& other) const { if (Version < other.Version) { return true; } else if (Version == other.Version) { return Channel < other.Channel; } else { return false; } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/SQLiteIndexSource.h" namespace AppInstaller::Repository::Microsoft::details::V1 { // The IPackage implementation for V1 index. struct SQLitePackage : public std::enable_shared_from_this, public SourceReference, public IPackage, public ICompositePackage { static constexpr IPackageType PackageType = IPackageType::SQLitePackage1; SQLitePackage(const std::shared_ptr& source, SQLiteIndex::IdType idId, const std::shared_ptr& manifestCache, bool isInstalled); // Inherited via IPackage Utility::LocIndString GetProperty(PackageProperty property) const; std::vector GetMultiProperty(PackageMultiProperty property) const override; std::vector GetVersionKeys() const override; std::shared_ptr GetLatestVersion() const override; std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const override; Source GetSource() const override; bool IsSame(const IPackage* other) const override; const void* CastTo(IPackageType type) const override; // Inherited via ICompositePackage std::shared_ptr GetInstalled() override; std::vector> GetAvailable() override; private: // Contains the information needed to map a version key to it's rows. struct MapKey { Utility::NormalizedString Version; Utility::NormalizedString Channel; bool operator<(const MapKey& other) const; }; SQLiteIndex::IdType m_idId; std::shared_ptr m_manifestCache; bool m_isInstalled; // To avoid removing const from the interface mutable wil::srwlock m_versionKeysLock; mutable std::vector m_versionKeys; mutable std::map m_versionKeysMap; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/SQLiteIndexSourceV2.h" #include using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Microsoft::details::V2 { // Get the relative path and hash for the package version data manifest. std::pair CreatePackageVersionDataRelativePath(const std::shared_ptr& source, SQLiteIndex::IdType packageRowId) { const SQLiteIndex& index = source->GetIndex(); std::string identifier = index.GetPropertyByPrimaryId(packageRowId, PackageVersionProperty::Id).value(); std::string hash = index.GetPropertyByPrimaryId(packageRowId, PackageVersionProperty::ManifestSHA256Hash).value(); std::filesystem::path relativePath = Manifest::PackageVersionDataManifest::GetRelativeDirectoryPath(identifier, hash) / Manifest::PackageVersionDataManifest::VersionManifestCompressedFileName(); return std::make_pair(std::move(relativePath), std::move(hash)); } // Gets package version data for the given package in the index. Manifest::PackageVersionDataManifest GetPackageVersionData(const std::shared_ptr& source, SQLiteIndex::IdType packageRowId, const Caching::FileCache& fileCache) { auto pathAndHash = CreatePackageVersionDataRelativePath(source, packageRowId); auto fileStream = fileCache.GetFile(pathAndHash.first, SHA256::ConvertToBytes(pathAndHash.second)); auto fileBytes = ReadEntireStreamAsByteArray(*fileStream); Manifest::PackageVersionDataManifest result; result.Deserialize(Manifest::PackageVersionDataManifest::CreateDecompressor().Decompress(fileBytes)); return result; } // The IPackageVersion implementation for V2 index. struct PackageVersion : public SourceReference, public IPackageVersion { PackageVersion( const std::shared_ptr& source, SQLiteIndex::IdType packageRowId, std::optional packageVersionData, const std::shared_ptr& manifestCache, const std::shared_ptr& packageVersionDataCache) : SourceReference(source), m_packageRowId(packageRowId), m_packageVersionData(std::move(packageVersionData)), m_manifestCache(manifestCache), m_packageVersionDataCache(packageVersionDataCache) {} // Inherited via IPackageVersion LocIndString GetProperty(PackageVersionProperty property) const override { switch (property) { case PackageVersionProperty::SourceIdentifier: return LocIndString{ GetReferenceSource()->GetIdentifier() }; case PackageVersionProperty::SourceName: return LocIndString{ GetReferenceSource()->GetDetails().Name }; case PackageVersionProperty::RelativePath: case PackageVersionProperty::ManifestSHA256Hash: { // These values can only come from the version data. EnsurePackageVersionData(); return GetPropertyFromVersionData(property); } break; case PackageVersionProperty::Publisher: { // These values can only come from the manifest. EnsureManifest(); return GetPropertyFromManifest(property); } break; case PackageVersionProperty::Id: case PackageVersionProperty::Name: case PackageVersionProperty::Moniker: { // These properties can come from the manifest or the index. // The index values will be for the latest version rather than this specific one though. auto sharedLock = m_versionAndManifestLock.lock_shared(); if (m_manifest) { return GetPropertyFromManifestWithLock(property); } else { return GetPropertyFromIndex(property); } } break; case PackageVersionProperty::Version: case PackageVersionProperty::Channel: case PackageVersionProperty::ArpMinVersion: case PackageVersionProperty::ArpMaxVersion: { // These properties can come from the manifest, version data, or the index. // The index values are only for the latest version, but we should always already have the version data // for any version that is not the latest. auto sharedLock = m_versionAndManifestLock.lock_shared(); if (m_manifest) { return GetPropertyFromManifestWithLock(property); } else if (m_packageVersionData) { return GetPropertyFromVersionDataWithLock(property); } else { return GetPropertyFromIndex(property); } } break; default: THROW_HR(E_UNEXPECTED); } } std::vector GetMultiProperty(PackageVersionMultiProperty property) const override { switch (property) { case PackageVersionMultiProperty::Locale: { // These values can only come from the manifest. EnsureManifest(); return GetMultiPropertyFromManifest(property); } break; case PackageVersionMultiProperty::PackageFamilyName: case PackageVersionMultiProperty::ProductCode: case PackageVersionMultiProperty::UpgradeCode: case PackageVersionMultiProperty::Name: case PackageVersionMultiProperty::Publisher: case PackageVersionMultiProperty::Tag: case PackageVersionMultiProperty::Command: { // These properties can come from the manifest or the index. // The index values will be for all versions rather than this specific one though. auto sharedLock = m_versionAndManifestLock.lock_shared(); if (m_manifest) { return GetMultiPropertyFromManifestWithLock(property); } else { return GetMultiPropertyFromIndex(property); } } break; default: THROW_HR(E_UNEXPECTED); } } Manifest::Manifest GetManifest() override { EnsureManifest(); auto sharedLock = m_versionAndManifestLock.lock_shared(); return m_manifest.value(); } Source GetSource() const override { return Source{ GetReferenceSource() }; } IPackageVersion::Metadata GetMetadata() const override { return {}; } private: // Ensures that the package version data is present. void EnsurePackageVersionData() const { { auto sharedLock = m_versionAndManifestLock.lock_shared(); if (m_packageVersionData) { return; } } auto exclusiveLock = m_versionAndManifestLock.lock_exclusive(); if (m_packageVersionData) { return; } Manifest::PackageVersionDataManifest packageVersionDataManifest = GetPackageVersionData(GetReferenceSource(), m_packageRowId, *m_packageVersionDataCache); for (const auto& versionData : packageVersionDataManifest.Versions()) { // We should only ever be looking for the latest version here. if (!m_packageVersionData || m_packageVersionData->Version < versionData.Version) { m_packageVersionData = versionData; } } } // Ensures that the manifest is present. void EnsureManifest() const { { auto sharedLock = m_versionAndManifestLock.lock_shared(); if (m_manifest) { return; } } // We will need the package version data to get the manifest. EnsurePackageVersionData(); auto exclusiveLock = m_versionAndManifestLock.lock_exclusive(); if (m_manifest) { return; } std::unique_ptr manifestStream = m_manifestCache->GetFile(ConvertToUTF16(m_packageVersionData->ManifestRelativePath), SHA256::ConvertToBytes(m_packageVersionData->ManifestHash)); m_manifest = Manifest::YamlParser::Create(ReadEntireStream(*manifestStream)); m_manifest->ApplyLocale(); } LocIndString GetPropertyFromIndex(PackageVersionProperty property) const { switch (property) { case PackageVersionProperty::Id: case PackageVersionProperty::Name: case PackageVersionProperty::Moniker: case PackageVersionProperty::Version: case PackageVersionProperty::ArpMinVersion: case PackageVersionProperty::ArpMaxVersion: { // Values coming from the index will always be localized/independent. std::optional optValue = GetReferenceSource()->GetIndex().GetPropertyByPrimaryId(m_packageRowId, property); return LocIndString{ optValue ? optValue.value() : std::string{} }; } default: return {}; } } LocIndString GetPropertyFromVersionData(PackageVersionProperty property) const { auto sharedLock = m_versionAndManifestLock.lock_shared(); return GetPropertyFromVersionDataWithLock(property); } LocIndString GetPropertyFromVersionDataWithLock(PackageVersionProperty property) const { std::string result; switch (property) { case PackageVersionProperty::RelativePath: result = m_packageVersionData->ManifestRelativePath; break; case PackageVersionProperty::ManifestSHA256Hash: result = m_packageVersionData->ManifestHash; break; case PackageVersionProperty::Version: result = m_packageVersionData->Version.ToString(); break; case PackageVersionProperty::ArpMinVersion: result = m_packageVersionData->ArpMinVersion.value_or(""); break; case PackageVersionProperty::ArpMaxVersion: result = m_packageVersionData->ArpMaxVersion.value_or(""); break; } return LocIndString{ std::move(result) }; } LocIndString GetPropertyFromManifest(PackageVersionProperty property) const { auto sharedLock = m_versionAndManifestLock.lock_shared(); return GetPropertyFromManifestWithLock(property); } LocIndString GetPropertyFromManifestWithLock(PackageVersionProperty property) const { std::string result; switch (property) { case PackageVersionProperty::Publisher: result = m_manifest->CurrentLocalization.Get(); break; case PackageVersionProperty::Id: result = m_manifest->Id; break; case PackageVersionProperty::Name: result = m_manifest->CurrentLocalization.Get(); break; case PackageVersionProperty::Moniker: result = m_manifest->Moniker; break; case PackageVersionProperty::Version: result = m_manifest->Version; break; case PackageVersionProperty::Channel: result = m_manifest->Channel; break; case PackageVersionProperty::ArpMinVersion: { auto versionRange = m_manifest->GetArpVersionRange(); if (!versionRange.IsEmpty()) { result = versionRange.GetMinVersion().ToString(); } } break; case PackageVersionProperty::ArpMaxVersion: { auto versionRange = m_manifest->GetArpVersionRange(); if (!versionRange.IsEmpty()) { result = versionRange.GetMaxVersion().ToString(); } } break; } return LocIndString{ std::move(result) }; } std::vector GetMultiPropertyFromIndex(PackageVersionMultiProperty property) const { std::vector result; for (auto&& value : GetReferenceSource()->GetIndex().GetMultiPropertyByPrimaryId(m_packageRowId, property)) { // Values coming from the index will always be localized/independent. result.emplace_back(std::move(value)); } return result; } std::vector GetMultiPropertyFromManifest(PackageVersionMultiProperty property) const { auto sharedLock = m_versionAndManifestLock.lock_shared(); return GetMultiPropertyFromManifestWithLock(property); } std::vector GetMultiPropertyFromManifestWithLock(PackageVersionMultiProperty property) const { std::vector intermediate; switch (property) { case PackageVersionMultiProperty::PackageFamilyName: intermediate = m_manifest->GetPackageFamilyNames(); break; case PackageVersionMultiProperty::ProductCode: intermediate = m_manifest->GetProductCodes(); break; case PackageVersionMultiProperty::UpgradeCode: intermediate = m_manifest->GetUpgradeCodes(); break; case PackageVersionMultiProperty::Name: intermediate = m_manifest->GetPackageNames(); break; case PackageVersionMultiProperty::Publisher: intermediate = m_manifest->GetPublishers(); break; case PackageVersionMultiProperty::Locale: for (const auto& localization : m_manifest->Localizations) { intermediate.emplace_back(localization.Locale); } break; case PackageVersionMultiProperty::Tag: intermediate = m_manifest->GetAggregatedTags(); break; case PackageVersionMultiProperty::Command: intermediate = m_manifest->GetAggregatedCommands(); break; } std::vector result; for (auto&& value : intermediate) { // Values coming from the manifest will always be localized/independent. result.emplace_back(std::move(value)); } return result; } SQLiteIndex::IdType m_packageRowId; mutable wil::srwlock m_versionAndManifestLock; mutable std::optional m_packageVersionData; mutable std::optional m_manifest; std::shared_ptr m_manifestCache; std::shared_ptr m_packageVersionDataCache; }; SQLitePackage::SQLitePackage( const std::shared_ptr& source, SQLiteIndex::IdType packageRowId, const std::shared_ptr& manifestCache, const std::shared_ptr& packageVersionDataCache, bool isInstalled) : SourceReference(source), m_packageRowId(packageRowId), m_manifestCache(manifestCache), m_packageVersionDataCache(packageVersionDataCache), m_isInstalled(isInstalled) {} LocIndString SQLitePackage::GetProperty(PackageProperty property) const { std::optional result; std::shared_ptr source = GetReferenceSource(); switch (property) { case PackageProperty::Id: result = source->GetIndex().GetPropertyByPrimaryId(m_packageRowId, PackageVersionProperty::Id); break; case PackageProperty::Name: result = source->GetIndex().GetPropertyByPrimaryId(m_packageRowId, PackageVersionProperty::Name); break; default: THROW_HR(E_UNEXPECTED); } return LocIndString{ result ? std::move(result).value() : std::string{} }; } std::vector SQLitePackage::GetMultiProperty(PackageMultiProperty property) const { std::vector result; for (auto&& value : GetReferenceSource()->GetIndex().GetMultiPropertyByPrimaryId(m_packageRowId, PackageMultiPropertyToPackageVersionMultiProperty(property))) { // Values coming from the index will always be localized/independent. result.emplace_back(std::move(value)); } return result; } std::vector SQLitePackage::GetVersionKeys() const { std::shared_ptr source = GetReferenceSource(); { auto sharedLock = m_versionKeysLock.lock_shared(); if (!m_versionKeys.empty()) { return m_versionKeys; } } EnsurePackageVersionData(source); auto sharedLock = m_versionKeysLock.lock_shared(); return m_versionKeys; } std::shared_ptr SQLitePackage::GetLatestVersion() const { std::shared_ptr source = GetReferenceSource(); auto sharedLock = m_versionKeysLock.lock_shared(); return std::make_shared(source, m_packageRowId, m_latestVersionData, m_manifestCache, m_packageVersionDataCache); } std::shared_ptr SQLitePackage::GetVersion(const PackageVersionKey& versionKey) const { std::shared_ptr source = GetReferenceSource(); // Ensure that this key targets this (or any) source if (!versionKey.SourceId.empty() && versionKey.SourceId != source->GetIdentifier()) { return {}; } std::optional versionData; // Check for a latest version request. if (versionKey.IsDefaultLatest()) { auto sharedLock = m_versionKeysLock.lock_shared(); return std::make_shared(source, m_packageRowId, m_latestVersionData, m_manifestCache, m_packageVersionDataCache); } EnsurePackageVersionData(source); { MapKey requested{ versionKey.Version, versionKey.Channel }; auto sharedLock = m_versionKeysLock.lock_shared(); auto itr = m_versionKeysMap.find(requested); if (itr != m_versionKeysMap.end()) { versionData = itr->second; } } if (versionData) { return std::make_shared(source, m_packageRowId, std::move(versionData), m_manifestCache, m_packageVersionDataCache); } return {}; } Source SQLitePackage::GetSource() const { return Source{ GetReferenceSource() }; } bool SQLitePackage::IsSame(const IPackage* other) const { const SQLitePackage* otherSQLite = PackageCast(other); if (otherSQLite) { return GetReferenceSource()->IsSame(otherSQLite->GetReferenceSource().get()) && m_packageRowId == otherSQLite->m_packageRowId; } return false; } const void* SQLitePackage::CastTo(IPackageType type) const { if (type == PackageType) { return this; } return nullptr; } std::shared_ptr SQLitePackage::GetInstalled() { return m_isInstalled ? shared_from_this() : std::shared_ptr{}; } std::vector> SQLitePackage::GetAvailable() { return m_isInstalled ? std::vector>{} : std::vector>{ shared_from_this() }; } bool SQLitePackage::MapKey::operator<(const MapKey& other) const { if (Version < other.Version) { return true; } else if (Version == other.Version) { return Channel < other.Channel; } else { return false; } } // Ensures that we have the package version data present. void SQLitePackage::EnsurePackageVersionData(const std::shared_ptr& source) const { { auto sharedLock = m_versionKeysLock.lock_shared(); if (!m_versionKeys.empty()) { return; } } auto exclusiveLock = m_versionKeysLock.lock_exclusive(); if (!m_versionKeys.empty()) { return; } Manifest::PackageVersionDataManifest packageVersionDataManifest = GetPackageVersionData(source, m_packageRowId, *m_packageVersionDataCache); for (const auto& versionData : packageVersionDataManifest.Versions()) { std::string version = versionData.Version.ToString(); std::string channel; m_versionKeys.emplace_back(source->GetIdentifier(), version, channel); m_versionKeysMap.emplace(MapKey{ std::move(version), std::move(channel) }, versionData); if (!m_latestVersionData || m_latestVersionData->Version < versionData.Version) { m_latestVersionData = versionData; } } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/SQLiteIndexSource.h" #include namespace AppInstaller::Repository::Microsoft::details::V2 { // The IPackage implementation for V2 index. struct SQLitePackage : public std::enable_shared_from_this, public SourceReference, public IPackage, public ICompositePackage { static constexpr IPackageType PackageType = IPackageType::SQLitePackage2; SQLitePackage( const std::shared_ptr& source, SQLiteIndex::IdType packageRowId, const std::shared_ptr& manifestCache, const std::shared_ptr& packageVersionDataCache, bool isInstalled); // Inherited via IPackage Utility::LocIndString GetProperty(PackageProperty property) const; std::vector GetMultiProperty(PackageMultiProperty property) const override; std::vector GetVersionKeys() const override; std::shared_ptr GetLatestVersion() const override; std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const override; Source GetSource() const override; bool IsSame(const IPackage* other) const override; const void* CastTo(IPackageType type) const override; // Inherited via ICompositePackage std::shared_ptr GetInstalled() override; std::vector> GetAvailable() override; private: // Contains the information needed to map a version key to it's rows. struct MapKey { Utility::Version Version; Utility::NormalizedString Channel; bool operator<(const MapKey& other) const; }; // Ensures that we have the package version data present. void EnsurePackageVersionData(const std::shared_ptr& source) const; SQLiteIndex::IdType m_packageRowId; std::shared_ptr m_manifestCache; std::shared_ptr m_packageVersionDataCache; bool m_isInstalled; // To avoid removing const from the interface mutable wil::srwlock m_versionKeysLock; mutable std::vector m_versionKeys; mutable std::map m_versionKeysMap; mutable std::optional m_latestVersionData; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ChannelTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToOneTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; struct ChannelTableInfo { inline static constexpr std::string_view TableName() { return "channels"sv; } inline static constexpr std::string_view ValueName() { return "channel"sv; } }; } // The table for Channel. using ChannelTable = OneToOneTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/CommandsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToManyTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; struct CommandsTableInfo { inline static constexpr std::string_view TableName() { return "commands"sv; } inline static constexpr std::string_view ValueName() { return "command"sv; } }; } // The table for Commands. using CommandsTable = OneToManyTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/IdTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToOneTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; struct IdTableInfo { inline static constexpr std::string_view TableName() { return "ids"sv; } inline static constexpr std::string_view ValueName() { return "id"sv; } }; } // The table for Id. using IdTable = OneToOneTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_0/SearchResultsTable.h" #include "Microsoft/Schema/1_0/OneToManyTable.h" #include #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public ISQLiteIndex { // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; std::pair UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; SQLite::rowid_t RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest) override; void RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) override; void PrepareForPackaging(SQLite::Connection& connection) override; bool CheckConsistency(const SQLite::Connection& connection, bool log) const override; SearchResult Search(const SQLite::Connection& connection, const SearchRequest& request) const override; std::optional GetPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionProperty property) const override; std::vector GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const override; std::optional GetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) const override; std::optional GetManifestIdByManifest(const SQLite::Connection& connection, const Manifest::Manifest& manifest) const override; std::vector GetVersionKeysById(const SQLite::Connection& connection, SQLite::rowid_t id) const override; // Version 1.1 MetadataResult GetMetadataByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId) const override; void SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value) override; // Version 1.2 Utility::NormalizedName NormalizeName(std::string_view name, std::string_view publisher) const override; // Version 1.4 Get all the dependencies for a specific manifest. std::set> GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId) const override; std::vector> GetDependentsById(const SQLite::Connection& connection, AppInstaller::Manifest::string_t packageId) const override; // Version 1.7 void DropTables(SQLite::Connection& connection) override; // Version 2.0 bool MigrateFrom(SQLite::Connection& connection, const ISQLiteIndex* current) override; protected: virtual bool NotNeeded(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id) const; // Creates the search results table. virtual std::unique_ptr CreateSearchResultsTable(const SQLite::Connection& connection) const; // Executes all relevant searches for the query. virtual void PerformQuerySearch(SearchResultsTable& resultsTable, const RequestMatch& query) const; // Gets a property already knowing that the manifest id is valid. virtual std::optional GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const; // Gets the one to many table schema to use. virtual OneToManyTableSchema GetOneToManyTableSchema() const; // Force the database to shrink the file size. // This *must* be done outside of an active transaction. void Vacuum(const SQLite::Connection& connection); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_0/Interface.h" #include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/NameTable.h" #include "Microsoft/Schema/1_0/MonikerTable.h" #include "Microsoft/Schema/1_0/VersionTable.h" #include "Microsoft/Schema/1_0/ChannelTable.h" #include "Microsoft/Schema/1_0/PathPartTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_0/TagsTable.h" #include "Microsoft/Schema/1_0/CommandsTable.h" #include "Microsoft/Schema/1_0/SearchResultsTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace { // Gets an existing manifest by its rowid., if it exists. std::optional GetExistingManifestId(const SQLite::Connection& connection, const Manifest::Manifest& manifest) { std::optional idId = IdTable::SelectIdByValue(connection, manifest.Id, true); if (!idId) { AICLI_LOG(Repo, Verbose, << "Did not find an Id { " << manifest.Id << " }"); return {}; } std::optional versionId = VersionTable::SelectIdByValue(connection, manifest.Version, true); if (!versionId) { AICLI_LOG(Repo, Verbose, << "Did not find a Version { " << manifest.Version << " }"); return {}; } std::optional channelId = ChannelTable::SelectIdByValue(connection, manifest.Channel, true); if (!channelId) { AICLI_LOG(Repo, Verbose, << "Did not find a Channel { " << manifest.Channel << " }"); return {}; } auto result = ManifestTable::SelectByValueIds(connection, { idId.value(), versionId.value(), channelId.value() }); if (!result) { AICLI_LOG(Repo, Verbose, << "Did not find a manifest row for { " << manifest.Id << ", " << manifest.Version << ", " << manifest.Channel << " }"); } return result; } // Gets a manifest id by the given key values. std::optional StaticGetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version = "", std::string_view channel = "") { std::optional channelIdOpt = ChannelTable::SelectIdByValue(connection, channel, true); if (!channelIdOpt && !channel.empty()) { AICLI_LOG(Repo, Info, << "Did not find a Channel { " << channel << " }"); return {}; } std::optional versionIdOpt; std::vector> versionStrings; if (channelIdOpt) { versionStrings = ManifestTable::GetAllValuesByIds(connection, { id, channelIdOpt.value() }); } else { versionStrings = ManifestTable::GetAllValuesByIds(connection, { id }); } if (versionStrings.empty()) { AICLI_LOG(Repo, Info, << "Did not find any Versions { " << id << ", " << channel << " }"); return {}; } // Convert the strings to Versions and sort them struct VersionAndRow { SQLite::rowid_t Row = 0; Utility::Version Version; bool operator<(const VersionAndRow& other) const { return Version < other.Version; } }; std::vector versions; for (auto& v : versionStrings) { versions.emplace_back(VersionAndRow{ v.first, std::move(v.second) }); } std::sort(versions.begin(), versions.end()); if (version.empty()) { // Get the last version in the list (the highest version) versionIdOpt = versions.back().Row; } else { VersionAndRow requested; requested.Version = Utility::Version{ std::string(version) }; auto itr = std::lower_bound(versions.begin(), versions.end(), requested); if (itr != versions.end() && itr->Version == requested.Version) { versionIdOpt = itr->Row; } } if (!versionIdOpt) { AICLI_LOG(Repo, Info, << "Did not find a Version for { " << version << " }"); return {}; } if (channelIdOpt) { return ManifestTable::SelectByValueIds(connection, { id, versionIdOpt.value(), channelIdOpt.value() }); } else { return ManifestTable::SelectByValueIds(connection, { id, versionIdOpt.value() }); } } bool NotNeededInternal(const SQLite::Connection& connection, std::string_view, std::string_view valueName, SQLite::rowid_t id) { return !ManifestTable::IsValueReferenced(connection, valueName, id); } // Updates the manifest column and related table based on the given value. template void UpdateManifestValueById(SQLite::Connection& connection, const typename Table::value_t& value, SQLite::rowid_t manifestId, bool overwriteLikeMatch = false) { auto [oldValueId] = ManifestTable::GetIdsById(connection, manifestId); SQLite::rowid_t newValueId = Table::EnsureExists(connection, value, overwriteLikeMatch); ManifestTable::UpdateValueIdById
(connection, manifestId, newValueId); if (NotNeededInternal(connection, Table::TableName(), Table::ValueName(), oldValueId)) { Table::DeleteById(connection, oldValueId); } } } SQLite::Version Interface::GetVersion() const { return { 1, 0 }; } void Interface::CreateTables(SQLite::Connection& connection, CreateOptions options) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createtables_v1_0"); IdTable::Create_deprecated(connection); NameTable::Create_deprecated(connection); MonikerTable::Create_deprecated(connection); VersionTable::Create_deprecated(connection); ChannelTable::Create_deprecated(connection); PathPartTable::Create_deprecated(connection); ManifestTable::Create_deprecated(connection, { { IdTable::ValueName(), true, false }, { NameTable::ValueName(), false, false }, { MonikerTable::ValueName(), false, false }, { VersionTable::ValueName(), true, false }, { ChannelTable::ValueName(), true, false }, { PathPartTable::ValueName(), false, WI_IsFlagClear(options, CreateOptions::SupportPathless) } }); TagsTable::Create(connection, GetOneToManyTableSchema()); CommandsTable::Create(connection, GetOneToManyTableSchema()); savepoint.Commit(); } SQLite::rowid_t Interface::AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { auto manifestResult = GetExistingManifestId(connection, manifest); // If this manifest is already present, we can't add it. THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), manifestResult.has_value()); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addmanifest_v1_0"); auto [pathAdded, pathLeafId] = PathPartTable::EnsurePathExists(connection, relativePath, true); // If we get false from the function, this manifest path already exists in the index. THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), relativePath && !pathAdded); // Ensure that all of the 1:1 data exists. SQLite::rowid_t idId = IdTable::EnsureExists(connection, manifest.Id, true); SQLite::rowid_t nameId = NameTable::EnsureExists(connection, manifest.DefaultLocalization.Get()); SQLite::rowid_t monikerId = MonikerTable::EnsureExists(connection, manifest.Moniker); SQLite::rowid_t versionId = VersionTable::EnsureExists(connection, manifest.Version); SQLite::rowid_t channelId = ChannelTable::EnsureExists(connection, manifest.Channel); // Insert the manifest entry. SQLite::rowid_t manifestId = ManifestTable::Insert(connection, { { IdTable::ValueName(), idId}, { NameTable::ValueName(), nameId }, { MonikerTable::ValueName(), monikerId }, { VersionTable::ValueName(), versionId }, { ChannelTable::ValueName(), channelId }, { PathPartTable::ValueName(), pathLeafId } }); // Add all of the 1:N data. TagsTable::EnsureExistsAndInsert(connection, manifest.GetAggregatedTags(), manifestId); CommandsTable::EnsureExistsAndInsert(connection, manifest.GetAggregatedCommands(), manifestId); savepoint.Commit(); return manifestId; } std::pair Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { auto manifestResult = GetExistingManifestId(connection, manifest); // If the manifest doesn't actually exist, fail the update. THROW_HR_IF(E_NOT_SET, !manifestResult); SQLite::rowid_t manifestId = manifestResult.value(); auto [idInIndex, nameInIndex, monikerInIndex, versionInIndex, channelInIndex] = ManifestTable::GetValuesById(connection, manifestId); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updatemanifest_v1_0"); bool indexModified = false; // Id, Version, and Channel may have changed casing. If so, they too need to be updated. if (idInIndex != manifest.Id) { UpdateManifestValueById(connection, manifest.Id, manifestId, true); indexModified = true; } if (versionInIndex != manifest.Version) { UpdateManifestValueById(connection, manifest.Version, manifestId); indexModified = true; } if (channelInIndex != manifest.Channel) { UpdateManifestValueById(connection, manifest.Channel, manifestId); indexModified = true; } auto packageName = manifest.DefaultLocalization.Get(); if (nameInIndex != packageName) { UpdateManifestValueById(connection, packageName, manifestId); indexModified = true; } if (monikerInIndex != manifest.Moniker) { UpdateManifestValueById(connection, manifest.Moniker, manifestId); indexModified = true; } // Update path table if necessary auto [existingPathLeafId] = ManifestTable::GetIdsById(connection, manifestId); auto [pathAdded, newPathLeafId] = PathPartTable::EnsurePathExists(connection, relativePath, true); if (relativePath && pathAdded) { // Path was added, so we need to update the manifest table and delete the old path ManifestTable::UpdateValueIdById(connection, manifestId, newPathLeafId); PathPartTable::RemovePathById(connection, existingPathLeafId); indexModified = true; } else { // The path already existed, so it must either match the existing manifest path or it is an error THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingPathLeafId != newPathLeafId); } // Update all 1:N tables as necessary indexModified = TagsTable::UpdateIfNeededByManifestId(connection, manifest.GetAggregatedTags(), manifestId) || indexModified; indexModified = CommandsTable::UpdateIfNeededByManifestId(connection, manifest.GetAggregatedCommands(), manifestId) || indexModified; savepoint.Commit(); return { indexModified, manifestId }; } SQLite::rowid_t Interface::RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest) { auto manifestResult = GetExistingManifestId(connection, manifest); // If the manifest doesn't actually exist, fail the remove. THROW_HR_IF(E_NOT_SET, !manifestResult); RemoveManifestById(connection, manifestResult.value()); return manifestResult.value(); } void Interface::RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) { // Get the ids of the values from the manifest table auto [idId, nameId, monikerId, versionId, channelId, pathLeafId] = ManifestTable::GetIdsById(connection, manifestId); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "RemoveManifestById_v1_0"); // Remove the manifest row ManifestTable::DeleteById(connection, manifestId); // Remove all of the 1:1 data that is no longer referenced. if (NotNeeded(connection, IdTable::TableName(), IdTable::ValueName(), idId)) { IdTable::DeleteById(connection, idId); } if (NotNeeded(connection, NameTable::TableName(), NameTable::ValueName(), nameId)) { NameTable::DeleteById(connection, nameId); } if (NotNeeded(connection, MonikerTable::TableName(), MonikerTable::ValueName(), monikerId)) { MonikerTable::DeleteById(connection, monikerId); } if (NotNeeded(connection, VersionTable::TableName(), VersionTable::ValueName(), versionId)) { VersionTable::DeleteById(connection, versionId); } if (NotNeeded(connection, ChannelTable::TableName(), ChannelTable::ValueName(), channelId)) { ChannelTable::DeleteById(connection, channelId); } // Remove the path PathPartTable::RemovePathById(connection, pathLeafId); // Remove all of the 1:N data that is no longer referenced. TagsTable::DeleteIfNotNeededByManifestId(connection, manifestId); CommandsTable::DeleteIfNotNeededByManifestId(connection, manifestId); savepoint.Commit(); } bool Interface::NotNeeded(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id) const { return NotNeededInternal(connection, tableName, valueName, id); } void Interface::PrepareForPackaging(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareforpackaging_v1_0"); IdTable::PrepareForPackaging_deprecated(connection); NameTable::PrepareForPackaging_deprecated(connection); MonikerTable::PrepareForPackaging_deprecated(connection); VersionTable::PrepareForPackaging_deprecated(connection); ChannelTable::PrepareForPackaging_deprecated(connection); PathPartTable::PrepareForPackaging_deprecated(connection); ManifestTable::PrepareForPackaging_deprecated(connection, { VersionTable::ValueName(), ChannelTable::ValueName(), PathPartTable::ValueName(), }); TagsTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), false, false); CommandsTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), false, false); savepoint.Commit(); Vacuum(connection); } bool Interface::CheckConsistency(const SQLite::Connection& connection, bool log) const { bool result = true; #define AICLI_CHECK_CONSISTENCY(_check_) \ if (result || log) \ { \ result = _check_ && result; \ } // Check the manifest table references to it's 1:1 tables AICLI_CHECK_CONSISTENCY(ManifestTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(ManifestTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(ManifestTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(ManifestTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(ManifestTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(ManifestTable::CheckConsistency(connection, log)); // Check the 1:1 tables' consistency AICLI_CHECK_CONSISTENCY(IdTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(NameTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(MonikerTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(VersionTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(ChannelTable::CheckConsistency(connection, log)); // Check the pathparts table for consistency AICLI_CHECK_CONSISTENCY(PathPartTable::CheckConsistency(connection, log)); // Check the 1:N map tables for consistency AICLI_CHECK_CONSISTENCY(TagsTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(CommandsTable::CheckConsistency(connection, log)); #undef AICLI_CHECK_CONSISTENCY return result; } ISQLiteIndex::SearchResult Interface::Search(const SQLite::Connection& connection, const SearchRequest& request) const { if (request.IsForEverything()) { std::vector ids = IdTable::GetAllRowIds(connection, request.MaximumResults); SearchResult result; for (SQLite::rowid_t id : ids) { result.Matches.emplace_back(std::make_pair(id, PackageMatchFilter(PackageMatchField::Id, MatchType::Wildcard))); } result.Truncated = (request.MaximumResults && IdTable::GetCount(connection) > request.MaximumResults); return result; } // First phase, create the search results table and populate it with the initial results. // If the Query is provided, we search across many fields and put results in together. // If Inclusions has fields, we add these to the data. // If neither is defined, we take the first filter and use it as the initial results search. std::unique_ptr resultsTable = CreateSearchResultsTable(connection); bool inclusionsAttempted = false; if (request.Query) { // Perform searches across multiple tables to populate the initial results. PerformQuerySearch(*resultsTable.get(), request.Query.value()); inclusionsAttempted = true; } if (!request.Inclusions.empty()) { for (auto include : request.Inclusions) { for (MatchType match : GetDefaultMatchTypeOrder(include.Type)) { include.Type = match; resultsTable->SearchOnField(include); } } inclusionsAttempted = true; } size_t filterIndex = 0; if (!inclusionsAttempted) { THROW_HR_IF(E_UNEXPECTED, request.Filters.empty()); // Perform search for just the field matching the first filter PackageMatchFilter filter = request.Filters[0]; for (MatchType match : GetDefaultMatchTypeOrder(filter.Type)) { filter.Type = match; resultsTable->SearchOnField(filter); } // Skip the filter as we already know everything matches filterIndex = 1; } // Remove any duplicate manifest entries resultsTable->RemoveDuplicateManifestRows(); // Second phase, for remaining filters, flag matching search results, then remove unflagged values. for (size_t i = filterIndex; i < request.Filters.size(); ++i) { PackageMatchFilter filter = request.Filters[i]; resultsTable->PrepareToFilter(); for (MatchType match : GetDefaultMatchTypeOrder(filter.Type)) { filter.Type = match; resultsTable->FilterOnField(filter); } resultsTable->CompleteFilter(); } return resultsTable->GetSearchResults(request.MaximumResults); } std::optional Interface::GetPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionProperty property) const { return GetPropertyByManifestIdInternal(connection, primaryId, property); } std::vector Interface::GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const { switch (property) { case PackageVersionMultiProperty::Tag: return TagsTable::GetValuesByManifestId(connection, primaryId); case PackageVersionMultiProperty::Command: return CommandsTable::GetValuesByManifestId(connection, primaryId); default: return {}; } } std::optional Interface::GetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) const { return StaticGetManifestIdByKey(connection, id, version, channel); } std::optional Interface::GetManifestIdByManifest(const SQLite::Connection& connection, const Manifest::Manifest& manifest) const { return GetExistingManifestId(connection, manifest); } std::set> Interface::GetDependenciesByManifestRowId(const SQLite::Connection&, SQLite::rowid_t) const { return {}; } std::vector> Interface::GetDependentsById(const SQLite::Connection&, AppInstaller::Manifest::string_t) const { return {}; } void Interface::DropTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "drop_tables_v1_0"); IdTable::Drop(connection); NameTable::Drop(connection); MonikerTable::Drop(connection); VersionTable::Drop(connection); ChannelTable::Drop(connection); PathPartTable::Drop(connection); ManifestTable::Drop(connection); TagsTable::Drop(connection); CommandsTable::Drop(connection); savepoint.Commit(); } bool Interface::MigrateFrom(SQLite::Connection&, const ISQLiteIndex*) { return false; } std::vector Interface::GetVersionKeysById(const SQLite::Connection& connection, SQLite::rowid_t id) const { auto versionsAndChannels = ManifestTable::GetAllValuesById(connection, id); std::vector result; result.reserve(versionsAndChannels.size()); for (auto&& vac : versionsAndChannels) { result.emplace_back(ISQLiteIndex::VersionKey{ Utility::VersionAndChannel{ Utility::Version{ std::move(std::get<1>(vac)) }, Utility::Channel{ std::move(std::get<2>(vac)) } }, std::get<0>(vac) }); } std::sort(result.begin(), result.end()); return result; } ISQLiteIndex::MetadataResult Interface::GetMetadataByManifestId(const SQLite::Connection&, SQLite::rowid_t) const { return {}; } void Interface::SetMetadataByManifestId(SQLite::Connection&, SQLite::rowid_t, PackageVersionMetadata, std::string_view) { } Utility::NormalizedName Interface::NormalizeName(std::string_view name, std::string_view publisher) const { Utility::NormalizedName result; result.Name(name); result.Publisher(publisher); return result; } std::unique_ptr Interface::CreateSearchResultsTable(const SQLite::Connection& connection) const { return std::make_unique(connection); } void Interface::PerformQuerySearch(SearchResultsTable& resultsTable, const RequestMatch& query) const { // Arbitrary values to create a reusable filter with the given value. PackageMatchFilter filter(PackageMatchField::Id, MatchType::Exact, query.Value); for (MatchType match : GetDefaultMatchTypeOrder(query.Type)) { filter.Type = match; for (auto field : { PackageMatchField::Id, PackageMatchField::Name, PackageMatchField::Moniker, PackageMatchField::Command, PackageMatchField::Tag }) { filter.Field = field; resultsTable.SearchOnField(filter); } } } std::optional Interface::GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const { switch (property) { case AppInstaller::Repository::PackageVersionProperty::Id: return ManifestTable::GetValueById(connection, manifestId); case AppInstaller::Repository::PackageVersionProperty::Name: return ManifestTable::GetValueById(connection, manifestId); case AppInstaller::Repository::PackageVersionProperty::Version: return ManifestTable::GetValueById(connection, manifestId); case AppInstaller::Repository::PackageVersionProperty::Channel: return ManifestTable::GetValueById(connection, manifestId); case AppInstaller::Repository::PackageVersionProperty::RelativePath: return PathPartTable::GetPathById(connection, std::get<0>(ManifestTable::GetIdsById(connection, manifestId))); case AppInstaller::Repository::PackageVersionProperty::Moniker: return ManifestTable::GetValueById(connection, manifestId); default: return {}; } } OneToManyTableSchema Interface::GetOneToManyTableSchema() const { return OneToManyTableSchema::Version_1_0; } void Interface::Vacuum(const SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Vacuum(); builder.Execute(connection); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestTable.h" #include #include "OneToManyTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { using namespace std::string_view_literals; static constexpr std::string_view s_ManifestTable_Table_Name = "manifest"sv; static constexpr std::string_view s_ManifestTable_Index_Separator = "_"sv; static constexpr std::string_view s_ManifestTable_Index_Suffix = "_index"sv; namespace details { std::optional ManifestTableSelectByValueIds( const SQLite::Connection& connection, std::initializer_list values, std::initializer_list ids) { THROW_HR_IF(E_INVALIDARG, values.size() != ids.size()); SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::RowIDName).From(s_ManifestTable_Table_Name); bool isFirst = true; for (const auto& value : values) { if (isFirst) { builder.Where(value).Equals(SQLite::Builder::Unbound); isFirst = false; } else { builder.And(value).Equals(SQLite::Builder::Unbound); } } builder.Limit(1); SQLite::Statement select = builder.Prepare(connection); int bindIndex = 0; for (const auto& id : ids) { select.Bind(++bindIndex, id); } if (select.Step()) { return select.GetColumn(0); } else { return {}; } } SQLite::Statement ManifestTableGetIdsById_Statement( const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list values, bool stepAndVerify) { SQLite::Builder::StatementBuilder builder; builder.Select(values).From(s_ManifestTable_Table_Name).Where(SQLite::RowIDName).Equals(id); SQLite::Statement result = builder.Prepare(connection); if (stepAndVerify) { THROW_HR_IF(E_NOT_SET, !result.Step()); } return result; } // Creates a statement and executes it, select the actual values for a given manifest id. // Ex. // SELECT [ids].[id] FROM [manifest] // JOIN [ids] ON [manifest].[id] = [ids].[rowid] // WHERE [manifest].[rowid] = 1 SQLite::Statement ManifestTableGetValuesById_Statement( const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list columns, std::initializer_list manifestColumnNames, bool stepAndVerify) { THROW_HR_IF(E_UNEXPECTED, manifestColumnNames.size() != columns.size()); using QCol = SQLite::Builder::QualifiedColumn; SQLite::Builder::StatementBuilder builder; builder.Select(columns).From(s_ManifestTable_Table_Name); // join tables auto columnItr = columns.begin(); auto manifestColumnNameItr = manifestColumnNames.begin(); while (columnItr != columns.end()) { builder.Join(columnItr->Table).On(QCol{ s_ManifestTable_Table_Name, *manifestColumnNameItr }, QCol{ columnItr->Table, SQLite::RowIDName }); columnItr++; manifestColumnNameItr++; } builder.Where(QCol{ s_ManifestTable_Table_Name, SQLite::RowIDName }).Equals(id); SQLite::Statement result = builder.Prepare(connection); if (stepAndVerify) { THROW_HR_IF(E_NOT_SET, !result.Step()); } return result; } SQLite::Statement ManifestTableGetAllValuesByIds_Statement( const SQLite::Connection& connection, std::initializer_list valueColumns, std::initializer_list joinColumns, std::initializer_list idColumns, std::initializer_list ids) { using QCol = SQLite::Builder::QualifiedColumn; THROW_HR_IF(E_INVALIDARG, idColumns.size() != ids.size()); SQLite::Builder::StatementBuilder builder; builder.Select(valueColumns).From(s_ManifestTable_Table_Name); for (const auto& joinColumn : joinColumns) { builder.Join(joinColumn.Table).On(QCol{ s_ManifestTable_Table_Name, joinColumn.Column }, QCol{ joinColumn.Table, SQLite::RowIDName }); } bool isFirst = true; for (const auto& idColumn : idColumns) { if (isFirst) { builder.Where(idColumn).Equals(SQLite::Builder::Unbound); isFirst = false; } else { builder.And(idColumn).Equals(SQLite::Builder::Unbound); } } SQLite::Statement select = builder.Prepare(connection); int bindIndex = 0; for (const auto& id : ids) { select.Bind(++bindIndex, id); } return select; } std::vector> ManifestTableGetAllValuesByIds( const SQLite::Connection& connection, std::initializer_list valueColumns, std::initializer_list joinColumns, std::initializer_list idColumns, std::initializer_list ids) { auto select = ManifestTableGetAllValuesByIds_Statement(connection, valueColumns, joinColumns, idColumns, ids); std::vector> result; while (select.Step()) { result.emplace_back(select.GetColumn(0), select.GetColumn(1)); } return result; } std::vector ManifestTableBuildSearchStatement( SQLite::Builder::StatementBuilder& builder, std::initializer_list columns, std::initializer_list isOneToOnes, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) { using QCol = SQLite::Builder::QualifiedColumn; // Build a statement like: // SELECT manifest.rowid as m, ids.id as v from manifest // join ids on manifest.id = ids.rowid // where ids.id = // OR // SELECT manifest.rowid as m, tags.tag as v from manifest // join tags_map on manifest.rowid = tags_map.manifest // join tags on tags_map.tag = tags.rowid // where tags.tag = // Where the joins and where portions are repeated for each table in question. builder.Select(). Column(QCol(s_ManifestTable_Table_Name, SQLite::RowIDName)).As(manifestAlias); // Value will be captured for single tables references, and left empty for multi-tables if (columns.size() == 1) { builder.Column(*columns.begin()); } else { builder.Value(std::string_view{}); } builder.As(valueAlias).From(s_ManifestTable_Table_Name); // Create join clauses THROW_HR_IF(E_INVALIDARG, columns.size() != isOneToOnes.size()); auto columnItr = columns.begin(); auto isOneToOneItr = isOneToOnes.begin(); for (; columnItr != columns.end(); ++columnItr, ++isOneToOneItr) { const SQLite::Builder::QualifiedColumn& column = *columnItr; if (*isOneToOneItr) { builder. Join(column.Table).On(QCol(s_ManifestTable_Table_Name, column.Column), QCol(column.Table, SQLite::RowIDName)); } else { std::string mapTableName = details::OneToManyTableGetMapTableName(column.Table); builder. Join(mapTableName).On(QCol(s_ManifestTable_Table_Name, SQLite::RowIDName), QCol(mapTableName, details::OneToManyTableGetManifestColumnName())). Join(column.Table).On(QCol(mapTableName, column.Column), QCol(column.Table, SQLite::RowIDName)); } } std::vector result; // Create where clause for (const SQLite::Builder::QualifiedColumn& column : columns) { if (result.empty()) { builder.Where(column); } else { builder.And(column); } if (useLike) { builder.Like(SQLite::Builder::Unbound); result.push_back(builder.GetLastBindIndex()); builder.Escape(SQLite::EscapeCharForLike); } else { builder.Equals(SQLite::Builder::Unbound); result.push_back(builder.GetLastBindIndex()); } } return result; } SQLite::Statement ManifestTableUpdateValueIdById_Statement(SQLite::Connection& connection, std::string_view valueName) { SQLite::Builder::StatementBuilder builder; builder.Update(s_ManifestTable_Table_Name).Set().Column(valueName).Equals(SQLite::Builder::Unbound).Where(SQLite::RowIDName).Equals(SQLite::Builder::Unbound); return builder.Prepare(connection); } bool ManifestTableCheckConsistency(const SQLite::Connection& connection, const SQLite::Builder::QualifiedColumn& target, std::string_view manifestColumnName, bool log) { using QCol = SQLite::Builder::QualifiedColumn; // Build a select statement to find manifest rows containing references to 1:1 tables with nonexistent rowids // Such as: // Select manifest.rowid, manifest.id, ids.id from manifest left outer join ids on manifest.id = ids.rowid where ids.id is NULL SQLite::Builder::StatementBuilder builder; builder. Select({ QCol(s_ManifestTable_Table_Name, SQLite::RowIDName), QCol(s_ManifestTable_Table_Name, manifestColumnName) }). From(s_ManifestTable_Table_Name). LeftOuterJoin(target.Table).On(QCol(s_ManifestTable_Table_Name, manifestColumnName), QCol(target.Table, SQLite::RowIDName)). Where(target).IsNull(); SQLite::Statement select = builder.Prepare(connection); bool result = true; while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] manifest [" << select.GetColumn(0) << "] refers to " << target.Table << " [" << select.GetColumn(1) << "]"); } return result; } } std::string_view ManifestTable::TableName() { return s_ManifestTable_Table_Name; } // Starting in V1.1, all code should be going this route of creating named indices rather than using primary or unique keys on columns. // The resulting database will function the same, but give us control to drop the indices to reduce space. void ManifestTable::Create(SQLite::Connection& connection, std::initializer_list values) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createManifestTable_v1_1"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_ManifestTable_Table_Name).BeginColumns(); // Add an integer primary key to keep the manifest rowid consistent createTableBuilder.Column(IntegerPrimaryKey()); for (const ManifestColumnInfo& value : values) { createTableBuilder.Column(ColumnBuilder(value.Name, Type::Int64).NotNull()); } createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); // Create a unique index with the primary key values StatementBuilder pkIndexBuilder; pkIndexBuilder.CreateUniqueIndex({ s_ManifestTable_Table_Name, s_ManifestTable_Index_Suffix }).On(s_ManifestTable_Table_Name).BeginColumns(); for (const ManifestColumnInfo& value : values) { if (value.PrimaryKey) { pkIndexBuilder.Column(value.Name); } } pkIndexBuilder.EndColumns(); pkIndexBuilder.Execute(connection); // Create an index on every value to improve performance for (const ManifestColumnInfo& value : values) { StatementBuilder createIndexBuilder; if (value.Unique) { createIndexBuilder.CreateUniqueIndex({ s_ManifestTable_Table_Name, s_ManifestTable_Index_Separator, value.Name, s_ManifestTable_Index_Suffix }); } else { createIndexBuilder.CreateIndex({ s_ManifestTable_Table_Name, s_ManifestTable_Index_Separator, value.Name, s_ManifestTable_Index_Suffix }); } createIndexBuilder.On(s_ManifestTable_Table_Name).Columns(value.Name); createIndexBuilder.Execute(connection); } savepoint.Commit(); } void ManifestTable::AddColumn(SQLite::Connection& connection, AddedColumnInfo value) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addColumnManifestTable_v1_3"); StatementBuilder alterTableBuilder; alterTableBuilder.AlterTable(s_ManifestTable_Table_Name).Add(value.Name, value.Type); alterTableBuilder.Execute(connection); savepoint.Commit(); } void ManifestTable::Create_deprecated(SQLite::Connection& connection, std::initializer_list values) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createManifestTable_v1_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_ManifestTable_Table_Name).BeginColumns(); for (const ManifestColumnInfo& value : values) { createTableBuilder.Column(ColumnBuilder(value.Name, Type::Int64).NotNull().Unique(value.Unique)); } PrimaryKeyBuilder pkBuilder; for (const ManifestColumnInfo& value : values) { if (value.PrimaryKey) { pkBuilder.Column(value.Name); } } createTableBuilder.Column(pkBuilder).EndColumns(); createTableBuilder.Execute(connection); // Create an index on every value to improve performance for (const ManifestColumnInfo& value : values) { StatementBuilder createIndexBuilder; createIndexBuilder.CreateIndex({ s_ManifestTable_Table_Name, s_ManifestTable_Index_Separator, value.Name, s_ManifestTable_Index_Suffix }). On(s_ManifestTable_Table_Name).Columns(value.Name); createIndexBuilder.Execute(connection); } savepoint.Commit(); } void ManifestTable::Drop(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable(s_ManifestTable_Table_Name); dropTableBuilder.Execute(connection); } SQLite::rowid_t ManifestTable::Insert(SQLite::Connection& connection, std::initializer_list values) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_ManifestTable_Table_Name).BeginColumns(); for (const ManifestOneToOneValue& value : values) { builder.Column(value.Name); } builder.EndColumns().BeginValues(); for (const ManifestOneToOneValue& value : values) { builder.Value(value.Value); } builder.EndValues(); builder.Execute(connection); return connection.GetLastInsertRowID(); } bool ManifestTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_ManifestTable_Table_Name).Where(SQLite::RowIDName).Equals(id); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) != 0); } void ManifestTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_ManifestTable_Table_Name).Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); } void ManifestTable::PrepareForPackaging(SQLite::Connection& connection, std::initializer_list values) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "pfpManifestTable_v1_1"); PrepareForPackaging_deprecated(connection, values); SQLite::Builder::StatementBuilder dropPKIndexBuilder; dropPKIndexBuilder.DropIndex({ s_ManifestTable_Table_Name, s_ManifestTable_Index_Suffix }); dropPKIndexBuilder.Execute(connection); savepoint.Commit(); } void ManifestTable::PrepareForPackaging_deprecated(SQLite::Connection& connection, std::initializer_list values) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "pfpManifestTable_v1_0"); // Drop the index on the requested values for (std::string_view value : values) { SQLite::Builder::StatementBuilder dropIndexBuilder; dropIndexBuilder.DropIndex({ s_ManifestTable_Table_Name, s_ManifestTable_Index_Separator, value, s_ManifestTable_Index_Suffix }); dropIndexBuilder.Execute(connection); } savepoint.Commit(); } bool ManifestTable::IsValueReferenced(const SQLite::Connection& connection, std::string_view valueName, SQLite::rowid_t valueRowId) { return details::ManifestTableSelectByValueIds(connection, { valueName }, { valueRowId }).has_value(); } bool ManifestTable::IsEmpty(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_ManifestTable_Table_Name); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) == 0); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "Microsoft/Schema/1_0/VirtualTableBase.h" #include #include #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { template std::string_view GetManifestTableColumnName() { if constexpr (std::is_base_of()) { return Table::ManifestColumnName(); } else { return Table::ValueName(); } } // Selects a manifest by the given value id. std::optional ManifestTableSelectByValueIds( const SQLite::Connection& connection, std::initializer_list values, std::initializer_list ids); // Gets the requested ids for the manifest with the given rowid. SQLite::Statement ManifestTableGetIdsById_Statement( const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list values, bool stepAndVerify = true); // Gets the requested values for the manifest with the given rowid. SQLite::Statement ManifestTableGetValuesById_Statement( const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list columns, std::initializer_list manifestColumnNames, bool stepAndVerify = true); // Gets all values for rows that match the given ids. SQLite::Statement ManifestTableGetAllValuesByIds_Statement( const SQLite::Connection& connection, std::initializer_list valueColumns, std::initializer_list joinColumns, std::initializer_list idColumns, std::initializer_list ids); // Gets all values for rows that match the given ids. std::vector> ManifestTableGetAllValuesByIds( const SQLite::Connection& connection, std::initializer_list valueColumns, std::initializer_list joinColumns, std::initializer_list idColumns, std::initializer_list ids); // Builds the search select statement base on the given values. std::vector ManifestTableBuildSearchStatement( SQLite::Builder::StatementBuilder& builder, std::initializer_list columns, std::initializer_list isOneToOnes, std::string_view manifestAlias, std::string_view valueAlias, bool useLike); // Prepares a statement to update the value of a single column for the manifest with the given rowid. // The first bind value will be the value to set. // The second bind value will be the manifest rowid to modify. SQLite::Statement ManifestTableUpdateValueIdById_Statement(SQLite::Connection& connection, std::string_view valueName); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. bool ManifestTableCheckConsistency(const SQLite::Connection& connection, const SQLite::Builder::QualifiedColumn& target, std::string_view manifestColumnName, bool log); } // Info on the manifest columns. struct ManifestColumnInfo { std::string_view Name; bool PrimaryKey; bool Unique; }; // Information on a column being added via ALTER TABLE struct AddedColumnInfo { std::string_view Name; SQLite::Builder::Type Type; }; // A value that is 1:1 with the manifest. struct ManifestOneToOneValue { std::string_view Name; SQLite::rowid_t Value; }; // A table that represents a single manifest struct ManifestTable { // Get the table name. static std::string_view TableName(); // Creates the table with named indices. static void Create(SQLite::Connection& connection, std::initializer_list values); // Alters the table, adding the columns provided. static void AddColumn(SQLite::Connection& connection, AddedColumnInfo value); // Creates the table with standard primary keys. static void Create_deprecated(SQLite::Connection& connection, std::initializer_list values); // Drops the table. static void Drop(SQLite::Connection& connection); // Insert the given values into the table. static SQLite::rowid_t Insert(SQLite::Connection& connection, std::initializer_list values); // Gets a value indicating whether the manifest with rowid id exists. static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); // Select the first rowid of the manifest with the given value. template static std::optional SelectByValueIds(const SQLite::Connection& connection, std::initializer_list ids) { static_assert(sizeof...(Tables) >= 1); return details::ManifestTableSelectByValueIds(connection, { Tables::ValueName()... }, ids); } // Gets the ids requested for the manifest with the given rowid. template static auto GetIdsById(const SQLite::Connection& connection, SQLite::rowid_t id) { return details::ManifestTableGetIdsById_Statement(connection, id, { details::GetManifestTableColumnName()...}).GetRow(); } // Gets the id requested for the manifest with the given rowid, if it exists. template static std::optional GetIdById(const SQLite::Connection& connection, SQLite::rowid_t id) { auto statement = details::ManifestTableGetIdsById_Statement(connection, id, { details::GetManifestTableColumnName
() }, false); if (statement.Step()) { return statement.GetColumn(0); } else { return std::nullopt; } } // Gets the values requested for the manifest with the given rowid. template static auto GetValuesById(const SQLite::Connection& connection, SQLite::rowid_t id) { return details::ManifestTableGetValuesById_Statement(connection, id, { SQLite::Builder::QualifiedColumn{ Tables::TableName(), Tables::ValueName() }... }, { details::GetManifestTableColumnName()... }).GetRow(); } // Gets the value requested for the manifest with the given rowid, if it exists. template static std::optional GetValueById(const SQLite::Connection& connection, SQLite::rowid_t id) { auto statement = details::ManifestTableGetValuesById_Statement(connection, id, { SQLite::Builder::QualifiedColumn{ Table::TableName(), Table::ValueName() } }, { details::GetManifestTableColumnName
() }, false); if (statement.Step()) { return statement.GetColumn(0); } else { return std::nullopt; } } // Gets the row ids and values for rows that match the given ids. template static std::vector> GetAllValuesByIds(const SQLite::Connection& connection, std::initializer_list ids) { return details::ManifestTableGetAllValuesByIds(connection, { SQLite::Builder::QualifiedColumn{ ValueTable::TableName(), SQLite::RowIDName }, SQLite::Builder::QualifiedColumn{ ValueTable::TableName(), ValueTable::ValueName() } }, { SQLite::Builder::QualifiedColumn{ ValueTable::TableName(), ValueTable::ValueName() } }, { IdTables::ValueName()... }, ids); } // Gets all values for rows that match the given id. template static std::vector> GetAllValuesById(const SQLite::Connection& connection, SQLite::rowid_t id) { auto stmt = details::ManifestTableGetAllValuesByIds_Statement(connection, { SQLite::Builder::QualifiedColumn{ TableName(), SQLite::RowIDName }, SQLite::Builder::QualifiedColumn{ ValueTables::TableName(), ValueTables::ValueName() }... }, { SQLite::Builder::QualifiedColumn{ ValueTables::TableName(), ValueTables::ValueName() }... }, { IdTable::ValueName() }, { id }); std::vector> result; while (stmt.Step()) { result.emplace_back(stmt.GetRow()); } return result; } // Builds the search select statement base on the given values. // If more than one table is provided, no value will be captured. // The return value is the bind indices of the values to match against. template static std::vector BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) { return details::ManifestTableBuildSearchStatement(builder, { SQLite::Builder::QualifiedColumn{ Table::TableName(), Table::ValueName() }... }, { Table::IsOneToOne()... }, manifestAlias, valueAlias, useLike); } // Update the value of a single column for the manifest with the given rowid. template static void UpdateValueIdById(SQLite::Connection& connection, SQLite::rowid_t id, const typename Table::id_t& value) { auto stmt = details::ManifestTableUpdateValueIdById_Statement(connection, details::GetManifestTableColumnName
()); stmt.Bind(1, value); stmt.Bind(2, id); stmt.Execute(); } // Deletes the manifest row with the given rowid. static void DeleteById(SQLite::Connection& connection, SQLite::rowid_t id); // Removes data that is no longer needed for an index that is to be published. static void PrepareForPackaging(SQLite::Connection& connection, std::initializer_list values); // Removes data that is no longer needed for an index that is to be published. static void PrepareForPackaging_deprecated(SQLite::Connection& connection, std::initializer_list values); // Checks if the row id is present in the column denoted by the value supplied. static bool IsValueReferenced(const SQLite::Connection& connection, std::string_view valueName, SQLite::rowid_t valueRowId); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. template static bool CheckConsistency(const SQLite::Connection& connection, bool log) { return details::ManifestTableCheckConsistency( connection, SQLite::Builder::QualifiedColumn{ Table::TableName(), Table::ValueName() }, details::GetManifestTableColumnName
(), log); } // Determines if the table is empty. static bool IsEmpty(SQLite::Connection& connection); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/MonikerTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToOneTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; struct MonikerTableInfo { inline static constexpr std::string_view TableName() { return "monikers"sv; } inline static constexpr std::string_view ValueName() { return "moniker"sv; } }; } // The table for Moniker. using MonikerTable = OneToOneTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/NameTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToOneTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; struct NameTableInfo { inline static constexpr std::string_view TableName() { return "names"sv; } inline static constexpr std::string_view ValueName() { return "name"sv; } }; } // The table for Name. // TODO: Currently only indexing name from default locale, might need to be OneToMany table using NameTable = OneToOneTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_0/OneToManyTable.h" #include "Microsoft/Schema/1_0/OneToOneTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_0/IdTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; static constexpr std::string_view s_OneToManyTable_MapTable_ManifestName = "manifest"sv; static constexpr std::string_view s_OneToManyTable_MapTable_Suffix = "_map"sv; static constexpr std::string_view s_OneToManyTable_MapTable_PrimaryKeyIndexSuffix = "_pkindex"sv; static constexpr std::string_view s_OneToManyTable_MapTable_IndexSuffix = "_index"sv; namespace { // Create the mapping table insert statement for multiple use. // Bind the rowid of the value to 2. SQLite::Statement CreateMappingInsertStatementForManifestId(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId) { SQLite::Builder::StatementBuilder insertMappingBuilder; insertMappingBuilder.InsertInto({ tableName, s_OneToManyTable_MapTable_Suffix }). Columns({ s_OneToManyTable_MapTable_ManifestName, valueName }).Values(manifestId, SQLite::Builder::Unbound); return insertMappingBuilder.Prepare(connection); } // Get a collection of the value ids associated with the given manifest id. std::vector GetValueIdsByManifestId(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId) { std::vector result; SQLite::Builder::StatementBuilder selectMappingBuilder; selectMappingBuilder.Select(valueName).From({ tableName, s_OneToManyTable_MapTable_Suffix }).Where(s_OneToManyTable_MapTable_ManifestName).Equals(manifestId); SQLite::Statement selectMappingStatement = selectMappingBuilder.Prepare(connection); while (selectMappingStatement.Step()) { result.push_back(selectMappingStatement.GetColumn(0)); } return result; } struct DeleteValueIfNotNeededStatements { DeleteValueIfNotNeededStatements(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName) { SQLite::Builder::StatementBuilder selectValueMappingBuilder; selectValueMappingBuilder.Select(s_OneToManyTable_MapTable_ManifestName).From({ tableName, s_OneToManyTable_MapTable_Suffix }).Where(valueName).Equals(SQLite::Builder::Unbound).Limit(1); SelectIfAnyMappingsByValueId = selectValueMappingBuilder.Prepare(connection); SQLite::Builder::StatementBuilder deleteValueBuilder; deleteValueBuilder.DeleteFrom(tableName).Where(SQLite::RowIDName).Equals(SQLite::Builder::Unbound); DeleteValueById = deleteValueBuilder.Prepare(connection); } void Execute(SQLite::rowid_t valueId) { SelectIfAnyMappingsByValueId.Reset(); SelectIfAnyMappingsByValueId.Bind(1, valueId); // If no rows are found, we can delete the data. if (!SelectIfAnyMappingsByValueId.Step()) { DeleteValueById.Reset(); DeleteValueById.Bind(1, valueId); DeleteValueById.Execute(); } } private: // Bind valid rowid to 1. SQLite::Statement SelectIfAnyMappingsByValueId; // Bind valid rowid to 1. SQLite::Statement DeleteValueById; }; bool SchemaVersionUsesNamedIndices(OneToManyTableSchema schemaVersion) { return schemaVersion != OneToManyTableSchema::Version_1_0; } } std::string OneToManyTableGetMapTableName(std::string_view tableName) { std::string result(tableName); result += s_OneToManyTable_MapTable_Suffix; return result; } std::string_view OneToManyTableGetManifestColumnName() { return s_OneToManyTable_MapTable_ManifestName; } void CreateOneToManyTable(SQLite::Connection& connection, OneToManyTableSchema schemaVersion, std::string_view tableName, std::string_view valueName) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_create_v1_0"); // Create the data table as a 1:1 CreateOneToOneTable(connection, tableName, valueName, SchemaVersionUsesNamedIndices(schemaVersion)); switch (schemaVersion) { case OneToManyTableSchema::Version_1_0: { // Create the mapping table StatementBuilder createMapTableBuilder; createMapTableBuilder.CreateTable({ tableName, s_OneToManyTable_MapTable_Suffix }).Columns({ ColumnBuilder(s_OneToManyTable_MapTable_ManifestName, Type::Int64).NotNull(), ColumnBuilder(valueName, Type::Int64).NotNull(), PrimaryKeyBuilder({ valueName, s_OneToManyTable_MapTable_ManifestName }) }); createMapTableBuilder.Execute(connection); } break; case OneToManyTableSchema::Version_1_1: { // Create the mapping table StatementBuilder createMapTableBuilder; createMapTableBuilder.CreateTable({ tableName, s_OneToManyTable_MapTable_Suffix }).Columns({ ColumnBuilder(s_OneToManyTable_MapTable_ManifestName, Type::Int64).NotNull(), ColumnBuilder(valueName, Type::Int64).NotNull() }); createMapTableBuilder.Execute(connection); StatementBuilder pkIndexBuilder; pkIndexBuilder.CreateUniqueIndex({ tableName, s_OneToManyTable_MapTable_Suffix, s_OneToManyTable_MapTable_PrimaryKeyIndexSuffix }). On({ tableName, s_OneToManyTable_MapTable_Suffix }).Columns({ valueName, s_OneToManyTable_MapTable_ManifestName }); pkIndexBuilder.Execute(connection); } break; case OneToManyTableSchema::Version_1_7: { // Create the mapping table StatementBuilder createMapTableBuilder; createMapTableBuilder.CreateTable({ tableName, s_OneToManyTable_MapTable_Suffix }).Columns({ ColumnBuilder(s_OneToManyTable_MapTable_ManifestName, Type::Int64).NotNull(), ColumnBuilder(valueName, Type::Int64).NotNull(), PrimaryKeyBuilder({ valueName, s_OneToManyTable_MapTable_ManifestName }) }).WithoutRowID(); createMapTableBuilder.Execute(connection); } break; default: THROW_HR(E_UNEXPECTED); } StatementBuilder createMapTableIndexBuilder; createMapTableIndexBuilder.CreateIndex({ tableName, s_OneToManyTable_MapTable_Suffix, s_OneToManyTable_MapTable_IndexSuffix }). On({ tableName, s_OneToManyTable_MapTable_Suffix }).Columns({ s_OneToManyTable_MapTable_ManifestName, valueName }); createMapTableIndexBuilder.Execute(connection); savepoint.Commit(); } void DropOneToManyTable(SQLite::Connection& connection, std::string_view tableName) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_drop_v1_0"); DropOneToOneTable(connection, tableName); SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable({ tableName, s_OneToManyTable_MapTable_Suffix }); dropTableBuilder.Execute(connection); savepoint.Commit(); } std::vector OneToManyTableGetValuesByManifestId( const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId) { using QCol = SQLite::Builder::QualifiedColumn; std::vector result; SQLite::Builder::StatementBuilder builder; builder.Select(QCol(tableName, valueName)). From({ tableName, s_OneToManyTable_MapTable_Suffix }).As("map").Join(tableName). On(QCol("map", valueName), QCol(tableName, SQLite::RowIDName)).Where(QCol("map", s_OneToManyTable_MapTable_ManifestName)).Equals(manifestId); SQLite::Statement statement = builder.Prepare(connection); while (statement.Step()) { result.emplace_back(statement.GetColumn(0)); } return result; } void OneToManyTableEnsureExistsAndInsert(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, const std::vector& values, SQLite::rowid_t manifestId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_ensureandinsert_v1_0"); SQLite::Statement insertMapping = CreateMappingInsertStatementForManifestId(connection, tableName, valueName, manifestId); for (const std::string& value : values) { // First, ensure that the data exists SQLite::rowid_t dataId = OneToOneTableEnsureExists(connection, tableName, valueName, value); // Second, insert into the mapping table insertMapping.Reset(); insertMapping.Bind(2, dataId); insertMapping.Execute(); } savepoint.Commit(); } bool OneToManyTableUpdateIfNeededByManifestId(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, const std::vector& values, SQLite::rowid_t manifestId) { std::vector oldValueIds = GetValueIdsByManifestId(connection, tableName, valueName, manifestId); bool modificationNeeded = false; SQLite::Statement insertMapping = CreateMappingInsertStatementForManifestId(connection, tableName, valueName, manifestId); for (const std::string& value : values) { SQLite::rowid_t valueId = OneToOneTableEnsureExists(connection, tableName, valueName, value); auto itr = std::find(oldValueIds.begin(), oldValueIds.end(), valueId); if (itr != oldValueIds.end()) { oldValueIds.erase(itr); } else { modificationNeeded = true; insertMapping.Reset(); insertMapping.Bind(2, valueId); insertMapping.Execute(); } } // All incoming values are now present, we just need to delete the remaining old ones. SQLite::Builder::StatementBuilder deleteBuilder; deleteBuilder.DeleteFrom({ tableName, s_OneToManyTable_MapTable_Suffix }). Where(s_OneToManyTable_MapTable_ManifestName).Equals(manifestId).And(valueName).Equals(SQLite::Builder::Unbound); SQLite::Statement deleteStatement = deleteBuilder.Prepare(connection); DeleteValueIfNotNeededStatements dvinns(connection, tableName, valueName); for (SQLite::rowid_t valueId : oldValueIds) { modificationNeeded = true; // First, delete the mapping deleteStatement.Reset(); deleteStatement.Bind(2, valueId); deleteStatement.Execute(); // Second, delete the value itself if not needed dvinns.Execute(valueId); } return modificationNeeded; } void OneToManyTableDeleteIfNotNeededByManifestId(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_deleteifnotneeded_v1_0"); // Get values referenced by the manifest id. std::vector values = GetValueIdsByManifestId(connection, tableName, valueName, manifestId); // Delete the mapping table rows with the manifest id. SQLite::Builder::StatementBuilder deleteBuilder; deleteBuilder.DeleteFrom({ tableName, s_OneToManyTable_MapTable_Suffix }).Where(s_OneToManyTable_MapTable_ManifestName).Equals(manifestId); deleteBuilder.Execute(connection); // For each value, see if any references exist DeleteValueIfNotNeededStatements dvinns(connection, tableName, valueName); for (SQLite::rowid_t value : values) { dvinns.Execute(value); } savepoint.Commit(); } void OneToManyTablePrepareForPackaging(SQLite::Connection& connection, std::string_view tableName, OneToManyTableSchema schemaVersion, bool preserveManifestIndex, bool preserveValuesIndex) { if (!preserveManifestIndex) { SQLite::Builder::StatementBuilder dropMapTableIndexBuilder; dropMapTableIndexBuilder.DropIndex({ tableName, s_OneToManyTable_MapTable_Suffix, s_OneToManyTable_MapTable_IndexSuffix }); dropMapTableIndexBuilder.Execute(connection); } OneToOneTablePrepareForPackaging(connection, tableName, SchemaVersionUsesNamedIndices(schemaVersion), preserveValuesIndex); } bool OneToManyTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log) { using QCol = SQLite::Builder::QualifiedColumn; constexpr std::string_view s_map = "map"sv; bool result = true; { // Build a select statement to find map rows containing references to manifests with nonexistent rowids // Such as: // Select map.rowid, map.manifest from tags_map as map left outer join manifest on map.manifest = manifest.rowid where manifest.id is null SQLite::Builder::StatementBuilder builder; builder. Select({ QCol(s_map, s_OneToManyTable_MapTable_ManifestName), QCol(s_map, valueName) }). From({ tableName, s_OneToManyTable_MapTable_Suffix }).As(s_map). LeftOuterJoin(ManifestTable::TableName()).On(QCol(s_map, s_OneToManyTable_MapTable_ManifestName), QCol(ManifestTable::TableName(), SQLite::RowIDName)). Where(QCol(ManifestTable::TableName(), SQLite::RowIDName)).IsNull(); SQLite::Statement select = builder.Prepare(connection); while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] " << tableName << s_OneToManyTable_MapTable_Suffix << " [" << select.GetColumn(0) << ", " << select.GetColumn(1) << "] refers to invalid " << ManifestTable::TableName()); } } if (!result && !log) { return result; } { // Build a select statement to find map rows containing references to 1:1 tables with nonexistent rowids // Such as: // Select map.rowid, map.tag from tags_map as map left outer join tags on map.tag = tags.rowid where tags.tag is null SQLite::Builder::StatementBuilder builder; builder. Select({ QCol(s_map, s_OneToManyTable_MapTable_ManifestName), QCol(s_map, valueName) }). From({ tableName, s_OneToManyTable_MapTable_Suffix }).As(s_map). LeftOuterJoin(tableName).On(QCol(s_map, valueName), QCol(tableName, SQLite::RowIDName)). Where(QCol(tableName, valueName)).IsNull(); SQLite::Statement select = builder.Prepare(connection); bool secondaryResult = true; while (select.Step()) { secondaryResult = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] " << tableName << s_OneToManyTable_MapTable_Suffix << " [" << select.GetColumn(0) << ", " << select.GetColumn(1) << "] refers to invalid " << tableName); } result = result && secondaryResult; } if (!result && !log) { return result; } result = OneToOneTableCheckConsistency(connection, tableName, valueName, log) && result; return result; } bool OneToManyTableIsEmpty(SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder countBuilder; countBuilder.Select(SQLite::Builder::RowCount).From(tableName); SQLite::Statement countStatement = countBuilder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); SQLite::Builder::StatementBuilder countMapBuilder; countMapBuilder.Select(SQLite::Builder::RowCount).From({ tableName, s_OneToManyTable_MapTable_Suffix }); SQLite::Statement countMapStatement = countMapBuilder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countMapStatement.Step()); return ((countStatement.GetColumn(0) == 0) && (countMapStatement.GetColumn(0) == 0)); } SQLite::Statement OneToManyTablePrepareMapDataFoldingStatement(const SQLite::Connection& connection, std::string_view tableName) { using namespace SQLite::Builder; StatementBuilder builder; // Create a statement that will collapse (and dedupe) all rows in the map to the latest (max) manifest for a given id, like: // UPDATE OR REPLACE map SET manifest = (SELECT MAX(rowid) FROM manifest_table WHERE id = ?1) WHERE manifest IN (SELECT rowid FROM manifest_table WHERE id = ?1) builder.UpdateOrReplace({ tableName, s_OneToManyTable_MapTable_Suffix }).Set().Column(s_OneToManyTable_MapTable_ManifestName).Equals() .BeginParenthetical() .Select().Column(Aggregate::Max, SQLite::RowIDName).From(ManifestTable::TableName()).Where(IdTable::ValueName()).Equals(Unbound, 1) .EndParenthetical() .Where(s_OneToManyTable_MapTable_ManifestName).In() .BeginParenthetical() .Select(SQLite::RowIDName).From(ManifestTable::TableName()).Where(IdTable::ValueName()).Equals(Unbound, 1) .EndParenthetical(); return builder.Prepare(connection); } } std::optional OneToManyTableGetMapDataFoldingManifestTargetId(const SQLite::Connection& connection, SQLite::rowid_t manifestId) { using namespace SQLite::Builder; StatementBuilder builder; // Select the maximum manifest rowid from the manifests whose id is the same as the row with the given manifest rowid, like: // SELECT MAX(rowid) FROM manifest_table WHERE id = (SELECT id FROM manifest_table WHERE rowid = ?) builder.Select().Column(Aggregate::Max, SQLite::RowIDName).From(ManifestTable::TableName()).Where(IdTable::ValueName()).Equals() .BeginParenthetical() .Select(IdTable::ValueName()).From(ManifestTable::TableName()).Where(SQLite::RowIDName).Equals(manifestId) .EndParenthetical(); SQLite::Statement statement = builder.Prepare(connection); if (statement.Step() && !statement.GetColumnIsNull(0)) { return statement.GetColumn(0); } return std::nullopt; } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { // Allow the different schema version to indicate which they are. enum class OneToManyTableSchema { // Does not used a named unique index for either table. // Map table has primary key and rowid. Version_1_0, // Uses a named unique index for both tables. // Map table has rowid. Version_1_1, // Uses a named unique index for data table. (No change from 1.1) // Map table has primary key and no rowid. Version_1_7, }; namespace details { // Returns the map table name for a given table. std::string OneToManyTableGetMapTableName(std::string_view tableName); // Returns the manifest column name. std::string_view OneToManyTableGetManifestColumnName(); // Create the tables. void CreateOneToManyTable(SQLite::Connection& connection, OneToManyTableSchema schemaVersion, std::string_view tableName, std::string_view valueName); // Drop the tables. void DropOneToManyTable(SQLite::Connection& connection, std::string_view tableName); // Gets all values associated with the given manifest id. std::vector OneToManyTableGetValuesByManifestId( const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId); // Ensures that the value exists and inserts mapping entries. void OneToManyTableEnsureExistsAndInsert(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, const std::vector& values, SQLite::rowid_t manifestId); // Updates the mapping table to represent the given values for the manifest. bool OneToManyTableUpdateIfNeededByManifestId(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, const std::vector& values, SQLite::rowid_t manifestId); // Deletes the mapping rows for the given manifest, then removes any unused data rows. void OneToManyTableDeleteIfNotNeededByManifestId(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId); // Removes data that is no longer needed for an index that is to be published. void OneToManyTablePrepareForPackaging(SQLite::Connection& connection, std::string_view tableName, OneToManyTableSchema schemaVersion, bool preserveManifestIndex, bool preserveValuesIndex); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. bool OneToManyTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log); // Determines if the table is empty. bool OneToManyTableIsEmpty(SQLite::Connection& connection, std::string_view tableName); // Prepares a statement for replacing all of the map data to point to a single manifest for a given id. SQLite::Statement OneToManyTablePrepareMapDataFoldingStatement(const SQLite::Connection& connection, std::string_view tableName); } // Gets the manifest id that the PrepareMapDataFoldingStatement will fold to for the given manifest id. std::optional OneToManyTableGetMapDataFoldingManifestTargetId(const SQLite::Connection& connection, SQLite::rowid_t manifestId); // A table that represents a value that is 1:N with a primary entry. template struct OneToManyTable { // The name of the table. static constexpr std::string_view TableName() { return TableInfo::TableName(); } // The value name of the table. static constexpr std::string_view ValueName() { return TableInfo::ValueName(); } // Value indicating type. static constexpr bool IsOneToOne() { return false; } // Creates the table with named indices. static void Create(SQLite::Connection& connection, OneToManyTableSchema schemaVersion) { details::CreateOneToManyTable(connection, schemaVersion, TableInfo::TableName(), TableInfo::ValueName()); } // Drops the table. static void Drop(SQLite::Connection& connection) { details::DropOneToManyTable(connection, TableInfo::TableName()); } // Gets all values associated with the given manifest id. static std::vector GetValuesByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId) { return details::OneToManyTableGetValuesByManifestId(connection, TableInfo::TableName(), TableInfo::ValueName(), manifestId); } // Ensures that all values exist in the data table, and inserts into the mapping table for the given manifest id. static void EnsureExistsAndInsert(SQLite::Connection& connection, const std::vector& values, SQLite::rowid_t manifestId) { details::OneToManyTableEnsureExistsAndInsert(connection, TableInfo::TableName(), TableInfo::ValueName(), values, manifestId); } // Updates the mapping table to represent the given values for the manifest. static bool UpdateIfNeededByManifestId(SQLite::Connection& connection, const std::vector& values, SQLite::rowid_t manifestId) { return details::OneToManyTableUpdateIfNeededByManifestId(connection, TableInfo::TableName(), TableInfo::ValueName(), values, manifestId); } // Deletes the mapping rows for the given manifest, then removes any unused data rows. static void DeleteIfNotNeededByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId) { details::OneToManyTableDeleteIfNotNeededByManifestId(connection, TableInfo::TableName(), TableInfo::ValueName(), manifestId); } // Removes data that is no longer needed for an index that is to be published. // Preserving the manifest index will improve the efficiency of finding the values associated with a manifest. // Preserving the values index will improve searching when it is primarily done by equality. static void PrepareForPackaging(SQLite::Connection& connection, OneToManyTableSchema schemaVersion, bool preserveManifestIndex, bool preserveValuesIndex) { details::OneToManyTablePrepareForPackaging(connection, TableInfo::TableName(), schemaVersion, preserveManifestIndex, preserveValuesIndex); } // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. static bool CheckConsistency(const SQLite::Connection& connection, bool log) { return details::OneToManyTableCheckConsistency(connection, TableInfo::TableName(), TableInfo::ValueName(), log); } // Determines if the table is empty. static bool IsEmpty(SQLite::Connection& connection) { return details::OneToManyTableIsEmpty(connection, TableInfo::TableName()); } // Prepares a statement for replacing all of the map data to point to a single manifest for a given id. static SQLite::Statement PrepareMapDataFoldingStatement(const SQLite::Connection& connection) { return details::OneToManyTablePrepareMapDataFoldingStatement(connection, TableInfo::TableName()); } }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_0/OneToOneTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; static constexpr std::string_view s_OneToOneTable_IndexSuffix = "_pkindex"sv; void CreateOneToOneTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool useNamedIndices) { using namespace SQLite::Builder; // Starting in V1.1, all code should be going this route of creating named indices rather than using primary or unique keys on columns. // The resulting database will function the same, but give us control to drop the indices to reduce space. if (useNamedIndices) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_create_v1_1"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(tableName).Columns({ IntegerPrimaryKey(), ColumnBuilder(valueName, Type::Text).NotNull() }); createTableBuilder.Execute(connection); StatementBuilder indexBuilder; indexBuilder.CreateUniqueIndex({ tableName, s_OneToOneTable_IndexSuffix }).On(tableName).Columns(valueName); indexBuilder.Execute(connection); savepoint.Commit(); } else { StatementBuilder createTableBuilder; createTableBuilder.CreateTable(tableName).Columns({ ColumnBuilder(valueName, Type::Text).NotNull().PrimaryKey() }); createTableBuilder.Execute(connection); } } void DropOneToOneTable(SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable(tableName); dropTableBuilder.Execute(connection); } std::optional OneToOneTableSelectIdByValue(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool useLike) { SQLite::Builder::StatementBuilder selectBuilder; selectBuilder.Select(SQLite::RowIDName).From(tableName).Where(valueName); if (useLike) { selectBuilder.LikeWithEscape(value); } else { selectBuilder.Equals(value); } SQLite::Statement select = selectBuilder.Prepare(connection); if (select.Step()) { return select.GetColumn(0); } else { return {}; } } std::optional OneToOneTableSelectValueById(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder selectBuilder; selectBuilder.Select(valueName).From(tableName).Where(SQLite::RowIDName).Equals(id); SQLite::Statement select = selectBuilder.Prepare(connection); if (select.Step()) { return select.GetColumn(0); } else { return {}; } } std::vector OneToOneTableGetAllRowIds(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, size_t limit) { SQLite::Builder::StatementBuilder selectBuilder; selectBuilder.Select(SQLite::RowIDName).From(tableName).OrderBy(valueName); if (limit) { selectBuilder.Limit(limit); } SQLite::Statement select = selectBuilder.Prepare(connection); std::vector result; while (select.Step()) { result.emplace_back(select.GetColumn(0)); } return result; } SQLite::rowid_t OneToOneTableEnsureExists(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool overwriteLikeMatch) { auto selectResult = OneToOneTableSelectIdByValue(connection, tableName, valueName, value, overwriteLikeMatch); if (selectResult) { if (overwriteLikeMatch) { // If the value in the table is not an exact match, overwrite it with the incoming value auto tableValue = OneToOneTableSelectValueById(connection, tableName, valueName, selectResult.value()); if (tableValue.value() != value) { SQLite::Builder::StatementBuilder updateBuilder; updateBuilder.Update(tableName).Set().Column(valueName).Equals(value).Where(SQLite::RowIDName).Equals(selectResult); updateBuilder.Execute(connection); } } return selectResult.value(); } SQLite::Builder::StatementBuilder insertBuilder; insertBuilder.InsertInto(tableName).Columns(valueName).Values(value); insertBuilder.Execute(connection); return connection.GetLastInsertRowID(); } void OneToOneTablePrepareForPackaging(SQLite::Connection& connection, std::string_view tableName, bool useNamedIndices, bool preserveValuesIndex) { if (useNamedIndices && !preserveValuesIndex) { SQLite::Builder::StatementBuilder dropIndexBuilder; dropIndexBuilder.DropIndex({ tableName, s_OneToOneTable_IndexSuffix }); dropIndexBuilder.Execute(connection); } } uint64_t OneToOneTableGetCount(const SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(tableName); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return static_cast(countStatement.GetColumn(0)); } bool OneToOneTableIsEmpty(SQLite::Connection& connection, std::string_view tableName) { return (OneToOneTableGetCount(connection, tableName) == 0); } void OneToOneTableDeleteById(SQLite::Connection& connection, std::string_view tableName, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(tableName).Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); } bool OneToOneTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log) { // Build a select statement to find values that contain an embedded null character // Such as: // Select count(*) from table where instr(value,char(0))>0 SQLite::Builder::StatementBuilder builder; builder. Select({ SQLite::RowIDName, valueName }). From(tableName). WhereValueContainsEmbeddedNullCharacter(valueName); SQLite::Statement select = builder.Prepare(connection); bool result = true; while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] value in table [" << tableName << "] at row [" << select.GetColumn(0) << "] contains an embedded null character and starts with [" << select.GetColumn(1) << "]"); } return result; } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { // Creates the table. void CreateOneToOneTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool useNamedIndices); // Drops the table. void DropOneToOneTable(SQLite::Connection& connection, std::string_view tableName); // Selects the value from the table, returning the rowid if it exists. std::optional OneToOneTableSelectIdByValue(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool useLike = false); // Selects the value from the table, returning the rowid if it exists. std::optional OneToOneTableSelectValueById(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id); // Gets all row ids from the table. std::vector OneToOneTableGetAllRowIds(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, size_t limit); // Ensures that the values exists in the table. SQLite::rowid_t OneToOneTableEnsureExists(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool overwriteLikeMatch = false); // Removes data that is no longer needed for an index that is to be published. void OneToOneTablePrepareForPackaging(SQLite::Connection& connection, std::string_view tableName, bool useNamedIndices, bool preserveValuesIndex); // Gets the total number of rows in the table. uint64_t OneToOneTableGetCount(const SQLite::Connection& connection, std::string_view tableName); // Determines if the table is empty. bool OneToOneTableIsEmpty(SQLite::Connection& connection, std::string_view tableName); // Removes the given row by its rowid if it is no longer referenced. void OneToOneTableDeleteById(SQLite::Connection& connection, std::string_view tableName, SQLite::rowid_t id); // Checks the consistency of the table. bool OneToOneTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log); } // A table that represents a value that is 1:1 with a primary entry. template struct OneToOneTable { // The value type. using value_t = std::string; // The id type using id_t = SQLite::rowid_t; // Creates the table with named indices. static void Create(SQLite::Connection& connection) { details::CreateOneToOneTable(connection, TableInfo::TableName(), TableInfo::ValueName(), true); } // Creates the table with standard primary keys. static void Create_deprecated(SQLite::Connection& connection) { details::CreateOneToOneTable(connection, TableInfo::TableName(), TableInfo::ValueName(), false); } // Drops the table. static void Drop(SQLite::Connection& connection) { details::DropOneToOneTable(connection, TableInfo::TableName()); } // The name of the table. static constexpr std::string_view TableName() { return TableInfo::TableName(); } // The value name of the table. static constexpr std::string_view ValueName() { return TableInfo::ValueName(); } // Value indicating type. static constexpr bool IsOneToOne() { return true; } // Selects the value from the table, returning the rowid if it exists. static std::optional SelectIdByValue(const SQLite::Connection& connection, std::string_view value, bool useLike = false) { return details::OneToOneTableSelectIdByValue(connection, TableInfo::TableName(), TableInfo::ValueName(), value, useLike); } // Selects the value from the table, returning it if it exists. static std::optional SelectValueById(SQLite::Connection& connection, id_t id) { return details::OneToOneTableSelectValueById(connection, TableInfo::TableName(), TableInfo::ValueName(), id); } // Gets all row ids from the table. static std::vector GetAllRowIds(const SQLite::Connection& connection, size_t limit = 0) { return details::OneToOneTableGetAllRowIds(connection, TableInfo::TableName(), TableInfo::ValueName(), limit); } // Ensures that the given value exists in the table, returning the rowid. static SQLite::rowid_t EnsureExists(SQLite::Connection& connection, std::string_view value, bool overwriteLikeMatch = false) { return details::OneToOneTableEnsureExists(connection, TableInfo::TableName(), TableInfo::ValueName(), value, overwriteLikeMatch); } // Removes the given row by its rowid if it is no longer referenced. static void DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) { return details::OneToOneTableDeleteById(connection, TableInfo::TableName(), id); } // Removes data that is no longer needed for an index that is to be published. // Preserving the values index will improve searching when it is primarily done by equality. static void PrepareForPackaging(SQLite::Connection& connection, bool preserveValuesIndex = false) { details::OneToOneTablePrepareForPackaging(connection, TableInfo::TableName(), true, preserveValuesIndex); } // Removes data that is no longer needed for an index that is to be published. static void PrepareForPackaging_deprecated(SQLite::Connection& connection) { details::OneToOneTablePrepareForPackaging(connection, TableInfo::TableName(), false, false); } // Gets the total number of rows in the table. static uint64_t GetCount(const SQLite::Connection& connection) { return details::OneToOneTableGetCount(connection, TableInfo::TableName()); } // Determines if the table is empty. static bool IsEmpty(SQLite::Connection& connection) { return details::OneToOneTableIsEmpty(connection, TableInfo::TableName()); } // Checks the consistency of the table. static bool CheckConsistency(const SQLite::Connection& connection, bool log) { return details::OneToOneTableCheckConsistency(connection, TableInfo::TableName(), TableInfo::ValueName(), log); } }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PathPartTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { using namespace std::string_view_literals; static constexpr std::string_view s_PathPartTable_Table_Name = "pathparts"sv; static constexpr std::string_view s_PathPartTable_PrimaryKeyIndex_Name = "pathparts_pkindex"sv; static constexpr std::string_view s_PathPartTable_ParentIndex_Name = "pathparts_parentidx"sv; static constexpr std::string_view s_PathPartTable_ParentValue_Name = "parent"sv; static constexpr std::string_view s_PathPartTable_PartValue_Name = "pathpart"sv; namespace { // Attempts to select a path part given the input. // Returns an no value if none exists, or the rowid of the part if it is found. std::optional SelectPathPart(SQLite::Connection& connection, std::optional parent, std::string_view part) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::RowIDName).From(s_PathPartTable_Table_Name). Where(s_PathPartTable_ParentValue_Name).Equals(parent).And(s_PathPartTable_PartValue_Name).Equals(part); SQLite::Statement select = builder.Prepare(connection); if (select.Step()) { return select.GetColumn(0); } else { return {}; } } // Inserts the given path part into the table, returning the rowid of the inserted row. SQLite::rowid_t InsertPathPart(SQLite::Connection& connection, std::optional parent, std::string_view part) { THROW_HR_IF(E_INVALIDARG, part.empty()); SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_PathPartTable_Table_Name).Columns({ s_PathPartTable_ParentValue_Name, s_PathPartTable_PartValue_Name }).Values(parent, part); builder.Execute(connection); return connection.GetLastInsertRowID(); } // Inserts the no path part into the table. SQLite::rowid_t InsertNoPathPart(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder. InsertInto(s_PathPartTable_Table_Name). Columns({ SQLite::RowIDName, s_PathPartTable_ParentValue_Name, s_PathPartTable_PartValue_Name }). Values(PathPartTable::NoPathId, std::optional{}, std::string_view{}); builder.Execute(connection); return connection.GetLastInsertRowID(); } // Gets the parent of a given part by id. // This should only be called when the part must exist, as it will throw if not found. std::optional GetParentById(SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.Select(s_PathPartTable_ParentValue_Name).From(s_PathPartTable_Table_Name).Where(SQLite::RowIDName).Equals(id); SQLite::Statement select = builder.Prepare(connection); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED, !select.Step()); if (!select.GetColumnIsNull(0)) { return select.GetColumn(0); } else { return {}; } } // Determines if any part references this one as their parent. bool IsLeafPart(SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_PathPartTable_Table_Name).Where(s_PathPartTable_ParentValue_Name).Equals(id); SQLite::Statement select = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !select.Step()); // No rows with this as a parent means it is a leaf. return (select.GetColumn(0) == 0); } // Removes the given part by id. void RemovePartById(SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_PathPartTable_Table_Name).Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); } } // Starting in V1.1, all code should be going this route of creating named indices rather than using primary or unique keys on columns. // The resulting database will function the same, but give us control to drop the indices to reduce space. void PathPartTable::Create(SQLite::Connection& connection) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createPathParts_v1_1"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_PathPartTable_Table_Name).Columns({ IntegerPrimaryKey(), ColumnBuilder(s_PathPartTable_ParentValue_Name, Type::Int64), ColumnBuilder(s_PathPartTable_PartValue_Name, Type::Text).NotNull() }); createTableBuilder.Execute(connection); StatementBuilder createPKIndexBuilder; createPKIndexBuilder.CreateUniqueIndex(s_PathPartTable_PrimaryKeyIndex_Name).On(s_PathPartTable_Table_Name).Columns({ s_PathPartTable_PartValue_Name, s_PathPartTable_ParentValue_Name }); createPKIndexBuilder.Execute(connection); StatementBuilder createIndexBuilder; createIndexBuilder.CreateIndex(s_PathPartTable_ParentIndex_Name).On(s_PathPartTable_Table_Name).Columns(s_PathPartTable_ParentValue_Name); createIndexBuilder.Execute(connection); savepoint.Commit(); } void PathPartTable::Create_deprecated(SQLite::Connection& connection) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createPathParts_v1_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_PathPartTable_Table_Name).Columns({ ColumnBuilder(s_PathPartTable_ParentValue_Name, Type::Int64), ColumnBuilder(s_PathPartTable_PartValue_Name, Type::Text).NotNull(), PrimaryKeyBuilder({ s_PathPartTable_PartValue_Name, s_PathPartTable_ParentValue_Name }) }); createTableBuilder.Execute(connection); StatementBuilder createIndexBuilder; createIndexBuilder.CreateIndex(s_PathPartTable_ParentIndex_Name).On(s_PathPartTable_Table_Name).Columns(s_PathPartTable_ParentValue_Name); createIndexBuilder.Execute(connection); savepoint.Commit(); } void PathPartTable::Drop(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable(s_PathPartTable_Table_Name); dropTableBuilder.Execute(connection); } std::string_view PathPartTable::TableName() { return s_PathPartTable_Table_Name; } std::string_view PathPartTable::ValueName() { return s_PathPartTable_PartValue_Name; } std::tuple EnsurePathExistsInternal(SQLite::Connection& connection, const std::filesystem::path& relativePath, bool createIfNotFound) { THROW_HR_IF(E_INVALIDARG, !relativePath.has_relative_path()); THROW_HR_IF(E_INVALIDARG, relativePath.has_root_path()); THROW_HR_IF(E_INVALIDARG, !relativePath.has_filename()); std::unique_ptr savepoint; if (createIfNotFound) { savepoint = std::make_unique(SQLite::Savepoint::Create(connection, "ensurepathexists_v1_0")); } bool partsAdded = false; std::optional parent; for (const auto& part : relativePath) { std::string utf8part = part.u8string(); std::optional current = SelectPathPart(connection, parent, utf8part); if (!current) { if (createIfNotFound) { partsAdded = true; current = InsertPathPart(connection, parent, utf8part); } else { // Current part was not found, and we were told not to create. // Return false to indicate that the path does not exist. return {}; } } parent = current; } if (savepoint) { savepoint->Commit(); } // If we get this far, the path exists. // If we were asked to create it, return whether we needed to or it was already present. // If not, then true indicates that it exists. return { (createIfNotFound ? partsAdded : true), parent.value() }; } std::tuple PathPartTable::EnsurePathExists(SQLite::Connection& connection, const std::optional& relativePath, bool createIfNotFound) { if (relativePath) { return EnsurePathExistsInternal(connection, relativePath.value(), createIfNotFound); } std::unique_ptr savepoint; if (createIfNotFound) { savepoint = std::make_unique(SQLite::Savepoint::Create(connection, "ensurepathexists_v1_0")); } bool partsAdded = false; std::optional noPathPart = SelectPathPart(connection, {}, {}); if (!noPathPart) { if (createIfNotFound) { partsAdded = true; noPathPart = InsertNoPathPart(connection); } else { // Not found, and we were told not to create. // Return false to indicate that the path does not exist. return {}; } } if (savepoint) { savepoint->Commit(); } // If we get this far, the path exists. // If we were asked to create it, return whether we needed to or it was already present. // If not, then true indicates that it exists. return { (createIfNotFound ? partsAdded : true), noPathPart.value() }; } std::optional PathPartTable::GetPathById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.Select({ s_PathPartTable_ParentValue_Name, s_PathPartTable_PartValue_Name }). From(s_PathPartTable_Table_Name).Where(SQLite::RowIDName).Equals(SQLite::Builder::Unbound); SQLite::Statement select = builder.Prepare(connection); SQLite::rowid_t currentPart = id; std::string result; while (true) { select.Reset(); select.Bind(1, currentPart); if (select.Step()) { std::string partValue = select.GetColumn(1); if (result.empty()) { result = partValue; } else { result = partValue + '/' + result; } if (select.GetColumnIsNull(0)) { // If the parent of this column is null, then we have reached the relative root break; } else { currentPart = select.GetColumn(0); } } else { if (currentPart == id) { // The given id did not reference an actual path return {}; } else { // We found a broken path AICLI_LOG(Repo, Error, << "Path part references an invalid parent: " << currentPart); THROW_HR(APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED); } } } return result; } void PathPartTable::RemovePathById(SQLite::Connection& connection, SQLite::rowid_t id) { // Don't bother removing the pathless id if (id == NoPathId) { return; } SQLite::rowid_t currentPartToRemove = id; while (IsLeafPart(connection, currentPartToRemove)) { std::optional parent = GetParentById(connection, currentPartToRemove); RemovePartById(connection, currentPartToRemove); // If parent was NULL, this was a root part and we can stop if (!parent) { break; } else { currentPartToRemove = parent.value(); } } } void PathPartTable::PrepareForPackaging(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "pfpPathParts_v1_1"); PrepareForPackaging_deprecated(connection); SQLite::Builder::StatementBuilder dropPKIndexBuilder; dropPKIndexBuilder.DropIndex(s_PathPartTable_PrimaryKeyIndex_Name); dropPKIndexBuilder.Execute(connection); savepoint.Commit(); } void PathPartTable::PrepareForPackaging_deprecated(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder dropIndexBuilder; dropIndexBuilder.DropIndex(s_PathPartTable_ParentIndex_Name); dropIndexBuilder.Execute(connection); } bool PathPartTable::CheckConsistency(const SQLite::Connection& connection, bool log) { using QCol = SQLite::Builder::QualifiedColumn; // Build a select statement to find pathpart rows containing references to parents with nonexistent rowids // Such as: // Select l.rowid, l.parent from pathparts as l left outer join pathparts as r on l.parent = r.rowid where l.parent is not null and r.pathpart is null constexpr std::string_view s_left = "left"sv; constexpr std::string_view s_right = "right"sv; bool result = true; { SQLite::Builder::StatementBuilder builder; builder. Select({ QCol(s_left, SQLite::RowIDName), QCol(s_left, s_PathPartTable_ParentValue_Name) }). From(s_PathPartTable_Table_Name).As(s_left). LeftOuterJoin(s_PathPartTable_Table_Name).As(s_right).On(QCol(s_left, s_PathPartTable_ParentValue_Name), QCol(s_right, SQLite::RowIDName)). Where(QCol(s_left, s_PathPartTable_ParentValue_Name)).IsNotNull().And(QCol(s_right, s_PathPartTable_PartValue_Name)).IsNull(); SQLite::Statement select = builder.Prepare(connection); while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] pathparts [" << select.GetColumn(0) << "] refers to " << s_PathPartTable_ParentValue_Name << " [" << select.GetColumn(1) << "]"); } } if (!result && !log) { return result; } { // Build a select statement to find values that contain an embedded null character // Such as: // Select count(*) from table where instr(value,char(0))>0 SQLite::Builder::StatementBuilder builder; builder. Select({ SQLite::RowIDName, s_PathPartTable_PartValue_Name }). From(s_PathPartTable_Table_Name). WhereValueContainsEmbeddedNullCharacter(s_PathPartTable_PartValue_Name); SQLite::Statement select = builder.Prepare(connection); while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] value in table [" << s_PathPartTable_Table_Name << "] at row [" << select.GetColumn(0) << "] contains an embedded null character and starts with [" << select.GetColumn(1) << "]"); } } return result; } bool PathPartTable::IsEmpty(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_PathPartTable_Table_Name); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) == 0); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { // A table that represents the parts of a path struct PathPartTable { // The id type using id_t = SQLite::rowid_t; // The id used when no path is present. constexpr static id_t NoPathId = -1; // Creates the table with named indices. static void Create(SQLite::Connection& connection); // Creates the table with standard primary keys. static void Create_deprecated(SQLite::Connection& connection); // Drops the table. static void Drop(SQLite::Connection& connection); // Gets the table name. static std::string_view TableName(); // Gets the value name. static std::string_view ValueName(); // Ensure that the given relative path exists within the path parts table. // If createIfNotFound is true, the function will add the parts as needed. // The result bool will indicate whether it was necessary to add the path (true), // or it was already present (false). // If createIfNotFound is false, the function will simply determine if the path is present. // The result bool will indicate whether the path was found (true), or not (false). // In all cases except createIfNotFound == false and result bool == false, the int64_t value // will be valid and the rowid of the final path part in the path. // If relativePath is not provided, will always map to an entry with NoPathId. static std::tuple EnsurePathExists(SQLite::Connection& connection, const std::optional& relativePath, bool createIfNotFound); // Gets the path string using the given id as the leaf. static std::optional GetPathById(const SQLite::Connection& connection, SQLite::rowid_t id); // Removes the path that terminates at the given id. // Will not remove a path part if it is referenced. static void RemovePathById(SQLite::Connection& connection, SQLite::rowid_t id); // Removes data that is no longer needed for an index that is to be published. static void PrepareForPackaging(SQLite::Connection& connection); // Removes data that is no longer needed for an index that is to be published. static void PrepareForPackaging_deprecated(SQLite::Connection& connection); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. static bool CheckConsistency(const SQLite::Connection& connection, bool log); // Determines if the table is empty. static bool IsEmpty(SQLite::Connection& connection); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "Microsoft/Schema/ISQLiteIndex.h" #include "Public/winget/RepositorySearch.h" #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { // Table for holding temporary search results. struct SearchResultsTable : public SQLite::TempTable { SearchResultsTable(const SQLite::Connection& connection); SearchResultsTable(const SearchResultsTable&) = delete; SearchResultsTable& operator=(const SearchResultsTable&) = delete; SearchResultsTable(SearchResultsTable&&) = default; SearchResultsTable& operator=(SearchResultsTable&&) = default; // Performs the requested search type on the requested field. void SearchOnField(const PackageMatchFilter& filter); // Removes rows with manifest ids whose sort order is below the highest one. void RemoveDuplicateManifestRows(); // Prepares the table for a filtering pass. void PrepareToFilter(); // Performs the requested filter type on the requested field. void FilterOnField(const PackageMatchFilter& filter); // Completes a filtering pass, removing filtered rows. void CompleteFilter(); // Gets the results from the table. ISQLiteIndex::SearchResult GetSearchResults(size_t limit = 0); protected: // Builds the search statement for the specified field and match type. std::vector BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, PackageMatchField field, MatchType match) const; virtual std::vector BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const; static bool MatchUsesLike(MatchType match); void BindStatementForMatchType(SQLite::Statement& statement, MatchType match, int bindIndex, std::string_view value); virtual void BindStatementForMatchType(SQLite::Statement& statement, const PackageMatchFilter& filter, const std::vector& bindIndex); private: const SQLite::Connection& m_connection; int m_sortOrdinalValue = 0; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchResultsTable.h" #include #include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/NameTable.h" #include "Microsoft/Schema/1_0/MonikerTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_0/TagsTable.h" #include "Microsoft/Schema/1_0/CommandsTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace { using namespace std::string_literals; using namespace std::string_view_literals; constexpr std::string_view s_SearchResultsTable_Manifest = "manifest"sv; constexpr std::string_view s_SearchResultsTable_MatchField = "field"sv; constexpr std::string_view s_SearchResultsTable_MatchType = "match"sv; constexpr std::string_view s_SearchResultsTable_MatchValue = "value"sv; constexpr std::string_view s_SearchResultsTable_SortValue = "sort"sv; constexpr std::string_view s_SearchResultsTable_Filter = "filter"sv; constexpr std::string_view s_SearchResultsTable_Index_Suffix = "_i_m"sv; constexpr std::string_view s_SearchResultsTable_SubSelect_TableAlias = "valueTable"sv; constexpr std::string_view s_SearchResultsTable_SubSelect_ManifestAlias = "m"sv; constexpr std::string_view s_SearchResultsTable_SubSelect_ValueAlias = "v"sv; } SearchResultsTable::SearchResultsTable(const SQLite::Connection& connection) : m_connection(connection) { using namespace SQLite::Builder; { StatementBuilder builder; builder.CreateTable(GetQualifiedName()).BeginColumns(); builder.Column(ColumnBuilder(s_SearchResultsTable_Manifest, Type::RowId).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_MatchField, Type::Int).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_MatchType, Type::Int).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_MatchValue, Type::Text).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_SortValue, Type::Int).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_Filter, Type::Bool).NotNull()); builder.EndColumns(); builder.Execute(m_connection); } InitDropStatement(m_connection); { SQLite::Builder::QualifiedTable index = GetQualifiedName(); std::string indexName(index.Table); indexName += s_SearchResultsTable_Index_Suffix; index.Table = indexName; StatementBuilder builder; builder.CreateIndex(indexName).On(GetQualifiedName().Table).Columns(s_SearchResultsTable_Manifest); builder.Execute(m_connection); } } void SearchResultsTable::SearchOnField(const PackageMatchFilter& filter) { using namespace SQLite::Builder; int sortOrdinal = m_sortOrdinalValue++; // Create an insert statement to select values into the table as requested. // The goal is a statement like this: // INSERT INTO // SELECT valueTable.m, , , valueTable.v, , FROM // (SELECT manifest.rowid as m, manifest.id as v from manifest join ids on manifest.id = ids.rowid where ids.id = ) AS valueTable // Where the subselect is built by the owning table. StatementBuilder builder; builder.InsertInto(GetQualifiedName()).Select(). Column(QualifiedColumn(s_SearchResultsTable_SubSelect_TableAlias, s_SearchResultsTable_SubSelect_ManifestAlias)). Value(filter.Field). Value(filter.Type). Column(QualifiedColumn(s_SearchResultsTable_SubSelect_TableAlias, s_SearchResultsTable_SubSelect_ValueAlias)). Value(sortOrdinal). Value(false). From().BeginParenthetical(); // Add the field specific portion std::vector bindIndex = BuildSearchStatement(builder, filter.Field, filter.Type); if (bindIndex.empty()) { AICLI_LOG(Repo, Verbose, << "PackageMatchField not supported in this version: " << ToString(filter.Field)); return; } builder.EndParenthetical().As(s_SearchResultsTable_SubSelect_TableAlias); SQLite::Statement statement = builder.Prepare(m_connection); BindStatementForMatchType(statement, filter, bindIndex); statement.Execute(); AICLI_LOG(SQL, Verbose, << "Search found " << m_connection.GetChanges() << " rows"); } void SearchResultsTable::RemoveDuplicateManifestRows() { using namespace SQLite::Builder; // Create a delete statement to leave only one row with a given manifest. // This will arbitrarily choose one of the rows if multiple have the same lowest sort order. // The goal is a statement like this: // DELETE from where rowid not in ( // SELECT rowid from ( // SELECT rowid, min(sort) from group by manifest // ) // ) StatementBuilder builder; builder.DeleteFrom(GetQualifiedName()).Where(SQLite::RowIDName).Not().In().BeginParenthetical(). Select(SQLite::RowIDName).From().BeginParenthetical(). Select().Column(SQLite::RowIDName).Column(Aggregate::Min, s_SearchResultsTable_SortValue).From(GetQualifiedName()).GroupBy(s_SearchResultsTable_Manifest). EndParenthetical(). EndParenthetical(); builder.Execute(m_connection); AICLI_LOG(SQL, Verbose, << "Removed " << m_connection.GetChanges() << " duplicate rows"); } void SearchResultsTable::PrepareToFilter() { // Reset all filter values to unselected SQLite::Builder::StatementBuilder builder; builder.Update(GetQualifiedName()).Set().Column(s_SearchResultsTable_Filter).Equals(false); builder.Execute(m_connection); } void SearchResultsTable::FilterOnField(const PackageMatchFilter& filter) { using namespace SQLite::Builder; // Create an update statement to mark rows that are found by the search. // This will arbitrarily choose one of the rows if multiple have the same lowest sort order. // The goal is a statement like this: // UPDATE set filter = 1 where manifest in ( // SELECT m from ( // SELECT manifest.rowid as m, manifest.id as v from manifest join ids on manifest.id = ids.rowid where ids.id = // ) // ) StatementBuilder builder; builder.Update(GetQualifiedName()).Set().Column(s_SearchResultsTable_Filter).Equals(true).Where(s_SearchResultsTable_Manifest).In().BeginParenthetical(). Select(s_SearchResultsTable_SubSelect_ManifestAlias).From().BeginParenthetical(); // Add the field specific portion std::vector bindIndex = BuildSearchStatement(builder, filter.Field, filter.Type); if (bindIndex.empty()) { AICLI_LOG(Repo, Verbose, << "PackageMatchField not supported in this version: " << ToString(filter.Field)); return; } builder.EndParenthetical().EndParenthetical(); SQLite::Statement statement = builder.Prepare(m_connection); BindStatementForMatchType(statement, filter, bindIndex); statement.Execute(); AICLI_LOG(SQL, Verbose, << "Filter kept " << m_connection.GetChanges() << " rows"); } void SearchResultsTable::CompleteFilter() { // Delete all unselected values SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(GetQualifiedName()).Where(s_SearchResultsTable_Filter).Equals(false); builder.Execute(m_connection); AICLI_LOG(SQL, Verbose, << "Filter deleted " << m_connection.GetChanges() << " rows"); } ISQLiteIndex::SearchResult SearchResultsTable::GetSearchResults(size_t limit) { constexpr std::string_view tempTableAlias = "t"sv; using namespace SQLite::Builder; using QCol = QualifiedColumn; // Select all unique ids from the results table, and their highest ordered match. // The goal is a statement like this: // SELECT m.id, field, match, value, min(sort) from join manifest on rowid = manifest group by m.id order by t.sort // Through the "group by m.id", we will only ever have one row per id, and the "min(sort)" returns us one of the rows that matched // through the earliest search. We also order by the sort value to have the earliest search matches first in the list StatementBuilder builder; builder.Select(). Column(QCol(ManifestTable::TableName(), IdTable::ValueName())). Column(QCol(tempTableAlias, s_SearchResultsTable_MatchField)). Column(QCol(tempTableAlias, s_SearchResultsTable_MatchType)). Column(QCol(tempTableAlias, s_SearchResultsTable_MatchValue)). Column(Aggregate::Min, QCol(tempTableAlias, s_SearchResultsTable_SortValue)). From(GetQualifiedName()).As(tempTableAlias). Join(ManifestTable::TableName()).On(QCol(tempTableAlias, s_SearchResultsTable_Manifest), QCol(ManifestTable::TableName(), SQLite::RowIDName)). GroupBy(QCol(ManifestTable::TableName(), IdTable::ValueName())).OrderBy(QCol(tempTableAlias, s_SearchResultsTable_SortValue)); SQLite::Statement select = builder.Prepare(m_connection); ISQLiteIndex::SearchResult result; while (select.Step()) { if (limit && result.Matches.size() >= limit) { break; } result.Matches.emplace_back(select.GetColumn(0), PackageMatchFilter(select.GetColumn(1), select.GetColumn(2), select.GetColumn(3))); } result.Truncated = (select.GetState() != SQLite::Statement::State::Completed); return result; } std::vector SearchResultsTable::BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, PackageMatchField field, MatchType match) const { return BuildSearchStatement(builder, field, s_SearchResultsTable_SubSelect_ManifestAlias, s_SearchResultsTable_SubSelect_ValueAlias, MatchUsesLike(match)); } std::vector SearchResultsTable::BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const { switch (field) { case PackageMatchField::Id: return ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); case PackageMatchField::Name: return ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); case PackageMatchField::Moniker: return ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); case PackageMatchField::Tag: return ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); case PackageMatchField::Command: return ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); default: return {}; } } bool SearchResultsTable::MatchUsesLike(MatchType match) { return (match != MatchType::Exact); } void SearchResultsTable::BindStatementForMatchType(SQLite::Statement& statement, MatchType match, int bindIndex, std::string_view value) { std::string valueToUse; if (MatchUsesLike(match)) { valueToUse = SQLite::EscapeStringForLike(value); } else { valueToUse = value; } switch (match) { case AppInstaller::Repository::MatchType::StartsWith: valueToUse += '%'; break; case AppInstaller::Repository::MatchType::Substring: valueToUse = "%"s + valueToUse + '%'; break; default: // No changes required for others. break; } statement.Bind(bindIndex, valueToUse); } void SearchResultsTable::BindStatementForMatchType(SQLite::Statement& statement, const PackageMatchFilter& filter, const std::vector& bindIndex) { // TODO: Implement these more complex match types if (filter.Type == MatchType::Wildcard || filter.Type == MatchType::Fuzzy || filter.Type == MatchType::FuzzySubstring) { AICLI_LOG(Repo, Verbose, << "Specific match type not implemented, skipping: " << ToString(filter.Type)); return; } BindStatementForMatchType(statement, filter.Type, bindIndex[0], filter.Value); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/TagsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToManyTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; struct TagsTableInfo { inline static constexpr std::string_view TableName() { return "tags"sv; } inline static constexpr std::string_view ValueName() { return "tag"sv; } }; } // The table for Tags. using TagsTable = OneToManyTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/VersionTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToOneTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace details { using namespace std::string_view_literals; struct VersionTableInfo { inline static constexpr std::string_view TableName() { return "versions"sv; } inline static constexpr std::string_view ValueName() { return "version"sv; } }; } // The table for Version. using VersionTable = OneToOneTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/VirtualTableBase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { // Since we have multiple manifest columns pointing to same table now(i.e. versions table), and previous assumption // is manifest table column always has same name as referenced table column (i.e. manifest.version and versions.version), // we need to differentiate manifest column name and referenced table column name(i.e. manifest.arp_min_version and versions.version) // An optional ManifestColumnName() is added to virtual table for the above purpose, and can be used in the future if needed. // To let the template codes better determine virtual tables, the following struct is created. // Struct used as the base for virtual tables. // Future virtual tables reusing an existing table should derive from this and implement // static std::string_view ManifestColumnName(); // in addition to regular table info methods. struct VirtualTableBase { }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_0/Interface.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public V1_0::Interface { // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; std::pair UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; void RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) override; void PrepareForPackaging(SQLite::Connection& connection) override; bool CheckConsistency(const SQLite::Connection& connection, bool log) const override; SearchResult Search(const SQLite::Connection& connection, const SearchRequest& request) const override; std::vector GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const override; // Version 1.1 MetadataResult GetMetadataByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId) const override; void SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value) override; // Version 1.7 void DropTables(SQLite::Connection& connection) override; protected: std::unique_ptr CreateSearchResultsTable(const SQLite::Connection& connection) const override; void PerformQuerySearch(V1_0::SearchResultsTable& resultsTable, const RequestMatch& query) const override; V1_0::OneToManyTableSchema GetOneToManyTableSchema() const override; virtual SearchResult SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const; virtual void PrepareForPackaging(SQLite::Connection& connection, bool vacuum); // Gets a property already knowing that the manifest id is valid. virtual std::optional GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_1/Interface.h" #include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/NameTable.h" #include "Microsoft/Schema/1_0/MonikerTable.h" #include "Microsoft/Schema/1_0/VersionTable.h" #include "Microsoft/Schema/1_0/ChannelTable.h" #include "Microsoft/Schema/1_0/PathPartTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_0/TagsTable.h" #include "Microsoft/Schema/1_0/CommandsTable.h" #include "Microsoft/Schema/1_1/PackageFamilyNameTable.h" #include "Microsoft/Schema/1_1/ProductCodeTable.h" #include "Microsoft/Schema/1_1/SearchResultsTable.h" #include "Microsoft/Schema/1_1/ManifestMetadataTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { SQLite::Version Interface::GetVersion() const { return { 1, 1 }; } void Interface::CreateTables(SQLite::Connection& connection, CreateOptions options) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createtables_v1_1"); V1_0::IdTable::Create(connection); V1_0::NameTable::Create(connection); V1_0::MonikerTable::Create(connection); V1_0::VersionTable::Create(connection); V1_0::ChannelTable::Create(connection); V1_0::PathPartTable::Create(connection); V1_0::ManifestTable::Create(connection, { { V1_0::IdTable::ValueName(), true, false }, { V1_0::NameTable::ValueName(), false, false }, { V1_0::MonikerTable::ValueName(), false, false }, { V1_0::VersionTable::ValueName(), true, false }, { V1_0::ChannelTable::ValueName(), true, false }, { V1_0::PathPartTable::ValueName(), false, WI_IsFlagClear(options, CreateOptions::SupportPathless) } }); V1_0::TagsTable::Create(connection, GetOneToManyTableSchema()); V1_0::CommandsTable::Create(connection, GetOneToManyTableSchema()); PackageFamilyNameTable::Create(connection, GetOneToManyTableSchema()); ProductCodeTable::Create(connection, GetOneToManyTableSchema()); savepoint.Commit(); } SQLite::rowid_t Interface::AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addmanifest_v1_1"); SQLite::rowid_t manifestId = V1_0::Interface::AddManifest(connection, manifest, relativePath); // Add the new 1.1 data // These system reference strings are all stored with their cases folded so that they can be // looked up ordinally; enabling the index to provide efficient searches. PackageFamilyNameTable::EnsureExistsAndInsert(connection, manifest.GetPackageFamilyNames(), manifestId); ProductCodeTable::EnsureExistsAndInsert(connection, manifest.GetProductCodes(), manifestId); savepoint.Commit(); return manifestId; } std::pair Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updatemanifest_v1_1"); auto [indexModified, manifestId] = V1_0::Interface::UpdateManifest(connection, manifest, relativePath); // Update new 1:N tables as necessary indexModified = PackageFamilyNameTable::UpdateIfNeededByManifestId(connection, manifest.GetPackageFamilyNames(), manifestId) || indexModified; indexModified = ProductCodeTable::UpdateIfNeededByManifestId(connection, manifest.GetProductCodes(), manifestId) || indexModified; savepoint.Commit(); return { indexModified, manifestId }; } void Interface::RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "RemoveManifestById_v1_1"); V1_0::Interface::RemoveManifestById(connection, manifestId); // Remove all of the new 1:N data that is no longer referenced. PackageFamilyNameTable::DeleteIfNotNeededByManifestId(connection, manifestId); ProductCodeTable::DeleteIfNotNeededByManifestId(connection, manifestId); if (ManifestMetadataTable::Exists(connection)) { ManifestMetadataTable::DeleteByManifestId(connection, manifestId); } savepoint.Commit(); } void Interface::PrepareForPackaging(SQLite::Connection& connection) { PrepareForPackaging(connection, true); } bool Interface::CheckConsistency(const SQLite::Connection& connection, bool log) const { bool result = V1_0::Interface::CheckConsistency(connection, log); // If the v1.0 index was consistent, or if full logging of inconsistency was requested, check the v1.1 data. if (result || log) { result = PackageFamilyNameTable::CheckConsistency(connection, log) && result; } if (result || log) { result = ProductCodeTable::CheckConsistency(connection, log) && result; } return result; } ISQLiteIndex::SearchResult Interface::Search(const SQLite::Connection& connection, const SearchRequest& request) const { SearchRequest updatedRequest = request; return SearchInternal(connection, updatedRequest); } std::vector Interface::GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const { switch (property) { case PackageVersionMultiProperty::PackageFamilyName: return PackageFamilyNameTable::GetValuesByManifestId(connection, primaryId); case PackageVersionMultiProperty::ProductCode: return ProductCodeTable::GetValuesByManifestId(connection, primaryId); default: return V1_0::Interface::GetMultiPropertyByPrimaryId(connection, primaryId, property); } } ISQLiteIndex::MetadataResult Interface::GetMetadataByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId) const { ISQLiteIndex::MetadataResult result; if (ManifestMetadataTable::Exists(connection)) { result = ManifestMetadataTable::GetMetadataByManifestId(connection, manifestId); } return result; } void Interface::SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setmetadatabymanifestid_v1_1"); if (!ManifestMetadataTable::Exists(connection)) { ManifestMetadataTable::Create(connection); } ManifestMetadataTable::SetMetadataByManifestId(connection, manifestId, metadata, value); savepoint.Commit(); } void Interface::DropTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "drop_tables_v1_1"); V1_0::Interface::DropTables(connection); PackageFamilyNameTable::Drop(connection); ProductCodeTable::Drop(connection); ManifestMetadataTable::Drop(connection); savepoint.Commit(); } std::unique_ptr Interface::CreateSearchResultsTable(const SQLite::Connection& connection) const { return std::make_unique(connection); } void Interface::PerformQuerySearch(V1_0::SearchResultsTable& resultsTable, const RequestMatch& query) const { // First, do an exact match search for the folded system reference strings // We do this first because it is exact, and likely won't match anything else if it matches this. PackageMatchFilter filter(PackageMatchField::PackageFamilyName, MatchType::Exact, Utility::FoldCase(query.Value)); resultsTable.SearchOnField(filter); filter.Field = PackageMatchField::ProductCode; resultsTable.SearchOnField(filter); // Then do the 1.0 search V1_0::Interface::PerformQuerySearch(resultsTable, query); } V1_0::OneToManyTableSchema Interface::GetOneToManyTableSchema() const { return V1_0::OneToManyTableSchema::Version_1_1; } ISQLiteIndex::SearchResult Interface::SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const { // Update any system reference strings to be folded auto foldIfNeeded = [](PackageMatchFilter& filter) { if ((filter.Field == PackageMatchField::PackageFamilyName || filter.Field == PackageMatchField::ProductCode) && filter.Type == MatchType::Exact) { filter.Value = Utility::FoldCase(filter.Value); } }; for (auto& inclusion : request.Inclusions) { foldIfNeeded(inclusion); } for (auto& filter : request.Filters) { foldIfNeeded(filter); } return V1_0::Interface::Search(connection, request); } void Interface::PrepareForPackaging(SQLite::Connection& connection, bool vacuum) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareforpackaging_v1_1"); V1_0::IdTable::PrepareForPackaging(connection); V1_0::NameTable::PrepareForPackaging(connection); V1_0::MonikerTable::PrepareForPackaging(connection); V1_0::VersionTable::PrepareForPackaging(connection); V1_0::ChannelTable::PrepareForPackaging(connection); V1_0::PathPartTable::PrepareForPackaging(connection); V1_0::ManifestTable::PrepareForPackaging(connection, { V1_0::VersionTable::ValueName(), V1_0::ChannelTable::ValueName(), V1_0::PathPartTable::ValueName(), }); V1_0::TagsTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), false, false); V1_0::CommandsTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), false, false); PackageFamilyNameTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), true, true); ProductCodeTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), true, true); savepoint.Commit(); if (vacuum) { Vacuum(connection); } } std::optional Interface::GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const { switch (property) { case AppInstaller::Repository::PackageVersionProperty::Publisher: { // Publisher is not a primary data member in this version, but it may be stored in the metadata if (ManifestMetadataTable::Exists(connection)) { return ManifestMetadataTable::GetMetadataByManifestIdAndMetadata(connection, manifestId, PackageVersionMetadata::Publisher); } // No metadata, so no publisher return {}; } default: return V1_0::Interface::GetPropertyByManifestIdInternal(connection, manifestId, property); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestMetadataTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { using namespace SQLite; static constexpr std::string_view s_ManifestMetadataTable_Table_Name = "manifest_metadata"sv; static constexpr std::string_view s_ManifestMetadataTable_PrimaryKeyIndex_Name = "manifest_metadata_pk"sv; static constexpr std::string_view s_ManifestMetadataTable_Manifest_Column = "manifest"sv; static constexpr std::string_view s_ManifestMetadataTable_Metadata_Column = "metadata"sv; static constexpr std::string_view s_ManifestMetadataTable_Value_Column = "value"sv; bool ManifestMetadataTable::Exists(const SQLite::Connection& connection) { Builder::StatementBuilder builder; builder.Select(Builder::RowCount).From(Builder::Schema::MainTable). Where(Builder::Schema::TypeColumn).Equals(Builder::Schema::Type_Table).And(Builder::Schema::NameColumn).Equals(s_ManifestMetadataTable_Table_Name); Statement statement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !statement.Step()); return statement.GetColumn(0) != 0; } void ManifestMetadataTable::Create(SQLite::Connection& connection) { using namespace Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createmanifestmetadata_v1_1"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_ManifestMetadataTable_Table_Name).Columns({ ColumnBuilder(s_ManifestMetadataTable_Manifest_Column, Type::Int64).NotNull(), ColumnBuilder(s_ManifestMetadataTable_Metadata_Column, Type::Int64).NotNull(), ColumnBuilder(s_ManifestMetadataTable_Value_Column, Type::Text) }); createTableBuilder.Execute(connection); StatementBuilder createPKIndexBuilder; createPKIndexBuilder.CreateUniqueIndex(s_ManifestMetadataTable_PrimaryKeyIndex_Name).On(s_ManifestMetadataTable_Table_Name). Columns({ s_ManifestMetadataTable_Manifest_Column, s_ManifestMetadataTable_Metadata_Column }); createPKIndexBuilder.Execute(connection); savepoint.Commit(); } void ManifestMetadataTable::Drop(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTableIfExists(s_ManifestMetadataTable_Table_Name); dropTableBuilder.Execute(connection); } ISQLiteIndex::MetadataResult ManifestMetadataTable::GetMetadataByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId) { using namespace Builder; StatementBuilder builder; builder.Select({ s_ManifestMetadataTable_Metadata_Column, s_ManifestMetadataTable_Value_Column }).From(s_ManifestMetadataTable_Table_Name). Where(s_ManifestMetadataTable_Manifest_Column).Equals(manifestId); Statement statement = builder.Prepare(connection); ISQLiteIndex::MetadataResult result; while (statement.Step()) { result.emplace_back(std::make_pair(statement.GetColumn(0), statement.GetColumn(1))); } return result; } std::optional ManifestMetadataTable::GetMetadataByManifestIdAndMetadata(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata) { using namespace Builder; StatementBuilder builder; builder.Select(s_ManifestMetadataTable_Value_Column).From(s_ManifestMetadataTable_Table_Name). Where(s_ManifestMetadataTable_Manifest_Column).Equals(manifestId). And(s_ManifestMetadataTable_Metadata_Column).Equals(metadata); Statement statement = builder.Prepare(connection); if (statement.Step()) { return statement.GetColumn(0); } return {}; } void ManifestMetadataTable::SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value) { using namespace Builder; // First, we attempt to update an existing row. If not changes occurred, we then insert the new value. // UPSERT (aka ON CONFLICT) is not available to us, as it was only introduced in 3.24.0 (2018-06-04), // and we need to support Windows 10 (17763) which was released in 2017. StatementBuilder updateBuilder; updateBuilder.Update(s_ManifestMetadataTable_Table_Name).Set().Column(s_ManifestMetadataTable_Value_Column).Equals(value). Where(s_ManifestMetadataTable_Manifest_Column).Equals(manifestId).And(s_ManifestMetadataTable_Metadata_Column).Equals(metadata); updateBuilder.Execute(connection); // No changes means we need to insert the row if (connection.GetChanges() == 0) { StatementBuilder insertBuilder; insertBuilder.InsertInto(s_ManifestMetadataTable_Table_Name). Columns({ s_ManifestMetadataTable_Manifest_Column, s_ManifestMetadataTable_Metadata_Column, s_ManifestMetadataTable_Value_Column }) .Values(manifestId, metadata, value); insertBuilder.Execute(connection); } } void ManifestMetadataTable::DeleteByManifestId(SQLite::Connection & connection, SQLite::rowid_t manifestId) { using namespace Builder; StatementBuilder builder; builder.DeleteFrom(s_ManifestMetadataTable_Table_Name).Where(s_ManifestMetadataTable_Manifest_Column).Equals(manifestId); builder.Execute(connection); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Microsoft/Schema/ISQLiteIndex.h" #include "Public/winget/RepositorySearch.h" #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { // A table for storing arbitrary metadata on individual manifests. // The table and all metadata are optional. struct ManifestMetadataTable { // Determine if the table currently exists in the database. static bool Exists(const SQLite::Connection& connection); // Creates the table in the database. static void Create(SQLite::Connection& connection); // Drops the table. static void Drop(SQLite::Connection& connection); // Gets all metadata associated with the given manifest. // The table must exist. static ISQLiteIndex::MetadataResult GetMetadataByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId); // Gets the specific metadata value for the manifest, if it exists. // The table must exist. static std::optional GetMetadataByManifestIdAndMetadata(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata); // Sets the metadata value for the given manifest. // The table must exist. static void SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value); // Removes all metadata values for the given manifest. // The table must exist. static void DeleteByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/PackageFamilyNameTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToManyTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { namespace details { using namespace std::string_view_literals; struct PackageFamilyNameTableInfo { inline static constexpr std::string_view TableName() { return "pfns"sv; } inline static constexpr std::string_view ValueName() { return "pfn"sv; } }; } // The table for PackageFamilyName. using PackageFamilyNameTable = V1_0::OneToManyTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ProductCodeTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToManyTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { namespace details { using namespace std::string_view_literals; struct ProductCodeTableInfo { inline static constexpr std::string_view TableName() { return "productcodes"sv; } inline static constexpr std::string_view ValueName() { return "productcode"sv; } }; } // The table for ProductCode. using ProductCodeTable = V1_0::OneToManyTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/SearchResultsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/SearchResultsTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { // Table for holding temporary search results. struct SearchResultsTable : public V1_0::SearchResultsTable { SearchResultsTable(const SQLite::Connection& connection) : V1_0::SearchResultsTable(connection) {} SearchResultsTable(const SearchResultsTable&) = delete; SearchResultsTable& operator=(const SearchResultsTable&) = delete; SearchResultsTable(SearchResultsTable&&) = default; SearchResultsTable& operator=(SearchResultsTable&&) = default; protected: std::vector BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/SearchResultsTable_1_1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchResultsTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_1/PackageFamilyNameTable.h" #include "Microsoft/Schema/1_1/ProductCodeTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { std::vector SearchResultsTable::BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const { switch (field) { case PackageMatchField::PackageFamilyName: return V1_0::ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); case PackageMatchField::ProductCode: return V1_0::ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); default: return V1_0::SearchResultsTable::BuildSearchStatement(builder, field, manifestAlias, valueAlias, useLike); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_1/Interface.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_2 { // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public V1_1::Interface { Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; std::pair UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; void RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) override; bool CheckConsistency(const SQLite::Connection& connection, bool log) const override; std::vector GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const override; // Version 1.2 Utility::NormalizedName NormalizeName(std::string_view name, std::string_view publisher) const override; // Version 1.7 void DropTables(SQLite::Connection& connection) override; protected: std::unique_ptr CreateSearchResultsTable(const SQLite::Connection& connection) const override; SearchResult SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const override; void PrepareForPackaging(SQLite::Connection& connection, bool vacuum) override; // The name normalization utility Utility::NameNormalizer m_normalizer; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/Interface_1_2.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_2/Interface.h" #include "Microsoft/Schema/1_2/NormalizedPackageNameTable.h" #include "Microsoft/Schema/1_2/NormalizedPackagePublisherTable.h" #include "Microsoft/Schema/1_2/SearchResultsTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_2 { namespace anon { void AddNormalizedName( const Utility::NameNormalizer& normalizer, const Manifest::string_t& name, std::vector& out, Utility::NormalizationField fieldsToInclude = Utility::NormalizationField::None) { Utility::NormalizedString value = normalizer.NormalizeName(Utility::FoldCase(name)).GetNormalizedName(fieldsToInclude); if (std::find(out.begin(), out.end(), value) == out.end()) { out.emplace_back(std::move(value)); } } void AddLocalizationNormalizedName(const Utility::NameNormalizer& normalizer, const Manifest::ManifestLocalization& localization, std::vector& out) { if (localization.Contains(Manifest::Localization::PackageName)) { AddNormalizedName(normalizer, localization.Get(), out); } } void AddNormalizedPublisher(const Utility::NameNormalizer& normalizer, const Manifest::string_t& publisher, std::vector& out) { Utility::NormalizedString value = normalizer.NormalizePublisher(Utility::FoldCase(publisher)); if (std::find(out.begin(), out.end(), value) == out.end()) { out.emplace_back(std::move(value)); } } void AddLocalizationNormalizedPublisher(const Utility::NameNormalizer& normalizer, const Manifest::ManifestLocalization& localization, std::vector& out) { if (localization.Contains(Manifest::Localization::Publisher)) { AddNormalizedPublisher(normalizer, localization.Get(), out); } } std::vector GetNormalizedNames(const Utility::NameNormalizer& normalizer, const Manifest::Manifest& manifest) { std::vector result; AddLocalizationNormalizedName(normalizer, manifest.DefaultLocalization, result); for (const auto& loc : manifest.Localizations) { AddLocalizationNormalizedName(normalizer, loc, result); } // In addition to the names used for our display, add the display names from the ARP entries for (const auto& installer : manifest.Installers) { for (const auto& appsAndFeaturesEntry : installer.AppsAndFeaturesEntries) { if (!appsAndFeaturesEntry.DisplayName.empty()) { AddNormalizedName(normalizer, appsAndFeaturesEntry.DisplayName, result); // For arp display name, also add a copy with architecture info for more accurate correlation. AddNormalizedName(normalizer, appsAndFeaturesEntry.DisplayName, result, Utility::NormalizationField::Architecture); } } } return result; } std::vector GetNormalizedPublishers(const Utility::NameNormalizer& normalizer, const Manifest::Manifest& manifest) { std::vector result; AddLocalizationNormalizedPublisher(normalizer, manifest.DefaultLocalization, result); for (const auto& loc : manifest.Localizations) { AddLocalizationNormalizedPublisher(normalizer, loc, result); } // In addition to the publishers used for our display, add the publishers from the ARP entries for (const auto& installer : manifest.Installers) { for (const auto& appsAndFeaturesEntry : installer.AppsAndFeaturesEntries) { if (!appsAndFeaturesEntry.Publisher.empty()) { AddNormalizedPublisher(normalizer, appsAndFeaturesEntry.Publisher, result); } } } return result; } // Update NormalizedNameAndPublisher with normalization and folding // Returns true if the normalized name contains normalization field of fieldsToInclude bool UpdateNormalizedNameAndPublisher( PackageMatchFilter& filter, const Utility::NameNormalizer& normalizer, Utility::NormalizationField fieldsToInclude) { Utility::NormalizedName normalized = normalizer.Normalize(Utility::FoldCase(filter.Value), Utility::FoldCase(filter.Additional.value())); filter.Value = normalized.GetNormalizedName(fieldsToInclude); filter.Additional = normalized.Publisher(); return WI_AreAllFlagsSet(normalized.GetNormalizedFields(), fieldsToInclude); } // Update NormalizedNameAndPublisher with normalization and folding // Returns true if any of normalized name contains normalization field of fieldsToInclude bool UpdatePackageMatchFilters( std::vector& filters, const Utility::NameNormalizer& normalizer, Utility::NormalizationField normalizedNameFieldsToFilter = Utility::NormalizationField::None) { bool normalizedNameFieldsFound = false; for (auto itr = filters.begin(); itr != filters.end();) { if (itr->Field == PackageMatchField::NormalizedNameAndPublisher && itr->Type == MatchType::Exact) { if (!UpdateNormalizedNameAndPublisher(*itr, normalizer, normalizedNameFieldsToFilter)) { // If not matched, this package match filter will be removed. // For example, if caller is trying to search with arch info only, values without arch will be removed from search. itr = filters.erase(itr); continue; } normalizedNameFieldsFound = true; } ++itr; } return normalizedNameFieldsFound; } } Interface::Interface(Utility::NormalizationVersion normVersion) : m_normalizer(normVersion) { } SQLite::Version Interface::GetVersion() const { return { 1, 2 }; } void Interface::CreateTables(SQLite::Connection& connection, CreateOptions options) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createtables_v1_2"); V1_1::Interface::CreateTables(connection, options); // While the name and publisher should be linked per-locale, we are not implementing that here. // This will mean that one can match cross locale name and publisher, but the chance that this // leads to a confusion between packages is very small. More likely would be intentional attempts // to confuse the correlation, which could be fairly easily carried out even with linked values. NormalizedPackageNameTable::Create(connection, GetOneToManyTableSchema()); NormalizedPackagePublisherTable::Create(connection, GetOneToManyTableSchema()); savepoint.Commit(); } SQLite::rowid_t Interface::AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addmanifest_v1_2"); SQLite::rowid_t manifestId = V1_1::Interface::AddManifest(connection, manifest, relativePath); // Add the new 1.2 data // These normalized strings are all stored with their cases folded so that they can be // looked up ordinally; enabling the index to provide efficient searches. NormalizedPackageNameTable::EnsureExistsAndInsert(connection, anon::GetNormalizedNames(m_normalizer, manifest), manifestId); NormalizedPackagePublisherTable::EnsureExistsAndInsert(connection, anon::GetNormalizedPublishers(m_normalizer, manifest), manifestId); savepoint.Commit(); return manifestId; } std::pair Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updatemanifest_v1_2"); auto [indexModified, manifestId] = V1_1::Interface::UpdateManifest(connection, manifest, relativePath); // Update new 1.2 tables as necessary indexModified = NormalizedPackageNameTable::UpdateIfNeededByManifestId(connection, anon::GetNormalizedNames(m_normalizer, manifest), manifestId) || indexModified; indexModified = NormalizedPackagePublisherTable::UpdateIfNeededByManifestId(connection, anon::GetNormalizedPublishers(m_normalizer, manifest), manifestId) || indexModified; savepoint.Commit(); return { indexModified, manifestId }; } void Interface::RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "RemoveManifestById_v1_2"); V1_1::Interface::RemoveManifestById(connection, manifestId); // Remove all of the new 1.2 data that is no longer referenced. NormalizedPackageNameTable::DeleteIfNotNeededByManifestId(connection, manifestId); NormalizedPackagePublisherTable::DeleteIfNotNeededByManifestId(connection, manifestId); savepoint.Commit(); } bool Interface::CheckConsistency(const SQLite::Connection& connection, bool log) const { bool result = V1_1::Interface::CheckConsistency(connection, log); // If the v1.1 index was consistent, or if full logging of inconsistency was requested, check the v1.2 data. if (result || log) { result = NormalizedPackageNameTable::CheckConsistency(connection, log) && result; } if (result || log) { result = NormalizedPackagePublisherTable::CheckConsistency(connection, log) && result; } return result; } std::vector Interface::GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const { switch (property) { // These values are not right, as they are normalized. But they are good enough for now and all we have. case PackageVersionMultiProperty::Name: return NormalizedPackageNameTable::GetValuesByManifestId(connection, primaryId); case PackageVersionMultiProperty::Publisher: return NormalizedPackagePublisherTable::GetValuesByManifestId(connection, primaryId); default: return V1_1::Interface::GetMultiPropertyByPrimaryId(connection, primaryId, property); } } Utility::NormalizedName Interface::NormalizeName(std::string_view name, std::string_view publisher) const { return m_normalizer.Normalize(name, publisher); } void Interface::DropTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "drop_tables_v1_2"); V1_1::Interface::DropTables(connection); NormalizedPackageNameTable::Drop(connection); NormalizedPackagePublisherTable::Drop(connection); savepoint.Commit(); } std::unique_ptr Interface::CreateSearchResultsTable(const SQLite::Connection& connection) const { return std::make_unique(connection); } ISQLiteIndex::SearchResult Interface::SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const { if (request.Purpose == SearchPurpose::CorrelationToInstalled) { // Correlate from available package to installed package // For available package to installed package mapping, only one try is needed. // For example, if ARP DisplayName contains arch, then the installed package's ARP DisplayName should also include arch. auto candidateInclusionsWithArch = request.Inclusions; if (anon::UpdatePackageMatchFilters(candidateInclusionsWithArch, m_normalizer, Utility::NormalizationField::Architecture)) { // If DisplayNames contain arch, only use Inclusions with arch for search request.Inclusions = candidateInclusionsWithArch; } else { // Otherwise, just update the Inclusions with normalization anon::UpdatePackageMatchFilters(request.Inclusions, m_normalizer); } return V1_1::Interface::SearchInternal(connection, request); } else if (request.Purpose == SearchPurpose::CorrelationToAvailable) { // For installed package to available package correlation, // try the search with NormalizedName with Arch first, if not found, try with all values. // This can be extended in the future for more granular search requests. std::vector candidateSearches; auto candidateSearchWithArch = request; if (anon::UpdatePackageMatchFilters(candidateSearchWithArch.Inclusions, m_normalizer, Utility::NormalizationField::Architecture)) { candidateSearches.emplace_back(std::move(candidateSearchWithArch)); } anon::UpdatePackageMatchFilters(request.Inclusions, m_normalizer); candidateSearches.emplace_back(request); SearchResult result; for (auto& candidateSearch : candidateSearches) { result = V1_1::Interface::SearchInternal(connection, candidateSearch); if (!result.Matches.empty()) { break; } } return result; } else { anon::UpdatePackageMatchFilters(request.Inclusions, m_normalizer); anon::UpdatePackageMatchFilters(request.Filters, m_normalizer); return V1_1::Interface::SearchInternal(connection, request); } } void Interface::PrepareForPackaging(SQLite::Connection& connection, bool vacuum) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareforpackaging_v1_2"); V1_1::Interface::PrepareForPackaging(connection, false); NormalizedPackageNameTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), true, true); NormalizedPackagePublisherTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), true, true); savepoint.Commit(); if (vacuum) { Vacuum(connection); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/NormalizedPackageNameTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToManyTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_2 { namespace details { using namespace std::string_view_literals; struct NormalizedPackageNameTableInfo { inline static constexpr std::string_view TableName() { return "norm_names"sv; } inline static constexpr std::string_view ValueName() { return "norm_name"sv; } }; } // The table for NormalizedPackageName. using NormalizedPackageNameTable = V1_0::OneToManyTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/NormalizedPackagePublisherTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToManyTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_2 { namespace details { using namespace std::string_view_literals; struct NormalizedPackagePublisherTableInfo { inline static constexpr std::string_view TableName() { return "norm_publishers"sv; } inline static constexpr std::string_view ValueName() { return "norm_publisher"sv; } }; } // The table for NormalizedPackagePublisher. using NormalizedPackagePublisherTable = V1_0::OneToManyTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/SearchResultsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_1/SearchResultsTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_2 { // Table for holding temporary search results. struct SearchResultsTable : public V1_1::SearchResultsTable { SearchResultsTable(const SQLite::Connection& connection) : V1_1::SearchResultsTable(connection) {} SearchResultsTable(const SearchResultsTable&) = delete; SearchResultsTable& operator=(const SearchResultsTable&) = delete; SearchResultsTable(SearchResultsTable&&) = default; SearchResultsTable& operator=(SearchResultsTable&&) = default; protected: std::vector BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const override; // Import all overrides of this function using V1_0::SearchResultsTable::BindStatementForMatchType; void BindStatementForMatchType(SQLite::Statement& statement, const PackageMatchFilter& filter, const std::vector& bindIndex) override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/SearchResultsTable_1_2.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchResultsTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_2/NormalizedPackageNameTable.h" #include "Microsoft/Schema/1_2/NormalizedPackagePublisherTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_2 { std::vector SearchResultsTable::BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const { switch (field) { case PackageMatchField::NormalizedNameAndPublisher: return V1_0::ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); default: return V1_1::SearchResultsTable::BuildSearchStatement(builder, field, manifestAlias, valueAlias, useLike); } } void SearchResultsTable::BindStatementForMatchType(SQLite::Statement& statement, const PackageMatchFilter& filter, const std::vector& bindIndex) { V1_0::SearchResultsTable::BindStatementForMatchType(statement, filter, bindIndex); if (filter.Field == PackageMatchField::NormalizedNameAndPublisher) { BindStatementForMatchType(statement, filter.Type, bindIndex[1], filter.Additional.value()); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/HashVirtualTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include using namespace std::string_view_literals; namespace AppInstaller::Repository::Microsoft::Schema::V1_3 { // A virtual table used to add a direct column onto the manifest table. struct HashVirtualTable { // The id type (which is actually the value for this virtual table) using id_t = SQLite::blob_t; // The name of the column. static constexpr std::string_view ValueName() { return "hash"sv; } // The value type of the column. static constexpr SQLite::Builder::Type SQLiteType() { return SQLite::Builder::Type::Blob; } }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_2/Interface.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_3 { // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public V1_2::Interface { Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; std::pair UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; protected: // Gets a property already knowing that the manifest id is valid. std::optional GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface_1_3.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_3/Interface.h" #include #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_3/HashVirtualTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V1_3 { Interface::Interface(Utility::NormalizationVersion normVersion) : V1_2::Interface(normVersion) { } SQLite::Version Interface::GetVersion() const { return { 1, 3 }; } void Interface::CreateTables(SQLite::Connection& connection, CreateOptions options) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createtables_v1_3"); V1_2::Interface::CreateTables(connection, options); V1_0::ManifestTable::AddColumn(connection, { HashVirtualTable::ValueName(), HashVirtualTable::SQLiteType() }); savepoint.Commit(); } SQLite::rowid_t Interface::AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addmanifest_v1_3"); SQLite::rowid_t manifestId = V1_2::Interface::AddManifest(connection, manifest, relativePath); // Set the hash value if provided if (!manifest.StreamSha256.empty()) { THROW_HR_IF(E_INVALIDARG, manifest.StreamSha256.size() != Utility::SHA256::HashBufferSizeInBytes); V1_0::ManifestTable::UpdateValueIdById(connection, manifestId, manifest.StreamSha256); } savepoint.Commit(); return manifestId; } std::pair Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updatemanifest_v1_3"); auto [indexModified, manifestId] = V1_2::Interface::UpdateManifest(connection, manifest, relativePath); // Set the hash value if provided if (!manifest.StreamSha256.empty()) { THROW_HR_IF(E_INVALIDARG, manifest.StreamSha256.size() != Utility::SHA256::HashBufferSizeInBytes); auto currentHash = std::get<0>(V1_0::ManifestTable::GetIdsById(connection, manifestId)); if (currentHash.size() != Utility::SHA256::HashBufferSizeInBytes || !std::equal(currentHash.begin(), currentHash.end(), manifest.StreamSha256.begin())) { V1_0::ManifestTable::UpdateValueIdById(connection, manifestId, manifest.StreamSha256); indexModified = true; } } savepoint.Commit(); return { indexModified, manifestId }; } std::optional Interface::GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const { switch (property) { case AppInstaller::Repository::PackageVersionProperty::ManifestSHA256Hash: { std::optional hash = V1_0::ManifestTable::GetIdById(connection, manifestId); return (!hash || hash->empty()) ? std::optional{} : Utility::SHA256::ConvertToString(hash.value()); } default: return V1_2::Interface::GetPropertyByManifestIdInternal(connection, manifestId, property); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DependenciesTable.h" #include #include "winget\DependenciesGraph.h" #include "Microsoft/Schema/1_0/OneToOneTable.h" #include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_0/VersionTable.h" #include "Microsoft/Schema/1_0/Interface.h" #include "Microsoft/Schema/1_0/ChannelTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_4 { using namespace AppInstaller; using namespace std::string_view_literals; using namespace SQLite::Builder; using namespace Schema::V1_0; using QCol = SQLite::Builder::QualifiedColumn; static constexpr std::string_view s_DependenciesTable_Table_Name = "dependencies"sv; static constexpr std::string_view s_DependenciesTable_Index_Name = "dependencies_pkindex"sv; static constexpr std::string_view s_DependenciesTable_Manifest_Column_Name = "manifest"sv; static constexpr std::string_view s_DependenciesTable_MinVersion_Column_Name = "min_version"sv; static constexpr std::string_view s_DependenciesTable_PackageId_Column_Name = "package_id"; namespace { struct DependencyTableRow { SQLite::rowid_t m_packageRowId; SQLite::rowid_t m_manifestRowId; // Ideally this should be version row id, the version string is more needed than row id, // this prevents converting back and forth between version row id and version string. std::optional m_version; bool operator <(const DependencyTableRow& rhs) const { auto lhsVersion = m_version.has_value() ? m_version.value() : ""; auto rhsVersion = rhs.m_version.has_value() ? rhs.m_version.value() : ""; return std::tie(m_packageRowId, m_manifestRowId, lhsVersion) < std::tie(rhs.m_packageRowId, rhs.m_manifestRowId, rhsVersion); } }; void ThrowOnMissingPackageNodes(std::vector& missingPackageNodes) { if (!missingPackageNodes.empty()) { std::string missingPackages{ missingPackageNodes.begin()->Id()}; std::for_each( missingPackageNodes.begin() + 1, missingPackageNodes.end(), [&](auto& dep) { missingPackages.append(", " + dep.Id()); }); THROW_HR_MSG(APPINSTALLER_CLI_ERROR_MISSING_PACKAGE, "Missing packages: %hs", missingPackages.c_str()); } } std::set GetAndLinkDependencies( SQLite::Connection& connection, const Manifest::Manifest& manifest, SQLite::rowid_t manifestRowId, Manifest::DependencyType dependencyType) { std::set dependencies; std::vector missingPackageNodes; for (const auto& installer : manifest.Installers) { installer.Dependencies.ApplyToType(dependencyType, [&](Manifest::Dependency dependency) { auto packageRowId = IdTable::SelectIdByValue(connection, dependency.Id(), true); std::optional version; if (!packageRowId.has_value()) { missingPackageNodes.emplace_back(dependency); return; } if (dependency.MinVersion.has_value()) { version = dependency.MinVersion.value().ToString(); } dependencies.emplace(DependencyTableRow{ packageRowId.value(), manifestRowId, version }); }); } ThrowOnMissingPackageNodes(missingPackageNodes); return dependencies; } bool RemoveDependenciesByRowIds(SQLite::Connection& connection, std::vector dependencyTableRows) { using namespace SQLite::Builder; bool tableUpdated = false; if (dependencyTableRows.empty()) { return tableUpdated; } SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ s_DependenciesTable_Table_Name } + "remove_dependencies_by_rowid"); SQLite::Builder::StatementBuilder builder; builder .DeleteFrom(s_DependenciesTable_Table_Name) .Where(s_DependenciesTable_PackageId_Column_Name).Equals(Unbound) .And(s_DependenciesTable_Manifest_Column_Name).Equals(Unbound); SQLite::Statement deleteStmt = builder.Prepare(connection); for (auto row : dependencyTableRows) { deleteStmt.Reset(); deleteStmt.Bind(1, row.m_packageRowId); deleteStmt.Bind(2, row.m_manifestRowId); deleteStmt.Execute(); tableUpdated = true; } savepoint.Commit(); return tableUpdated; } bool InsertManifestDependencies( SQLite::Connection& connection, std::set& dependenciesTableRows) { using namespace SQLite::Builder; using namespace Schema::V1_0; bool tableUpdated = false; StatementBuilder insertBuilder; insertBuilder.InsertInto(s_DependenciesTable_Table_Name) .Columns({ s_DependenciesTable_Manifest_Column_Name, s_DependenciesTable_MinVersion_Column_Name, s_DependenciesTable_PackageId_Column_Name }) .Values(Unbound, Unbound, Unbound); SQLite::Statement insert = insertBuilder.Prepare(connection); for (const auto& dep : dependenciesTableRows) { insert.Reset(); insert.Bind(1, dep.m_manifestRowId); if (dep.m_version.has_value()) { insert.Bind(2, VersionTable::EnsureExists(connection, dep.m_version.value())); } else { insert.Bind(2, nullptr); } insert.Bind(3, dep.m_packageRowId); insert.Execute(); tableUpdated = true; } return tableUpdated; } } bool DependenciesTable::Exists(const SQLite::Connection& connection) { using namespace SQLite; Builder::StatementBuilder builder; builder.Select(Builder::RowCount).From(Builder::Schema::MainTable). Where(Builder::Schema::TypeColumn).Equals(Builder::Schema::Type_Table).And(Builder::Schema::NameColumn).Equals(s_DependenciesTable_Table_Name); Statement statement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !statement.Step()); return statement.GetColumn(0) != 0; } std::string_view DependenciesTable::TableName() { return s_DependenciesTable_Table_Name; } void DependenciesTable::Create(SQLite::Connection& connection) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createDependencyTable_v1_4"); constexpr std::string_view dependencyIndexByVersionId = "dependencies_version_id_index"; constexpr std::string_view dependencyIndexByPackageId = "dependencies_package_id_index"; StatementBuilder createTableBuilder; createTableBuilder.CreateTable(TableName()).BeginColumns(); createTableBuilder.Column(IntegerPrimaryKey()); std::array notNullableDependenciesColumns { DependenciesTableColumnInfo{ s_DependenciesTable_Manifest_Column_Name }, DependenciesTableColumnInfo{ s_DependenciesTable_PackageId_Column_Name } }; std::array nullableDependenciesColumns { DependenciesTableColumnInfo{ s_DependenciesTable_MinVersion_Column_Name } }; // Add dependencies column tables not null columns. for (const DependenciesTableColumnInfo& value : notNullableDependenciesColumns) { createTableBuilder.Column(ColumnBuilder(value.Name, Type::RowId).NotNull()); } // Add dependencies column tables null columns. for (const DependenciesTableColumnInfo& value : nullableDependenciesColumns) { createTableBuilder.Column(ColumnBuilder(value.Name, Type::RowId)); } createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); // Primary key index by package rowid and manifest rowid. StatementBuilder createPKIndexBuilder; createPKIndexBuilder.CreateUniqueIndex(s_DependenciesTable_Index_Name).On(s_DependenciesTable_Table_Name).Columns({ s_DependenciesTable_Manifest_Column_Name, s_DependenciesTable_PackageId_Column_Name }); createPKIndexBuilder.Execute(connection); // Index of dependency by Manifest id. StatementBuilder createIndexByManifestIdBuilder; createIndexByManifestIdBuilder.CreateIndex(dependencyIndexByVersionId).On(s_DependenciesTable_Table_Name).Columns({ s_DependenciesTable_MinVersion_Column_Name }); createIndexByManifestIdBuilder.Execute(connection); // Index of dependency by package id. StatementBuilder createIndexByPackageIdBuilder; createIndexByPackageIdBuilder.CreateIndex(dependencyIndexByPackageId).On(s_DependenciesTable_Table_Name).Columns({ s_DependenciesTable_PackageId_Column_Name }); createIndexByPackageIdBuilder.Execute(connection); savepoint.Commit(); } void DependenciesTable::Drop(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTableIfExists(s_DependenciesTable_Table_Name); dropTableBuilder.Execute(connection); } void DependenciesTable::AddDependencies(SQLite::Connection& connection, const Manifest::Manifest& manifest, SQLite::rowid_t manifestRowId) { if (!Exists(connection)) { return; } SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ s_DependenciesTable_Table_Name } + "add_dependencies_v1_4"); auto dependencies = GetAndLinkDependencies(connection, manifest, manifestRowId, Manifest::DependencyType::Package); if (!dependencies.size()) { return; } InsertManifestDependencies(connection, dependencies); savepoint.Commit(); } bool DependenciesTable::UpdateDependencies(SQLite::Connection& connection, const Manifest::Manifest& manifest, SQLite::rowid_t manifestRowId) { if (!Exists(connection)) { return false; } SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ s_DependenciesTable_Table_Name } + "update_dependencies_v1_4"); const auto dependencies = GetAndLinkDependencies(connection, manifest, manifestRowId, Manifest::DependencyType::Package); auto existingDependencies = GetDependenciesByManifestRowId(connection, manifestRowId); // Get dependencies to add. std::set toAddDependencies; std::copy_if( dependencies.begin(), dependencies.end(), std::inserter(toAddDependencies, toAddDependencies.begin()), [&](DependencyTableRow dep) { Utility::NormalizedString version = dep.m_version.has_value() ? dep.m_version.value() : ""; return existingDependencies.find(std::make_pair(dep.m_packageRowId, version)) == existingDependencies.end(); } ); // Get dependencies to remove. std::vector toRemoveDependencies; std::for_each( existingDependencies.begin(), existingDependencies.end(), [&](std::pair row) { if (dependencies.find(DependencyTableRow{row.first, manifestRowId, row.second}) == dependencies.end()) { toRemoveDependencies.emplace_back(DependencyTableRow{ row.first, manifestRowId }); } } ); bool tableUpdated = RemoveDependenciesByRowIds(connection, toRemoveDependencies); tableUpdated = InsertManifestDependencies(connection, toAddDependencies) || tableUpdated; savepoint.Commit(); return tableUpdated; } void DependenciesTable::RemoveDependencies(SQLite::Connection& connection, SQLite::rowid_t manifestRowId) { if (!Exists(connection)) { return; } SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ s_DependenciesTable_Table_Name } + "remove_dependencies_by_manifest_v1_4"); SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_DependenciesTable_Table_Name).Where(s_DependenciesTable_Manifest_Column_Name).Equals(manifestRowId); builder.Execute(connection); savepoint.Commit(); } std::vector> DependenciesTable::GetDependentsById(const SQLite::Connection& connection, Manifest::string_t packageId) { constexpr std::string_view depTableAlias = "dep"; constexpr std::string_view minVersionAlias = "minV"; constexpr std::string_view packageIdAlias = "pId"; StatementBuilder builder; // Find all manifest that depend on this package. Use outer join for joining version table as min_version may be NULL. // SELECT [dep].[manifest], [dep].[min_version], [pId].[id], [minV].[version] FROM [dependencies] AS [dep] // LEFT OUTER JOIN [versions] AS [minV] ON [dep].[min_version] = [minV].[rowid] // JOIN [ids] AS [pId] ON [pId].[rowid] = [dep].[package_id] // WHERE [pId].[id] = ? builder.Select() .Column(QCol(depTableAlias, s_DependenciesTable_Manifest_Column_Name)) .Column(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name)) .Column(QCol(packageIdAlias, IdTable::ValueName())) .Column(QCol(minVersionAlias, VersionTable::ValueName())) .From({ s_DependenciesTable_Table_Name }).As(depTableAlias) .LeftOuterJoin({ VersionTable::TableName() }).As(minVersionAlias) .On(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name), QCol(minVersionAlias, SQLite::RowIDName)) .Join({ IdTable::TableName() }).As(packageIdAlias) .On(QCol(packageIdAlias, SQLite::RowIDName), QCol(depTableAlias, s_DependenciesTable_PackageId_Column_Name)) .Where(QCol(packageIdAlias, IdTable::ValueName())).Equals(Unbound); SQLite::Statement stmt = builder.Prepare(connection); stmt.Bind(1, std::string{ packageId }); std::vector> resultSet; while (stmt.Step()) { Utility::NormalizedString version = ""; if (!stmt.GetColumnIsNull(1)) { // If min_version is not NULL, use the corresponding value from Version table. version = stmt.GetColumn(3); } resultSet.emplace_back( std::make_pair(stmt.GetColumn(0), std::move(version))); } return resultSet; } std::set> DependenciesTable::GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId) { SQLite::Builder::StatementBuilder builder; constexpr std::string_view depTableAlias = "dep"; constexpr std::string_view minVersionAlias = "minV"; std::set> resultSet; // Use Outer join since min_version could have NULL value. // SELECT [dep].[package_id], [dep].[min_version], [minV].[version] FROM [dependencies] AS [dep] // LEFT OUTER JOIN [versions] AS [minV] ON [minV].[rowid] = [dep].[min_version] // WHERE [dep].[manifest] = ? builder.Select() .Column(QCol(depTableAlias, s_DependenciesTable_PackageId_Column_Name)) .Column(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name)) .Column(QCol(minVersionAlias, VersionTable::ValueName())) .From({ s_DependenciesTable_Table_Name }).As(depTableAlias) .LeftOuterJoin({ VersionTable::TableName() }).As(minVersionAlias) .On(QCol(minVersionAlias, SQLite::RowIDName), QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name)) .Where(QCol(depTableAlias, s_DependenciesTable_Manifest_Column_Name)).Equals(Unbound); SQLite::Statement select = builder.Prepare(connection); select.Bind(1, manifestRowId); while (select.Step()) { Utility::NormalizedString version = ""; if (!select.GetColumnIsNull(1)) { // If min_version is not NULL, use the corresponding value from Version table. version = select.GetColumn(2); } resultSet.emplace(std::make_pair(select.GetColumn(0), std::move(version))); } return resultSet; } void DependenciesTable::PrepareForPackaging(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareForPacking_V1_4"); StatementBuilder dropIndexBuilder; dropIndexBuilder.DropIndex({ s_DependenciesTable_Index_Name }); dropIndexBuilder.Execute(connection); StatementBuilder dropTableBuilder; dropTableBuilder.DropTable({ s_DependenciesTable_Table_Name }); dropTableBuilder.Execute(connection); savepoint.Commit(); } bool DependenciesTable::CheckConsistency(const SQLite::Connection& connection, bool log) { StatementBuilder builder; if (!Exists(connection)) { return true; } builder.Select(QCol(s_DependenciesTable_Table_Name, SQLite::RowIDName)) .From(s_DependenciesTable_Table_Name) .LeftOuterJoin(IdTable::TableName()) .On(QCol(s_DependenciesTable_Table_Name, s_DependenciesTable_PackageId_Column_Name), QCol(IdTable::TableName(), SQLite::RowIDName)) .LeftOuterJoin(ManifestTable::TableName()) .On(QCol(s_DependenciesTable_Table_Name, s_DependenciesTable_Manifest_Column_Name), QCol(ManifestTable::TableName(), SQLite::RowIDName)) .LeftOuterJoin(VersionTable::TableName()) .On(QCol(s_DependenciesTable_Table_Name, s_DependenciesTable_MinVersion_Column_Name), QCol(VersionTable::TableName(), SQLite::RowIDName)) .Where(QCol(ManifestTable::TableName(), SQLite::RowIDName)).IsNull() .Or(QCol(VersionTable::TableName(), SQLite::RowIDName)).IsNull().And(QCol(s_DependenciesTable_Table_Name, s_DependenciesTable_MinVersion_Column_Name)).IsNotNull() .Or(QCol(IdTable::TableName(), SQLite::RowIDName)).IsNull(); SQLite::Statement select = builder.Prepare(connection); bool result = true; while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] row in [" << s_DependenciesTable_Table_Name << "] rowid [" << select.GetColumn(0) << "]"); } return result; } bool DependenciesTable::IsValueReferenced(const SQLite::Connection& connection, std::string_view tableName, SQLite::rowid_t valueRowId) { if (!Exists(connection)) { return false; } StatementBuilder builder; if (tableName != V1_0::VersionTable::TableName()) { return false; } std::array columns = { s_DependenciesTable_MinVersion_Column_Name }; bool referenced = false; for(auto column: columns) { builder.Select(SQLite::RowIDName).From(s_DependenciesTable_Table_Name).Where(column).Equals(Unbound).Limit(1); SQLite::Statement select = builder.Prepare(connection); select.Bind(1, valueRowId); if (select.Step()) { referenced = true; break; } } return referenced; } std::vector DependenciesTable::GetDependenciesMinVersionsRowIdByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId) { if (!Exists(connection)) { return {}; } StatementBuilder builder; std::vector result; // Find all versions for manifest row. // SELECT [min_version] FROM [dependencies] // WHERE [manifest] = ? builder.Select() .Column(s_DependenciesTable_MinVersion_Column_Name) .From({ s_DependenciesTable_Table_Name }) .Where(s_DependenciesTable_Manifest_Column_Name).Equals(Unbound); auto select = builder.Prepare(connection); select.Bind(1, manifestRowId); while (select.Step()) { if (!select.GetColumnIsNull(0)) { result.emplace_back(select.GetColumn(0)); } } return result; } std::vector> DependenciesTable::GetAllDependenciesWithMinVersions(const SQLite::Connection& connection) { if (!Exists(connection)) { return {}; } std::vector> result; constexpr std::string_view depTableAlias = "dep"; constexpr std::string_view minVersionAlias = "minV"; StatementBuilder builder; // SELECT [dep].[package_id], [minV].[version] FROM [dependencies] AS [dep] // JOIN [versions] AS [minV] ON [minV].[rowid] = [dep].[min_version] builder.Select() .Column(QCol(depTableAlias, s_DependenciesTable_PackageId_Column_Name)) .Column(QCol(minVersionAlias, VersionTable::ValueName())) .From({ s_DependenciesTable_Table_Name }).As(depTableAlias) .Join({ VersionTable::TableName() }).As(minVersionAlias) .On(QCol(minVersionAlias, SQLite::RowIDName), QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name)); SQLite::Statement select = builder.Prepare(connection); while (select.Step()) { Utility::NormalizedString version = ""; if (!select.GetColumnIsNull(1)) { version = select.GetColumn(1); } if (!version.empty()) { result.emplace_back(std::make_pair(select.GetColumn(0), version)); } } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "pch.h" #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V1_4 { using namespace AppInstaller; struct DependenciesTableColumnInfo { std::string_view Name; }; struct DependenciesTable { // Get the table name. static std::string_view TableName(); // Creates the table with named indices. static void Create(SQLite::Connection& connection); // Drops the table. static void Drop(SQLite::Connection& connection); static bool Exists(const SQLite::Connection& connection); // Add the dependencies for the specific manifest. static void AddDependencies(SQLite::Connection& connection, const Manifest::Manifest& manifest, SQLite::rowid_t manifestRowId); // update the dependencies for the specific manifest. static bool UpdateDependencies(SQLite::Connection& connection, const Manifest::Manifest& manifest, SQLite::rowid_t manifestRowId); // Remove the dependencies by manifest id static void RemoveDependencies(SQLite::Connection& connection, SQLite::rowid_t manifestRowId); // Get dependencies the dependencies static std::set> GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId); // Get dependencies by package id. static std::vector> GetDependentsById(const SQLite::Connection& connection, AppInstaller::Manifest::string_t packageId); // Check dependencies table consistency. static bool CheckConsistency(const SQLite::Connection& connection, bool log); // Checks if the row id is present in the column denoted by the value supplied. static bool IsValueReferenced(const SQLite::Connection& connection, std::string_view valueName, SQLite::rowid_t valueRowId); // The dependencies table and corresponding index are dropped. static void PrepareForPackaging(SQLite::Connection& connection); // Get all min version values of the given manifest's dependencies, used for VersionTable cleanup when updating or removing a manifest. static std::vector GetDependenciesMinVersionsRowIdByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId); // Get all dependencies with min versions in the dependencies table, used during consistency check. Returning a list of pair. static std::vector> GetAllDependenciesWithMinVersions(const SQLite::Connection& connection); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_3/Interface.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_4 { // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public V1_3::Interface { Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; std::pair UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; void RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) override; bool CheckConsistency(const SQLite::Connection& connection, bool log) const override; void PrepareForPackaging(SQLite::Connection& connection, bool vacuum) override; std::set> GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId) const override; std::vector> GetDependentsById(const SQLite::Connection& connection, AppInstaller::Manifest::string_t packageId) const override; // Version 1.7 void DropTables(SQLite::Connection& connection) override; protected: bool NotNeeded(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id) const override; private: // Semantic check to validate dependencies with min versions are satisfied. bool ValidateDependenciesWithMinVersions(const SQLite::Connection& connection, bool log) const; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/Interface_1_4.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_4/Interface.h" #include "Microsoft/Schema/1_0/VersionTable.h" #include "Microsoft/Schema/1_4/DependenciesTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_4 { Interface::Interface(Utility::NormalizationVersion normVersion) : V1_3::Interface(normVersion) { } SQLite::Version Interface::GetVersion() const { return { 1, 4 }; } void Interface::CreateTables(SQLite::Connection& connection, CreateOptions options) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createtables_v1_4"); V1_3::Interface::CreateTables(connection, options); if (WI_IsFlagClear(options, CreateOptions::DisableDependenciesSupport)) { DependenciesTable::Create(connection); } savepoint.Commit(); } SQLite::rowid_t Interface::AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addmanifest_v1_4"); SQLite::rowid_t manifestId = V1_3::Interface::AddManifest(connection, manifest, relativePath); DependenciesTable::AddDependencies(connection, manifest, manifestId); savepoint.Commit(); return manifestId; } std::pair Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updatemanifest_v1_4"); auto [indexModified, manifestId] = V1_3::Interface::UpdateManifest(connection, manifest, relativePath); bool dependenciesModified = DependenciesTable::UpdateDependencies(connection, manifest, manifestId); indexModified = indexModified || dependenciesModified; savepoint.Commit(); return { indexModified, manifestId }; } void Interface::RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) { // Get all versions that need cleaning from the version table. auto minVersions = DependenciesTable::GetDependenciesMinVersionsRowIdByManifestId(connection, manifestId); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removemanifest_v1_4"); // Removes dependences for the manifest id. DependenciesTable::RemoveDependencies(connection, manifestId); // Removes the manifest. V1_3::Interface::RemoveManifestById(connection, manifestId); // Remove the versions that are not needed. for (auto minVersion : minVersions) { if (NotNeeded(connection, Schema::V1_0::VersionTable::TableName(), Schema::V1_0::VersionTable::ValueName(), minVersion)) { Schema::V1_0::VersionTable::DeleteById(connection, minVersion); } } savepoint.Commit(); } bool Interface::NotNeeded(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id) const { bool result = V1_0::Interface::NotNeeded(connection, tableName, valueName, id); return !DependenciesTable::IsValueReferenced(connection, tableName, id) && result; } void Interface::PrepareForPackaging(SQLite::Connection& connection, bool vacuum) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareforpackaging_v1_4"); V1_3::Interface::PrepareForPackaging(connection, false); DependenciesTable::PrepareForPackaging(connection); savepoint.Commit(); if (vacuum) { Vacuum(connection); } } bool Interface::CheckConsistency(const SQLite::Connection& connection, bool log) const { bool result = V1_3::Interface::CheckConsistency(connection, log); // If the v1.3 index was consistent, or if full logging of inconsistency was requested, check the v1.4 data. if (result || log) { result = DependenciesTable::CheckConsistency(connection, log) && result; } if (result || log) { result = ValidateDependenciesWithMinVersions(connection, log) && result; } return result; } std::set> Interface::GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId) const { return DependenciesTable::GetDependenciesByManifestRowId(connection, manifestRowId); } std::vector> Interface::GetDependentsById(const SQLite::Connection& connection, AppInstaller::Manifest::string_t packageId) const { return DependenciesTable::GetDependentsById(connection, packageId); } void Interface::DropTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "drop_tables_v1_4"); V1_2::Interface::DropTables(connection); DependenciesTable::Drop(connection); savepoint.Commit(); } bool Interface::ValidateDependenciesWithMinVersions(const SQLite::Connection& connection, bool log) const { try { bool result = true; // A map to store already checked dependency package latest versions. std::map checkedVersions; auto dependencies = DependenciesTable::GetAllDependenciesWithMinVersions(connection); for (auto const& dependency : dependencies) { // If the dependency package has not been checked yet, add to the map. if (checkedVersions.find(dependency.first) == checkedVersions.end()) { auto versionKeys = GetVersionKeysById(connection, dependency.first); THROW_HR_IF(E_UNEXPECTED, versionKeys.empty()); checkedVersions.emplace(dependency.first, versionKeys[0].VersionAndChannel.GetVersion()); } // If the latest version is less than min version required, fail the validation. if (checkedVersions[dependency.first] < Utility::Version{ dependency.second }) { AICLI_LOG(Repo, Error, << "Dependency with min version not satisfied. Dependency package row id: " << dependency.first << " min version: " << dependency.second); result = false; if (!log) { break; } } } return result; } catch (...) { AICLI_LOG(Repo, Error, << "ValidateDependenciesWithMinVersions() encountered internal error. Returning false."); return false; } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/ArpVersionVirtualTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Microsoft/Schema/1_0/VersionTable.h" #include "Microsoft/Schema/1_0/VirtualTableBase.h" #include using namespace std::string_view_literals; namespace AppInstaller::Repository::Microsoft::Schema::V1_5 { // A virtual table used to add Arp min version to ManifestTable, the values are stored in VersionTable. struct ArpMinVersionVirtualTable : public V1_0::VirtualTableBase { // The id type using id_t = V1_0::VersionTable::id_t; // The value type using value_t = V1_0::VersionTable::value_t; // The name of the table. static constexpr std::string_view TableName() { return V1_0::VersionTable::TableName(); } // The value name of the column. static constexpr std::string_view ValueName() { return V1_0::VersionTable::ValueName(); } // The value name of the manifest table column. static constexpr std::string_view ManifestColumnName() { return "arp_min_version"sv; } }; // A virtual table used to add Arp max version to ManifestTable, the values are stored in VersionTable. struct ArpMaxVersionVirtualTable : public V1_0::VirtualTableBase { // The id type using id_t = V1_0::VersionTable::id_t; // The value type using value_t = V1_0::VersionTable::value_t; // The name of the table. static constexpr std::string_view TableName() { return V1_0::VersionTable::TableName(); } // The value name of the column. static constexpr std::string_view ValueName() { return V1_0::VersionTable::ValueName(); } // The value name of the manifest table column. static constexpr std::string_view ManifestColumnName() { return "arp_max_version"sv; } }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_4/Interface.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_5 { // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public V1_4::Interface { Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; std::pair UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; void RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) override; bool CheckConsistency(const SQLite::Connection& connection, bool log) const override; protected: bool NotNeeded(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id) const override; // Gets a property already knowing that the manifest id is valid. std::optional GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const override; private: // Gets the ARP version ranges for the given package identifier. std::vector GetArpVersionRanges(const SQLite::Connection& connection, SQLite::rowid_t packageIdentifier) const; // Semantic check to validate all arp version ranges within the index bool ValidateArpVersionConsistency(const SQLite::Connection& connection, bool log) const; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface_1_5.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_5/Interface.h" #include "Microsoft/Schema/1_5/ArpVersionVirtualTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/VersionTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_5 { Interface::Interface(Utility::NormalizationVersion normVersion) : V1_4::Interface(normVersion) { } SQLite::Version Interface::GetVersion() const { return { 1, 5 }; } void Interface::CreateTables(SQLite::Connection& connection, CreateOptions options) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createtables_v1_5"); V1_4::Interface::CreateTables(connection, options); V1_0::ManifestTable::AddColumn(connection, { ArpMinVersionVirtualTable::ManifestColumnName(), SQLite::Builder::Type::RowId }); V1_0::ManifestTable::AddColumn(connection, { ArpMaxVersionVirtualTable::ManifestColumnName(), SQLite::Builder::Type::RowId }); savepoint.Commit(); } SQLite::rowid_t Interface::AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addmanifest_v1_5"); SQLite::rowid_t manifestId = V1_4::Interface::AddManifest(connection, manifest, relativePath); auto arpVersionRange = manifest.GetArpVersionRange(); Manifest::string_t arpMinVersion, arpMaxVersion; if (!arpVersionRange.IsEmpty()) { // Check to see if adding this version range will create a conflict SQLite::rowid_t packageIdentifier = V1_0::ManifestTable::GetIdById(connection, manifestId).value(); std::vector ranges = GetArpVersionRanges(connection, packageIdentifier); ranges.push_back(arpVersionRange); if (Utility::HasOverlapInVersionRanges(ranges)) { AICLI_LOG(Repo, Error, << "Overlapped Arp version ranges found for package. All ranges currently in index followed by new range:\n" << [&]() { std::stringstream stream; for (const auto& range : ranges) { stream << '[' << range.GetMinVersion().ToString() << "] - [" << range.GetMaxVersion().ToString() << "]\n"; } return std::move(stream).str(); }()); THROW_HR(APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); } arpMinVersion = arpVersionRange.GetMinVersion().ToString(); arpMaxVersion = arpVersionRange.GetMaxVersion().ToString(); } SQLite::rowid_t arpMinVersionId = V1_0::VersionTable::EnsureExists(connection, arpMinVersion); SQLite::rowid_t arpMaxVersionId = V1_0::VersionTable::EnsureExists(connection, arpMaxVersion); V1_0::ManifestTable::UpdateValueIdById(connection, manifestId, arpMinVersionId); V1_0::ManifestTable::UpdateValueIdById(connection, manifestId, arpMaxVersionId); savepoint.Commit(); return manifestId; } std::pair Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updatemanifest_v1_5"); auto [indexModified, manifestId] = V1_4::Interface::UpdateManifest(connection, manifest, relativePath); auto [oldMinVersionId, oldMaxVersionId] = V1_0::ManifestTable::GetIdsById(connection, manifestId); auto arpVersionRange = manifest.GetArpVersionRange(); Manifest::string_t arpMinVersion = arpVersionRange.IsEmpty() ? "" : arpVersionRange.GetMinVersion().ToString(); Manifest::string_t arpMaxVersion = arpVersionRange.IsEmpty() ? "" : arpVersionRange.GetMaxVersion().ToString(); SQLite::rowid_t arpMinVersionId = V1_0::VersionTable::EnsureExists(connection, arpMinVersion); SQLite::rowid_t arpMaxVersionId = V1_0::VersionTable::EnsureExists(connection, arpMaxVersion); // For cleaning up the old entries after update if applicable bool cleanOldMinVersionId = false; bool cleanOldMaxVersionId = false; if (arpMinVersionId != oldMinVersionId) { V1_0::ManifestTable::UpdateValueIdById(connection, manifestId, arpMinVersionId); cleanOldMinVersionId = true; indexModified = true; } if (arpMaxVersionId != oldMaxVersionId) { V1_0::ManifestTable::UpdateValueIdById(connection, manifestId, arpMaxVersionId); cleanOldMaxVersionId = true; indexModified = true; } if (!arpVersionRange.IsEmpty()) { // Check to see if the new set of version ranges created a conflict. // We could have done this before attempting the update but it would be more complex and SQLite gives us easy rollback. SQLite::rowid_t packageIdentifier = V1_0::ManifestTable::GetIdById(connection, manifestId).value(); std::vector ranges = GetArpVersionRanges(connection, packageIdentifier); if (Utility::HasOverlapInVersionRanges(ranges)) { AICLI_LOG(Repo, Error, << "Overlapped Arp version ranges found for package. Ranges that would be present with attempted upgrade:\n" << [&]() { std::stringstream stream; for (const auto& range : ranges) { stream << '[' << range.GetMinVersion().ToString() << "] - [" << range.GetMaxVersion().ToString() << "]\n"; } return std::move(stream).str(); }()); THROW_HR(APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); } } if (cleanOldMinVersionId && NotNeeded(connection, V1_0::VersionTable::TableName(), V1_0::VersionTable::ValueName(), oldMinVersionId)) { V1_0::VersionTable::DeleteById(connection, oldMinVersionId); } if (cleanOldMaxVersionId && oldMaxVersionId != oldMinVersionId && NotNeeded(connection, V1_0::VersionTable::TableName(), V1_0::VersionTable::ValueName(), oldMaxVersionId)) { V1_0::VersionTable::DeleteById(connection, oldMaxVersionId); } savepoint.Commit(); return { indexModified, manifestId }; } void Interface::RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) { // Get the old arp version ids of the values from the manifest table auto [arpMinVersionId, arpMaxVersionId] = V1_0::ManifestTable::GetIdsById(connection, manifestId); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "RemoveManifestById_v1_5"); // Removes the manifest. V1_4::Interface::RemoveManifestById(connection, manifestId); // Remove the versions that are not needed. if (NotNeeded(connection, V1_0::VersionTable::TableName(), V1_0::VersionTable::ValueName(), arpMinVersionId)) { V1_0::VersionTable::DeleteById(connection, arpMinVersionId); } if (arpMaxVersionId != arpMinVersionId && NotNeeded(connection, V1_0::VersionTable::TableName(), V1_0::VersionTable::ValueName(), arpMaxVersionId)) { V1_0::VersionTable::DeleteById(connection, arpMaxVersionId); } savepoint.Commit(); } bool Interface::NotNeeded(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id) const { bool result = V1_4::Interface::NotNeeded(connection, tableName, valueName, id); if (result && tableName == V1_0::VersionTable::TableName()) { if (valueName != V1_0::VersionTable::ValueName()) { result = !V1_0::ManifestTable::IsValueReferenced(connection, V1_0::VersionTable::ValueName(), id) && result; } if (valueName != ArpMinVersionVirtualTable::ManifestColumnName()) { result = !V1_0::ManifestTable::IsValueReferenced(connection, ArpMinVersionVirtualTable::ManifestColumnName(), id) && result; } if (valueName != ArpMaxVersionVirtualTable::ManifestColumnName()) { result = !V1_0::ManifestTable::IsValueReferenced(connection, ArpMaxVersionVirtualTable::ManifestColumnName(), id) && result; } } return result; } bool Interface::CheckConsistency(const SQLite::Connection& connection, bool log) const { bool result = V1_4::Interface::CheckConsistency(connection, log); // If the v1.4 index was consistent, or if full logging of inconsistency was requested, check the v1.5 data. if (result || log) { result = V1_0::ManifestTable::CheckConsistency(connection, log) && result; } if (result || log) { result = V1_0::ManifestTable::CheckConsistency(connection, log) && result; } if (result || log) { result = ValidateArpVersionConsistency(connection, log) && result; } return result; } std::optional Interface::GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const { switch (property) { case AppInstaller::Repository::PackageVersionProperty::ArpMinVersion: return V1_0::ManifestTable::GetValueById(connection, manifestId); case AppInstaller::Repository::PackageVersionProperty::ArpMaxVersion: return V1_0::ManifestTable::GetValueById(connection, manifestId); default: return V1_4::Interface::GetPropertyByManifestIdInternal(connection, manifestId, property); } } std::vector Interface::GetArpVersionRanges(const SQLite::Connection& connection, SQLite::rowid_t packageIdentifier) const { std::vector ranges; auto versionKeys = GetVersionKeysById(connection, packageIdentifier); for (auto const& versionKey : versionKeys) { auto arpMinVersion = GetPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionProperty::ArpMinVersion).value_or(""); auto arpMaxVersion = GetPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionProperty::ArpMaxVersion).value_or(""); // Either both empty or both not empty THROW_HR_IF(E_UNEXPECTED, arpMinVersion.empty() != arpMaxVersion.empty()); if (!arpMinVersion.empty() && !arpMaxVersion.empty()) { ranges.emplace_back(Utility::VersionRange{ Utility::Version{ std::move(arpMinVersion) }, Utility::Version{ std::move(arpMaxVersion) } }); } } return ranges; } bool Interface::ValidateArpVersionConsistency(const SQLite::Connection& connection, bool log) const { try { bool result = true; // Search everything SearchRequest request; auto searchResult = Search(connection, request); for (auto const& match : searchResult.Matches) { // Get arp version ranges for each package to check std::vector ranges = GetArpVersionRanges(connection, match.first); // Check overlap if (Utility::HasOverlapInVersionRanges(ranges)) { AICLI_LOG(Repo, Error, << "Overlapped Arp version ranges found for package. PackageRowId: " << match.first); result = false; if (!log) { break; } } } return result; } catch (...) { AICLI_LOG(Repo, Error, << "ValidateArpVersionConsistency() encountered internal error. Returning false."); return false; } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_5/Interface.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_6 { // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public V1_5::Interface { Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; std::pair UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; void RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) override; bool CheckConsistency(const SQLite::Connection& connection, bool log) const override; std::vector GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const override; // Version 1.7 void DropTables(SQLite::Connection& connection) override; protected: std::unique_ptr CreateSearchResultsTable(const SQLite::Connection& connection) const override; void PerformQuerySearch(V1_0::SearchResultsTable& resultsTable, const RequestMatch& query) const override; ISQLiteIndex::SearchResult SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const; void PrepareForPackaging(SQLite::Connection& connection, bool vacuum) override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/Interface_1_6.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_6/Interface.h" #include "Microsoft/Schema/1_6/UpgradeCodeTable.h" #include "Microsoft/Schema/1_6/SearchResultsTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_0/VersionTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_6 { Interface::Interface(Utility::NormalizationVersion normVersion) : V1_5::Interface(normVersion) { } SQLite::Version Interface::GetVersion() const { return { 1, 6 }; } void Interface::CreateTables(SQLite::Connection& connection, CreateOptions options) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createtables_v1_6"); V1_5::Interface::CreateTables(connection, options); UpgradeCodeTable::Create(connection, GetOneToManyTableSchema()); savepoint.Commit(); } SQLite::rowid_t Interface::AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addmanifest_v1_6"); SQLite::rowid_t manifestId = V1_5::Interface::AddManifest(connection, manifest, relativePath); // Add the new 1.6 data // These system reference strings are all stored with their cases folded so that they can be // looked up ordinally; enabling the index to provide efficient searches. UpgradeCodeTable::EnsureExistsAndInsert(connection, manifest.GetUpgradeCodes(), manifestId); savepoint.Commit(); return manifestId; } std::pair Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updatemanifest_v1_6"); auto [indexModified, manifestId] = V1_5::Interface::UpdateManifest(connection, manifest, relativePath); // Update new 1:N tables as necessary indexModified = UpgradeCodeTable::UpdateIfNeededByManifestId(connection, manifest.GetUpgradeCodes(), manifestId) || indexModified; savepoint.Commit(); return { indexModified, manifestId }; } void Interface::RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "RemoveManifestById_v1_6"); // Removes the manifest. V1_5::Interface::RemoveManifestById(connection, manifestId); // Remove all of the new 1:N data that is no longer referenced. UpgradeCodeTable::DeleteIfNotNeededByManifestId(connection, manifestId); savepoint.Commit(); } bool Interface::CheckConsistency(const SQLite::Connection& connection, bool log) const { bool result = V1_5::Interface::CheckConsistency(connection, log); // If the v1.5 index was consistent, or if full logging of inconsistency was requested, check the v1.6 data. if (result || log) { result = UpgradeCodeTable::CheckConsistency(connection, log) && result; } return result; } std::vector Interface::GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const { switch (property) { case PackageVersionMultiProperty::UpgradeCode: return UpgradeCodeTable::GetValuesByManifestId(connection, primaryId); default: return V1_5::Interface::GetMultiPropertyByPrimaryId(connection, primaryId, property); } } void Interface::DropTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "drop_tables_v1_6"); V1_4::Interface::DropTables(connection); UpgradeCodeTable::Drop(connection); savepoint.Commit(); } std::unique_ptr Interface::CreateSearchResultsTable(const SQLite::Connection& connection) const { return std::make_unique(connection); } void Interface::PerformQuerySearch(V1_0::SearchResultsTable& resultsTable, const RequestMatch& query) const { // First, do an exact match search for the folded system reference strings // We do this first because it is exact, and likely won't match anything else if it matches this. PackageMatchFilter filter(PackageMatchField::UpgradeCode, MatchType::Exact, Utility::FoldCase(query.Value)); resultsTable.SearchOnField(filter); // Then do the 1.5 search V1_5::Interface::PerformQuerySearch(resultsTable, query); } ISQLiteIndex::SearchResult Interface::SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const { // Update any system reference strings to be folded auto foldIfNeeded = [](PackageMatchFilter& filter) { if (filter.Field == PackageMatchField::UpgradeCode && filter.Type == MatchType::Exact) { filter.Value = Utility::FoldCase(filter.Value); } }; for (auto& inclusion : request.Inclusions) { foldIfNeeded(inclusion); } for (auto& filter : request.Filters) { foldIfNeeded(filter); } return V1_5::Interface::SearchInternal(connection, request); } void Interface::PrepareForPackaging(SQLite::Connection& connection, bool vacuum) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareforpackaging_v1_6"); V1_5::Interface::PrepareForPackaging(connection, false); UpgradeCodeTable::PrepareForPackaging(connection, GetOneToManyTableSchema(), true, true); savepoint.Commit(); if (vacuum) { Vacuum(connection); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/SearchResultsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_2/SearchResultsTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_6 { // Table for holding temporary search results. struct SearchResultsTable : public V1_2::SearchResultsTable { SearchResultsTable(const SQLite::Connection& connection) : V1_2::SearchResultsTable(connection) {} SearchResultsTable(const SearchResultsTable&) = delete; SearchResultsTable& operator=(const SearchResultsTable&) = delete; SearchResultsTable(SearchResultsTable&&) = default; SearchResultsTable& operator=(SearchResultsTable&&) = default; protected: std::vector BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/SearchResultsTable_1_6.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchResultsTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_6/UpgradeCodeTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_6 { std::vector SearchResultsTable::BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const { switch (field) { case PackageMatchField::UpgradeCode: return V1_0::ManifestTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike); default: return V1_2::SearchResultsTable::BuildSearchStatement(builder, field, manifestAlias, valueAlias, useLike); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/UpgradeCodeTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/1_0/OneToManyTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_6 { namespace details { using namespace std::string_view_literals; struct UpgradeCodeTableInfo { inline static constexpr std::string_view TableName() { return "upgradecodes"sv; } inline static constexpr std::string_view ValueName() { return "upgradecode"sv; } }; } // The table for UpgradeCode. using UpgradeCodeTable = V1_0::OneToManyTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_7/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_6/Interface.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_7 { using namespace std::string_view_literals; // Version 1.7 static constexpr std::string_view s_MetadataValueName_MapDataFolded = "mapDataFolded"sv; static constexpr char s_MetadataValue_MapDataFolded_Separator = ';'; // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public V1_6::Interface { static constexpr std::string_view MapDataFolded_VersionSpecifier = "1.7"sv; Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 SQLite::Version GetVersion() const override; std::vector GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const override; protected: void PrepareForPackaging(SQLite::Connection& connection, bool vacuum) override; V1_0::OneToManyTableSchema GetOneToManyTableSchema() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/1_7/Interface_1_7.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_7/Interface.h" #include #include "Microsoft/Schema/1_0/CommandsTable.h" #include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/TagsTable.h" #include "Microsoft/Schema/1_1/PackageFamilyNameTable.h" #include "Microsoft/Schema/1_2/NormalizedPackageNameTable.h" #include "Microsoft/Schema/1_2/NormalizedPackagePublisherTable.h" #include "Microsoft/Schema/1_6/UpgradeCodeTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_7 { namespace { bool ShouldFoldPropertyLookup(const SQLite::Connection& connection) { // Get the metadata indicator that we folded these multi properties. // If it contains the value for folding these properties in the 1.7 manner, also fold the incoming manifest // to the same value that it would have been folded to so that all manifest entries will have all of these properties. std::optional mapDataFolded = SQLite::MetadataTable::TryGetNamedValue(connection, s_MetadataValueName_MapDataFolded); if (mapDataFolded) { std::vector foldedSplit = Utility::Split(mapDataFolded.value(), s_MetadataValue_MapDataFolded_Separator); for (const std::string& splitValue : foldedSplit) { if (splitValue == Interface::MapDataFolded_VersionSpecifier) { return true; } } } return false; } } Interface::Interface(Utility::NormalizationVersion normVersion) : V1_6::Interface(normVersion) { } SQLite::Version Interface::GetVersion() const { return { 1, 7 }; } std::vector Interface::GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const { if (property == PackageVersionMultiProperty::PackageFamilyName || property == PackageVersionMultiProperty::Name || property == PackageVersionMultiProperty::Publisher || property == PackageVersionMultiProperty::UpgradeCode) { if (ShouldFoldPropertyLookup(connection)) { std::optional maximumManifestId = V1_0::OneToManyTableGetMapDataFoldingManifestTargetId(connection, primaryId); if (maximumManifestId) { primaryId = maximumManifestId.value(); } } } return V1_6::Interface::GetMultiPropertyByPrimaryId(connection, primaryId, property); } void Interface::PrepareForPackaging(SQLite::Connection& connection, bool vacuum) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareforpackaging_v1_7"); // Remove data that is not particularly interesting per-manifest std::array dataRemovalStatements{ V1_0::CommandsTable::PrepareMapDataFoldingStatement(connection), V1_0::TagsTable::PrepareMapDataFoldingStatement(connection), V1_1::PackageFamilyNameTable::PrepareMapDataFoldingStatement(connection), V1_2::NormalizedPackageNameTable::PrepareMapDataFoldingStatement(connection), V1_2::NormalizedPackagePublisherTable::PrepareMapDataFoldingStatement(connection), V1_6::UpgradeCodeTable::PrepareMapDataFoldingStatement(connection), }; std::vector allIdentifiers = V1_0::IdTable::GetAllRowIds(connection); for (SQLite::rowid_t id : allIdentifiers) { for (SQLite::Statement& statement : dataRemovalStatements) { statement.Reset(); statement.Bind(1, id); statement.Execute(); } } SQLite::MetadataTable::SetNamedValue(connection, s_MetadataValueName_MapDataFolded, MapDataFolded_VersionSpecifier); V1_6::Interface::PrepareForPackaging(connection, false); savepoint.Commit(); if (vacuum) { Vacuum(connection); } } V1_0::OneToManyTableSchema Interface::GetOneToManyTableSchema() const { return V1_0::OneToManyTableSchema::Version_1_7; } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/CommandsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/2_0/OneToManyTableWithMap.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using namespace std::string_view_literals; struct CommandsTableInfo { inline static constexpr std::string_view TableName() { return "commands2"sv; } inline static constexpr std::string_view ValueName() { return "command"sv; } }; } using CommandsTable = OneToManyTableWithMap; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/2_0/SearchResultsTable.h" #include "Microsoft/Schema/2_0/OneToManyTableWithMap.h" #include #include using namespace std::string_view_literals; namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { // Version 2.0 static constexpr std::string_view s_MetadataValueName_PackageUpdateTrackingBaseTime = "updateTrackingBase"sv; // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public ISQLiteIndex { Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; std::pair UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) override; SQLite::rowid_t RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest) override; void RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) override; void PrepareForPackaging(SQLite::Connection& connection) override; void PrepareForPackaging(const SQLiteIndexContext& context) override; bool CheckConsistency(const SQLite::Connection& connection, bool log) const override; SearchResult Search(const SQLite::Connection& connection, const SearchRequest& request) const override; std::optional GetPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionProperty property) const override; std::vector GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const override; std::optional GetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) const override; std::optional GetManifestIdByManifest(const SQLite::Connection& connection, const Manifest::Manifest& manifest) const override; std::vector GetVersionKeysById(const SQLite::Connection& connection, SQLite::rowid_t id) const override; // Version 1.1 MetadataResult GetMetadataByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId) const override; void SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value) override; // Version 1.2 Utility::NormalizedName NormalizeName(std::string_view name, std::string_view publisher) const override; // Version 1.4 Get all the dependencies for a specific manifest. std::set> GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId) const override; std::vector> GetDependentsById(const SQLite::Connection& connection, AppInstaller::Manifest::string_t packageId) const override; // Version 1.7 void DropTables(SQLite::Connection& connection) override; // Version 2.0 bool MigrateFrom(SQLite::Connection& connection, const ISQLiteIndex* current) override; void SetProperty(SQLite::Connection& connection, Property property, const std::string& value) override; protected: // Creates the search results table. virtual std::unique_ptr CreateSearchResultsTable(const SQLite::Connection& connection) const; // Executes all relevant searches for the query. virtual void PerformQuerySearch(SearchResultsTable& resultsTable, const RequestMatch& query) const; // Gets the one to many table schema to use. virtual OneToManyTableSchema GetOneToManyTableSchema() const; // Executes search on a request that can be modified. virtual SearchResult SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const; // Executes search on the given request. SearchResult BasicSearchInternal(const SQLite::Connection& connection, const SearchRequest& request) const; // Prepares for packaging, optionally vacuuming the database. virtual void PrepareForPackaging(const SQLiteIndexContext& context, bool vacuum); // Force the database to shrink the file size. // This *must* be done outside of an active transaction. void Vacuum(const SQLite::Connection& connection); // If before PrepareForPackaging is called, this should find the internal interface schema version and create the object. // If after PrepareForPackaging is called, this should not find the internal interface schema version and allow the code to fall through. // requireInternalInterface should be set to true for modifying functions. void EnsureInternalInterface(const SQLite::Connection& connection, bool requireInternalInterface = false) const; // Allows derived types to move to a different internal schema version. virtual std::unique_ptr CreateInternalInterface() const; // If EnsureInternalInterface has been called. mutable bool m_internalInterfaceChecked = false; // Interface to the data before PrepareForPackaging is called. mutable std::unique_ptr m_internalInterface; // The name normalization utility Utility::NameNormalizer m_normalizer; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/Interface_2_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "Microsoft/Schema/2_0/Interface.h" #include "Microsoft/Schema/2_0/PackagesTable.h" #include "Microsoft/Schema/2_0/TagsTable.h" #include "Microsoft/Schema/2_0/CommandsTable.h" #include "Microsoft/Schema/2_0/PackageFamilyNameTable.h" #include "Microsoft/Schema/2_0/ProductCodeTable.h" #include "Microsoft/Schema/2_0/NormalizedPackageNameTable.h" #include "Microsoft/Schema/2_0/NormalizedPackagePublisherTable.h" #include "Microsoft/Schema/2_0/UpgradeCodeTable.h" #include "Microsoft/Schema/2_0/SearchResultsTable.h" #include "Microsoft/Schema/2_0/PackageUpdateTrackingTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace anon { // Folds the values of the fields that are stored folded. void FoldPackageMatchFilters(std::vector& filters) { for (auto& filter : filters) { if ((filter.Field == PackageMatchField::PackageFamilyName || filter.Field == PackageMatchField::ProductCode || filter.Field == PackageMatchField::UpgradeCode) && filter.Type == MatchType::Exact) { filter.Value = Utility::FoldCase(filter.Value); } } } // Update NormalizedNameAndPublisher with normalization and folding // Returns true if the normalized name contains normalization field of fieldsToInclude bool UpdateNormalizedNameAndPublisher( PackageMatchFilter& filter, const Utility::NameNormalizer& normalizer, Utility::NormalizationField fieldsToInclude) { Utility::NormalizedName normalized = normalizer.Normalize(Utility::FoldCase(filter.Value), Utility::FoldCase(filter.Additional.value())); filter.Value = normalized.GetNormalizedName(fieldsToInclude); filter.Additional = normalized.Publisher(); return WI_AreAllFlagsSet(normalized.GetNormalizedFields(), fieldsToInclude); } bool UpdatePackageMatchFilters( std::vector& filters, const Utility::NameNormalizer& normalizer, Utility::NormalizationField normalizedNameFieldsToFilter = Utility::NormalizationField::None) { bool normalizedNameFieldsFound = false; for (auto itr = filters.begin(); itr != filters.end();) { if (itr->Field == PackageMatchField::NormalizedNameAndPublisher && itr->Type == MatchType::Exact) { if (!UpdateNormalizedNameAndPublisher(*itr, normalizer, normalizedNameFieldsToFilter)) { // If not matched, this package match filter will be removed. // For example, if caller is trying to search with arch info only, values without arch will be removed from search. itr = filters.erase(itr); continue; } normalizedNameFieldsFound = true; } ++itr; } return normalizedNameFieldsFound; } } Interface::Interface(Utility::NormalizationVersion normVersion) : m_normalizer(normVersion) { } SQLite::Version Interface::GetVersion() const { return { 2, 0 }; } void Interface::CreateTables(SQLite::Connection& connection, CreateOptions options) { m_internalInterface = CreateInternalInterface(); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createtables_v2_0"); // We only create the internal tables at this point, the actual 2.0 tables are created in PrepareForPackaging m_internalInterface->CreateTables(connection, options); savepoint.Commit(); m_internalInterfaceChecked = true; } SQLite::rowid_t Interface::AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { EnsureInternalInterface(connection, true); SQLite::rowid_t manifestId = m_internalInterface->AddManifest(connection, manifest, relativePath); PackageUpdateTrackingTable::Update(connection, m_internalInterface.get(), m_internalInterface->GetPropertyByPrimaryId(connection, manifestId, PackageVersionProperty::Id).value()); return manifestId; } std::pair Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) { EnsureInternalInterface(connection, true); std::pair result = m_internalInterface->UpdateManifest(connection, manifest, relativePath); if (result.first) { PackageUpdateTrackingTable::Update(connection, m_internalInterface.get(), m_internalInterface->GetPropertyByPrimaryId(connection, result.second, PackageVersionProperty::Id).value()); } return result; } SQLite::rowid_t Interface::RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest) { EnsureInternalInterface(connection, true); std::optional result = m_internalInterface->GetManifestIdByManifest(connection, manifest); // If the manifest doesn't actually exist, fail the remove. THROW_HR_IF(E_NOT_SET, !result); SQLite::rowid_t manifestId = result.value(); RemoveManifestById(connection, manifestId); return manifestId; } void Interface::RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) { EnsureInternalInterface(connection, true); std::optional identifier = m_internalInterface->GetPropertyByPrimaryId(connection, manifestId, PackageVersionProperty::Id); m_internalInterface->RemoveManifestById(connection, manifestId); if (identifier) { PackageUpdateTrackingTable::Update(connection, m_internalInterface.get(), identifier.value()); } } void Interface::PrepareForPackaging(SQLite::Connection&) { // We implement the context version THROW_HR(E_NOTIMPL); } void Interface::PrepareForPackaging(const SQLiteIndexContext& context) { EnsureInternalInterface(context.Connection, true); PrepareForPackaging(context, true); } bool Interface::CheckConsistency(const SQLite::Connection& connection, bool log) const { EnsureInternalInterface(connection); bool result = true; #define AICLI_CHECK_CONSISTENCY(_check_) \ if (result || log) \ { \ result = _check_ && result; \ } if (m_internalInterface) { AICLI_CHECK_CONSISTENCY(m_internalInterface->CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(PackageUpdateTrackingTable::CheckConsistency(connection, m_internalInterface.get(), log)); return result; } AICLI_CHECK_CONSISTENCY((PackagesTable::CheckConsistency< PackagesTable::IdColumn, PackagesTable::NameColumn, PackagesTable::MonikerColumn, PackagesTable::LatestVersionColumn, PackagesTable::ARPMinVersionColumn, PackagesTable::ARPMaxVersionColumn>(connection, log))); // Check the 1:N map tables for consistency AICLI_CHECK_CONSISTENCY(TagsTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(CommandsTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(PackageFamilyNameTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(ProductCodeTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(NormalizedPackageNameTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(NormalizedPackagePublisherTable::CheckConsistency(connection, log)); AICLI_CHECK_CONSISTENCY(UpgradeCodeTable::CheckConsistency(connection, log)); #undef AICLI_CHECK_CONSISTENCY return result; } ISQLiteIndex::SearchResult Interface::Search(const SQLite::Connection& connection, const SearchRequest& request) const { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->Search(connection, request); } SearchRequest requestCopy = request; return SearchInternal(connection, requestCopy); } std::optional Interface::GetPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionProperty property) const { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->GetPropertyByPrimaryId(connection, primaryId, property); } switch (property) { case PackageVersionProperty::Id: return PackagesTable::GetValueById(connection, primaryId); case PackageVersionProperty::Name: return PackagesTable::GetValueById(connection, primaryId); case PackageVersionProperty::Version: return PackagesTable::GetValueById(connection, primaryId); case PackageVersionProperty::Channel: return ""; case PackageVersionProperty::ManifestSHA256Hash: { std::optional hash = PackagesTable::GetValueById(connection, primaryId); return (!hash || hash->empty()) ? std::optional{} : Utility::SHA256::ConvertToString(hash.value()); } case PackageVersionProperty::ArpMinVersion: return PackagesTable::GetValueById(connection, primaryId); case PackageVersionProperty::ArpMaxVersion: return PackagesTable::GetValueById(connection, primaryId); case PackageVersionProperty::Moniker: return PackagesTable::GetValueById(connection, primaryId); default: return {}; } } std::vector Interface::GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->GetMultiPropertyByPrimaryId(connection, primaryId, property); } switch (property) { case PackageVersionMultiProperty::PackageFamilyName: return PackageFamilyNameTable::GetValuesByPrimaryId(connection, primaryId); case PackageVersionMultiProperty::ProductCode: return ProductCodeTable::GetValuesByPrimaryId(connection, primaryId); // These values are not right, as they are normalized. But they are good enough for now and all we have. case PackageVersionMultiProperty::Name: return NormalizedPackageNameTable::GetValuesByPrimaryId(connection, primaryId); case PackageVersionMultiProperty::Publisher: return NormalizedPackagePublisherTable::GetValuesByPrimaryId(connection, primaryId); case PackageVersionMultiProperty::UpgradeCode: return UpgradeCodeTable::GetValuesByPrimaryId(connection, primaryId); case PackageVersionMultiProperty::Tag: return TagsTable::GetValuesByPrimaryId(connection, primaryId); case PackageVersionMultiProperty::Command: return CommandsTable::GetValuesByPrimaryId(connection, primaryId); default: return {}; } } std::optional Interface::GetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) const { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->GetManifestIdByKey(connection, id, version, channel); } THROW_HR(E_NOT_VALID_STATE); } std::optional Interface::GetManifestIdByManifest(const SQLite::Connection& connection, const Manifest::Manifest& manifest) const { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->GetManifestIdByManifest(connection, manifest); } THROW_HR(E_NOT_VALID_STATE); } std::vector Interface::GetVersionKeysById(const SQLite::Connection& connection, SQLite::rowid_t id) const { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->GetVersionKeysById(connection, id); } THROW_HR(E_NOT_VALID_STATE); } ISQLiteIndex::MetadataResult Interface::GetMetadataByManifestId(const SQLite::Connection&, SQLite::rowid_t) const { return {}; } void Interface::SetMetadataByManifestId(SQLite::Connection&, SQLite::rowid_t, PackageVersionMetadata, std::string_view) { } Utility::NormalizedName Interface::NormalizeName(std::string_view name, std::string_view publisher) const { if (m_internalInterface) { return m_internalInterface->NormalizeName(name, publisher); } return m_normalizer.Normalize(name, publisher); } std::set> Interface::GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t rowid) const { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->GetDependenciesByManifestRowId(connection, rowid); } THROW_HR(E_NOT_VALID_STATE); } std::vector> Interface::GetDependentsById(const SQLite::Connection& connection, AppInstaller::Manifest::string_t id) const { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->GetDependentsById(connection, id); } THROW_HR(E_NOT_VALID_STATE); } void Interface::DropTables(SQLite::Connection& connection) { EnsureInternalInterface(connection); if (m_internalInterface) { return m_internalInterface->DropTables(connection); } SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "drop_tables_v2_0"); PackagesTable::Drop(connection); TagsTable::Drop(connection); CommandsTable::Drop(connection); PackageFamilyNameTable::Drop(connection); ProductCodeTable::Drop(connection); NormalizedPackageNameTable::Drop(connection); NormalizedPackagePublisherTable::Drop(connection); UpgradeCodeTable::Drop(connection); savepoint.Commit(); } bool Interface::MigrateFrom(SQLite::Connection& connection, const ISQLiteIndex* current) { THROW_HR_IF_NULL(E_POINTER, current); auto currentVersion = current->GetVersion(); if (currentVersion.MajorVersion != 1 || currentVersion.MinorVersion != 7) { return false; } SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "migrate_from_v2_0"); // We only need to insert all of the existing packages into the update tracking table. PackageUpdateTrackingTable::EnsureExists(connection); SearchResult allPackages = current->Search(connection, {}); for (const auto& packageMatch : allPackages.Matches) { std::vector versionKeys = current->GetVersionKeysById(connection, packageMatch.first); ISQLiteIndex::VersionKey& latestVersionKey = versionKeys[0]; PackageUpdateTrackingTable::Update(connection, current, current->GetPropertyByPrimaryId(connection, latestVersionKey.ManifestId, PackageVersionProperty::Id).value(), false); } savepoint.Commit(); return true; } void Interface::SetProperty(SQLite::Connection& connection, Property property, const std::string& value) { switch (property) { case Property::PackageUpdateTrackingBaseTime: { int64_t baseTime = 0; if (value.empty()) { baseTime = Utility::GetCurrentUnixEpoch(); } else { baseTime = std::stoll(value); } SQLite::MetadataTable::SetNamedValue(connection, s_MetadataValueName_PackageUpdateTrackingBaseTime, std::to_string(baseTime)); } break; default: THROW_WIN32(ERROR_NOT_SUPPORTED); } } std::unique_ptr Interface::CreateSearchResultsTable(const SQLite::Connection& connection) const { return std::make_unique(connection); } void Interface::PerformQuerySearch(SearchResultsTable& resultsTable, const RequestMatch& query) const { // First, do an exact match search for the folded system reference strings // We do this first because it is exact, and likely won't match anything else if it matches this. PackageMatchFilter filter(PackageMatchField::Unknown, MatchType::Exact, Utility::FoldCase(query.Value)); for (PackageMatchField field : { PackageMatchField::PackageFamilyName, PackageMatchField::ProductCode, PackageMatchField::UpgradeCode }) { filter.Field = field; resultsTable.SearchOnField(filter); } // Now search on the unfolded value filter.Value = query.Value; for (MatchType match : GetDefaultMatchTypeOrder(query.Type)) { filter.Type = match; for (auto field : { PackageMatchField::Id, PackageMatchField::Name, PackageMatchField::Moniker, PackageMatchField::Command, PackageMatchField::Tag }) { filter.Field = field; resultsTable.SearchOnField(filter); } } } OneToManyTableSchema Interface::GetOneToManyTableSchema() const { return OneToManyTableSchema::Version_2_0; } ISQLiteIndex::SearchResult Interface::SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const { anon::FoldPackageMatchFilters(request.Inclusions); anon::FoldPackageMatchFilters(request.Filters); if (request.Purpose == SearchPurpose::CorrelationToInstalled) { // Correlate from available package to installed package // For available package to installed package mapping, only one try is needed. // For example, if ARP DisplayName contains arch, then the installed package's ARP DisplayName should also include arch. auto candidateInclusionsWithArch = request.Inclusions; if (anon::UpdatePackageMatchFilters(candidateInclusionsWithArch, m_normalizer, Utility::NormalizationField::Architecture)) { // If DisplayNames contain arch, only use Inclusions with arch for search request.Inclusions = candidateInclusionsWithArch; } else { // Otherwise, just update the Inclusions with normalization anon::UpdatePackageMatchFilters(request.Inclusions, m_normalizer); } return BasicSearchInternal(connection, request); } else if (request.Purpose == SearchPurpose::CorrelationToAvailable) { // For installed package to available package correlation, // try the search with NormalizedName with Arch first, if not found, try with all values. // This can be extended in the future for more granular search requests. std::vector candidateSearches; auto candidateSearchWithArch = request; if (anon::UpdatePackageMatchFilters(candidateSearchWithArch.Inclusions, m_normalizer, Utility::NormalizationField::Architecture)) { candidateSearches.emplace_back(std::move(candidateSearchWithArch)); } anon::UpdatePackageMatchFilters(request.Inclusions, m_normalizer); candidateSearches.emplace_back(request); SearchResult result; for (auto& candidateSearch : candidateSearches) { result = BasicSearchInternal(connection, candidateSearch); if (!result.Matches.empty()) { break; } } return result; } else { anon::UpdatePackageMatchFilters(request.Inclusions, m_normalizer); anon::UpdatePackageMatchFilters(request.Filters, m_normalizer); return BasicSearchInternal(connection, request); } } ISQLiteIndex::SearchResult Interface::BasicSearchInternal(const SQLite::Connection& connection, const SearchRequest& request) const { if (request.IsForEverything()) { std::vector ids = PackagesTable::GetAllRowIds(connection, PackagesTable::IdColumn::Name, request.MaximumResults); SearchResult result; for (SQLite::rowid_t id : ids) { result.Matches.emplace_back(std::make_pair(id, PackageMatchFilter(PackageMatchField::Id, MatchType::Wildcard))); } result.Truncated = (request.MaximumResults && PackagesTable::GetCount(connection) > request.MaximumResults); return result; } // First phase, create the search results table and populate it with the initial results. // If the Query is provided, we search across many fields and put results in together. // If Inclusions has fields, we add these to the data. // If neither is defined, we take the first filter and use it as the initial results search. std::unique_ptr resultsTable = CreateSearchResultsTable(connection); bool inclusionsAttempted = false; if (request.Query) { // Perform searches across multiple tables to populate the initial results. PerformQuerySearch(*resultsTable.get(), request.Query.value()); inclusionsAttempted = true; } if (!request.Inclusions.empty()) { for (auto include : request.Inclusions) { for (MatchType match : GetDefaultMatchTypeOrder(include.Type)) { include.Type = match; resultsTable->SearchOnField(include); } } inclusionsAttempted = true; } size_t filterIndex = 0; if (!inclusionsAttempted) { THROW_HR_IF(E_UNEXPECTED, request.Filters.empty()); // Perform search for just the field matching the first filter PackageMatchFilter filter = request.Filters[0]; for (MatchType match : GetDefaultMatchTypeOrder(filter.Type)) { filter.Type = match; resultsTable->SearchOnField(filter); } // Skip the filter as we already know everything matches filterIndex = 1; } // Remove any duplicate manifest entries resultsTable->RemoveDuplicatePackageRows(); // Second phase, for remaining filters, flag matching search results, then remove unflagged values. for (size_t i = filterIndex; i < request.Filters.size(); ++i) { PackageMatchFilter filter = request.Filters[i]; resultsTable->PrepareToFilter(); for (MatchType match : GetDefaultMatchTypeOrder(filter.Type)) { filter.Type = match; resultsTable->FilterOnField(filter); } resultsTable->CompleteFilter(); } return resultsTable->GetSearchResults(request.MaximumResults); } void Interface::PrepareForPackaging(const SQLiteIndexContext& context, bool vacuum) { SQLite::Connection& connection = context.Connection; // Get the base time from metadata int64_t updateBaseTime = 0; std::optional updateBaseTimeString = SQLite::MetadataTable::TryGetNamedValue(connection, s_MetadataValueName_PackageUpdateTrackingBaseTime); if (updateBaseTimeString && !updateBaseTimeString->empty()) { updateBaseTime = std::stoll(updateBaseTimeString.value()); } // Get the output directory or use the file path std::filesystem::path baseOutputDirectory; if (context.Data.Contains(Property::IntermediateFileOutputPath)) { baseOutputDirectory = context.Data.Get(); } else if (context.Data.Contains(Property::DatabaseFilePath)) { baseOutputDirectory = context.Data.Get(); baseOutputDirectory = baseOutputDirectory.parent_path(); } THROW_WIN32_IF(ERROR_INVALID_STATE, baseOutputDirectory.empty() || baseOutputDirectory.is_relative()); // Output all of the changed package version manifests since the base time to the target location for (const auto& packageData : PackageUpdateTrackingTable::GetUpdatesSince(connection, updateBaseTime)) { std::filesystem::path packageDirectory = baseOutputDirectory / Manifest::PackageVersionDataManifest::GetRelativeDirectoryPath(packageData.PackageIdentifier, Utility::SHA256::ConvertToString(packageData.Hash)); std::filesystem::create_directories(packageDirectory); std::filesystem::path manifestPath = packageDirectory / Manifest::PackageVersionDataManifest::VersionManifestCompressedFileName(); AICLI_LOG(Repo, Info, << "Writing PackageVersionDataManifest for [" << packageData.PackageIdentifier << "] to [" << manifestPath << "]"); std::ofstream stream(manifestPath, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); THROW_LAST_ERROR_IF(stream.fail()); stream.write(reinterpret_cast(packageData.Manifest.data()), packageData.Manifest.size()); THROW_LAST_ERROR_IF(stream.fail()); stream.flush(); } SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareforpackaging_v2_0"); // Create the 2.0 data tables PackagesTable::Create< PackagesTable::IdColumn, PackagesTable::NameColumn, PackagesTable::MonikerColumn, PackagesTable::LatestVersionColumn, PackagesTable::ARPMinVersionColumn, PackagesTable::ARPMaxVersionColumn, PackagesTable::HashColumn >(connection); TagsTable::Create(connection, GetOneToManyTableSchema()); CommandsTable::Create(connection, GetOneToManyTableSchema()); PackageFamilyNameTable::Create(connection); ProductCodeTable::Create(connection); NormalizedPackageNameTable::Create(connection); NormalizedPackagePublisherTable::Create(connection); UpgradeCodeTable::Create(connection); // Copy data from 1.7 tables to 2.0 tables SearchResult allPackages = m_internalInterface->Search(connection, {}); for (const auto& packageMatch : allPackages.Matches) { std::vector versionKeys = m_internalInterface->GetVersionKeysById(connection, packageMatch.first); ISQLiteIndex::VersionKey& latestVersionKey = versionKeys[0]; std::string packageIdentifier = m_internalInterface->GetPropertyByPrimaryId(connection, latestVersionKey.ManifestId, PackageVersionProperty::Id).value(); std::vector packageData{ { PackagesTable::IdColumn::Name, packageIdentifier }, { PackagesTable::NameColumn::Name, m_internalInterface->GetPropertyByPrimaryId(connection, latestVersionKey.ManifestId, PackageVersionProperty::Name).value() }, { PackagesTable::LatestVersionColumn::Name, latestVersionKey.VersionAndChannel.GetVersion().ToString() }, }; auto addIfPresent = [&](std::string_view name, std::optional&& value) { if (value && !value->empty()) { packageData.emplace_back(PackagesTable::NameValuePair{ name, std::move(value).value() }); } }; addIfPresent(PackagesTable::MonikerColumn::Name, m_internalInterface->GetPropertyByPrimaryId(connection, latestVersionKey.ManifestId, PackageVersionProperty::Moniker).value()); addIfPresent(PackagesTable::ARPMinVersionColumn::Name, m_internalInterface->GetPropertyByPrimaryId(connection, latestVersionKey.ManifestId, PackageVersionProperty::ArpMinVersion).value()); addIfPresent(PackagesTable::ARPMaxVersionColumn::Name, m_internalInterface->GetPropertyByPrimaryId(connection, latestVersionKey.ManifestId, PackageVersionProperty::ArpMaxVersion).value()); SQLite::rowid_t packageId = PackagesTable::Insert(connection, packageData); PackagesTable::UpdateValueIdById(connection, packageId, PackageUpdateTrackingTable::GetDataHash(connection, packageIdentifier)); for (const auto& versionKey : versionKeys) { TagsTable::EnsureExistsAndInsert(connection, m_internalInterface->GetMultiPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionMultiProperty::Tag), packageId); CommandsTable::EnsureExistsAndInsert(connection, m_internalInterface->GetMultiPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionMultiProperty::Command), packageId); PackageFamilyNameTable::EnsureExists(connection, m_internalInterface->GetMultiPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionMultiProperty::PackageFamilyName), packageId); ProductCodeTable::EnsureExists(connection, m_internalInterface->GetMultiPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionMultiProperty::ProductCode), packageId); NormalizedPackageNameTable::EnsureExists(connection, m_internalInterface->GetMultiPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionMultiProperty::Name), packageId); NormalizedPackagePublisherTable::EnsureExists(connection, m_internalInterface->GetMultiPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionMultiProperty::Publisher), packageId); UpgradeCodeTable::EnsureExists(connection, m_internalInterface->GetMultiPropertyByPrimaryId(connection, versionKey.ManifestId, PackageVersionMultiProperty::UpgradeCode), packageId); } } PackagesTable::PrepareForPackaging< PackagesTable::IdColumn, PackagesTable::NameColumn, PackagesTable::MonikerColumn, PackagesTable::LatestVersionColumn, PackagesTable::ARPMinVersionColumn, PackagesTable::ARPMaxVersionColumn, PackagesTable::HashColumn >(connection); TagsTable::PrepareForPackaging(connection); CommandsTable::PrepareForPackaging(connection); PackageUpdateTrackingTable::Drop(connection); // The tables based on SystemReferenceStringTable don't need a prepare currently // Drop 1.7 tables m_internalInterface->DropTables(connection); savepoint.Commit(); m_internalInterface.reset(); if (vacuum) { Vacuum(connection); } } void Interface::Vacuum(const SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Vacuum(); builder.Execute(connection); } void Interface::EnsureInternalInterface(const SQLite::Connection& connection, bool requireInternalInterface) const { if (!m_internalInterfaceChecked) { if (!PackagesTable::Exists(connection)) { m_internalInterface = CreateInternalInterface(); } m_internalInterfaceChecked = true; } THROW_HR_IF(E_NOT_VALID_STATE, requireInternalInterface && !m_internalInterface); } std::unique_ptr Interface::CreateInternalInterface() const { return CreateISQLiteIndex({ 1, 7 }); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/NormalizedPackageNameTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/2_0/SystemReferenceStringTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using namespace std::string_view_literals; struct NormalizedPackageNameTableInfo { inline static constexpr std::string_view TableName() { return "norm_names2"sv; } inline static constexpr std::string_view ValueName() { return "norm_name"sv; } }; } // The table for Commands. using NormalizedPackageNameTable = SystemReferenceStringTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/NormalizedPackagePublisherTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/2_0/SystemReferenceStringTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using namespace std::string_view_literals; struct NormalizedPackagePublisherTableInfo { inline static constexpr std::string_view TableName() { return "norm_publishers2"sv; } inline static constexpr std::string_view ValueName() { return "norm_publisher"sv; } }; } using NormalizedPackagePublisherTable = SystemReferenceStringTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/OneToManyTableWithMap.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/2_0/OneToManyTableWithMap.h" #include "Microsoft/Schema/2_0/PackagesTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using PrimaryTable = PackagesTable; using namespace std::string_view_literals; static constexpr std::string_view s_OneToManyTableWithMap_MapTable_PrimaryName = "package"sv; static constexpr std::string_view s_OneToManyTableWithMap_MapTable_Suffix = "_map"sv; static constexpr std::string_view s_OneToManyTableWithMap_MapTable_IndexSuffix = "_index"sv; static constexpr std::string_view s_OneToManyTableWithMap_PrimaryKeyIndexSuffix = "_pkindex"sv; namespace anon { // Create the mapping table insert statement for multiple use. // Bind the rowid of the value to 2. SQLite::Statement CreateMappingInsertStatementForPrimaryId(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId) { SQLite::Builder::StatementBuilder insertMappingBuilder; insertMappingBuilder.InsertOrIgnore({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }). Columns({ s_OneToManyTableWithMap_MapTable_PrimaryName, valueName }).Values(manifestId, SQLite::Builder::Unbound); return insertMappingBuilder.Prepare(connection); } // Get a collection of the value ids associated with the given primary id. std::vector GetValueIdsByPrimaryId(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId) { std::vector result; SQLite::Builder::StatementBuilder selectMappingBuilder; selectMappingBuilder.Select(valueName).From({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }).Where(s_OneToManyTableWithMap_MapTable_PrimaryName).Equals(manifestId); SQLite::Statement selectMappingStatement = selectMappingBuilder.Prepare(connection); while (selectMappingStatement.Step()) { result.push_back(selectMappingStatement.GetColumn(0)); } return result; } void CreateDataTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_create_v2_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(tableName).Columns({ IntegerPrimaryKey(), ColumnBuilder(valueName, Type::Text).NotNull() }); createTableBuilder.Execute(connection); StatementBuilder indexBuilder; indexBuilder.CreateUniqueIndex({ tableName, s_OneToManyTableWithMap_PrimaryKeyIndexSuffix }).On(tableName).Columns(valueName); indexBuilder.Execute(connection); savepoint.Commit(); } void DropDataTable(SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable(tableName); dropTableBuilder.Execute(connection); } std::optional DataTableSelectIdByValue(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool useLike) { SQLite::Builder::StatementBuilder selectBuilder; selectBuilder.Select(SQLite::RowIDName).From(tableName).Where(valueName); if (useLike) { selectBuilder.LikeWithEscape(value); } else { selectBuilder.Equals(value); } SQLite::Statement select = selectBuilder.Prepare(connection); if (select.Step()) { return select.GetColumn(0); } else { return {}; } } std::optional DataTableSelectValueById(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t rowid) { SQLite::Builder::StatementBuilder selectBuilder; selectBuilder.Select(valueName).From(tableName).Where(SQLite::RowIDName).Equals(rowid); SQLite::Statement select = selectBuilder.Prepare(connection); if (select.Step()) { return select.GetColumn(0); } else { return {}; } } SQLite::rowid_t DataTableEnsureExists(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool overwriteLikeMatch = false) { auto selectResult = DataTableSelectIdByValue(connection, tableName, valueName, value, overwriteLikeMatch); if (selectResult) { if (overwriteLikeMatch) { // If the value in the table is not an exact match, overwrite it with the incoming value auto tableValue = DataTableSelectValueById(connection, tableName, valueName, selectResult.value()); if (tableValue.value() != value) { SQLite::Builder::StatementBuilder updateBuilder; updateBuilder.Update(tableName).Set().Column(valueName).Equals(value).Where(SQLite::RowIDName).Equals(selectResult); updateBuilder.Execute(connection); } } return selectResult.value(); } SQLite::Builder::StatementBuilder insertBuilder; insertBuilder.InsertInto(tableName).Columns(valueName).Values(value); insertBuilder.Execute(connection); return connection.GetLastInsertRowID(); } void DataTablePrepareForPackaging(SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder dropIndexBuilder; dropIndexBuilder.DropIndex({ tableName, s_OneToManyTableWithMap_PrimaryKeyIndexSuffix }); dropIndexBuilder.Execute(connection); } bool DataTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log) { // Build a select statement to find values that contain an embedded null character // Such as: // Select count(*) from table where instr(value,char(0))>0 SQLite::Builder::StatementBuilder builder; builder. Select({ SQLite::RowIDName, valueName }). From(tableName). WhereValueContainsEmbeddedNullCharacter(valueName); SQLite::Statement select = builder.Prepare(connection); bool result = true; while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] value in table [" << tableName << "] at row [" << select.GetColumn(0) << "] contains an embedded null character and starts with [" << select.GetColumn(1) << "]"); } return result; } } std::string OneToManyTableWithMapGetMapTableName(std::string_view tableName) { std::string result(tableName); result += s_OneToManyTableWithMap_MapTable_Suffix; return result; } std::string_view OneToManyTableWithMapGetManifestColumnName() { return s_OneToManyTableWithMap_MapTable_PrimaryName; } void CreateOneToManyTableWithMap(SQLite::Connection& connection, OneToManyTableSchema schemaVersion, std::string_view tableName, std::string_view valueName) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_create_v2_0"); // Create the data table as a 1:1 anon::CreateDataTable(connection, tableName, valueName); switch (schemaVersion) { case OneToManyTableSchema::Version_2_0: { // Create the mapping table StatementBuilder createMapTableBuilder; createMapTableBuilder.CreateTable({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }).Columns({ ColumnBuilder(valueName, Type::Int64).NotNull(), ColumnBuilder(s_OneToManyTableWithMap_MapTable_PrimaryName, Type::Int64).NotNull(), PrimaryKeyBuilder({ valueName, s_OneToManyTableWithMap_MapTable_PrimaryName }) }).WithoutRowID(); createMapTableBuilder.Execute(connection); } break; default: THROW_HR(E_UNEXPECTED); } StatementBuilder createMapTableIndexBuilder; createMapTableIndexBuilder.CreateIndex({ tableName, s_OneToManyTableWithMap_MapTable_Suffix, s_OneToManyTableWithMap_MapTable_IndexSuffix }). On({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }).Columns({ s_OneToManyTableWithMap_MapTable_PrimaryName, valueName }); createMapTableIndexBuilder.Execute(connection); savepoint.Commit(); } void DropOneToManyTableWithMap(SQLite::Connection& connection, std::string_view tableName) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_drop_v2_0"); anon::DropDataTable(connection, tableName); SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }); dropTableBuilder.Execute(connection); savepoint.Commit(); } std::vector OneToManyTableWithMapGetValuesByPrimaryId( const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t manifestId) { using QCol = SQLite::Builder::QualifiedColumn; std::vector result; SQLite::Builder::StatementBuilder builder; builder.Select(QCol(tableName, valueName)). From({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }).As("map").Join(tableName). On(QCol("map", valueName), QCol(tableName, SQLite::RowIDName)).Where(QCol("map", s_OneToManyTableWithMap_MapTable_PrimaryName)).Equals(manifestId); SQLite::Statement statement = builder.Prepare(connection); while (statement.Step()) { result.emplace_back(statement.GetColumn(0)); } return result; } void OneToManyTableWithMapEnsureExistsAndInsert(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, const std::vector& values, SQLite::rowid_t manifestId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_ensureandinsert_v2_0"); SQLite::Statement insertMapping = anon::CreateMappingInsertStatementForPrimaryId(connection, tableName, valueName, manifestId); for (const std::string& value : values) { // First, ensure that the data exists SQLite::rowid_t dataId = anon::DataTableEnsureExists(connection, tableName, valueName, value); // Second, insert into the mapping table insertMapping.Reset(); insertMapping.Bind(2, dataId); insertMapping.Execute(); } savepoint.Commit(); } void OneToManyTableWithMapPrepareForPackaging(SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder dropMapTableIndexBuilder; dropMapTableIndexBuilder.DropIndex({ tableName, s_OneToManyTableWithMap_MapTable_Suffix, s_OneToManyTableWithMap_MapTable_IndexSuffix }); dropMapTableIndexBuilder.Execute(connection); anon::DataTablePrepareForPackaging(connection, tableName); } bool OneToManyTableWithMapCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log) { using QCol = SQLite::Builder::QualifiedColumn; constexpr std::string_view s_map = "map"sv; bool result = true; { // Build a select statement to find map rows containing references to primaries with nonexistent rowids // Such as: // Select map.rowid, map.primary from tags_map as map left outer join primary on map.primary = primary.rowid where primary.id is null SQLite::Builder::StatementBuilder builder; builder. Select({ QCol(s_map, s_OneToManyTableWithMap_MapTable_PrimaryName), QCol(s_map, valueName) }). From({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }).As(s_map). LeftOuterJoin(details::PrimaryTable::TableName()).On(QCol(s_map, s_OneToManyTableWithMap_MapTable_PrimaryName), QCol(details::PrimaryTable::TableName(), SQLite::RowIDName)). Where(QCol(details::PrimaryTable::TableName(), SQLite::RowIDName)).IsNull(); SQLite::Statement select = builder.Prepare(connection); while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] " << tableName << s_OneToManyTableWithMap_MapTable_Suffix << " [" << select.GetColumn(0) << ", " << select.GetColumn(1) << "] refers to invalid " << details::PrimaryTable::TableName()); } } if (!result && !log) { return result; } { // Build a select statement to find map rows containing references to 1:1 tables with nonexistent rowids // Such as: // Select map.rowid, map.tag from tags_map as map left outer join tags on map.tag = tags.rowid where tags.tag is null SQLite::Builder::StatementBuilder builder; builder. Select({ QCol(s_map, s_OneToManyTableWithMap_MapTable_PrimaryName), QCol(s_map, valueName) }). From({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }).As(s_map). LeftOuterJoin(tableName).On(QCol(s_map, valueName), QCol(tableName, SQLite::RowIDName)). Where(QCol(tableName, valueName)).IsNull(); SQLite::Statement select = builder.Prepare(connection); bool secondaryResult = true; while (select.Step()) { secondaryResult = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] " << tableName << s_OneToManyTableWithMap_MapTable_Suffix << " [" << select.GetColumn(0) << ", " << select.GetColumn(1) << "] refers to invalid " << tableName); } result = result && secondaryResult; } if (!result && !log) { return result; } result = anon::DataTableCheckConsistency(connection, tableName, valueName, log) && result; return result; } bool OneToManyTableWithMapIsEmpty(SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder countBuilder; countBuilder.Select(SQLite::Builder::RowCount).From(tableName); SQLite::Statement countStatement = countBuilder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); SQLite::Builder::StatementBuilder countMapBuilder; countMapBuilder.Select(SQLite::Builder::RowCount).From({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }); SQLite::Statement countMapStatement = countMapBuilder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countMapStatement.Step()); return ((countStatement.GetColumn(0) == 0) && (countMapStatement.GetColumn(0) == 0)); } int OneToManyTableWithMapBuildSearchStatement( SQLite::Builder::StatementBuilder& builder, std::string_view tableName, std::string_view valueName, std::string_view primaryAlias, std::string_view valueAlias, bool useLike) { using QCol = SQLite::Builder::QualifiedColumn; constexpr std::string_view s_map = "map"sv; // Build a statement like: // SELECT map.package as p, table.value as v from table // join map on table.rowid = map.value // where table.value = builder.Select(). Column(QCol(s_map, s_OneToManyTableWithMap_MapTable_PrimaryName)).As(primaryAlias). Column(QCol(tableName, valueName)).As(valueAlias). From(tableName). Join({ tableName, s_OneToManyTableWithMap_MapTable_Suffix }).As(s_map).On(QCol(tableName, SQLite::RowIDName), QCol(s_map, valueName)). Where(QCol(tableName, valueName)); int result = -1; if (useLike) { builder.Like(SQLite::Builder::Unbound); result = builder.GetLastBindIndex(); builder.Escape(SQLite::EscapeCharForLike); } else { builder.Equals(SQLite::Builder::Unbound); result = builder.GetLastBindIndex(); } return result; } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/OneToManyTableWithMap.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { // Allow the different schema version to indicate which they are. enum class OneToManyTableSchema { // Uses a named unique index for data table. // Map table has primary key and no rowid. // Column order is consistent with primary key order. Version_2_0, }; namespace details { // Returns the map table name for a given table. std::string OneToManyTableGetMapTableName(std::string_view tableName); // Returns the primary column name. std::string_view OneToManyTableGetManifestColumnName(); // Create the tables. void CreateOneToManyTableWithMap(SQLite::Connection& connection, OneToManyTableSchema schemaVersion, std::string_view tableName, std::string_view valueName); // Drops the tables. void DropOneToManyTableWithMap(SQLite::Connection& connection, std::string_view tableName); // Gets all values associated with the given primary id. std::vector OneToManyTableWithMapGetValuesByPrimaryId( const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t primaryId); // Ensures that the value exists and inserts mapping entries. void OneToManyTableWithMapEnsureExistsAndInsert(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, const std::vector& values, SQLite::rowid_t primaryId); // Removes data that is no longer needed for an index that is to be published. void OneToManyTableWithMapPrepareForPackaging(SQLite::Connection& connection, std::string_view tableName); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. bool OneToManyTableWithMapCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log); // Determines if the table is empty. bool OneToManyTableWithMapIsEmpty(SQLite::Connection& connection, std::string_view tableName); // Builds the search select statement base on the given value. // The return value is the bind index of the value to match against. int OneToManyTableWithMapBuildSearchStatement( SQLite::Builder::StatementBuilder& builder, std::string_view tableName, std::string_view valueName, std::string_view primaryAlias, std::string_view valueAlias, bool useLike); } // A table that represents a value that is 1:N with a primary entry. template struct OneToManyTableWithMap { // The name of the table. static constexpr std::string_view TableName() { return TableInfo::TableName(); } // The value name of the table. static constexpr std::string_view ValueName() { return TableInfo::ValueName(); } // Creates the table with named indices. static void Create(SQLite::Connection& connection, OneToManyTableSchema schemaVersion) { details::CreateOneToManyTableWithMap(connection, schemaVersion, TableInfo::TableName(), TableInfo::ValueName()); } // Drops the tables. static void Drop(SQLite::Connection& connection) { details::DropOneToManyTableWithMap(connection, TableInfo::TableName()); } // Gets all values associated with the given primary id. static std::vector GetValuesByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId) { return details::OneToManyTableWithMapGetValuesByPrimaryId(connection, TableInfo::TableName(), TableInfo::ValueName(), primaryId); } // Ensures that all values exist in the data table, and inserts into the mapping table for the given primary id. static void EnsureExistsAndInsert(SQLite::Connection& connection, const std::vector& values, SQLite::rowid_t primaryId) { details::OneToManyTableWithMapEnsureExistsAndInsert(connection, TableInfo::TableName(), TableInfo::ValueName(), values, primaryId); } // Removes data that is no longer needed for an index that is to be published. // Preserving the primary index will improve the efficiency of finding the values associated with a primary. // Preserving the values index will improve searching when it is primarily done by equality. static void PrepareForPackaging(SQLite::Connection& connection) { details::OneToManyTableWithMapPrepareForPackaging(connection, TableInfo::TableName()); } // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. static bool CheckConsistency(const SQLite::Connection& connection, bool log) { return details::OneToManyTableWithMapCheckConsistency(connection, TableInfo::TableName(), TableInfo::ValueName(), log); } // Determines if the table is empty. static bool IsEmpty(SQLite::Connection& connection) { return details::OneToManyTableWithMapIsEmpty(connection, TableInfo::TableName()); } // Builds the search select statement base on the given value. // The return value is the bind index of the value to match against. static int BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, std::string_view primaryAlias, std::string_view valueAlias, bool useLike) { return details::OneToManyTableWithMapBuildSearchStatement(builder, TableInfo::TableName(), TableInfo::ValueName(), primaryAlias, valueAlias, useLike); } }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/PackageFamilyNameTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/2_0/SystemReferenceStringTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using namespace std::string_view_literals; struct PackageFamilyNameTableInfo { inline static constexpr std::string_view TableName() { return "pfns2"sv; } inline static constexpr std::string_view ValueName() { return "pfn"sv; } }; } using PackageFamilyNameTable = SystemReferenceStringTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/PackageUpdateTrackingTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageUpdateTrackingTable.h" #include #include using namespace AppInstaller::SQLite; namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { using namespace std::string_view_literals; static constexpr std::string_view s_PUTT_Table_Name = "update_tracking"sv; static constexpr std::string_view s_PUTT_WriteTimeIndex_Name = "update_tracking_write_idx"sv; static constexpr std::string_view s_PUTT_Package = "package"sv; static constexpr std::string_view s_PUTT_WriteTime = "write_time"sv; static constexpr std::string_view s_PUTT_Manifest = "manifest"sv; static constexpr std::string_view s_PUTT_Hash = "hash"sv; std::string_view PackageUpdateTrackingTable::TableName() { return s_PUTT_Table_Name; } void PackageUpdateTrackingTable::Create(SQLite::Connection& connection) { using namespace Builder; StatementBuilder builder; builder.CreateTable(s_PUTT_Table_Name).BeginColumns(); builder.Column(IntegerPrimaryKey()); builder.Column(ColumnBuilder(s_PUTT_Package, Type::Text).NotNull()); builder.Column(ColumnBuilder(s_PUTT_WriteTime, Type::Int64).NotNull()); builder.Column(ColumnBuilder(s_PUTT_Manifest, Type::Blob).NotNull()); builder.Column(ColumnBuilder(s_PUTT_Hash, Type::Blob).NotNull()); builder.EndColumns(); builder.Execute(connection); StatementBuilder indexBuilder; indexBuilder.CreateIndex(s_PUTT_WriteTimeIndex_Name).On(s_PUTT_Table_Name).Columns(s_PUTT_WriteTime); indexBuilder.Execute(connection); } void PackageUpdateTrackingTable::EnsureExists(SQLite::Connection& connection) { if (!Exists(connection)) { Create(connection); } } void PackageUpdateTrackingTable::Drop(SQLite::Connection& connection) { Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable(s_PUTT_Table_Name); dropTableBuilder.Execute(connection); } bool PackageUpdateTrackingTable::Exists(const SQLite::Connection& connection) { Builder::StatementBuilder builder; builder.Select(Builder::RowCount).From(Builder::Schema::MainTable). Where(Builder::Schema::TypeColumn).Equals(Builder::Schema::Type_Table).And(Builder::Schema::NameColumn).Equals(s_PUTT_Table_Name); Statement statement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !statement.Step()); return statement.GetColumn(0) != 0; } void PackageUpdateTrackingTable::Update(SQLite::Connection& connection, const ISQLiteIndex* internalIndex, const std::string& packageIdentifier, bool ensureTable) { if (ensureTable) { EnsureExists(connection); } SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, packageIdentifier); auto result = internalIndex->Search(connection, request); if (result.Matches.empty()) { // Remove any existing package update row Builder::StatementBuilder deleteBuilder; deleteBuilder.DeleteFrom(s_PUTT_Table_Name).Where(s_PUTT_Package).LikeWithEscape(packageIdentifier); deleteBuilder.Execute(connection); } else { THROW_HR_IF(E_UNEXPECTED, result.Matches.size() != 1); // Insert or update the package row std::vector versionKeys = internalIndex->GetVersionKeysById(connection, result.Matches[0].first); Manifest::PackageVersionDataManifest manifest; for (const auto& key : versionKeys) { Manifest::PackageVersionDataManifest::VersionData versionData{ key.VersionAndChannel, internalIndex->GetPropertyByPrimaryId(connection, key.ManifestId, PackageVersionProperty::ArpMinVersion), internalIndex->GetPropertyByPrimaryId(connection, key.ManifestId, PackageVersionProperty::ArpMaxVersion), internalIndex->GetPropertyByPrimaryId(connection, key.ManifestId, PackageVersionProperty::RelativePath), internalIndex->GetPropertyByPrimaryId(connection, key.ManifestId, PackageVersionProperty::ManifestSHA256Hash) }; manifest.AddVersion(std::move(versionData)); } std::string manifestString = manifest.Serialize(); auto compressor = Manifest::PackageVersionDataManifest::CreateCompressor(); std::vector compressedManifest = compressor.Compress(manifestString); Utility::SHA256::HashBuffer manifestHash = Utility::SHA256::ComputeHash(compressedManifest); int64_t currentTime = Utility::GetCurrentUnixEpoch(); // First attempt to update the row and then insert it if no modification occurred. Builder::StatementBuilder updateBuilder; updateBuilder.Update(s_PUTT_Table_Name).Set(). Column(s_PUTT_WriteTime).Equals(currentTime). Column(s_PUTT_Manifest).Equals(compressedManifest). Column(s_PUTT_Hash).Equals(manifestHash). Where(s_PUTT_Package).LikeWithEscape(packageIdentifier); updateBuilder.Execute(connection); if (connection.GetChanges() == 0) { Builder::StatementBuilder insertBuilder; insertBuilder.InsertInto(s_PUTT_Table_Name). Columns({ s_PUTT_Package, s_PUTT_WriteTime, s_PUTT_Manifest, s_PUTT_Hash }). Values(packageIdentifier, currentTime, compressedManifest, manifestHash); insertBuilder.Execute(connection); } } } bool PackageUpdateTrackingTable::CheckConsistency(const SQLite::Connection& connection, ISQLiteIndex* internalIndex, bool log) { bool result = true; // Ensure that all data in the update table matches the internal index for (const PackageData& packageData : GetUpdatesSince(connection, 0)) { auto manifestHash = Utility::SHA256::ComputeHash(packageData.Manifest); if (!Utility::SHA256::AreEqual(packageData.Hash, manifestHash)) { if (!log) { return false; } result = false; AICLI_LOG(Repo, Info, << " [INVALID] value [" << s_PUTT_Hash << "] in table [" << s_PUTT_Table_Name << "] at row [" << packageData.RowID << "]; the hash of the manifest value does not match the hash in the row"); } SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, packageData.PackageIdentifier); if (internalIndex->Search(connection, request).Matches.empty()) { if (!log) { return false; } result = false; AICLI_LOG(Repo, Info, << " [INVALID] value [" << s_PUTT_Package << "] in table [" << s_PUTT_Table_Name << "] at row [" << packageData.RowID << "]; the package [" << packageData.PackageIdentifier << "] was not found in the internal index"); } } // Ensure that all packages in the internal index are present in the update table Builder::StatementBuilder builder; builder.Select(Builder::RowCount).From(s_PUTT_Table_Name).Where(s_PUTT_Package).Like(Builder::Unbound).Escape(EscapeCharForLike); Statement select = builder.Prepare(connection); for (const auto& packageMatch : internalIndex->Search(connection, {}).Matches) { std::vector versionKeys = internalIndex->GetVersionKeysById(connection, packageMatch.first); ISQLiteIndex::VersionKey& latestVersionKey = versionKeys[0]; std::string packageIdentifier = internalIndex->GetPropertyByPrimaryId(connection, latestVersionKey.ManifestId, PackageVersionProperty::Id).value(); select.Reset(); select.Bind(1, packageIdentifier); select.Step(); if (select.GetColumn(0) != 1) { if (!log) { return false; } result = false; AICLI_LOG(Repo, Info, << " [INVALID] value [" << packageIdentifier << "] in the internal index was not found in [" << s_PUTT_Table_Name << "]"); } } return result; } std::vector PackageUpdateTrackingTable::GetUpdatesSince(const SQLite::Connection& connection, int64_t updateBaseTime) { Builder::StatementBuilder builder; builder.Select({ RowIDName, s_PUTT_Package, s_PUTT_WriteTime, s_PUTT_Manifest, s_PUTT_Hash }). From(s_PUTT_Table_Name).Where(s_PUTT_WriteTime).IsGreaterThanOrEqualTo(updateBaseTime); Statement select = builder.Prepare(connection); std::vector result; while (select.Step()) { PackageData item; item.RowID = select.GetColumn(0); item.PackageIdentifier = select.GetColumn(1); item.WriteTime = select.GetColumn(2); item.Manifest = select.GetColumn(3); item.Hash = select.GetColumn(4); result.emplace_back(std::move(item)); } return result; } SQLite::blob_t PackageUpdateTrackingTable::GetDataHash(const SQLite::Connection& connection, const std::string& packageIdentifier) { Builder::StatementBuilder builder; builder.Select(s_PUTT_Hash).From(s_PUTT_Table_Name).Where(s_PUTT_Package).LikeWithEscape(packageIdentifier); Statement select = builder.Prepare(connection); THROW_HR_IF(E_NOT_SET, !select.Step()); return select.GetColumn(0); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/PackageUpdateTrackingTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ISQLiteIndex.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { // Table for tracking the updates to the internal table so that prepare can output // only the necessary package manifests. struct PackageUpdateTrackingTable { // Get the table name. static std::string_view TableName(); // Creates the table. static void Create(SQLite::Connection& connection); // Creates the table if it does not exist. static void EnsureExists(SQLite::Connection& connection); // Drops the table. static void Drop(SQLite::Connection& connection); // Determine if the table currently exists in the database. static bool Exists(const SQLite::Connection& connection); // Updates the tracking table for the given package identifier in the internal index. static void Update(SQLite::Connection& connection, const ISQLiteIndex* internalIndex, const std::string& packageIdentifier, bool ensureTable = true); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. static bool CheckConsistency(const SQLite::Connection& connection, ISQLiteIndex* internalIndex, bool log); // Data on a single row in the table. struct PackageData { SQLite::rowid_t RowID = 0; std::string PackageIdentifier; int64_t WriteTime = 0; SQLite::blob_t Manifest; SQLite::blob_t Hash; }; // Gets the data on updates that have been written since the given base time. static std::vector GetUpdatesSince(const SQLite::Connection& connection, int64_t updateBaseTime); // Gets the data hash for the given package identifier. static SQLite::blob_t GetDataHash(const SQLite::Connection& connection, const std::string& packageIdentifier); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/PackagesTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackagesTable.h" #include #include "OneToManyTableWithMap.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { using namespace std::string_view_literals; static constexpr std::string_view s_PackagesTable_Table_Name = "packages"sv; static constexpr std::string_view s_PackagesTable_Index_Separator = "_"sv; static constexpr std::string_view s_PackagesTable_Index_Suffix = "_index"sv; namespace details { void PackagesTableCreate(SQLite::Connection& connection, std::initializer_list values) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createPackagesTable_v2_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_PackagesTable_Table_Name).BeginColumns(); // Add an integer primary key to keep the manifest rowid consistent createTableBuilder.Column(IntegerPrimaryKey()); for (const ColumnInfo& value : values) { ColumnBuilder columnBuilder(value.Name, value.Type); if (!value.AllowNull) { columnBuilder.NotNull(); } createTableBuilder.Column(columnBuilder); } createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); // Create a unique index with the primary key values StatementBuilder pkIndexBuilder; pkIndexBuilder.CreateUniqueIndex({ s_PackagesTable_Table_Name, s_PackagesTable_Index_Suffix }).On(s_PackagesTable_Table_Name).BeginColumns(); for (const ColumnInfo& value : values) { if (value.PrimaryKey) { pkIndexBuilder.Column(value.Name); } } pkIndexBuilder.EndColumns(); pkIndexBuilder.Execute(connection); // Create an index on every value to improve performance for (const ColumnInfo& value : values) { StatementBuilder createIndexBuilder; createIndexBuilder.CreateIndex({ s_PackagesTable_Table_Name, s_PackagesTable_Index_Separator, value.Name, s_PackagesTable_Index_Suffix }); createIndexBuilder.On(s_PackagesTable_Table_Name).Columns(value.Name); createIndexBuilder.Execute(connection); } savepoint.Commit(); } // Creates a statement and executes it, select the actual values for a given manifest id. // Ex. // SELECT [ids].[id] FROM [manifest] // JOIN [ids] ON [manifest].[id] = [ids].[rowid] // WHERE [manifest].[rowid] = 1 SQLite::Statement PackagesTableGetValuesById_Statement( const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list columns, bool stepAndVerify) { SQLite::Builder::StatementBuilder builder; builder.Select(columns).From(s_PackagesTable_Table_Name).Where(SQLite::RowIDName).Equals(id); SQLite::Statement result = builder.Prepare(connection); if (stepAndVerify) { THROW_HR_IF(E_NOT_SET, !result.Step()); } return result; } SQLite::Statement PackagesTableUpdateValueIdById_Statement(SQLite::Connection& connection, std::string_view valueName) { SQLite::Builder::StatementBuilder builder; builder.Update(s_PackagesTable_Table_Name).Set().Column(valueName).Equals(SQLite::Builder::Unbound).Where(SQLite::RowIDName).Equals(SQLite::Builder::Unbound); return builder.Prepare(connection); } void PackagesTablePrepareForPackaging(SQLite::Connection& connection, std::initializer_list values) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "pfpPackagesTable_v2_0"); // Drop the index on the requested values for (const auto& value : values) { SQLite::Builder::StatementBuilder dropIndexBuilder; dropIndexBuilder.DropIndex({ s_PackagesTable_Table_Name, s_PackagesTable_Index_Separator, value.Name, s_PackagesTable_Index_Suffix }); dropIndexBuilder.Execute(connection); } SQLite::Builder::StatementBuilder dropPKIndexBuilder; dropPKIndexBuilder.DropIndex({ s_PackagesTable_Table_Name, s_PackagesTable_Index_Suffix }); dropPKIndexBuilder.Execute(connection); savepoint.Commit(); } bool PackagesTableCheckColumnForNulls(const SQLite::Connection& connection, std::string_view valueName, bool log) { // Build a select statement to find values that contain an embedded null character // Such as: // Select count(*) from table where instr(value,char(0))>0 SQLite::Builder::StatementBuilder builder; builder. Select({ SQLite::RowIDName, valueName }). From(s_PackagesTable_Table_Name). WhereValueContainsEmbeddedNullCharacter(valueName); SQLite::Statement select = builder.Prepare(connection); bool result = true; while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] value [" << valueName << "] in table [" << s_PackagesTable_Table_Name << "] at row [" << select.GetColumn(0) << "] contains an embedded null character and starts with [" << select.GetColumn(1) << "]"); } return result; } bool PackagesTableCheckConsistency(const SQLite::Connection& connection, std::initializer_list values, bool log) { bool result = true; for (const auto& value : values) { if (result || log) { result = details::PackagesTableCheckColumnForNulls(connection, value, log) && result; } } return result; } } std::string_view PackagesTable::TableName() { return s_PackagesTable_Table_Name; } void PackagesTable::Drop(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable(s_PackagesTable_Table_Name); dropTableBuilder.Execute(connection); } bool PackagesTable::Exists(const SQLite::Connection& connection) { using namespace SQLite; Builder::StatementBuilder builder; builder.Select(Builder::RowCount).From(Builder::Schema::MainTable). Where(Builder::Schema::TypeColumn).Equals(Builder::Schema::Type_Table).And(Builder::Schema::NameColumn).Equals(s_PackagesTable_Table_Name); Statement statement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !statement.Step()); return statement.GetColumn(0) != 0; } void PackagesTable::AddColumn(SQLite::Connection& connection, const ColumnInfo& value) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addColumnPackagesTable_v2_0"); StatementBuilder alterTableBuilder; alterTableBuilder.AlterTable(s_PackagesTable_Table_Name).Add(value.Name, value.Type); alterTableBuilder.Execute(connection); savepoint.Commit(); } SQLite::rowid_t PackagesTable::Insert(SQLite::Connection& connection, const std::vector& values) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_PackagesTable_Table_Name).BeginColumns(); for (const NameValuePair& value : values) { builder.Column(value.Name); } builder.EndColumns().BeginValues(); for (const NameValuePair& value : values) { builder.Value(value.Value); } builder.EndValues(); builder.Execute(connection); return connection.GetLastInsertRowID(); } bool PackagesTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_PackagesTable_Table_Name).Where(SQLite::RowIDName).Equals(id); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) != 0); } std::vector PackagesTable::GetAllRowIds(const SQLite::Connection& connection, std::string_view orderByColumn, size_t limit) { SQLite::Builder::StatementBuilder selectBuilder; selectBuilder.Select(SQLite::RowIDName).From(s_PackagesTable_Table_Name).OrderBy(orderByColumn); if (limit) { selectBuilder.Limit(limit); } SQLite::Statement select = selectBuilder.Prepare(connection); std::vector result; while (select.Step()) { result.emplace_back(select.GetColumn(0)); } return result; } uint64_t PackagesTable::GetCount(const SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_PackagesTable_Table_Name); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return static_cast(countStatement.GetColumn(0)); } int PackagesTable::BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, std::string_view valueName, std::string_view primaryAlias, std::string_view valueAlias, bool useLike) { using QCol = SQLite::Builder::QualifiedColumn; // Build a statement like: // SELECT packages.rowid as p, packages.id as v from packages // where packages.id = builder.Select(). Column(QCol(s_PackagesTable_Table_Name, SQLite::RowIDName)).As(primaryAlias). Column(QCol(s_PackagesTable_Table_Name, valueName)).As(valueAlias). From(s_PackagesTable_Table_Name).Where(QCol(s_PackagesTable_Table_Name, valueName)); int result = -1; if (useLike) { builder.Like(SQLite::Builder::Unbound); result = builder.GetLastBindIndex(); builder.Escape(SQLite::EscapeCharForLike); } else { builder.Equals(SQLite::Builder::Unbound); result = builder.GetLastBindIndex(); } return result; } bool PackagesTable::IsEmpty(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_PackagesTable_Table_Name); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) == 0); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/PackagesTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { // Info on the columns. struct ColumnInfo { template static constexpr ColumnInfo Create() { ColumnInfo result; result.Name = Column::Name; result.Type = Column::Type; result.PrimaryKey = Column::PrimaryKey; result.AllowNull = Column::AllowNull; return result; } std::string_view Name; SQLite::Builder::Type Type = {}; bool PrimaryKey = false; bool AllowNull = false; }; // Creates the table. void PackagesTableCreate(SQLite::Connection& connection, std::initializer_list values); // Gets the requested values for the manifest with the given rowid. SQLite::Statement PackagesTableGetValuesById_Statement( const SQLite::Connection& connection, SQLite::rowid_t id, std::initializer_list columns, bool stepAndVerify = true); // Prepares a statement to update the value of a single column for the manifest with the given rowid. // The first bind value will be the value to set. // The second bind value will be the manifest rowid to modify. SQLite::Statement PackagesTableUpdateValueIdById_Statement(SQLite::Connection& connection, std::string_view valueName); // Removes data that is no longer needed for an index that is to be published. void PackagesTablePrepareForPackaging(SQLite::Connection& connection, std::initializer_list values); // Checks for embedded nulls in the database. bool PackagesTableCheckConsistency(const SQLite::Connection& connection, std::initializer_list values, bool log); } // A table in which each row represents a single package. struct PackagesTable { // Get the table name. static std::string_view TableName(); struct IdColumn { static constexpr std::string_view Name = "id"sv; static constexpr SQLite::Builder::Type Type = SQLite::Builder::Type::Text; static constexpr bool PrimaryKey = true; static constexpr bool AllowNull = false; }; struct NameColumn { static constexpr std::string_view Name = "name"sv; static constexpr SQLite::Builder::Type Type = SQLite::Builder::Type::Text; static constexpr bool PrimaryKey = false; static constexpr bool AllowNull = false; }; struct MonikerColumn { static constexpr std::string_view Name = "moniker"sv; static constexpr SQLite::Builder::Type Type = SQLite::Builder::Type::Text; static constexpr bool PrimaryKey = false; static constexpr bool AllowNull = true; }; struct LatestVersionColumn { static constexpr std::string_view Name = "latest_version"sv; static constexpr SQLite::Builder::Type Type = SQLite::Builder::Type::Text; static constexpr bool PrimaryKey = false; static constexpr bool AllowNull = false; }; struct ARPMinVersionColumn { static constexpr std::string_view Name = "arp_min_version"sv; static constexpr SQLite::Builder::Type Type = SQLite::Builder::Type::Text; static constexpr bool PrimaryKey = false; static constexpr bool AllowNull = true; }; struct ARPMaxVersionColumn { static constexpr std::string_view Name = "arp_max_version"sv; static constexpr SQLite::Builder::Type Type = SQLite::Builder::Type::Text; static constexpr bool PrimaryKey = false; static constexpr bool AllowNull = true; }; struct HashColumn { static constexpr std::string_view Name = "hash"sv; static constexpr SQLite::Builder::Type Type = SQLite::Builder::Type::Blob; static constexpr bool PrimaryKey = false; static constexpr bool AllowNull = true; }; using ColumnInfo = details::ColumnInfo; // Creates the table. template static void Create(SQLite::Connection& connection) { details::PackagesTableCreate(connection, { ColumnInfo::Create()... }); } // Drops the table. static void Drop(SQLite::Connection& connection); // Determine if the table currently exists in the database. static bool Exists(const SQLite::Connection& connection); // Alters the table, adding the column provided. static void AddColumn(SQLite::Connection& connection, const ColumnInfo& value); // A string value for the package. struct NameValuePair { std::string_view Name; std::string Value; }; // Insert the given values into the table. static SQLite::rowid_t Insert(SQLite::Connection& connection, const std::vector& values); // Gets a value indicating whether the package with rowid exists. static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t rowid); // Gets all row ids from the table. static std::vector GetAllRowIds(const SQLite::Connection& connection, std::string_view orderByColumn, size_t limit = 0); // Gets the total number of rows in the table. static uint64_t GetCount(const SQLite::Connection& connection); // Gets the values requested for the package with the given rowid. template static auto GetValuesById(const SQLite::Connection& connection, SQLite::rowid_t rowid) { return details::PackagesTableGetValuesById_Statement(connection, rowid, { Columns::Name... }).GetRow::value_t...>(); } // Gets the value requested for the package with the given rowid, if it exists. template static std::optional::value_t> GetValueById(const SQLite::Connection& connection, SQLite::rowid_t rowid) { auto statement = details::PackagesTableGetValuesById_Statement(connection, rowid, { Column::Name }, false); if (statement.Step()) { return statement.GetColumn::value_t>(0); } else { return std::nullopt; } } // Builds the search select statement base on the given value. // The return value is the bind index of the value to match against. static int BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, std::string_view valueName, std::string_view primaryAlias, std::string_view valueAlias, bool useLike); // Update the value of a single column for the package with the given rowid. template static void UpdateValueIdById(SQLite::Connection& connection, SQLite::rowid_t id, const typename SQLite::Builder::TypeInfo::value_t& value) { auto stmt = details::PackagesTableUpdateValueIdById_Statement(connection, Column::Name); stmt.Bind(1, value); stmt.Bind(2, id); stmt.Execute(); } // Removes data that is no longer needed for an index that is to be published. template static void PrepareForPackaging(SQLite::Connection& connection) { details::PackagesTablePrepareForPackaging(connection, { ColumnInfo::Create()... }); } // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. template static bool CheckConsistency(const SQLite::Connection& connection, bool log) { return details::PackagesTableCheckConsistency(connection, { Columns::Name... }, log); } // Determines if the table is empty. static bool IsEmpty(SQLite::Connection& connection); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/ProductCodeTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/2_0/SystemReferenceStringTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using namespace std::string_view_literals; struct ProductCodeTableInfo { inline static constexpr std::string_view TableName() { return "productcodes2"sv; } inline static constexpr std::string_view ValueName() { return "productcode"sv; } }; } using ProductCodeTable = SystemReferenceStringTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/SearchResultsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "Microsoft/Schema/ISQLiteIndex.h" #include "Public/winget/RepositorySearch.h" #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { // Table for holding temporary search results. struct SearchResultsTable : public SQLite::TempTable { SearchResultsTable(const SQLite::Connection& connection); SearchResultsTable(const SearchResultsTable&) = delete; SearchResultsTable& operator=(const SearchResultsTable&) = delete; SearchResultsTable(SearchResultsTable&&) = default; SearchResultsTable& operator=(SearchResultsTable&&) = default; // Performs the requested search type on the requested field. void SearchOnField(const PackageMatchFilter& filter); // Removes rows with package ids whose sort order is below the highest one. void RemoveDuplicatePackageRows(); // Prepares the table for a filtering pass. void PrepareToFilter(); // Performs the requested filter type on the requested field. void FilterOnField(const PackageMatchFilter& filter); // Completes a filtering pass, removing filtered rows. void CompleteFilter(); // Gets the results from the table. ISQLiteIndex::SearchResult GetSearchResults(size_t limit = 0); protected: // Builds the search statement for the specified field and match type. std::vector BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, PackageMatchField field, MatchType match) const; virtual std::vector BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const; static bool MatchUsesLike(MatchType match); void BindStatementForMatchType(SQLite::Statement& statement, MatchType match, int bindIndex, std::string_view value); virtual void BindStatementForMatchType(SQLite::Statement& statement, const PackageMatchFilter& filter, const std::vector& bindIndex); private: const SQLite::Connection& m_connection; int m_sortOrdinalValue = 0; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/SearchResultsTable_2_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchResultsTable.h" #include #include "Microsoft/Schema/2_0/PackagesTable.h" #include "Microsoft/Schema/2_0/TagsTable.h" #include "Microsoft/Schema/2_0/CommandsTable.h" #include "Microsoft/Schema/2_0/PackageFamilyNameTable.h" #include "Microsoft/Schema/2_0/ProductCodeTable.h" #include "Microsoft/Schema/2_0/UpgradeCodeTable.h" #include "Microsoft/Schema/2_0/NormalizedPackageNameTable.h" #include "Microsoft/Schema/2_0/NormalizedPackagePublisherTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace { using namespace std::string_literals; using namespace std::string_view_literals; constexpr std::string_view s_SearchResultsTable_Package = "package"sv; constexpr std::string_view s_SearchResultsTable_MatchField = "field"sv; constexpr std::string_view s_SearchResultsTable_MatchType = "match"sv; constexpr std::string_view s_SearchResultsTable_MatchValue = "value"sv; constexpr std::string_view s_SearchResultsTable_SortValue = "sort"sv; constexpr std::string_view s_SearchResultsTable_Filter = "filter"sv; constexpr std::string_view s_SearchResultsTable_Index_Suffix = "_i_m"sv; constexpr std::string_view s_SearchResultsTable_SubSelect_TableAlias = "valueTable"sv; constexpr std::string_view s_SearchResultsTable_SubSelect_PackageAlias = "p"sv; constexpr std::string_view s_SearchResultsTable_SubSelect_ValueAlias = "v"sv; } SearchResultsTable::SearchResultsTable(const SQLite::Connection& connection) : m_connection(connection) { using namespace SQLite::Builder; { StatementBuilder builder; builder.CreateTable(GetQualifiedName()).BeginColumns(); builder.Column(ColumnBuilder(s_SearchResultsTable_Package, Type::RowId).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_MatchField, Type::Int).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_MatchType, Type::Int).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_MatchValue, Type::Text).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_SortValue, Type::Int).NotNull()); builder.Column(ColumnBuilder(s_SearchResultsTable_Filter, Type::Bool).NotNull()); builder.EndColumns(); builder.Execute(m_connection); } InitDropStatement(m_connection); { SQLite::Builder::QualifiedTable index = GetQualifiedName(); std::string indexName(index.Table); indexName += s_SearchResultsTable_Index_Suffix; index.Table = indexName; StatementBuilder builder; builder.CreateIndex(indexName).On(GetQualifiedName().Table).Columns(s_SearchResultsTable_Package); builder.Execute(m_connection); } } void SearchResultsTable::SearchOnField(const PackageMatchFilter& filter) { using namespace SQLite::Builder; int sortOrdinal = m_sortOrdinalValue++; // Create an insert statement to select values into the table as requested. // The goal is a statement like this: // INSERT INTO // SELECT valueTable.p, , , valueTable.v, , FROM // (SELECT packages.rowid as p, packages.id as v from packages where packages.id = ) AS valueTable // Where the subselect is built by the owning table. StatementBuilder builder; builder.InsertInto(GetQualifiedName()).Select(). Column(QualifiedColumn(s_SearchResultsTable_SubSelect_TableAlias, s_SearchResultsTable_SubSelect_PackageAlias)). Value(filter.Field). Value(filter.Type). Column(QualifiedColumn(s_SearchResultsTable_SubSelect_TableAlias, s_SearchResultsTable_SubSelect_ValueAlias)). Value(sortOrdinal). Value(false). From().BeginParenthetical(); // Add the field specific portion std::vector bindIndex = BuildSearchStatement(builder, filter.Field, filter.Type); if (bindIndex.empty()) { AICLI_LOG(Repo, Verbose, << "PackageMatchField not supported in this version: " << ToString(filter.Field)); return; } builder.EndParenthetical().As(s_SearchResultsTable_SubSelect_TableAlias); SQLite::Statement statement = builder.Prepare(m_connection); BindStatementForMatchType(statement, filter, bindIndex); statement.Execute(); AICLI_LOG(SQL, Verbose, << "Search found " << m_connection.GetChanges() << " rows"); } void SearchResultsTable::RemoveDuplicatePackageRows() { using namespace SQLite::Builder; // Create a delete statement to leave only one row with a given package. // This will arbitrarily choose one of the rows if multiple have the same lowest sort order. // The goal is a statement like this: // DELETE from where rowid not in ( // SELECT rowid from ( // SELECT rowid, min(sort) from group by package // ) // ) StatementBuilder builder; builder.DeleteFrom(GetQualifiedName()).Where(SQLite::RowIDName).Not().In().BeginParenthetical(). Select(SQLite::RowIDName).From().BeginParenthetical(). Select().Column(SQLite::RowIDName).Column(Aggregate::Min, s_SearchResultsTable_SortValue).From(GetQualifiedName()).GroupBy(s_SearchResultsTable_Package). EndParenthetical(). EndParenthetical(); builder.Execute(m_connection); AICLI_LOG(SQL, Verbose, << "Removed " << m_connection.GetChanges() << " duplicate rows"); } void SearchResultsTable::PrepareToFilter() { // Reset all filter values to unselected SQLite::Builder::StatementBuilder builder; builder.Update(GetQualifiedName()).Set().Column(s_SearchResultsTable_Filter).Equals(false); builder.Execute(m_connection); } void SearchResultsTable::FilterOnField(const PackageMatchFilter& filter) { using namespace SQLite::Builder; // Create an update statement to mark rows that are found by the search. // This will arbitrarily choose one of the rows if multiple have the same lowest sort order. // The goal is a statement like this: // UPDATE set filter = 1 where package in ( // SELECT p from ( // SELECT packages.rowid as p, packages.id as v from packages where packages.id = // ) // ) StatementBuilder builder; builder.Update(GetQualifiedName()).Set().Column(s_SearchResultsTable_Filter).Equals(true).Where(s_SearchResultsTable_Package).In().BeginParenthetical(). Select(s_SearchResultsTable_SubSelect_PackageAlias).From().BeginParenthetical(); // Add the field specific portion std::vector bindIndex = BuildSearchStatement(builder, filter.Field, filter.Type); if (bindIndex.empty()) { AICLI_LOG(Repo, Verbose, << "PackageMatchField not supported in this version: " << ToString(filter.Field)); return; } builder.EndParenthetical().EndParenthetical(); SQLite::Statement statement = builder.Prepare(m_connection); BindStatementForMatchType(statement, filter, bindIndex); statement.Execute(); AICLI_LOG(SQL, Verbose, << "Filter kept " << m_connection.GetChanges() << " rows"); } void SearchResultsTable::CompleteFilter() { // Delete all unselected values SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(GetQualifiedName()).Where(s_SearchResultsTable_Filter).Equals(false); builder.Execute(m_connection); AICLI_LOG(SQL, Verbose, << "Filter deleted " << m_connection.GetChanges() << " rows"); } ISQLiteIndex::SearchResult SearchResultsTable::GetSearchResults(size_t limit) { using namespace SQLite::Builder; using QCol = QualifiedColumn; // Select all of the results from the table; it is expected that RemoveDuplicatePackageRows has been called. StatementBuilder builder; builder.Select({ s_SearchResultsTable_Package, s_SearchResultsTable_MatchField, s_SearchResultsTable_MatchType, s_SearchResultsTable_MatchValue, }). From(GetQualifiedName()).OrderBy(s_SearchResultsTable_SortValue); SQLite::Statement select = builder.Prepare(m_connection); ISQLiteIndex::SearchResult result; while (select.Step()) { if (limit && result.Matches.size() >= limit) { break; } result.Matches.emplace_back(select.GetColumn(0), PackageMatchFilter(select.GetColumn(1), select.GetColumn(2), select.GetColumn(3))); } result.Truncated = (select.GetState() != SQLite::Statement::State::Completed); return result; } std::vector SearchResultsTable::BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, PackageMatchField field, MatchType match) const { return BuildSearchStatement(builder, field, s_SearchResultsTable_SubSelect_PackageAlias, s_SearchResultsTable_SubSelect_ValueAlias, MatchUsesLike(match)); } std::vector SearchResultsTable::BuildSearchStatement( SQLite::Builder::StatementBuilder& builder, PackageMatchField field, std::string_view manifestAlias, std::string_view valueAlias, bool useLike) const { std::vector result; switch (field) { case PackageMatchField::Id: result.push_back(PackagesTable::BuildSearchStatement(builder, PackagesTable::IdColumn::Name, manifestAlias, valueAlias, useLike)); break; case PackageMatchField::Name: result.push_back(PackagesTable::BuildSearchStatement(builder, PackagesTable::NameColumn::Name, manifestAlias, valueAlias, useLike)); break; case PackageMatchField::Moniker: result.push_back(PackagesTable::BuildSearchStatement(builder, PackagesTable::MonikerColumn::Name, manifestAlias, valueAlias, useLike)); break; case PackageMatchField::Tag: result.push_back(TagsTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike)); break; case PackageMatchField::Command: result.push_back(CommandsTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike)); break; case PackageMatchField::PackageFamilyName: result.push_back(PackageFamilyNameTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike)); break; case PackageMatchField::ProductCode: result.push_back(ProductCodeTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike)); break; case PackageMatchField::UpgradeCode: result.push_back(UpgradeCodeTable::BuildSearchStatement(builder, manifestAlias, valueAlias, useLike)); break; case PackageMatchField::NormalizedNameAndPublisher: result = NormalizedPackageNameTable::BuildPairedSearchStatement(builder, manifestAlias, valueAlias, useLike); break; } return result; } bool SearchResultsTable::MatchUsesLike(MatchType match) { return (match != MatchType::Exact); } void SearchResultsTable::BindStatementForMatchType(SQLite::Statement& statement, MatchType match, int bindIndex, std::string_view value) { std::string valueToUse; if (MatchUsesLike(match)) { valueToUse = SQLite::EscapeStringForLike(value); } else { valueToUse = value; } switch (match) { case AppInstaller::Repository::MatchType::StartsWith: valueToUse += '%'; break; case AppInstaller::Repository::MatchType::Substring: valueToUse = "%"s + valueToUse + '%'; break; default: // No changes required for others. break; } statement.Bind(bindIndex, valueToUse); } void SearchResultsTable::BindStatementForMatchType(SQLite::Statement& statement, const PackageMatchFilter& filter, const std::vector& bindIndex) { // TODO: Implement these more complex match types if (filter.Type == MatchType::Wildcard || filter.Type == MatchType::Fuzzy || filter.Type == MatchType::FuzzySubstring) { AICLI_LOG(Repo, Verbose, << "Specific match type not implemented, skipping: " << ToString(filter.Type)); return; } BindStatementForMatchType(statement, filter.Type, bindIndex[0], filter.Value); if (filter.Field == PackageMatchField::NormalizedNameAndPublisher) { BindStatementForMatchType(statement, filter.Type, bindIndex[1], filter.Additional.value()); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/SystemReferenceStringTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/2_0/SystemReferenceStringTable.h" #include "Microsoft/Schema/2_0/PackagesTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using PrimaryTable = PackagesTable; using namespace std::string_view_literals; static constexpr std::string_view s_SystemReferenceStringTable_PrimaryName = "package"sv; std::string_view SystemReferenceStringTableGetPrimaryColumnName() { return s_SystemReferenceStringTable_PrimaryName; } void SystemReferenceStringTableCreate(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_create_v2_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(tableName).Columns({ ColumnBuilder(valueName, Type::Text).NotNull(), ColumnBuilder(s_SystemReferenceStringTable_PrimaryName, Type::RowId).NotNull(), PrimaryKeyBuilder({ valueName, s_SystemReferenceStringTable_PrimaryName }) }).WithoutRowID(); createTableBuilder.Execute(connection); savepoint.Commit(); } void SystemReferenceStringTableDrop(SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder dropTableBuilder; dropTableBuilder.DropTable(tableName); dropTableBuilder.Execute(connection); } std::vector SystemReferenceStringTableGetValuesByPrimaryId( const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t primaryId) { std::vector result; SQLite::Builder::StatementBuilder builder; builder.Select(valueName). From(tableName).Where(s_SystemReferenceStringTable_PrimaryName).Equals(primaryId); SQLite::Statement statement = builder.Prepare(connection); while (statement.Step()) { result.emplace_back(statement.GetColumn(0)); } return result; } void SystemReferenceStringTableEnsureExists( SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, const std::vector& values, SQLite::rowid_t primaryId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_ensure_v2_0"); SQLite::Builder::StatementBuilder builder; builder.InsertOrIgnore(tableName). Columns({ valueName, s_SystemReferenceStringTable_PrimaryName }).Values(SQLite::Builder::Unbound, primaryId); SQLite::Statement insertStatement = builder.Prepare(connection); for (const std::string& value : values) { // Second, insert into the mapping table insertStatement.Reset(); insertStatement.Bind(1, value); insertStatement.Execute(); } savepoint.Commit(); } bool SystemReferenceStringTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log) { using QCol = SQLite::Builder::QualifiedColumn; bool result = true; { // Build a select statement to find rows containing references to primaries with nonexistent rowids // Such as: // Select data.data, data.primary from data left outer join primary on data.primary = primary.rowid where primary.id is null SQLite::Builder::StatementBuilder builder; builder. Select({ QCol(tableName, valueName), QCol(tableName, s_SystemReferenceStringTable_PrimaryName) }). From(tableName). LeftOuterJoin(details::PrimaryTable::TableName()).On(QCol(tableName, s_SystemReferenceStringTable_PrimaryName), QCol(details::PrimaryTable::TableName(), SQLite::RowIDName)). Where(QCol(details::PrimaryTable::TableName(), SQLite::RowIDName)).IsNull(); SQLite::Statement select = builder.Prepare(connection); while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] " << tableName << " [" << select.GetColumn(0) << ", " << select.GetColumn(1) << "] refers to invalid " << details::PrimaryTable::TableName()); } } if (!result && !log) { return result; } // Build a select statement to find values that contain an embedded null character // Such as: // Select count(*) from table where instr(value,char(0))>0 SQLite::Builder::StatementBuilder builder; builder. Select({ valueName, s_SystemReferenceStringTable_PrimaryName }). From(tableName). WhereValueContainsEmbeddedNullCharacter(valueName); SQLite::Statement select = builder.Prepare(connection); while (select.Step()) { result = false; if (!log) { break; } AICLI_LOG(Repo, Info, << " [INVALID] value in table [" << tableName << "] for primary [" << select.GetColumn(1) << "] contains an embedded null character and starts with [" << select.GetColumn(0) << "]"); } return result; } bool SystemReferenceStringTableIsEmpty(SQLite::Connection& connection, std::string_view tableName) { SQLite::Builder::StatementBuilder countBuilder; countBuilder.Select(SQLite::Builder::RowCount).From(tableName); SQLite::Statement countStatement = countBuilder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return countStatement.GetColumn(0) == 0; } int SystemReferenceStringTableBuildSearchStatement( SQLite::Builder::StatementBuilder& builder, std::string_view tableName, std::string_view valueName, std::string_view primaryAlias, std::string_view valueAlias, bool useLike) { using QCol = SQLite::Builder::QualifiedColumn; // Build a statement like: // SELECT table.package as p, table.value as v from table // where table.value = builder.Select(). Column(s_SystemReferenceStringTable_PrimaryName).As(primaryAlias). Column(valueName).As(valueAlias). From(tableName). Where(valueName); int result = -1; if (useLike) { builder.Like(SQLite::Builder::Unbound); result = builder.GetLastBindIndex(); builder.Escape(SQLite::EscapeCharForLike); } else { builder.Equals(SQLite::Builder::Unbound); result = builder.GetLastBindIndex(); } return result; } std::vector SystemReferenceStringTableBuildPairedSearchStatement( SQLite::Builder::StatementBuilder& builder, std::string_view tableName, std::string_view valueName, std::string_view pairedTableName, std::string_view pairedValueName, std::string_view primaryAlias, std::string_view valueAlias, bool useLike) { using QCol = SQLite::Builder::QualifiedColumn; // Build a statement like: // SELECT table.package as p, '' as v from table // join paired on table.package = paired.package // where table.value = and paired.pairedValue = builder.Select(). Column(QCol(tableName, s_SystemReferenceStringTable_PrimaryName)).As(primaryAlias). Value(std::string_view{}).As(valueAlias). From(tableName). Join(pairedTableName).On(QCol(tableName, s_SystemReferenceStringTable_PrimaryName), QCol(pairedTableName, s_SystemReferenceStringTable_PrimaryName)). Where(QCol(tableName, valueName)); std::vector result; if (useLike) { builder.Like(SQLite::Builder::Unbound); result.push_back(builder.GetLastBindIndex()); builder.Escape(SQLite::EscapeCharForLike); } else { builder.Equals(SQLite::Builder::Unbound); result.push_back(builder.GetLastBindIndex()); } builder.And(QCol(pairedTableName, pairedValueName)); if (useLike) { builder.Like(SQLite::Builder::Unbound); result.push_back(builder.GetLastBindIndex()); builder.Escape(SQLite::EscapeCharForLike); } else { builder.Equals(SQLite::Builder::Unbound); result.push_back(builder.GetLastBindIndex()); } return result; } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/SystemReferenceStringTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { // Returns the primary column name. std::string_view SystemReferenceStringTableGetPrimaryColumnName(); // Create the table. void SystemReferenceStringTableCreate(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName); // Drops the table. void SystemReferenceStringTableDrop(SQLite::Connection& connection, std::string_view tableName); // Gets all values associated with the given primary id. std::vector SystemReferenceStringTableGetValuesByPrimaryId( const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t primaryId); // Ensures that the value exists and inserts mapping entries. void SystemReferenceStringTableEnsureExists( SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, const std::vector& values, SQLite::rowid_t primaryId); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. bool SystemReferenceStringTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log); // Determines if the table is empty. bool SystemReferenceStringTableIsEmpty(SQLite::Connection& connection, std::string_view tableName); // Builds the search select statement base on the given value. // The return value is the bind index of the value to match against. int SystemReferenceStringTableBuildSearchStatement( SQLite::Builder::StatementBuilder& builder, std::string_view tableName, std::string_view valueName, std::string_view primaryAlias, std::string_view valueAlias, bool useLike); // Builds the search select statement base on the given value. // The return value is the bind index of the value to match against. std::vector SystemReferenceStringTableBuildPairedSearchStatement( SQLite::Builder::StatementBuilder& builder, std::string_view tableName, std::string_view valueName, std::string_view pairedTableName, std::string_view pairedValueName, std::string_view primaryAlias, std::string_view valueAlias, bool useLike); } // A table that represents a value that is 1:N with a primary entry. template struct SystemReferenceStringTable { // The name of the table. static constexpr std::string_view TableName() { return TableInfo::TableName(); } // The value name of the table. static constexpr std::string_view ValueName() { return TableInfo::ValueName(); } // Creates the table. static void Create(SQLite::Connection& connection) { details::SystemReferenceStringTableCreate(connection, TableInfo::TableName(), TableInfo::ValueName()); } // Drops the table. static void Drop(SQLite::Connection& connection) { details::SystemReferenceStringTableDrop(connection, TableInfo::TableName()); } // Gets all values associated with the given primary id. static std::vector GetValuesByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId) { return details::SystemReferenceStringTableGetValuesByPrimaryId(connection, TableInfo::TableName(), TableInfo::ValueName(), primaryId); } // Ensures that all values exist in the data table, and inserts into the mapping table for the given primary id. static void EnsureExists(SQLite::Connection& connection, const std::vector& values, SQLite::rowid_t primaryId) { details::SystemReferenceStringTableEnsureExists(connection, TableInfo::TableName(), TableInfo::ValueName(), values, primaryId); } // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. static bool CheckConsistency(const SQLite::Connection& connection, bool log) { return details::SystemReferenceStringTableCheckConsistency(connection, TableInfo::TableName(), TableInfo::ValueName(), log); } // Determines if the table is empty. static bool IsEmpty(SQLite::Connection& connection) { return details::SystemReferenceStringTableIsEmpty(connection, TableInfo::TableName()); } // Builds the search select statement base on the given value. // The return value is the bind index of the value to match against. static int BuildSearchStatement(SQLite::Builder::StatementBuilder& builder, std::string_view primaryAlias, std::string_view valueAlias, bool useLike) { return details::SystemReferenceStringTableBuildSearchStatement(builder, TableInfo::TableName(), TableInfo::ValueName(), primaryAlias, valueAlias, useLike); } // Builds the search select statement base on the given value. // The return value is the bind index of the value to match against. template static std::vector BuildPairedSearchStatement(SQLite::Builder::StatementBuilder& builder, std::string_view primaryAlias, std::string_view valueAlias, bool useLike) { return details::SystemReferenceStringTableBuildPairedSearchStatement(builder, TableInfo::TableName(), TableInfo::ValueName(), PairedTable::TableName(), PairedTable::ValueName(), primaryAlias, valueAlias, useLike); } }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/TagsTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/2_0/OneToManyTableWithMap.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using namespace std::string_view_literals; struct TagsTableInfo { inline static constexpr std::string_view TableName() { return "tags2"sv; } inline static constexpr std::string_view ValueName() { return "tag"sv; } }; } using TagsTable = OneToManyTableWithMap; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/2_0/UpgradeCodeTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/2_0/SystemReferenceStringTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V2_0 { namespace details { using namespace std::string_view_literals; struct UpgradeCodeTableInfo { inline static constexpr std::string_view TableName() { return "upgradecodes2"sv; } inline static constexpr std::string_view ValueName() { return "upgradecode"sv; } }; } using UpgradeCodeTable = SystemReferenceStringTable; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "CheckpointDataTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace SQLite; using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointDataTable_Table_Name = "CheckpointData"sv; static constexpr std::string_view s_CheckpointDataTable_CheckpointId_Column = "CheckpointId"sv; static constexpr std::string_view s_CheckpointDataTable_ContextData_Column = "ContextData"sv; static constexpr std::string_view s_CheckpointDataTable_Name_Column = "Name"sv; static constexpr std::string_view s_CheckpointDataTable_Value_Column = "Value"sv; static constexpr std::string_view s_CheckpointDataTable_Index_Column = "Index"sv; namespace { SQLite::rowid_t SetNamedValue(SQLite::Connection& connection, std::string_view name, std::string_view value) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_CheckpointDataTable_Table_Name) .Columns({ s_CheckpointDataTable_CheckpointId_Column, s_CheckpointDataTable_ContextData_Column, s_CheckpointDataTable_Name_Column, s_CheckpointDataTable_Value_Column, s_CheckpointDataTable_Index_Column}) .Values(name, value); builder.Execute(connection); return connection.GetLastInsertRowID(); } std::string GetNamedValue(SQLite::Connection& connection, std::string_view name) { SQLite::Builder::StatementBuilder builder; builder.Select({ s_CheckpointDataTable_Value_Column }) .From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_Name_Column).Equals(name); SQLite::Statement statement = builder.Prepare(connection); THROW_HR_IF(E_NOT_SET, !statement.Step()); return statement.GetColumn(0); } } std::string_view CheckpointDataTable::TableName() { return s_CheckpointDataTable_Table_Name; } void CheckpointDataTable::Create(SQLite::Connection& connection) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointDataTable_v1_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_CheckpointDataTable_Table_Name).BeginColumns(); createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_CheckpointId_Column, Type::Int).NotNull()); createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_ContextData_Column, Type::Int).NotNull()); createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Name_Column, Type::Text).NotNull()); createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Value_Column, Type::Text)); createTableBuilder.Column(ColumnBuilder(s_CheckpointDataTable_Index_Column, Type::Int).NotNull()); PrimaryKeyBuilder pkBuilder; pkBuilder.Column(s_CheckpointDataTable_CheckpointId_Column); pkBuilder.Column(s_CheckpointDataTable_ContextData_Column); pkBuilder.Column(s_CheckpointDataTable_Name_Column); pkBuilder.Column(s_CheckpointDataTable_Index_Column); createTableBuilder.Column(pkBuilder).EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); } bool CheckpointDataTable::IsEmpty(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_CheckpointDataTable_Table_Name); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) == 0); } std::vector CheckpointDataTable::GetAvailableData(SQLite::Connection& connection, SQLite::rowid_t checkpointId) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointDataTable_ContextData_Column).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); builder.Equals(checkpointId); SQLite::Statement select = builder.Prepare(connection); std::vector availableData; while (select.Step()) { availableData.emplace_back(select.GetColumn(0)); } return availableData; } SQLite::rowid_t CheckpointDataTable::AddCheckpointData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index) { SQLite::Builder::StatementBuilder builder; if (value.empty()) { builder.InsertInto(s_CheckpointDataTable_Table_Name) .Columns({ s_CheckpointDataTable_CheckpointId_Column, s_CheckpointDataTable_ContextData_Column, s_CheckpointDataTable_Name_Column, s_CheckpointDataTable_Index_Column }) .Values(checkpointId, contextData, name, index); } else { builder.InsertInto(s_CheckpointDataTable_Table_Name) .Columns({ s_CheckpointDataTable_CheckpointId_Column, s_CheckpointDataTable_ContextData_Column, s_CheckpointDataTable_Name_Column, s_CheckpointDataTable_Value_Column, s_CheckpointDataTable_Index_Column }) .Values(checkpointId, contextData, name, value, index); } builder.Execute(connection); return connection.GetLastInsertRowID(); } bool CheckpointDataTable::HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); builder.Equals(checkpointId).And(s_CheckpointDataTable_ContextData_Column).Equals(type).And(s_CheckpointDataTable_Name_Column).Equals(name); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) == 0); } std::vector CheckpointDataTable::GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointDataTable_Name_Column).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); builder.Equals(checkpointId).And(s_CheckpointDataTable_ContextData_Column).Equals(type); SQLite::Statement select = builder.Prepare(connection); std::vector fields; while (select.Step()) { fields.emplace_back(select.GetColumn(0)); } return fields; } std::vector CheckpointDataTable::GetDataValuesByFieldName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointDataTable_Value_Column).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); builder.Equals(checkpointId).And(s_CheckpointDataTable_ContextData_Column).Equals(contextData).And(s_CheckpointDataTable_Name_Column).Equals(name); SQLite::Statement select = builder.Prepare(connection); std::vector values; while (select.Step()) { values.emplace_back(select.GetColumn(0)); } return values; } std::string CheckpointDataTable::GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type) { SQLite::Builder::StatementBuilder builder; builder.Select(s_CheckpointDataTable_Value_Column).From(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column); builder.Equals(checkpointId).And(s_CheckpointDataTable_ContextData_Column).Equals(type); SQLite::Statement select = builder.Prepare(connection); if (select.Step()) { return select.GetColumn(0); } else { return {}; } } void CheckpointDataTable::RemoveDataType(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData) { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_CheckpointDataTable_Table_Name).Where(s_CheckpointDataTable_CheckpointId_Column).Equals(checkpointId) .And(s_CheckpointDataTable_ContextData_Column).Equals(contextData); builder.Execute(connection); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { struct CheckpointDataTable { // Get the table name. static std::string_view TableName(); // Creates the table with named indices. static void Create(SQLite::Connection& connection); // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); // Get all available context data. static std::vector GetAvailableData(SQLite::Connection& connection, SQLite::rowid_t checkpointId); // Adds a context data for a checkpoint. Index is used to represent the item number if the context data has more than one value. static SQLite::rowid_t AddCheckpointData(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name, std::string_view value, int index = 1); // Gets all fields for a context data. static std::vector GetDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); // Gets the context data values by property name from a checkpoint id. static std::vector GetDataValuesByFieldName(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData, std::string_view name); // Returns a boolean value indicating whether a field exists for a context data. static bool HasDataField(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type, std::string_view name); // Removes the context data by checkpoint id. static void RemoveDataType(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int contextData); // Gets a single data value for a context data. static std::string GetDataValue(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int type); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/ICheckpointDatabase.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { struct CheckpointDatabaseInterface : public ICheckpointDatabase { // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection) override; private: bool IsEmpty(SQLite::Connection& connection) override; SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) override; std::vector GetCheckpointIds(SQLite::Connection& connection) override; std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) override; std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; void SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, const std::vector& values) override; std::optional> GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) override; void RemoveCheckpointDataType(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h" #include "Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { SQLite::Version CheckpointDatabaseInterface::GetVersion() const { return { 1, 0 }; } void CheckpointDatabaseInterface::CreateTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTables_v1_0"); Checkpoint_V1_0::CheckpointTable::Create(connection); Checkpoint_V1_0::CheckpointDataTable::Create(connection); savepoint.Commit(); } bool CheckpointDatabaseInterface::IsEmpty(SQLite::Connection& connection) { return CheckpointDataTable::IsEmpty(connection); } SQLite::rowid_t CheckpointDatabaseInterface::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addCheckpoint_v1_0"); SQLite::rowid_t checkpointId = CheckpointTable::AddCheckpoint(connection, checkpointName); savepoint.Commit(); return checkpointId; } std::vector CheckpointDatabaseInterface::GetCheckpointIds(SQLite::Connection& connection) { return CheckpointTable::GetCheckpointIds(connection); } std::vector CheckpointDatabaseInterface::GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) { return CheckpointDataTable::GetAvailableData(connection, checkpointId); } std::vector CheckpointDatabaseInterface::GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) { return CheckpointDataTable::GetDataFields(connection, checkpointId, dataType); } void CheckpointDatabaseInterface::SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, const std::vector& values) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "setCheckpointData_v1_0"); if (values.empty()) { CheckpointDataTable::AddCheckpointData(connection, checkpointId, dataType, name, {}, 0); } else { int index = 0; for (const auto& value : values) { CheckpointDataTable::AddCheckpointData(connection, checkpointId, dataType, name, value, index); index++; } } savepoint.Commit(); } std::optional> CheckpointDatabaseInterface::GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) { return CheckpointDataTable::GetDataValuesByFieldName(connection, checkpointId, dataType, name); } void CheckpointDatabaseInterface::RemoveCheckpointDataType(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) { CheckpointDataTable::RemoveDataType(connection, checkpointId, dataType); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "CheckpointTable.h" #include namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { using namespace std::string_view_literals; static constexpr std::string_view s_CheckpointTable_Table_Name = "Checkpoints"sv; static constexpr std::string_view s_CheckpointTable_Index_Name = "Checkpoints_pkindex"sv; static constexpr std::string_view s_CheckpointTable_Name_Column = "Name"; static constexpr std::string_view s_CheckpointTable_CreationTime_Column = "CreationTime"; std::string_view CheckpointTable::TableName() { return s_CheckpointTable_Table_Name; } void CheckpointTable::Create(SQLite::Connection& connection) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createCheckpointTable_v1_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_CheckpointTable_Table_Name).Columns({ ColumnBuilder(s_CheckpointTable_Index_Name, Type::Integer).PrimaryKey(), ColumnBuilder(s_CheckpointTable_Name_Column, Type::Text).NotNull(), ColumnBuilder(s_CheckpointTable_CreationTime_Column, Type::Int64).NotNull() }); createTableBuilder.Execute(connection); savepoint.Commit(); } std::vector CheckpointTable::GetCheckpointIds(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::RowIDName).From(s_CheckpointTable_Table_Name).OrderBy(SQLite::RowIDName).Descending(); SQLite::Statement select = builder.Prepare(connection); std::vector checkpoints; while (select.Step()) { checkpoints.emplace_back(select.GetColumn(0)); } return checkpoints; } SQLite::rowid_t CheckpointTable::AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_CheckpointTable_Table_Name) .Columns({ s_CheckpointTable_Name_Column, s_CheckpointTable_CreationTime_Column }) .Values(checkpointName, Utility::GetCurrentUnixEpoch()); builder.Execute(connection); return connection.GetLastInsertRowID(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { struct CheckpointTable { // Get the table name. static std::string_view TableName(); // Creates the table with named indices. static void Create(SQLite::Connection& connection); // Returns all checkpoint ids in descending (newest at the front) order. static std::vector GetCheckpointIds(SQLite::Connection& connection); // Adds a checkpoint. static SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointDatabase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Repository::Microsoft::Schema { struct ICheckpointDatabase { virtual ~ICheckpointDatabase() = default; // Gets the schema version that this index interface is built for. virtual SQLite::Version GetVersion() const = 0; // Creates all of the version dependent tables within the database. virtual void CreateTables(SQLite::Connection& connection) = 0; // Version 1.0 // Returns a bool value indicating whether all checkpoint tables are empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; // Returns all checkpoint ids in descending (newest at the front) order. virtual std::vector GetCheckpointIds(SQLite::Connection& connection) = 0; // Adds a new checkpoint to the Checkpoint table. virtual SQLite::rowid_t AddCheckpoint(SQLite::Connection& connection, std::string_view checkpointName) = 0; // Gets the data types associated with a checkpoint id. virtual std::vector GetCheckpointDataTypes(SQLite::Connection& connection, SQLite::rowid_t checkpointId) = 0; // Sets the field values for a checkpoint data type. virtual void SetCheckpointDataValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name, const std::vector& values) = 0; // Gets all field names for a checkpoint data type. virtual std::vector GetCheckpointDataFields(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; // Gets all field values for a checkpoint data type. virtual std::optional> GetCheckpointDataFieldValues(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType, std::string_view name) = 0; // Removes all values for a checkpoint data type. virtual void RemoveCheckpointDataType(SQLite::Connection& connection, SQLite::rowid_t checkpointId, int dataType) = 0; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/IPinningIndex.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "winget/Pin.h" namespace AppInstaller::Repository::Microsoft::Schema { struct IPinningIndex { virtual ~IPinningIndex() = default; // Gets the schema version that this index interface is built for. virtual SQLite::Version GetVersion() const = 0; // Creates all of the version dependent tables within the database. virtual void CreateTables(SQLite::Connection& connection) = 0; // Version 1.0 // Adds a pin to the index. virtual SQLite::rowid_t AddPin(SQLite::Connection& connection, const Pinning::Pin& pin) = 0; // Updates an existing pin in the index. // The return value indicates whether the index was modified by the function. virtual std::pair UpdatePin(SQLite::Connection& connection, const Pinning::Pin& pin) = 0; // Removes a pin from the index. virtual SQLite::rowid_t RemovePin(SQLite::Connection& connection, const Pinning::PinKey& pinKey) = 0; // Returns the current pin for a given package if it exists. virtual std::optional GetPin(SQLite::Connection& connection, const Pinning::PinKey& pinKey) = 0; // Returns a vector containing all the existing pins. virtual std::vector GetAllPins(SQLite::Connection& connection) = 0; // Removes all the pins from a given source, or from all sources if none is specified. // Returns a value indicating whether any pin was deleted. virtual bool ResetAllPins(SQLite::Connection& connection, std::string_view sourceId) = 0; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/IPortableIndex.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "winget/PortableFileEntry.h" #include namespace AppInstaller::Repository::Microsoft::Schema { struct IPortableIndex { virtual ~IPortableIndex() = default; // Gets the schema version that this index interface is built for. virtual SQLite::Version GetVersion() const = 0; // Creates all of the version dependent tables within the database. virtual void CreateTable(SQLite::Connection& connection) = 0; // Version 1.0 // Adds a portable file to the index. virtual SQLite::rowid_t AddPortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) = 0; // Removes a portable file from the index. virtual SQLite::rowid_t RemovePortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) = 0; // Updates the file with matching FilePath in the index. // The return value indicates whether the index was modified by the function. virtual std::pair UpdatePortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) = 0; // Returns a bool value indicating whether the PortableFile already exists in the index. virtual bool Exists(SQLite::Connection& connection, const Portable::PortableFileEntry& file) = 0; // Returns a bool value indicating whether the index is empty. virtual bool IsEmpty(SQLite::Connection& connection) = 0; // Returns a vector including all the portable files recorded in the index. virtual std::vector GetAllPortableFiles(SQLite::Connection& connection) = 0; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/ISQLiteIndex.h" #include "Microsoft/Schema/1_0/Interface.h" #include "Microsoft/Schema/1_1/Interface.h" #include "Microsoft/Schema/1_2/Interface.h" #include "Microsoft/Schema/1_3/Interface.h" #include "Microsoft/Schema/1_4/Interface.h" #include "Microsoft/Schema/1_5/Interface.h" #include "Microsoft/Schema/1_6/Interface.h" #include "Microsoft/Schema/1_7/Interface.h" #include "Microsoft/Schema/2_0/Interface.h" namespace AppInstaller::Repository::Microsoft::Schema { void ISQLiteIndex::PrepareForPackaging(const SQLiteIndexContext& context) { PrepareForPackaging(context.Connection); } void ISQLiteIndex::SetProperty(SQLite::Connection&, Property, const std::string&) { THROW_WIN32(ERROR_NOT_SUPPORTED); } std::unique_ptr CreateISQLiteIndex(const SQLite::Version& version) { if (version.MajorVersion == 1 || version.IsLatest()) { constexpr std::array(*)(), 8> versionCreatorMap = { []() { return std::unique_ptr(std::make_unique()); }, []() { return std::unique_ptr(std::make_unique()); }, []() { return std::unique_ptr(std::make_unique()); }, []() { return std::unique_ptr(std::make_unique()); }, []() { return std::unique_ptr(std::make_unique()); }, []() { return std::unique_ptr(std::make_unique()); }, []() { return std::unique_ptr(std::make_unique()); }, []() { return std::unique_ptr(std::make_unique()); }, }; return versionCreatorMap[std::min(static_cast(version.MinorVersion), versionCreatorMap.size() - 1)](); } // Version 2.0 is designed solely for minimizing the size of the index for transport. // Unless it is prepared for packaging, it will be identical to a 1.N index. if (version.MajorVersion == 2) { constexpr std::array(*)(), 1> versionCreatorMap = { []() { return std::unique_ptr(std::make_unique()); }, }; return versionCreatorMap[std::min(static_cast(version.MinorVersion), versionCreatorMap.size() - 1)](); } // We do not have the capacity to operate on this schema version THROW_WIN32(ERROR_NOT_SUPPORTED); } std::vector GetDefaultMatchTypeOrder(MatchType type) { switch (type) { case MatchType::Exact: return { MatchType::Exact }; case MatchType::CaseInsensitive: return { MatchType::CaseInsensitive }; case MatchType::StartsWith: return { MatchType::CaseInsensitive, MatchType::StartsWith }; case MatchType::Substring: return { MatchType::CaseInsensitive, MatchType::Substring }; case MatchType::Wildcard: return { MatchType::Wildcard }; case MatchType::Fuzzy: return { MatchType::CaseInsensitive, MatchType::Fuzzy }; case MatchType::FuzzySubstring: return { MatchType::CaseInsensitive, MatchType::Fuzzy, MatchType::Substring, MatchType::FuzzySubstring }; default: THROW_HR(E_UNEXPECTED); } } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "ISource.h" #include "Microsoft/Schema/SQLiteIndexContextData.h" #include #include #include #include #include namespace AppInstaller::Repository::Microsoft::Schema { // Contains the database connection and any other data that the owning index might need to pass in. struct SQLiteIndexContext { SQLite::Connection& Connection; SQLiteIndexContextData& Data; }; // The common interface used to interact with all schema versions of the index. struct ISQLiteIndex { virtual ~ISQLiteIndex() = default; // The non-version specific return value of Search. // New fields must have initializers to their down-schema defaults. struct SearchResult { std::vector> Matches; bool Truncated = false; }; // The non-version specific return value of GetMetadataByManifestId. using MetadataResult = std::vector>; // Version 1.0 // Gets the schema version that this index interface is built for. virtual SQLite::Version GetVersion() const = 0; // Options for creating the index. enum class CreateOptions { // Standard None = 0x0, // Enable support for passing in nullopt values to Add/UpdateManifest SupportPathless = 0x1, // Disable support for dependencies DisableDependenciesSupport = 0x2, // Use maximum page size in SQLite. // This was part of an exploration of ways to reduce file size but ultimately led to a larger // compressed file with a worse ratio (limited testing but was significant enough to warrant abandonment). // Leaving this here as a valid null result to prevent future maintainers from needing to investigate. LargePageSize = 0x4, }; // Contains both the object representation of the version key and the rows. struct VersionKey { Utility::VersionAndChannel VersionAndChannel; SQLite::rowid_t ManifestId; bool operator<(const VersionKey& other) const { return VersionAndChannel < other.VersionAndChannel; } }; // Creates all of the version dependent tables within the database. virtual void CreateTables(SQLite::Connection& connection, CreateOptions options) = 0; // Adds the manifest at the repository relative path to the index. virtual SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) = 0; // Updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the index was modified by the function. virtual std::pair UpdateManifest( SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional& relativePath) = 0; // Removes the manifest with matching { Id, Version, Channel } from the index. virtual SQLite::rowid_t RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest) = 0; // Removes the manifest with the given id. virtual void RemoveManifestById(SQLite::Connection& connection, SQLite::rowid_t manifestId) = 0; // Removes data that is no longer needed for an index that is to be published. virtual void PrepareForPackaging(SQLite::Connection& connection) = 0; // Removes data that is no longer needed for an index that is to be published. virtual void PrepareForPackaging(const SQLiteIndexContext& context); // Checks the consistency of the index to ensure that every referenced row exists. // Returns true if index is consistent; false if it is not. virtual bool CheckConsistency(const SQLite::Connection& connection, bool log) const = 0; // Performs a search based on the given criteria. virtual SearchResult Search(const SQLite::Connection& connection, const SearchRequest& request) const = 0; // Gets the string for the given property and primary id, if present. virtual std::optional GetPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionProperty property) const = 0; // Gets the string values for the given property and primary id, if present. virtual std::vector GetMultiPropertyByPrimaryId(const SQLite::Connection& connection, SQLite::rowid_t primaryId, PackageVersionMultiProperty property) const = 0; // Gets the manifest id for the given { id, version, channel }, if present. // If version is empty, gets the value for the 'latest' version. virtual std::optional GetManifestIdByKey(const SQLite::Connection& connection, SQLite::rowid_t id, std::string_view version, std::string_view channel) const = 0; // Gets the manifest id for the given manifest, if present. virtual std::optional GetManifestIdByManifest(const SQLite::Connection& connection, const Manifest::Manifest& manifest) const = 0; // Gets all versions and channels for the given id. virtual std::vector GetVersionKeysById(const SQLite::Connection& connection, SQLite::rowid_t id) const = 0; // Version 1.1 // Gets the string for the given metadata and manifest id, if present. virtual MetadataResult GetMetadataByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId) const = 0; // Sets the string for the given metadata and manifest id. virtual void SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value) = 0; // Version 1.2 // Normalizes a name using the internal rules used by the index. // Largely a utility function; should not be used to do work on behalf of the index by the caller. virtual Utility::NormalizedName NormalizeName(std::string_view name, std::string_view publisher) const = 0; // Version 1.4 // Get all the dependencies for a specific manifest. virtual std::set> GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId) const = 0; virtual std::vector> GetDependentsById(const SQLite::Connection& connection, AppInstaller::Manifest::string_t packageId) const = 0; // Version 1.7 // Drops all tables that would have been created. virtual void DropTables(SQLite::Connection& connection) = 0; // Version 2.0 // Migrates from the current interface given. // Returns true if supported; false if not. // Throws on errors that occur during an attempted migration. virtual bool MigrateFrom(SQLite::Connection& connection, const ISQLiteIndex* current) = 0; // Set the property value. virtual void SetProperty(SQLite::Connection& connection, Property property, const std::string& value); }; DEFINE_ENUM_FLAG_OPERATORS(ISQLiteIndex::CreateOptions); // Creates the ISQLiteIndex interface object for the given version. std::unique_ptr CreateISQLiteIndex(const SQLite::Version& version); // For a given match type, gets the set of match types that are more specific subsets of it. std::vector GetDefaultMatchTypeOrder(MatchType type); } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PinTable.h" #include #include "Microsoft/Schema/IPinningIndex.h" namespace AppInstaller::Repository::Microsoft::Schema::Pinning_V1_0 { namespace { std::optional GetPinFromRow( std::string_view packageId, std::string_view sourceId, Pinning::PinType type, std::string_view version) { switch (type) { case Pinning::PinType::Blocking: return Pinning::Pin::CreateBlockingPin({ packageId, sourceId }); case Pinning::PinType::Pinning: return Pinning::Pin::CreatePinningPin({ packageId, sourceId }); case Pinning::PinType::Gating: return Pinning::Pin::CreateGatingPin({ packageId, sourceId }, Utility::GatedVersion{ version }); default: return {}; } } } using namespace std::string_view_literals; static constexpr std::string_view s_PinTable_Table_Name = "pin"sv; static constexpr std::string_view s_PinTable_PackageId_Column = "package_id"sv; static constexpr std::string_view s_PinTable_SourceId_Column = "source_id"sv; static constexpr std::string_view s_PinTable_Type_Column = "type"sv; static constexpr std::string_view s_PinTable_Version_Column = "version"sv; static constexpr std::string_view s_PinTable_Index = "pin_index"sv; std::string_view PinTable::TableName() { return s_PinTable_Table_Name; } void PinTable::Create(SQLite::Connection& connection) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createpintable_v1_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_PinTable_Table_Name).BeginColumns(); createTableBuilder.Column(ColumnBuilder(s_PinTable_PackageId_Column, Type::Text).NotNull()); createTableBuilder.Column(ColumnBuilder(s_PinTable_SourceId_Column, Type::Text).NotNull()); createTableBuilder.Column(ColumnBuilder(s_PinTable_Type_Column, Type::Int64).NotNull()); createTableBuilder.Column(ColumnBuilder(s_PinTable_Version_Column, Type::Text).NotNull()); createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); // Create an index over the pairs package,source StatementBuilder createIndexBuilder; createIndexBuilder.CreateUniqueIndex(s_PinTable_Index).On(s_PinTable_Table_Name) .Columns({ s_PinTable_PackageId_Column, s_PinTable_SourceId_Column }); createIndexBuilder.Execute(connection); savepoint.Commit(); } std::optional PinTable::GetIdByPinKey(SQLite::Connection& connection, const Pinning::PinKey& pinKey) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::RowIDName).From(s_PinTable_Table_Name) .Where(s_PinTable_PackageId_Column).Equals((std::string_view)pinKey.PackageId) .And(s_PinTable_SourceId_Column).Equals((std::string_view)pinKey.SourceId); SQLite::Statement select = builder.Prepare(connection); if (select.Step()) { return select.GetColumn(0); } else { return {}; } } SQLite::rowid_t PinTable::AddPin(SQLite::Connection& connection, const Pinning::Pin& pin) { SQLite::Builder::StatementBuilder builder; const auto& pinKey = pin.GetKey(); builder.InsertInto(s_PinTable_Table_Name) .Columns({ s_PinTable_PackageId_Column, s_PinTable_SourceId_Column, s_PinTable_Type_Column, s_PinTable_Version_Column }) .Values( (std::string_view)pinKey.PackageId, pinKey.SourceId, pin.GetType(), pin.GetGatedVersion().ToString()); builder.Execute(connection); return connection.GetLastInsertRowID(); } bool PinTable::UpdatePinById(SQLite::Connection& connection, SQLite::rowid_t pinId, const Pinning::Pin& pin) { SQLite::Builder::StatementBuilder builder; const auto& pinKey = pin.GetKey(); builder.Update(s_PinTable_Table_Name).Set() .Column(s_PinTable_PackageId_Column).Equals((std::string_view)pinKey.PackageId) .Column(s_PinTable_SourceId_Column).Equals(pinKey.SourceId) .Column(s_PinTable_Type_Column).Equals(pin.GetType()) .Column(s_PinTable_Version_Column).Equals(pin.GetGatedVersion().ToString()) .Where(SQLite::RowIDName).Equals(pinId); builder.Execute(connection); return connection.GetChanges() != 0; } void PinTable::RemovePinById(SQLite::Connection& connection, SQLite::rowid_t pinId) { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_PinTable_Table_Name).Where(SQLite::RowIDName).Equals(pinId); builder.Execute(connection); } std::optional PinTable::GetPinById(SQLite::Connection& connection, const SQLite::rowid_t pinId) { SQLite::Builder::StatementBuilder builder; builder.Select({ s_PinTable_PackageId_Column, s_PinTable_SourceId_Column, s_PinTable_Type_Column, s_PinTable_Version_Column }) .From(s_PinTable_Table_Name).Where(SQLite::RowIDName).Equals(pinId); SQLite::Statement select = builder.Prepare(connection); if (!select.Step()) { return {}; } auto [packageId, sourceId, pinType, gatedVersion] = select.GetRow(); return GetPinFromRow(packageId, sourceId, pinType, gatedVersion); } std::vector PinTable::GetAllPins(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select({ s_PinTable_PackageId_Column, s_PinTable_SourceId_Column, s_PinTable_Type_Column, s_PinTable_Version_Column }) .From(s_PinTable_Table_Name); SQLite::Statement select = builder.Prepare(connection); std::vector pins; while (select.Step()) { auto [packageId, sourceId, pinType, gatedVersion] = select.GetRow(); auto pin = GetPinFromRow(packageId, sourceId, pinType, gatedVersion); if (pin) { pins.push_back(std::move(pin.value())); } } return pins; } bool PinTable::ResetAllPins(SQLite::Connection& connection, std::string_view sourceId) { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_PinTable_Table_Name); if (!sourceId.empty()) { builder.Where(s_PinTable_SourceId_Column).Equals(sourceId); } builder.Execute(connection); return connection.GetChanges() != 0; } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "Microsoft/Schema/IPinningIndex.h" #include namespace AppInstaller::Repository::Microsoft::Schema::Pinning_V1_0 { struct PinTable { // Get the table name. static std::string_view TableName(); // Creates the table with named indices. static void Create(SQLite::Connection& connection); // Gets the row ID for the pin, if it exists. static std::optional GetIdByPinKey(SQLite::Connection& connection, const Pinning::PinKey& pinKey); // Adds a new pin. Returns the row ID of the added pin. static SQLite::rowid_t AddPin(SQLite::Connection& connection, const Pinning::Pin& pin); // Updates an existing pin. // Returns a value indicating whether there were any changes. static bool UpdatePinById(SQLite::Connection& connection, SQLite::rowid_t pinId, const Pinning::Pin& pin); // Removes a pin given its row ID. static void RemovePinById(SQLite::Connection& connection, SQLite::rowid_t pinId); // Gets a pin by its row ID if it exists. // Used for testing static std::optional GetPinById(SQLite::Connection& connection, const SQLite::rowid_t pinId); // Gets all the currently existing pins. static std::vector GetAllPins(SQLite::Connection& connection); // Resets all pins from a given source, or from all sources if none is specified. // Returns a value indicating whether there were any changes. static bool ResetAllPins(SQLite::Connection& connection, std::string_view sourceId = {}); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/IPinningIndex.h" namespace AppInstaller::Repository::Microsoft::Schema::Pinning_V1_0 { struct PinningIndexInterface : public IPinningIndex { // Version 1.0 SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection) override; private: SQLite::rowid_t AddPin(SQLite::Connection& connection, const Pinning::Pin& pin) override; std::pair UpdatePin(SQLite::Connection& connection, const Pinning::Pin& pin) override; SQLite::rowid_t RemovePin(SQLite::Connection& connection, const Pinning::PinKey& pinKey) override; std::optional GetPin(SQLite::Connection& connection, const Pinning::PinKey& pinKey) override; std::vector GetAllPins(SQLite::Connection& connection) override; bool ResetAllPins(SQLite::Connection& connection, std::string_view sourceId) override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/Pinning_1_0/PinningIndexInterface.h" #include "Microsoft/Schema/Pinning_1_0/PinTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Pinning_V1_0 { namespace { std::optional GetExistingPinId(SQLite::Connection& connection, const Pinning::PinKey& pinKey) { auto result = PinTable::GetIdByPinKey(connection, pinKey); if (!result) { AICLI_LOG(Repo, Verbose, << "Did not find pin " << pinKey.ToString()); } return result; } } // Version 1.0 SQLite::Version PinningIndexInterface::GetVersion() const { return { 1, 0 }; } void PinningIndexInterface::CreateTables(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createpintable_v1_0"); Pinning_V1_0::PinTable::Create(connection); savepoint.Commit(); } SQLite::rowid_t PinningIndexInterface::AddPin(SQLite::Connection& connection, const Pinning::Pin& pin) { auto existingPin = GetExistingPinId(connection, pin.GetKey()); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingPin.has_value()); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addpin_v1_0"); SQLite::rowid_t pinId = PinTable::AddPin(connection, pin); savepoint.Commit(); return pinId; } std::pair PinningIndexInterface::UpdatePin(SQLite::Connection& connection, const Pinning::Pin& pin) { auto existingPinId = GetExistingPinId(connection, pin.GetKey()); // If the pin doesn't exist, fail the update THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingPinId); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updatepin_v1_0"); bool status = PinTable::UpdatePinById(connection, existingPinId.value(), pin); savepoint.Commit(); return { status, existingPinId.value() }; } SQLite::rowid_t PinningIndexInterface::RemovePin(SQLite::Connection& connection, const Pinning::PinKey& pinKey) { auto existingPinId = GetExistingPinId(connection, pinKey); // If the pin doesn't exist, fail the remove THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), !existingPinId); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removepin_v1_0"); PinTable::RemovePinById(connection, existingPinId.value()); savepoint.Commit(); return existingPinId.value(); } std::optional PinningIndexInterface::GetPin(SQLite::Connection& connection, const Pinning::PinKey& pinKey) { auto existingPinId = GetExistingPinId(connection, pinKey); if (!existingPinId) { return {}; } return PinTable::GetPinById(connection, existingPinId.value()); } std::vector PinningIndexInterface::GetAllPins(SQLite::Connection& connection) { return PinTable::GetAllPins(connection); } bool PinningIndexInterface::ResetAllPins(SQLite::Connection& connection, std::string_view sourceId) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "resetpins_v1_0"); bool result = PinTable::ResetAllPins(connection, sourceId); savepoint.Commit(); return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableIndexInterface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/Schema/IPortableIndex.h" #include "Microsoft/Schema/Portable_1_0/PortableTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Portable_V1_0 { struct PortableIndexInterface : public IPortableIndex { // Version 1.0 SQLite::Version GetVersion() const override; void CreateTable(SQLite::Connection& connection) override; private: SQLite::rowid_t AddPortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) override; SQLite::rowid_t RemovePortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) override; std::pair UpdatePortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) override; bool Exists(SQLite::Connection& connection, const Portable::PortableFileEntry& file) override; bool IsEmpty(SQLite::Connection& connection) override; std::vector GetAllPortableFiles(SQLite::Connection& connection) override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableIndexInterface_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/Portable_1_0/PortableIndexInterface.h" #include "Microsoft/Schema/Portable_1_0/PortableTable.h" namespace AppInstaller::Repository::Microsoft::Schema::Portable_V1_0 { namespace { std::optional GetExistingPortableFileId(const SQLite::Connection& connection, const Portable::PortableFileEntry& file) { auto result = PortableTable::SelectByFilePath(connection, file.GetFilePath()); if (!result) { AICLI_LOG(Repo, Verbose, << "Did not find a portable file with the path { " << file.GetFilePath() << " }"); } return result; } } SQLite::Version PortableIndexInterface::GetVersion() const { return { 1, 0 }; } void PortableIndexInterface::CreateTable(SQLite::Connection& connection) { SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createportabletable_v1_0"); Portable_V1_0::PortableTable::Create(connection); savepoint.Commit(); } SQLite::rowid_t PortableIndexInterface::AddPortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) { auto portableEntryResult = GetExistingPortableFileId(connection, file); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), portableEntryResult.has_value()); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addportablefile_v1_0"); SQLite::rowid_t portableFileId = PortableTable::AddPortableFile(connection, file); savepoint.Commit(); return portableFileId; } SQLite::rowid_t PortableIndexInterface::RemovePortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) { auto portableEntryResult = GetExistingPortableFileId(connection, file); // If the portable file doesn't actually exist, fail the remove. THROW_HR_IF(E_NOT_SET, !portableEntryResult); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removeportablefile_v1_0"); PortableTable::RemovePortableFileById(connection, portableEntryResult.value()); savepoint.Commit(); return portableEntryResult.value(); } std::pair PortableIndexInterface::UpdatePortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) { auto portableEntryResult = GetExistingPortableFileId(connection, file); // If the portable file doesn't actually exist, fail the update. THROW_HR_IF(E_NOT_SET, !portableEntryResult); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "updateportablefile_v1_0"); bool status = PortableTable::UpdatePortableFileById(connection, portableEntryResult.value(), file); savepoint.Commit(); return { status, portableEntryResult.value() }; } bool PortableIndexInterface::Exists(SQLite::Connection& connection, const Portable::PortableFileEntry& file) { return GetExistingPortableFileId(connection, file).has_value(); } bool PortableIndexInterface::IsEmpty(SQLite::Connection& connection) { return PortableTable::IsEmpty(connection); } std::vector PortableIndexInterface::GetAllPortableFiles(SQLite::Connection& connection) { return PortableTable::GetAllPortableFiles(connection); } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PortableTable.h" #include #include "Microsoft/Schema/IPortableIndex.h" namespace AppInstaller::Repository::Microsoft::Schema::Portable_V1_0 { using namespace std::string_view_literals; static constexpr std::string_view s_PortableTable_Table_Name = "portable"sv; static constexpr std::string_view s_PortableTable_FilePath_Column = "filepath"sv; static constexpr std::string_view s_PortableTable_FileType_Column = "filetype"sv; static constexpr std::string_view s_PortableTable_SHA256_Column = "sha256"sv; static constexpr std::string_view s_PortableTable_SymlinkTarget_Column = "symlinktarget"sv; std::string_view PortableTable::TableName() { return s_PortableTable_Table_Name; } void PortableTable::Create(SQLite::Connection& connection) { using namespace SQLite::Builder; SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createPortableTable_v1_0"); StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_PortableTable_Table_Name).BeginColumns(); createTableBuilder.Column(ColumnBuilder(s_PortableTable_FilePath_Column, Type::Text).NotNull().Unique().CollateNoCase()); createTableBuilder.Column(ColumnBuilder(s_PortableTable_FileType_Column, Type::Int64).NotNull()); createTableBuilder.Column(ColumnBuilder(s_PortableTable_SHA256_Column, Type::Blob)); createTableBuilder.Column(ColumnBuilder(s_PortableTable_SymlinkTarget_Column, Type::Text)); createTableBuilder.EndColumns(); createTableBuilder.Execute(connection); savepoint.Commit(); } std::optional PortableTable::SelectByFilePath(const SQLite::Connection& connection, const std::filesystem::path& path) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::RowIDName).From(s_PortableTable_Table_Name).Where(s_PortableTable_FilePath_Column); builder.Equals(path.u8string()); SQLite::Statement select = builder.Prepare(connection); if (select.Step()) { return select.GetColumn(0); } else { return {}; } } void PortableTable::RemovePortableFileById(SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_PortableTable_Table_Name).Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); } SQLite::rowid_t PortableTable::AddPortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file) { SQLite::Builder::StatementBuilder builder; builder.InsertInto(s_PortableTable_Table_Name) .Columns({ s_PortableTable_FilePath_Column, s_PortableTable_FileType_Column, s_PortableTable_SHA256_Column, s_PortableTable_SymlinkTarget_Column }) .Values(file.GetFilePath().u8string(), file.FileType, file.SHA256, file.SymlinkTarget); builder.Execute(connection); return connection.GetLastInsertRowID(); } bool PortableTable::UpdatePortableFileById(SQLite::Connection& connection, SQLite::rowid_t id, const Portable::PortableFileEntry& file) { SQLite::Builder::StatementBuilder builder; builder.Update(s_PortableTable_Table_Name).Set() .Column(s_PortableTable_FilePath_Column).Equals(file.GetFilePath().u8string()) .Column(s_PortableTable_FileType_Column).Equals(file.FileType) .Column(s_PortableTable_SHA256_Column).Equals(file.SHA256) .Column(s_PortableTable_SymlinkTarget_Column).Equals(file.SymlinkTarget) .Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); return connection.GetChanges() != 0; } std::optional PortableTable::GetPortableFileById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.Select({ s_PortableTable_FilePath_Column, s_PortableTable_FileType_Column, s_PortableTable_SHA256_Column, s_PortableTable_SymlinkTarget_Column}) .From(s_PortableTable_Table_Name).Where(SQLite::RowIDName).Equals(id); SQLite::Statement select = builder.Prepare(connection); Portable::PortableFileEntry portableFile; if (select.Step()) { auto [filePath, fileType, sha256, symlinkTarget] = select.GetRow(); portableFile.FileType = fileType; portableFile.SetFilePath(Utility::ConvertToUTF16(filePath)); portableFile.SHA256 = std::move(sha256); portableFile.SymlinkTarget = std::move(symlinkTarget); return portableFile; } else { return {}; } } bool PortableTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_PortableTable_Table_Name).Where(SQLite::RowIDName).Equals(id); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) != 0); } void PortableTable::DeleteById(SQLite::Connection& connection, SQLite::rowid_t id) { SQLite::Builder::StatementBuilder builder; builder.DeleteFrom(s_PortableTable_Table_Name).Where(SQLite::RowIDName).Equals(id); builder.Execute(connection); } bool PortableTable::IsEmpty(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select(SQLite::Builder::RowCount).From(s_PortableTable_Table_Name); SQLite::Statement countStatement = builder.Prepare(connection); THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); return (countStatement.GetColumn(0) == 0); } std::vector PortableTable::GetAllPortableFiles(SQLite::Connection& connection) { SQLite::Builder::StatementBuilder builder; builder.Select({ s_PortableTable_FilePath_Column, s_PortableTable_FileType_Column, s_PortableTable_SHA256_Column, s_PortableTable_SymlinkTarget_Column }) .From(s_PortableTable_Table_Name); SQLite::Statement select = builder.Prepare(connection); std::vector result; while (select.Step()) { Portable::PortableFileEntry portableFile; auto [filePath, fileType, sha256, symlinkTarget] = select.GetRow(); portableFile.FileType = fileType; portableFile.SetFilePath(Utility::ConvertToUTF16(filePath)); portableFile.SHA256 = std::move(sha256); portableFile.SymlinkTarget = std::move(symlinkTarget); result.emplace_back(std::move(portableFile)); } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "Microsoft/Schema/IPortableIndex.h" #include namespace AppInstaller::Repository::Microsoft::Schema::Portable_V1_0 { // A table the represents a single portable file struct PortableTable { // Get the table name. static std::string_view TableName(); // Creates the table with named indices. static void Create(SQLite::Connection& connection); // Gets a value indicating whether the portable file with rowid id exists. static bool ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id); // Deletes the portable file row with the given rowid static void DeleteById(SQLite::Connection& connection, SQLite::rowid_t id); // Gets a value indicating whether the table is empty. static bool IsEmpty(SQLite::Connection& connection); // Selects the portable file by filepath from the table, returning the rowid if it exists. static std::optional SelectByFilePath(const SQLite::Connection& connection, const std::filesystem::path& path); // Selects the portable file by rowid from the table, returning the portable file object if it exists. static std::optional GetPortableFileById(const SQLite::Connection& connection, SQLite::rowid_t id); // Adds the portable file into the table. static SQLite::rowid_t AddPortableFile(SQLite::Connection& connection, const Portable::PortableFileEntry& file); // Removes the portable file from the table by id. static void RemovePortableFileById(SQLite::Connection& connection, SQLite::rowid_t id); // Updates the portable file in the table by id. static bool UpdatePortableFileById(SQLite::Connection& connection, SQLite::rowid_t id, const Portable::PortableFileEntry& file); // Gets all portable files recorded in the index. static std::vector GetAllPortableFiles(SQLite::Connection& connection); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Microsoft/Schema/SQLiteIndexContextData.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Repository::Microsoft::Schema { // Names a property enum class Property : size_t { PackageUpdateTrackingBaseTime, IntermediateFileOutputPath, DatabaseFilePath, Max }; namespace details { template struct PropertyMapping { // value_t type specifies the type of this property }; template <> struct PropertyMapping { using value_t = int64_t; static constexpr bool SetThroughInterface = true; }; template <> struct PropertyMapping { using value_t = std::filesystem::path; static constexpr bool SetThroughInterface = false; }; template <> struct PropertyMapping { using value_t = std::filesystem::path; static constexpr bool SetThroughInterface = false; }; } using SQLiteIndexContextData = EnumBasedVariantMap; } ================================================ FILE: src/AppInstallerRepositoryCore/PackageDependenciesValidation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "pch.h" #include "PackageDependenciesValidation.h" #include #include namespace AppInstaller::Repository { namespace { struct DependentManifestInfo { Utility::NormalizedString Id; Utility::NormalizedString Version; }; Manifest::DependencyList GetDependencies( const Manifest::Manifest& manifest, AppInstaller::Manifest::DependencyType dependencyType) { Manifest::DependencyList depList; std::vector dependencies; for (const auto& installer : manifest.Installers) { installer.Dependencies.ApplyToType(dependencyType, [&](AppInstaller::Manifest::Dependency dependency) { depList.Add(dependency); }); } return depList; } std::optional GetPackageLatestVersion( SQLiteIndex* index, Manifest::string_t packageId, std::set exclusions = {}) { SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, packageId); auto results = index->Search(request); if (results.Matches.empty()) { return {}; } auto packageRowId = results.Matches[0].first; auto vac = index->GetVersionKeysById(packageRowId); if (vac.empty()) { return {}; } SQLiteIndex::VersionKey maxVersion{ Utility::VersionAndChannel{ Utility::Version::CreateUnknown(), Utility::Channel("") } }; for (auto& v : vac) { auto currentVersion = v.VersionAndChannel.GetVersion(); if (exclusions.find(currentVersion) != exclusions.end()) { continue; } if (currentVersion > maxVersion.VersionAndChannel.GetVersion()) { maxVersion = v; } } if (maxVersion.VersionAndChannel.GetVersion().IsUnknown()) { return {}; } return maxVersion; } void ThrowOnManifestValidationFailed( std::vector> failedManifests, AppInstaller::StringResource::StringId error) { auto itrStart = failedManifests.begin(); std::vector validationErrors; validationErrors.emplace_back(error, "PackageIdentifier.PackageVersion", itrStart->first.Id + '.' + itrStart->first.Version ); std::for_each( itrStart + 1, failedManifests.end(), [&](std::pair current) { validationErrors.emplace_back(error, "PackageIdentifier.PackageVersion", current.first.Id + '.' + current.first.Version); }); THROW_EXCEPTION( Manifest::ManifestException( std::move(validationErrors), APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED)); } }; bool PackageDependenciesValidation::ValidateManifestDependencies(SQLiteIndex* index, const Manifest::Manifest& manifest) { using namespace Manifest; Dependency rootId(DependencyType::Package, manifest.Id, manifest.Version); std::vector dependenciesError; bool foundErrors = false; DependencyGraph graph { rootId, [&](const Dependency& node) { DependencyList depList; if (node.Id() == rootId.Id()) { return GetDependencies(manifest, DependencyType::Package); } auto packageLatest = GetPackageLatestVersion(index, node.Id()); if (!packageLatest.has_value()) { dependenciesError.emplace_back( ManifestError::MissingManifestDependenciesNode, "PackageIdentifier", node.Id()); foundErrors = true; return depList; } if (node.MinVersion > packageLatest->VersionAndChannel.GetVersion()) { dependenciesError.emplace_back(ManifestError::NoSuitableMinVersionDependency, "PackageIdentifier", node.Id()); foundErrors = true; return depList; } auto packageLatestDependencies = index->GetDependenciesByManifestRowId(packageLatest->ManifestId); std::for_each( packageLatestDependencies.begin(), packageLatestDependencies.end(), [&](std::pair row) { auto manifestRowId = index->GetManifestIdByKey(row.first, "", ""); auto packageId = index->GetPropertyByPrimaryId(manifestRowId.value(), PackageVersionProperty::Id); Dependency dep(DependencyType::Package, packageId.value(), row.second); depList.Add(dep); }); return depList; } }; graph.BuildGraph(); if (foundErrors) { THROW_EXCEPTION(ManifestException(std::move(dependenciesError), APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED)); } if (graph.HasLoop()) { dependenciesError.emplace_back(ManifestError::FoundDependencyLoop); THROW_EXCEPTION(ManifestException(std::move(dependenciesError), APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED)); } return true; } bool PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(SQLiteIndex* index, const Manifest::Manifest& manifest) { auto dependentsSet = index->GetDependentsById(manifest.Id); if (!dependentsSet.size()) { // all good this manifest is not a dependency of any manifest. return true; } std::vector> dependentManifestInfoToVersionPair; std::for_each( dependentsSet.begin(), dependentsSet.end(), [&](std::pair current) { DependentManifestInfo dependentManifestInfo; dependentManifestInfo.Id = index->GetPropertyByPrimaryId(current.first, PackageVersionProperty::Id).value(); dependentManifestInfo.Version = index->GetPropertyByPrimaryId(current.first, PackageVersionProperty::Version).value(); dependentManifestInfoToVersionPair.emplace_back(std::make_pair(dependentManifestInfo, current.second)); }); auto packageLatest = GetPackageLatestVersion(index, manifest.Id); if (!packageLatest.has_value()) { // this is a fatal error, a manifest should exists in the very least(including the current manifest being deleted), // since this is a delete operation. THROW_HR(APPINSTALLER_CLI_ERROR_MISSING_PACKAGE); } if (Utility::Version(manifest.Version) < packageLatest->VersionAndChannel.GetVersion()) { // all good, since it's min version the criteria is still satisfied. return true; } auto nextLatestAfterDelete = GetPackageLatestVersion(index, manifest.Id, { packageLatest->VersionAndChannel.GetVersion() }); if (!nextLatestAfterDelete.has_value()) { ThrowOnManifestValidationFailed( dependentManifestInfoToVersionPair, Manifest::ManifestError::SingleManifestPackageHasDependencies); } std::vector> breakingManifests; // Gets breaking manifests. std::copy_if( dependentManifestInfoToVersionPair.begin(), dependentManifestInfoToVersionPair.end(), std::back_inserter(breakingManifests), [&](std::pair current) { return current.second > nextLatestAfterDelete->VersionAndChannel.GetVersion(); } ); if (!breakingManifests.empty()) { ThrowOnManifestValidationFailed( breakingManifests, Manifest::ManifestError::MultiManifestPackageHasDependencies); } return true; } } ================================================ FILE: src/AppInstallerRepositoryCore/PackageDependenciesValidation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Microsoft/SQLiteIndex.h" #include namespace AppInstaller::Repository { using namespace AppInstaller::Repository::Microsoft; struct PackageDependenciesValidation { // Validate the dependencies of the given manifest. static bool ValidateManifestDependencies(SQLiteIndex* index, const Manifest::Manifest& manifest); static bool VerifyDependenciesStructureForManifestDelete(SQLiteIndex* index, const Manifest::Manifest& manifest); }; } ================================================ FILE: src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/InstalledStatus.h" #include "Public/winget/PackageVersionSelection.h" #include using namespace AppInstaller::Settings; using namespace std::chrono_literals; namespace AppInstaller::Repository { namespace { HRESULT CheckInstalledLocationStatus(const std::filesystem::path& installedLocation) { HRESULT installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; if (!installedLocation.empty()) { // Use the none throw version, if the directory cannot be reached, it's treated as not found and later file checks are not performed. std::error_code error; installLocationStatus = std::filesystem::exists(installedLocation, error) && std::filesystem::is_directory(installedLocation, error) ? WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND : WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND; } return installLocationStatus; } // Map to cache already calculated file hashes. struct FilePathComparator { bool operator()(const std::filesystem::path& a, const std::filesystem::path& b) const { if (std::filesystem::equivalent(a, b)) { return false; } return a < b; } }; using FileHashMap = std::map; HRESULT CheckInstalledFileStatus( const std::filesystem::path& filePath, const Utility::SHA256::HashBuffer& expectedHash, FileHashMap& fileHashes) { HRESULT fileStatus = WINGET_INSTALLED_STATUS_FILE_NOT_FOUND; try { if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) { fileStatus = WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK; if (!expectedHash.empty()) { auto itr = fileHashes.find(filePath); if (itr == fileHashes.end()) { // If not found in cache, compute the hash. std::ifstream in{ filePath, std::ifstream::binary }; itr = fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)).first; } fileStatus = Utility::SHA256::AreEqual(expectedHash, itr->second) ? WINGET_INSTALLED_STATUS_FILE_HASH_MATCH : WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH; } } } catch (...) { fileStatus = WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR; } return fileStatus; } std::vector CheckInstalledStatusInternal( const std::shared_ptr& package, InstalledStatusType checkTypes) { using namespace AppInstaller::Manifest; std::vector result; bool checkFileHash = false; std::shared_ptr installedVersion = GetInstalledVersion(package); std::shared_ptr availableVersion; FileHashMap fileHashes; // Variables for metadata from installed version. InstallerTypeEnum installedType = InstallerTypeEnum::Unknown; ScopeEnum installedScope = ScopeEnum::Unknown; std::filesystem::path installedLocation; std::string installedLocale; Utility::Architecture installedArchitecture = Utility::Architecture::Unknown; HRESULT installedLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; std::shared_ptr availableVersions = GetAvailableVersionsForInstalledVersion(package); // Prepare installed metadata from installed version. // Determine the available package version to be used for installed status checking. // Only perform file hash check if we find an available version that matches installed version. if (installedVersion) { // Installed metadata. auto installedMetadata = installedVersion->GetMetadata(); installedType = ConvertToInstallerTypeEnum(installedMetadata[PackageVersionMetadata::InstalledType]); installedScope = ConvertToScopeEnum(installedMetadata[PackageVersionMetadata::InstalledScope]); installedLocation = Filesystem::GetExpandedPath(installedMetadata[PackageVersionMetadata::InstalledLocation]); installedLocale = installedMetadata[PackageVersionMetadata::InstalledLocale]; installedArchitecture = Utility::ConvertToArchitectureEnum(installedMetadata[PackageVersionMetadata::InstalledArchitecture]); installedLocationStatus = CheckInstalledLocationStatus(installedLocation); // Determine available version. Utility::Version installedVersionAsVersion{ installedVersion->GetProperty(PackageVersionProperty::Version) }; auto installedChannel = installedVersion->GetProperty(PackageVersionProperty::Channel); PackageVersionKey versionKey; versionKey.Channel = installedChannel.get(); if (installedVersionAsVersion.IsApproximate()) { // Use the base version as available version if installed version is mapped to be an approximate. versionKey.Version = installedVersionAsVersion.GetBaseVersion().ToString(); availableVersion = availableVersions->GetVersion(versionKey); // It's unexpected if the installed version is already mapped to some version. THROW_HR_IF(E_UNEXPECTED, !availableVersion); } else { versionKey.Version = installedVersionAsVersion.ToString(); availableVersion = availableVersions->GetVersion(versionKey); if (availableVersion) { checkFileHash = true; } } } if (!availableVersion) { // No installed version, or installed version not found in available versions, // then attempt to check installed status using latest version. availableVersion = availableVersions->GetLatestVersion(); THROW_HR_IF(E_UNEXPECTED, !availableVersion); } auto manifest = availableVersion->GetManifest(); for (auto const& installer : manifest.Installers) { InstallerInstalledStatus installerStatus; installerStatus.Installer = installer; // ARP related checks if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllAppsAndFeaturesEntryChecks)) { bool isMatchingInstaller = installedVersion && IsInstallerTypeCompatible(installedType, installer.EffectiveInstallerType()) && (installedScope == ScopeEnum::Unknown || installer.Scope == ScopeEnum::Unknown || installedScope == installer.Scope) && // Treat unknown scope as compatible (installedArchitecture == Utility::Architecture::Unknown || installer.Arch == Utility::Architecture::Neutral || installedArchitecture == installer.Arch) && // Treat unknown installed architecture as compatible (installedLocale.empty() || installer.Locale.empty() || !Locale::IsWellFormedBcp47Tag(installedLocale) || Locale::GetDistanceOfLanguage(installedLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch); // Treat invalid locale as compatible // ARP entry status if (WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntry)) { installerStatus.Status.emplace_back( InstalledStatusType::AppsAndFeaturesEntry, "", isMatchingInstaller ? WINGET_INSTALLED_STATUS_ARP_ENTRY_FOUND : WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND); } // ARP install location status if (isMatchingInstaller && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocation)) { installerStatus.Status.emplace_back( InstalledStatusType::AppsAndFeaturesEntryInstallLocation, installedLocation.u8string(), installedLocationStatus); } // ARP install location files if (isMatchingInstaller && installedLocationStatus == WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile)) { for (auto const& file : installer.InstallationMetadata.Files) { std::filesystem::path filePath = installedLocation / Utility::ConvertToUTF16(file.RelativeFilePath); auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); installerStatus.Status.emplace_back( InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile, filePath.u8string(), fileStatus); } } } // Default install location related checks if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllDefaultInstallLocationChecks) && installer.InstallationMetadata.HasData()) { auto defaultInstalledLocation = Filesystem::GetExpandedPath(installer.InstallationMetadata.DefaultInstallLocation); HRESULT defaultInstalledLocationStatus = CheckInstalledLocationStatus(defaultInstalledLocation); // Default install location status if (WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocation)) { installerStatus.Status.emplace_back( InstalledStatusType::DefaultInstallLocation, defaultInstalledLocation.u8string(), defaultInstalledLocationStatus); } // Default install location files if (defaultInstalledLocationStatus == WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND && WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocationFile)) { for (auto const& file : installer.InstallationMetadata.Files) { std::filesystem::path filePath = defaultInstalledLocation / Utility::ConvertToUTF16(file.RelativeFilePath); auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); installerStatus.Status.emplace_back( InstalledStatusType::DefaultInstallLocationFile, filePath.u8string(), fileStatus); } } } if (!installerStatus.Status.empty()) { result.emplace_back(std::move(installerStatus)); } } return result; } } std::vector CheckPackageInstalledStatus(const std::shared_ptr& package, InstalledStatusType checkTypes) { return CheckInstalledStatusInternal(package, checkTypes); } } ================================================ FILE: src/AppInstallerRepositoryCore/PackageTrackingCatalog.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/PackageTrackingCatalog.h" #include "PackageTrackingCatalogSourceFactory.h" #include "winget/Pin.h" #include "winget/RepositorySource.h" #include "Microsoft/SQLiteIndexSource.h" #include "AppInstallerDateTime.h" using namespace std::string_literals; using namespace AppInstaller::Repository::Microsoft; namespace AppInstaller::Repository { namespace { constexpr std::string_view c_PackageTrackingFileName = "installed.db"; constexpr std::string_view c_PackageTrackingCorruptedFileName = "installed-corrupted.db"; std::string CreateNameForCPL(const std::string& pathName) { return "PackageTrackingCPL_"s + pathName; } std::filesystem::path GetPackageTrackingFilePath(const std::string& pathName) { std::filesystem::path result = Runtime::GetPathTo(Runtime::PathName::LocalState); result /= pathName; result /= c_PackageTrackingFileName; return result; } // Call while holding the CrossProcessLock SQLiteIndex CreateOnlyTrackingIndex(const std::filesystem::path& trackingDB) { return SQLiteIndex::CreateNew(trackingDB.u8string(), SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); } // Call while holding the CrossProcessLock SQLiteIndex CreateOrOpenTrackingIndex(const std::filesystem::path& trackingDB) { if (!std::filesystem::exists(trackingDB)) { std::filesystem::create_directories(trackingDB.parent_path()); return CreateOnlyTrackingIndex(trackingDB); } else { try { // TODO: Check schema version and upgrade as necessary when there is a relevant new schema. // Could write this all now but it will be better tested when there is a new schema. return SQLiteIndex::Open(trackingDB.u8string(), SQLiteIndex::OpenDisposition::ReadWrite); } catch(...) { LOG_CAUGHT_EXCEPTION_MSG("Exception opening tracking catalog"); } // Move existing database and create a new one std::filesystem::path destination{ trackingDB }; destination.replace_filename(c_PackageTrackingCorruptedFileName); SQLite::SQLiteStorageBase::RenameSQLiteDatabase(trackingDB, destination, true); return CreateOnlyTrackingIndex(trackingDB); } } struct PackageTrackingCatalogSourceReference : public ISourceReference { PackageTrackingCatalogSourceReference(const SourceDetails& details) : m_details(details) {} SourceDetails& GetDetails() override { return m_details; } std::string GetIdentifier() override { return m_details.Identifier; } std::shared_ptr Open(IProgressCallback& callback) override { m_details.Arg = Utility::MakeSuitablePathPart(m_details.Data); std::filesystem::path trackingDB = GetPackageTrackingFilePath(m_details.Arg); Synchronization::CrossProcessLock lock(CreateNameForCPL(m_details.Arg)); if (!lock.Acquire(callback)) { return {}; } return std::make_shared(m_details, CreateOrOpenTrackingIndex(trackingDB)); } private: // Store the identifier of the source in the Data field. SourceDetails m_details; }; struct PackageTrackingCatalogSourceFactoryImpl : public ISourceFactory { std::string_view TypeName() const override final { return PackageTrackingCatalogSourceFactory::Type(); } std::shared_ptr Create(const SourceDetails& details) override final { THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, PackageTrackingCatalogSourceFactory::Type())); return std::make_shared(details); } bool Add(SourceDetails&, IProgressCallback&) override final { THROW_HR(E_NOTIMPL); } bool Update(const SourceDetails&, IProgressCallback&) override final { THROW_HR(E_NOTIMPL); } bool Remove(const SourceDetails& details, IProgressCallback& progress) override final { THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, PackageTrackingCatalogSourceFactory::Type())); std::string pathName = Utility::MakeSuitablePathPart(details.Data); Synchronization::CrossProcessLock lock(CreateNameForCPL(pathName)); if (!lock.Acquire(progress)) { return false; } std::filesystem::path trackingDB = GetPackageTrackingFilePath(pathName); if (std::filesystem::exists(trackingDB)) { std::filesystem::remove(trackingDB); } return true; } }; } struct PackageTrackingCatalog::implementation { std::shared_ptr Source; }; PackageTrackingCatalog::PackageTrackingCatalog() = default; PackageTrackingCatalog::PackageTrackingCatalog(const PackageTrackingCatalog&) = default; PackageTrackingCatalog& PackageTrackingCatalog::operator=(const PackageTrackingCatalog&) = default; PackageTrackingCatalog::PackageTrackingCatalog(PackageTrackingCatalog&&) noexcept = default; PackageTrackingCatalog& PackageTrackingCatalog::operator=(PackageTrackingCatalog&&) noexcept = default; PackageTrackingCatalog::~PackageTrackingCatalog() = default; PackageTrackingCatalog PackageTrackingCatalog::CreateForSource(const Source& source) { // Not a valid source for tracking const std::string sourceIdentifier = source.GetIdentifier(); if (sourceIdentifier.empty() || !source.ContainsAvailablePackages()) { THROW_HR(E_INVALIDARG); } // Create fake details for the source while stashing some information that might be helpful for debugging SourceDetails details; details.Type = PackageTrackingCatalogSourceFactory::Type(); details.Identifier = "*Tracking"; details.Name = "Tracking for "s + source.GetDetails().Name; details.Origin = SourceOrigin::PackageTracking; details.Data = sourceIdentifier; ProgressCallback dummyProgress; PackageTrackingCatalog result; result.m_implementation = std::make_shared(); result.m_implementation->Source = SourceCast(ISourceFactory::GetForType(details.Type)->Create(details)->Open(dummyProgress)); return result; } void PackageTrackingCatalog::RemoveForSource(const std::string& identifier) { if (identifier.empty()) { THROW_HR(E_INVALIDARG); } // Create details to pass to the factory; the identifier of the source is passed in the Data field. SourceDetails dummyDetails; dummyDetails.Type = PackageTrackingCatalogSourceFactory::Type(); dummyDetails.Data = identifier; ProgressCallback dummyProgress; ISourceFactory::GetForType(dummyDetails.Type)->Remove(dummyDetails, dummyProgress); } PackageTrackingCatalog::operator bool() const { return static_cast(m_implementation); } SearchResult PackageTrackingCatalog::Search(const SearchRequest& request) const { return m_implementation->Source->Search(request); } struct PackageTrackingCatalog::Version::implementation { SQLiteIndex::IdType Id; }; PackageTrackingCatalog::Version::Version(const Version&) = default; PackageTrackingCatalog::Version& PackageTrackingCatalog::Version::operator=(const Version&) = default; PackageTrackingCatalog::Version::Version(Version&&) noexcept = default; PackageTrackingCatalog::Version& PackageTrackingCatalog::Version::operator=(Version&&) noexcept = default; PackageTrackingCatalog::Version::~Version() = default; PackageTrackingCatalog::Version::Version(PackageTrackingCatalog& catalog, std::shared_ptr&& value) : m_catalog(&catalog), m_implementation(std::move(value)) {} void PackageTrackingCatalog::Version::SetMetadata(PackageVersionMetadata metadata, const Utility::NormalizedString& value) { auto& index = m_catalog->m_implementation->Source->GetIndex(); index.SetMetadataByManifestId(m_implementation->Id, metadata, value); } PackageTrackingCatalog::Version PackageTrackingCatalog::RecordInstall( Manifest::Manifest& manifest, const Manifest::ManifestInstaller& installer, bool isUpgrade) { // TODO: Store additional information from these if needed UNREFERENCED_PARAMETER(isUpgrade); auto& index = m_implementation->Source->GetIndex(); // Strip ARP version information from the manifest if it is present for (auto& arpRangeRemovedInstaller : manifest.Installers) { for (auto& arpRangeRemovedEntry : arpRangeRemovedInstaller.AppsAndFeaturesEntries) { arpRangeRemovedEntry.DisplayVersion.clear(); } } // Check for an existing manifest that matches this one (could be reinstalling) auto manifestIdOpt = index.GetManifestIdByManifest(manifest); if (manifestIdOpt) { index.UpdateManifest(manifest); } else { manifestIdOpt = index.AddManifest(manifest); } SQLiteIndex::IdType manifestId = manifestIdOpt.value(); // Write additional metadata for package tracking std::ostringstream strstr; strstr << Utility::GetCurrentUnixEpoch(); index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::TrackingWriteTime, strstr.str()); if (installer.RequireExplicitUpgrade) { index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::PinnedState, ToString(Pinning::PinType::PinnedByManifest)); } // Record installed architecture and locale if applicable index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledArchitecture, ToString(installer.Arch)); if (!installer.Locale.empty()) { index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledLocale, installer.Locale); } std::shared_ptr result = std::make_shared(); result->Id = manifestId; return { *this, std::move(result) }; } void PackageTrackingCatalog::RecordUninstall(const Utility::LocIndString& packageIdentifier) { auto& index = m_implementation->Source->GetIndex(); SearchRequest idSearch; idSearch.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, packageIdentifier.get()); auto searchResult = index.Search(idSearch); for (const auto& match : searchResult.Matches) { auto versions = index.GetVersionKeysById(match.first); for (const auto& version : versions) { index.RemoveManifestById(version.ManifestId); } } } #ifndef AICLI_DISABLE_TEST_HOOKS std::filesystem::path PackageTrackingCatalog::GetFilePath() const { return m_implementation->Source->GetIndex().GetContextData().Get(); } #endif std::unique_ptr PackageTrackingCatalogSourceFactory::Create() { return std::make_unique(); } } ================================================ FILE: src/AppInstallerRepositoryCore/PackageTrackingCatalogSourceFactory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "SourceFactory.h" using namespace std::string_view_literals; namespace AppInstaller::Repository { // Enable test to inject a custom tracking source by integrating the internal source creation into the standard flows. // If overriding, the opened ISource must be a SQLiteIndexSource or the code will fail. struct PackageTrackingCatalogSourceFactory { // Get the type string for this source. static constexpr std::string_view Type() { using namespace std::string_view_literals; return "Microsoft.PackageTracking"sv; } // Creates a source factory for this type. static std::unique_ptr Create(); }; } ================================================ FILE: src/AppInstallerRepositoryCore/PackageVersionSelection.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/PackageVersionSelection.h" #include "Public/winget/RepositorySource.h" #include "Public/winget/PinningData.h" namespace AppInstaller::Repository { namespace { std::shared_ptr GetAvailablePackageFromSource(const std::vector>& packages, const std::string_view sourceIdentifier) { for (const std::shared_ptr& package : packages) { if (sourceIdentifier == package->GetSource().GetIdentifier()) { return package; } } return {}; } struct AvailablePackageVersionCollection : public IPackageVersionCollection { AvailablePackageVersionCollection(const std::shared_ptr& composite, const std::shared_ptr& installedVersion) : m_packages(composite->GetAvailable()) { if (!installedVersion) { return; } m_channel = installedVersion->GetProperty(PackageVersionProperty::Channel); // Remove the packages that are not from the installed source. Source installedVersionSource = installedVersion->GetSource(); if (installedVersionSource && installedVersionSource.ContainsAvailablePackages()) { m_packages.erase(std::remove_if(m_packages.begin(), m_packages.end(), [&](const std::shared_ptr& p) { return installedVersionSource != p->GetSource(); }), m_packages.end()); } } std::vector GetVersionKeys() const override { std::vector result; for (const std::shared_ptr& package : m_packages) { std::vector versionKeys = package->GetVersionKeys(); std::copy(versionKeys.begin(), versionKeys.end(), std::back_inserter(result)); } // Remove all elements whose channel does not match the installed package. if (m_channel) { result.erase( std::remove_if(result.begin(), result.end(), [&](const PackageVersionKey& pvk) { return !Utility::ICUCaseInsensitiveEquals(pvk.Channel, m_channel.value()); }), result.end()); } // Put latest versions at the front; for versions available from multiple sources maintain the order they were added in std::stable_sort(result.begin(), result.end()); return result; } std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const override { // If there is a specific source, just use that package's result std::shared_ptr package; if (!versionKey.SourceId.empty()) { package = GetAvailablePackageFromSource(m_packages, versionKey.SourceId); } else if (m_packages.size() == 1) { package = m_packages[0]; } else if (versionKey.IsDefaultLatest()) { std::shared_ptr result; Utility::Version resultVersion; for (const auto& p : m_packages) { std::shared_ptr latest = p->GetLatestVersion(); Utility::Version version { latest->GetProperty(PackageVersionProperty::Version) }; if (!result || resultVersion < version) { result = std::move(latest); resultVersion = std::move(version); } } return result; } else { // Otherwise, find the first version that matches std::vector versions = GetVersionKeys(); for (const PackageVersionKey& key : versions) { if (key.IsMatch(versionKey)) { package = GetAvailablePackageFromSource(m_packages, key.SourceId); break; } } } return package ? package->GetVersion(versionKey) : nullptr; } std::shared_ptr GetLatestVersion() const override { return GetVersion({ "", "", m_channel.value_or("") }); } private: std::optional m_channel; std::vector> m_packages; }; } std::shared_ptr GetAvailableVersionsForInstalledVersion(const std::shared_ptr& composite) { return std::make_shared(composite, GetInstalledVersion(composite)); } std::shared_ptr GetAvailableVersionsForInstalledVersion( const std::shared_ptr& composite, const std::shared_ptr& installedVersion) { return std::make_shared(composite, installedVersion); } std::shared_ptr GetAllAvailableVersions(const std::shared_ptr& composite) { return GetAvailableVersionsForInstalledVersion(composite, nullptr); } std::shared_ptr GetInstalledVersion(const std::shared_ptr& composite) { auto installedPackage = composite->GetInstalled(); return installedPackage ? installedPackage->GetLatestVersion() : nullptr; } std::shared_ptr GetAvailablePackageFromSource(const std::shared_ptr& composite, const std::string_view sourceIdentifier) { return GetAvailablePackageFromSource(composite->GetAvailable(), sourceIdentifier); } LatestApplicableVersionData GetLatestApplicableVersion(const std::shared_ptr& composite) { using namespace AppInstaller::Pinning; LatestApplicableVersionData result; auto installedVersion = AppInstaller::Repository::GetInstalledVersion(composite); auto availableVersions = AppInstaller::Repository::GetAvailableVersionsForInstalledVersion(composite, installedVersion); PinningData pinningData{ PinningData::Disposition::ReadOnly }; auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, installedVersion); AppInstaller::Manifest::ManifestComparator::Options options; if (installedVersion) { GetManifestComparatorOptionsFromMetadata(options, installedVersion->GetMetadata()); } AppInstaller::Manifest::ManifestComparator manifestComparator{ options }; auto availableVersionKeys = availableVersions->GetVersionKeys(); for (const auto& availableVersionKey : availableVersionKeys) { auto availableVersion = availableVersions->GetVersion(availableVersionKey); if (installedVersion && !evaluator.IsUpdate(availableVersion)) { // Version too low or different channel for upgrade continue; } if (evaluator.EvaluatePinType(availableVersion) != AppInstaller::Pinning::PinType::Unknown) { // Pinned continue; } auto manifestComparatorResult = manifestComparator.GetPreferredInstaller(availableVersion->GetManifest()); if (!manifestComparatorResult.installer.has_value()) { // No applicable installer continue; } result.LatestApplicableVersion = availableVersion; if (installedVersion) { result.UpdateAvailable = true; } break; } return result; } void GetManifestComparatorOptionsFromMetadata(AppInstaller::Manifest::ManifestComparator::Options& options, const IPackageVersion::Metadata& metadata, bool includeAllowedArchitectures) { auto installedTypeItr = metadata.find(Repository::PackageVersionMetadata::InstalledType); if (installedTypeItr != metadata.end()) { options.CurrentlyInstalledType = Manifest::ConvertToInstallerTypeEnum(installedTypeItr->second); } auto installedScopeItr = metadata.find(Repository::PackageVersionMetadata::InstalledScope); if (installedScopeItr != metadata.end()) { options.CurrentlyInstalledScope = Manifest::ConvertToScopeEnum(installedScopeItr->second); } auto userIntentLocaleItr = metadata.find(Repository::PackageVersionMetadata::UserIntentLocale); if (userIntentLocaleItr != metadata.end()) { options.PreviousUserIntentLocale = userIntentLocaleItr->second; } auto installedLocaleItr = metadata.find(Repository::PackageVersionMetadata::InstalledLocale); if (installedLocaleItr != metadata.end()) { options.CurrentlyInstalledLocale = installedLocaleItr->second; } if (includeAllowedArchitectures) { auto userIntentItr = metadata.find(Repository::PackageVersionMetadata::UserIntentArchitecture); if (userIntentItr != metadata.end()) { // For upgrade, user intent from previous install is considered requirement options.AllowedArchitectures.emplace_back(Utility::ConvertToArchitectureEnum(userIntentItr->second)); } else { auto installedItr = metadata.find(Repository::PackageVersionMetadata::InstalledArchitecture); if (installedItr != metadata.end()) { // For upgrade, previous installed architecture should be considered first preference and is always allowed. // Then check settings requirements and preferences. options.AllowedArchitectures.emplace_back(Utility::ConvertToArchitectureEnum(installedItr->second)); } std::vector requiredArchitectures = Settings::User().Get(); std::vector optionalArchitectures = Settings::User().Get(); if (!requiredArchitectures.empty()) { // Required architecture list from settings if applicable options.AllowedArchitectures.insert(options.AllowedArchitectures.end(), requiredArchitectures.begin(), requiredArchitectures.end()); } else { // Preferred architecture list from settings if applicable, add Unknown to indicate allowing remaining applicable if (!optionalArchitectures.empty()) { options.AllowedArchitectures.insert(options.AllowedArchitectures.end(), optionalArchitectures.begin(), optionalArchitectures.end()); } options.AllowedArchitectures.emplace_back(Utility::Architecture::Unknown); } } } } std::optional GetSourcePriority(const std::shared_ptr& composite) { auto installed = composite->GetInstalled(); if (installed) { auto installedVersion = installed->GetLatestVersion(); if (installedVersion) { auto installedSource = installedVersion->GetSource(); if (installedSource.ContainsAvailablePackages()) { return installedSource.GetDetails().Priority; } } } auto available = composite->GetAvailable(); if (!available.empty()) { return available.front()->GetSource().GetDetails().Priority; } return std::nullopt; } } ================================================ FILE: src/AppInstallerRepositoryCore/PinningData.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/PinningData.h" #include "Microsoft/PinningIndex.h" #include "Public/winget/RepositorySource.h" using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; namespace AppInstaller::Pinning { namespace { // Evaluates the pinning state of a version for a single pin. PinType EvaluatePinnedStateForVersion( const Utility::Version& version, const std::optional& pin, PinBehavior behavior) { if (pin) { if (pin->GetType() == PinType::Blocking || (pin->GetType() == PinType::Pinning && behavior != PinBehavior::IncludePinned) || (pin->GetType() == PinType::Gating && !pin->GetGatedVersion().IsValidVersion(version))) { return pin->GetType(); } } return PinType::Unknown; } // Gets the pinned state for an available version that may have a pin, // and optionally an additional pin that could come from the installed version. // If both pins are present, we return the one that is the most strict. Pinning::PinType GetPinnedStateForVersion( const Utility::Version& version, const std::optional& availablePin, const std::optional& installedPin, PinBehavior behavior) { if (behavior == PinBehavior::IgnorePins) { return Pinning::PinType::Unknown; } return Stricter( EvaluatePinnedStateForVersion(version, availablePin, behavior), EvaluatePinnedStateForVersion(version, installedPin, behavior)); } } PinningData::PinningData() = default; PinningData::PinningData(const PinningData&) = default; PinningData& PinningData::operator=(const PinningData&) = default; PinningData::PinningData(PinningData&&) noexcept = default; PinningData& PinningData::operator=(PinningData&&) noexcept = default; PinningData::~PinningData() = default; PinningData::PinningData(Disposition disposition) { if (disposition == Disposition::ReadOnly) { m_database = PinningIndex::OpenIfExists(SQLiteStorageBase::OpenDisposition::Read); } else { m_database = PinningIndex::OpenOrCreateDefault(SQLiteStorageBase::OpenDisposition::ReadWrite); } } PinningData::operator bool() const { return IsDatabaseConnected(); } bool PinningData::IsDatabaseConnected() const { return static_cast(m_database); } void PinningData::AddOrUpdatePin(const Pin& pin) { THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected()); m_database->AddOrUpdatePin(pin); } void PinningData::RemovePin(const PinKey& pinKey) { THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected()); m_database->RemovePin(pinKey); } std::optional PinningData::GetPin(const PinKey& pinKey) { return IsDatabaseConnected() ? m_database->GetPin(pinKey) : std::nullopt; } std::vector PinningData::GetAllPins() { return IsDatabaseConnected() ? m_database->GetAllPins() : std::vector{}; } bool PinningData::ResetAllPins(std::string_view sourceId) { THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected()); return m_database->ResetAllPins(sourceId); } PinningData::PinStateEvaluator::PinStateEvaluator( PinBehavior behavior, std::shared_ptr database, const std::shared_ptr& installedVersion) : m_behavior(behavior), m_database(std::move(database)) { if (m_behavior == PinBehavior::IgnorePins || !installedVersion) { // Because the database isn't guaranteed to be present, align ignoring pins with there being no pins to ignore. // Also do not consider pins when there is no installed version. This is to remain consistent with the previous // implementation. If this is to be changed, more install paths will need to be do pinning checks to ensure // that one could, for instance, block the install of a package. m_database.reset(); } else if (m_database) { PinKey key = PinKey::GetPinKeyForInstalled(installedVersion->GetProperty(PackageVersionProperty::Id)); m_installedPin = m_database->GetPin(key); } if (installedVersion) { m_installedVersion = Utility::VersionAndChannel{ Utility::Version{ installedVersion->GetProperty(PackageVersionProperty::Version) }, Utility::Channel{ installedVersion->GetProperty(PackageVersionProperty::Channel) } }; } } PinningData::PinStateEvaluator::PinStateEvaluator(const PinStateEvaluator&) = default; PinningData::PinStateEvaluator& PinningData::PinStateEvaluator::operator=(const PinStateEvaluator&) = default; PinningData::PinStateEvaluator::PinStateEvaluator(PinStateEvaluator&&) noexcept = default; PinningData::PinStateEvaluator& PinningData::PinStateEvaluator::operator=(PinStateEvaluator&&) noexcept = default; PinningData::PinStateEvaluator::~PinStateEvaluator() = default; std::shared_ptr PinningData::PinStateEvaluator::GetLatestAvailableVersionForPins(const std::shared_ptr& package) { if (!m_database) { return package->GetLatestVersion(); } auto availableVersionKeys = package->GetVersionKeys(); // Skip until we find a version that isn't pinned for (const auto& availableVersion : availableVersionKeys) { std::shared_ptr packageVersion = package->GetVersion(availableVersion); if (EvaluatePinType(packageVersion) == Pinning::PinType::Unknown) { return packageVersion; } } return {}; } bool PinningData::PinStateEvaluator::IsUpdate(const std::shared_ptr& availableVersion) { if (m_installedVersion && availableVersion) { Utility::VersionAndChannel availableVersionAndChannel{ Utility::Version{ availableVersion->GetProperty(PackageVersionProperty::Version) }, Utility::Channel{ availableVersion->GetProperty(PackageVersionProperty::Channel) } }; return m_installedVersion->IsUpdatedBy(availableVersionAndChannel); } return false; } PinType PinningData::PinStateEvaluator::EvaluatePinType(const std::shared_ptr& packageVersion) { if (!m_database || !packageVersion) { return PinType::Unknown; } std::optional incomingPin; PinKey pinKey{ packageVersion->GetProperty(PackageVersionProperty::Id).get(), packageVersion->GetSource().GetIdentifier()}; auto itr = m_availablePins.find(pinKey); if (itr == m_availablePins.end()) { incomingPin = m_database->GetPin(pinKey); m_availablePins[pinKey] = incomingPin; } else { incomingPin = itr->second; } return GetPinnedStateForVersion(packageVersion->GetProperty(PackageVersionProperty::Version).get(), incomingPin, m_installedPin, m_behavior); } // Creates an object for use in evaluating pinning data for a given package PinningData::PinStateEvaluator PinningData::CreatePinStateEvaluator( PinBehavior behavior, const std::shared_ptr& installedVersion) { return { behavior, m_database, installedVersion }; } } ================================================ FILE: src/AppInstallerRepositoryCore/PropertySheet.props ================================================ ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/ARPCorrelation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller { namespace Manifest { struct Manifest; struct ManifestLocalization; } namespace Repository { struct IPackage; struct IPackageVersion; struct Source; } } namespace AppInstaller::Repository::Correlation { // Contains the { Id, Version, Channel } using ARPEntrySnapshot = std::tuple; // Struct holding all the data from an ARP entry we use for the correlation struct ARPEntry { ARPEntry(std::shared_ptr entry, bool isNewOrUpdated) : Entry(std::move(entry)), IsNewOrUpdated(isNewOrUpdated) {} // Data found in the ARP entry std::shared_ptr Entry; // Whether this entry changed with the current installation bool IsNewOrUpdated; }; // One of the possible options that could be chosen for correlation. struct CorrelationMeasure { // The value that the correlation algorithm assigned to the match with the package. double Measure{}; // The package that was measured. std::shared_ptr Package{}; }; // The result of a heuristics correlation attempt. struct ARPHeuristicsCorrelationResult { // Correlated package from ARP std::shared_ptr Package{}; // The reason for the correlation (for diagnostics). std::string Reason; // The correlation metrics and their associated ARP package information (for diagnostics). std::vector Measures; }; // The result of a correlation attempt. struct ARPCorrelationResult : public ARPHeuristicsCorrelationResult { // Number of ARP entries that are new or updated size_t ChangesToARP{}; // Number of ARP entries that match with the installed package size_t MatchesInARP{}; // Number of changed ARP entries that match the installed package size_t CountOfIntersectionOfChangesAndMatches{}; ARPCorrelationResult& operator=(ARPHeuristicsCorrelationResult&& other) { *static_cast(this) = std::move(other); return *this; } }; // Allows callers finer control over how the correlation result will be chosen. // The values appear in order of their application in the correlation algorithm, meaning that a later // setting that is set to true can be preempted by an earlier setting, if a correlation occurs with the // earlier setting. // The default values are chosen to reflect what is used after an install on a consumer system. struct ARPCorrelationSettings { // This setting controls whether the name and publisher normalization algorithm will be used for correlation. // When true, normalization will be the first choice for correlation. This means that a normalized name+publisher // match will result in correlation (unless there are multiple matches). // When false, normalization will only be used for the statistics (MatchesInARP), but the correlation result package // will not be based on normalization. bool AllowNormalization = true; // This settings controls whether a single changed ARP entry is sufficient to result in correlation. // When true, if only a single ARP entry is detected as new or changed, it will be chosen as the correlated result. bool AllowSingleChange = false; }; struct IARPMatchConfidenceAlgorithm { virtual ~IARPMatchConfidenceAlgorithm() = default; virtual void Init(const AppInstaller::Manifest::Manifest& manifest) = 0; virtual double ComputeConfidence(const ARPEntry& arpEntry) const = 0; // Returns an instance of the algorithm we will actually use. // We may use multiple instances/specializations for testing and experimentation. static IARPMatchConfidenceAlgorithm& Instance(); #ifndef AICLI_DISABLE_TEST_HOOKS static void OverrideInstance(IARPMatchConfidenceAlgorithm* algorithmOverride); static void ResetInstance(); #endif }; ARPHeuristicsCorrelationResult FindARPEntryForNewlyInstalledPackageWithHeuristics( const AppInstaller::Manifest::Manifest& manifest, const std::vector& arpEntries); ARPHeuristicsCorrelationResult FindARPEntryForNewlyInstalledPackageWithHeuristics( const AppInstaller::Manifest::Manifest& manifest, const std::vector& arpEntries, IARPMatchConfidenceAlgorithm& algorithm); // Holds data needed for ARP correlation, as well as functions to run correlation on the collected data. struct ARPCorrelationData { ARPCorrelationData() = default; virtual ~ARPCorrelationData() = default; // Captures the ARP state before the package installation. void CapturePreInstallSnapshot(); // Captures the ARP state differences after the package installation. void CapturePostInstallSnapshot(); // Correlates the given manifest against the data previously collected with capture calls. virtual ARPCorrelationResult CorrelateForNewlyInstalled(const Manifest::Manifest& manifest, const ARPCorrelationSettings& settings = {}); const std::vector& GetPreInstallSnapshot() const { return m_preInstallSnapshot; } private: std::vector m_preInstallSnapshot; Source m_postInstallSnapshotSource; std::vector m_postInstallSnapshot; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/ARPCorrelationAlgorithms.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Repository::Correlation { struct EmptyMatchConfidenceAlgorithm : public IARPMatchConfidenceAlgorithm { void Init(const AppInstaller::Manifest::Manifest&) override {} double ComputeConfidence(const ARPEntry&) const override { return 0; } }; // Algorithm that computes the match confidence by looking at the edit distance between // the sequences of words in the name and publisher. // The edit distance for this allows only adding or removing words, not editing them, // as that would make any two names too similar. struct WordsEditDistanceMatchConfidenceAlgorithm : public IARPMatchConfidenceAlgorithm { using WordSequence = std::vector; struct NameAndPublisher { NameAndPublisher(const WordSequence& name, const WordSequence& publisher); NameAndPublisher(WordSequence&& name, WordSequence&& publisher); WordSequence Name; WordSequence Publisher; WordSequence NamePublisher; }; void Init(const AppInstaller::Manifest::Manifest& manifest) override; double ComputeConfidence(const ARPEntry& arpEntry) const override; private: WordSequence PrepareString(std::string_view s) const; WordSequence NormalizeAndPrepareName(std::string_view name) const; WordSequence NormalizeAndPreparePublisher(std::string_view publisher) const; AppInstaller::Utility::NameNormalizer m_normalizer{ AppInstaller::Utility::NormalizationVersion::InitialPreserveWhiteSpace }; std::vector m_namesAndPublishers; // Parameters for the algorithm // How much weight to give to the string matching score. // The rest is assigned by whether the entry is new. const double m_stringMatchingWeight = 0.8; // How much weight to give to the matching score of the package name; // the rest is assigned to the publisher score. const double m_nameMatchingScoreWeight = 2. / 3.; // Minimum score needed in the name for us to accept a match. // This prevents us from being misled by a long match in the publisher. const double m_nameMatchingScoreMinThreshold = 0.2; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/Checkpoint.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include using namespace AppInstaller::Repository::Microsoft; namespace AppInstaller::Checkpoints { enum AutomaticCheckpointData { ClientVersion, Command, Arguments, ResumeCount }; struct CheckpointManager; // A representation of a row in the Checkpoint table. template struct Checkpoint { static_assert(std::is_enum::value); friend CheckpointManager; std::vector GetCheckpointDataTypes() { return m_checkpointDatabase->GetDataTypes(m_checkpointId); } // Returns a boolean value indicating whether the field name exists. bool Has(T dataType, const std::string& fieldName) { return m_checkpointDatabase->HasDataField(m_checkpointId, dataType, fieldName); } // Gets all available field names. std::vector GetFieldNames(T dataType) { return m_checkpointDatabase->GetDataFieldNames(m_checkpointId, dataType); } // Sets a single field value for a data type. void Set(T dataType, const std::string& fieldName, const std::string& value) { m_checkpointDatabase->SetDataValue(m_checkpointId, dataType, fieldName, { value }); } // Sets multiple field values for a data type. void SetMany(T dataType, const std::string& fieldName, const std::vector& values) { m_checkpointDatabase->SetDataValue(m_checkpointId, dataType, fieldName, values); } // Update a single existing field value for a data type. void Update(T dataType, const std::string& fieldName, const std::string& value) { m_checkpointDatabase->UpdateDataValue(m_checkpointId, dataType, fieldName, { value }); } // Gets a single field value for a data type. std::string Get(T dataType, const std::string& fieldName) { return m_checkpointDatabase->GetDataFieldSingleValue(m_checkpointId, dataType, fieldName); } // Gets multiple field values for a data type. std::vector GetMany(T dataType, const std::string& fieldName) { return m_checkpointDatabase->GetDataFieldMultiValue(m_checkpointId, dataType, fieldName); } private: Checkpoint(std::shared_ptr checkpointDatabase, AppInstaller::Repository::Microsoft::CheckpointDatabase::IdType checkpointId) : m_checkpointDatabase(checkpointDatabase), m_checkpointId(checkpointId){}; AppInstaller::Repository::Microsoft::CheckpointDatabase::IdType m_checkpointId; std::shared_ptr m_checkpointDatabase; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/CheckpointDatabase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Repository::Microsoft { namespace Schema { struct ICheckpointDatabase; } struct CheckpointDatabase : SQLite::SQLiteStorageBase { // An id that refers to a specific Checkpoint. using IdType = SQLite::rowid_t; CheckpointDatabase(const CheckpointDatabase&) = delete; CheckpointDatabase& operator=(const CheckpointDatabase&) = delete; CheckpointDatabase(CheckpointDatabase&&); CheckpointDatabase& operator=(CheckpointDatabase&&); // Create a new checkpoint database. static std::shared_ptr CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); // Opens an existing checkpoint database. static std::shared_ptr Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}); // Returns a value indicating whether the database is empty. bool IsEmpty(); // Adds a new checkpoint name to the checkpoint table. IdType AddCheckpoint(std::string_view checkpointName); // Returns all checkpoint ids in descending (newest at the front) order. std::vector GetCheckpointIds(); // Returns a boolean value indicating a field exists for a checkpoint data type. bool HasDataField(IdType checkpointId, int type, const std::string& name); // Returns the available data types for a checkpoint id. std::vector GetDataTypes(IdType checkpointId); // Returns the available field names for a checkpoint data. std::vector GetDataFieldNames(IdType checkpointId, int dataType); // Sets the value(s) for a data type and field. void SetDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector& values); // Updates the value(s) for a data type and field. void UpdateDataValue(IdType checkpointId, int dataType, const std::string& field, const std::vector& values); // Gets a single value for a data type field. std::string GetDataFieldSingleValue(IdType checkpointId, int dataType, const std::string& field); // Gets multiple values for a data type field. std::vector GetDataFieldMultiValue(IdType checkpointId, int dataType, const std::string& field); // Removes the value(s) for a data type. void RemoveDataType(IdType checkpointId, int dataType); private: // Constructor used to open an existing index. CheckpointDatabase(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); // Constructor used to create a new index. CheckpointDatabase(const std::string& target, SQLite::Version version); std::unique_ptr m_interface; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/IconExtraction.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Repository { struct ExtractedIconInfo { std::vector IconContent; std::vector IconSha256; AppInstaller::Manifest::IconFileTypeEnum IconFileType = AppInstaller::Manifest::IconFileTypeEnum::Unknown; AppInstaller::Manifest::IconThemeEnum IconTheme = AppInstaller::Manifest::IconThemeEnum::Unknown; AppInstaller::Manifest::IconResolutionEnum IconResolution = AppInstaller::Manifest::IconResolutionEnum::Unknown; }; // The usage of iconIndex is same as usage in ExtractIconEx at // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-extracticonexw // The icon index usage is basically: // 0, 1, 2 means zero-based index of the first icon to extract. // -1, -2, -3 means extract icon whose resource name is 1, 2, 3 etc std::vector ExtractIconFromBinaryFile(const std::filesystem::path binaryPath, int iconIndex = 0); // Extracts the app icon given the app's product code. // This method uses similar logic for retrieving icon as app list in settings page. // This method returns empty if only default icons would be picked. // This method returns contents of .ico icons. std::vector ExtractIconFromArpEntry(const std::string& productCode, AppInstaller::Manifest::ScopeEnum scope = AppInstaller::Manifest::ScopeEnum::Unknown); } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/InstalledFilesCorrelation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Repository::Correlation { // TODO: This definition could be moved to Manifest when winget supports launch scenarios. struct InstalledStartupLinkFile { // Relative file path to the startup menu folder. // Same installers write the links to user startup folder or machine startup folder depending on the scope // the installers were running for. So only relative file paths were collected. And seems enough. AppInstaller::Manifest::string_t RelativeFilePath; // Heuristic startup link type. AppInstaller::Manifest::InstalledFileTypeEnum FileType = AppInstaller::Manifest::InstalledFileTypeEnum::Unknown; }; struct InstallationMetadata { // Installed files metadata. Currently only capturing files pointed by a startup link. AppInstaller::Manifest::InstallationMetadataInfo InstalledFiles; // Startup links metadata. std::vector StartupLinkFiles; }; struct InstalledFilesCorrelation { // Constructor initializes the file watchers. InstalledFilesCorrelation(); virtual ~InstalledFilesCorrelation() = default; // Start the file watcher before the package installation. virtual void StartFileWatcher(); // Stop the file watcher after the package installation. virtual void StopFileWatcher(); // Correlates the given manifest against the data previously collected with capture calls. virtual InstallationMetadata CorrelateForNewlyInstalled( const Manifest::Manifest& manifest, const std::string& arpInstallLocation); private: struct FileWatcherFiles { // FileWatcher folder base. std::filesystem::path Folder; // List of files represented as relative file path to the base folder. std::vector Files; }; std::vector m_fileWatchers; std::vector m_files; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/InstalledStatus.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Repository { // Defines the installed status check type. enum class InstalledStatusType : uint32_t { // None is checked. None = 0x0, // Check Apps and Features entry. AppsAndFeaturesEntry = 0x0001, // Check Apps and Features entry install location if applicable. AppsAndFeaturesEntryInstallLocation = 0x0002, // Check Apps and Features entry install location with installed files if applicable. AppsAndFeaturesEntryInstallLocationFile = 0x0004, // Check default install location if applicable. DefaultInstallLocation = 0x0008, // Check default install location with installed files if applicable. DefaultInstallLocationFile = 0x0010, // Below are helper values for calling CheckInstalledStatus as input. // AppsAndFeaturesEntry related checks AllAppsAndFeaturesEntryChecks = AppsAndFeaturesEntry | AppsAndFeaturesEntryInstallLocation | AppsAndFeaturesEntryInstallLocationFile, // DefaultInstallLocation related checks AllDefaultInstallLocationChecks = DefaultInstallLocation | DefaultInstallLocationFile, // All checks AllChecks = AllAppsAndFeaturesEntryChecks | AllDefaultInstallLocationChecks, }; DEFINE_ENUM_FLAG_OPERATORS(InstalledStatusType); // Struct representing an individual installed status. struct InstalledStatus { // The installed status type. InstalledStatusType Type = InstalledStatusType::None; // The installed status path. Utility::NormalizedString Path; // The installed status result. HRESULT Status; InstalledStatus(InstalledStatusType type, Utility::NormalizedString path, HRESULT status) : Type(type), Path(std::move(path)), Status(status) {} }; // Struct representing installed status from an installer. struct InstallerInstalledStatus { Manifest::ManifestInstaller Installer; std::vector Status; }; // Checks installed status of a package. std::vector CheckPackageInstalledStatus(const std::shared_ptr& package, InstalledStatusType checkTypes = InstalledStatusType::AllChecks); } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/InstallerMetadataCollectionContext.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace AppInstaller::Repository::Metadata { // The overall metadata that we collect. struct ProductMetadata { ProductMetadata() = default; // Removes all stored data. void Clear(); // Load the metadata from an existing JSON blob. void FromJson(const web::json::value& json); // Create a JSON value for the metadata using the given schema version. web::json::value ToJson(const Utility::Version& schemaVersion, size_t maximumSizeInBytes); // Copies the metadata from the source. If the given submission identifier does not match // the source, it's data is moved to historical. void CopyFrom(const ProductMetadata& source, std::string_view submissionIdentifier); // The installer specific metadata that we collect. struct InstallerMetadata { friend ProductMetadata; // 1.0 std::string SubmissionIdentifier; std::vector AppsAndFeaturesEntries; // 1.1 // If Scope value is empty, the value is not set before. If the value is Unknown, a conflicting value is encountered. std::string Scope; // 1.2 // If std::nullopt, the value is not set before. If the value is empty(i.e. !HasData()), a conflicting value is encountered. std::optional InstalledFiles; // If std::nullopt, the value is not set before. If the vector is empty, conflicting values are encountered. std::optional> StartupLinkFiles; // Extracted icons std::vector Icons; }; // Metadata from previous product revisions. struct HistoricalMetadata { // 1.0 Utility::Version ProductVersionMin; Utility::Version ProductVersionMax; std::set Names; std::set Publishers; std::set ProductCodes; std::set UpgradeCodes; }; // 1.0 Utility::Version SchemaVersion; Utility::Version ProductVersionMin; Utility::Version ProductVersionMax; // Map from installer hash to metadata std::map InstallerMetadataMap; std::vector HistoricalMetadataList; private: void FromJson_1_N(const web::json::value& json); web::json::value ToJson_1_N(); // Removes the historical data with the oldest version. // Returns true if something was removed; false it not. bool DropOldestHistoricalData(); }; // Contains the functions and data used for collecting metadata from installers. struct InstallerMetadataCollectionContext { InstallerMetadataCollectionContext(); InstallerMetadataCollectionContext( std::unique_ptr correlationData, std::unique_ptr installedFilesCorrelation, const std::wstring& json); InstallerMetadataCollectionContext(const InstallerMetadataCollectionContext&) = delete; InstallerMetadataCollectionContext& operator=(const InstallerMetadataCollectionContext&) = delete; InstallerMetadataCollectionContext(InstallerMetadataCollectionContext&&) = default; InstallerMetadataCollectionContext& operator=(InstallerMetadataCollectionContext&&) = default; // Create from various forms of JSON input to prevent type collisions on constructor. static std::unique_ptr FromFile(const std::filesystem::path& file, const std::filesystem::path& logFile); static std::unique_ptr FromURI(std::wstring_view uri, const std::filesystem::path& logFile); static std::unique_ptr FromJSON(const std::wstring& json, const std::filesystem::path& logFile); // Completes the collection, writing to the given location. void Complete(const std::filesystem::path& output); // Completes the collection, writing to the given location. void Complete(std::ostream& output); static std::wstring Merge(const std::wstring& json, size_t maximumSizeInBytes, const std::filesystem::path& logFile); private: // Initializes the context runtime, including the log file if provided. static std::unique_ptr InitializeLogging(ThreadLocalStorage::WingetThreadGlobals& threadGlobals, const std::filesystem::path& logFile); std::unique_ptr InitializeLogging(const std::filesystem::path& logFile); // Sets the collection context input and the preinstall state. void InitializePreinstallState(const std::wstring& json); // Creates the output ProductMetadata and diagnostics objects for output void ComputeOutputData(); // Callers should set the thread globals before calling this. void CompleteWithThreadGlobalsSet(std::ostream& output); // Parse version 1.0 of input JSON void ParseInputJson_1_0(web::json::value& input); // Create version 1.0 of output JSON web::json::value CreateOutputJson_1_0(); // Determines whether an error has occurred in the context. bool ContainsError() const; // Collects information from the exception for error reporting. void CollectErrorDataFromException(std::exception_ptr exception); // Create version 1.0 of error JSON web::json::value CreateErrorJson_1_0(); // Merge using merge input version 1.0 static web::json::value Merge_1_0(web::json::value& input, size_t maximumSizeInBytes); ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; // Parsed input Utility::Version m_inputVersion; Utility::Version m_supportedMetadataVersion; ProductMetadata m_currentMetadata; web::json::value m_submissionData; std::string m_submissionIdentifier; std::string m_installerHash; Manifest::Manifest m_incomingManifest; std::unique_ptr m_correlationData; std::unique_ptr m_installedFilesCorrelation; // Output data enum class OutputStatus { // Version 1.0 status values Unknown, Success, Error, LowConfidence, }; // Convert status to a JSON string value static utility::string_t ToString(OutputStatus status); OutputStatus m_outputStatus = OutputStatus::Unknown; ProductMetadata m_outputMetadata; web::json::value m_outputDiagnostics; // Error data storage HRESULT m_errorHR = S_OK; std::string m_errorText; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/ManifestJSONParser.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Repository::JSON { // Exposes functions for parsing JSON REST responses to manifest requests. struct ManifestJSONParser { ManifestJSONParser(const Utility::Version& responseSchemaVersion); ManifestJSONParser(const ManifestJSONParser&) = delete; ManifestJSONParser& operator=(const ManifestJSONParser&) = delete; ManifestJSONParser(ManifestJSONParser&&) noexcept; ManifestJSONParser& operator=(ManifestJSONParser&&) noexcept; ~ManifestJSONParser(); // Deserializes the manifests from the REST response object root. // May potentially contain multiple versions of the same package. std::vector Deserialize(const web::json::value& response) const; // Deserializes the manifests from the Data field of the REST response object. // May potentially contain multiple versions of the same package. std::vector DeserializeData(const web::json::value& data) const; // Deserializes the AppsAndFeaturesEntries node, returning the set of values below it. std::vector DeserializeAppsAndFeaturesEntries(const web::json::array& data) const; // Deserializes the locale node; returning an object if a proper locale was found. std::optional DeserializeLocale(const web::json::value& locale) const; // Deserializes the InstallationMetadata node; returning an object if a proper InstallationMetadata was found. std::optional DeserializeInstallationMetadata(const web::json::value& installationMetadata) const; private: struct impl; std::unique_ptr m_pImpl; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/PackageTrackingCatalog.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #ifndef AICLI_DISABLE_TEST_HOOKS #include #endif namespace AppInstaller::Repository { struct Source; struct SearchRequest; struct SearchResult; enum class PackageVersionMetadata; // A catalog for tracking package actions from a given source. struct PackageTrackingCatalog { friend Source; PackageTrackingCatalog(); PackageTrackingCatalog(const PackageTrackingCatalog&); PackageTrackingCatalog& operator=(const PackageTrackingCatalog&); PackageTrackingCatalog(PackageTrackingCatalog&&) noexcept; PackageTrackingCatalog& operator=(PackageTrackingCatalog&&) noexcept; ~PackageTrackingCatalog(); // Removes the package tracking catalog for a given source identifier. static void RemoveForSource(const std::string& identifier); // Determines if the current object holds anything. operator bool() const; // Execute a search against the catalog. // Note that the packages in the results have the versions under "available" in order to // expose all versions contained therein (in the event that this is deemed useful at some point). SearchResult Search(const SearchRequest& request) const; // Enables more granular control over the metadata in the tracking catalog if necessary. struct Version { friend PackageTrackingCatalog; Version(const Version&); Version& operator=(const Version&); Version(Version&&) noexcept; Version& operator=(Version&&) noexcept; ~Version(); // Set the given metadata value. void SetMetadata(PackageVersionMetadata metadata, const Utility::NormalizedString& value); private: struct implementation; Version(PackageTrackingCatalog& catalog, std::shared_ptr&& value); std::shared_ptr m_implementation; PackageTrackingCatalog* m_catalog; }; // Records an installation of the given package. Version RecordInstall(Manifest::Manifest& manifest, const Manifest::ManifestInstaller& installer, bool isUpgrade); // Records an uninstall of the given package. void RecordUninstall(const Utility::LocIndString& packageIdentifier); #ifndef AICLI_DISABLE_TEST_HOOKS // Gets the path to the database file. std::filesystem::path GetFilePath() const; #endif protected: // Creates or opens the tracking catalog for the given source. static PackageTrackingCatalog CreateForSource(const Source& source); private: struct implementation; std::shared_ptr m_implementation; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/PackageVersionSelection.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Repository { // Gets an IPackageVersionCollection that represents the available package versions for the installed version. // If we have tracking data, will remove packages not from the tracked source. Will also remove versions that do not correspond to the tracked channel. // This function uses the latest installed version as a temporary convenience until side-by-side is implemented. std::shared_ptr GetAvailableVersionsForInstalledVersion(const std::shared_ptr& composite); // Gets an IPackageVersionCollection that represents the available package versions for the given installed version. std::shared_ptr GetAvailableVersionsForInstalledVersion( const std::shared_ptr& composite, const std::shared_ptr& installedVersion); // Equivalent to `GetAvailableVersionsForInstalledVersion(composite, nullptr)` to make the intent more clear that the caller wants to ignore any installed // package information. std::shared_ptr GetAllAvailableVersions(const std::shared_ptr& composite); // Gets the installed version, or a null if there isn't one. std::shared_ptr GetInstalledVersion(const std::shared_ptr& composite); // Gets the available IPackage corresponding to the given source identifier. std::shared_ptr GetAvailablePackageFromSource(const std::shared_ptr& composite, const std::string_view sourceIdentifier); struct LatestApplicableVersionData { std::shared_ptr LatestApplicableVersion; bool UpdateAvailable = false; }; // Determines the default install version and whether an update is available. LatestApplicableVersionData GetLatestApplicableVersion(const std::shared_ptr& composite); // Fills the options from the given metadata, optionally including the allowed architectures. void GetManifestComparatorOptionsFromMetadata(AppInstaller::Manifest::ManifestComparator::Options& options, const IPackageVersion::Metadata& metadata, bool includeAllowedArchitectures = true); // Gets the source priority for a given composite package, taking into account installed relationships. std::optional GetSourcePriority(const std::shared_ptr& composite); } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/PinningData.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Repository::Microsoft { struct PinningIndex; } namespace AppInstaller::Pinning { // Possible ways to consider pins when getting a package's available versions enum class PinBehavior { // Ignore pins, returns all available versions. IgnorePins, // Include available versions for packages with a Pinning pin. // Blocking pins and Gating pins still respected. IncludePinned, // Respect all the types of pins. ConsiderPins, }; // The public representation of the pinning database. struct PinningData { // Creates an empty pinning data. PinningData(); // Enum to make the pinning data disposition clear in the caller. enum class Disposition { // The data can only be read. ReadOnly, // The data can be read and written. ReadWrite, }; // Creates a usable pinning data with the given read/write capability. PinningData(Disposition disposition); PinningData(const PinningData&); PinningData& operator=(const PinningData&); PinningData(PinningData&&) noexcept; PinningData& operator=(PinningData&&) noexcept; ~PinningData(); // Determines if the pinning database is opened operator bool() const; bool IsDatabaseConnected() const; // Pass through functions to the index itself void AddOrUpdatePin(const Pin& pin); void RemovePin(const PinKey& pinKey); std::optional GetPin(const PinKey& pinKey); std::vector GetAllPins(); bool ResetAllPins(std::string_view sourceId = {}); // A type used for evaluating the pinning state for a given package. struct PinStateEvaluator { PinStateEvaluator( PinBehavior behavior, std::shared_ptr database, const std::shared_ptr& installedVersion); PinStateEvaluator(const PinStateEvaluator&); PinStateEvaluator& operator=(const PinStateEvaluator&); PinStateEvaluator(PinStateEvaluator&&) noexcept; PinStateEvaluator& operator=(PinStateEvaluator&&) noexcept; ~PinStateEvaluator(); // Gets the latest available package version that fits within the pinning restrictions. // This should be the package object that contains available versions associated with the installed version for which this evaluator was created. std::shared_ptr GetLatestAvailableVersionForPins(const std::shared_ptr& package); // Determines if the given version is an update to the installed version that this object was created with. // This should be a version associated with the installed version for which this evaluator was created. bool IsUpdate(const std::shared_ptr& availableVersion); // Determines the pin type to apply to the given version. PinType EvaluatePinType(const std::shared_ptr& packageVersion); private: PinBehavior m_behavior; std::shared_ptr m_database; std::optional m_installedPin; std::optional m_installedVersion; // Cache pins for available version to reduce database lookups. std::map> m_availablePins; }; // Creates an object for use in evaluating pinning data for a given package PinStateEvaluator CreatePinStateEvaluator( PinBehavior behavior, const std::shared_ptr& installedVersion); private: std::shared_ptr m_database; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/PortableIndex.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include using namespace AppInstaller::Portable; namespace AppInstaller::Repository::Microsoft { namespace Schema { struct IPortableIndex; } struct PortableIndex : SQLite::SQLiteStorageBase { // An id that refers to a specific portable file. using IdType = SQLite::rowid_t; PortableIndex(const PortableIndex&) = delete; PortableIndex& operator=(const PortableIndex&) = delete; PortableIndex(PortableIndex&&); PortableIndex& operator=(PortableIndex&&); ~PortableIndex(); // Creates a new PortableIndex database of the given version. static PortableIndex CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); // Opens an existing PortableIndex database. static PortableIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}); IdType AddPortableFile(const Portable::PortableFileEntry& file); void RemovePortableFile(const Portable::PortableFileEntry& file); bool UpdatePortableFile(const Portable::PortableFileEntry& file); void AddOrUpdatePortableFile(const Portable::PortableFileEntry& file); std::vector GetAllPortableFiles(); bool Exists(const Portable::PortableFileEntry& file); bool IsEmpty(); private: // Constructor used to open an existing index. PortableIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); // Constructor used to create a new index. PortableIndex(const std::string& target, SQLite::Version version); // Creates the IPortableIndex interface object for this version. std::unique_ptr CreateIPortableIndex() const; std::unique_ptr m_interface; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace AppInstaller::Repository { struct Source; // The type of matching to perform during a search. // The values must be declared in order of preference in search results. enum class MatchType { Exact = 0, CaseInsensitive, StartsWith, Fuzzy, Substring, FuzzySubstring, Wildcard, }; // Convert a MatchType to a string. std::string_view ToString(MatchType type); // The field to match on. // The values must be declared in order of preference in search results. enum class PackageMatchField { Id = 0, Name, Moniker, Command, Tag, PackageFamilyName, ProductCode, UpgradeCode, NormalizedNameAndPublisher, Market, Unknown = 9999 }; // Convert a PackageMatchField to a string. std::string_view ToString(PackageMatchField matchField); // Parse a string to PackageMatchField. PackageMatchField StringToPackageMatchField(std::string_view field); // A single match to be performed during a search. struct RequestMatch { MatchType Type; Utility::NormalizedString Value; std::optional Additional; RequestMatch(MatchType t) : Type(t) {} RequestMatch(MatchType t, Utility::NormalizedString& v) : Type(t), Value(v) {} RequestMatch(MatchType t, const Utility::NormalizedString& v) : Type(t), Value(v) {} RequestMatch(MatchType t, Utility::NormalizedString&& v) : Type(t), Value(std::move(v)) {} RequestMatch(MatchType t, std::string_view v1, std::string_view v2) : Type(t), Value(v1), Additional(Utility::NormalizedString{ v2 }) {} }; // A match on a specific field to be performed during a search. struct PackageMatchFilter : public RequestMatch { PackageMatchField Field; PackageMatchFilter(PackageMatchField f, MatchType t) : RequestMatch(t), Field(f) { EnsureRequiredValues(); } PackageMatchFilter(PackageMatchField f, MatchType t, Utility::NormalizedString& v) : RequestMatch(t, v), Field(f) { EnsureRequiredValues(); } PackageMatchFilter(PackageMatchField f, MatchType t, const Utility::NormalizedString& v) : RequestMatch(t, v), Field(f) { EnsureRequiredValues(); } PackageMatchFilter(PackageMatchField f, MatchType t, Utility::NormalizedString&& v) : RequestMatch(t, std::move(v)), Field(f) { EnsureRequiredValues(); } PackageMatchFilter(PackageMatchField f, MatchType t, std::string_view v1, std::string_view v2) : RequestMatch(t, v1, v2), Field(f) { EnsureRequiredValues(); } protected: void EnsureRequiredValues() { // Ensure that the second value always exists when it should if (Field == PackageMatchField::NormalizedNameAndPublisher && !Additional) { Additional = Utility::NormalizedString{}; } } }; // The search purpose of the search request. enum class SearchPurpose { // Default search purpose. Default, // The result is used for correlation to an installed package. CorrelationToInstalled, // The result is used for correlation to an available package. CorrelationToAvailable, }; // Container for data used to filter the available manifests in a source. // It can be thought of as: // (Query || Inclusions...) && Filters... // If Query and Inclusions are both empty, the starting data set will be the entire database. // Everything && Filters... struct SearchRequest { // The generic query matches against a source defined set of fields. std::optional Query; // Specific fields used to include more data. // If Query is defined, this can add more rows afterward. // If Query is not defined, this is the only set of data included. std::vector Inclusions; // Specific fields used to filter the data further. std::vector Filters; // The search purpose of the search request. SearchPurpose Purpose = SearchPurpose::Default; // The maximum number of results to return. // The default of 0 will place no limit. size_t MaximumResults{}; // Returns a value indicating whether this request is for all available data. bool IsForEverything() const; // Returns a string summarizing the search request. std::string ToString() const; }; // A property of a package version. enum class PackageVersionProperty { Id, Name, SourceIdentifier, SourceName, Version, Channel, RelativePath, // Returned in hexadecimal format ManifestSHA256Hash, Publisher, ArpMinVersion, ArpMaxVersion, Moniker, }; // A property of a package version that can have multiple values. enum class PackageVersionMultiProperty { // The package family names (PFN) associated with the package version PackageFamilyName, // The product codes associated with the package version. ProductCode, // The upgrade codes associated with the package version. UpgradeCode, // TODO: Fully implement these 3; the data is not yet in the index source (name and publisher are hacks and locale is not present) // For future usage of these, be aware of the limitations. // The package names for the version; ideally these would match in number and order with both Publisher and Locale. Name, // The publisher values for the version; ideally these would match in number and order with both Name and Locale. Publisher, // The locale of the matching Name and Publisher values; ideally these would match in number and order with both Name and Publisher. // May be empty if there is only a single value for Name and Publisher. Locale, // The tags associated with a package version. Tag, // The commands associated with a package version. Command, }; // A metadata item of a package version. These values are persisted and cannot be changed. enum class PackageVersionMetadata : int32_t { // The InstallerType of an installed package InstalledType, // The Scope of an installed package InstalledScope, // The system path where the package is installed InstalledLocation, // The standard uninstall command; which may be interactive StandardUninstallCommand, // An uninstall command that should be non-interactive SilentUninstallCommand, // The publisher of the package Publisher, // The locale of the package InstalledLocale, // The write time for the given version TrackingWriteTime, // The Architecture of an installed package InstalledArchitecture, // The pinned state of the installed package // As a package can have multiple pins for multiple sources, this is the strictest pin PinnedState, // The Architecture of user intent UserIntentArchitecture, // The locale of user intent UserIntentLocale, // The standard modify command; which may be interactive StandardModifyCommand, // No Modify flag NoModify, // No Repair flag NoRepair, }; // Convert a PackageVersionMetadata to a string. std::string_view ToString(PackageVersionMetadata pvm); // A single package version. struct IPackageVersion { using Metadata = std::map; virtual ~IPackageVersion() = default; // Gets a property of this package version. virtual Utility::LocIndString GetProperty(PackageVersionProperty property) const = 0; // Gets a property of this package version that can have multiple values. virtual std::vector GetMultiProperty(PackageVersionMultiProperty property) const = 0; // Gets the manifest of this package version. virtual Manifest::Manifest GetManifest() = 0; // Gets the source where this package version is from. virtual Source GetSource() const = 0; // Gets any metadata associated with this package version. // Primarily stores data on installed packages. virtual Metadata GetMetadata() const = 0; }; // A key to identify a package version within a package. struct PackageVersionKey { PackageVersionKey() = default; PackageVersionKey(std::string sourceId, Utility::NormalizedString version, Utility::NormalizedString channel) : SourceId(std::move(sourceId)), Version(std::move(version)), Channel(std::move(channel)) {} // The source id that this version came from. std::string SourceId; // The version. Utility::NormalizedString Version; // The channel. Utility::NormalizedString Channel; bool operator<(const PackageVersionKey& other) const { // Sort using only the version and channel. // The order for the sources depends on the context. return Utility::VersionAndChannel({ Version }, { Channel }) < Utility::VersionAndChannel({ other.Version }, { other.Channel }); } // Determines if a well defined key (this one) is matched by the provided key. // The provided key may use empty values to indicate no specific matching requirements. bool IsMatch(const PackageVersionKey& other) const; // Determines if this version is the simple "latest" targeting version (version and channel are both empty). bool IsDefaultLatest() const; }; // A property of a package. enum class PackageProperty { Id, Name, }; // A property of a package that can have multiple values. enum class PackageMultiProperty { // The package family names (PFN) associated with the package. PackageFamilyName, // The product codes associated with the package. ProductCode, // The upgrade codes associated with the package. UpgradeCode, // The normalized names for the package. NormalizedName, // The normalized publisher names for the package. NormalizedPublisher, // The tags associated with the package. Tag, // The commands associated with the package. Command, }; // Maps the package multi-property value to its package version multi-property value for internal use. PackageVersionMultiProperty PackageMultiPropertyToPackageVersionMultiProperty(PackageMultiProperty property); // To allow for runtime casting from IPackage to the specific types, this enum contains all of the IPackage implementations. enum class IPackageType { TestPackage, RestPackage, SQLitePackage1, SQLitePackage2, PinnablePackage, CompositeInstalledPackage, }; // Contains a collection of package versions. struct IPackageVersionCollection { virtual ~IPackageVersionCollection() = default; // Gets all versions of this package. // The versions will be returned in sorted, descending order. // Ex. { 4, 3, 2, 1 } virtual std::vector GetVersionKeys() const = 0; // Gets a specific version of this package. virtual std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const = 0; // A convenience method to effectively call `GetVersion(GetVersionKeys[0])`. virtual std::shared_ptr GetLatestVersion() const = 0; }; // Contains information about a package and its versions from a single source. struct IPackage : public IPackageVersionCollection { virtual ~IPackage() = default; // Gets a property of this package. virtual Utility::LocIndString GetProperty(PackageProperty property) const = 0; // Gets a property of this package that can have multiple values. virtual std::vector GetMultiProperty(PackageMultiProperty property) const = 0; // Gets the source that this package is from. virtual Source GetSource() const = 0; // Determines if the given IPackage refers to the same package as this one. virtual bool IsSame(const IPackage*) const = 0; // Gets this object as the requested type, or null if it is not the requested type. virtual const void* CastTo(IPackageType type) const = 0; }; // Contains information about the graph of packages related to a search. struct ICompositePackage { virtual ~ICompositePackage() = default; // Gets a property of this package result. virtual Utility::LocIndString GetProperty(PackageProperty property) const = 0; // Gets the installed package information. virtual std::shared_ptr GetInstalled() = 0; // Gets all of the available packages for this result. // There will be at most one package per source in this list. virtual std::vector> GetAvailable() = 0; }; // Does the equivalent of a dynamic_cast, but without it to allow RTTI to be disabled. // Example usage: // bool IsSame(const IPackage* other) const override // { // const MyPackage* otherAsMyType = PackageCast(other); // ... // } template PackageType PackageCast(const IPackage* package) { static_assert(std::is_pointer_v, "The target type of the PackageCast must be a pointer; use the same type as if this were a dynamic_cast."); if (!package) { return nullptr; } using ActualPackageType = std::remove_pointer_t>; return reinterpret_cast(package->CastTo(ActualPackageType::PackageType)); } // A single result from the search. struct ResultMatch { // The package found by the search request. std::shared_ptr Package; // The highest order field on which the package matched the search. PackageMatchFilter MatchCriteria; ResultMatch(std::shared_ptr p, PackageMatchFilter f) : Package(std::move(p)), MatchCriteria(std::move(f)) {} }; // Search result data. struct SearchResult { // Contains a failure from the Search. struct Failure { std::string SourceName; std::exception_ptr Exception; }; // The full set of results from the search. std::vector Matches; // If true, the results were truncated by the given SearchRequest::MaximumResults. bool Truncated = false; // Present if the Search was against a composite source and one failed, but not limited to that scenario. std::vector Failures; }; struct UnsupportedRequestException : public wil::ResultException { UnsupportedRequestException() : wil::ResultException(APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST) {} UnsupportedRequestException( std::vector unsupportedPackageMatchFields, std::vector requiredPackageMatchFields, std::vector unsupportedQueryParameters, std::vector requiredQueryParameters) : wil::ResultException(APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST), UnsupportedPackageMatchFields(std::move(unsupportedPackageMatchFields)), RequiredPackageMatchFields(std::move(requiredPackageMatchFields)), UnsupportedQueryParameters(std::move(unsupportedQueryParameters)), RequiredQueryParameters(std::move(requiredQueryParameters)) {} std::vector UnsupportedPackageMatchFields; std::vector RequiredPackageMatchFields; std::vector UnsupportedQueryParameters; std::vector RequiredQueryParameters; const char* what() const noexcept override; private: mutable std::string m_whatMessage; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace AppInstaller::Repository { // The interval is of 100 nano seconds precision.This is used by file date period and the Windows::Foundation::TimeSpan exposed in COM api. using TimeSpan = std::chrono::duration, std::nano>>; struct ISourceReference; struct ISource; // Defines the origin of the source details. enum class SourceOrigin { Default, User, Predefined, GroupPolicy, Metadata, PackageTracking, }; // Defines the trust level of the source. enum class SourceTrustLevel : uint32_t { None = 0x00000000, Trusted = 0x00000001, StoreOrigin = 0x00000002, }; DEFINE_ENUM_FLAG_OPERATORS(SourceTrustLevel); // Converts a string_view to the corresponding SourceTrustLevel enum. SourceTrustLevel ConvertToSourceTrustLevelEnum(std::string_view trustLevel); // Converts a vector of trust level strings to the corresponding SourceTrustLevel enum flag. SourceTrustLevel ConvertToSourceTrustLevelFlag(std::vector values); // Converts a SourceTrustLevel flag to a list of trust level strings. std::vector SourceTrustLevelFlagToList(SourceTrustLevel trustLevel); // Converts a SourceTrustLevel enum to the corresponding string. std::string_view SourceTrustLevelEnumToString(SourceTrustLevel trustLevel); // Gets the full trust level string name for display. std::string GetSourceTrustLevelForDisplay(SourceTrustLevel trustLevel); std::string_view ToString(SourceOrigin origin); // Fields that require user agreements. enum class ImplicitAgreementFieldEnum : int { None = 0x0, Market = 0x1, }; DEFINE_ENUM_FLAG_OPERATORS(ImplicitAgreementFieldEnum); // A predefined source. // These sources are not under the direct control of the user, such as packages installed on the system. enum class PredefinedSource { // Default behavior. Contains ARP packages installed as for user and for machine, MSIX packages for current user. Installed, // Only contains packages installed as for user InstalledUser, // Only contains packages installed as for machine InstalledMachine, ARP, MSIX, Installing, // Same as `Installed`, but creating the source reference for this is sufficient to cause the cache to be updated // on next Open of any `Installed` or `InstalledForceCacheUpdate`. InstalledForceCacheUpdate, }; // A well known source. // These come with the app and can be disabled but not removed. enum class WellKnownSource { WinGet, MicrosoftStore, DesktopFrameworks, WinGetFont, }; // Search behavior for composite sources. // Only relevant for composite sources with an installed source, not for aggregates of multiple available sources. // Installed and available packages in the result are always correlated when possible. enum class CompositeSearchBehavior { // Search only installed packages. Installed, // Search both installed and available packages. AllPackages, // Search only available packages. AvailablePackages, }; // Interface for source configurations. Source configurations are used to get a source reference without opening the source. struct SourceDetails { // The name of the source. std::string Name; // The type of the source. std::string Type; // The argument used when adding the source. std::string Arg; // The source's extra data string. std::string Data; // The source's unique identifier. std::string Identifier; // The origin of the source. SourceOrigin Origin = SourceOrigin::Default; // The trust level of the source SourceTrustLevel TrustLevel = SourceTrustLevel::None; // The last time that this source was updated. std::chrono::system_clock::time_point LastUpdateTime = {}; // Stores the earliest time that a background update should be attempted. std::chrono::system_clock::time_point DoNotUpdateBefore = {}; // Whether the source supports InstalledSource correlation. bool SupportInstalledSearchCorrelation = true; // The configuration of how the server certificate will be validated. Certificates::PinningConfiguration CertificatePinningConfiguration; // This value is used as an alternative to the `Arg` value if it is failing to function properly. // The alternate location must point to identical data or inconsistencies may arise. std::string AlternateArg; // Whether the source should be hidden by default unless explicitly declared. bool Explicit = false; // Value used for sorting the sources and making decisions // (like preferring one source over the other if both have a package to install). // Higher values come first in priority order. int32_t Priority = 0; }; // Check if a source matches a well known source std::optional CheckForWellKnownSource(const SourceDetails& sourceDetails); // Individual source agreement entry. Label will be highlighted in the display as the key of the agreement entry. struct SourceAgreement { SourceAgreement() = default; SourceAgreement(std::string label, std::string text, std::string url) : Label(std::move(label)), Text(std::move(text)), Url(std::move(url)) {} std::string Label; std::string Text; std::string Url; }; // Interface for retrieving information about a source after opening the source. struct SourceInformation { // Identifier of the source agreements. This is used to identify if source agreements have changed. std::string SourceAgreementsIdentifier; // List of source agreements that require user to accept. std::vector SourceAgreements; // Unsupported match fields in search request. If this field is in the filters, the request may fail. std::vector UnsupportedPackageMatchFields; // Required match fields in search request. If this field is not found in the filters, the request may fail(except Market). std::vector RequiredPackageMatchFields; // Unsupported query parameters in get manifest request. std::vector UnsupportedQueryParameters; // Required query parameters in get manifest request. std::vector RequiredQueryParameters; // Source authentication info. Authentication::AuthenticationInfo Authentication; }; // Contains information about edits to a source. struct SourceEdit { SourceEdit() = default; // The Explicit property of a source. std::optional Explicit; // The Priority property of a source. std::optional Priority; }; // Allows calling code to inquire about specific features of an ISource implementation. // The default state of any new flag is false. enum class SourceFeatureFlag { // If true, the manifests for this source may contain more data than is available from just the // version information found from a search. ManifestMayContainAdditionalSystemReferenceStrings, }; // Represents a source which would be interacted from outside of repository lib. struct Source { // Default constructor with an empty source. Source(); // Constructor to get a named source, passing empty string will get all available sources. Source(std::string_view name); // Constructor to get a PredefinedSource. Like installed source, etc. Source(PredefinedSource source); // Constructor to get a source coming with winget. Like winget community source, etc. Source(WellKnownSource source); // Constructor for a source to be added. Source(std::string_view name, std::string_view arg, std::string_view type, SourceTrustLevel trustLevel, const SourceEdit& additionalProperties); // Constructor for creating a composite source from a list of available sources. Source(const std::vector& availableSources); // Constructor for creating a composite source from an installed source and available source(may be composite already). Source( const Source& installedSource, const Source& availableSource, CompositeSearchBehavior searchBehavior = CompositeSearchBehavior::Installed); // Constructor for creating a Source object from an existing ISource. // Should only be used internally by ISource implementations to return the value from IPackageVersion::GetSource. Source(std::shared_ptr source); // Bool operator to check if a source reference is successfully acquired. // Theoretically, the constructor could just throw when CreateSource returns empty. // To avoid putting try catch everywhere, we use bool operator here. operator bool() const; // Determines if the sources are equivalent. // Currently only works for individual sources, not composites. bool operator==(const Source& other) const; bool operator!=(const Source& other) const; // Gets the source's identifier; a unique identifier independent of the name // that will not change between a remove/add or between additional adds. // Must be suitable for filesystem names unless the source is internal to winget, // in which case the identifier should begin with a '*' character. std::string GetIdentifier() const; // Get the source's configuration details from settings. SourceDetails GetDetails() const; // Get the source's information. SourceInformation GetInformation() const; // Query the value of the given feature flag. // The default state of any new flag is false. bool QueryFeatureFlag(SourceFeatureFlag flag) const; // Returns true if the origin type can contain available packages. bool ContainsAvailablePackages() const; // Set custom header. Must be set before Open to have effect. bool SetCustomHeader(std::optional header); // Set caller. Must be set before Open to have effect. void SetCaller(std::string caller); // Set authentication arguments. Must be set before Open to have effect. void SetAuthenticationArguments(Authentication::AuthenticationArguments args); // Set thread globals. Must be set before Open to have effect. void SetThreadGlobals(const std::shared_ptr& threadGlobals); // Set background update check interval. void SetBackgroundUpdateInterval(TimeSpan interval); // Indicates that we are only interested in the PackageTrackingCatalog for the source. // Must be set before Open to have effect, and will prevent the underlying source from being updated or opened. void InstalledPackageInformationOnly(bool value); // Determines if this source refers to the given well known source. bool IsWellKnownSource(WellKnownSource wellKnownSource); // Execute a search on the source. SearchResult Search(const SearchRequest& request) const; /* Source agreements */ // Get required agreement fields info. ImplicitAgreementFieldEnum GetAgreementFieldsFromSourceInformation() const; // Checks the source agreements and returns if agreements are satisfied. bool CheckSourceAgreements() const; // Saves the accepted source agreements in metadata. void SaveAcceptedSourceAgreements() const; /* Composite sources */ // Gets a value indicating whether this source is a composite of other sources, // and thus the packages may come from disparate sources as well. bool IsComposite() const; // Gets the available sources if the source is composite. std::vector GetAvailableSources() const; /* Writable sources */ // Adds a package version to the source. void AddPackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); // Removes a package version from the source. void RemovePackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); /* Source operations */ // Opens the source. This function should throw upon open failure rather than returning an empty pointer. std::vector Open(IProgressCallback& progress); // Add source. Source add command. bool Add(IProgressCallback& progress); // Update Source. Source update command. std::vector Update(IProgressCallback& progress); // Remove source. Source remove command. bool Remove(IProgressCallback& progress); // Edit source. Source edit command. void Edit(const SourceEdit& edits); // Determines if this source is a valid edit of otherSource. // Returns true if this source qualifies as an edit of the other source. bool RequiresChanges(const SourceEdit& edits); // Gets the tracking catalog for the current source. PackageTrackingCatalog GetTrackingCatalog() const; // Drop source. Source reset command. static bool DropSource(std::string_view name); // Get a list of all available SourceDetails. static std::vector GetCurrentSources(); // Get a default source type is the source type used when adding a source without specifying a type. static std::string_view GetDefaultSourceType(); private: void InitializeSourceReference(std::string_view name); std::vector> m_sourceReferences; std::shared_ptr m_source; bool m_isSourceToBeAdded = false; bool m_isComposite = false; std::optional m_backgroundUpdateInterval; bool m_installedPackageInformationOnly = false; mutable std::shared_ptr m_trackingCatalog; }; } ================================================ FILE: src/AppInstallerRepositoryCore/RepositorySearch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/RepositorySearch.h" using namespace AppInstaller::Settings; using namespace std::chrono_literals; namespace AppInstaller::Repository { namespace { std::string GetStringVectorMessage(const std::vector& input) { std::string result; bool first = true; for (auto const& field : input) { if (first) { result += field; first = false; } else { result += ", " + field; } } return result; } } bool SearchRequest::IsForEverything() const { return (!Query.has_value() && Inclusions.empty() && Filters.empty()); } std::string SearchRequest::ToString() const { std::ostringstream result; result << "Query:"; if (Query) { result << '\'' << Query.value().Value << "'[" << Repository::ToString(Query.value().Type) << ']'; } else { result << "[none]"; } for (const auto& include : Inclusions) { result << " Include:" << Repository::ToString(include.Field) << "='" << include.Value << "'"; if (include.Additional) { result << "+'" << include.Additional.value() << "'"; } result << "[" << Repository::ToString(include.Type) << "]"; } for (const auto& filter : Filters) { result << " Filter:" << Repository::ToString(filter.Field) << "='" << filter.Value << "'[" << Repository::ToString(filter.Type) << "]"; } if (MaximumResults) { result << " Limit:" << MaximumResults; } return result.str(); } std::string_view ToString(PackageVersionMetadata pvm) { switch (pvm) { case PackageVersionMetadata::InstalledType: return "InstalledType"sv; case PackageVersionMetadata::InstalledScope: return "InstalledScope"sv; case PackageVersionMetadata::InstalledLocation: return "InstalledLocation"sv; case PackageVersionMetadata::StandardUninstallCommand: return "StandardUninstallCommand"sv; case PackageVersionMetadata::SilentUninstallCommand: return "SilentUninstallCommand"sv; case PackageVersionMetadata::Publisher: return "Publisher"sv; case PackageVersionMetadata::InstalledLocale: return "InstalledLocale"sv; case PackageVersionMetadata::TrackingWriteTime: return "TrackingWriteTime"sv; case PackageVersionMetadata::InstalledArchitecture: return "InstalledArchitecture"sv; case PackageVersionMetadata::PinnedState: return "PinnedState"sv; case PackageVersionMetadata::UserIntentArchitecture: return "UserIntentArchitecture"sv; case PackageVersionMetadata::UserIntentLocale: return "UserIntentLocale"sv; default: return "Unknown"sv; } } bool PackageVersionKey::IsMatch(const PackageVersionKey& other) const { return ((other.SourceId.empty() || other.SourceId == SourceId) && (other.Version.empty() || Utility::Version{ other.Version } == Utility::Version{ Version }) && (other.Channel.empty() || Utility::ICUCaseInsensitiveEquals(other.Channel, Channel))); } bool PackageVersionKey::IsDefaultLatest() const { return Version.empty() && Channel.empty(); } PackageVersionMultiProperty PackageMultiPropertyToPackageVersionMultiProperty(PackageMultiProperty property) { switch (property) { case PackageMultiProperty::PackageFamilyName: return PackageVersionMultiProperty::PackageFamilyName; case PackageMultiProperty::ProductCode: return PackageVersionMultiProperty::ProductCode; case PackageMultiProperty::UpgradeCode: return PackageVersionMultiProperty::UpgradeCode; case PackageMultiProperty::NormalizedName: return PackageVersionMultiProperty::Name; case PackageMultiProperty::NormalizedPublisher: return PackageVersionMultiProperty::Publisher; case PackageMultiProperty::Tag: return PackageVersionMultiProperty::Tag; case PackageMultiProperty::Command: return PackageVersionMultiProperty::Command; default: THROW_HR_MSG(E_UNEXPECTED, "PackageMultiProperty must map to a PackageVersionMultiProperty"); } } const char* UnsupportedRequestException::what() const noexcept { if (m_whatMessage.empty()) { m_whatMessage = "The request is not supported."; if (!UnsupportedPackageMatchFields.empty()) { m_whatMessage += "Unsupported Package Match Fields: " + GetStringVectorMessage(UnsupportedPackageMatchFields); } if (!RequiredPackageMatchFields.empty()) { m_whatMessage += "Required Package Match Fields: " + GetStringVectorMessage(RequiredPackageMatchFields); } if (!UnsupportedQueryParameters.empty()) { m_whatMessage += "Unsupported Query Parameters: " + GetStringVectorMessage(UnsupportedQueryParameters); } if (!RequiredQueryParameters.empty()) { m_whatMessage += "Required Query Parameters: " + GetStringVectorMessage(RequiredQueryParameters); } } return m_whatMessage.c_str(); } std::string_view ToString(MatchType type) { using namespace std::string_view_literals; switch (type) { case MatchType::Exact: return "Exact"sv; case MatchType::CaseInsensitive: return "CaseInsensitive"sv; case MatchType::StartsWith: return "StartsWith"sv; case MatchType::Substring: return "Substring"sv; case MatchType::Wildcard: return "Wildcard"sv; case MatchType::Fuzzy: return "Fuzzy"sv; case MatchType::FuzzySubstring: return "FuzzySubstring"sv; } return "UnknownMatchType"sv; } std::string_view ToString(PackageMatchField matchField) { using namespace std::string_view_literals; switch (matchField) { case PackageMatchField::Command: return "Command"sv; case PackageMatchField::Id: return "Id"sv; case PackageMatchField::Moniker: return "Moniker"sv; case PackageMatchField::Name: return "Name"sv; case PackageMatchField::Tag: return "Tag"sv; case PackageMatchField::PackageFamilyName: return "PackageFamilyName"sv; case PackageMatchField::ProductCode: return "ProductCode"sv; case PackageMatchField::UpgradeCode: return "UpgradeCode"sv; case PackageMatchField::NormalizedNameAndPublisher: return "NormalizedNameAndPublisher"sv; case PackageMatchField::Market: return "Market"sv; } return "UnknownMatchField"sv; } PackageMatchField StringToPackageMatchField(std::string_view field) { std::string toLower = Utility::ToLower(field); if (toLower == "command") { return PackageMatchField::Command; } else if (toLower == "id") { return PackageMatchField::Id; } else if (toLower == "moniker") { return PackageMatchField::Moniker; } else if (toLower == "name") { return PackageMatchField::Name; } else if (toLower == "tag") { return PackageMatchField::Tag; } else if (toLower == "packagefamilyname") { return PackageMatchField::PackageFamilyName; } else if (toLower == "productcode") { return PackageMatchField::ProductCode; } else if (toLower == "upgradecode") { return PackageMatchField::UpgradeCode; } else if (toLower == "normalizednameandpublisher") { return PackageMatchField::NormalizedNameAndPublisher; } else if (toLower == "market") { return PackageMatchField::Market; } return PackageMatchField::Unknown; } } ================================================ FILE: src/AppInstallerRepositoryCore/RepositorySource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ISource.h" #include "CompositeSource.h" #include "SourceFactory.h" #include "SourceList.h" #include "SourcePolicy.h" #include "Microsoft/PredefinedInstalledSourceFactory.h" #include "Microsoft/PredefinedWriteableSourceFactory.h" #include "Microsoft/PreIndexedPackageSourceFactory.h" #include "Rest/RestSourceFactory.h" #include "PackageTrackingCatalogSourceFactory.h" #include "SourceUpdateChecks.h" #ifndef AICLI_DISABLE_TEST_HOOKS #include "Microsoft/ConfigurableTestSourceFactory.h" #endif #include using namespace AppInstaller::Settings; using namespace std::chrono_literals; using namespace AppInstaller::Utility::literals; namespace AppInstaller::Repository { namespace { #ifndef AICLI_DISABLE_TEST_HOOKS static std::map()>> s_Sources_TestHook_SourceFactories; #endif std::shared_ptr CreateSourceFromDetails(const SourceDetails& details) { return ISourceFactory::GetForType(details.Type)->Create(details); } std::chrono::milliseconds GetMillisecondsToWait(std::chrono::seconds retryAfter, size_t randomMultiplier = 1) { if (retryAfter != 0s) { return std::chrono::duration_cast(retryAfter); } else { // Add a bit of randomness to the retry wait time std::default_random_engine randomEngine(std::random_device{}()); std::uniform_int_distribution distribution(2000, 10000); return std::chrono::milliseconds(distribution(randomEngine) * randomMultiplier); } } struct AddOrUpdateResult { bool UpdateChecked = false; bool MetadataWritten = false; }; template AddOrUpdateResult AddOrUpdateFromDetails(SourceDetails& details, MemberFunc member, IProgressCallback& progress) { AddOrUpdateResult result; auto factory = ISourceFactory::GetForType(details.Type); // If we are instructed to wait longer than this, just fail rather than retrying. constexpr std::chrono::seconds maximumWaitTimeAllowed = 60s; std::chrono::seconds waitSecondsForRetry = 0s; // Attempt; if it fails, wait a short time and retry. try { result.UpdateChecked = (factory.get()->*member)(details, progress); if (result.UpdateChecked) { details.LastUpdateTime = std::chrono::system_clock::now(); result.MetadataWritten = true; } return result; } catch (const Utility::ServiceUnavailableException& sue) { waitSecondsForRetry = sue.RetryAfter(); // Do not retry if the server tell us to wait more than the max time allowed. if (waitSecondsForRetry > maximumWaitTimeAllowed) { details.DoNotUpdateBefore = std::chrono::system_clock::now() + waitSecondsForRetry; AICLI_LOG(Repo, Info, << "Source `" << details.Name << "` unavailable first try, setting DoNotUpdateBefore to " << details.DoNotUpdateBefore); result.MetadataWritten = true; return result; } } CATCH_LOG(); std::chrono::milliseconds millisecondsToWait = GetMillisecondsToWait(waitSecondsForRetry); AICLI_LOG(Repo, Info, << "Source add/update failed, waiting " << millisecondsToWait.count() << " milliseconds and retrying: " << details.Name); if (!ProgressCallback::Wait(progress, millisecondsToWait)) { AICLI_LOG(Repo, Info, << "Source second try cancelled."); return {}; } try { // If this one fails, maybe the problem is persistent. result.UpdateChecked = (factory.get()->*member)(details, progress); if (result.UpdateChecked) { details.LastUpdateTime = std::chrono::system_clock::now(); result.MetadataWritten = true; } } catch (const Utility::ServiceUnavailableException& sue) { details.DoNotUpdateBefore = std::chrono::system_clock::now() + GetMillisecondsToWait(sue.RetryAfter(), 3); AICLI_LOG(Repo, Info, << "Source `" << details.Name << "` unavailable second try, setting DoNotUpdateBefore to " << details.DoNotUpdateBefore); result.MetadataWritten = true; } return result; } AddOrUpdateResult AddSourceFromDetails(SourceDetails& details, IProgressCallback& progress) { return AddOrUpdateFromDetails(details, &ISourceFactory::Add, progress); } AddOrUpdateResult UpdateSourceFromDetails(SourceDetails& details, IProgressCallback& progress) { return AddOrUpdateFromDetails(details, &ISourceFactory::Update, progress); } AddOrUpdateResult BackgroundUpdateSourceFromDetails(SourceDetails& details, IProgressCallback& progress) { return AddOrUpdateFromDetails(details, &ISourceFactory::BackgroundUpdate, progress); } bool RemoveSourceFromDetails(const SourceDetails& details, IProgressCallback& progress) { auto factory = ISourceFactory::GetForType(details.Type); return factory->Remove(details, progress); } bool ContainsAvailablePackagesInternal(SourceOrigin origin) { return (origin == SourceOrigin::Default || origin == SourceOrigin::GroupPolicy || origin == SourceOrigin::User); } SourceDetails GetPredefinedSourceDetails(PredefinedSource source) { SourceDetails details; details.Origin = SourceOrigin::Predefined; switch (source) { case PredefinedSource::Installed: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::None); return details; case PredefinedSource::InstalledForceCacheUpdate: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::NoneWithForcedCacheUpdate); return details; case PredefinedSource::InstalledUser: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::User); return details; case PredefinedSource::InstalledMachine: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::Machine); return details; case PredefinedSource::ARP: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::ARP); return details; case PredefinedSource::MSIX: details.Type = Microsoft::PredefinedInstalledSourceFactory::Type(); details.Arg = Microsoft::PredefinedInstalledSourceFactory::FilterToString(Microsoft::PredefinedInstalledSourceFactory::Filter::MSIX); return details; case PredefinedSource::Installing: details.Type = Microsoft::PredefinedWriteableSourceFactory::Type(); // As long as there is only one type this is not particularly needed, but Arg is exposed publicly // so this is used here for consistency with other predefined sources. details.Arg = Microsoft::PredefinedWriteableSourceFactory::TypeToString(Microsoft::PredefinedWriteableSourceFactory::WriteableType::Installing); return details; } THROW_HR(E_UNEXPECTED); } // Carries the exception from an OpenSource call and presents it back at search time. struct OpenExceptionProxy : public ISource, std::enable_shared_from_this { static constexpr ISourceType SourceType = ISourceType::OpenExceptionProxy; OpenExceptionProxy(const SourceDetails& details, std::exception_ptr exception) : m_details(details), m_exception(std::move(exception)) {} const SourceDetails& GetDetails() const override { return m_details; } const std::string& GetIdentifier() const override { return m_details.Identifier; } SearchResult Search(const SearchRequest&) const override { SearchResult result; result.Failures.emplace_back(SearchResult::Failure{ GetDetails().Name, m_exception }); return result; } void* CastTo(ISourceType type) override { if (type == SourceType) { return this; } return nullptr; } private: SourceDetails m_details; std::exception_ptr m_exception; }; // A wrapper that doesn't actually forward the search requests. struct TrackingOnlySourceWrapper : public ISource { TrackingOnlySourceWrapper(std::shared_ptr wrapped) : m_wrapped(std::move(wrapped)) { m_identifier = m_wrapped->GetIdentifier(); } const std::string& GetIdentifier() const override { return m_identifier; } SourceDetails& GetDetails() const override { return m_wrapped->GetDetails(); } SourceInformation GetInformation() const override { return m_wrapped->GetInformation(); } SearchResult Search(const SearchRequest&) const override { return {}; } void* CastTo(ISourceType) override { return nullptr; } private: std::shared_ptr m_wrapped; std::string m_identifier; }; // A wrapper to create another wrapper. struct TrackingOnlyReferenceWrapper : public ISourceReference { TrackingOnlyReferenceWrapper(std::shared_ptr wrapped) : m_wrapped(std::move(wrapped)) {} std::string GetIdentifier() override { return m_wrapped->GetIdentifier(); } SourceDetails& GetDetails() override { return m_wrapped->GetDetails(); } SourceInformation GetInformation() override { return m_wrapped->GetInformation(); } bool SetCustomHeader(std::optional) override { return false; } void SetCaller(std::string caller) override { m_wrapped->SetCaller(std::move(caller)); } std::shared_ptr Open(IProgressCallback&) override { return std::make_shared(m_wrapped); } private: std::shared_ptr m_wrapped; }; } std::unique_ptr ISourceFactory::GetForType(std::string_view type) { #ifndef AICLI_DISABLE_TEST_HOOKS // Tests can ensure case matching auto itr = s_Sources_TestHook_SourceFactories.find(std::string(type)); if (itr != s_Sources_TestHook_SourceFactories.end()) { return itr->second(); } if (Utility::CaseInsensitiveEquals(Microsoft::ConfigurableTestSourceFactory::Type(), type)) { return Microsoft::ConfigurableTestSourceFactory::Create(); } #endif // For now, enable an empty type to represent the only one we have. if (type.empty() || Utility::CaseInsensitiveEquals(Microsoft::PreIndexedPackageSourceFactory::Type(), type)) { return Microsoft::PreIndexedPackageSourceFactory::Create(); } // Should always come from code, so no need for case insensitivity else if (Microsoft::PredefinedInstalledSourceFactory::Type() == type) { return Microsoft::PredefinedInstalledSourceFactory::Create(); } // Should always come from code, so no need for case insensitivity else if (Microsoft::PredefinedWriteableSourceFactory::Type() == type) { return Microsoft::PredefinedWriteableSourceFactory::Create(); } // Should always come from code, so no need for case insensitivity else if (PackageTrackingCatalogSourceFactory::Type() == type) { return PackageTrackingCatalogSourceFactory::Create(); } else if (Utility::CaseInsensitiveEquals(Rest::RestSourceFactory::Type(), type)) { return Rest::RestSourceFactory::Create(); } THROW_HR(APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE); } SourceTrustLevel ConvertToSourceTrustLevelEnum(std::string_view trustLevel) { std::string lowerTrustLevel = Utility::ToLower(trustLevel); if (lowerTrustLevel == "storeorigin") { return SourceTrustLevel::StoreOrigin; } else if (lowerTrustLevel == "trusted") { return SourceTrustLevel::Trusted; } else if (lowerTrustLevel == "none") { return SourceTrustLevel::None; } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } } std::string_view SourceTrustLevelEnumToString(SourceTrustLevel trustLevel) { switch (trustLevel) { case SourceTrustLevel::StoreOrigin: return "StoreOrigin"sv; case SourceTrustLevel::Trusted: return "Trusted"sv; case SourceTrustLevel::None: return "None"sv; } return "Unknown"sv; } SourceTrustLevel ConvertToSourceTrustLevelFlag(std::vector trustLevels) { Repository::SourceTrustLevel result = Repository::SourceTrustLevel::None; for (auto& trustLevel : trustLevels) { Repository::SourceTrustLevel trustLevelEnum = ConvertToSourceTrustLevelEnum(trustLevel); if (trustLevelEnum == Repository::SourceTrustLevel::None) { return Repository::SourceTrustLevel::None; } else if (trustLevelEnum == Repository::SourceTrustLevel::Trusted) { WI_SetFlag(result, Repository::SourceTrustLevel::Trusted); } else if (trustLevelEnum == Repository::SourceTrustLevel::StoreOrigin) { WI_SetFlag(result, Repository::SourceTrustLevel::StoreOrigin); } else { THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Invalid source trust level."); } } return result; } std::vector SourceTrustLevelFlagToList(SourceTrustLevel trustLevel) { std::vector result; if (WI_IsFlagSet(trustLevel, Repository::SourceTrustLevel::Trusted)) { result.emplace_back(Repository::SourceTrustLevelEnumToString(Repository::SourceTrustLevel::Trusted)); } if (WI_IsFlagSet(trustLevel, Repository::SourceTrustLevel::StoreOrigin)) { result.emplace_back(Repository::SourceTrustLevelEnumToString(Repository::SourceTrustLevel::StoreOrigin)); } return result; } std::string GetSourceTrustLevelForDisplay(SourceTrustLevel trustLevel) { std::vector trustLevelList = Repository::SourceTrustLevelFlagToList(trustLevel); std::vector locIndList(trustLevelList.begin(), trustLevelList.end()); return Utility::Join("|"_liv, locIndList); } std::string_view ToString(SourceOrigin origin) { switch (origin) { case SourceOrigin::Default: return "Default"sv; case SourceOrigin::User: return "User"sv; case SourceOrigin::Predefined: return "Predefined"sv; case SourceOrigin::GroupPolicy: return "GroupPolicy"sv; case SourceOrigin::Metadata: return "Metadata"sv; default: THROW_HR(E_UNEXPECTED); } } std::optional CheckForWellKnownSource(const SourceDetails& sourceDetails) { return CheckForWellKnownSourceMatch(sourceDetails.Name, sourceDetails.Arg, sourceDetails.Type); } Source::Source() {} Source::Source(std::string_view name) { InitializeSourceReference(name); } Source::Source(PredefinedSource source) { SourceDetails details = GetPredefinedSourceDetails(source); m_sourceReferences.emplace_back(CreateSourceFromDetails(details)); } Source::Source(WellKnownSource source) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !IsWellKnownSourceEnabled(source)); auto details = GetWellKnownSourceDetailsInternal(source); // Populate metadata SourceList sourceList; auto sourceDetailsWithMetadata = sourceList.GetSource(details.Name); if (sourceDetailsWithMetadata) { sourceDetailsWithMetadata->CopyMetadataFieldsTo(details); } m_sourceReferences.emplace_back(CreateSourceFromDetails(details)); } Source::Source(std::string_view name, std::string_view arg, std::string_view type, SourceTrustLevel trustLevel, const SourceEdit& additionalProperties) { m_isSourceToBeAdded = true; SourceDetails details; std::optional wellKnownSourceCheck = CheckForWellKnownSourceMatch(name, arg, type); if (wellKnownSourceCheck) { details = GetWellKnownSourceDetailsInternal(wellKnownSourceCheck.value()); } else { details.Name = name; details.Arg = arg; details.Type = type; details.TrustLevel = trustLevel; if (additionalProperties.Explicit) { details.Explicit = additionalProperties.Explicit.value(); } if (additionalProperties.Priority) { details.Priority = additionalProperties.Priority.value(); } } m_sourceReferences.emplace_back(CreateSourceFromDetails(details)); } Source::Source(const std::vector& availableSources) { std::shared_ptr compositeSource = std::make_shared("*CompositeSource"); for (const auto& availableSource : availableSources) { THROW_HR_IF(E_INVALIDARG, !availableSource.m_source || availableSource.IsComposite()); compositeSource->AddAvailableSource(availableSource.m_source); } m_source = compositeSource; m_isComposite = true; } Source::Source(const Source& installedSource, const Source& availableSource, CompositeSearchBehavior searchBehavior) { THROW_HR_IF(E_INVALIDARG, !installedSource.m_source || installedSource.m_isComposite || !availableSource.m_source); std::shared_ptr compositeSource = SourceCast(availableSource.m_source); if (!compositeSource) { compositeSource = std::make_shared("*CompositeSource"); compositeSource->AddAvailableSource(availableSource.m_source); } compositeSource->SetInstalledSource(installedSource, searchBehavior); m_source = compositeSource; m_isComposite = true; } Source::Source(std::shared_ptr source) : m_source(std::move(source)) {} Source::operator bool() const { return !m_sourceReferences.empty() || m_source != nullptr; } void Source::InitializeSourceReference(std::string_view name) { SourceList sourceList; if (name.empty()) { auto currentSources = sourceList.GetCurrentSourceRefs(); if (currentSources.empty()) { AICLI_LOG(Repo, Info, << "Default source requested, but no sources configured"); } else if (currentSources.size() == 1) { if (!currentSources[0].get().Explicit) { AICLI_LOG(Repo, Info, << "Default source requested, only 1 source available, using the only source: " << currentSources[0].get().Name); InitializeSourceReference(currentSources[0].get().Name); } else { AICLI_LOG(Repo, Info, << "Skipping explicit source reference " << currentSources[0].get().Name); } } else { AICLI_LOG(Repo, Info, << "Default source requested, multiple sources available, adding all to source references."); for (auto& source : currentSources) { if (!source.get().Explicit) { AICLI_LOG(Repo, Info, << "Adding to source references " << source.get().Name); m_sourceReferences.emplace_back(CreateSourceFromDetails(source)); } else { AICLI_LOG(Repo, Info, << "Skipping explicit source reference " << source.get().Name); } } if (m_sourceReferences.size() > 1) { m_isComposite = true; } } } else { auto source = sourceList.GetCurrentSource(name); if (!source) { AICLI_LOG(Repo, Info, << "Named source requested, but not found: " << name); } else { AICLI_LOG(Repo, Info, << "Named source requested, found: " << source->Name); m_sourceReferences.emplace_back(CreateSourceFromDetails(*source)); } } } bool Source::operator==(const Source& other) const { SourceDetails thisDetails = GetDetails(); SourceDetails otherDetails = other.GetDetails(); return (thisDetails.Type == otherDetails.Type && thisDetails.Identifier == otherDetails.Identifier); } bool Source::operator!=(const Source& other) const { return !operator==(other); } std::string Source::GetIdentifier() const { if (m_source) { return m_source->GetIdentifier(); } else if (m_sourceReferences.size() == 1) { return m_sourceReferences[0]->GetIdentifier(); } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } } SourceDetails Source::GetDetails() const { if (m_source) { return m_source->GetDetails(); } else if (m_sourceReferences.size() == 1) { return m_sourceReferences[0]->GetDetails(); } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } } SourceInformation Source::GetInformation() const { if (m_source && !m_isComposite) { return m_source->GetInformation(); } else if (m_sourceReferences.size() == 1) { return m_sourceReferences[0]->GetInformation(); } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } } bool Source::QueryFeatureFlag(SourceFeatureFlag flag) const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_source); return m_source->QueryFeatureFlag(flag); } bool Source::ContainsAvailablePackages() const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), IsComposite()); return ContainsAvailablePackagesInternal(GetDetails().Origin); } bool Source::SetCustomHeader(std::optional header) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_sourceReferences.size() != 1); return m_sourceReferences[0]->SetCustomHeader(header); } void Source::SetCaller(std::string caller) { for (auto& sourceReference : m_sourceReferences) { sourceReference->SetCaller(caller); } } void Source::SetAuthenticationArguments(Authentication::AuthenticationArguments args) { for (auto& sourceReference : m_sourceReferences) { sourceReference->SetAuthenticationArguments(args); } } void Source::SetThreadGlobals(const std::shared_ptr& threadGlobals) { for (auto& sourceReference : m_sourceReferences) { sourceReference->SetThreadGlobals(threadGlobals); } } void Source::SetBackgroundUpdateInterval(TimeSpan interval) { m_backgroundUpdateInterval = interval; } void Source::InstalledPackageInformationOnly(bool value) { m_installedPackageInformationOnly = value; } bool Source::IsWellKnownSource(WellKnownSource wellKnownSource) { SourceDetails details = GetDetails(); auto wellKnown = CheckForWellKnownSourceMatch(details.Name, details.Arg, details.Type); return wellKnown && wellKnown.value() == wellKnownSource; } SearchResult Source::Search(const SearchRequest& request) const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_source); return m_source->Search(request); } ImplicitAgreementFieldEnum Source::GetAgreementFieldsFromSourceInformation() const { ImplicitAgreementFieldEnum result = ImplicitAgreementFieldEnum::None; auto info = GetInformation(); if (info.RequiredPackageMatchFields.end() != std::find_if(info.RequiredPackageMatchFields.begin(), info.RequiredPackageMatchFields.end(), [&](const auto& field) { return Utility::CaseInsensitiveEquals(field, "market"); }) || info.RequiredQueryParameters.end() != std::find_if(info.RequiredQueryParameters.begin(), info.RequiredQueryParameters.end(), [&](const auto& param) { return Utility::CaseInsensitiveEquals(param, "market"); })) { WI_SetFlag(result, ImplicitAgreementFieldEnum::Market); } return result; } bool Source::CheckSourceAgreements() const { auto sourceName = GetDetails().Name; auto agreementFields = GetAgreementFieldsFromSourceInformation(); auto agreementsIdentifier = GetInformation().SourceAgreementsIdentifier; SourceList sourceList; return sourceList.CheckSourceAgreements(sourceName, agreementsIdentifier, agreementFields); } void Source::SaveAcceptedSourceAgreements() const { auto sourceName = GetDetails().Name; auto agreementFields = GetAgreementFieldsFromSourceInformation(); auto agreementsIdentifier = GetInformation().SourceAgreementsIdentifier; SourceList sourceList; return sourceList.SaveAcceptedSourceAgreements(sourceName, agreementsIdentifier, agreementFields); } bool Source::IsComposite() const { return m_isComposite; } std::vector Source::GetAvailableSources() const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_source || !m_isComposite); auto compositeSource = SourceCast(m_source); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !compositeSource); return compositeSource->GetAvailableSources(); } void Source::AddPackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_source); auto writableSource = SourceCast(m_source); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !writableSource); writableSource->AddPackageVersion(manifest, relativePath); } void Source::RemovePackageVersion(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_source); auto writableSource = SourceCast(m_source); THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !writableSource); writableSource->RemovePackageVersion(manifest, relativePath); } std::vector Source::Open(IProgressCallback& progress) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_isSourceToBeAdded || m_sourceReferences.empty()); std::vector result; if (!m_source) { std::vector>* sourceReferencesToOpen = nullptr; std::vector> sourceReferencesForTrackingOnly; std::unique_ptr sourceList; if (m_installedPackageInformationOnly) { sourceReferencesToOpen = &sourceReferencesForTrackingOnly; // Create a wrapper for each reference for (auto& sourceReference : m_sourceReferences) { sourceReferencesForTrackingOnly.emplace_back(std::make_shared(sourceReference)); } } else { // Check for updates before opening. for (auto& sourceReference : m_sourceReferences) { if (ShouldUpdateBeforeOpen(sourceReference.get(), m_backgroundUpdateInterval)) { auto& details = sourceReference->GetDetails(); try { // TODO: Consider adding a context callback to indicate we are doing the same action // to avoid the progress bar fill up multiple times. AddOrUpdateResult updateResult = BackgroundUpdateSourceFromDetails(details, progress); if (updateResult.MetadataWritten) { if (sourceList == nullptr) { sourceList = std::make_unique(); } auto detailsInternal = sourceList->GetSource(details.Name); detailsInternal->CopyMetadataFieldsFrom(details); sourceList->SaveMetadata(*detailsInternal); } if (!updateResult.UpdateChecked) { AICLI_LOG(Repo, Error, << "Failed to update source: " << details.Name); result.emplace_back(details); } } catch (...) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Repo, Warning, << "Failed to update source: " << details.Name); result.emplace_back(details); } } } sourceReferencesToOpen = &m_sourceReferences; } if (sourceReferencesToOpen->size() > 1) { AICLI_LOG(Repo, Info, << "Multiple sources available, creating aggregated source."); auto aggregatedSource = std::make_shared("*DefaultSource"); std::vector> openExceptionProxies; for (auto& sourceReference : *sourceReferencesToOpen) { AICLI_LOG(Repo, Info, << "Adding to aggregated source: " << sourceReference->GetDetails().Name); try { aggregatedSource->AddAvailableSource(sourceReference->Open(progress)); } catch (...) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Repo, Warning, << "Failed to open available source: " << sourceReference->GetDetails().Name); openExceptionProxies.emplace_back(std::make_shared(sourceReference->GetDetails(), std::current_exception())); } } // If all sources failed to open, then throw an exception that is specific to this case. THROW_HR_IF(APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES, !aggregatedSource->HasAvailableSource()); // Place all of the proxies into the source to be searched later for (auto& proxy : openExceptionProxies) { aggregatedSource->AddAvailableSource(Source{ std::move(proxy) }); } m_source = aggregatedSource; } else { m_source = (*sourceReferencesToOpen)[0]->Open(progress); } } return result; } bool Source::Add(IProgressCallback& progress) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_isSourceToBeAdded || m_sourceReferences.size() != 1); auto& sourceDetails = m_sourceReferences[0]->GetDetails(); // If the source type is empty, use a default. // AddSourceForDetails will also check for empty, but we need the actual type before that for validation. if (sourceDetails.Type.empty()) { sourceDetails.Type = GetDefaultSourceType(); } AICLI_LOG(Repo, Info, << "Adding source: Name[" << sourceDetails.Name << "], Type[" << sourceDetails.Type << "], Arg[" << sourceDetails.Arg << "]"); // Check all sources for the given name. SourceList sourceList; auto source = sourceList.GetSource(sourceDetails.Name); THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, source != nullptr && source->Origin != SourceOrigin::Metadata && !source->IsTombstone); // Check sources allowed by group policy auto blockingPolicy = GetPolicyBlockingUserSource(sourceDetails.Name, sourceDetails.Type, sourceDetails.Arg, false); if (blockingPolicy != TogglePolicy::Policy::None) { throw GroupPolicyException(blockingPolicy); } sourceDetails.LastUpdateTime = Utility::ConvertUnixEpochToSystemClock(0); // Allow the origin to stay as Default if the incoming details match a well known value if (!(sourceDetails.Origin == SourceOrigin::Default && CheckForWellKnownSourceMatch(sourceDetails.Name, sourceDetails.Arg, sourceDetails.Type))) { sourceDetails.Origin = SourceOrigin::User; } bool result = AddSourceFromDetails(sourceDetails, progress).UpdateChecked; if (result) { sourceList.AddSource(sourceDetails); SaveAcceptedSourceAgreements(); m_isSourceToBeAdded = false; AICLI_LOG(Repo, Info, << "Source created with extra data: " << sourceDetails.Data); } return result; } std::vector Source::Update(IProgressCallback& progress) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_isSourceToBeAdded || m_source || m_sourceReferences.empty()); SourceList sourceList; std::vector result; for (auto& sourceReference : m_sourceReferences) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !ContainsAvailablePackagesInternal(sourceReference->GetDetails().Origin)); auto& details = sourceReference->GetDetails(); AICLI_LOG(Repo, Info, << "Named source to be updated, found: " << details.Name); try { // TODO: Consider adding a context callback to indicate we are doing the same action // to avoid the progress bar fill up multiple times. AddOrUpdateResult updateResult = UpdateSourceFromDetails(details, progress); if (updateResult.MetadataWritten) { auto detailsInternal = sourceList.GetSource(details.Name); detailsInternal->CopyMetadataFieldsFrom(details); sourceList.SaveMetadata(*detailsInternal); } if (!updateResult.UpdateChecked) { AICLI_LOG(Repo, Error, << "Failed to update source: " << details.Name); result.emplace_back(details); } } catch (...) { LOG_CAUGHT_EXCEPTION(); AICLI_LOG(Repo, Error, << "Failed to update source: " << details.Name); result.emplace_back(details); } } return result; } bool Source::Remove(IProgressCallback& progress) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_isSourceToBeAdded || m_sourceReferences.size() != 1 || m_source); const auto& details = m_sourceReferences[0]->GetDetails(); AICLI_LOG(Repo, Info, << "Named source to be removed, found: " << details.Name << " [" << ToString(details.Origin) << ']'); EnsureSourceIsRemovable(details); bool result = RemoveSourceFromDetails(details, progress); if (result) { SourceList sourceList; sourceList.RemoveSource(details); } return result; } void Source::Edit(const SourceEdit& edits) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_isSourceToBeAdded || m_sourceReferences.size() != 1 || m_source); auto& details = m_sourceReferences[0]->GetDetails(); AICLI_LOG(Repo, Info, << "Named source to be edited, found: " << details.Name << " [" << ToString(details.Origin) << ']'); // This is intentionally the same policy checks as Remove. If the source cannot be removed then it cannot be edited. EnsureSourceIsRemovable(details); if (RequiresChanges(edits)) { if (edits.Explicit.has_value()) { details.Explicit = edits.Explicit.value(); } if (edits.Priority.has_value()) { details.Priority = edits.Priority.value(); } // Apply the edits and update source list. SourceList sourceList; sourceList.EditSource(details); } } bool Source::RequiresChanges(const SourceEdit& edits) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_sourceReferences.size() != 1); const auto& details = m_sourceReferences[0]->GetDetails(); bool isChanged = false; if (edits.Explicit.has_value() && edits.Explicit.value() != details.Explicit) { isChanged = true; } if (edits.Priority.has_value() && edits.Priority.value() != details.Priority) { isChanged = true; } return isChanged; } PackageTrackingCatalog Source::GetTrackingCatalog() const { // With C++20, consider removing the shared_ptr here and making the one inside PackageTrackingCatalog atomic. std::shared_ptr currentTrackingCatalog = std::atomic_load(&m_trackingCatalog); if (!currentTrackingCatalog) { std::shared_ptr newTrackingCatalog = std::make_shared(PackageTrackingCatalog::CreateForSource(*this)); if (std::atomic_compare_exchange_strong(&m_trackingCatalog, ¤tTrackingCatalog, newTrackingCatalog)) { currentTrackingCatalog = newTrackingCatalog; } } return *currentTrackingCatalog; } std::vector Source::GetCurrentSources() { SourceList sourceList; std::vector result; for (auto&& source : sourceList.GetCurrentSourceRefs()) { result.emplace_back(std::move(source)); } return result; } bool Source::DropSource(std::string_view name) { if (name.empty()) { SourceList::RemoveSettingsStreams(); return true; } else { SourceList sourceList; auto source = sourceList.GetCurrentSource(name); if (!source) { AICLI_LOG(Repo, Info, << "Named source to be dropped, but not found: " << name); return false; } else { AICLI_LOG(Repo, Info, << "Named source to be dropped, found: " << source->Name); EnsureSourceIsRemovable(*source); sourceList.RemoveSource(*source); return true; } } } std::string_view Source::GetDefaultSourceType() { return ISourceFactory::GetForType("")->TypeName(); } #ifndef AICLI_DISABLE_TEST_HOOKS void TestHook_SetSourceFactoryOverride(const std::string& type, std::function()>&& factory) { s_Sources_TestHook_SourceFactories[type] = std::move(factory); } void TestHook_ClearSourceFactoryOverrides() { s_Sources_TestHook_SourceFactories.clear(); } #endif } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/RestClient.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RestClient.h" #include "RestInformationCache.h" #include "Rest/Schema/1_0/Interface.h" #include "Rest/Schema/1_1/Interface.h" #include "Rest/Schema/1_4/Interface.h" #include "Rest/Schema/1_5/Interface.h" #include "Rest/Schema/1_6/Interface.h" #include "Rest/Schema/1_7/Interface.h" #include "Rest/Schema/1_9/Interface.h" #include "Rest/Schema/1_10/Interface.h" #include "Rest/Schema/1_12/Interface.h" #include "Rest/Schema/InformationResponseDeserializer.h" #include "Rest/Schema/CommonRestConstants.h" #include #include #include #include using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_0; using namespace AppInstaller::Utility; using namespace AppInstaller::Http; namespace AppInstaller::Repository::Rest { // Supported versions std::set WingetSupportedContracts = { Version_1_0_0, Version_1_1_0, Version_1_4_0, Version_1_5_0, Version_1_6_0, Version_1_7_0, Version_1_9_0, Version_1_10_0, Version_1_12_0, }; constexpr std::string_view WindowsPackageManagerHeader = "Windows-Package-Manager"sv; constexpr size_t WindowsPackageManagerHeaderMaxLength = 1024; namespace { HttpClientHelper::HttpRequestHeaders GetHeaders(const std::optional& customHeader, std::string_view caller) { HttpClientHelper::HttpRequestHeaders headers; if (customHeader) { AICLI_LOG(Repo, Verbose, << "Custom header found: " << customHeader.value()); THROW_HR_IF(APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH, customHeader.value().size() > WindowsPackageManagerHeaderMaxLength); headers.emplace(JSON::GetUtilityString(WindowsPackageManagerHeader), JSON::GetUtilityString(customHeader.value())); } if (!caller.empty()) { AICLI_LOG(Repo, Verbose, << "User agent caller found: " << caller); std::wstring userAgentWide = JSON::GetUtilityString(Runtime::GetUserAgent(caller)); try { // Replace user profile if the caller binary is under user profile. userAgentWide = Utility::ReplaceWhileCopying(userAgentWide, Runtime::GetPathTo(Runtime::PathName::UserProfile).wstring(), L"%USERPROFILE%"); } CATCH_LOG(); headers.emplace(web::http::header_names::user_agent, userAgentWide); } return headers; } } RestClient::RestClient(std::unique_ptr supportedInterface, std::string sourceIdentifier) : m_interface(std::move(supportedInterface)), m_sourceIdentifier(std::move(sourceIdentifier)) { } std::optional RestClient::GetManifestByVersion(const std::string& packageId, const std::string& version, const std::string& channel) const { return m_interface->GetManifestByVersion(packageId, version, channel); } IRestClient::SearchResult RestClient::Search(const SearchRequest& request) const { return m_interface->Search(request); } std::string RestClient::GetSourceIdentifier() const { return m_sourceIdentifier; } IRestClient::Information RestClient::GetSourceInformation() const { return m_interface->GetSourceInformation(); } std::optional RestClient::GetLatestCommonVersion( const std::vector& serverSupportedVersions, const std::set& wingetSupportedVersions) { std::set commonVersions; for (auto& version : serverSupportedVersions) { Version versionInfo(version); auto itr = std::find_if(wingetSupportedVersions.begin(), wingetSupportedVersions.end(), [&](const Version& v) { // Only check major and minor version match if applicable if (v.GetParts().size() >= 2) { return versionInfo.GetParts().size() >= 2 && versionInfo.GetParts().at(0) == v.GetParts().at(0) && versionInfo.GetParts().at(1) == v.GetParts().at(1); } else { return versionInfo == v; } }); if (itr != wingetSupportedVersions.end()) { commonVersions.insert(*itr); } } if (commonVersions.empty()) { return {}; } return *commonVersions.rbegin(); } Schema::IRestClient::Information RestClient::GetInformation(const std::string& restApi, const std::optional& customHeader, std::string_view caller, const HttpClientHelper& helper) { utility::string_t restEndpoint = AppInstaller::Rest::GetRestAPIBaseUri(restApi); THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !AppInstaller::Rest::IsValidUri(restEndpoint)); utility::string_t endpoint = AppInstaller::Rest::AppendPathToUri(restEndpoint, JSON::GetUtilityString(InformationGetEndpoint)); // Check the cache for a valid information entry RestInformationCache informationCache; std::optional cachedInformation = informationCache.Get(endpoint, customHeader, caller); if (cachedInformation) { return std::move(cachedInformation).value(); } // Not in cache, make REST call to retrieve it auto headers = GetHeaders(customHeader, caller); CacheControlPolicy cacheControl; std::optional response = helper.HandleGet( endpoint, headers, {}, [&](const web::http::http_response& httpResponse) { cacheControl = CacheControlPolicy{ httpResponse.headers().cache_control() }; return Http::HttpClientHelper::HttpResponseHandlerResult{ std::nullopt, true }; }); THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !response); InformationResponseDeserializer responseDeserializer; auto result = responseDeserializer.Deserialize(response.value()); // Cache the information value as requested informationCache.Cache(endpoint, customHeader, caller, cacheControl, std::move(response).value()); return result; } std::unique_ptr RestClient::GetSupportedInterface( const std::string& api, const HttpClientHelper::HttpRequestHeaders& additionalHeaders, const IRestClient::Information& information, const Authentication::AuthenticationArguments& authArgs, const Version& version, const HttpClientHelper& helper) { if (version == Version_1_0_0) { return std::make_unique(api, helper); } else if (version == Version_1_1_0) { return std::make_unique(api, helper, information, additionalHeaders); } else if (version == Version_1_4_0) { return std::make_unique(api, helper, information, additionalHeaders); } else if (version == Version_1_5_0) { return std::make_unique(api, helper, information, additionalHeaders); } else if (version == Version_1_6_0) { return std::make_unique(api, helper, information, additionalHeaders); } else if (version == Version_1_7_0) { return std::make_unique(api, helper, information, additionalHeaders, authArgs); } else if (version == Version_1_9_0) { return std::make_unique(api, helper, information, additionalHeaders, authArgs); } else if (version == Version_1_10_0) { return std::make_unique(api, helper, information, additionalHeaders, authArgs); } else if (version == Version_1_12_0) { return std::make_unique(api, helper, information, additionalHeaders, authArgs); } THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION); } RestClient RestClient::Create( const std::string& restApi, const std::optional& customHeader, std::string_view caller, const HttpClientHelper& helper, const Schema::IRestClient::Information& information, const Authentication::AuthenticationArguments& authArgs) { utility::string_t restEndpoint = AppInstaller::Rest::GetRestAPIBaseUri(restApi); THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !AppInstaller::Rest::IsValidUri(restEndpoint)); auto headers = GetHeaders(customHeader, caller); std::optional latestCommonVersion = GetLatestCommonVersion(information.ServerSupportedVersions, WingetSupportedContracts); THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !latestCommonVersion); std::unique_ptr supportedInterface = GetSupportedInterface(utility::conversions::to_utf8string(restEndpoint), headers, information, authArgs, latestCommonVersion.value(), helper); return RestClient{ std::move(supportedInterface), information.SourceIdentifier }; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/RestClient.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Rest/Schema/IRestClient.h" #include "ISource.h" #include namespace AppInstaller::Repository::Rest { struct RestClient { RestClient(const RestClient&) = delete; RestClient& operator=(const RestClient&) = delete; RestClient(RestClient&&) = default; RestClient& operator=(RestClient&&) = default; // Performs a search based on the given criteria. Schema::IRestClient::SearchResult Search(const SearchRequest& request) const; std::optional GetManifestByVersion(const std::string& packageId, const std::string& version, const std::string& channel) const; std::string GetSourceIdentifier() const; Schema::IRestClient::Information GetSourceInformation() const; static std::optional GetLatestCommonVersion(const std::vector& serverSupportedVersions, const std::set& wingetSupportedVersions); // Responsible for getting the source information contracts with minimal validation. Does not try to create a rest interface out of it. static Schema::IRestClient::Information GetInformation(const std::string& restApi, const std::optional& customHeader, std::string_view caller, const Http::HttpClientHelper& helper); static std::unique_ptr GetSupportedInterface( const std::string& restApi, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders, const Schema::IRestClient::Information& information, const Authentication::AuthenticationArguments& authArgs, const AppInstaller::Utility::Version& version, const Http::HttpClientHelper& helper); // Creates the rest client. Full validation performed (just as opening the source) static RestClient Create( const std::string& restApi, const std::optional& customHeader, std::string_view caller, const Http::HttpClientHelper& helper, const Schema::IRestClient::Information& information, const Authentication::AuthenticationArguments& authArgs = {}); private: RestClient(std::unique_ptr supportedInterface, std::string sourceIdentifier); std::unique_ptr m_interface; std::string m_sourceIdentifier; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/RestInformationCache.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RestInformationCache.h" #include "Rest/Schema/InformationResponseDeserializer.h" #include namespace AppInstaller::Repository::Rest { namespace { constexpr std::wstring_view s_EndpointName = L"endpoint"sv; constexpr std::wstring_view s_HashName = L"hash"sv; constexpr std::wstring_view s_ExpirationName = L"expiration"sv; constexpr std::wstring_view s_DataName = L"data"sv; // Calculates the hash of values that might change per-call. Utility::SHA256::HashBuffer GetHash(const std::optional& customHeader, std::string_view caller) { std::stringstream stream; if (customHeader) { stream << customHeader.value(); } stream << '|' << caller; return Utility::SHA256::ComputeHash(stream); } uint64_t CalculateExpiration(std::chrono::seconds duration) { // If no expiration information is provided, use 1 minute if (!duration.count()) { duration = 60s; } return Utility::ConvertSystemClockToUnixEpoch(std::chrono::system_clock::now() + duration); } } std::optional RestInformationCache::Get(const std::wstring& endpoint, const std::optional& customHeader, std::string_view caller) #ifdef AICLI_DISABLE_TEST_HOOKS try #endif { LoadCacheView(); Utility::SHA256::HashBuffer hashValue = GetHash(customHeader, caller); CacheItem* item = FindCacheItem(endpoint, hashValue); // If we don't find a private match, see if there is a public one. if (!item) { item = FindCacheItem(endpoint, {}); } if (!item) { return std::nullopt; } Schema::InformationResponseDeserializer responseDeserializer; return responseDeserializer.Deserialize(item->Data); } #ifdef AICLI_DISABLE_TEST_HOOKS catch (...) { LOG_CAUGHT_EXCEPTION_MSG("RestInformationCache::Get exception"); return std::nullopt; } #endif void RestInformationCache::Cache(const std::wstring& endpoint, const std::optional& customHeader, std::string_view caller, const Utility::CacheControlPolicy& cacheControl, web::json::value response) #ifdef AICLI_DISABLE_TEST_HOOKS try #endif { // If requested, do not cache this response. // Since this data is small, treat no-cache as no-store. if (cacheControl.NoStore || cacheControl.NoCache) { return; } // If not public, we use the header values to differentiate the cache items. Utility::SHA256::HashBuffer hashValue; if (!cacheControl.Public) { hashValue = GetHash(customHeader, caller); } uint64_t expirationEpoch = CalculateExpiration(std::chrono::seconds{ cacheControl.MaxAge }); // Due to the exchange semantics on the setting stream, we may have to retry storing the value. for (int i = 0; i < 10; ++i) { CacheItem* item = FindCacheItem(endpoint, hashValue); if (!item) { item = &m_cacheView.emplace_back(); item->Endpoint = endpoint; item->Hash = hashValue; } item->UnixEpochExpiration = expirationEpoch; item->Data = std::move(response); if (StoreCacheView()) { AICLI_LOG(Repo, Verbose, << "RestInformationCache stored information for: " << Utility::ConvertToUTF8(endpoint)); return; } else { // Extract the response back from the item for the next iteration response = std::move(item->Data); // Failed to store due to the cache changing, reload and try again. LoadCacheView(); } } AICLI_LOG(Repo, Warning, << "RestInformationCache failed to store information cache after 10 attempts."); } #ifdef AICLI_DISABLE_TEST_HOOKS CATCH_LOG(); #endif void RestInformationCache::LoadCacheView() { using namespace web::json; std::unique_ptr stream = m_settingsStream.Get(); m_cacheView.clear(); if (!stream) { return; } value cacheValue = value::parse(*stream); if (!cacheValue.is_array()) { AICLI_LOG(Repo, Warning, << "RestInformationCache value was not an array."); return; } array& cacheArray = cacheValue.as_array(); for (const value& cacheItemValue : cacheArray) { if (!cacheItemValue.is_object()) { AICLI_LOG(Repo, Warning, << "RestInformationCache cache item was not an object."); continue; } std::optional expiration = JSON::GetRawUInt64ValueFromJsonNode(cacheItemValue, std::wstring{ s_ExpirationName }); if (!expiration) { AICLI_LOG(Repo, Warning, << "RestInformationCache cache item missing expiration."); continue; } if (std::chrono::system_clock::now() > Utility::ConvertUnixEpochToSystemClock(expiration.value())) { AICLI_LOG(Repo, Verbose, << "RestInformationCache cache item has expired."); continue; } std::optional endpoint = JSON::GetWideStringValueFromJsonNode(cacheItemValue, std::wstring{ s_EndpointName }); if (!JSON::IsValidNonEmptyStringValue(endpoint)) { AICLI_LOG(Repo, Warning, << "RestInformationCache cache item missing endpoint."); continue; } CacheItem cacheItem; cacheItem.Endpoint = endpoint.value(); cacheItem.UnixEpochExpiration = expiration.value(); std::optional hash = JSON::GetWideStringValueFromJsonNode(cacheItemValue, std::wstring{ s_HashName }); if (JSON::IsValidNonEmptyStringValue(hash)) { cacheItem.Hash = Utility::SHA256::ConvertToBytes(hash.value()); } auto dataValue = JSON::GetJsonValueFromNode(cacheItemValue, std::wstring{ s_DataName }); if (!dataValue) { AICLI_LOG(Repo, Warning, << "RestInformationCache cache item missing data."); continue; } cacheItem.Data = dataValue.value().get(); if (cacheItem.Data.is_null()) { AICLI_LOG(Repo, Warning, << "RestInformationCache cache item data value null."); continue; } m_cacheView.emplace_back(std::move(cacheItem)); } } RestInformationCache::CacheItem* RestInformationCache::FindCacheItem(const std::wstring& endpoint, const Utility::SHA256::HashBuffer& hash) { for (CacheItem& item : m_cacheView) { if (item.Endpoint == endpoint && Utility::SHA256::AreEqual(item.Hash, hash)) { return &item; } } return nullptr; } [[nodiscard]] bool RestInformationCache::StoreCacheView() { using namespace web::json; value cacheValue = value::array(); array& cacheArray = cacheValue.as_array(); for (const CacheItem& item : m_cacheView) { value cacheItemValue = value::object(); object& cacheItemObject = cacheItemValue.as_object(); cacheItemObject[std::wstring{ s_EndpointName }] = value::value(item.Endpoint); cacheItemObject[std::wstring{ s_HashName }] = value::value(Utility::ConvertToUTF16(Utility::ConvertToHexString(item.Hash))); cacheItemObject[std::wstring{ s_ExpirationName }] = value::value(item.UnixEpochExpiration); cacheItemObject[std::wstring{ s_DataName }] = item.Data; cacheArray[cacheArray.size()] = std::move(cacheItemValue); } std::stringstream stream; cacheValue.serialize(stream); return m_settingsStream.Set(std::move(stream).str()); } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/RestInformationCache.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/IRestClient.h" #include #include #include #include #include #include #include namespace AppInstaller::Repository::Rest { // Provides access to cached responses to the /information request. struct RestInformationCache { // Attempts to get a cached information response for the provided inputs. std::optional Get(const std::wstring& endpoint, const std::optional& customHeader, std::string_view caller); // Stores the information response as appropriate. void Cache(const std::wstring& endpoint, const std::optional& customHeader, std::string_view caller, const Utility::CacheControlPolicy& cacheControl, web::json::value response); private: struct CacheItem { std::wstring Endpoint; Utility::SHA256::HashBuffer Hash; uint64_t UnixEpochExpiration = 0; web::json::value Data; }; // Reads from the cache, constructing our view of the items it contains. // Discards any expired items while reading the cache. void LoadCacheView(); // Finds the cache item for the given inputs, or nullptr if it is not found. CacheItem* FindCacheItem(const std::wstring& endpoint, const Utility::SHA256::HashBuffer& hash); // Attempts to store the current cache view back to the cache. // Returns true if successful; false if the cache was updated since our last read and a retry is necessary. [[nodiscard]] bool StoreCacheView(); Settings::Stream m_settingsStream{ Settings::Stream::RestInformationCache }; std::vector m_cacheView; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/RestSource.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RestSource.h" #include "MatchCriteriaResolver.h" using namespace AppInstaller::Utility; namespace AppInstaller::Repository::Rest { namespace { using namespace AppInstaller::Repository::Rest::Schema; // The source reference used by package objects. struct SourceReference { SourceReference(const std::shared_ptr& source) : m_source(source) {} protected: std::shared_ptr GetReferenceSource() const { std::shared_ptr source = m_source.lock(); THROW_HR_IF(E_NOT_VALID_STATE, !source); return source; } private: std::weak_ptr m_source; }; // The IPackage implementation for Available packages from RestSource. struct RestPackage : public std::enable_shared_from_this, public SourceReference, public IPackage, public ICompositePackage { static constexpr IPackageType PackageType = IPackageType::RestPackage; RestPackage(const std::shared_ptr& source, IRestClient::Package&& package) : SourceReference(source), m_package(std::move(package)) { SortVersionsInternal(); } // Inherited via IPackage Utility::LocIndString GetProperty(PackageProperty property) const override { switch (property) { case PackageProperty::Id: return Utility::LocIndString{ m_package.PackageInformation.PackageIdentifier }; case PackageProperty::Name: return Utility::LocIndString{ m_package.PackageInformation.PackageName }; default: THROW_HR(E_UNEXPECTED); } } std::vector GetMultiProperty(PackageMultiProperty property) const override; std::vector GetVersionKeys() const override { std::shared_ptr source = GetReferenceSource(); std::scoped_lock versionsLock{ m_packageVersionsLock }; std::vector result; for (const auto& versionInfo : m_package.Versions) { result.emplace_back( source->GetIdentifier(), versionInfo.VersionAndChannel.GetVersion().ToString(), versionInfo.VersionAndChannel.GetChannel().ToString()); } return result; } std::shared_ptr GetLatestVersion() const override { std::scoped_lock versionsLock{ m_packageVersionsLock }; return GetLatestVersionInternal(); } std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const override; Source GetSource() const override { return Source{ GetReferenceSource() }; } bool IsSame(const IPackage* other) const override { const RestPackage* otherPackage = PackageCast(other); if (otherPackage) { return GetReferenceSource()->IsSame(otherPackage->GetReferenceSource().get()) && Utility::CaseInsensitiveEquals(m_package.PackageInformation.PackageIdentifier, otherPackage->m_package.PackageInformation.PackageIdentifier); } return false; } const void* CastTo(IPackageType type) const override { if (type == PackageType) { return this; } return nullptr; } // Inherited via ICompositePackage std::shared_ptr GetInstalled() override { return {}; } std::vector> GetAvailable() override { return std::vector>{ shared_from_this() }; } // Helpers for PackageVersion interop const IRestClient::PackageInfo& PackageInfo() const { return m_package.PackageInformation; } // This function is designed to handle the case where the only version that is returned by the // initial search is Unknown. In that case, we perform a search intended to trigger the optimized // path and directly get all manifests. bool HandleSingleUnknownVersion(IRestClient::VersionInfo& versionInfo) { // If the calling version is unknown then we want to update it if we already // have the results in the package. if (versionInfo.VersionAndChannel.GetVersion().IsUnknown() && !versionInfo.Manifest) { std::scoped_lock versionsLock{ m_packageVersionsLock }; if (m_package.Versions.size() == 1 && m_package.Versions[0].VersionAndChannel.GetVersion().IsUnknown() && !m_package.Versions[0].Manifest) { SearchRequest request; request.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, m_package.PackageInformation.PackageIdentifier); IRestClient::SearchResult result = GetReferenceSource()->GetRestClient().Search(request); if (result.Matches.size() == 1) { m_package.Versions = std::move(result.Matches[0].Versions); SortVersionsInternal(); } else { // Unexpected, but just leave things as they are AICLI_LOG(Repo, Warning, << "Found " << result.Matches.size() << " matches for optimized search of " << m_package.PackageInformation.PackageIdentifier); } } if (!m_package.Versions.empty()) { // The results are now sorted; either take the last one if it is unknown // or the first one if it is not (aka latest). if (m_package.Versions.back().VersionAndChannel.GetVersion().IsUnknown()) { versionInfo = m_package.Versions.back(); } else { versionInfo = m_package.Versions.front(); } } return true; } return false; } private: std::shared_ptr NonConstSharedFromThis() const { return const_cast(this)->shared_from_this(); } // Must hold m_packageVersionsLock while calling this std::shared_ptr GetLatestVersionInternal() const; // Must hold m_packageVersionsLock while calling this void SortVersionsInternal() { std::sort(m_package.Versions.begin(), m_package.Versions.end(), [](const IRestClient::VersionInfo& a, const IRestClient::VersionInfo& b) { return a.VersionAndChannel < b.VersionAndChannel; }); } IRestClient::Package m_package; // Protects access to m_package.Versions mutable std::mutex m_packageVersionsLock; }; void GetMultiPropertyValues( const RestPackage* package, const IRestClient::VersionInfo& versionInfo, PackageVersionMultiProperty property, std::vector& result, void (*Action)(std::vector&, Utility::LocIndString&&)) { switch (property) { case PackageVersionMultiProperty::PackageFamilyName: for (const std::string& pfn : versionInfo.PackageFamilyNames) { Action(result, Utility::LocIndString{ pfn }); } break; case PackageVersionMultiProperty::ProductCode: for (const std::string& productCode : versionInfo.ProductCodes) { Action(result, Utility::LocIndString{ productCode }); } break; case PackageVersionMultiProperty::UpgradeCode: for (const std::string& upgradeCode : versionInfo.UpgradeCodes) { Action(result, Utility::LocIndString{ upgradeCode }); } break; case PackageVersionMultiProperty::Name: if (versionInfo.Manifest) { for (auto&& name : versionInfo.Manifest->GetPackageNames()) { Action(result, Utility::LocIndString{ std::move(name) }); } } else { Action(result, Utility::LocIndString{ package->PackageInfo().PackageName }); } break; case PackageVersionMultiProperty::Publisher: if (versionInfo.Manifest) { for (auto&& publisher : versionInfo.Manifest->GetPublishers()) { Action(result, Utility::LocIndString{ std::move(publisher) }); } } else { Action(result, Utility::LocIndString{ package->PackageInfo().Publisher }); } break; case PackageVersionMultiProperty::Locale: if (versionInfo.Manifest) { Action(result, Utility::LocIndString{ versionInfo.Manifest->DefaultLocalization.Locale }); for (const auto& loc : versionInfo.Manifest->Localizations) { Action(result, Utility::LocIndString{ loc.Locale }); } } break; } } std::vector RestPackage::GetMultiProperty(PackageMultiProperty property) const { std::scoped_lock versionsLock{ m_packageVersionsLock }; std::vector result; PackageVersionMultiProperty mappedProperty = PackageMultiPropertyToPackageVersionMultiProperty(property); for (const auto& versionInfo : m_package.Versions) { GetMultiPropertyValues( this, versionInfo, mappedProperty, result, [](std::vector& result, Utility::LocIndString&& string) { auto itr = std::lower_bound(result.begin(), result.end(), string); if (itr == result.end() || *itr != string) { result.emplace(itr, std::move(string)); } }); } return result; } // The IPackageVersion impl for RestSource. struct PackageVersion : public SourceReference, public IPackageVersion { PackageVersion( const std::shared_ptr& source, std::shared_ptr&& package, IRestClient::VersionInfo versionInfo) : SourceReference(source), m_package(std::move(package)), m_versionInfo(std::move(versionInfo)) {} // Inherited via IPackageVersion Utility::LocIndString GetProperty(PackageVersionProperty property) const override { switch (property) { case PackageVersionProperty::SourceIdentifier: return Utility::LocIndString{ GetReferenceSource()->GetIdentifier() }; case PackageVersionProperty::SourceName: return Utility::LocIndString{ GetReferenceSource()->GetDetails().Name }; case PackageVersionProperty::Id: return Utility::LocIndString{ m_package->PackageInfo().PackageIdentifier }; case PackageVersionProperty::Name: return Utility::LocIndString{ m_package->PackageInfo().PackageName }; case PackageVersionProperty::Version: return Utility::LocIndString{ m_versionInfo.VersionAndChannel.GetVersion().ToString() }; case PackageVersionProperty::Channel: return Utility::LocIndString{ m_versionInfo.VersionAndChannel.GetChannel().ToString() }; case PackageVersionProperty::Publisher: return Utility::LocIndString{ m_package->PackageInfo().Publisher }; case PackageVersionProperty::ArpMinVersion: if (!m_versionInfo.ArpVersions.empty()) { return Utility::LocIndString{ m_versionInfo.ArpVersions.front().ToString() }; } else if (m_versionInfo.Manifest) { auto arpVersionRange = m_versionInfo.Manifest->GetArpVersionRange(); return arpVersionRange.IsEmpty() ? Utility::LocIndString{} : Utility::LocIndString{ arpVersionRange.GetMinVersion().ToString() }; } else { return {}; } case PackageVersionProperty::ArpMaxVersion: if (!m_versionInfo.ArpVersions.empty()) { return Utility::LocIndString{ m_versionInfo.ArpVersions.back().ToString() }; } else if (m_versionInfo.Manifest) { auto arpVersionRange = m_versionInfo.Manifest->GetArpVersionRange(); return arpVersionRange.IsEmpty() ? Utility::LocIndString{} : Utility::LocIndString{ arpVersionRange.GetMaxVersion().ToString() }; } else { return {}; } default: return {}; } } std::vector GetMultiProperty(PackageVersionMultiProperty property) const override { std::vector result; GetMultiPropertyValues( m_package.get(), m_versionInfo, property, result, [](std::vector& result, Utility::LocIndString&& string) { result.emplace_back(std::move(string)); }); return result; } Manifest::Manifest GetManifest() override { AICLI_LOG(Repo, Verbose, << "Getting manifest"); if (m_versionInfo.Manifest) { return m_versionInfo.Manifest.value(); } if (m_package->HandleSingleUnknownVersion(m_versionInfo) && m_versionInfo.Manifest) { return m_versionInfo.Manifest.value(); } std::optional manifest = GetReferenceSource()->GetRestClient().GetManifestByVersion( m_package->PackageInfo().PackageIdentifier, m_versionInfo.VersionAndChannel.GetVersion().ToString(), m_versionInfo.VersionAndChannel.GetChannel().ToString()); if (!manifest) { AICLI_LOG(Repo, Verbose, << "Valid manifest not found for package: " << m_package->PackageInfo().PackageIdentifier); return {}; } m_versionInfo.Manifest = std::move(manifest.value()); return m_versionInfo.Manifest.value(); } Source GetSource() const override { return Source{ GetReferenceSource() }; } IPackageVersion::Metadata GetMetadata() const override { IPackageVersion::Metadata result; return result; } private: std::shared_ptr m_package; IRestClient::VersionInfo m_versionInfo; }; std::shared_ptr RestPackage::GetVersion(const PackageVersionKey& versionKey) const { std::shared_ptr source = GetReferenceSource(); std::scoped_lock versionsLock{ m_packageVersionsLock }; // Ensure that this key targets this (or any) source if (!versionKey.SourceId.empty() && versionKey.SourceId != source->GetIdentifier()) { return {}; } std::shared_ptr packageVersion; if (!versionKey.Version.empty() && !versionKey.Channel.empty()) { for (const auto& versionInfo : m_package.Versions) { if (CaseInsensitiveEquals(versionInfo.VersionAndChannel.GetVersion().ToString(), versionKey.Version) && CaseInsensitiveEquals(versionInfo.VersionAndChannel.GetChannel().ToString(), versionKey.Channel)) { packageVersion = std::make_shared(source, NonConstSharedFromThis(), versionInfo); break; } } } else if (versionKey.Version.empty() && versionKey.Channel.empty()) { packageVersion = GetLatestVersionInternal(); } else if (versionKey.Version.empty()) { for (const auto& versionInfo : m_package.Versions) { if (CaseInsensitiveEquals(versionInfo.VersionAndChannel.GetChannel().ToString(), versionKey.Channel)) { packageVersion = std::make_shared(source, NonConstSharedFromThis(), versionInfo); break; } } } else if (versionKey.Channel.empty()) { for (const auto& versionInfo : m_package.Versions) { if (CaseInsensitiveEquals(versionInfo.VersionAndChannel.GetVersion().ToString(), versionKey.Version)) { packageVersion = std::make_shared(source, NonConstSharedFromThis(), versionInfo); break; } } } return packageVersion; } std::shared_ptr RestPackage::GetLatestVersionInternal() const { return std::make_shared(GetReferenceSource(), NonConstSharedFromThis(), m_package.Versions.front()); } } RestSource::RestSource(const SourceDetails& details, SourceInformation information, RestClient&& restClient) : m_details(details), m_information(std::move(information)), m_restClient(std::move(restClient)) { } const std::string& RestSource::GetIdentifier() const { return m_details.Identifier; } const SourceDetails& RestSource::GetDetails() const { return m_details; } SourceInformation RestSource::GetInformation() const { return m_information; } bool RestSource::QueryFeatureFlag(SourceFeatureFlag flag) const { switch (flag) { case SourceFeatureFlag::ManifestMayContainAdditionalSystemReferenceStrings: return true; } return false; } SearchResult RestSource::Search(const SearchRequest& request) const { IRestClient::SearchResult results = m_restClient.Search(request); SearchResult searchResult; std::shared_ptr sharedThis = NonConstSharedFromThis(); for (auto& result : results.Matches) { std::shared_ptr package = std::make_shared(sharedThis, std::move(result)); PackageMatchFilter packageFilter{ FindBestMatchCriteria(request, package->GetLatestVersion().get()) }; searchResult.Matches.emplace_back(std::move(package), std::move(packageFilter)); } searchResult.Truncated = results.Truncated; return searchResult; } void* RestSource::CastTo(ISourceType type) { if (type == SourceType) { return this; } return nullptr; } const RestClient& RestSource::GetRestClient() const { return m_restClient; } bool RestSource::IsSame(const RestSource* other) const { return (other && GetIdentifier() == other->GetIdentifier()); } std::shared_ptr RestSource::NonConstSharedFromThis() const { return const_cast(this)->shared_from_this(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/RestSource.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" #include "RestClient.h" namespace AppInstaller::Repository::Rest { // A source that holds a RestSource. struct RestSource : public std::enable_shared_from_this, public ISource { static constexpr ISourceType SourceType = ISourceType::RestSource; RestSource(const SourceDetails& details, SourceInformation information, RestClient&& restClient); RestSource(const RestSource&) = delete; RestSource& operator=(const RestSource&) = delete; RestSource(RestSource&&) = default; RestSource& operator=(RestSource&&) = default; ~RestSource() = default; // Gets the source's identifier; a unique identifier independent of the name // that will not change between a remove/add or between additional adds. // Must be suitable for filesystem names. const std::string& GetIdentifier() const override; // Get the source's details. const SourceDetails& GetDetails() const override; SourceInformation GetInformation() const override; bool QueryFeatureFlag(SourceFeatureFlag flag) const override; // Execute a search on the source. SearchResult Search(const SearchRequest& request) const override; // Casts to the requested type. void* CastTo(ISourceType type) override; // Gets the rest client. const RestClient& GetRestClient() const; // Determines if the other source refers to the same as this. bool IsSame(const RestSource* other) const; private: std::shared_ptr NonConstSharedFromThis() const; SourceDetails m_details; SourceInformation m_information; RestClient m_restClient; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RestSourceFactory.h" #include "RestClient.h" #include "RestSource.h" using namespace std::string_literals; using namespace std::string_view_literals; namespace AppInstaller::Repository::Rest { namespace { struct RestSourceReference : public ISourceReference { RestSourceReference(const SourceDetails& details) : m_details(details) {} SourceDetails& GetDetails() override { return m_details; }; std::string GetIdentifier() override { Initialize(); return m_details.Identifier; } SourceInformation GetInformation() override { Initialize(); return m_information; } // Set custom header. Returns false if custom header is not supported. bool SetCustomHeader(std::optional header) override { m_customHeader = header; return true; } void SetCaller(std::string caller) override { m_caller = std::move(caller); } void SetAuthenticationArguments(Authentication::AuthenticationArguments authArgs) override { m_authArgs = std::move(authArgs); } std::shared_ptr Open(IProgressCallback&) override { Initialize(); RestClient restClient = RestClient::Create(m_details.Arg, m_customHeader, m_caller, m_httpClientHelper, m_restClientInformation, m_authArgs); return std::make_shared(m_details, m_information, std::move(restClient)); } void SetThreadGlobals(const std::shared_ptr& threadGlobals) override { m_threadGlobals = threadGlobals; } private: void Initialize() { std::call_once(m_initializeFlag, [&]() { m_httpClientHelper.SetPinningConfiguration(m_details.CertificatePinningConfiguration, m_threadGlobals); m_restClientInformation = RestClient::GetInformation(m_details.Arg, m_customHeader, m_caller, m_httpClientHelper); m_details.Identifier = m_restClientInformation.SourceIdentifier; m_information.UnsupportedPackageMatchFields = m_restClientInformation.UnsupportedPackageMatchFields; m_information.RequiredPackageMatchFields = m_restClientInformation.RequiredPackageMatchFields; m_information.UnsupportedQueryParameters = m_restClientInformation.UnsupportedQueryParameters; m_information.RequiredQueryParameters = m_restClientInformation.RequiredQueryParameters; m_information.SourceAgreementsIdentifier = m_restClientInformation.SourceAgreementsIdentifier; for (auto const& agreement : m_restClientInformation.SourceAgreements) { m_information.SourceAgreements.emplace_back(agreement.Label, agreement.Text, agreement.Url); } m_information.Authentication = m_restClientInformation.Authentication; }); } SourceDetails m_details; Http::HttpClientHelper m_httpClientHelper; SourceInformation m_information; Schema::IRestClient::Information m_restClientInformation; std::optional m_customHeader; std::string m_caller; Authentication::AuthenticationArguments m_authArgs; std::once_flag m_initializeFlag; std::shared_ptr m_threadGlobals; }; // The base class for data that comes from a rest based source. struct RestSourceFactoryImpl : public ISourceFactory { std::string_view TypeName() const override final { return RestSourceFactory::Type(); } std::shared_ptr Create(const SourceDetails& details) override final { THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); return std::make_shared(details); } bool Add(SourceDetails& details, IProgressCallback&) override final { if (details.Type.empty()) { details.Type = RestSourceFactory::Type(); } else { THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); } // Check if URL is remote and secure THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NOT_REMOTE, !Utility::IsUrlRemote(details.Arg)); THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE, !Utility::IsUrlSecure(details.Arg)); return true; } bool Update(const SourceDetails& details, IProgressCallback&) override final { THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); return true; } bool Remove(const SourceDetails& details, IProgressCallback&) override final { THROW_HR_IF(E_INVALIDARG, !Utility::CaseInsensitiveEquals(details.Type, RestSourceFactory::Type())); return true; } }; } std::unique_ptr RestSourceFactory::Create() { return std::make_unique(); } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/RestSourceFactory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" #include "SourceFactory.h" #include namespace AppInstaller::Repository::Rest { using namespace std::string_view_literals; // A source where the information is stored on a REST based server. // In addition, the manifest information is also available on the server. // Arg :: Expected to be a API which supports querying functionality. struct RestSourceFactory { // Get the type string for this source. static constexpr std::string_view Type() { using namespace std::string_view_literals; return "Microsoft.Rest"sv; } // Creates a source factory for this type. static std::unique_ptr Create(); }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_0/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/IRestClient.h" #include namespace AppInstaller::Repository::Rest::Schema::V1_0 { // Interface to this schema version exposed through IRestClient. struct Interface : public IRestClient { Interface(const std::string& restApi, const Http::HttpClientHelper& helper); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; IRestClient::Information GetSourceInformation() const override; IRestClient::SearchResult Search(const SearchRequest& request) const override; std::optional GetManifestByVersion(const std::string& packageId, const std::string& version, const std::string& channel) const override; std::vector GetManifests(const std::string& packageId, const std::map& params = {}) const override; protected: bool MeetsOptimizedSearchCriteria(const SearchRequest& request) const; IRestClient::SearchResult OptimizedSearch(const SearchRequest& request) const; IRestClient::SearchResult SearchInternal(const SearchRequest& request) const; // Check query params against source information and update if necessary. virtual std::map GetValidatedQueryParams(const std::map& params) const; // Check search request against source information and get json search body. virtual web::json::value GetValidatedSearchBody(const SearchRequest& searchRequest) const; virtual SearchResult GetSearchResult(const web::json::value& searchResponseObject) const; virtual std::vector GetParsedManifests(const web::json::value& manifestsResponseObject) const; // Gets auth headers if source requires authentication for access. virtual Http::HttpClientHelper::HttpRequestHeaders GetAuthHeaders() const; Http::HttpClientHelper::HttpRequestHeaders m_requiredRestApiHeaders; private: std::string m_restApiUri; utility::string_t m_searchEndpoint; Http::HttpClientHelper m_httpClientHelper; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Repository::Rest::Schema::V1_0::Json { // Manifest Deserializer. struct ManifestDeserializer { // Gets the manifest from the given json object received from a REST request std::vector Deserialize(const web::json::value& responseJsonObject) const; // Gets the manifest from the given json Data field std::vector DeserializeData(const web::json::value& dataJsonObject) const; // Deserializes the AppsAndFeaturesEntries node, returning the set of values below it. virtual std::vector DeserializeAppsAndFeaturesEntries(const web::json::array& entries) const; // Deserializes the locale; requires that the PackageLocale be set to return an object. virtual std::optional DeserializeLocale(const web::json::value& localeJsonObject) const; // Deserializes the locale; requires that the PackageLocale be set to return an object. virtual std::optional DeserializeInstallationMetadata(const web::json::value& installationMetadataJsonObject) const; protected: template inline void TryParseStringLocaleField(Manifest::ManifestLocalization& manifestLocale, const web::json::value& localeJsonObject, std::string_view localeJsonFieldName) const { auto value = AppInstaller::JSON::GetRawStringValueFromJsonNode(localeJsonObject, AppInstaller::JSON::GetUtilityString(localeJsonFieldName)); if (AppInstaller::JSON::IsValidNonEmptyStringValue(value)) { manifestLocale.Add(value.value()); } } virtual std::optional DeserializeInstaller(const web::json::value& installerJsonObject) const; virtual std::map DeserializeInstallerSwitches(const web::json::value& installerSwitchesJsonObject) const; std::optional DeserializeDependency(const web::json::value& dependenciesJsonObject) const; virtual Manifest::InstallerTypeEnum ConvertToInstallerType(std::string_view in) const; virtual Manifest::UpdateBehaviorEnum ConvertToUpdateBehavior(std::string_view in) const; std::vector ConvertToManifestStringArray(const std::vector& values) const; virtual Manifest::ManifestVer GetManifestVersion() const; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_0/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include "ManifestDeserializer.h" #include #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_0::Json { namespace { // Manifest response constants specific to this deserializer constexpr std::string_view PackageIdentifier = "PackageIdentifier"sv; constexpr std::string_view PackageFamilyName = "PackageFamilyName"sv; constexpr std::string_view ProductCode = "ProductCode"sv; constexpr std::string_view Versions = "Versions"sv; constexpr std::string_view PackageVersion = "PackageVersion"sv; constexpr std::string_view Channel = "Channel"sv; // Locale constexpr std::string_view DefaultLocale = "DefaultLocale"sv; constexpr std::string_view Locales = "Locales"sv; constexpr std::string_view PackageLocale = "PackageLocale"sv; constexpr std::string_view Publisher = "Publisher"sv; constexpr std::string_view PublisherUrl = "PublisherUrl"sv; constexpr std::string_view PublisherSupportUrl = "PublisherSupportUrl"sv; constexpr std::string_view PrivacyUrl = "PrivacyUrl"sv; constexpr std::string_view Author = "Author"sv; constexpr std::string_view PackageName = "PackageName"sv; constexpr std::string_view PackageUrl = "PackageUrl"sv; constexpr std::string_view License = "License"sv; constexpr std::string_view LicenseUrl = "LicenseUrl"sv; constexpr std::string_view Copyright = "Copyright"sv; constexpr std::string_view CopyrightUrl = "CopyrightUrl"sv; constexpr std::string_view ShortDescription = "ShortDescription"sv; constexpr std::string_view Description = "Description"sv; constexpr std::string_view Tags = "Tags"sv; constexpr std::string_view Moniker = "Moniker"sv; // Installer constexpr std::string_view Installers = "Installers"sv; constexpr std::string_view InstallerIdentifier = "InstallerIdentifier"sv; constexpr std::string_view InstallerSha256 = "InstallerSha256"sv; constexpr std::string_view InstallerUrl = "InstallerUrl"sv; constexpr std::string_view Architecture = "Architecture"sv; constexpr std::string_view InstallerLocale = "InstallerLocale"sv; constexpr std::string_view Platform = "Platform"sv; constexpr std::string_view MinimumOSVersion = "MinimumOSVersion"sv; constexpr std::string_view InstallerType = "InstallerType"sv; constexpr std::string_view Scope = "Scope"sv; constexpr std::string_view SignatureSha256 = "SignatureSha256"sv; constexpr std::string_view InstallModes = "InstallModes"sv; // Installer switches constexpr std::string_view InstallerSwitches = "InstallerSwitches"sv; constexpr std::string_view Silent = "Silent"sv; constexpr std::string_view SilentWithProgress = "SilentWithProgress"sv; constexpr std::string_view Interactive = "Interactive"sv; constexpr std::string_view InstallLocation = "InstallLocation"sv; constexpr std::string_view Log = "Log"sv; constexpr std::string_view Upgrade = "Upgrade"sv; constexpr std::string_view Custom = "Custom"sv; constexpr std::string_view InstallerSuccessCodes = "InstallerSuccessCodes"sv; constexpr std::string_view UpgradeBehavior = "UpgradeBehavior"sv; constexpr std::string_view Commands = "Commands"sv; constexpr std::string_view Protocols = "Protocols"sv; constexpr std::string_view FileExtensions = "FileExtensions"sv; // Dependencies constexpr std::string_view Dependencies = "Dependencies"sv; constexpr std::string_view WindowsFeatures = "WindowsFeatures"sv; constexpr std::string_view WindowsLibraries = "WindowsLibraries"sv; constexpr std::string_view PackageDependencies = "PackageDependencies"sv; constexpr std::string_view MinimumVersion = "MinimumVersion"sv; constexpr std::string_view ExternalDependencies = "ExternalDependencies"sv; constexpr std::string_view Capabilities = "Capabilities"sv; constexpr std::string_view RestrictedCapabilities = "RestrictedCapabilities"sv; void TryParseInstallerSwitchField( std::map& installerSwitches, InstallerSwitchType switchType, const web::json::value& switchesJsonObject, std::string_view switchJsonFieldName) { auto value = JSON::GetRawStringValueFromJsonNode(switchesJsonObject, JSON::GetUtilityString(switchJsonFieldName)); if (JSON::IsValidNonEmptyStringValue(value)) { installerSwitches[switchType] = value.value(); } } } std::vector ManifestDeserializer::Deserialize(const web::json::value& responseJsonObject) const { if (responseJsonObject.is_null()) { AICLI_LOG(Repo, Error, << "Missing json object."); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } try { std::optional> manifestObject = JSON::GetJsonValueFromNode(responseJsonObject, JSON::GetUtilityString(Data)); if (!manifestObject || manifestObject.value().get().is_null()) { AICLI_LOG(Repo, Verbose, << "No manifest results returned."); return {}; } return DeserializeData(manifestObject.value()); } catch (const wil::ResultException&) { throw; } catch (const std::exception& e) { AICLI_LOG(Repo, Error, << "Error encountered while deserializing manifest. Reason: " << e.what()); } catch (...) { AICLI_LOG(Repo, Error, << "Error encountered while deserializing manifest..."); } // If we make it here, there was an exception above that we didn't throw. // This will convert it into our standard error. THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } std::vector ManifestDeserializer::DeserializeData(const web::json::value& dataJsonObject) const { THROW_HR_IF(E_INVALIDARG, dataJsonObject.is_null()); std::vector manifests; std::optional id = JSON::GetRawStringValueFromJsonNode(dataJsonObject, JSON::GetUtilityString(PackageIdentifier)); if (!JSON::IsValidNonEmptyStringValue(id)) { AICLI_LOG(Repo, Error, << "Missing package identifier."); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } std::optional> versions = JSON::GetRawJsonArrayFromJsonNode(dataJsonObject, JSON::GetUtilityString(Versions)); if (!versions || versions.value().get().size() == 0) { AICLI_LOG(Repo, Error, << "Missing versions in package: " << id.value()); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } for (auto& versionItem : versions.value().get()) { Manifest::Manifest manifest; manifest.ManifestVersion = GetManifestVersion(); manifest.Id = id.value(); std::optional packageVersion = JSON::GetRawStringValueFromJsonNode(versionItem, JSON::GetUtilityString(PackageVersion)); if (!JSON::IsValidNonEmptyStringValue(packageVersion)) { AICLI_LOG(Repo, Error, << "Missing package version in package: " << manifest.Id); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } manifest.Version = std::move(packageVersion.value()); manifest.Channel = JSON::GetRawStringValueFromJsonNode(versionItem, JSON::GetUtilityString(Channel)).value_or(""); // Default locale std::optional> defaultLocale = JSON::GetJsonValueFromNode(versionItem, JSON::GetUtilityString(DefaultLocale)); if (!defaultLocale) { AICLI_LOG(Repo, Error, << "Missing default locale in package: " << manifest.Id); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } else { std::optional defaultLocaleObject = DeserializeLocale(defaultLocale.value().get()); if (!defaultLocaleObject) { AICLI_LOG(Repo, Error, << "Missing default locale in package: " << manifest.Id); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } if (!defaultLocaleObject.value().Contains(Manifest::Localization::PackageName) || !defaultLocaleObject.value().Contains(Manifest::Localization::Publisher) || !defaultLocaleObject.value().Contains(Manifest::Localization::ShortDescription)) { AICLI_LOG(Repo, Error, << "Missing PackageName, Publisher or ShortDescription in default locale: " << manifest.Id); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } manifest.DefaultLocalization = std::move(defaultLocaleObject.value()); // Moniker is in Default locale manifest.Moniker = JSON::GetRawStringValueFromJsonNode(defaultLocale.value().get(), JSON::GetUtilityString(Moniker)).value_or(""); } // Installers std::optional> installers = JSON::GetRawJsonArrayFromJsonNode(versionItem, JSON::GetUtilityString(Installers)); if (!installers || installers.value().get().size() == 0) { AICLI_LOG(Repo, Error, << "Missing installers in package: " << manifest.Id); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } for (auto& installer : installers.value().get()) { std::optional installerObject = DeserializeInstaller(installer); if (installerObject) { // Merge default switches after parsing. auto defaultSwitches = Manifest::GetDefaultKnownSwitches(installerObject->EffectiveInstallerType()); for (auto const& defaultSwitch : defaultSwitches) { if (installerObject->Switches.find(defaultSwitch.first) == installerObject->Switches.end()) { installerObject->Switches[defaultSwitch.first] = defaultSwitch.second; } } manifest.Installers.emplace_back(std::move(installerObject.value())); } } if (manifest.Installers.size() == 0) { AICLI_LOG(Repo, Error, << "Missing valid installers in package: " << manifest.Id); THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA); } // Other locales std::optional> locales = JSON::GetRawJsonArrayFromJsonNode(versionItem, JSON::GetUtilityString(Locales)); if (locales) { for (auto& locale : locales.value().get()) { std::optional localeObject = DeserializeLocale(locale); if (localeObject) { manifest.Localizations.emplace_back(std::move(localeObject.value())); } } } manifests.emplace_back(std::move(manifest)); } return manifests; } std::vector ManifestDeserializer::DeserializeAppsAndFeaturesEntries(const web::json::array&) const { return {}; } std::optional ManifestDeserializer::DeserializeInstallationMetadata(const web::json::value&) const { return {}; } std::optional ManifestDeserializer::DeserializeLocale(const web::json::value& localeJsonObject) const { if (localeJsonObject.is_null()) { return {}; } Manifest::ManifestLocalization locale; std::optional packageLocale = JSON::GetRawStringValueFromJsonNode(localeJsonObject, JSON::GetUtilityString(PackageLocale)); if (!JSON::IsValidNonEmptyStringValue(packageLocale)) { AICLI_LOG(Repo, Error, << "Missing package locale."); return {}; } locale.Locale = std::move(packageLocale.value()); TryParseStringLocaleField(locale, localeJsonObject, PackageName); TryParseStringLocaleField(locale, localeJsonObject, Publisher); TryParseStringLocaleField(locale, localeJsonObject, ShortDescription); TryParseStringLocaleField(locale, localeJsonObject, PublisherUrl); TryParseStringLocaleField(locale, localeJsonObject, PublisherSupportUrl); TryParseStringLocaleField(locale, localeJsonObject, PrivacyUrl); TryParseStringLocaleField(locale, localeJsonObject, Author); TryParseStringLocaleField(locale, localeJsonObject, PackageUrl); TryParseStringLocaleField(locale, localeJsonObject, License); TryParseStringLocaleField(locale, localeJsonObject, LicenseUrl); TryParseStringLocaleField(locale, localeJsonObject, Copyright); TryParseStringLocaleField(locale, localeJsonObject, CopyrightUrl); TryParseStringLocaleField(locale, localeJsonObject, Description); auto tags = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(localeJsonObject, JSON::GetUtilityString(Tags))); if (!tags.empty()) { locale.Add(tags); } return locale; } std::optional ManifestDeserializer::DeserializeInstaller(const web::json::value& installerJsonObject) const { if (installerJsonObject.is_null()) { return {}; } Manifest::ManifestInstaller installer; installer.Url = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(InstallerUrl)).value_or(""); std::optional sha256 = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(InstallerSha256)); if (JSON::IsValidNonEmptyStringValue(sha256)) { installer.Sha256 = Utility::SHA256::ConvertToBytes(sha256.value()); } std::optional arch = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(Architecture)); if (!JSON::IsValidNonEmptyStringValue(arch)) { AICLI_LOG(Repo, Error, << "Missing installer architecture."); return {}; } installer.Arch = Utility::ConvertToArchitectureEnum(arch.value()); std::optional installerType = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(InstallerType)); if (!JSON::IsValidNonEmptyStringValue(installerType)) { AICLI_LOG(Repo, Error, << "Missing installer type."); return {}; } installer.BaseInstallerType = ConvertToInstallerType(installerType.value()); installer.Locale = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(InstallerLocale)).value_or(""); // platform std::optional> platforms = JSON::GetRawJsonArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(Platform)); if (platforms) { for (auto& platform : platforms.value().get()) { std::optional platformValue = JSON::GetRawStringValueFromJsonValue(platform); if (platformValue) { installer.Platform.emplace_back(Manifest::ConvertToPlatformEnum(platformValue.value())); } } } installer.MinOSVersion = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(MinimumOSVersion)).value_or(""); std::optional scope = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(Scope)); if (scope) { installer.Scope = Manifest::ConvertToScopeEnum(scope.value()); } std::optional signatureSha256 = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(SignatureSha256)); if (signatureSha256) { installer.SignatureSha256 = Utility::SHA256::ConvertToBytes(signatureSha256.value()); } // Install modes std::optional> installModes = JSON::GetRawJsonArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(InstallModes)); if (installModes) { for (auto& mode : installModes.value().get()) { std::optional modeObject = JSON::GetRawStringValueFromJsonValue(mode); if (modeObject) { installer.InstallModes.emplace_back(Manifest::ConvertToInstallModeEnum(modeObject.value())); } } } // Installer Switches std::optional> switches = JSON::GetJsonValueFromNode(installerJsonObject, JSON::GetUtilityString(InstallerSwitches)); if (switches) { installer.Switches = DeserializeInstallerSwitches(switches.value().get()); } // Installer SuccessCodes std::optional> installSuccessCodes = JSON::GetRawJsonArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(InstallerSuccessCodes)); if (installSuccessCodes) { for (auto& code : installSuccessCodes.value().get()) { std::optional codeValue = JSON::GetRawIntValueFromJsonValue(code); if (codeValue) { installer.InstallerSuccessCodes.emplace_back(std::move(codeValue.value())); } } } std::optional updateBehavior = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(UpgradeBehavior)); if (updateBehavior) { installer.UpdateBehavior = ConvertToUpdateBehavior(updateBehavior.value()); } installer.Commands = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(Commands))); installer.Protocols = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(Protocols))); installer.FileExtensions = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(FileExtensions))); // Dependencies std::optional> dependenciesObject = JSON::GetJsonValueFromNode(installerJsonObject, JSON::GetUtilityString(Dependencies)); if (dependenciesObject) { std::optional dependencyList = DeserializeDependency(dependenciesObject.value().get()); if (dependencyList) { installer.Dependencies = std::move(dependencyList.value()); } } installer.PackageFamilyName = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(PackageFamilyName)).value_or(""); installer.ProductCode = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(ProductCode)).value_or(""); installer.Capabilities = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(Capabilities))); installer.RestrictedCapabilities = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(RestrictedCapabilities))); return installer; } std::map ManifestDeserializer::DeserializeInstallerSwitches(const web::json::value& installerSwitchesJsonObject) const { std::map installerSwitches; TryParseInstallerSwitchField(installerSwitches, InstallerSwitchType::Silent, installerSwitchesJsonObject, Silent); TryParseInstallerSwitchField(installerSwitches, InstallerSwitchType::SilentWithProgress, installerSwitchesJsonObject, SilentWithProgress); TryParseInstallerSwitchField(installerSwitches, InstallerSwitchType::Interactive, installerSwitchesJsonObject, Interactive); TryParseInstallerSwitchField(installerSwitches, InstallerSwitchType::InstallLocation, installerSwitchesJsonObject, InstallLocation); TryParseInstallerSwitchField(installerSwitches, InstallerSwitchType::Log, installerSwitchesJsonObject, Log); TryParseInstallerSwitchField(installerSwitches, InstallerSwitchType::Update, installerSwitchesJsonObject, Upgrade); TryParseInstallerSwitchField(installerSwitches, InstallerSwitchType::Custom, installerSwitchesJsonObject, Custom); return installerSwitches; } std::optional ManifestDeserializer::DeserializeDependency(const web::json::value& dependenciesObject) const { if (dependenciesObject.is_null()) { return {}; } Manifest::DependencyList dependencyList; auto wfIds = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(dependenciesObject, JSON::GetUtilityString(WindowsFeatures))); for (auto&& id : wfIds) { dependencyList.Add(Dependency(DependencyType::WindowsFeature, std::move(id))); }; const auto& wlIds = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(dependenciesObject, JSON::GetUtilityString(WindowsLibraries))); for (auto id : wlIds) { dependencyList.Add(Dependency(DependencyType::WindowsLibrary, id)); }; const auto& extIds = ConvertToManifestStringArray(JSON::GetRawStringArrayFromJsonNode(dependenciesObject, JSON::GetUtilityString(ExternalDependencies))); for (auto id : extIds) { dependencyList.Add(Dependency(DependencyType::External, id)); }; // Package Dependencies std::optional> packageDependencies = JSON::GetRawJsonArrayFromJsonNode(dependenciesObject, JSON::GetUtilityString(PackageDependencies)); if (packageDependencies) { for (auto& packageDependency : packageDependencies.value().get()) { std::optional id = JSON::GetRawStringValueFromJsonNode(packageDependency, JSON::GetUtilityString(PackageIdentifier)); if (id) { Dependency pkg{ DependencyType::Package, std::move(id.value()) , JSON::GetRawStringValueFromJsonNode(packageDependency, JSON::GetUtilityString(MinimumVersion)).value_or("") }; dependencyList.Add(std::move(pkg)); } } } return dependencyList; } Manifest::InstallerTypeEnum ManifestDeserializer::ConvertToInstallerType(std::string_view in) const { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "inno") { return InstallerTypeEnum::Inno; } else if (inStrLower == "wix") { return InstallerTypeEnum::Wix; } else if (inStrLower == "msi") { return InstallerTypeEnum::Msi; } else if (inStrLower == "nullsoft") { return InstallerTypeEnum::Nullsoft; } else if (inStrLower == "zip") { return InstallerTypeEnum::Zip; } else if (inStrLower == "appx" || inStrLower == "msix") { return InstallerTypeEnum::Msix; } else if (inStrLower == "exe") { return InstallerTypeEnum::Exe; } else if (inStrLower == "burn") { return InstallerTypeEnum::Burn; } return InstallerTypeEnum::Unknown; } Manifest::UpdateBehaviorEnum ManifestDeserializer::ConvertToUpdateBehavior(std::string_view in) const { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "install") { return UpdateBehaviorEnum::Install; } else if (inStrLower == "uninstallprevious") { return UpdateBehaviorEnum::UninstallPrevious; } return UpdateBehaviorEnum::Unknown; } std::vector ManifestDeserializer::ConvertToManifestStringArray(const std::vector& values) const { std::vector result; for (const auto& value : values) { result.emplace_back(value); } return result; } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/SearchRequestSerializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Rest/Schema/IRestClient.h" namespace AppInstaller::Repository::Rest::Schema::V1_0::Json { // Search Result Serializer. struct SearchRequestSerializer { web::json::value Serialize(const SearchRequest& searchRequest) const; protected: std::optional SerializeSearchRequest(const SearchRequest& searchRequest) const; std::optional GetRequestMatchJsonObject(const AppInstaller::Repository::RequestMatch& requestMatch) const; std::optional GetPackageMatchFilterJsonObject(const PackageMatchFilter& packageMatchFilter) const; virtual std::optional ConvertPackageMatchFieldToString(AppInstaller::Repository::PackageMatchField field) const; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/SearchRequestSerializer_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/IRestClient.h" #include "SearchRequestSerializer.h" #include #include "Rest/Schema/CommonRestConstants.h" namespace AppInstaller::Repository::Rest::Schema::V1_0::Json { namespace { // Search request constants constexpr std::string_view Query = "Query"sv; constexpr std::string_view Filters = "Filters"sv; constexpr std::string_view Inclusions = "Inclusions"sv; constexpr std::string_view MaximumResults = "MaximumResults"sv; constexpr std::string_view RequestMatch = "RequestMatch"sv; constexpr std::string_view KeyWord = "KeyWord"sv; constexpr std::string_view MatchType = "MatchType"sv; constexpr std::string_view PackageMatchField = "PackageMatchField"sv; constexpr std::string_view FetchAllManifests = "FetchAllManifests"sv; std::optional ConvertMatchTypeToString(AppInstaller::Repository::MatchType type) { // Match types supported by Rest API schema. switch (type) { case MatchType::Exact: return "Exact"sv; case MatchType::CaseInsensitive: return "CaseInsensitive"sv; case MatchType::StartsWith: return "StartsWith"sv; case MatchType::Substring: return "Substring"sv; case MatchType::Wildcard: return "Wildcard"sv; case MatchType::Fuzzy: return "Fuzzy"sv; case MatchType::FuzzySubstring: return "FuzzySubstring"sv; } return {}; } } web::json::value SearchRequestSerializer::Serialize(const SearchRequest& searchRequest) const { std::optional result = SerializeSearchRequest(searchRequest); THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTAPI_INTERNAL_ERROR, !result); return result.value(); } std::optional SearchRequestSerializer::SerializeSearchRequest(const SearchRequest& searchRequest) const { try { web::json::value json_body; if (searchRequest.MaximumResults > 0) { json_body[JSON::GetUtilityString(MaximumResults)] = searchRequest.MaximumResults; } if (searchRequest.IsForEverything()) { json_body[JSON::GetUtilityString(FetchAllManifests)] = web::json::value::boolean(true); return json_body; } if (searchRequest.Query) { auto& requestMatch = searchRequest.Query.value(); web::json::value requestMatchObject = web::json::value::object(); std::optional requestMatchJson = GetRequestMatchJsonObject(requestMatch); if (requestMatchJson) { json_body[JSON::GetUtilityString(Query)] = std::move(requestMatchJson.value()); } } if (!searchRequest.Filters.empty()) { web::json::value filters = web::json::value::array(); int i = 0; for (auto& filter : searchRequest.Filters) { std::optional jsonObject = GetPackageMatchFilterJsonObject(filter); if (jsonObject) { filters[i++] = std::move(jsonObject.value()); } } json_body[JSON::GetUtilityString(Filters)] = filters; } if (!searchRequest.Inclusions.empty()) { web::json::value inclusions = web::json::value::array(); int i = 0; for (auto& inclusion : searchRequest.Inclusions) { std::optional jsonObject = GetPackageMatchFilterJsonObject(inclusion); if (jsonObject) { inclusions[i++] = std::move(jsonObject.value()); } } json_body[JSON::GetUtilityString(Inclusions)] = inclusions; } return json_body; } catch (const std::exception& e) { AICLI_LOG(Repo, Error, << "Error occurred while serializing search request. Reason: " << e.what()); } catch (...) { AICLI_LOG(Repo, Error, << "Error occurred while serializing search request"); } return {}; } std::optional SearchRequestSerializer::GetPackageMatchFilterJsonObject(const PackageMatchFilter& packageMatchFilter) const { web::json::value filter = web::json::value::object(); std::optional matchField = ConvertPackageMatchFieldToString(packageMatchFilter.Field); if (!matchField) { AICLI_LOG(Repo, Warning, << "Skipping unsupported package match field: " << packageMatchFilter.Field); return {}; } filter[JSON::GetUtilityString(PackageMatchField)] = web::json::value::string(JSON::GetUtilityString(matchField.value())); std::optional requestMatchJson = GetRequestMatchJsonObject(packageMatchFilter); if (!requestMatchJson) { AICLI_LOG(Repo, Warning, << "Skipping unsupported request match object."); return {}; } filter[JSON::GetUtilityString(RequestMatch)] = std::move(requestMatchJson.value()); return filter; } std::optional SearchRequestSerializer::GetRequestMatchJsonObject(const AppInstaller::Repository::RequestMatch& requestMatch) const { web::json::value match = web::json::value::object(); match[JSON::GetUtilityString(KeyWord)] = web::json::value::string(JSON::GetUtilityString(requestMatch.Value)); std::optional matchType = ConvertMatchTypeToString(requestMatch.Type); if (!matchType) { AICLI_LOG(Repo, Warning, << "Skipping unsupported match type: " << requestMatch.Type); return {}; } match[JSON::GetUtilityString(MatchType)] = web::json::value::string(JSON::GetUtilityString(matchType.value())); return match; } std::optional SearchRequestSerializer::ConvertPackageMatchFieldToString(AppInstaller::Repository::PackageMatchField field) const { // Match fields supported by Rest API schema. switch (field) { case PackageMatchField::Command: return "Command"sv; case PackageMatchField::Id: return "PackageIdentifier"sv; case PackageMatchField::Moniker: return "Moniker"sv; case PackageMatchField::Name: return "PackageName"sv; case PackageMatchField::Tag: return "Tag"sv; case PackageMatchField::PackageFamilyName: return "PackageFamilyName"sv; case PackageMatchField::ProductCode: return "ProductCode"sv; case PackageMatchField::NormalizedNameAndPublisher: return "NormalizedPackageNameAndPublisher"sv; } return {}; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/SearchResponseDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Rest/Schema/IRestClient.h" namespace AppInstaller::Repository::Rest::Schema::V1_0::Json { // Search Result Deserializer. struct SearchResponseDeserializer { // Gets the search result for given version IRestClient::SearchResult Deserialize(const web::json::value& searchResultJsonObject) const; protected: virtual std::optional DeserializeSearchResult(const web::json::value& searchResultJsonObject) const; virtual std::optional DeserializeVersionInfo(const web::json::value& versionInfoJsonObject) const; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/SearchResponseDeserializer_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include "SearchResponseDeserializer.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_0::Json { namespace { // Search response constants constexpr std::string_view PackageIdentifier = "PackageIdentifier"sv; constexpr std::string_view PackageName = "PackageName"sv; constexpr std::string_view Publisher = "Publisher"sv; constexpr std::string_view PackageFamilyNames = "PackageFamilyNames"sv; constexpr std::string_view ProductCodes = "ProductCodes"sv; constexpr std::string_view Versions = "Versions"sv; constexpr std::string_view PackageVersion = "PackageVersion"sv; constexpr std::string_view Channel = "Channel"sv; } IRestClient::SearchResult SearchResponseDeserializer::Deserialize(const web::json::value& searchResponseObject) const { std::optional response = DeserializeSearchResult(searchResponseObject); THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA, !response); return response.value(); } std::optional SearchResponseDeserializer::DeserializeSearchResult(const web::json::value& searchResponseObject) const { // Make search result from json output. if (searchResponseObject.is_null()) { AICLI_LOG(Repo, Error, << "Missing json object."); return {}; } IRestClient::SearchResult result; try { std::optional> dataArray = JSON::GetRawJsonArrayFromJsonNode(searchResponseObject, JSON::GetUtilityString(Data)); if (!dataArray || dataArray.value().get().size() == 0) { AICLI_LOG(Repo, Verbose, << "No search results returned."); return result; } for (auto& manifestItem : dataArray.value().get()) { std::optional packageId = JSON::GetRawStringValueFromJsonNode(manifestItem, JSON::GetUtilityString(PackageIdentifier)); std::optional packageName = JSON::GetRawStringValueFromJsonNode(manifestItem, JSON::GetUtilityString(PackageName)); std::optional publisher = JSON::GetRawStringValueFromJsonNode(manifestItem, JSON::GetUtilityString(Publisher)); if (!JSON::IsValidNonEmptyStringValue(packageId) || !JSON::IsValidNonEmptyStringValue(packageName) || !JSON::IsValidNonEmptyStringValue(publisher)) { AICLI_LOG(Repo, Error, << "Missing required package fields in manifest search results."); return {}; } std::optional> versionValue = JSON::GetRawJsonArrayFromJsonNode(manifestItem, JSON::GetUtilityString(Versions)); std::vector versionList; if (versionValue) { for (auto& versionItem : versionValue.value().get()) { auto versionInfo = DeserializeVersionInfo(versionItem); if (!versionInfo.has_value()) { AICLI_LOG(Repo, Error, << "Received incomplete package version in package: " << packageId.value()); return {}; } versionList.emplace_back(std::move(*versionInfo)); } } if (versionList.size() == 0) { AICLI_LOG(Repo, Error, << "Received no versions in package: " << packageId.value()); return {}; } IRestClient::PackageInfo packageInfo{ std::move(packageId.value()), std::move(packageName.value()), std::move(publisher.value()) }; IRestClient::Package package{ std::move(packageInfo), std::move(versionList) }; result.Matches.emplace_back(std::move(package)); } return result; } catch (const std::exception& e) { AICLI_LOG(Repo, Error, << "Error encountered while deserializing search result. Reason: " << e.what()); } catch (...) { AICLI_LOG(Repo, Error, << "Error encountered while deserializing search result..."); } return {}; } std::optional SearchResponseDeserializer::DeserializeVersionInfo(const web::json::value& versionInfoJsonObject) const { std::optional version = JSON::GetRawStringValueFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(PackageVersion)); if (!JSON::IsValidNonEmptyStringValue(version)) { AICLI_LOG(Repo, Error, << "Received incomplete package version"); return {}; } std::string channel = JSON::GetRawStringValueFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(Channel)).value_or(""); std::vector packageFamilyNames = AppInstaller::Rest::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(PackageFamilyNames))); std::vector productCodes = AppInstaller::Rest::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(ProductCodes))); return IRestClient::VersionInfo{ AppInstaller::Utility::VersionAndChannel{std::move(version.value()), std::move(channel)}, {}, std::move(packageFamilyNames), std::move(productCodes) }; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_0/RestInterface_1_0.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_0/Interface.h" #include "Rest/Schema/IRestClient.h" #include #include #include #include #include #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/SearchResponseParser.h" #include "Rest/Schema/SearchRequestComposer.h" using namespace std::string_view_literals; namespace AppInstaller::Repository::Rest::Schema::V1_0 { namespace { // Query params constexpr std::string_view VersionQueryParam = "Version"sv; constexpr std::string_view ChannelQueryParam = "Channel"sv; utility::string_t GetSearchEndpoint(const std::string& restApiUri) { return AppInstaller::Rest::AppendPathToUri(AppInstaller::JSON::GetUtilityString(restApiUri), AppInstaller::JSON::GetUtilityString(ManifestSearchPostEndpoint)); } utility::string_t GetManifestByVersionEndpoint( const std::string& restApiUri, const std::string& packageId, const std::map& queryParameters) { utility::string_t getManifestEndpoint = AppInstaller::Rest::AppendPathToUri( AppInstaller::JSON::GetUtilityString(restApiUri), AppInstaller::JSON::GetUtilityString(ManifestByVersionAndChannelGetEndpoint)); utility::string_t getManifestWithPackageIdPath = AppInstaller::Rest::AppendPathToUri(getManifestEndpoint, AppInstaller::JSON::GetUtilityString(packageId)); // Create the endpoint with query parameters return AppInstaller::Rest::AppendQueryParamsToUri(getManifestWithPackageIdPath, queryParameters); } std::optional GetContinuationToken(const web::json::value& jsonObject) { std::optional continuationToken = AppInstaller::JSON::GetRawStringValueFromJsonNode(jsonObject, AppInstaller::JSON::GetUtilityString(ContinuationToken)); if (continuationToken) { return utility::conversions::to_string_t(continuationToken.value()); } return {}; } AppInstaller::Http::HttpClientHelper::HttpResponseHandlerResult CustomRestCallResponseHandler(const web::http::http_response& response) { AppInstaller::Http::HttpClientHelper::HttpResponseHandlerResult result; result.UseDefaultHandling = true; if (response.status_code() == web::http::status_codes::NotFound && response.headers().content_type()._Starts_with(web::http::details::mime_types::application_json)) { auto responseJson = response.extract_json().get(); if (responseJson.is_object() && responseJson.has_field(L"code") && responseJson.has_field(L"message")) { // We'll treat 404 with json response containing code and message fields as empty result. // Leave the HttpResponseHandlerResult result empty and disable default HttpClientHelper handling. result.UseDefaultHandling = false; } } return result; } } Interface::Interface(const std::string& restApi, const Http::HttpClientHelper& httpClientHelper) : m_restApiUri(restApi), m_httpClientHelper(httpClientHelper) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !AppInstaller::Rest::IsValidUri(AppInstaller::JSON::GetUtilityString(restApi))); m_searchEndpoint = GetSearchEndpoint(m_restApiUri); m_requiredRestApiHeaders.emplace(AppInstaller::JSON::GetUtilityString(ContractVersion), AppInstaller::JSON::GetUtilityString(Version_1_0_0.ToString())); } Utility::Version Interface::GetVersion() const { return Version_1_0_0; } IRestClient::Information Interface::GetSourceInformation() const { return {}; } IRestClient::SearchResult Interface::Search(const SearchRequest& request) const { // Optimization if (MeetsOptimizedSearchCriteria(request)) { return OptimizedSearch(request); } return SearchInternal(request); } IRestClient::SearchResult Interface::SearchInternal(const SearchRequest& request) const { SearchResult results; utility::string_t continuationToken; Http::HttpClientHelper::HttpRequestHeaders searchHeaders = m_requiredRestApiHeaders; do { if (!continuationToken.empty()) { AICLI_LOG(Repo, Verbose, << "Received continuation token. Retrieving more results."); searchHeaders.insert_or_assign(AppInstaller::JSON::GetUtilityString(ContinuationToken), continuationToken); } std::optional jsonObject = m_httpClientHelper.HandlePost(m_searchEndpoint, GetValidatedSearchBody(request), searchHeaders, GetAuthHeaders(), CustomRestCallResponseHandler); utility::string_t ct; if (jsonObject) { SearchResult currentResult = GetSearchResult(jsonObject.value()); size_t insertElements = !request.MaximumResults ? currentResult.Matches.size() : std::min(currentResult.Matches.size(), request.MaximumResults - results.Matches.size()); if (insertElements < currentResult.Matches.size()) { results.Truncated = true; } std::move(currentResult.Matches.begin(), std::next(currentResult.Matches.begin(), insertElements), std::inserter(results.Matches, results.Matches.end())); ct = GetContinuationToken(jsonObject.value()).value_or(L""); } continuationToken = ct; } while (!continuationToken.empty() && (!request.MaximumResults || results.Matches.size() < request.MaximumResults)); if (!continuationToken.empty()) { results.Truncated = true; } if (results.Matches.empty()) { AICLI_LOG(Repo, Verbose, << "No search results returned by rest source"); } return results; } std::optional Interface::GetManifestByVersion(const std::string& packageId, const std::string& version, const std::string& channel) const { std::map queryParams; if (!version.empty()) { queryParams.emplace(VersionQueryParam, version); } if (!channel.empty()) { queryParams.emplace(ChannelQueryParam, channel); } std::vector manifests = GetManifests(packageId, queryParams); if (!manifests.empty()) { for (Manifest::Manifest manifest : manifests) { if (Utility::CaseInsensitiveEquals(manifest.Version, version) && Utility::CaseInsensitiveEquals(manifest.Channel, channel)) { return manifest; } } } return {}; } bool Interface::MeetsOptimizedSearchCriteria(const SearchRequest& request) const { // Optimization: If the user wants to install a certain package with an exact match on package id and a particular rest source, we will // call the package manifest endpoint to get the manifest directly instead of running a search for it. if (!request.Query && request.Inclusions.size() == 0 && request.Filters.size() == 1 && request.Filters[0].Field == PackageMatchField::Id && (request.Filters[0].Type == MatchType::Exact || request.Filters[0].Type == MatchType::CaseInsensitive)) { AICLI_LOG(Repo, Verbose, << "Search request meets optimized search criteria."); return true; } return false; } IRestClient::SearchResult Interface::OptimizedSearch(const SearchRequest& request) const { SearchResult searchResult; std::vector manifests = GetManifests(request.Filters[0].Value); if (!manifests.empty()) { auto& manifest = manifests.at(0); PackageInfo packageInfo = PackageInfo{ manifest.Id, manifest.DefaultLocalization.Get(), manifest.DefaultLocalization.Get() }; // Add all the versions to the package info object std::vector versions; for (auto& manifestVersion : manifests) { auto packageFamilyNames = manifestVersion.GetPackageFamilyNames(); auto productCodes = manifestVersion.GetProductCodes(); auto arpVersionRange = manifestVersion.GetArpVersionRange(); auto upgradeCodes = manifestVersion.GetUpgradeCodes(); versions.emplace_back( VersionInfo{ AppInstaller::Utility::VersionAndChannel {manifestVersion.Version, manifestVersion.Channel}, manifestVersion, std::vector{ packageFamilyNames.begin(), packageFamilyNames.end()}, std::vector{ productCodes.begin(), productCodes.end()}, arpVersionRange.IsEmpty() ? std::vector{} : std::vector{ arpVersionRange.GetMinVersion(), arpVersionRange.GetMaxVersion() }, std::vector{ upgradeCodes.begin(), upgradeCodes.end()} }); } Package package = Package{ std::move(packageInfo), std::move(versions) }; searchResult.Matches.emplace_back(std::move(package)); } return searchResult; } std::vector Interface::GetManifests(const std::string& packageId, const std::map& params) const { auto validatedParams = GetValidatedQueryParams(params); std::vector results; utility::string_t continuationToken; Http::HttpClientHelper::HttpRequestHeaders searchHeaders = m_requiredRestApiHeaders; std::optional jsonObject = m_httpClientHelper.HandleGet(GetManifestByVersionEndpoint(m_restApiUri, packageId, validatedParams), searchHeaders, GetAuthHeaders(), CustomRestCallResponseHandler); if (!jsonObject) { AICLI_LOG(Repo, Verbose, << "No results were returned by the rest source for package id: " << packageId); return results; } // Parse json and return Manifests std::vector manifests = GetParsedManifests(jsonObject.value()); // Manifest validation for (auto& manifestItem : manifests) { std::vector validationErrors = AppInstaller::Manifest::ValidateManifest(manifestItem, false); int errors = 0; for (auto& error : validationErrors) { if (error.ErrorLevel == Manifest::ValidationError::Level::Error) { AICLI_LOG(Repo, Error, << "Received manifest contains validation error: " << error.GetErrorMessage()); errors++; } } THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA, errors > 0); results.emplace_back(manifestItem); } return results; } std::map Interface::GetValidatedQueryParams(const std::map& params) const { return params; } web::json::value Interface::GetValidatedSearchBody(const SearchRequest& searchRequest) const { SearchRequestComposer searchRequestComposer{ GetVersion() }; return searchRequestComposer.Serialize(searchRequest); } IRestClient::SearchResult Interface::GetSearchResult(const web::json::value& searchResponseObject) const { SearchResponseParser searchResponseParser{ GetVersion() }; return searchResponseParser.Deserialize(searchResponseObject); } std::vector Interface::GetParsedManifests(const web::json::value& manifestsResponseObject) const { JSON::ManifestJSONParser manifestParser{ GetVersion() }; return manifestParser.Deserialize(manifestsResponseObject); } Http::HttpClientHelper::HttpRequestHeaders Interface::GetAuthHeaders() const { return {}; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_1/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_0/Interface.h" namespace AppInstaller::Repository::Rest::Schema::V1_1 { // Interface to this schema version exposed through IRestClient. struct Interface : public V1_0::Interface { Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; IRestClient::Information GetSourceInformation() const override; protected: // Check query params against source information and update if necessary. std::map GetValidatedQueryParams(const std::map& params) const override; // Check search request against source information and get json search body. web::json::value GetValidatedSearchBody(const SearchRequest& searchRequest) const override; SearchResult GetSearchResult(const web::json::value& searchResponseObject) const override; std::vector GetParsedManifests(const web::json::value& manifestsResponseObject) const override; PackageMatchField ConvertStringToPackageMatchField(std::string_view field) const; IRestClient::Information m_information; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_0/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_1::Json { // Manifest Deserializer. struct ManifestDeserializer : public V1_0::Json::ManifestDeserializer { std::vector DeserializeAppsAndFeaturesEntries(const web::json::array& entries) const override; std::optional DeserializeLocale(const web::json::value& localeJsonObject) const override; protected: std::optional DeserializeInstaller(const web::json::value& installerJsonObject) const override; Manifest::InstallerTypeEnum ConvertToInstallerType(std::string_view in) const override; virtual Manifest::ExpectedReturnCodeEnum ConvertToExpectedReturnCodeEnum(std::string_view in) const; virtual Manifest::ManifestInstaller::ExpectedReturnCodeInfo DeserializeExpectedReturnCodeInfo(const web::json::value& expectedReturnCodeJsonObject) const; Manifest::ManifestVer GetManifestVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestDeserializer.h" #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_1::Json { namespace { // Installer constexpr std::string_view MSStoreProductIdentifier = "MSStoreProductIdentifier"sv; constexpr std::string_view ReleaseDate = "ReleaseDate"sv; constexpr std::string_view InstallerAbortsTerminal = "InstallerAbortsTerminal"sv; constexpr std::string_view InstallLocationRequired = "InstallLocationRequired"sv; constexpr std::string_view RequireExplicitUpgrade = "RequireExplicitUpgrade"sv; constexpr std::string_view UnsupportedOSArchitectures = "UnsupportedOSArchitectures"sv; constexpr std::string_view AppsAndFeaturesEntries = "AppsAndFeaturesEntries"sv; constexpr std::string_view DisplayName = "DisplayName"sv; constexpr std::string_view Publisher = "Publisher"sv; constexpr std::string_view DisplayVersion = "DisplayVersion"sv; constexpr std::string_view ProductCode = "ProductCode"sv; constexpr std::string_view UpgradeCode = "UpgradeCode"sv; constexpr std::string_view InstallerType = "InstallerType"sv; constexpr std::string_view Markets = "Markets"sv; constexpr std::string_view AllowedMarkets = "AllowedMarkets"sv; constexpr std::string_view ExcludedMarkets = "ExcludedMarkets"sv; constexpr std::string_view ElevationRequirement = "ElevationRequirement"sv; constexpr std::string_view ExpectedReturnCodes = "ExpectedReturnCodes"sv; constexpr std::string_view InstallerReturnCode = "InstallerReturnCode"sv; constexpr std::string_view ReturnResponse = "ReturnResponse"sv; // Locale constexpr std::string_view ReleaseNotes = "ReleaseNotes"sv; constexpr std::string_view ReleaseNotesUrl = "ReleaseNotesUrl"sv; constexpr std::string_view Agreements = "Agreements"sv; constexpr std::string_view AgreementLabel = "AgreementLabel"sv; constexpr std::string_view Agreement = "Agreement"sv; constexpr std::string_view AgreementUrl = "AgreementUrl"sv; } std::vector ManifestDeserializer::DeserializeAppsAndFeaturesEntries(const web::json::array& entries) const { std::vector result; for (auto& arpEntryNode : entries) { AppsAndFeaturesEntry arpEntry; arpEntry.DisplayName = JSON::GetRawStringValueFromJsonNode(arpEntryNode, JSON::GetUtilityString(DisplayName)).value_or(""); arpEntry.Publisher = JSON::GetRawStringValueFromJsonNode(arpEntryNode, JSON::GetUtilityString(Publisher)).value_or(""); arpEntry.DisplayVersion = JSON::GetRawStringValueFromJsonNode(arpEntryNode, JSON::GetUtilityString(DisplayVersion)).value_or(""); arpEntry.ProductCode = JSON::GetRawStringValueFromJsonNode(arpEntryNode, JSON::GetUtilityString(ProductCode)).value_or(""); arpEntry.UpgradeCode = JSON::GetRawStringValueFromJsonNode(arpEntryNode, JSON::GetUtilityString(UpgradeCode)).value_or(""); arpEntry.InstallerType = ConvertToInstallerType(JSON::GetRawStringValueFromJsonNode(arpEntryNode, JSON::GetUtilityString(InstallerType)).value_or("")); // Only add when at least one field is valid if (!arpEntry.DisplayName.empty() || !arpEntry.Publisher.empty() || !arpEntry.DisplayVersion.empty() || !arpEntry.ProductCode.empty() || !arpEntry.UpgradeCode.empty() || arpEntry.InstallerType != InstallerTypeEnum::Unknown) { result.emplace_back(std::move(arpEntry)); } } return result; } Manifest::InstallerTypeEnum ManifestDeserializer::ConvertToInstallerType(std::string_view in) const { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "msstore") { return InstallerTypeEnum::MSStore; } return V1_0::Json::ManifestDeserializer::ConvertToInstallerType(inStrLower); } Manifest::ExpectedReturnCodeEnum ManifestDeserializer::ConvertToExpectedReturnCodeEnum(std::string_view in) const { std::string inStrLower = Utility::ToLower(in); ExpectedReturnCodeEnum result = ExpectedReturnCodeEnum::Unknown; if (inStrLower == "packageinuse") { result = ExpectedReturnCodeEnum::PackageInUse; } else if (inStrLower == "installinprogress") { result = ExpectedReturnCodeEnum::InstallInProgress; } else if (inStrLower == "fileinuse") { result = ExpectedReturnCodeEnum::FileInUse; } else if (inStrLower == "missingdependency") { result = ExpectedReturnCodeEnum::MissingDependency; } else if (inStrLower == "diskfull") { result = ExpectedReturnCodeEnum::DiskFull; } else if (inStrLower == "insufficientmemory") { result = ExpectedReturnCodeEnum::InsufficientMemory; } else if (inStrLower == "nonetwork") { result = ExpectedReturnCodeEnum::NoNetwork; } else if (inStrLower == "contactsupport") { result = ExpectedReturnCodeEnum::ContactSupport; } else if (inStrLower == "rebootrequiredtofinish") { result = ExpectedReturnCodeEnum::RebootRequiredToFinish; } else if (inStrLower == "rebootrequiredforinstall") { result = ExpectedReturnCodeEnum::RebootRequiredForInstall; } else if (inStrLower == "rebootinitiated") { result = ExpectedReturnCodeEnum::RebootInitiated; } else if (inStrLower == "cancelledbyuser") { result = ExpectedReturnCodeEnum::CancelledByUser; } else if (inStrLower == "alreadyinstalled") { result = ExpectedReturnCodeEnum::AlreadyInstalled; } else if (inStrLower == "downgrade") { result = ExpectedReturnCodeEnum::Downgrade; } else if (inStrLower == "blockedbypolicy") { result = ExpectedReturnCodeEnum::BlockedByPolicy; } return result; } Manifest::ManifestInstaller::ExpectedReturnCodeInfo ManifestDeserializer::DeserializeExpectedReturnCodeInfo(const web::json::value& expectedReturnCodeJsonObject) const { ExpectedReturnCodeEnum returnResponse = ConvertToExpectedReturnCodeEnum(JSON::GetRawStringValueFromJsonNode(expectedReturnCodeJsonObject, JSON::GetUtilityString(ReturnResponse)).value_or("")); return { returnResponse, "" }; } std::optional ManifestDeserializer::DeserializeInstaller(const web::json::value& installerJsonObject) const { auto result = V1_0::Json::ManifestDeserializer::DeserializeInstaller(installerJsonObject); if (result) { auto& installer = result.value(); installer.ProductId = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(MSStoreProductIdentifier)).value_or(""); installer.ReleaseDate = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(ReleaseDate)).value_or(""); installer.InstallerAbortsTerminal = JSON::GetRawBoolValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(InstallerAbortsTerminal)).value_or(false); installer.InstallLocationRequired = JSON::GetRawBoolValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(InstallLocationRequired)).value_or(false); installer.RequireExplicitUpgrade = JSON::GetRawBoolValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(RequireExplicitUpgrade)).value_or(false); installer.ElevationRequirement = Manifest::ConvertToElevationRequirementEnum( JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(ElevationRequirement)).value_or("")); // list of unsupported OS architectures std::optional> unsupportedOSArchitectures = JSON::GetRawJsonArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(UnsupportedOSArchitectures)); if (unsupportedOSArchitectures) { for (auto& archValue : unsupportedOSArchitectures.value().get()) { std::optional arch = JSON::GetRawStringValueFromJsonValue(archValue); if (JSON::IsValidNonEmptyStringValue(arch)) { auto archEnum = Utility::ConvertToArchitectureEnum(arch.value()); if (archEnum == Utility::Architecture::Neutral) { AICLI_LOG(Repo, Error, << "Unsupported OS architectures cannot contain neutral value."); return {}; } if (archEnum != Utility::Architecture::Unknown) { installer.UnsupportedOSArchitectures.emplace_back(archEnum); } } } } // Apps and Features Entries std::optional> arpEntriesNode = JSON::GetRawJsonArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(AppsAndFeaturesEntries)); if (arpEntriesNode) { installer.AppsAndFeaturesEntries = DeserializeAppsAndFeaturesEntries(arpEntriesNode.value()); } // Markets std::optional> marketsNode = JSON::GetJsonValueFromNode(installerJsonObject, JSON::GetUtilityString(Markets)); if (marketsNode && !marketsNode.value().get().is_null()) { installer.Markets.ExcludedMarkets = V1_0::Json::ManifestDeserializer::ConvertToManifestStringArray( JSON::GetRawStringArrayFromJsonNode(marketsNode.value().get(), JSON::GetUtilityString(ExcludedMarkets))); installer.Markets.AllowedMarkets = V1_0::Json::ManifestDeserializer::ConvertToManifestStringArray( JSON::GetRawStringArrayFromJsonNode(marketsNode.value().get(), JSON::GetUtilityString(AllowedMarkets))); } // Expected return codes std::optional> expectedReturnCodesNode = JSON::GetRawJsonArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(ExpectedReturnCodes)); if (expectedReturnCodesNode) { for (auto& returnCodeNode : expectedReturnCodesNode.value().get()) { DWORD installerReturnCode = static_cast(JSON::GetRawIntValueFromJsonNode(returnCodeNode, JSON::GetUtilityString(InstallerReturnCode)).value_or(0)); auto returnCodeInfo = DeserializeExpectedReturnCodeInfo(returnCodeNode); // Only add when it is valid if (installerReturnCode != 0 && returnCodeInfo.ReturnResponseEnum != ExpectedReturnCodeEnum::Unknown) { if (!installer.ExpectedReturnCodes.insert({ installerReturnCode, std::move(returnCodeInfo) }).second) { AICLI_LOG(Repo, Error, << "Expected return codes cannot have repeated value."); return {}; } } } } // Populate installer default return codes if not present in ExpectedReturnCodes and InstallerSuccessCodes auto defaultReturnCodes = GetDefaultKnownReturnCodes(installer.EffectiveInstallerType()); for (auto const& defaultReturnCode : defaultReturnCodes) { if (installer.ExpectedReturnCodes.find(defaultReturnCode.first) == installer.ExpectedReturnCodes.end() && std::find(installer.InstallerSuccessCodes.begin(), installer.InstallerSuccessCodes.end(), defaultReturnCode.first) == installer.InstallerSuccessCodes.end()) { installer.ExpectedReturnCodes[defaultReturnCode.first].ReturnResponseEnum = defaultReturnCode.second; } } } return result; } std::optional ManifestDeserializer::DeserializeLocale(const web::json::value& localeJsonObject) const { auto result = V1_0::Json::ManifestDeserializer::DeserializeLocale(localeJsonObject); if (result) { auto& locale = result.value(); TryParseStringLocaleField(locale, localeJsonObject, ReleaseNotes); TryParseStringLocaleField(locale, localeJsonObject, ReleaseNotesUrl); // Agreements auto agreementsNode = JSON::GetRawJsonArrayFromJsonNode(localeJsonObject, JSON::GetUtilityString(Agreements)); if (agreementsNode) { std::vector agreements; for (auto const& agreementNode : agreementsNode.value().get()) { Manifest::Agreement agreementEntry; agreementEntry.Label = JSON::GetRawStringValueFromJsonNode(agreementNode, JSON::GetUtilityString(AgreementLabel)).value_or(""); agreementEntry.AgreementText = JSON::GetRawStringValueFromJsonNode(agreementNode, JSON::GetUtilityString(Agreement)).value_or(""); agreementEntry.AgreementUrl = JSON::GetRawStringValueFromJsonNode(agreementNode, JSON::GetUtilityString(AgreementUrl)).value_or(""); if (!agreementEntry.Label.empty() || !agreementEntry.AgreementText.empty() || !agreementEntry.AgreementUrl.empty()) { agreements.emplace_back(std::move(agreementEntry)); } } if (!agreements.empty()) { locale.Add(std::move(agreements)); } } } return result; } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1_1; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/SearchRequestSerializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Rest/Schema/1_0/Json/SearchRequestSerializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_1::Json { // Search Result Serializer. struct SearchRequestSerializer : public V1_0::Json::SearchRequestSerializer { protected: std::optional ConvertPackageMatchFieldToString(AppInstaller::Repository::PackageMatchField field) const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/SearchRequestSerializer_1_1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchRequestSerializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_1::Json { std::optional SearchRequestSerializer::ConvertPackageMatchFieldToString(AppInstaller::Repository::PackageMatchField field) const { if (field == PackageMatchField::Market) { return "Market"sv; } return V1_0::Json::SearchRequestSerializer::ConvertPackageMatchFieldToString(field); } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_1/RestInterface_1_1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_1/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include #include using namespace std::string_view_literals; namespace AppInstaller::Repository::Rest::Schema::V1_1 { namespace { // Query params constexpr std::string_view MarketQueryParam = "Market"sv; // Response constants constexpr std::string_view UnsupportedPackageMatchFields = "UnsupportedPackageMatchFields"sv; constexpr std::string_view RequiredPackageMatchFields = "RequiredPackageMatchFields"sv; constexpr std::string_view UnsupportedQueryParameters = "UnsupportedQueryParameters"sv; constexpr std::string_view RequiredQueryParameters = "RequiredQueryParameters"sv; } Interface::Interface( const std::string& restApi, const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_0::Interface(restApi, httpClientHelper), m_information(std::move(information)) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_1_0.ToString()); if (!additionalHeaders.empty()) { m_requiredRestApiHeaders.insert(additionalHeaders.begin(), additionalHeaders.end()); } } Utility::Version Interface::GetVersion() const { return Version_1_1_0; } IRestClient::Information Interface::GetSourceInformation() const { return m_information; } std::map Interface::GetValidatedQueryParams(const std::map& params) const { std::map result = params; for (auto const& param : m_information.RequiredQueryParameters) { if (params.end() == std::find_if(params.begin(), params.end(), [&](const auto& pair) { return Utility::CaseInsensitiveEquals(pair.first, param); })) { if (Utility::CaseInsensitiveEquals(param, MarketQueryParam)) { result.emplace(MarketQueryParam, Runtime::GetOSRegion()); continue; } AICLI_LOG(Repo, Error, << "Search request is not supported by the rest source. Required query Parameter: " << param); throw UnsupportedRequestException({}, {}, {}, m_information.RequiredQueryParameters); } } for (auto const& param : m_information.UnsupportedQueryParameters) { if (params.end() != std::find_if(params.begin(), params.end(), [&](const auto& pair) { return Utility::CaseInsensitiveEquals(pair.first, param); })) { AICLI_LOG(Repo, Error, << "Search request is not supported by the rest source. Unsupported query Parameter: " << param); throw UnsupportedRequestException({}, {}, m_information.UnsupportedQueryParameters, {}); } } return result; } web::json::value Interface::GetValidatedSearchBody(const SearchRequest& searchRequest) const { SearchRequest resultSearchRequest = searchRequest; for (auto const& field : m_information.RequiredPackageMatchFields) { PackageMatchField matchField = ConvertStringToPackageMatchField(field); if (searchRequest.Filters.end() == std::find_if(searchRequest.Filters.begin(), searchRequest.Filters.end(), [&](const PackageMatchFilter& filter) { return filter.Field == matchField; })) { if (matchField == PackageMatchField::Market) { resultSearchRequest.Filters.emplace_back(PackageMatchFilter(PackageMatchField::Market, MatchType::CaseInsensitive, Runtime::GetOSRegion())); continue; } AICLI_LOG(Repo, Error, << "Search request is not supported by the rest source. Required package match field: " << field); throw UnsupportedRequestException({}, m_information.RequiredPackageMatchFields, {}, {}); } } for (auto const& field : m_information.UnsupportedPackageMatchFields) { PackageMatchField matchField = ConvertStringToPackageMatchField(field); if (matchField == PackageMatchField::Unknown) { continue; } if (searchRequest.Inclusions.end() != std::find_if(searchRequest.Inclusions.begin(), searchRequest.Inclusions.end(), [&](const PackageMatchFilter& inclusion) { return inclusion.Field == matchField; })) { AICLI_LOG(Repo, Info, << "Search request Inclusions contains package match field not supported by the rest source. Ignoring the field. Unsupported package match field: " << field); resultSearchRequest.Inclusions.erase( std::remove_if( resultSearchRequest.Inclusions.begin(), resultSearchRequest.Inclusions.end(), [&](const PackageMatchFilter& inclusion) { return inclusion.Field == matchField; }), resultSearchRequest.Inclusions.end()); } if (searchRequest.Filters.end() != std::find_if(searchRequest.Filters.begin(), searchRequest.Filters.end(), [&](const PackageMatchFilter& filter) { return filter.Field == matchField; })) { AICLI_LOG(Repo, Error, << "Search request is not supported by the rest source. Unsupported package match field: " << field); throw UnsupportedRequestException(m_information.UnsupportedPackageMatchFields, {}, {}, {}); } } return V1_0::Interface::GetValidatedSearchBody(resultSearchRequest); } IRestClient::SearchResult Interface::GetSearchResult(const web::json::value& searchResponseObject) const { IRestClient::SearchResult result = V1_0::Interface::GetSearchResult(searchResponseObject); if (result.Matches.size() == 0) { auto requiredPackageMatchFields = JSON::GetRawStringArrayFromJsonNode(searchResponseObject, JSON::GetUtilityString(RequiredPackageMatchFields)); auto unsupportedPackageMatchFields = JSON::GetRawStringArrayFromJsonNode(searchResponseObject, JSON::GetUtilityString(UnsupportedPackageMatchFields)); if (requiredPackageMatchFields.size() != 0 || unsupportedPackageMatchFields.size() != 0) { AICLI_LOG(Repo, Error, << "Search request is not supported by the rest source"); throw UnsupportedRequestException(std::move(unsupportedPackageMatchFields), std::move(requiredPackageMatchFields), {}, {}); } } return result; } std::vector Interface::GetParsedManifests(const web::json::value& manifestsResponseObject) const { auto result = V1_0::Interface::GetParsedManifests(manifestsResponseObject); if (result.size() == 0) { auto requiredQueryParameters = JSON::GetRawStringArrayFromJsonNode(manifestsResponseObject, JSON::GetUtilityString(RequiredQueryParameters)); auto unsupportedQueryParameters = JSON::GetRawStringArrayFromJsonNode(manifestsResponseObject, JSON::GetUtilityString(UnsupportedQueryParameters)); if (requiredQueryParameters.size() != 0 || unsupportedQueryParameters.size() != 0) { AICLI_LOG(Repo, Error, << "Search request is not supported by the rest source"); throw UnsupportedRequestException({}, {}, std::move(unsupportedQueryParameters), std::move(requiredQueryParameters)); } } return result; } PackageMatchField Interface::ConvertStringToPackageMatchField(std::string_view field) const { std::string toLower = Utility::ToLower(field); if (toLower == "command") { return PackageMatchField::Command; } else if (toLower == "packageidentifier") { return PackageMatchField::Id; } else if (toLower == "moniker") { return PackageMatchField::Moniker; } else if (toLower == "packagename") { return PackageMatchField::Name; } else if (toLower == "tag") { return PackageMatchField::Tag; } else if (toLower == "packagefamilyname") { return PackageMatchField::PackageFamilyName; } else if (toLower == "productcode") { return PackageMatchField::ProductCode; } else if (toLower == "normalizedpackagenameandpublisher") { return PackageMatchField::NormalizedNameAndPublisher; } else if (toLower == "market") { return PackageMatchField::Market; } return PackageMatchField::Unknown; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_10/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_9/Interface.h" namespace AppInstaller::Repository::Rest::Schema::V1_10 { // Interface to this schema version exposed through IRestClient. struct Interface : public V1_9::Interface { Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}, Authentication::AuthenticationArguments authArgs = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_10/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_9/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_10::Json { // Manifest Deserializer. struct ManifestDeserializer : public V1_9::Json::ManifestDeserializer { protected: std::optional DeserializeInstaller(const web::json::value& installerJsonObject) const override; Manifest::ManifestVer GetManifestVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_10/Json/ManifestDeserializer_1_10.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestDeserializer.h" #include "Rest/Schema/AuthenticationInfoParser.h" #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_10::Json { std::optional ManifestDeserializer::DeserializeInstaller(const web::json::value& installerJsonObject) const { auto result = V1_9::Json::ManifestDeserializer::DeserializeInstaller(installerJsonObject); if (result) { auto& installer = result.value(); installer.AuthInfo = ParseAuthenticationInfo(installerJsonObject, ParseAuthenticationInfoType::Installer, GetManifestVersion()); } return result; } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1_10; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_10/RestInterface_1_10.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_10/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_10 { Interface::Interface( const std::string& restApi, const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders, Authentication::AuthenticationArguments authArgs) : V1_9::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders, std::move(authArgs)) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_10_0.ToString()); } Utility::Version Interface::GetVersion() const { return Version_1_10_0; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_12/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_10/Interface.h" namespace AppInstaller::Repository::Rest::Schema::V1_12 { // Interface to this schema version exposed through IRestClient. struct Interface : public V1_10::Interface { Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}, Authentication::AuthenticationArguments authArgs = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_12/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_10/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_12::Json { // Manifest Deserializer. struct ManifestDeserializer : public V1_10::Json::ManifestDeserializer { protected: Manifest::InstallerTypeEnum ConvertToInstallerType(std::string_view in) const override; Manifest::ManifestVer GetManifestVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_12/Json/ManifestDeserializer_1_12.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestDeserializer.h" #include "Rest/Schema/AuthenticationInfoParser.h" #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_12::Json { Manifest::InstallerTypeEnum ManifestDeserializer::ConvertToInstallerType(std::string_view in) const { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "font") { return InstallerTypeEnum::Font; } return V1_10::Json::ManifestDeserializer::ConvertToInstallerType(inStrLower); } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1_12; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_12/RestInterface_1_12.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_12/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_12 { Interface::Interface( const std::string& restApi, const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders, Authentication::AuthenticationArguments authArgs) : V1_10::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders, std::move(authArgs)) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_12_0.ToString()); } Utility::Version Interface::GetVersion() const { return Version_1_12_0; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_4/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_1/Interface.h" namespace AppInstaller::Repository::Rest::Schema::V1_4 { // Interface to this schema version exposed through IRestClient. struct Interface : public V1_1::Interface { Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_4/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_1/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_4::Json { // Manifest Deserializer. struct ManifestDeserializer : public V1_1::Json::ManifestDeserializer { std::optional DeserializeLocale(const web::json::value& localeJsonObject) const override; std::optional DeserializeInstallationMetadata(const web::json::value& installationMetadataJsonObject) const override; protected: std::optional DeserializeInstaller(const web::json::value& installerJsonObject) const override; Manifest::InstallerTypeEnum ConvertToInstallerType(std::string_view in) const override; Manifest::ExpectedReturnCodeEnum ConvertToExpectedReturnCodeEnum(std::string_view in) const override; Manifest::ManifestInstaller::ExpectedReturnCodeInfo DeserializeExpectedReturnCodeInfo(const web::json::value& expectedReturnCodeJsonObject) const override; Manifest::ManifestVer GetManifestVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_4/Json/ManifestDeserializer_1_4.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestDeserializer.h" #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_4::Json { namespace { // Installer constexpr std::string_view ReturnResponseUrl = "ReturnResponseUrl"sv; constexpr std::string_view NestedInstallerType = "NestedInstallerType"sv; constexpr std::string_view DisplayInstallWarnings = "DisplayInstallWarnings"sv; constexpr std::string_view UnsupportedArguments = "UnsupportedArguments"sv; constexpr std::string_view NestedInstallerFiles = "NestedInstallerFiles"sv; constexpr std::string_view NestedInstallerFileRelativeFilePath = "RelativeFilePath"sv; constexpr std::string_view PortableCommandAlias = "PortableCommandAlias"sv; constexpr std::string_view InstallationMetadata = "InstallationMetadata"sv; constexpr std::string_view DefaultInstallLocation = "DefaultInstallLocation"sv; constexpr std::string_view InstallationMetadataFiles = "Files"sv; constexpr std::string_view InstallationMetadataRelativeFilePath = "RelativeFilePath"sv; constexpr std::string_view FileSha256 = "FileSha256"sv; constexpr std::string_view FileType = "FileType"sv; constexpr std::string_view InvocationParameter = "InvocationParameter"sv; constexpr std::string_view DisplayName = "DisplayName"sv; // Locale constexpr std::string_view InstallationNotes = "InstallationNotes"sv; constexpr std::string_view PurchaseUrl = "PurchaseUrl"sv; constexpr std::string_view Documentations = "Documentations"sv; constexpr std::string_view DocumentLabel = "DocumentLabel"sv; constexpr std::string_view DocumentUrl = "DocumentUrl"sv; } Manifest::InstallerTypeEnum ManifestDeserializer::ConvertToInstallerType(std::string_view in) const { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "portable") { return InstallerTypeEnum::Portable; } return V1_1::Json::ManifestDeserializer::ConvertToInstallerType(inStrLower); } Manifest::ExpectedReturnCodeEnum ManifestDeserializer::ConvertToExpectedReturnCodeEnum(std::string_view in) const { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "custom") { return ExpectedReturnCodeEnum::Custom; } else if (inStrLower == "packageinusebyapplication") { return ExpectedReturnCodeEnum::PackageInUseByApplication; } else if (inStrLower == "invalidparameter") { return ExpectedReturnCodeEnum::InvalidParameter; } else if (inStrLower == "systemnotsupported") { return ExpectedReturnCodeEnum::SystemNotSupported; } return V1_1::Json::ManifestDeserializer::ConvertToExpectedReturnCodeEnum(inStrLower); } Manifest::ManifestInstaller::ExpectedReturnCodeInfo ManifestDeserializer::DeserializeExpectedReturnCodeInfo(const web::json::value& expectedReturnCodeJsonObject) const { auto result = V1_1::Json::ManifestDeserializer::DeserializeExpectedReturnCodeInfo(expectedReturnCodeJsonObject); result.ReturnResponseUrl = JSON::GetRawStringValueFromJsonNode(expectedReturnCodeJsonObject, JSON::GetUtilityString(ReturnResponseUrl)).value_or(""); return result; } std::optional ManifestDeserializer::DeserializeInstallationMetadata(const web::json::value& installationMetadataJsonObject) const { if (installationMetadataJsonObject.is_null() || !installationMetadataJsonObject.is_object()) { return {}; } Manifest::InstallationMetadataInfo installationMetadata; installationMetadata.DefaultInstallLocation = JSON::GetRawStringValueFromJsonNode(installationMetadataJsonObject, JSON::GetUtilityString(DefaultInstallLocation)).value_or(""); auto filesNode = JSON::GetRawJsonArrayFromJsonNode(installationMetadataJsonObject, JSON::GetUtilityString(InstallationMetadataFiles)); if (filesNode) { for (auto const& fileNode : filesNode->get()) { std::optional relativeFilePath = JSON::GetRawStringValueFromJsonNode(fileNode, JSON::GetUtilityString(InstallationMetadataRelativeFilePath)); if (!JSON::IsValidNonEmptyStringValue(relativeFilePath)) { AICLI_LOG(Repo, Error, << "Missing RelativeFilePath in InstallationMetadata Files."); return {}; } Manifest::InstalledFile installedFile; installedFile.RelativeFilePath = std::move(*relativeFilePath); installedFile.InvocationParameter = JSON::GetRawStringValueFromJsonNode(fileNode, JSON::GetUtilityString(InvocationParameter)).value_or(""); installedFile.DisplayName = JSON::GetRawStringValueFromJsonNode(fileNode, JSON::GetUtilityString(DisplayName)).value_or(""); std::optional sha256 = JSON::GetRawStringValueFromJsonNode(fileNode, JSON::GetUtilityString(FileSha256)); if (JSON::IsValidNonEmptyStringValue(sha256)) { installedFile.FileSha256 = Utility::SHA256::ConvertToBytes(*sha256); } std::optional fileType = JSON::GetRawStringValueFromJsonNode(fileNode, JSON::GetUtilityString(FileType)); if (JSON::IsValidNonEmptyStringValue(fileType)) { installedFile.FileType = Manifest::ConvertToInstalledFileTypeEnum(*fileType); } installationMetadata.Files.emplace_back(std::move(installedFile)); } } return installationMetadata; } std::optional ManifestDeserializer::DeserializeInstaller(const web::json::value& installerJsonObject) const { auto result = V1_1::Json::ManifestDeserializer::DeserializeInstaller(installerJsonObject); if (result) { auto& installer = result.value(); std::optional nestedInstallerType = JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(NestedInstallerType)); if (nestedInstallerType) { installer.NestedInstallerType = ConvertToInstallerType(*nestedInstallerType); } installer.DisplayInstallWarnings = JSON::GetRawBoolValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(DisplayInstallWarnings)).value_or(false); // UnsupportedArguments auto unsupportedArguments = JSON::GetRawStringArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(UnsupportedArguments)); for (auto const& unsupportedArgument : unsupportedArguments) { auto unsupportedArgumentEnum = Manifest::ConvertToUnsupportedArgumentEnum(unsupportedArgument); if (unsupportedArgumentEnum != Manifest::UnsupportedArgumentEnum::Unknown) { installer.UnsupportedArguments.emplace_back(unsupportedArgumentEnum); } } // NestedInstallerFiles auto nestedInstallerFilesNode = JSON::GetRawJsonArrayFromJsonNode(installerJsonObject, JSON::GetUtilityString(NestedInstallerFiles)); if (nestedInstallerFilesNode) { for (auto const& nestedInstallerFileNode : nestedInstallerFilesNode->get()) { std::optional relativeFilePath = JSON::GetRawStringValueFromJsonNode(nestedInstallerFileNode, JSON::GetUtilityString(NestedInstallerFileRelativeFilePath)); if (!JSON::IsValidNonEmptyStringValue(relativeFilePath)) { AICLI_LOG(Repo, Error, << "Missing RelativeFilePath in NestedInstallerFiles."); return {}; } Manifest::NestedInstallerFile nestedInstallerFile; nestedInstallerFile.RelativeFilePath = std::move(*relativeFilePath); nestedInstallerFile.PortableCommandAlias = JSON::GetRawStringValueFromJsonNode(nestedInstallerFileNode, JSON::GetUtilityString(PortableCommandAlias)).value_or(""); installer.NestedInstallerFiles.emplace_back(std::move(nestedInstallerFile)); } } // InstallationMetadata auto installationMetadataNode = JSON::GetJsonValueFromNode(installerJsonObject, JSON::GetUtilityString(InstallationMetadata)); if (installationMetadataNode) { auto installationMetadata = DeserializeInstallationMetadata(installationMetadataNode->get()); if (installationMetadata) { installer.InstallationMetadata = std::move(*installationMetadata); } } } return result; } std::optional ManifestDeserializer::DeserializeLocale(const web::json::value& localeJsonObject) const { auto result = V1_1::Json::ManifestDeserializer::DeserializeLocale(localeJsonObject); if (result) { auto& locale = result.value(); TryParseStringLocaleField(locale, localeJsonObject, InstallationNotes); TryParseStringLocaleField(locale, localeJsonObject, PurchaseUrl); // Documentations auto documentationsNode = JSON::GetRawJsonArrayFromJsonNode(localeJsonObject, JSON::GetUtilityString(Documentations)); if (documentationsNode) { std::vector documentations; for (auto const& documentationNode : documentationsNode->get()) { Manifest::Documentation documentationEntry; documentationEntry.DocumentLabel = JSON::GetRawStringValueFromJsonNode(documentationNode, JSON::GetUtilityString(DocumentLabel)).value_or(""); documentationEntry.DocumentUrl = JSON::GetRawStringValueFromJsonNode(documentationNode, JSON::GetUtilityString(DocumentUrl)).value_or(""); if (!documentationEntry.DocumentLabel.empty() || !documentationEntry.DocumentUrl.empty()) { documentations.emplace_back(std::move(documentationEntry)); } } if (!documentations.empty()) { locale.Add(std::move(documentations)); } } } return result; } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1_4; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_4/Json/SearchResponseDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Rest/Schema/1_0/Json/SearchResponseDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_4::Json { // Search Result Deserializer. struct SearchResponseDeserializer : public V1_0::Json::SearchResponseDeserializer { protected: std::optional DeserializeVersionInfo(const web::json::value& versionInfoJsonObject) const; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_4/Json/SearchResponseDeserializer_1_4.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchResponseDeserializer.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_4::Json { namespace { // Search response constants constexpr std::string_view UpgradeCodes = "UpgradeCodes"sv; constexpr std::string_view AppsAndFeaturesEntryVersions = "AppsAndFeaturesEntryVersions"sv; } std::optional SearchResponseDeserializer::DeserializeVersionInfo(const web::json::value& versionInfoJsonObject) const { auto result = V1_0::Json::SearchResponseDeserializer::DeserializeVersionInfo(versionInfoJsonObject); if (result.has_value()) { result->UpgradeCodes = AppInstaller::Rest::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(UpgradeCodes))); auto arpVersions = AppInstaller::Rest::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(AppsAndFeaturesEntryVersions))); for (auto const& version : arpVersions) { result->ArpVersions.emplace_back(Utility::Version{ version }); } // Sort the arp versions for later querying std::sort(result->ArpVersions.begin(), result->ArpVersions.end()); } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_4/RestInterface_1_4.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_4/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_4 { Interface::Interface( const std::string& restApi, const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_1::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_4_0.ToString()); } Utility::Version Interface::GetVersion() const { return Version_1_4_0; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_5/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_4/Interface.h" namespace AppInstaller::Repository::Rest::Schema::V1_5 { // Interface to this schema version exposed through IRestClient. struct Interface : public V1_4::Interface { Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_5/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_4/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_5::Json { // Manifest Deserializer. struct ManifestDeserializer : public V1_4::Json::ManifestDeserializer { std::optional DeserializeLocale(const web::json::value& localeJsonObject) const override; protected: Manifest::ManifestVer GetManifestVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_5/Json/ManifestDeserializer_1_5.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestDeserializer.h" #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_5::Json { namespace { // Locale constexpr std::string_view Icons = "Icons"sv; constexpr std::string_view IconUrl = "IconUrl"sv; constexpr std::string_view IconFileType = "IconFileType"sv; constexpr std::string_view IconResolution = "IconResolution"sv; constexpr std::string_view IconTheme = "IconTheme"sv; constexpr std::string_view IconSha256 = "IconSha256"sv; } std::optional ManifestDeserializer::DeserializeLocale(const web::json::value& localeJsonObject) const { auto result = V1_4::Json::ManifestDeserializer::DeserializeLocale(localeJsonObject); if (result) { auto& locale = result.value(); // Icons auto iconsNode = JSON::GetRawJsonArrayFromJsonNode(localeJsonObject, JSON::GetUtilityString(Icons)); if (iconsNode) { std::vector icons; for (auto const& iconNode : iconsNode->get()) { Manifest::Icon iconEntry; iconEntry.Url = JSON::GetRawStringValueFromJsonNode(iconNode, JSON::GetUtilityString(IconUrl)).value_or(""); if (iconEntry.Url.empty()) { continue; } auto fileType = JSON::GetRawStringValueFromJsonNode(iconNode, JSON::GetUtilityString(IconFileType)).value_or(""); if (fileType.empty()) { continue; } iconEntry.FileType = Manifest::ConvertToIconFileTypeEnum(fileType); iconEntry.Resolution = Manifest::ConvertToIconResolutionEnum(JSON::GetRawStringValueFromJsonNode(iconNode, JSON::GetUtilityString(IconResolution)).value_or("")); iconEntry.Theme = Manifest::ConvertToIconThemeEnum(JSON::GetRawStringValueFromJsonNode(iconNode, JSON::GetUtilityString(IconTheme)).value_or("")); std::optional sha256 = JSON::GetRawStringValueFromJsonNode(iconNode, JSON::GetUtilityString(IconSha256)); if (JSON::IsValidNonEmptyStringValue(sha256)) { iconEntry.Sha256 = Utility::SHA256::ConvertToBytes(*sha256); } icons.emplace_back(std::move(iconEntry)); } if (!icons.empty()) { locale.Add(std::move(icons)); } } } return result; } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1_5; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_5/RestInterface_1_5.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_5/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_5 { Interface::Interface( const std::string& restApi, const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_4::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_5_0.ToString()); } Utility::Version Interface::GetVersion() const { return Version_1_5_0; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_6/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_5/Interface.h" namespace AppInstaller::Repository::Rest::Schema::V1_6 { // Interface to this schema version exposed through IRestClient. struct Interface : public V1_5::Interface { Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_6/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_5/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_6::Json { // Manifest Deserializer. struct ManifestDeserializer : public V1_5::Json::ManifestDeserializer { protected: std::optional DeserializeInstaller(const web::json::value& installerJsonObject) const override; Manifest::UpdateBehaviorEnum ConvertToUpdateBehavior(std::string_view in) const override; Manifest::ManifestVer GetManifestVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_6/Json/ManifestDeserializer_1_6.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestDeserializer.h" #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_6::Json { namespace { // Installer constexpr std::string_view DownloadCommandProhibited = "DownloadCommandProhibited"sv; } std::optional ManifestDeserializer::DeserializeInstaller(const web::json::value& installerJsonObject) const { auto result = V1_5::Json::ManifestDeserializer::DeserializeInstaller(installerJsonObject); if (result) { auto& installer = result.value(); installer.DownloadCommandProhibited = JSON::GetRawBoolValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(DownloadCommandProhibited)).value_or(false); } return result; } Manifest::UpdateBehaviorEnum ManifestDeserializer::ConvertToUpdateBehavior(std::string_view in) const { std::string inStrLower = Utility::ToLower(in); if (inStrLower == "deny") { return UpdateBehaviorEnum::Deny; } return V1_5::Json::ManifestDeserializer::ConvertToUpdateBehavior(inStrLower); } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1_6; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_6/RestInterface_1_6.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_6/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_6 { Interface::Interface( const std::string& restApi, const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_5::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_6_0.ToString()); } Utility::Version Interface::GetVersion() const { return Version_1_6_0; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_7/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_6/Interface.h" namespace AppInstaller::Repository::Rest::Schema::V1_7 { // Interface to this schema version exposed through IRestClient. struct Interface : public V1_6::Interface { Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}, Authentication::AuthenticationArguments authArgs = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; Http::HttpClientHelper::HttpRequestHeaders GetAuthHeaders() const override; protected: std::unique_ptr m_authenticator; Authentication::AuthenticationArguments m_authArgs; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_7/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_6/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_7::Json { // Manifest Deserializer. struct ManifestDeserializer : public V1_6::Json::ManifestDeserializer { protected: std::optional DeserializeInstaller(const web::json::value& installerJsonObject) const override; std::map DeserializeInstallerSwitches(const web::json::value& installerSwitchesJsonObject) const override; Manifest::ManifestVer GetManifestVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_7/Json/ManifestDeserializer_1_7.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestDeserializer.h" #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_7::Json { namespace { // Installer constexpr std::string_view InstallerSwitchRepair = "Repair"sv; constexpr std::string_view RepairBehavior = "RepairBehavior"sv; } std::optional ManifestDeserializer::DeserializeInstaller(const web::json::value& installerJsonObject) const { auto result = V1_6::Json::ManifestDeserializer::DeserializeInstaller(installerJsonObject); if (result) { auto& installer = result.value(); installer.RepairBehavior = Manifest::ConvertToRepairBehaviorEnum(JSON::GetRawStringValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(RepairBehavior)).value_or("")); } return result; } std::map ManifestDeserializer::DeserializeInstallerSwitches(const web::json::value& installerSwitchesJsonObject) const { auto installerSwitches = V1_6::Json::ManifestDeserializer::DeserializeInstallerSwitches(installerSwitchesJsonObject); auto repairValue = JSON::GetRawStringValueFromJsonNode(installerSwitchesJsonObject, JSON::GetUtilityString(InstallerSwitchRepair)); if (JSON::IsValidNonEmptyStringValue(repairValue)) { installerSwitches[Manifest::InstallerSwitchType::Repair] = repairValue.value(); } return installerSwitches; } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1_7; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_7/RestInterface_1_7.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_7/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_7 { Interface::Interface( const std::string& restApi, const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders, Authentication::AuthenticationArguments authArgs) : V1_6::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders), m_authArgs(std::move(authArgs)) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_7_0.ToString()); if (m_information.Authentication.Type == Authentication::AuthenticationType::MicrosoftEntraId) { AICLI_LOG(Repo, Info, << "Creating authenticator for MicrosoftEntraId authentication. Source Identifier: " << m_information.SourceIdentifier); m_authenticator = std::make_unique(m_information.Authentication, m_authArgs); } else if (m_information.Authentication.Type == Authentication::AuthenticationType::Unknown) { AICLI_LOG(Repo, Error, << "Authentication type unknown for rest source. Source Identifier: " << m_information.SourceIdentifier); THROW_HR(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED); } } Utility::Version Interface::GetVersion() const { return Version_1_7_0; } Http::HttpClientHelper::HttpRequestHeaders Interface::GetAuthHeaders() const { Http::HttpClientHelper::HttpRequestHeaders result; if (m_information.Authentication.Type == Authentication::AuthenticationType::MicrosoftEntraId) { auto authResult = m_authenticator->AuthenticateForToken(); if (FAILED(authResult.Status)) { AICLI_LOG(Repo, Error, << "Authentication failed. Result: " << authResult.Status); THROW_HR_MSG(authResult.Status, "Failed to authenticate for MicrosoftEntraId"); } result.insert_or_assign(web::http::header_names::authorization, JSON::GetUtilityString(Authentication::CreateBearerToken(authResult.Token))); } return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_9/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_7/Interface.h" namespace AppInstaller::Repository::Rest::Schema::V1_9 { // Interface to this schema version exposed through IRestClient. struct Interface : public V1_7::Interface { Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}, Authentication::AuthenticationArguments authArgs = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; Utility::Version GetVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_9/Json/ManifestDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/1_7/Json/ManifestDeserializer.h" namespace AppInstaller::Repository::Rest::Schema::V1_9::Json { // Manifest Deserializer. struct ManifestDeserializer : public V1_7::Json::ManifestDeserializer { protected: std::optional DeserializeInstaller(const web::json::value& installerJsonObject) const override; Manifest::ManifestVer GetManifestVersion() const override; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_9/Json/ManifestDeserializer_1_9.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ManifestDeserializer.h" #include using namespace AppInstaller::Manifest; namespace AppInstaller::Repository::Rest::Schema::V1_9::Json { namespace { // Installer constexpr std::string_view ArchiveBinariesDependOnPath = "ArchiveBinariesDependOnPath"sv; } std::optional ManifestDeserializer::DeserializeInstaller(const web::json::value& installerJsonObject) const { auto result = V1_7::Json::ManifestDeserializer::DeserializeInstaller(installerJsonObject); if (result) { auto& installer = result.value(); installer.ArchiveBinariesDependOnPath = JSON::GetRawBoolValueFromJsonNode(installerJsonObject, JSON::GetUtilityString(ArchiveBinariesDependOnPath)).value_or(false); } return result; } Manifest::ManifestVer ManifestDeserializer::GetManifestVersion() const { return Manifest::s_ManifestVersionV1_9; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/1_9/RestInterface_1_9.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_9/Interface.h" #include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include #include namespace AppInstaller::Repository::Rest::Schema::V1_9 { Interface::Interface( const std::string& restApi, const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders, Authentication::AuthenticationArguments authArgs) : V1_7::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders, std::move(authArgs)) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_9_0.ToString()); } Utility::Version Interface::GetVersion() const { return Version_1_9_0; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/AuthenticationInfoParser.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AuthenticationInfoParser.h" #include namespace AppInstaller::Repository::Rest::Schema { namespace { // Authentication info constants constexpr std::string_view Authentication = "Authentication"sv; constexpr std::string_view AuthenticationType = "AuthenticationType"sv; constexpr std::string_view MicrosoftEntraIdAuthenticationInfo = "MicrosoftEntraIdAuthenticationInfo"sv; constexpr std::string_view MicrosoftEntraId_Resource = "Resource"sv; constexpr std::string_view MicrosoftEntraId_Scope = "Scope"sv; Authentication::AuthenticationType ConvertToAuthenticationTypeForSource(std::string_view in) { std::string inStrLower = Utility::ToLower(in); Authentication::AuthenticationType result = Authentication::AuthenticationType::Unknown; if (inStrLower == "none") { result = Authentication::AuthenticationType::None; } else if (inStrLower == "microsoftentraid") { result = Authentication::AuthenticationType::MicrosoftEntraId; } return result; } Authentication::AuthenticationType ConvertToAuthenticationTypeForInstaller(std::string_view in, Manifest::ManifestVer manifestVersion) { if (manifestVersion >= Manifest::ManifestVer{ Manifest::s_ManifestVersionV1_10 }) { return Authentication::ConvertToAuthenticationType(in); } return Authentication::AuthenticationType::Unknown; } } // The authentication info json looks like below: // "Authentication": { // "AuthenticationType": "microsoftEntraId", // "MicrosoftEntraIdAuthenticationInfo" : { // "Resource": "GUID", // "Scope" : "test" // } // } Authentication::AuthenticationInfo ParseAuthenticationInfo(const web::json::value& dataObject, ParseAuthenticationInfoType parseType, std::optional manifestVersion) { auto authenticationObject = JSON::GetJsonValueFromNode(dataObject, JSON::GetUtilityString(Authentication)); if (!authenticationObject) { AICLI_LOG(Repo, Info, << "Authentication node not found. Assuming authentication type none."); return {}; } const auto& authenticationObjectNode = authenticationObject.value().get(); if (authenticationObjectNode.is_null()) { AICLI_LOG(Repo, Info, << "Authentication node is null. Assuming authentication type none."); return {}; } Authentication::AuthenticationInfo result; result.Type = Authentication::AuthenticationType::Unknown; auto authenticationTypeString = JSON::GetRawStringValueFromJsonNode(authenticationObjectNode, JSON::GetUtilityString(AuthenticationType)); // AuthenticationType required if Authentication exists and is not null. THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO, !JSON::IsValidNonEmptyStringValue(authenticationTypeString)); if (parseType == ParseAuthenticationInfoType::Source) { result.Type = ConvertToAuthenticationTypeForSource(authenticationTypeString.value()); } else if (parseType == ParseAuthenticationInfoType::Installer) { THROW_HR_IF(E_INVALIDARG, !manifestVersion); result.Type = ConvertToAuthenticationTypeForInstaller(authenticationTypeString.value(), manifestVersion.value()); } // Parse MicrosoftEntraId info auto microsoftEntraIdInfoObject = JSON::GetJsonValueFromNode(authenticationObjectNode, JSON::GetUtilityString(MicrosoftEntraIdAuthenticationInfo)); if (microsoftEntraIdInfoObject) { const auto& microsoftEntraIdInfoNode = microsoftEntraIdInfoObject.value().get(); Authentication::MicrosoftEntraIdAuthenticationInfo microsoftEntraIdInfo; auto resourceString = JSON::GetRawStringValueFromJsonNode(microsoftEntraIdInfoNode, JSON::GetUtilityString(MicrosoftEntraId_Resource)); // Resource required if MicrosoftEntraIdAuthenticationInfo exists and is not null. THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO, !JSON::IsValidNonEmptyStringValue(resourceString)); microsoftEntraIdInfo.Resource = std::move(resourceString.value()); auto scopeString = JSON::GetRawStringValueFromJsonNode(microsoftEntraIdInfoNode, JSON::GetUtilityString(MicrosoftEntraId_Scope)); if (JSON::IsValidNonEmptyStringValue(scopeString)) { microsoftEntraIdInfo.Scope = std::move(scopeString.value()); } result.MicrosoftEntraIdInfo = std::move(microsoftEntraIdInfo); } result.UpdateRequiredFieldsIfNecessary(); THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO, !result.ValidateIntegrity()); return result; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/AuthenticationInfoParser.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Repository::Rest::Schema { enum class ParseAuthenticationInfoType { Source, Installer, }; // Parses AuthenticationInfo from json object. // This could be used for installer level parsing as well in manifest deserializer (currently not supported, manifestVersion not used). // The authentication info json looks like below: // "Authentication": { // "AuthenticationType": "microsoftEntraId", // "MicrosoftEntraIdAuthenticationInfo" : { // "Resource": "GUID", // "Scope" : "test" // } // } Authentication::AuthenticationInfo ParseAuthenticationInfo(const web::json::value& dataObject, ParseAuthenticationInfoType parseType, std::optional manifestVersion = {}); } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/CommonRestConstants.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::Repository::Rest::Schema { // Winget supported contract versions const Utility::Version Version_1_0_0{ "1.0.0" }; const Utility::Version Version_1_1_0{ "1.1.0" }; const Utility::Version Version_1_4_0{ "1.4.0" }; const Utility::Version Version_1_5_0{ "1.5.0" }; const Utility::Version Version_1_6_0{ "1.6.0" }; const Utility::Version Version_1_7_0{ "1.7.0" }; const Utility::Version Version_1_9_0{ "1.9.0" }; const Utility::Version Version_1_10_0{ "1.10.0" }; const Utility::Version Version_1_12_0{ "1.12.0" }; // General API response constants constexpr std::string_view Data = "Data"sv; constexpr std::string_view ContinuationToken = "ContinuationToken"sv; // General API Header constant constexpr std::string_view ContractVersion = "Version"sv; // General endpoint constants constexpr std::string_view InformationGetEndpoint = "/information"sv; constexpr std::string_view ManifestSearchPostEndpoint = "/manifestSearch"sv; constexpr std::string_view ManifestByVersionAndChannelGetEndpoint = "/packageManifests/"sv; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/IRestClient.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Repository::Rest::Schema { // The common interface used to interact with RestAPI responses. struct IRestClient { virtual ~IRestClient() = default; struct PackageInfo { std::string PackageIdentifier; std::string PackageName; std::string Publisher; PackageInfo(std::string packageIdentifier, std::string packageName, std::string publisher) : PackageIdentifier(std::move(packageIdentifier)), PackageName(std::move(packageName)), Publisher(std::move(publisher)) {} }; // NOTE: When changes are made to VersionInfo struct, remember to update the OptimizedSearch path in RestInterface1_0 // where VersionInfo struct was directly created from manifest. struct VersionInfo { AppInstaller::Utility::VersionAndChannel VersionAndChannel; std::optional Manifest; std::vector PackageFamilyNames; std::vector ProductCodes; std::vector ArpVersions; std::vector UpgradeCodes; VersionInfo(AppInstaller::Utility::VersionAndChannel versionAndChannel, std::optional manifest, std::vector packageFamilyNames = {}, std::vector productCodes = {}, std::vector arpVersions = {}, std::vector upgradeCodes = {}) : VersionAndChannel(std::move(versionAndChannel)), Manifest(std::move(manifest)), PackageFamilyNames(std::move(packageFamilyNames)), ProductCodes(std::move(productCodes)), ArpVersions(std::move(arpVersions)), UpgradeCodes(std::move(upgradeCodes)) {} }; // Minimal information retrieved for any search request. struct Package { PackageInfo PackageInformation; std::vector Versions; Package(PackageInfo packageInfo, std::vector versions) : PackageInformation(std::move(packageInfo)), Versions(std::move(versions)) {} }; struct SearchResult { std::vector Matches; bool Truncated = false; }; struct SourceAgreementEntry { std::string Label; std::string Text; std::string Url; }; // Information endpoint models struct Information { std::string SourceIdentifier; std::vector ServerSupportedVersions; std::string SourceAgreementsIdentifier; std::vector SourceAgreements; std::vector UnsupportedPackageMatchFields; std::vector RequiredPackageMatchFields; std::vector UnsupportedQueryParameters; std::vector RequiredQueryParameters; Authentication::AuthenticationInfo Authentication; Information() {} Information(std::string sourceId, std::vector versions) : SourceIdentifier(std::move(sourceId)), ServerSupportedVersions(std::move(versions)) {} }; // Get interface version. virtual Utility::Version GetVersion() const = 0; // Get source information. virtual Information GetSourceInformation() const = 0; // Performs a search based on the given criteria. virtual SearchResult Search(const SearchRequest& request) const = 0; // Gets the manifest for given version virtual std::optional GetManifestByVersion(const std::string& packageId, const std::string& version, const std::string& channel) const = 0; // Gets the manifests for given query parameters virtual std::vector GetManifests(const std::string& packageId, const std::map& params = {}) const = 0; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/InformationResponseDeserializer.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/IRestClient.h" #include #include "Rest/Schema/CommonRestConstants.h" #include "AuthenticationInfoParser.h" #include "InformationResponseDeserializer.h" namespace AppInstaller::Repository::Rest::Schema { namespace { // Information response constants constexpr std::string_view SourceIdentifier = "SourceIdentifier"sv; constexpr std::string_view ServerSupportedVersions = "ServerSupportedVersions"sv; constexpr std::string_view SourceAgreements = "SourceAgreements"sv; constexpr std::string_view SourceAgreementsIdentifier = "AgreementsIdentifier"sv; constexpr std::string_view SourceAgreementsContent = "Agreements"sv; constexpr std::string_view SourceAgreementLabel = "AgreementLabel"sv; constexpr std::string_view SourceAgreementText = "Agreement"sv; constexpr std::string_view SourceAgreementUrl = "AgreementUrl"sv; constexpr std::string_view UnsupportedPackageMatchFields = "UnsupportedPackageMatchFields"sv; constexpr std::string_view RequiredPackageMatchFields = "RequiredPackageMatchFields"sv; constexpr std::string_view UnsupportedQueryParameters = "UnsupportedQueryParameters"sv; constexpr std::string_view RequiredQueryParameters = "RequiredQueryParameters"sv; } IRestClient::Information InformationResponseDeserializer::Deserialize(const web::json::value& dataObject) const { // Get information result from json output. std::optional information = DeserializeInformation(dataObject); THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !information); return information.value(); } std::optional InformationResponseDeserializer::DeserializeInformation(const web::json::value& dataObject) const { try { if (dataObject.is_null()) { AICLI_LOG(Repo, Error, << "Missing json object."); return {}; } std::optional> data = JSON::GetJsonValueFromNode(dataObject, JSON::GetUtilityString(Data)); if (!data) { AICLI_LOG(Repo, Error, << "Missing data"); return {}; } const auto& dataValue = data.value().get(); std::optional sourceId = JSON::GetRawStringValueFromJsonNode(dataValue, JSON::GetUtilityString(SourceIdentifier)); if (!JSON::IsValidNonEmptyStringValue(sourceId)) { AICLI_LOG(Repo, Error, << "Missing source identifier"); return {}; } std::vector allVersions = JSON::GetRawStringArrayFromJsonNode(dataValue, JSON::GetUtilityString(ServerSupportedVersions)); if (allVersions.size() == 0) { AICLI_LOG(Repo, Error, << "Missing supported versions."); return {}; } IRestClient::Information info{ std::move(sourceId.value()), std::move(allVersions) }; auto agreements = JSON::GetJsonValueFromNode(dataValue, JSON::GetUtilityString(SourceAgreements)); if (agreements) { const auto& agreementsValue = agreements.value().get(); auto agreementsIdentifier = JSON::GetRawStringValueFromJsonNode(agreementsValue, JSON::GetUtilityString(SourceAgreementsIdentifier)); if (!JSON::IsValidNonEmptyStringValue(agreementsIdentifier)) { AICLI_LOG(Repo, Error, << "SourceAgreements node exists but AgreementsIdentifier is missing."); return {}; } info.SourceAgreementsIdentifier = std::move(agreementsIdentifier.value()); auto agreementsContent = JSON::GetRawJsonArrayFromJsonNode(agreementsValue, JSON::GetUtilityString(SourceAgreementsContent)); if (agreementsContent) { for (auto const& agreementNode : agreementsContent.value().get()) { IRestClient::SourceAgreementEntry agreementEntry; std::optional label = JSON::GetRawStringValueFromJsonNode(agreementNode, JSON::GetUtilityString(SourceAgreementLabel)); if (JSON::IsValidNonEmptyStringValue(label)) { agreementEntry.Label = std::move(label.value()); } std::optional text = JSON::GetRawStringValueFromJsonNode(agreementNode, JSON::GetUtilityString(SourceAgreementText)); if (JSON::IsValidNonEmptyStringValue(text)) { agreementEntry.Text = std::move(text.value()); } std::optional url = JSON::GetRawStringValueFromJsonNode(agreementNode, JSON::GetUtilityString(SourceAgreementUrl)); if (JSON::IsValidNonEmptyStringValue(url)) { agreementEntry.Url = std::move(url.value()); } if (!agreementEntry.Label.empty() || !agreementEntry.Text.empty() || !agreementEntry.Url.empty()) { info.SourceAgreements.emplace_back(std::move(agreementEntry)); } } } } info.RequiredPackageMatchFields = JSON::GetRawStringArrayFromJsonNode(dataValue, JSON::GetUtilityString(RequiredPackageMatchFields)); info.UnsupportedPackageMatchFields = JSON::GetRawStringArrayFromJsonNode(dataValue, JSON::GetUtilityString(UnsupportedPackageMatchFields)); info.RequiredQueryParameters = JSON::GetRawStringArrayFromJsonNode(dataValue, JSON::GetUtilityString(RequiredQueryParameters)); info.UnsupportedQueryParameters = JSON::GetRawStringArrayFromJsonNode(dataValue, JSON::GetUtilityString(UnsupportedQueryParameters)); info.Authentication = ParseAuthenticationInfo(dataValue, ParseAuthenticationInfoType::Source); return info; } catch (const std::exception& e) { AICLI_LOG(Repo, Error, << "Error encountered while deserializing Information. Reason: " << e.what()); } catch (...) { AICLI_LOG(Repo, Error, << "Received invalid information."); } return {}; } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/InformationResponseDeserializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Rest/Schema/IRestClient.h" namespace AppInstaller::Repository::Rest::Schema { // Information response Deserializer. struct InformationResponseDeserializer { // Gets the information model for given response IRestClient::Information Deserialize(const web::json::value& dataObject) const; protected: std::optional DeserializeInformation(const web::json::value& dataObject) const; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/SearchRequestComposer.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchRequestComposer.h" #include "Rest/Schema/1_0/Json/SearchRequestSerializer.h" #include "Rest/Schema/1_1/Json/SearchRequestSerializer.h" namespace AppInstaller::Repository::Rest::Schema { struct SearchRequestComposer::impl { // The serializer. We only have one lineage (1.0+) right now. std::unique_ptr m_serializer; }; SearchRequestComposer::SearchRequestComposer(SearchRequestComposer&&) noexcept = default; SearchRequestComposer& SearchRequestComposer::operator=(SearchRequestComposer&&) noexcept = default; SearchRequestComposer::~SearchRequestComposer() = default; SearchRequestComposer::SearchRequestComposer(const Utility::Version& schemaVersion) { const auto& parts = schemaVersion.GetParts(); THROW_HR_IF(E_INVALIDARG, parts.empty()); m_pImpl = std::make_unique(); if (parts[0].Integer == 1) { if (parts.size() == 1 || parts[1].Integer == 0) { m_pImpl->m_serializer = std::make_unique(); } else { m_pImpl->m_serializer = std::make_unique(); } } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)); } } web::json::value SearchRequestComposer::Serialize(const AppInstaller::Repository::SearchRequest& searchRequest) const { return m_pImpl->m_serializer->Serialize(searchRequest); } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/SearchRequestComposer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::Repository::Rest::Schema { // Exposes functions for creating JSON REST request body from search requests. struct SearchRequestComposer { SearchRequestComposer(const Utility::Version& schemaVersion); SearchRequestComposer(const SearchRequestComposer&) = delete; SearchRequestComposer& operator=(const SearchRequestComposer&) = delete; SearchRequestComposer(SearchRequestComposer&&) noexcept; SearchRequestComposer& operator=(SearchRequestComposer&&) noexcept; ~SearchRequestComposer(); // Create search request rest call body from a given SearchRequest web::json::value Serialize(const AppInstaller::Repository::SearchRequest& searchRequest) const; private: struct impl; std::unique_ptr m_pImpl; }; } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/SearchResponseParser.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SearchResponseParser.h" #include "Rest/Schema/1_0/Json/SearchResponseDeserializer.h" #include "Rest/Schema/1_4/Json/SearchResponseDeserializer.h" namespace AppInstaller::Repository::Rest::Schema { struct SearchResponseParser::impl { // The deserializer. We only have one lineage (1.0+) right now. std::unique_ptr m_deserializer; }; SearchResponseParser::SearchResponseParser(SearchResponseParser&&) noexcept = default; SearchResponseParser& SearchResponseParser::operator=(SearchResponseParser&&) noexcept = default; SearchResponseParser::~SearchResponseParser() = default; SearchResponseParser::SearchResponseParser(const Utility::Version& schemaVersion) { const auto& parts = schemaVersion.GetParts(); THROW_HR_IF(E_INVALIDARG, parts.empty()); m_pImpl = std::make_unique(); if (parts[0].Integer == 1) { if (parts.size() == 1 || parts[1].Integer < 4) { m_pImpl->m_deserializer = std::make_unique(); } else { m_pImpl->m_deserializer = std::make_unique(); } } else { THROW_HR(HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)); } } IRestClient::SearchResult SearchResponseParser::Deserialize(const web::json::value& searchResultJsonObject) const { return m_pImpl->m_deserializer->Deserialize(searchResultJsonObject); } } ================================================ FILE: src/AppInstallerRepositoryCore/Rest/Schema/SearchResponseParser.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include "Rest/Schema/IRestClient.h" #include #include namespace AppInstaller::Repository::Rest::Schema { // Exposes functions for parsing JSON REST responses to IRestClient SearchResult. struct SearchResponseParser { SearchResponseParser(const Utility::Version& schemaVersion); SearchResponseParser(const SearchResponseParser&) = delete; SearchResponseParser& operator=(const SearchResponseParser&) = delete; SearchResponseParser(SearchResponseParser&&) noexcept; SearchResponseParser& operator=(SearchResponseParser&&) noexcept; ~SearchResponseParser(); // Gets the search result for response object IRestClient::SearchResult Deserialize(const web::json::value& searchResultJsonObject) const; private: struct impl; std::unique_ptr m_pImpl; }; } ================================================ FILE: src/AppInstallerRepositoryCore/SourceFactory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" #include #include namespace AppInstaller::Repository { // Interface for manipulating a source based on its details. struct ISourceFactory { virtual ~ISourceFactory() = default; // Gets the name of the source type. virtual std::string_view TypeName() const = 0; // Creates a source object from the given details. virtual std::shared_ptr Create(const SourceDetails& details) = 0; // Adds the source from the given details, writing back to the details any changes. // Return value indicates whether the action completed. virtual bool Add(SourceDetails& details, IProgressCallback& progress) = 0; // Updates the source from the given details (may not change the details). // Return value indicates whether the action completed. virtual bool Update(const SourceDetails& details, IProgressCallback& progress) = 0; // Updates the source from the given details (may not change the details). // This version is for use in automatic, background updates to the source. // It is done this way to preserve the signature for use with member function pointers. // Return value indicates whether the action completed. virtual bool BackgroundUpdate(const SourceDetails& details, IProgressCallback& progress) { return Update(details, progress); } // Removes the source from the given details. // Return value indicates whether the action completed. virtual bool Remove(const SourceDetails& details, IProgressCallback& progress) = 0; // Gets the factory for the given type. static std::unique_ptr GetForType(std::string_view type); }; } ================================================ FILE: src/AppInstallerRepositoryCore/SourceList.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SourceList.h" #include "SourcePolicy.h" #include "Microsoft/PreIndexedPackageSourceFactory.h" #include "Rest/RestSourceFactory.h" #include #include #include using namespace AppInstaller::Settings; using namespace std::string_view_literals; namespace AppInstaller::Repository { namespace { constexpr std::string_view s_SourcesYaml_Sources = "Sources"sv; constexpr std::string_view s_SourcesYaml_Source_Name = "Name"sv; constexpr std::string_view s_SourcesYaml_Source_Type = "Type"sv; constexpr std::string_view s_SourcesYaml_Source_Arg = "Arg"sv; constexpr std::string_view s_SourcesYaml_Source_Data = "Data"sv; constexpr std::string_view s_SourcesYaml_Source_Identifier = "Identifier"sv; constexpr std::string_view s_SourcesYaml_Source_IsTombstone = "IsTombstone"sv; constexpr std::string_view s_SourcesYaml_Source_IsOverride = "IsOverride"sv; constexpr std::string_view s_SourcesYaml_Source_Explicit = "Explicit"sv; constexpr std::string_view s_SourcesYaml_Source_TrustLevel = "TrustLevel"sv; constexpr std::string_view s_SourcesYaml_Source_Priority = "Priority"sv; constexpr std::string_view s_MetadataYaml_Sources = "Sources"sv; constexpr std::string_view s_MetadataYaml_Source_Name = "Name"sv; constexpr std::string_view s_MetadataYaml_Source_LastUpdate = "LastUpdate"sv; constexpr std::string_view s_MetadataYaml_Source_DoNotUpdateBefore = "DoNotUpdateBefore"sv; constexpr std::string_view s_MetadataYaml_Source_AcceptedAgreementsIdentifier = "AcceptedAgreementsIdentifier"sv; constexpr std::string_view s_MetadataYaml_Source_AcceptedAgreementFields = "AcceptedAgreementFields"sv; constexpr std::string_view s_Source_WingetCommunityDefault_Name = "winget"sv; constexpr std::string_view s_Source_WingetCommunityDefault_Arg = "https://cdn.winget.microsoft.com/cache"sv; constexpr std::string_view s_Source_WingetCommunityDefault_Data = "Microsoft.Winget.Source_8wekyb3d8bbwe"sv; constexpr std::string_view s_Source_WingetCommunityDefault_Identifier = "Microsoft.Winget.Source_8wekyb3d8bbwe"sv; constexpr std::string_view s_Source_MSStoreDefault_Name = "msstore"sv; constexpr std::string_view s_Source_MSStoreDefault_Arg = "https://storeedgefd.dsx.mp.microsoft.com/v9.0"sv; constexpr std::string_view s_Source_MSStoreDefault_Identifier = "StoreEdgeFD"sv; constexpr std::string_view s_Source_DesktopFrameworks_Name = "microsoft.builtin.desktop.frameworks"sv; constexpr std::string_view s_Source_DesktopFrameworks_Arg = "https://cdn.winget.microsoft.com/platform"sv; constexpr std::string_view s_Source_DesktopFrameworks_Data = "Microsoft.Winget.Platform.Source_8wekyb3d8bbwe"sv; constexpr std::string_view s_Source_DesktopFrameworks_Identifier = "Microsoft.Winget.Platform.Source_8wekyb3d8bbwe"sv; constexpr std::string_view s_Source_WingetCommunityFont_Name = "winget-font"sv; constexpr std::string_view s_Source_WingetCommunityFont_Arg = "https://cdn.winget.microsoft.com/fonts"sv; constexpr std::string_view s_Source_WingetCommunityFont_Data = "Microsoft.Winget.Fonts.Source_8wekyb3d8bbwe"sv; constexpr std::string_view s_Source_WingetCommunityFont_Identifier = "Microsoft.Winget.Fonts.Source_8wekyb3d8bbwe"sv; // Attempts to read a single scalar value from the node. template bool TryReadScalar(std::string_view settingName, const std::string& settingValue, const YAML::Node& sourceNode, std::string_view name, Value& value, bool required = true) { YAML::Node valueNode = sourceNode[std::string{ name }]; if (!valueNode || !valueNode.IsScalar()) { if (required) { AICLI_LOG(Repo, Error, << "Setting '" << settingName << "' did not contain the expected format (" << name << " is invalid within a source):\n" << settingValue); } return false; } value = valueNode.as(); return true; } // Attempts to read the source details from the given stream. // Results are all or nothing; if any failures occur, no details are returned. bool TryReadSourceDetails( std::string_view settingName, std::istream& stream, std::string_view rootName, std::function parse, std::vector& sourceDetails) { std::vector result; std::string settingValue = Utility::ReadEntireStream(stream); YAML::Node document; try { document = YAML::Load(settingValue); } catch (const std::exception& e) { AICLI_LOG(YAML, Error, << "Setting '" << settingName << "' contained invalid YAML (" << e.what() << "):\n" << settingValue); return false; } try { YAML::Node sources = document[rootName]; if (!sources) { AICLI_LOG(Repo, Error, << "Setting '" << settingName << "' did not contain the expected format (missing " << rootName << "):\n" << settingValue); return false; } if (sources.IsNull()) { // An empty sources is an acceptable thing. return true; } if (!sources.IsSequence()) { AICLI_LOG(Repo, Error, << "Setting '" << settingName << "' did not contain the expected format (" << rootName << " was not a sequence):\n" << settingValue); return false; } for (const auto& source : sources.Sequence()) { SourceDetailsInternal details; if (!parse(details, settingValue, source)) { return false; } result.emplace_back(std::move(details)); } } catch (const std::exception& e) { AICLI_LOG(YAML, Error, << "Setting '" << settingName << "' contained unexpected YAML (" << e.what() << "):\n" << settingValue); return false; } sourceDetails = std::move(result); return true; } // Gets the source details from a particular setting, or an empty optional if no setting exists. std::optional> TryGetSourcesFromSetting( Settings::Stream& setting, std::string_view rootName, std::function parse) { auto sourcesStream = setting.Get(); if (!sourcesStream) { // Note that this case is different than the one in which all sources have been removed. return {}; } else { std::vector result; if (!TryReadSourceDetails(setting.GetName(), *sourcesStream, rootName, parse, result)) { AICLI_LOG(YAML, Error, << "Ignoring corrupted source data."); } return result; } } // Gets the source details from a particular setting. std::vector GetSourcesFromSetting( Settings::Stream& setting, std::string_view rootName, std::function parse) { return TryGetSourcesFromSetting(setting, rootName, parse).value_or(std::vector{}); } // Sets the sources for a particular setting, from a particular origin. [[nodiscard]] bool SetSourcesToSettingWithFilter(Settings::Stream& setting, SourceOrigin origin, const std::vector& sources) { YAML::Emitter out; out << YAML::BeginMap; out << YAML::Key << s_SourcesYaml_Sources; out << YAML::BeginSeq; for (const auto& details : sources) { if (details.Origin == origin) { out << YAML::BeginMap; out << YAML::Key << s_SourcesYaml_Source_Name << YAML::Value << details.Name; out << YAML::Key << s_SourcesYaml_Source_Type << YAML::Value << details.Type; out << YAML::Key << s_SourcesYaml_Source_Arg << YAML::Value << details.Arg; out << YAML::Key << s_SourcesYaml_Source_Data << YAML::Value << details.Data; out << YAML::Key << s_SourcesYaml_Source_Identifier << YAML::Value << details.Identifier; out << YAML::Key << s_SourcesYaml_Source_IsTombstone << YAML::Value << details.IsTombstone; out << YAML::Key << s_SourcesYaml_Source_IsOverride << YAML::Value << details.IsOverride; out << YAML::Key << s_SourcesYaml_Source_Explicit << YAML::Value << details.Explicit; out << YAML::Key << s_SourcesYaml_Source_TrustLevel << YAML::Value << static_cast(details.TrustLevel); out << YAML::Key << s_SourcesYaml_Source_Priority << YAML::Value << details.Priority; out << YAML::EndMap; } } out << YAML::EndSeq; out << YAML::EndMap; return setting.Set(out.str()); } // Assumes that names match already bool DoSourceDetailsInternalMatch(const SourceDetailsInternal& left, const SourceDetailsInternal& right) { return left.Arg == right.Arg && left.Identifier == right.Identifier && Utility::CaseInsensitiveEquals(left.Type, right.Type); } bool ShouldBeHidden(const SourceDetailsInternal& details) { return details.IsTombstone || details.Origin == SourceOrigin::Metadata || !details.IsVisible; } } void SourceDetailsInternal::CopyMetadataFieldsTo(SourceDetailsInternal& target) { if (LastUpdateTime > target.LastUpdateTime) { target.LastUpdateTime = LastUpdateTime; } if (DoNotUpdateBefore > target.DoNotUpdateBefore) { target.DoNotUpdateBefore = DoNotUpdateBefore; } target.AcceptedAgreementFields = AcceptedAgreementFields; target.AcceptedAgreementsIdentifier = AcceptedAgreementsIdentifier; } void SourceDetailsInternal::CopyMetadataFieldsFrom(const SourceDetails& source) { LastUpdateTime = source.LastUpdateTime; DoNotUpdateBefore = source.DoNotUpdateBefore; } void SourceDetailsInternal::CopyOverrideFieldsFrom(const SourceDetails& overrideSource) { // These are the supported Override fields. Explicit = overrideSource.Explicit; Priority = overrideSource.Priority; } bool SourceDetailsInternal::operator<(const SourceDetailsInternal& other) const { // Higher values come first in ordering and must be "less than" for standard sorting return Priority > other.Priority; } std::string_view GetWellKnownSourceName(WellKnownSource source) { switch (source) { case WellKnownSource::WinGet: return s_Source_WingetCommunityDefault_Name; case WellKnownSource::MicrosoftStore: return s_Source_MSStoreDefault_Name; case WellKnownSource::DesktopFrameworks: return s_Source_DesktopFrameworks_Name; case WellKnownSource::WinGetFont: return s_Source_WingetCommunityFont_Name; } return {}; } std::string_view GetWellKnownSourceArg(WellKnownSource source) { switch (source) { case WellKnownSource::WinGet: return s_Source_WingetCommunityDefault_Arg; case WellKnownSource::MicrosoftStore: return s_Source_MSStoreDefault_Arg; case WellKnownSource::DesktopFrameworks: return s_Source_DesktopFrameworks_Arg; case WellKnownSource::WinGetFont: return s_Source_WingetCommunityFont_Arg; } return {}; } std::string_view GetWellKnownSourceIdentifier(WellKnownSource source) { switch (source) { case WellKnownSource::WinGet: return s_Source_WingetCommunityDefault_Identifier; case WellKnownSource::MicrosoftStore: return s_Source_MSStoreDefault_Identifier; case WellKnownSource::DesktopFrameworks: return s_Source_DesktopFrameworks_Identifier; case WellKnownSource::WinGetFont: return s_Source_WingetCommunityFont_Identifier; } return {}; } std::optional CheckForWellKnownSourceMatch(std::string_view name, std::string_view arg, std::string_view type) { if (name == s_Source_WingetCommunityDefault_Name && arg == s_Source_WingetCommunityDefault_Arg && type == Microsoft::PreIndexedPackageSourceFactory::Type()) { return WellKnownSource::WinGet; } if (name == s_Source_MSStoreDefault_Name && arg == s_Source_MSStoreDefault_Arg && type == Rest::RestSourceFactory::Type()) { return WellKnownSource::MicrosoftStore; } if (name == s_Source_DesktopFrameworks_Name && arg == s_Source_DesktopFrameworks_Arg && type == Microsoft::PreIndexedPackageSourceFactory::Type()) { return WellKnownSource::DesktopFrameworks; } if (name == s_Source_WingetCommunityFont_Name && arg == s_Source_WingetCommunityFont_Arg && type == Rest::RestSourceFactory::Type()) { return WellKnownSource::WinGetFont; } return {}; } SourceDetailsInternal GetWellKnownSourceDetailsInternal(WellKnownSource source) { switch (source) { case WellKnownSource::WinGet: { SourceDetailsInternal details; details.Origin = SourceOrigin::Default; details.Name = s_Source_WingetCommunityDefault_Name; details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); details.Arg = s_Source_WingetCommunityDefault_Arg; details.Data = s_Source_WingetCommunityDefault_Data; details.Identifier = s_Source_WingetCommunityDefault_Identifier; details.TrustLevel = SourceTrustLevel::Trusted | SourceTrustLevel::StoreOrigin; return details; } case WellKnownSource::MicrosoftStore: { SourceDetailsInternal details; details.Origin = SourceOrigin::Default; details.Name = s_Source_MSStoreDefault_Name; details.Type = Rest::RestSourceFactory::Type(); details.Arg = s_Source_MSStoreDefault_Arg; details.Identifier = s_Source_MSStoreDefault_Identifier; details.TrustLevel = SourceTrustLevel::Trusted; details.SupportInstalledSearchCorrelation = false; if (!Settings::IsAdminSettingEnabled(Settings::BoolAdminSetting::BypassCertificatePinningForMicrosoftStore)) { using namespace AppInstaller::Certificates; PinningChain chain; auto chainElement = chain.Root(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_1, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_1, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chainElement = chainElement.Next(); chainElement->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_1, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); PinningChain chain2; auto chainElement2 = chain2.Root(); chainElement2->LoadCertificate(IDX_CERTIFICATE_STORE_ROOT_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::PublicKey); chainElement2 = chainElement2.Next(); chainElement2->LoadCertificate(IDX_CERTIFICATE_STORE_INTERMEDIATE_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); chainElement2 = chainElement2.Next(); chainElement2->LoadCertificate(IDX_CERTIFICATE_STORE_LEAF_2, CERTIFICATE_RESOURCE_TYPE).SetPinning(PinningVerificationType::Subject | PinningVerificationType::Issuer); // See https://aka.ms/AzureTLSCAs (internal) for the source of these CAs PinningChain chain3; chain3.PartialChain().Root()-> LoadCertificate(IDX_CERTIFICATE_MS_TLS_ECC_ROOT_G2, CERTIFICATE_RESOURCE_TYPE). SetPinning(PinningVerificationType::PublicKey | PinningVerificationType::AnyIssuer | PinningVerificationType::RequireNonLeaf); PinningChain chain4; chain4.PartialChain().Root()-> LoadCertificate(IDX_CERTIFICATE_MS_TLS_RSA_ROOT_G2, CERTIFICATE_RESOURCE_TYPE). SetPinning(PinningVerificationType::PublicKey | PinningVerificationType::AnyIssuer | PinningVerificationType::RequireNonLeaf); details.CertificatePinningConfiguration = PinningConfiguration("Microsoft Store Source"); details.CertificatePinningConfiguration.AddChain(std::move(chain)); details.CertificatePinningConfiguration.AddChain(std::move(chain2)); details.CertificatePinningConfiguration.AddChain(std::move(chain3)); details.CertificatePinningConfiguration.AddChain(std::move(chain4)); } return details; } case WellKnownSource::DesktopFrameworks: { SourceDetailsInternal details; details.Origin = SourceOrigin::Default; details.Name = s_Source_DesktopFrameworks_Name; details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); details.Arg = s_Source_DesktopFrameworks_Arg; details.Data = s_Source_DesktopFrameworks_Data; details.Identifier = s_Source_DesktopFrameworks_Identifier; details.TrustLevel = SourceTrustLevel::Trusted | SourceTrustLevel::StoreOrigin; details.IsVisible = false; return details; } case WellKnownSource::WinGetFont: { SourceDetailsInternal details; details.Origin = SourceOrigin::Default; details.Name = s_Source_WingetCommunityFont_Name; details.Type = Microsoft::PreIndexedPackageSourceFactory::Type(); details.Arg = s_Source_WingetCommunityFont_Arg; details.Data = s_Source_WingetCommunityFont_Data; details.Identifier = s_Source_WingetCommunityFont_Identifier; details.TrustLevel = SourceTrustLevel::Trusted | SourceTrustLevel::StoreOrigin; details.Explicit = true; return details; } } THROW_HR(E_UNEXPECTED); } SourceList::SourceList() : m_userSourcesStream(Stream::UserSources), m_metadataStream(Stream::SourcesMetadata) { OverwriteSourceList(); OverwriteMetadata(); } std::vector> SourceList::GetCurrentSourceRefs() { std::vector> result; for (auto& s : m_sourceList) { if (!ShouldBeHidden(s)) { result.emplace_back(std::ref(s)); } else { AICLI_LOG(Repo, Verbose, << "GetCurrentSourceRefs: Source named '" << s.Name << "' from origin " << ToString(s.Origin) << " is hidden and is dropped."); } } return result; } auto SourceList::FindSource(std::string_view name, bool includeHidden) { return std::find_if(m_sourceList.begin(), m_sourceList.end(), [name, includeHidden](const SourceDetailsInternal& sd) { return Utility::ICUCaseInsensitiveEquals(sd.Name, name) && (includeHidden || !ShouldBeHidden(sd)); }); } bool SourceList::TryFindSourceByOrigin(std::string_view name, SourceOrigin origin, SourceDetailsInternal& targetSourceOut, bool includeHidden) { auto defaultSources = GetSourcesByOrigin(origin); auto iter = std::find_if(defaultSources.begin(), defaultSources.end(), [name, includeHidden](const SourceDetailsInternal& sd) { return Utility::ICUCaseInsensitiveEquals(sd.Name, name) && (includeHidden || !ShouldBeHidden(sd)); }); if (iter == defaultSources.end()) { return false; } targetSourceOut = (*iter); return true; } SourceDetailsInternal* SourceList::GetCurrentSource(std::string_view name) { auto itr = FindSource(name); return itr == m_sourceList.end() ? nullptr : &(*itr); } SourceDetailsInternal* SourceList::GetSource(std::string_view name) { auto itr = FindSource(name, true); return itr == m_sourceList.end() ? nullptr : &(*itr); } void SourceList::AddSource(const SourceDetailsInternal& details) { bool sourcesSet = false; for (size_t i = 0; !sourcesSet && i < 10; ++i) { auto itr = FindSource(details.Name, true); THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, itr != m_sourceList.end() && itr->Origin != SourceOrigin::Metadata && !itr->IsTombstone); // Erase the source's entry if applicable if (itr != m_sourceList.end()) { m_sourceList.erase(itr); } m_sourceList.emplace_back(details); sourcesSet = SetSourcesByOrigin(SourceOrigin::User, m_sourceList); if (!sourcesSet) { OverwriteSourceList(); OverwriteMetadata(); } } THROW_HR_IF_MSG(E_UNEXPECTED, !sourcesSet, "Too many attempts at SetSourcesByOrigin"); SaveMetadataInternal(details); } void SourceList::RemoveSource(const SourceDetailsInternal& detailsRef) { // Copy the incoming details because we might destroy the referenced structure // when reloading the source details from settings. SourceDetailsInternal details = detailsRef; bool sourcesSet = false; for (size_t i = 0; !sourcesSet && i < 10; ++i) { switch (details.Origin) { case SourceOrigin::Default: { auto target = FindSource(details.Name, true); if (target == m_sourceList.end()) { THROW_HR_MSG(E_UNEXPECTED, "Default source not in SourceList"); } if (!target->IsTombstone) { SourceDetailsInternal tombstone; tombstone.Name = details.Name; tombstone.IsTombstone = true; tombstone.Origin = SourceOrigin::User; m_sourceList.emplace_back(std::move(tombstone)); } } break; case SourceOrigin::User: { auto target = FindSource(details.Name); if (target == m_sourceList.end()) { // Assumed that an update to the sources removed it first return; } // If this is an override of a default source, turn this into a tombstone instead of removing it. if (target->IsOverride) { target->IsOverride = false; target->IsTombstone = true; break; } m_sourceList.erase(target); } break; case SourceOrigin::GroupPolicy: // This should have already been blocked higher up. AICLI_LOG(Repo, Error, << "Attempting to remove Group Policy source: " << details.Name); THROW_HR(E_UNEXPECTED); default: THROW_HR(E_UNEXPECTED); } sourcesSet = SetSourcesByOrigin(SourceOrigin::User, m_sourceList); if (!sourcesSet) { OverwriteSourceList(); OverwriteMetadata(); } } THROW_HR_IF_MSG(E_UNEXPECTED, !sourcesSet, "Too many attempts at SetSourcesByOrigin"); SaveMetadataInternal(details, true); } void SourceList::EditSource(const SourceDetailsInternal& detailsRef) { // Copy the incoming details because we might destroy the referenced structure // when reloading the source details from settings. SourceDetailsInternal details = detailsRef; bool sourcesSet = false; for (size_t i = 0; !sourcesSet && i < 10; ++i) { switch (details.Origin) { case SourceOrigin::Default: { auto target = FindSource(details.Name, true); if (target == m_sourceList.end()) { THROW_HR_MSG(E_UNEXPECTED, "Default source not in SourceList"); } if (!target->IsTombstone) { // Copy the original and then apply the override fields. SourceDetailsInternal override = *target; override.Origin = SourceOrigin::User; override.IsOverride = true; override.CopyOverrideFieldsFrom(details); m_sourceList.emplace_back(std::move(override)); } } break; case SourceOrigin::User: { auto target = FindSource(details.Name); if (target == m_sourceList.end()) { // Assumed that an update to the sources removed it first return; } // Editing a User Source is just replacing the fields that can be edited. target->CopyOverrideFieldsFrom(details); } break; case SourceOrigin::GroupPolicy: // This should have already been blocked higher up. AICLI_LOG(Repo, Error, << "Attempting to edit a Group Policy source: " << details.Name); THROW_HR(E_UNEXPECTED); default: THROW_HR(E_UNEXPECTED); } sourcesSet = SetSourcesByOrigin(SourceOrigin::User, m_sourceList); if (!sourcesSet) { OverwriteSourceList(); OverwriteMetadata(); } } THROW_HR_IF_MSG(E_UNEXPECTED, !sourcesSet, "Too many attempts at SetSourcesByOrigin"); SaveMetadataInternal(details, true); } void SourceList::SaveMetadata(const SourceDetailsInternal& details) { SaveMetadataInternal(details); } bool SourceList::CheckSourceAgreements(std::string_view sourceName, std::string_view agreementsIdentifier, ImplicitAgreementFieldEnum agreementFields) { if (agreementFields == ImplicitAgreementFieldEnum::None && agreementsIdentifier.empty()) { // No agreements to be accepted. return true; } auto detailsInternal = GetCurrentSource(sourceName); if (!detailsInternal) { // Source not found. return false; } return static_cast(agreementFields) == detailsInternal->AcceptedAgreementFields && agreementsIdentifier == detailsInternal->AcceptedAgreementsIdentifier; } void SourceList::SaveAcceptedSourceAgreements(std::string_view sourceName, std::string_view agreementsIdentifier, ImplicitAgreementFieldEnum agreementFields) { if (agreementFields == ImplicitAgreementFieldEnum::None && agreementsIdentifier.empty()) { // No agreements to be accepted. return; } auto detailsInternal = GetCurrentSource(sourceName); if (!detailsInternal) { // No source to update. return; } detailsInternal->AcceptedAgreementFields = static_cast(agreementFields); detailsInternal->AcceptedAgreementsIdentifier = agreementsIdentifier; SaveMetadataInternal(*detailsInternal); } void SourceList::RemoveSettingsStreams() { Stream{ Stream::UserSources }.Remove(); Stream{ Stream::SourcesMetadata }.Remove(); } void SourceList::OverwriteSourceList() { m_sourceList.clear(); for (SourceOrigin origin : { SourceOrigin::GroupPolicy, SourceOrigin::User, SourceOrigin::Default }) { auto forOrigin = GetSourcesByOrigin(origin); for (auto&& source : forOrigin) { auto foundSource = GetSource(source.Name); if (!foundSource) { // Name not already defined, add it m_sourceList.emplace_back(std::move(source)); } else { AICLI_LOG(Repo, Info, << "Source named '" << foundSource->Name << "' is already defined at origin " << ToString(foundSource->Origin) << ". The source from origin " << ToString(origin) << " is dropped."); } } } if (ExperimentalFeature::IsEnabled(ExperimentalFeature::Feature::SourcePriority)) { std::stable_sort(m_sourceList.begin(), m_sourceList.end()); } } void SourceList::OverwriteMetadata() { auto metadata = GetMetadata(); for (auto& metaSource : metadata) { auto source = GetSource(metaSource.Name); if (source) { metaSource.CopyMetadataFieldsTo(*source); } else { m_sourceList.emplace_back(std::move(metaSource)); } } } // Gets the sources from a particular origin. std::vector SourceList::GetSourcesByOrigin(SourceOrigin origin) { std::vector result; switch (origin) { case SourceOrigin::Default: { if (IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore)) { result.emplace_back(GetWellKnownSourceDetailsInternal(WellKnownSource::MicrosoftStore)); } if (IsWellKnownSourceEnabled(WellKnownSource::WinGet)) { result.emplace_back(GetWellKnownSourceDetailsInternal(WellKnownSource::WinGet)); } if (IsWellKnownSourceEnabled(WellKnownSource::WinGetFont)) { result.emplace_back(GetWellKnownSourceDetailsInternal(WellKnownSource::WinGetFont)); } // Since the source is not visible outside, this is added just to have the source in the internal // list for tracking updates. Thus there is no need to check a policy. result.emplace_back(GetWellKnownSourceDetailsInternal(WellKnownSource::DesktopFrameworks)); } break; case SourceOrigin::User: { std::vector userSources = GetSourcesFromSetting( m_userSourcesStream, s_SourcesYaml_Sources, [&](SourceDetailsInternal& details, const std::string& settingValue, const YAML::Node& source) { std::string_view name = m_userSourcesStream.GetName(); if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Name, details.Name)) { return false; } if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Type, details.Type)) { return false; } if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Arg, details.Arg)) { return false; } if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Data, details.Data)) { return false; } if (!TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_IsTombstone, details.IsTombstone)) { return false; } TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Explicit, details.Explicit, false); TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Identifier, details.Identifier, false); TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_IsOverride, details.IsOverride, false); TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_Priority, details.Priority, false); int64_t trustLevelValue; if (TryReadScalar(name, settingValue, source, s_SourcesYaml_Source_TrustLevel, trustLevelValue, false)) { details.TrustLevel = static_cast(trustLevelValue); } return true; }); for (auto& source : userSources) { // Check source against list of allowed sources and drop tombstones for required sources if (!IsUserSourceAllowedByPolicy(source.Name, source.Type, source.Arg, source.IsTombstone)) { AICLI_LOG(Repo, Warning, << "User source " << source.Name << " dropped because of group policy"); continue; } // If this is an override source, we need to get the target of the override and apply the override data on top of it. if (source.IsOverride) { SourceDetailsInternal override; if (!TryFindSourceByOrigin(source.Name, SourceOrigin::Default, override)) { // The default source may be disabled, in which case it may not be returned in the list of default sources. AICLI_LOG(Repo, Warning, << "User source " << source.Name << " is an override for a nonexistent Default Source."); continue; } override.CopyOverrideFieldsFrom(source); override.Origin = SourceOrigin::User; override.IsOverride = true; result.emplace_back(std::move(override)); AICLI_LOG(Repo, Info, << "User source " << source.Name << " is overriding the Default source of the same name."); continue; } result.emplace_back(std::move(source)); } } break; case SourceOrigin::GroupPolicy: { if (GroupPolicies().GetState(TogglePolicy::Policy::AdditionalSources) == PolicyState::Enabled) { AICLI_LOG(Repo, Verbose, << "Additional sources GP is enabled..."); auto additionalSourcesOpt = GroupPolicies().GetValueRef(); if (additionalSourcesOpt.has_value()) { const auto& additionalSources = additionalSourcesOpt->get(); for (const auto& additionalSource : additionalSources) { AICLI_LOG(Repo, Verbose, << "... with configured source " << additionalSource.Name); SourceDetailsInternal details; details.Name = additionalSource.Name; details.Type = additionalSource.Type; details.Arg = additionalSource.Arg; details.Data = additionalSource.Data; details.Identifier = additionalSource.Identifier; details.Origin = SourceOrigin::GroupPolicy; details.Explicit = additionalSource.Explicit; #ifndef AICLI_DISABLE_TEST_HOOKS details.CertificatePinningConfiguration = additionalSource.PinningConfiguration; #endif try { details.TrustLevel = Repository::ConvertToSourceTrustLevelFlag(additionalSource.TrustLevel); } catch (...) { details.TrustLevel = Repository::SourceTrustLevel::None; AICLI_LOG(Repo, Verbose, << "Invalid source trust level from policy. Trust level set to None."); } result.emplace_back(std::move(details)); } } else { AICLI_LOG(Repo, Verbose, << "... but has no values."); } } else { AICLI_LOG(Repo, Verbose, << "Additional sources GP is not enabled."); } } break; default: THROW_HR(E_UNEXPECTED); } for (auto& source : result) { source.Origin = origin; } return result; } bool SourceList::SetSourcesByOrigin(SourceOrigin origin, const std::vector& sources) { switch (origin) { case SourceOrigin::User: return SetSourcesToSettingWithFilter(m_userSourcesStream, SourceOrigin::User, sources); } THROW_HR(E_UNEXPECTED); } std::vector SourceList::GetMetadata() { return GetSourcesFromSetting( m_metadataStream, s_MetadataYaml_Sources, [&](SourceDetailsInternal& details, const std::string& settingValue, const YAML::Node& source) { details.Origin = SourceOrigin::Metadata; std::string_view name = m_metadataStream.GetName(); if (!TryReadScalar(name, settingValue, source, s_MetadataYaml_Source_Name, details.Name)) { return false; } int64_t lastUpdateInEpoch{}; if (!TryReadScalar(name, settingValue, source, s_MetadataYaml_Source_LastUpdate, lastUpdateInEpoch)) { return false; } details.LastUpdateTime = Utility::ConvertUnixEpochToSystemClock(lastUpdateInEpoch); int64_t doNotUpdateBeforeInEpoch{}; if (TryReadScalar(name, settingValue, source, s_MetadataYaml_Source_DoNotUpdateBefore, doNotUpdateBeforeInEpoch, false)) { details.DoNotUpdateBefore = Utility::ConvertUnixEpochToSystemClock(doNotUpdateBeforeInEpoch); } TryReadScalar(name, settingValue, source, s_MetadataYaml_Source_AcceptedAgreementsIdentifier, details.AcceptedAgreementsIdentifier, false); TryReadScalar(name, settingValue, source, s_MetadataYaml_Source_AcceptedAgreementFields, details.AcceptedAgreementFields, false); return true; }); } bool SourceList::SetMetadata(const std::vector& sources) { YAML::Emitter out; out << YAML::BeginMap; out << YAML::Key << s_MetadataYaml_Sources; out << YAML::BeginSeq; for (const auto& details : sources) { out << YAML::BeginMap; out << YAML::Key << s_MetadataYaml_Source_Name << YAML::Value << details.Name; out << YAML::Key << s_MetadataYaml_Source_LastUpdate << YAML::Value << Utility::ConvertSystemClockToUnixEpoch(details.LastUpdateTime); out << YAML::Key << s_MetadataYaml_Source_DoNotUpdateBefore << YAML::Value << Utility::ConvertSystemClockToUnixEpoch(details.DoNotUpdateBefore); out << YAML::Key << s_MetadataYaml_Source_AcceptedAgreementsIdentifier << YAML::Value << details.AcceptedAgreementsIdentifier; out << YAML::Key << s_MetadataYaml_Source_AcceptedAgreementFields << YAML::Value << details.AcceptedAgreementFields; out << YAML::EndMap; } out << YAML::EndSeq; out << YAML::EndMap; return m_metadataStream.Set(out.str()); } void SourceList::SaveMetadataInternal(const SourceDetailsInternal& detailsRef, bool remove) { // Copy the incoming details because we might overwrite the metadata // when reloading the source details from settings. SourceDetailsInternal details = detailsRef; bool metadataSet = false; for (size_t i = 0; !metadataSet && i < 10; ++i) { metadataSet = SetMetadata(m_sourceList); if (!metadataSet) { OverwriteMetadata(); auto target = FindSource(details.Name, true); if (target == m_sourceList.end()) { // Didn't find the metadata, so we consider this a success return; } if (remove) { // The remove will have removed the source but not the metadata. // Remove it again here. m_sourceList.erase(target); } else { // Update the freshly read metadata with the update that was requested. details.CopyMetadataFieldsTo(*target); } } } THROW_HR_IF_MSG(E_UNEXPECTED, !metadataSet, "Too many attempts at SetMetadata"); } } ================================================ FILE: src/AppInstallerRepositoryCore/SourceList.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ISource.h" #include namespace AppInstaller::Repository { // Built-in values for default sources std::string_view GetWellKnownSourceName(WellKnownSource source); std::string_view GetWellKnownSourceArg(WellKnownSource source); std::string_view GetWellKnownSourceIdentifier(WellKnownSource source); std::optional CheckForWellKnownSourceMatch(std::string_view name, std::string_view arg, std::string_view type); // SourceDetails with additional data used internally. struct SourceDetailsInternal : public SourceDetails { SourceDetailsInternal() = default; SourceDetailsInternal(const SourceDetails& details) : SourceDetails(details) {} // Copies the metadata fields to this target. void CopyMetadataFieldsTo(SourceDetailsInternal& target); // Copies the metadata fields from this source. This only include partial metadata. void CopyMetadataFieldsFrom(const SourceDetails& source); // Copies the overridden fields from the target source to this source. This is only the supported override fields. void CopyOverrideFieldsFrom(const SourceDetails& overrideSource); // Sorts by Priority with higher values coming first in the order. bool operator<(const SourceDetailsInternal& other) const; // If true, this is a tombstone, marking the deletion of a source at a lower priority origin. bool IsTombstone = false; // If true, this is an override of a source at a lower priority. An override source only defines // changes on top of the lower priority source, otherwise uses the same as the lower priority source. bool IsOverride = false; // If false, this is not visible in GetCurrentSource or GetAllSources, it's only available when explicitly requested. bool IsVisible = true; // Accepted agreements info. std::string AcceptedAgreementsIdentifier; int AcceptedAgreementFields = 0; }; // Gets the internal details for a well known source. SourceDetailsInternal GetWellKnownSourceDetailsInternal(WellKnownSource source); // Struct containing internal implementation of the source list. // This contains all source data; including tombstoned sources. struct SourceList { SourceList(); // Get a list of current sources references which can be used to update the contents in place. // e.g. update the LastTimeUpdated value of sources. std::vector> GetCurrentSourceRefs(); // Current source means source that's not in tombstone SourceDetailsInternal* GetCurrentSource(std::string_view name); // Source includes ones in tombstone SourceDetailsInternal* GetSource(std::string_view name); // Add/remove/edit a current source void AddSource(const SourceDetailsInternal& details); void RemoveSource(const SourceDetailsInternal& details); void EditSource(const SourceDetailsInternal& details); // Save source metadata; the particular source with the metadata update is given. // The given source must already be in the internal source list. void SaveMetadata(const SourceDetailsInternal& details); // Checks the source agreements and returns if agreements are satisfied. bool CheckSourceAgreements(std::string_view sourceName, std::string_view agreementsIdentifier, ImplicitAgreementFieldEnum agreementFields); // Save agreements information. void SaveAcceptedSourceAgreements(std::string_view sourceName, std::string_view agreementsIdentifier, ImplicitAgreementFieldEnum agreementFields); // Removes all settings streams associated with the source list. // Implements `winget source reset --force`. static void RemoveSettingsStreams(); private: // Overwrites the source list with all sources. void OverwriteSourceList(); // Overwrites the source list with the current metadata. void OverwriteMetadata(); // calls std::find_if and return the iterator. auto FindSource(std::string_view name, bool includeHidden = false); // Tries to find a named source from the specified origin. [[nodiscard]] bool TryFindSourceByOrigin(std::string_view name, SourceOrigin origin, SourceDetailsInternal& targetSourceOut, bool includeHidden = false); std::vector GetSourcesByOrigin(SourceOrigin origin); // Does *NOT* set metadata; call SaveMetadataInternal afterward. [[nodiscard]] bool SetSourcesByOrigin(SourceOrigin origin, const std::vector& sources); std::vector GetMetadata(); [[nodiscard]] bool SetMetadata(const std::vector& sources); // Save source metadata; the particular source with the metadata update is given. // If remove is true, the given source is being removed. void SaveMetadataInternal(const SourceDetailsInternal& details, bool remove = false); std::vector m_sourceList; Settings::Stream m_userSourcesStream; Settings::Stream m_metadataStream; }; } ================================================ FILE: src/AppInstallerRepositoryCore/SourcePolicy.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SourcePolicy.h" #include "Microsoft/PreIndexedPackageSourceFactory.h" #include "Rest/RestSourceFactory.h" using namespace AppInstaller::Settings; namespace AppInstaller::Repository { namespace { // Checks whether a default source is enabled with the current settings. // onlyExplicit determines whether we consider the not-configured state to be enabled or not. bool IsDefaultSourceEnabled(WellKnownSource sourceToLog, ExperimentalFeature::Feature feature, bool onlyExplicit, TogglePolicy::Policy policy) { if (!ExperimentalFeature::IsEnabled(feature)) { // No need to log here return false; } if (onlyExplicit) { // No need to log here return GroupPolicies().GetState(policy) == PolicyState::Enabled; } if (!GroupPolicies().IsEnabled(policy)) { AICLI_LOG(Repo, Info, << "The default source " << GetWellKnownSourceName(sourceToLog) << " is disabled due to Group Policy"); return false; } return true; } template std::optional FindSourceInPolicy(std::string_view name, std::string_view type, std::string_view arg) { auto sourcesOpt = GroupPolicies().GetValueRef

(); if (!sourcesOpt.has_value()) { return std::nullopt; } const auto& sources = sourcesOpt->get(); auto source = std::find_if( sources.begin(), sources.end(), [&](const SourceFromPolicy& policySource) { return Utility::ICUCaseInsensitiveEquals(name, policySource.Name) && Utility::ICUCaseInsensitiveEquals(type, policySource.Type) && arg == policySource.Arg; }); if (source == sources.end()) { return std::nullopt; } return *source; } template bool IsSourceInPolicy(std::string_view name, std::string_view type, std::string_view arg) { return FindSourceInPolicy

(name, type, arg).has_value(); } } // Checks whether the Group Policy allows this user source. // If it does it returns None, otherwise it returns which policy is blocking it. // Note that this applies to user sources that are being added as well as user sources // that already existed when the Group Policy came into effect. TogglePolicy::Policy GetPolicyBlockingUserSource(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone) { // Reasons for not allowing: // 1. The source is a tombstone for default source that is explicitly enabled // 2. The source is a default source that is disabled // 3. The source has the same name as a default source that is explicitly enabled (to prevent shadowing) // 4. Allowed sources are disabled, blocking all user sources // 5. There is an explicit list of allowed sources and this source is not in it // // We don't need to check sources added by policy as those have higher priority. // // Use the name and arg to match sources as we don't have the identifier before adding. // Case 1: // The source is a tombstone and we need the policy to be explicitly enabled. if (isTombstone) { if (name == GetWellKnownSourceName(WellKnownSource::WinGet) && IsWellKnownSourceEnabled(WellKnownSource::WinGet, true)) { return TogglePolicy::Policy::DefaultSource; } if (name == GetWellKnownSourceName(WellKnownSource::MicrosoftStore) && IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore, true)) { return TogglePolicy::Policy::MSStoreSource; } if (name == GetWellKnownSourceName(WellKnownSource::WinGetFont) && IsWellKnownSourceEnabled(WellKnownSource::WinGetFont, true)) { return TogglePolicy::Policy::FontSource; } // Any other tombstone is allowed return TogglePolicy::Policy::None; } // Case 2: // - The source is not a tombstone and we don't need the policy to be explicitly enabled. // - Check only against the source argument and type as the user source may have a different name. // - Do a case-insensitive check as the domain portion of the URL is case-insensitive, // and we don't need case sensitivity for the rest as we control the domain. if (Utility::CaseInsensitiveEquals(arg, GetWellKnownSourceArg(WellKnownSource::WinGet)) && Utility::CaseInsensitiveEquals(type, Microsoft::PreIndexedPackageSourceFactory::Type())) { return IsWellKnownSourceEnabled(WellKnownSource::WinGet) ? TogglePolicy::Policy::None : TogglePolicy::Policy::DefaultSource; } if (Utility::CaseInsensitiveEquals(arg, GetWellKnownSourceArg(WellKnownSource::MicrosoftStore)) && Utility::CaseInsensitiveEquals(type, Rest::RestSourceFactory::Type())) { return IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore) ? TogglePolicy::Policy::None : TogglePolicy::Policy::MSStoreSource; } if (Utility::CaseInsensitiveEquals(arg, GetWellKnownSourceArg(WellKnownSource::WinGetFont)) && Utility::CaseInsensitiveEquals(type, Microsoft::PreIndexedPackageSourceFactory::Type())) { return IsWellKnownSourceEnabled(WellKnownSource::WinGetFont) ? TogglePolicy::Policy::None : TogglePolicy::Policy::FontSource; } // Case 3: // If the source has the same name as a default source, it is shadowing with a different argument // (as it didn't match above). We only care if Group Policy requires the default source. if (name == GetWellKnownSourceName(WellKnownSource::WinGet) && IsWellKnownSourceEnabled(WellKnownSource::WinGet, true)) { AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows the default source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); return TogglePolicy::Policy::DefaultSource; } if (name == GetWellKnownSourceName(WellKnownSource::MicrosoftStore) && IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore, true)) { AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default MS Store source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); return TogglePolicy::Policy::MSStoreSource; } if (name == GetWellKnownSourceName(WellKnownSource::WinGetFont) && IsWellKnownSourceEnabled(WellKnownSource::WinGetFont, true)) { AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default font source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); return TogglePolicy::Policy::FontSource; } // Case 4: // The guard in the source add command should already block adding. // This check drops existing user sources. auto allowedSourcesPolicy = GroupPolicies().GetState(TogglePolicy::Policy::AllowedSources); if (allowedSourcesPolicy == PolicyState::Disabled) { AICLI_LOG(Repo, Warning, << "User sources are disabled by Group Policy"); return TogglePolicy::Policy::AllowedSources; } // Case 5: if (allowedSourcesPolicy == PolicyState::Enabled) { if (!IsSourceInPolicy(name, type, arg)) { AICLI_LOG(Repo, Warning, << "Source is not in the Group Policy allowed list. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); return TogglePolicy::Policy::AllowedSources; } } return TogglePolicy::Policy::None; } bool IsUserSourceAllowedByPolicy(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone) { return GetPolicyBlockingUserSource(name, type, arg, isTombstone) == TogglePolicy::Policy::None; } bool IsWellKnownSourceEnabled(WellKnownSource source, bool onlyExplicit) { switch (source) { case AppInstaller::Repository::WellKnownSource::WinGet: return IsDefaultSourceEnabled(source, ExperimentalFeature::Feature::None, onlyExplicit, TogglePolicy::Policy::DefaultSource); case AppInstaller::Repository::WellKnownSource::MicrosoftStore: return IsDefaultSourceEnabled(source, ExperimentalFeature::Feature::None, onlyExplicit, TogglePolicy::Policy::MSStoreSource); case AppInstaller::Repository::WellKnownSource::WinGetFont: return IsDefaultSourceEnabled(source, ExperimentalFeature::Feature::None, onlyExplicit, TogglePolicy::Policy::FontSource); case AppInstaller::Repository::WellKnownSource::DesktopFrameworks: // No corresponding policy available for this source. return true; } return false; } void EnsureSourceIsRemovable(const SourceDetailsInternal& source) { // Block removing sources added by Group Policy if (source.Origin == SourceOrigin::GroupPolicy) { AICLI_LOG(Repo, Error, << "Cannot remove source added by Group Policy"); throw GroupPolicyException(TogglePolicy::Policy::AdditionalSources); } // Block removing default sources required by Group Policy. if (source.Origin == SourceOrigin::Default) { if (GroupPolicies().GetState(TogglePolicy::Policy::DefaultSource) == PolicyState::Enabled && source.Identifier == GetWellKnownSourceIdentifier(WellKnownSource::WinGet)) { throw GroupPolicyException(TogglePolicy::Policy::DefaultSource); } if (GroupPolicies().GetState(TogglePolicy::Policy::MSStoreSource) == PolicyState::Enabled && source.Identifier == GetWellKnownSourceIdentifier(WellKnownSource::MicrosoftStore)) { throw GroupPolicyException(TogglePolicy::Policy::MSStoreSource); } if (GroupPolicies().GetState(TogglePolicy::Policy::FontSource) == PolicyState::Enabled && source.Identifier == GetWellKnownSourceIdentifier(WellKnownSource::WinGetFont)) { throw GroupPolicyException(TogglePolicy::Policy::FontSource); } } } } ================================================ FILE: src/AppInstallerRepositoryCore/SourcePolicy.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "SourceList.h" #include #include namespace AppInstaller::Repository { // Checks whether the Group Policy allows this user source. // If it does it returns None, otherwise it returns which policy is blocking it. // Note that this applies to user sources that are being added as well as user sources // that already existed when the Group Policy came into effect. Settings::TogglePolicy::Policy GetPolicyBlockingUserSource(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone); // Helper that converts the result of GetPolicyBlockingUserSource into a bool. bool IsUserSourceAllowedByPolicy(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone); // Determines if a well known source is enabled; if onlyExplicit is true, it must be explicitly enabled by group policy. bool IsWellKnownSourceEnabled(WellKnownSource source, bool onlyExplicit = false); // Checks that the specified source is removable per policy; throwing if it is not. void EnsureSourceIsRemovable(const SourceDetailsInternal& source); } ================================================ FILE: src/AppInstallerRepositoryCore/SourceUpdateChecks.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SourceUpdateChecks.h" #include "ISource.h" #include using namespace std::chrono_literals; namespace AppInstaller::Repository { bool ShouldUpdateBeforeOpen(ISourceReference* sourceReference, const std::optional& requestedUpdateInterval) { const SourceDetails& details = sourceReference->GetDetails(); // Always respect this value to prevent server overloading if (IsBeforeDoNotUpdateBeforeTime(details)) { return false; } // Allow the source reference to decide beyond this return sourceReference->ShouldUpdateBeforeOpen(requestedUpdateInterval); } bool IsBeforeDoNotUpdateBeforeTime(const SourceDetails& details) { if (std::chrono::system_clock::now() < details.DoNotUpdateBefore) { AICLI_LOG(Repo, Info, << "Background update for `" << details.Name << "` is suppressed until: " << details.DoNotUpdateBefore); return true; } else { return false; } } bool IsAfterUpdateCheckTime(const SourceDetails& details, std::optional requestedUpdateInterval) { return IsAfterUpdateCheckTime(details.Name, details.LastUpdateTime, requestedUpdateInterval); } bool IsAfterUpdateCheckTime(std::string_view name, std::chrono::system_clock::time_point lastUpdateTime, std::optional requestedUpdateInterval) { constexpr static TimeSpan s_ZeroMins = 0min; TimeSpan autoUpdateTime; if (requestedUpdateInterval) { autoUpdateTime = requestedUpdateInterval.value(); } else { autoUpdateTime = Settings::User().Get(); } // A value of zero means no auto update, to get update the source run `winget update` if (autoUpdateTime != s_ZeroMins) { auto timeSinceLastUpdate = std::chrono::system_clock::now() - lastUpdateTime; if (timeSinceLastUpdate > autoUpdateTime) { AICLI_LOG(Repo, Info, << "Source `" << name << "` after auto update time [" << (requestedUpdateInterval ? "(override) " : "") << std::chrono::duration_cast(autoUpdateTime).count() << " mins]; it has been at least " << std::chrono::duration_cast(timeSinceLastUpdate).count() << " mins"); return true; } } return false; } } ================================================ FILE: src/AppInstallerRepositoryCore/SourceUpdateChecks.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Public/winget/RepositorySource.h" #include namespace AppInstaller::Repository { // Determines if the given source should update before opening. bool ShouldUpdateBeforeOpen(ISourceReference* sourceReference, const std::optional& requestedUpdateInterval); // Determines if the current time is before a previously stored "do note update before" time. bool IsBeforeDoNotUpdateBeforeTime(const SourceDetails& details); // Determines if the given details and desired update interval indicate an update check should occur. bool IsAfterUpdateCheckTime(const SourceDetails& details, std::optional requestedUpdateInterval); // Determines if the given details and desired update interval indicate an update check should occur. bool IsAfterUpdateCheckTime(std::string_view name, std::chrono::system_clock::time_point lastUpdateTime, std::optional requestedUpdateInterval); } ================================================ FILE: src/AppInstallerRepositoryCore/packages.config ================================================  ================================================ FILE: src/AppInstallerRepositoryCore/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/AppInstallerRepositoryCore/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include #include #include #include #include #include #pragma warning( push ) #pragma warning ( disable : 6001 6340 6387 6388 26451 26495 28196 ) #include #include #include #include #include #include #pragma warning( pop ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma warning( push ) #pragma warning ( disable : 26495 26439 ) #include #include #include #pragma warning( pop ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ================================================ FILE: src/AppInstallerSharedLib/AppInstallerLogging.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerDateTime.h" #include "Public/winget/SharedThreadGlobals.h" namespace AppInstaller::Logging { std::string_view GetChannelName(Channel channel) { switch(channel) { case Channel::Fail: return "FAIL"; case Channel::CLI: return "CLI"; case Channel::SQL: return "SQL"; case Channel::Repo: return "REPO"; case Channel::YAML: return "YAML"; case Channel::Core: return "CORE"; case Channel::Test: return "TEST"; case Channel::Config: return "CONF"; case Channel::Workflow: return "WORK"; default: return "NONE"; } } Channel GetChannelFromName(std::string_view channel) { std::string lowerChannel = Utility::ToLower(channel); if (lowerChannel == "fail") { return Channel::Fail; } else if (lowerChannel == "cli") { return Channel::CLI; } else if (lowerChannel == "sql") { return Channel::SQL; } else if (lowerChannel == "repo") { return Channel::Repo; } else if (lowerChannel == "yaml") { return Channel::YAML; } else if (lowerChannel == "core") { return Channel::Core; } else if (lowerChannel == "test") { return Channel::Test; } else if (lowerChannel == "conf" || lowerChannel == "config") { return Channel::Config; } else if (lowerChannel == "workflow") { return Channel::Workflow; } else if (lowerChannel == "default" || lowerChannel == "defaults") { return Channel::Defaults; } else if (lowerChannel == "all") { return Channel::All; } return Channel::None; } size_t GetMaxChannelNameLength() { return 4; } void DiagnosticLogger::AddLogger(std::unique_ptr&& logger) { m_loggers.emplace_back(std::move(logger)); } bool DiagnosticLogger::ContainsLogger(const std::string& name) { for (auto i = m_loggers.begin(); i != m_loggers.end(); ++i) { if ((*i)->GetName() == name) { return true; } } return false; } std::unique_ptr DiagnosticLogger::RemoveLogger(const std::string& name) { std::unique_ptr result; for (auto i = m_loggers.begin(); i != m_loggers.end(); ++i) { if ((*i)->GetName() == name) { result = std::move(*i); m_loggers.erase(i); break; } } return result; } void DiagnosticLogger::RemoveAllLoggers() { m_loggers.clear(); } void DiagnosticLogger::EnableChannel(Channel channel) { WI_SetAllFlags(m_enabledChannels, channel); } void DiagnosticLogger::SetEnabledChannels(Channel channel) { m_enabledChannels = channel; } void DiagnosticLogger::DisableChannel(Channel channel) { WI_ClearAllFlags(m_enabledChannels, channel); } void DiagnosticLogger::SetLevel(Level level) { m_enabledLevel = level; } Level DiagnosticLogger::GetLevel() const { return m_enabledLevel; } bool DiagnosticLogger::IsEnabled(Channel channel, Level level) const { return (!m_loggers.empty() && WI_IsAnyFlagSet(m_enabledChannels, channel) && (ToIntegral(level) >= ToIntegral(m_enabledLevel))); } void DiagnosticLogger::Write(Channel channel, Level level, std::string_view message) { THROW_HR_IF_MSG(E_INVALIDARG, channel == Channel::All, "Cannot write to all channels"); if (IsEnabled(channel, level)) { for (auto& logger : m_loggers) { logger->Write(channel, level, message); } } } void DiagnosticLogger::WriteDirect(Channel channel, Level level, std::string_view message) { THROW_HR_IF_MSG(E_INVALIDARG, channel == Channel::All, "Cannot write to all channels"); if (IsEnabled(channel, level)) { for (auto& logger : m_loggers) { logger->WriteDirect(channel, level, message); } } } void DiagnosticLogger::SetTag(Tag tag) { for (auto& logger : m_loggers) { logger->SetTag(tag); } } DiagnosticLogger& Log() { ThreadLocalStorage::ThreadGlobals* pThreadGlobals = ThreadLocalStorage::ThreadGlobals::GetForCurrentThread(); if (pThreadGlobals) { return pThreadGlobals->GetDiagnosticLogger(); } else { static DiagnosticLogger processGlobalLogger; return processGlobalLogger; } } std::ostream& SetHRFormat(std::ostream& out) { return out << std::hex << std::setw(8) << std::setfill('0'); } } namespace std { std::ostream& operator<<(std::ostream& out, const std::chrono::system_clock::time_point& time) { AppInstaller::Utility::OutputTimePoint(out, time); return out; } std::ostream& operator<<(std::ostream& out, const GUID& guid) { wchar_t buffer[256]; if (StringFromGUID2(guid, buffer, ARRAYSIZE(buffer))) { out << AppInstaller::Utility::ConvertToUTF8(buffer); } else { out << "error"; } return out; } } ================================================ FILE: src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj ================================================ true true true 15.0 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51} Win32Proj 10.0.26100.0 10.0.17763.0 true Debug ARM64 Debug Win32 Fuzzing x64 Fuzzing Win32 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Debug x64 Release x64 StaticLibrary true true false true false false true false false false false true Spectre Spectre Spectre Spectre Spectre Spectre true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING Disabled _NO_ASYNCRTIMP;_DEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true true true true true true false false false false Windows Windows _NO_ASYNCRTIMP;WIN32;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true true true false false Windows MaxSpeed true true _NO_ASYNCRTIMP;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true true true true true true false false false false false false true true false Windows Windows Windows MaxSpeed true true _NO_ASYNCRTIMP;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true true true true true true false false false false false false MultiThreaded MultiThreaded MultiThreaded true true false Windows Windows Windows MaxSpeed true true _NO_ASYNCRTIMP;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD;WINGET_DISABLE_FOR_FUZZING;_DISABLE_VECTOR_ANNOTATION;_DISABLE_STRING_ANNOTATION $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true stdcpp17 MultiThreaded %(AdditionalOptions) /fsanitize=address /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div false true true false Windows NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing Create This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {5cdf3fa3-e657-4d84-81bb-f740aa476143} {41035fd6-dc74-4464-b9b1-4ffe95d6789c} {3a5b2424-6c80-4edc-85bc-f371f2e93a33} {d5b3a812-cc25-4826-b380-0488cd0944e1} {2403870a-5bb9-461f-8558-877c23ec487b} Header Files Public Public Public Public Public Header Files Public\winget Public\winget Public\winget Public\winget Public\winget Public Public Public\Telemetry - Do Not Modify Public\Telemetry - Do Not Modify Public\winget Public\winget Header Files Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget ICU Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Public\winget Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files SQLite SQLite SQLite SQLite SQLite ICU SQLite Source Files Source Files Source Files SQLite Source Files ================================================ FILE: src/AppInstallerSharedLib/AppInstallerStrings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerSHA256.h" namespace AppInstaller::Utility { // Same as std::isspace(char) #define AICLI_SPACE_CHARS " \f\n\r\t\v"sv using namespace std::string_view_literals; constexpr std::string_view s_SpaceChars = AICLI_SPACE_CHARS; constexpr std::wstring_view s_WideSpaceChars = L"" AICLI_SPACE_CHARS; namespace { // Contains the ICU objects necessary to do break iteration. struct ICUBreakIterator { ICUBreakIterator(std::string_view input, UBreakIteratorType type) { UErrorCode err = U_ZERO_ERROR; m_text.reset(utext_openUTF8(nullptr, input.data(), wil::safe_cast(input.length()), &err)); if (U_FAILURE(err)) { AICLI_LOG(Core, Error, << "utext_openUTF8 returned " << err); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_BREAK_ITERATOR_ERROR); } m_brk.reset(ubrk_open(type, nullptr, nullptr, 0, &err)); if (U_FAILURE(err)) { AICLI_LOG(Core, Error, << "ubrk_open returned " << err); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_BREAK_ITERATOR_ERROR); } ubrk_setUText(m_brk.get(), m_text.get(), &err); if (U_FAILURE(err)) { AICLI_LOG(Core, Error, << "ubrk_setUText returned " << err); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_BREAK_ITERATOR_ERROR); } int32_t i = ubrk_first(m_brk.get()); if (i != 0) { AICLI_LOG(Core, Error, << "ubrk_first returned " << i); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_BREAK_ITERATOR_ERROR); } } // Gets the current break value; the byte offset or UBRK_DONE. int32_t CurrentBreak() const { return m_currentBrk; } // Gets the current byte offset, throwing if the value is UBRK_DONE or negative. size_t CurrentOffset() const { THROW_HR_IF(E_NOT_VALID_STATE, m_currentBrk < 0); return static_cast(m_currentBrk); } // Returns the byte offset of the next break in the string int32_t Next() { m_currentBrk = ubrk_next(m_brk.get()); return m_currentBrk; } // Returns the byte offset of the next count'th break in the string int32_t Advance(size_t count) { for (size_t i = 0; i < count && m_currentBrk != UBRK_DONE; ++i) { Next(); } return m_currentBrk; } // Returns code point of the character at m_currentBrk, or U_SENTINEL if m_currentBrk points to the end. UChar32 CurrentCodePoint() { return utext_char32At(m_text.get(), m_currentBrk); } // Returns the status from the break rule that determined the most recently break position. int32_t CurrentRuleStatus() { return ubrk_getRuleStatus(m_brk.get()); } private: wil::unique_any m_text; wil::unique_any m_brk; int32_t m_currentBrk = 0; }; template StringType& TrimTemplate(StringType& input, std::basic_string_view spaceChars) { if (!input.empty()) { size_t begin = input.find_first_not_of(spaceChars); size_t end = input.find_last_not_of(spaceChars); if (begin == StringType::npos || end == StringType::npos) { input = {}; } else if (begin != 0 || end != input.length() - 1) { input = input.substr(begin, (end - begin) + 1); } } return input; } template StringType TrimCopyTemplate(const StringType& input) { StringType result = input; Utility::Trim(result); return result; } template StringType TrimMoveTemplate(StringType&& input) { StringType result = std::move(input); Utility::Trim(result); return result; } template std::vector SplitTemplate(const StringType& input, typename StringType::value_type separator, bool trim) { std::vector result; size_t startIndex = 0; size_t endIndex = 0; while ((endIndex = input.find(separator, startIndex)) != StringType::npos) { StringType substring = input.substr(startIndex, endIndex - startIndex); if (trim) { Utility::Trim(substring); } result.emplace_back(std::move(substring)); startIndex = endIndex + 1; } result.emplace_back(trim ? Utility::Trim(input.substr(startIndex)) : input.substr(startIndex)); return result; } } bool CaseInsensitiveEquals(std::string_view a, std::string_view b) { return ToLower(a) == ToLower(b); } bool CaseInsensitiveEquals(std::wstring_view a, std::wstring_view b) { return ToLower(a) == ToLower(b); } bool CaseInsensitiveContains(const std::vector& a, std::string_view b) { auto B = ToLower(b); return std::any_of(a.begin(), a.end(), [&](const std::string_view& s) { return ToLower(s) == B; }); } bool StartsWith(std::wstring_view a, std::wstring_view b) { return a.length() >= b.length() && a.substr(0, b.length()) == b; } bool CaseInsensitiveStartsWith(std::string_view a, std::string_view b) { return a.length() >= b.length() && CaseInsensitiveEquals(a.substr(0, b.length()), b); } bool CaseInsensitiveStartsWith(std::wstring_view a, std::wstring_view b) { return a.length() >= b.length() && CaseInsensitiveEquals(a.substr(0, b.length()), b); } bool CaseInsensitiveContainsSubstring(std::string_view a, std::string_view b) { auto it = std::search( a.begin(), a.end(), b.begin(), b.end(), [](char ch1, char ch2) { return std::tolower(ch1) == std::tolower(ch2); } ); return (it != a.end()); } bool ContainsSubstring(std::string_view a, std::string_view b) { auto it = std::search( a.begin(), a.end(), b.begin(), b.end() ); return (it != a.end()); } bool ICUCaseInsensitiveEquals(std::string_view a, std::string_view b) { return FoldCase(a) == FoldCase(b); } bool ICUCaseInsensitiveStartsWith(std::string_view a, std::string_view b) { return a.length() >= b.length() && ICUCaseInsensitiveEquals(a.substr(0, b.length()), b); } std::string ConvertToUTF8(std::wstring_view input) { if (input.empty()) { return {}; } int utf8ByteCount = WideCharToMultiByte(CP_UTF8, 0, input.data(), wil::safe_cast(input.length()), nullptr, 0, nullptr, nullptr); THROW_LAST_ERROR_IF(utf8ByteCount == 0); // Since the string view should not contain the null char, the result won't either. // This allows us to use the resulting size value directly in the string constructor. std::string result(wil::safe_cast(utf8ByteCount), '\0'); int utf8BytesWritten = WideCharToMultiByte(CP_UTF8, 0, input.data(), wil::safe_cast(input.length()), &result[0], wil::safe_cast(result.size()), nullptr, nullptr); FAIL_FAST_HR_IF(E_UNEXPECTED, utf8ByteCount != utf8BytesWritten); return result; } std::wstring ConvertToUTF16(std::string_view input, UINT codePage) { if (input.empty()) { return {}; } int utf16CharCount = MultiByteToWideChar(codePage, 0, input.data(), wil::safe_cast(input.length()), nullptr, 0); THROW_LAST_ERROR_IF(utf16CharCount == 0); // Since the string view should not contain the null char, the result won't either. // This allows us to use the resulting size value directly in the string constructor. std::wstring result(wil::safe_cast(utf16CharCount), L'\0'); int utf16CharsWritten = MultiByteToWideChar(codePage, 0, input.data(), wil::safe_cast(input.length()), &result[0], wil::safe_cast(result.size())); FAIL_FAST_HR_IF(E_UNEXPECTED, utf16CharCount != utf16CharsWritten); return result; } std::optional TryConvertToUTF16(std::string_view input, UINT codePage) { if (input.empty()) { return std::wstring{}; } int utf16CharCount = MultiByteToWideChar(codePage, 0, input.data(), wil::safe_cast(input.length()), nullptr, 0); if (utf16CharCount == 0) { return {}; } // Since the string view should not contain the null char, the result won't either. // This allows us to use the resulting size value directly in the string constructor. std::wstring result(wil::safe_cast(utf16CharCount), L'\0'); int utf16CharsWritten = MultiByteToWideChar(codePage, 0, input.data(), wil::safe_cast(input.length()), &result[0], wil::safe_cast(result.size())); if (utf16CharCount != utf16CharsWritten) { return {}; } return std::optional{ result }; } std::u32string ConvertToUTF32(std::string_view input) { if (input.empty()) { return {}; } UErrorCode errorCode = UErrorCode::U_ZERO_ERROR; auto utf32ByteCount= ucnv_convert("UTF-32", "UTF-8", nullptr, 0, input.data(), static_cast(input.size()), &errorCode); if (errorCode != U_BUFFER_OVERFLOW_ERROR) { AICLI_LOG(Core, Error, << "ucnv_convert returned " << errorCode); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_CONVERSION_ERROR); } FAIL_FAST_HR_IF(E_UNEXPECTED, utf32ByteCount % sizeof(char32_t) != 0); auto utf32CharCount = utf32ByteCount / sizeof(char32_t); std::u32string result(utf32CharCount, U'\0'); errorCode = UErrorCode::U_ZERO_ERROR; auto utf32BytesWritten = ucnv_convert("UTF-32", "UTF-8", (char*)(result.data()), utf32ByteCount, input.data(), static_cast(input.size()), &errorCode); // The size we pass to ucnv_convert is not enough for it to put in the null terminator, // which wouldn't work anyways as it puts a single byte. if (errorCode != U_STRING_NOT_TERMINATED_WARNING) { AICLI_LOG(Core, Error, << "ucnv_convert returned " << errorCode); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_CONVERSION_ERROR); } FAIL_FAST_HR_IF(E_UNEXPECTED, utf32ByteCount != utf32BytesWritten); return result; } size_t UTF8Length(std::string_view input) { ICUBreakIterator itr{ input, UBRK_CHARACTER }; size_t numGraphemeClusters = 0; while (itr.Next() != UBRK_DONE) { numGraphemeClusters++; } return numGraphemeClusters; } size_t UTF8ColumnWidth(const NormalizedUTF8& input) { ICUBreakIterator itr{ input, UBRK_CHARACTER }; size_t columnWidth = 0; UChar32 currentCP = 0; currentCP = itr.CurrentCodePoint(); while (itr.Next() != UBRK_DONE && currentCP != U_SENTINEL) { int32_t width = u_getIntPropertyValue(currentCP, UCHAR_EAST_ASIAN_WIDTH); columnWidth += width == U_EA_FULLWIDTH || width == U_EA_WIDE ? 2 : 1; currentCP = itr.CurrentCodePoint(); } return columnWidth; } std::string_view UTF8Substring(std::string_view input, size_t offset, size_t count) { ICUBreakIterator itr{ input, UBRK_CHARACTER }; // Offset was past end, throw just like std::string::substr if (itr.Advance(offset) == UBRK_DONE) { throw std::out_of_range("UTF8Substring: offset past end of input"); } size_t utf8Offset = itr.CurrentOffset(); size_t utf8Count = 0; // Count past end, convert to npos to get all of string if (itr.Advance(count) == UBRK_DONE) { utf8Count = std::string_view::npos; } else { utf8Count = itr.CurrentOffset() - utf8Offset; } return input.substr(utf8Offset, utf8Count); } std::string UTF8TrimRightToColumnWidth(const NormalizedUTF8& input, size_t expectedWidth, size_t& actualWidth) { ICUBreakIterator itr{ input, UBRK_CHARACTER }; size_t columnWidth = 0; UChar32 currentCP = 0; int32_t currentBrk = 0; int32_t nextBrk = 0; currentCP = itr.CurrentCodePoint(); currentBrk = itr.CurrentBreak(); nextBrk = itr.Next(); while (nextBrk != UBRK_DONE && currentCP != U_SENTINEL) { int32_t width = u_getIntPropertyValue(currentCP, UCHAR_EAST_ASIAN_WIDTH); int charWidth = width == U_EA_FULLWIDTH || width == U_EA_WIDE ? 2 : 1; columnWidth += charWidth; if (columnWidth > expectedWidth) { columnWidth -= charWidth; break; } currentCP = itr.CurrentCodePoint(); currentBrk = nextBrk; nextBrk = itr.Next(); } actualWidth = columnWidth; return input.substr(0, currentBrk); } std::string Normalize(std::string_view input, NORM_FORM form) { if (input.empty()) { return {}; } return ConvertToUTF8(Normalize(ConvertToUTF16(input), form)); } std::wstring Normalize(std::wstring_view input, NORM_FORM form) { if (input.empty()) { return {}; } std::wstring result; int cchEstimate = NormalizeString(form, input.data(), static_cast(input.length()), NULL, 0); for (;;) { result.resize(cchEstimate); cchEstimate = NormalizeString(form, input.data(), static_cast(input.length()), &result[0], cchEstimate); if (cchEstimate > 0) { result.resize(cchEstimate); return result; } else { DWORD dwError = GetLastError(); THROW_LAST_ERROR_IF(dwError != ERROR_INSUFFICIENT_BUFFER); // New guess is negative of the return value. cchEstimate = -cchEstimate; THROW_HR_IF_MSG(E_UNEXPECTED, static_cast(cchEstimate) <= result.size(), "New estimate should never be less than previous value"); } } } void ReplaceEmbeddedNullCharacters(std::string& s, char c) { for (size_t i = 0; i < s.length(); ++i) { if (s[i] == '\0') { s[i] = c; } } } std::string ToLower(std::string_view in) { std::string result(in); std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return result; } std::wstring ToLower(std::wstring_view in) { std::wstring result(in); std::transform(result.begin(), result.end(), result.begin(), [](unsigned short c) { return std::towlower(c); }); return result; } std::string FoldCase(std::string_view input) { if (input.empty()) { return {}; } wil::unique_any caseMap; UErrorCode errorCode = UErrorCode::U_ZERO_ERROR; caseMap.reset(ucasemap_open(nullptr, U_FOLD_CASE_DEFAULT, &errorCode)); if (U_FAILURE(errorCode)) { AICLI_LOG(Core, Error, << "ucasemap_open returned " << errorCode); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_CASEMAP_ERROR); } int32_t cch = ucasemap_utf8FoldCase(caseMap.get(), nullptr, 0, input.data(), static_cast(input.size()), &errorCode); if (errorCode != U_BUFFER_OVERFLOW_ERROR) { AICLI_LOG(Core, Error, << "ucasemap_utf8FoldCase returned " << errorCode); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_CASEMAP_ERROR); } errorCode = UErrorCode::U_ZERO_ERROR; std::string result(cch, '\0'); cch = ucasemap_utf8FoldCase(caseMap.get(), &result[0], cch, input.data(), static_cast(input.size()), &errorCode); if (U_FAILURE(errorCode)) { AICLI_LOG(Core, Error, << "ucasemap_utf8FoldCase returned " << errorCode); THROW_HR(APPINSTALLER_CLI_ERROR_ICU_CASEMAP_ERROR); } while (result.back() == '\0') { result.pop_back(); } return result; } NormalizedString FoldCase(const NormalizedString& input) { NormalizedString result; result.assign(FoldCase(static_cast(input))); return result; } bool IsEmptyOrWhitespace(std::string_view str) { if (str.empty()) { return true; } return str.find_last_not_of(s_SpaceChars) == std::string_view::npos; } bool IsEmptyOrWhitespace(std::wstring_view str) { if (str.empty()) { return true; } return str.find_last_not_of(s_WideSpaceChars) == std::wstring_view::npos; } bool FindAndReplace(std::string& inputStr, std::string_view token, std::string_view value) { bool result = false; std::string::size_type pos = 0u; while ((pos = inputStr.find(token, pos)) != std::string::npos) { result = true; inputStr.replace(pos, token.length(), value); pos += value.length(); } return result; } std::wstring ReplaceWhileCopying(std::wstring_view input, std::wstring_view token, std::wstring_view value) { if (token.empty()) { return std::wstring{ input }; } std::wstring result; result.reserve(input.size()); std::wstring::size_type pos = 0u; do { std::wstring::size_type findPos = input.find(token, pos); if (findPos == std::wstring::npos) { result.append(input.substr(pos)); } else { result.append(input.substr(pos, findPos - pos)); result.append(value); findPos += token.length(); } pos = findPos; } while (pos != std::wstring::npos); return result; } std::string& Trim(std::string& str) { return TrimTemplate(str, s_SpaceChars); } std::string Trim(const std::string& str) { return TrimCopyTemplate(str); } std::string Trim(std::string&& str) { return TrimMoveTemplate(std::move(str)); } std::string_view& Trim(std::string_view& str) { return TrimTemplate(str, s_SpaceChars); } std::string_view Trim(std::string_view&& str) { return TrimCopyTemplate(str); } std::wstring& Trim(std::wstring& str) { return TrimTemplate(str, s_WideSpaceChars); } std::wstring Trim(const std::wstring& str) { return TrimCopyTemplate(str); } std::wstring Trim(std::wstring&& str) { return TrimMoveTemplate(std::move(str)); } std::wstring_view& Trim(std::wstring_view& str) { return TrimTemplate(str, s_WideSpaceChars); } std::wstring_view Trim(std::wstring_view&& str) { return TrimCopyTemplate(str); } std::string ReadEntireStream(std::istream& stream) { std::streampos currentPos = stream.tellg(); stream.seekg(0, std::ios_base::end); auto offset = stream.tellg() - currentPos; stream.seekg(currentPos); // Don't allow use of this API for reading very large streams. THROW_HR_IF(E_OUTOFMEMORY, offset > static_cast(std::numeric_limits::max())); std::string result(static_cast(offset), '\0'); stream.read(&result[0], offset); return result; } std::vector ReadEntireStreamAsByteArray(std::istream& stream) { std::streampos currentPos = stream.tellg(); stream.seekg(0, std::ios_base::end); auto offset = stream.tellg() - currentPos; stream.seekg(currentPos); // Don't allow use of this API for reading very large streams. THROW_HR_IF(E_OUTOFMEMORY, offset > static_cast(std::numeric_limits::max())); std::vector result; result.resize(static_cast(offset)); stream.read(reinterpret_cast(result.data()), offset); return result; } std::wstring ExpandEnvironmentVariables(const std::wstring& input) { if (input.empty()) { return {}; } DWORD charCount = ExpandEnvironmentStringsW(input.c_str(), nullptr, 0); THROW_LAST_ERROR_IF(charCount == 0); std::wstring result(wil::safe_cast(charCount), L'\0'); DWORD charCountWritten = ExpandEnvironmentStringsW(input.c_str(), &result[0], charCount); THROW_HR_IF(E_UNEXPECTED, charCount != charCountWritten); if (result.back() == L'\0') { result.resize(result.size() - 1); } return result; } // Follow the rules at https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file to replace // invalid characters in a candidate path part. // Additionally, based on https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits // limit the number of characters to 255. std::string MakeSuitablePathPart(std::string_view candidate) { constexpr char replaceChar = '_'; constexpr std::string_view illegalChars = R"(<>:"/\|?*)"; constexpr size_t pathLengthLimit = 255; // First, walk the string and replace illegal characters std::string result; result.reserve(candidate.size()); ICUBreakIterator itr{ candidate, UBRK_CHARACTER }; size_t resultBreakCount = 0; while (itr.CurrentBreak() != UBRK_DONE && itr.CurrentOffset() < candidate.size() && resultBreakCount <= pathLengthLimit) { UChar32 current = itr.CurrentCodePoint(); bool isIllegal = current < 32 || (current < 256 && illegalChars.find(static_cast(current)) != std::string::npos); int32_t offset = itr.CurrentBreak(); int32_t nextOffset = itr.Next(); // Don't allow a . at the end of a name if (static_cast(nextOffset) >= candidate.size()) { if (current == static_cast('.')) { isIllegal = true; } } if (isIllegal) { result.append(1, replaceChar); } else { size_t count = (nextOffset == UBRK_DONE ? std::string::npos : static_cast(nextOffset) - static_cast(offset)); result.append(candidate.substr(static_cast(offset), count)); } ++resultBreakCount; } // If there are too many characters for a single path; switch to a hash. // This should basically never happen, but if it does it will prevent collisions better. if (resultBreakCount > pathLengthLimit) { return SHA256::ConvertToString(SHA256::ComputeHash(candidate)); } // Second, look for any newly formed illegal names. // For now just error on these cases; they should not happen often. for (const auto& illegalName : { "."sv, "CON"sv, "PRN"sv, "AUX"sv, "NUL"sv, "COM1"sv, "COM2"sv, "COM3"sv, "COM4"sv, "COM5"sv, "COM6"sv, "COM7"sv, "COM8"sv, "COM9"sv, "LPT1"sv, "LPT2"sv, "LPT3"sv, "LPT4"sv, "LPT5"sv, "LPT6"sv, "LPT7"sv, "LPT8"sv, "LPT9"sv }) { // Either equals the illegal name (starts with and same length) or starts with and the first character after is a . if (CaseInsensitiveStartsWith(result, illegalName) && (result.size() == illegalName.size() || result[illegalName.size()] == '.')) { THROW_HR(E_INVALIDARG); } } return result; } std::pair SplitFileNameFromURI(std::string_view uri) { std::filesystem::path filename = GetFileNameFromURI(uri); return { std::string{ uri.substr(0, uri.size() - filename.u8string().size()) }, filename }; } std::filesystem::path GetFileNameFromURI(std::string_view uri) { winrt::Windows::Foundation::Uri winrtUri{ winrt::hstring{ ConvertToUTF16(uri) } }; std::filesystem::path path{ static_cast(winrtUri.Path()) }; return path.filename(); } std::vector SplitIntoWords(std::string_view input) { ICUBreakIterator itr{ input, UBRK_WORD }; std::size_t currentOffset = 0; std::vector result; while (itr.Next() != UBRK_DONE) { std::size_t nextOffset = itr.CurrentOffset(); // Ignore spaces and punctuation, accept words and numbers if (itr.CurrentRuleStatus() != UBRK_WORD_NONE) { auto wordSize = nextOffset - currentOffset; result.emplace_back(input, currentOffset, wordSize); } currentOffset = nextOffset; } return result; } std::vector SplitIntoLines(std::string_view input, size_t maximum) { std::size_t currentOffset = 0; std::vector result; while (currentOffset < input.size() && (!maximum || result.size() < maximum)) { std::size_t nextOffset = input.find_first_of("\r\n", currentOffset); if (nextOffset == std::string_view::npos) { nextOffset = input.size(); } if (nextOffset - currentOffset > 1) { result.emplace_back(input.substr(currentOffset, nextOffset - currentOffset)); } currentOffset = nextOffset + 1; } return result; } bool LimitOutputLines(std::vector& lines, size_t lineWidth, size_t maximum) { size_t totalLines = 0; size_t currentLine = 0; bool result = false; for (; currentLine < lines.size() && totalLines < maximum; ++currentLine) { size_t currentLineWidth = UTF8ColumnWidth(lines[currentLine]); // If current line is empty, the cost is 1 line (0 + 1). // If not, round up to the next line count (by rounding down through integer division after subtracting 1 + 1). size_t currentLineActualLineCount = (currentLineWidth ? (currentLineWidth - 1) / lineWidth : 0) + 1; // The current line may be too big to be the last line, or it may be just the right size but we will end up trimming // additional lines. In either case, append an ellipsis to indicate that we trimmed the value. size_t availableLines = maximum - totalLines; if (currentLineActualLineCount > availableLines || (currentLineActualLineCount == availableLines && currentLine != lines.size() - 1)) { size_t actualWidth = 0; std::string trimmedLine = UTF8TrimRightToColumnWidth(lines[currentLine], (availableLines * lineWidth) - 1, actualWidth); trimmedLine += "\xE2\x80\xA6"; // UTF8 encoding of ellipsis (�) character lines[currentLine] = trimmedLine; currentLineActualLineCount = availableLines; result = true; } totalLines += currentLineActualLineCount; } // Drop any unprocessed lines if (currentLine != lines.size()) { lines.resize(currentLine); result = true; } return result; } std::string ConvertToHexString(const std::vector& buffer, size_t byteCount) { if (byteCount && buffer.size() != byteCount) { THROW_HR_MSG(E_INVALIDARG, "ConvertToHexString: Invalid buffer size"); } std::string result(2 * buffer.size(), '\0'); static constexpr std::array hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; for (size_t i = 0; i < buffer.size(); ++i) { result[2 * i] = hexChars[(buffer[i] >> 4) & 0xF]; result[2 * i + 1] = hexChars[buffer[i] & 0xF]; } return result; } std::vector ParseFromHexString(const std::string& value, size_t byteCount) { if ((byteCount && value.size() != (2 * byteCount)) || (value.size() % 2)) { THROW_HR_MSG(E_INVALIDARG, "ParseFromHexString: Invalid value size"); } const char* valuePtr = value.c_str(); std::vector result; result.resize(value.size() / 2); for (size_t i = 0; i < result.size(); i++) { sscanf_s(valuePtr + 2 * i, "%02hhx", &result[i]); } return result; } template static std::string JoinInternal(std::string_view separator, const std::vector& vector) { auto vectorSize = vector.size(); if (vectorSize == 0) { return {}; } std::ostringstream ssJoin; ssJoin << vector[0]; for (size_t i = 1; i < vectorSize; ++i) { ssJoin << separator << vector[i]; } return ssJoin.str(); } LocIndString Join(LocIndView separator, const std::vector& vector) { return LocIndString{ JoinInternal(separator, vector) }; } std::string Join(std::string_view separator, const std::vector& vector) { return JoinInternal(separator, vector); } std::vector Split(const std::string& input, char separator, bool trim) { return SplitTemplate(input, separator, trim); } std::vector SplitView(const std::string& input, char separator, bool trim) { return SplitTemplate(static_cast(input), separator, trim); } std::vector Split(std::string_view input, char separator, bool trim) { return SplitTemplate(input, separator, trim); } std::vector Split(const std::wstring& input, wchar_t separator, bool trim) { return SplitTemplate(input, separator, trim); } std::vector SplitView(const std::wstring& input, wchar_t separator, bool trim) { return SplitTemplate(static_cast(input), separator, trim); } std::vector Split(std::wstring_view input, wchar_t separator, bool trim) { return SplitTemplate(input, separator, trim); } std::string_view ConvertBoolToString(bool value) { return value ? "true"sv : "false"sv; } std::optional TryConvertStringToBool(const std::string_view& input) { try { if (CaseInsensitiveEquals(input, "false"sv)) { return { false }; } if (CaseInsensitiveEquals(input, "true"sv)) { return { true }; } return {}; } catch (...) { return {}; } } std::optional TryConvertStringToInt32(const std::string_view& input) { int32_t result = 0; auto parseResult = std::from_chars(input.data(), input.data() + input.length(), result); std::optional optionalResult; if (parseResult.ec == std::errc{}) { optionalResult = result; } return optionalResult; } std::string ConvertGuidToString(const GUID& value) { wchar_t buffer[40]; THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(value, buffer, ARRAYSIZE(buffer))); return ConvertToUTF8(buffer); } std::wstring CreateNewGuidNameWString() { GUID guid; THROW_IF_FAILED(CoCreateGuid(&guid)); wchar_t buffer[40]; THROW_HR_IF(E_UNEXPECTED, StringFromGUID2(guid, buffer, ARRAYSIZE(buffer)) != 39); return std::wstring{ &buffer[1], 36 }; } bool IsDwordFlagSet(const std::string& value) { if (std::empty(value)) { return false; } try { DWORD dwordValue = std::stoul(value); // If the value is 0, then it is not set. return dwordValue != 0; } catch (...) { return false; } } size_t FindControlCodeToConvert(std::string_view input, size_t offset) { size_t nextControl = offset; while (nextControl < input.size()) { char currentChar = input[nextControl]; // Convert all low controls except tab, line feed and carriage return if (currentChar >= 0 && currentChar < 0x20 && currentChar != '\t' && currentChar != '\n' && currentChar != '\r') { break; } // Convert the Delete control if (currentChar == 0x7F) { break; } ++nextControl; } return nextControl < input.size() ? nextControl : std::string::npos; } std::string ConvertControlCodesToPictures(std::string_view input) { std::string result; size_t pos = 0; while (pos < input.size()) { size_t nextControl = FindControlCodeToConvert(input, pos); if (nextControl == std::string::npos) { // No more control codes found result += input.substr(pos); break; } else { result += input.substr(pos, nextControl - pos); char currentChar = input[nextControl]; if (currentChar >= 0 && currentChar < 0x20) { // ASCII 0x00 - 0x1F => UTF-8 0x2400 - 0x241F // Then manually converted to UTF-8 since only the last character is affected result += '\xE2'; result += '\x90'; result += ('\x80' + currentChar); } else if (currentChar == 0x7F) { // UTF-8 for control picture of DELETE result += "\xE2\x90\xA1"; } pos = nextControl + 1; } } return result; } std::string GetRandomString(size_t size) { static constexpr char chars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static std::default_random_engine randomEngine(std::random_device{}()); static std::uniform_int_distribution distribution(0, 35); std::string result; result.resize(size); for (size_t i = 0; i < size; i++) { result[i] = chars[distribution(randomEngine)]; } return result; } } ================================================ FILE: src/AppInstallerSharedLib/COMStaticStorage.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/COMStaticStorage.h" namespace AppInstaller::WinRT { COMStaticStorageStatics& COMStaticStorageStatics::Instance() { static COMStaticStorageStatics s_instance; return s_instance; } void COMStaticStorageStatics::AddStaticStorageItem(const winrt::hstring& name, const winrt::Windows::Foundation::IInspectable& item) { COMStaticStorageStatics& instance = Instance(); const winrt::slim_lock_guard lock{ instance.m_lock }; winrt::Windows::ApplicationModel::Core::CoreApplication::Properties().Insert(name, item); instance.m_items.emplace(std::wstring{ name }); } void COMStaticStorageStatics::ResetAll() try { COMStaticStorageStatics& instance = Instance(); std::set localItems; { const winrt::slim_lock_guard lock{ instance.m_lock }; instance.m_items.swap(localItems); } for (const auto& item : localItems) { winrt::Windows::ApplicationModel::Core::CoreApplication::Properties().TryRemove(item); } } CATCH_LOG(); } ================================================ FILE: src/AppInstallerSharedLib/Certificates.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/Certificates.h" #include "AppInstallerDateTime.h" #include "AppInstallerLogging.h" #include "AppInstallerStrings.h" #include "winget/JsonUtil.h" #include "winget/Resources.h" namespace AppInstaller::Certificates { namespace { std::string GetNameString(PCCERT_CONTEXT certContext, DWORD nameType, bool forIssuer, void* typeParam = nullptr) { if (!certContext) { return ""; } DWORD flags = forIssuer ? CERT_NAME_ISSUER_FLAG : 0; DWORD characterCount = CertGetNameStringW(certContext, nameType, flags, typeParam, nullptr, 0); std::wstring result(characterCount, L'\0'); characterCount = CertGetNameStringW(certContext, nameType, flags, typeParam, &result[0], characterCount); if (static_cast(characterCount) == result.size()) { return Utility::ConvertToUTF8(static_cast(result).substr(0, result.size() - 1)); } else { return ""; } } std::string GetSimpleDisplayName(PCCERT_CONTEXT certContext, bool forIssuer = false) { return GetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, forIssuer); } std::string GetX500Name(PCCERT_CONTEXT certContext, bool forIssuer = false) { DWORD stringType = CERT_X500_NAME_STR; return GetNameString(certContext, CERT_NAME_RDN_TYPE, forIssuer, &stringType); } std::string GetCommonName(PCCERT_CONTEXT certContext, bool forIssuer = false) { std::string commonName = szOID_COMMON_NAME; return GetNameString(certContext, CERT_NAME_ATTR_TYPE, forIssuer, &commonName[0]); } std::string GetDescriptionOfCertChain(PCCERT_CHAIN_CONTEXT chainContext) { PCCERT_SIMPLE_CHAIN chain = chainContext->rgpChain[0]; std::ostringstream stream; std::string indent; for (DWORD i = 0; i < chain->cElement; ++i) { PCCERT_CHAIN_ELEMENT element = chain->rgpElement[(chain->cElement - 1) - i]; if (!indent.empty()) { stream << std::endl; } stream << indent; stream << GetSimpleDisplayName(element->pCertContext); indent.append(" "); } return std::move(stream).str(); } std::optional GetTypeFromString(std::string_view value) { std::string lowerValue = Utility::ToLower(value); if (lowerValue == "none") { return PinningVerificationType::None; } else if (lowerValue == "publickey") { return PinningVerificationType::PublicKey; } else if (lowerValue == "subject") { return PinningVerificationType::Subject; } else if (lowerValue == "issuer") { return PinningVerificationType::Issuer; } else if (lowerValue == "anyissuer") { return PinningVerificationType::AnyIssuer; } else if (lowerValue == "requirenonleaf") { return PinningVerificationType::RequireNonLeaf; } return {}; } CertificateChainPosition GetCertificateChainPosition(DWORD index, DWORD count) { THROW_HR_IF(E_INVALIDARG, count == 0); CertificateChainPosition position = CertificateChainPosition::Unknown; if (index == 0) { position |= CertificateChainPosition::Root; } if (index > 0 && index < (count - 1)) { position |= CertificateChainPosition::Intermediate; } if (index == (count - 1)) { position |= CertificateChainPosition::Leaf; } return position; } } std::ostream& operator<<(std::ostream& out, PinningVerificationType value) { if (value == PinningVerificationType::None) { out << "None"; } else { bool prepend = false; for (const auto& flag : std::initializer_list>{ { PinningVerificationType::PublicKey, "PublicKey" }, { PinningVerificationType::Subject, "Subject" }, { PinningVerificationType::Issuer, "Issuer" }, { PinningVerificationType::AnyIssuer, "AnyIssuer" }, { PinningVerificationType::RequireNonLeaf, "RequireNonLeaf" }, }) { if (WI_IsAnyFlagSet(value, flag.first)) { if (prepend) { out << " | "; } out << flag.second; prepend = true; } } } return out; } std::ostream& operator<<(std::ostream& out, CertificateChainPosition value) { if (value == CertificateChainPosition::Unknown) { out << "Unknown"; } else { bool prepend = false; for (const auto& flag : std::initializer_list>{ { CertificateChainPosition::Root, "Root" }, { CertificateChainPosition::Intermediate, "Intermediate" }, { CertificateChainPosition::Leaf, "Leaf" }, }) { if (WI_IsAnyFlagSet(value, flag.first)) { if (prepend) { out << " | "; } out << flag.second; prepend = true; } } } return out; } PinningDetails& PinningDetails::LoadCertificate(int resource, int resourceType) { return LoadCertificate(Resource::GetResourceAsBytes(resource, resourceType)); } PinningDetails& PinningDetails::LoadCertificate(const std::vector& certificateBytes) { return LoadCertificate(std::make_pair(&certificateBytes[0], certificateBytes.size())); } PinningDetails& PinningDetails::LoadCertificate(const std::pair certificateBytes) { m_certificateContext.reset(CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, certificateBytes.first, static_cast(certificateBytes.second))); THROW_LAST_ERROR_IF(!m_certificateContext); return *this; } PinningDetails& PinningDetails::SetPinning(PinningVerificationType type) { m_pinning = type; return *this; } // The JSON is expected to look like: // { // "Validation":["publickey"], // "EmbeddedCertificate":"" // } bool PinningDetails::LoadFrom(const Json::Value& configuration) { const std::string validationName = "Validation"; if (!configuration.isMember(validationName)) { AICLI_LOG(Core, Warning, << "Details JSON item has no member " << validationName); return false; } auto validationValue = JSON::GetValue>(configuration[validationName]); if (!validationValue) { AICLI_LOG(Core, Warning, << "Details JSON item member " << validationName << " was not an array of strings"); return false; } for (const std::string& singleValidation : validationValue.value()) { auto validationType = GetTypeFromString(singleValidation); if (!validationType) { AICLI_LOG(Core, Warning, << "Details JSON validation is unknown: " << singleValidation); return false; } m_pinning |= validationType.value(); } if (m_pinning == PinningVerificationType::None) { // No need to load a certificate if not doing any pinning return true; } const std::string embeddedCertificateName = "EmbeddedCertificate"; if (!configuration.isMember(embeddedCertificateName)) { AICLI_LOG(Core, Warning, << "Details JSON item has no member " << embeddedCertificateName); return false; } auto embeddedCertificateValue = JSON::GetValue(configuration[embeddedCertificateName]); if (!validationValue) { AICLI_LOG(Core, Warning, << "Details JSON item member " << embeddedCertificateName << " was not a string"); return false; } auto embeddedCertificateBytes = Utility::ParseFromHexString(embeddedCertificateValue.value()); LoadCertificate(embeddedCertificateBytes); return true; } CertificatePinningValidationResult PinningDetails::Validate(PCCERT_CONTEXT certContext, CertificateChainPosition position) const { CertificatePinningValidationResult failResult = WI_IsFlagSet(m_pinning, PinningVerificationType::AnyIssuer) ? CertificatePinningValidationResult::Skipped : CertificatePinningValidationResult::Rejected; if (WI_IsFlagSet(m_pinning, PinningVerificationType::RequireNonLeaf) && WI_IsFlagSet(position, CertificateChainPosition::Leaf)) { AICLI_LOG(Core, Verbose, << "Required non-leaf mismatch: Expected certificate [" << GetSimpleDisplayName(m_certificateContext.get()) << "], Actual certificate [" << GetSimpleDisplayName(certContext) << "] was " << position); return CertificatePinningValidationResult::Rejected; } if (WI_IsFlagSet(m_pinning, PinningVerificationType::PublicKey)) { THROW_HR_IF(E_NOT_VALID_STATE, !m_certificateContext); if (!CertComparePublicKeyInfo( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &m_certificateContext.get()->pCertInfo->SubjectPublicKeyInfo, &certContext->pCertInfo->SubjectPublicKeyInfo)) { AICLI_LOG(Core, Verbose, << "Public key mismatch: Expected certificate [" << GetSimpleDisplayName(m_certificateContext.get()) << "], Actual certificate [" << GetSimpleDisplayName(certContext) << "]"); return failResult; } } if (WI_IsFlagSet(m_pinning, PinningVerificationType::Subject)) { THROW_HR_IF(E_NOT_VALID_STATE, !m_certificateContext); if (!CertCompareCertificateName( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &m_certificateContext.get()->pCertInfo->Subject, &certContext->pCertInfo->Subject)) { AICLI_LOG(Core, Verbose, << "Subject mismatch: Expected certificate [" << GetSimpleDisplayName(m_certificateContext.get()) << "], Actual certificate [" << GetSimpleDisplayName(certContext) << "]"); return failResult; } } if (WI_IsFlagSet(m_pinning, PinningVerificationType::Issuer)) { THROW_HR_IF(E_NOT_VALID_STATE, !m_certificateContext); if (!CertCompareCertificateName( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &m_certificateContext.get()->pCertInfo->Issuer, &certContext->pCertInfo->Issuer)) { AICLI_LOG(Core, Verbose, << "Issuer mismatch: Expected certificate [" << GetSimpleDisplayName(m_certificateContext.get()) << "], Actual certificate [" << GetSimpleDisplayName(certContext) << "]"); return failResult; } } #ifndef AICLI_DISABLE_TEST_HOOKS if (m_customValidation) { if (!m_customValidation(*this, certContext, position)) { AICLI_LOG(Core, Verbose, << "Custom validation returned false: Expected certificate [" << GetSimpleDisplayName(m_certificateContext.get()) << "], Actual certificate [" << GetSimpleDisplayName(certContext) << "]"); return failResult; } } #endif return CertificatePinningValidationResult::Accepted; } void PinningDetails::OutputDescription(std::ostream& stream, std::string_view indent) const { stream << indent << GetSimpleDisplayName(m_certificateContext.get()) << " : " << m_pinning; } double PinningDetails::GetRemainingLifetimePercentage() const { THROW_HR_IF(E_NOT_VALID_STATE, !m_certificateContext); auto notBefore = Utility::ConvertFiletimeToSystemClock(m_certificateContext.get()->pCertInfo->NotBefore); auto notAfter = Utility::ConvertFiletimeToSystemClock(m_certificateContext.get()->pCertInfo->NotAfter); THROW_HR_IF(E_NOT_VALID_STATE, notBefore > notAfter); auto now = std::chrono::system_clock::now(); if (now < notBefore) { return 1.0; } else if (now > notAfter) { return 0.0; } auto totalTime = notAfter - notBefore; auto remainingTime = notAfter - now; return static_cast(remainingTime.count()) / static_cast(totalTime.count()); } PinningChain::Node PinningChain::Node::Next() { if (!HasNext()) { m_chain.get().emplace_back(); } return { m_chain, m_index + 1 }; } const PinningChain::Node PinningChain::Node::Next() const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !HasNext()); return { m_chain, m_index + 1 }; } void PinningChain::Node::RemoveNext() { m_chain.get().erase(m_chain.get().begin() + m_index + 1, m_chain.get().end()); } bool PinningChain::Node::HasNext() const { return (m_index + 1 < m_chain.get().size()); } PinningChain::Node::Node(std::vector& chain, size_t index) : m_chain(chain), m_index(index) {} PinningChain::Node PinningChain::Root() { if (m_chain.empty()) { m_chain.emplace_back(); } return { m_chain, 0 }; } const PinningChain::Node PinningChain::Root() const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), m_chain.empty()); return { const_cast&>(m_chain), 0 }; } PinningChain& PinningChain::PartialChain(bool isPartial) { m_partial = isPartial; return *this; } bool PinningChain::Validate(PCCERT_CHAIN_CONTEXT chainContext) const { if (m_chain.empty()) { // An empty chain rejects all inputs. AICLI_LOG(Core, Warning, << "Empty pinning chain blindly rejecting chain context"); return false; } THROW_HR_IF(E_INVALIDARG, chainContext->cChain == 0); // Currently don't support chains bridged with CTLs; there must be only one simple chain that terminates in a trusted root. if (chainContext->cChain > 1) { AICLI_LOG(Core, Verbose, << "Rejecting chain context with multiple chains"); return false; } PCCERT_SIMPLE_CHAIN chain = chainContext->rgpChain[0]; if (chain->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR) { AICLI_LOG(Core, Verbose, << "Rejecting simple chain context with bad TrustStatus: " << chain->TrustStatus.dwErrorStatus << " [" << chain->TrustStatus.dwInfoStatus << "]"); return false; } if (chain->pTrustListInfo) { // This should not happen as the only reason for pTrustListInfo to be set is when `chainContext->cChain > 1`, which is rejected above AICLI_LOG(Core, Verbose, << "Rejecting simple chain context with CTL info"); return false; } if (!m_partial && static_cast(chain->cElement) != m_chain.size()) { AICLI_LOG(Core, Verbose, << "Rejecting simple chain context based on size: expected " << m_chain.size() << ", got " << chain->cElement); return false; } size_t currentDetailsIndex = 0; for (DWORD i = 0; i < chain->cElement; ++i) { PCCERT_CHAIN_ELEMENT element = chain->rgpElement[(chain->cElement - 1) - i]; if (element->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR) { AICLI_LOG(Core, Verbose, << "Rejecting chain element with bad TrustStatus: " << element->TrustStatus.dwErrorStatus << " [" << element->TrustStatus.dwInfoStatus << "]"); return false; } CertificatePinningValidationResult result = m_chain[currentDetailsIndex].Validate(element->pCertContext, GetCertificateChainPosition(i, chain->cElement)); if (result == CertificatePinningValidationResult::Rejected) { return false; } else if (result == CertificatePinningValidationResult::Accepted) { ++currentDetailsIndex; } else { THROW_HR_IF(E_UNEXPECTED, !m_partial || result != CertificatePinningValidationResult::Skipped); AICLI_LOG(Core, Verbose, << "Skipping [" << GetSimpleDisplayName(element->pCertContext) << "] in partial chain validation."); } if (m_partial && m_chain.size() == currentDetailsIndex) { break; } } // Ensure that all chain elements have been accepted return m_chain.size() == currentDetailsIndex; } std::string PinningChain::GetDescription() const { if (m_chain.empty()) { return ""; } std::ostringstream stream; std::string indent; for (const PinningDetails& details : m_chain) { if (!indent.empty()) { stream << std::endl; } else if (m_partial) { stream << "[Partial Chain Validation]" << std::endl; } details.OutputDescription(stream, indent); indent.append(" "); } return std::move(stream).str(); } // The JSON is expected to look like: // { // "Chain":[ // { // "Validation":["publickey"], // "EmbeddedCertificate":"" // }, // { // "Validation":["subject","issuer"], // "EmbeddedCertificate":"" // }, // ... // ] // } bool PinningChain::LoadFrom(const Json::Value& configuration) { const std::string chainName = "Chain"; if (!configuration.isMember(chainName)) { AICLI_LOG(Core, Warning, << "Chains JSON item has no member " << chainName); return false; } const auto& chain = configuration[chainName]; if (!chain.isArray()) { AICLI_LOG(Core, Warning, << "Chain JSON input is not an array"); return false; } for (const auto& configItem : chain) { PinningDetails details; if (!details.LoadFrom(configItem)) { return false; } m_chain.emplace_back(std::move(details)); } return true; } double PinningChain::GetRemainingLifetimePercentage() const { double result = 1.0; for (const auto& details : m_chain) { result = std::min(result, details.GetRemainingLifetimePercentage()); } return result; } PinningConfiguration::PinningConfiguration(std::string identifier) : m_identifier(identifier) { if (m_identifier.empty()) { GUID guid; LOG_IF_FAILED(CoCreateGuid(&guid)); wchar_t identifierBuffer[256] = {}; (void)StringFromGUID2(guid, identifierBuffer, ARRAYSIZE(identifierBuffer)); m_identifier = Utility::ConvertToUTF8(identifierBuffer); } } void PinningConfiguration::AddChain(PinningChain chain) { AICLI_LOG(Core, Verbose, << "Adding chain to pinning configuration [" << m_identifier << "]:\n" << chain.GetDescription()); m_configuration.emplace_back(std::move(chain)); } bool PinningConfiguration::Validate(PCCERT_CONTEXT certContext) const { if (m_configuration.empty()) { // No pinning configured return true; } const BYTE* encodedBegin = certContext->pbCertEncoded; const BYTE* encodedEnd = encodedBegin + certContext->cbCertEncoded; if (certContext->cbCertEncoded == m_cachedCertificate.size() && std::equal(encodedBegin, encodedEnd, m_cachedCertificate.begin())) { // We have seen this certificate and deemed it valid already. return true; } // Get the chain for the given leaf certificate wil::unique_cert_chain_context chainContext; char oidPkixKpServerAuth[] = szOID_PKIX_KP_SERVER_AUTH; std::array chainUses = { oidPkixKpServerAuth, }; CERT_CHAIN_PARA chainParameters = {}; chainParameters.cbSize = sizeof(chainParameters); chainParameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; chainParameters.RequestedUsage.Usage.cUsageIdentifier = static_cast(chainUses.size()); chainParameters.RequestedUsage.Usage.rgpszUsageIdentifier = chainUses.data(); THROW_IF_WIN32_BOOL_FALSE(CertGetCertificateChain(nullptr, certContext, nullptr, certContext->hCertStore, &chainParameters, CERT_CHAIN_REVOCATION_CHECK_CHAIN, nullptr, &chainContext)); bool result = false; for (const auto& chain : m_configuration) { if (chain.Validate(chainContext.get())) { AICLI_LOG(Core, Verbose, << "Certificate `" << GetSimpleDisplayName(certContext) << "` accepted by pinning configuration:\n" << chain.GetDescription()); result = true; break; } } if (result) { // Only cache a successful validation m_cachedCertificate.assign(encodedBegin, encodedEnd); } else { AICLI_LOG(Core, Error, << "Rejecting certificate [" << GetSimpleDisplayName(certContext) << "] as it did not match anything in pinning configuration [" << m_identifier << "]:\n" << GetDescriptionOfCertChain(chainContext.get())); } return result; } // The JSON is expected to look like: // { // "Chains":[ // { // "Chain":[ // { // "Validation":["publickey"], // "EmbeddedCertificate":"" // }, // { // "Validation":["subject","issuer"], // "EmbeddedCertificate":"" // }, // ... // ] // } // ] // } bool PinningConfiguration::LoadFrom(const Json::Value& configuration) { const std::string chainsName = "Chains"; if (!configuration.isMember(chainsName)) { AICLI_LOG(Core, Warning, << "PinningConfiguration JSON item has no member " << chainsName); return false; } const auto& chains = configuration[chainsName]; if (!chains.isArray()) { AICLI_LOG(Core, Warning, << "PinningConfiguration.Chains is not an array"); return false; } std::vector resultCache; for (const auto& configItem : chains) { PinningChain chain; if (!chain.LoadFrom(configItem)) { return false; } resultCache.emplace_back(std::move(chain)); } // Move all chains into the config now that we have succeeded for (auto& result : resultCache) { AddChain(std::move(result)); } return true; } double PinningConfiguration::GetRemainingLifetimePercentage() const { double result = 0.0; for (const auto& chain : m_configuration) { result = std::max(result, chain.GetRemainingLifetimePercentage()); } return result; } } ================================================ FILE: src/AppInstallerSharedLib/Compression.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/Compression.h" namespace AppInstaller::Compression { Compressor::Compressor(DWORD algorithm) { THROW_IF_WIN32_BOOL_FALSE(CreateCompressor(algorithm, nullptr, &m_compressor)); } std::vector Compressor::Compress(std::string_view data) { std::vector result; if (!data.empty()) { SIZE_T compressedBufferSize = 0; THROW_HR_IF(E_UNEXPECTED, ::Compress(m_compressor.get(), data.data(), data.size(), nullptr, 0, &compressedBufferSize)); THROW_LAST_ERROR_IF(GetLastError() != ERROR_INSUFFICIENT_BUFFER); result.resize(compressedBufferSize); SIZE_T compressedDataSize = 0; THROW_IF_WIN32_BOOL_FALSE(::Compress(m_compressor.get(), data.data(), data.size(), &result[0], result.size(), &compressedDataSize)); result.resize(compressedDataSize); } return result; } void Compressor::Reset() { THROW_IF_WIN32_BOOL_FALSE(ResetCompressor(m_compressor.get())); } void Compressor::SetInformation(COMPRESS_INFORMATION_CLASS information, DWORD value) { THROW_IF_WIN32_BOOL_FALSE(SetCompressorInformation(m_compressor.get(), information, &value, sizeof(value))); } DWORD Compressor::GetInformation(COMPRESS_INFORMATION_CLASS information) { DWORD result = 0; THROW_IF_WIN32_BOOL_FALSE(QueryCompressorInformation(m_compressor.get(), information, &result, sizeof(result))); return result; } Decompressor::Decompressor(DWORD algorithm) { THROW_IF_WIN32_BOOL_FALSE(CreateDecompressor(algorithm, nullptr, &m_decompressor)); } std::vector Decompressor::Decompress(const std::vector& data) { std::vector result; if (!data.empty()) { SIZE_T decompressedBufferSize = 0; THROW_HR_IF(E_UNEXPECTED, ::Decompress(m_decompressor.get(), data.data(), data.size(), nullptr, 0, &decompressedBufferSize)); THROW_LAST_ERROR_IF(GetLastError() != ERROR_INSUFFICIENT_BUFFER); result.resize(decompressedBufferSize); SIZE_T decompressedDataSize = 0; THROW_IF_WIN32_BOOL_FALSE(::Decompress(m_decompressor.get(), data.data(), data.size(), &result[0], result.size(), &decompressedDataSize)); result.resize(decompressedDataSize); } return result; } void Decompressor::Reset() { THROW_IF_WIN32_BOOL_FALSE(ResetDecompressor(m_decompressor.get())); } void Decompressor::SetInformation(COMPRESS_INFORMATION_CLASS information, DWORD value) { THROW_IF_WIN32_BOOL_FALSE(SetDecompressorInformation(m_decompressor.get(), information, &value, sizeof(value))); } DWORD Decompressor::GetInformation(COMPRESS_INFORMATION_CLASS information) { DWORD result = 0; THROW_IF_WIN32_BOOL_FALSE(QueryDecompressorInformation(m_decompressor.get(), information, &result, sizeof(result))); return result; } } ================================================ FILE: src/AppInstallerSharedLib/DateTime.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerDateTime.h" using namespace std::chrono; namespace AppInstaller::Utility { namespace { struct OutputTimePointContext { OutputTimePointContext(std::ostream& stream, const std::chrono::system_clock::time_point& time, TimeFacet facet) : Stream(stream), Time(time), Facet(facet) { auto tt = system_clock::to_time_t(time); _localtime64_s(&LocalTime, &tt); } std::ostream& Stream; const std::chrono::system_clock::time_point& Time; tm LocalTime{}; TimeFacet Facet; }; struct OutputTimePointFacetInfo { TimeFacet Facet; char FollowingSeparator; void (*Action)(const OutputTimePointContext&); }; } void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, bool useRFC3339) { OutputTimePoint(stream, time, TimeFacet::Default | (useRFC3339 ? TimeFacet::RFC3339 : TimeFacet::None)); } // If moved to C++20, this can be replaced with standard library implementations. void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, TimeFacet facet) { OutputTimePointContext context{ stream, time, facet }; using Ctx = const OutputTimePointContext&; bool useRFC3339 = WI_IsFlagSet(facet, TimeFacet::RFC3339); bool filename = WI_IsFlagSet(facet, TimeFacet::Filename); char day_time_separator = useRFC3339 ? 'T' : (filename ? '-' : ' '); char time_field_separator = filename ? '-' : ':'; bool needsSeparator = false; char currentSeparator = '-'; for (const auto& info : { OutputTimePointFacetInfo{ TimeFacet::ShortYear, '-', [](Ctx ctx) { ctx.Stream << (ctx.LocalTime.tm_year - 100); }}, OutputTimePointFacetInfo{ TimeFacet::Year, '-', [](Ctx ctx) { ctx.Stream << (1900 + ctx.LocalTime.tm_year); }}, OutputTimePointFacetInfo{ TimeFacet::Month, '-', [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << (1 + ctx.LocalTime.tm_mon); }}, OutputTimePointFacetInfo{ TimeFacet::Day, day_time_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_mday; }}, OutputTimePointFacetInfo{ TimeFacet::Hour, time_field_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_hour; }}, OutputTimePointFacetInfo{ TimeFacet::Minute, time_field_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_min; }}, OutputTimePointFacetInfo{ TimeFacet::Second, '.', [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_sec; }}, OutputTimePointFacetInfo{ TimeFacet::Millisecond, '-', [](Ctx ctx) { // Get partial seconds auto sinceEpoch = ctx.Time.time_since_epoch(); auto leftoverMillis = duration_cast(sinceEpoch) - duration_cast(sinceEpoch); ctx.Stream << std::setw(3) << std::setfill('0') << leftoverMillis.count(); }}, OutputTimePointFacetInfo{ TimeFacet::RFC3339, '\0', [](Ctx ctx) { // RFC 3339 requires adding time zone info. // No need to bother getting the actual time zone as we don't need it. // -00:00 represents an unspecified time zone, not UTC. ctx.Stream << "00:00"; }}, }) { if (WI_AreAllFlagsSet(facet, info.Facet)) { if (needsSeparator) { stream << currentSeparator; } info.Action(context); needsSeparator = true; } // Getting this right for every mix of facets is probably not possible. // Future needs can dictate changes here. currentSeparator = info.FollowingSeparator; } } std::string TimePointToString(const std::chrono::system_clock::time_point& time, bool useRFC3339) { std::ostringstream stream; OutputTimePoint(stream, time, useRFC3339); return std::move(stream).str(); } std::string TimePointToString(const std::chrono::system_clock::time_point& time, TimeFacet facet) { std::ostringstream stream; OutputTimePoint(stream, time, facet); return std::move(stream).str(); } std::string GetCurrentTimeForFilename(bool shortTime) { return TimePointToString(std::chrono::system_clock::now(), (shortTime ? TimeFacet::ShortYearSecondPrecision : TimeFacet::Default) | TimeFacet::Filename); } std::string GetCurrentDateForARP() { auto now = std::chrono::system_clock::now(); std::time_t tt = std::chrono::system_clock::to_time_t(now); struct tm newTime; localtime_s(&newTime, &tt); std::stringstream ss; ss << std::put_time(&newTime, "%Y%m%d"); return ss.str(); } int64_t GetCurrentUnixEpoch() { static_assert(std::is_same_v, "time returns a 64-bit integer"); time_t now = time(nullptr); return static_cast(now); } int64_t ConvertSystemClockToUnixEpoch(const std::chrono::system_clock::time_point& time) { static_assert(std::is_same_v, "to_time_t returns a 64-bit integer"); time_t timeAsTimeT = std::chrono::system_clock::to_time_t(time); return static_cast(timeAsTimeT); } std::chrono::system_clock::time_point ConvertUnixEpochToSystemClock(int64_t epoch) { return std::chrono::system_clock::from_time_t(static_cast(epoch)); } std::chrono::system_clock::time_point ConvertFiletimeToSystemClock(const FILETIME& fileTime) { return winrt::clock::to_sys(winrt::clock::from_FILETIME(fileTime)); } std::chrono::system_clock::time_point GetTimePointFromVersion(const UInt64Version& version) { // Our custom format for converting UTC into a version is: // Major :: `Year` [1, 9999] // Minor :: `Month * 100 + Day` where Month [1, 12] and Day [1, 31] // Build :: `Hour * 100 + Minute` where Hour [1, 24] and Minute [0, 59] // Revision :: Milliseconds, but since no seconds are available we will disregard this tm versionTime{}; // Limit to the range supported by _mkgmtime64, which is 1970 to 3000 (hello to Y3K maintainers from 2023!) UINT64 majorVersion = version.Major(); if (majorVersion < 1970 || majorVersion > 3000) { return std::chrono::system_clock::time_point::min(); } versionTime.tm_year = static_cast(majorVersion) - 1900; UINT64 minorVersion = version.Minor(); UINT64 monthValue = minorVersion / 100; UINT64 dayValue = minorVersion % 100; if (monthValue < 1 || monthValue > 12 || dayValue < 1 || dayValue > 31) { return std::chrono::system_clock::time_point::min(); } versionTime.tm_mon = static_cast(monthValue) - 1; versionTime.tm_mday = static_cast(dayValue); UINT64 buildVersion = version.Build(); UINT64 hourValue = buildVersion / 100; UINT64 minuteValue = buildVersion % 100; if (hourValue < 1 || hourValue > 24 || minuteValue > 59) { return std::chrono::system_clock::time_point::min(); } versionTime.tm_hour = static_cast(hourValue) - 1; versionTime.tm_min = static_cast(minuteValue); return std::chrono::system_clock::from_time_t(_mkgmtime64(&versionTime)); } } ================================================ FILE: src/AppInstallerSharedLib/Errors.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" #include "Public/winget/Resources.h" namespace AppInstaller { namespace { // A simple struct to hold the data struct HResultData { HRESULT Value; std::string_view Symbol; std::string_view Description; bool operator<(const HResultData& other) const { return Value < other.Value; } }; // HRESULT information for our errors struct WinGetHResultInformation : public Errors::HResultInformation { constexpr WinGetHResultInformation(HRESULT value, std::string_view symbol, std::string_view unlocalizedDescription) : Errors::HResultInformation(value, symbol), m_unlocalizedDescription(unlocalizedDescription) {} constexpr WinGetHResultInformation(const HResultData& data) : Errors::HResultInformation(data.Value, data.Symbol), m_unlocalizedDescription(data.Description) {} Utility::LocIndString GetDescription() const override { auto localizedDescription = StringResource::TryResolveString(Utility::ConvertToUTF16(Symbol())); return localizedDescription ? std::move(localizedDescription).value() : Utility::LocIndString{ m_unlocalizedDescription }; } private: std::string_view m_unlocalizedDescription; }; // HRESULT information for our errors, with the unlocalized description only. struct WinGetHResultInformationUnlocalized : public Errors::HResultInformation { constexpr WinGetHResultInformationUnlocalized(HRESULT value, std::string_view symbol, std::string_view unlocalizedDescription) : Errors::HResultInformation(value, symbol), m_unlocalizedDescription(unlocalizedDescription) {} constexpr WinGetHResultInformationUnlocalized(const HResultData& data) : Errors::HResultInformation(data.Value, data.Symbol), m_unlocalizedDescription(data.Description) {} Utility::LocIndString GetDescription() const override { return Utility::LocIndString{ m_unlocalizedDescription }; } private: std::string_view m_unlocalizedDescription; }; // The information entry for an HRESULT not in the list (someone probably forgot to add an entry) struct UnknownHResultInformation : public Errors::HResultInformation { constexpr UnknownHResultInformation(HRESULT value) : Errors::HResultInformation(value) {} Utility::LocIndString GetDescription() const override { auto localizedDescription = StringResource::TryResolveString(StringResource::String::UnknownErrorCode); return localizedDescription ? std::move(localizedDescription).value() : Utility::LocIndString{ "Unknown error code"sv }; } }; #define WINGET_HRESULT_INFO(_name_,_description_) HResultData{ _name_, #_name_, _description_ } constexpr const HResultData s_wingetHResultData[] = { // Changes to any of these errors require the corresponding resource string in winget.resw to be updated. WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR, "Internal Error"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, "Invalid command line arguments"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_COMMAND_FAILED, "Executing command failed"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MANIFEST_FAILED, "Opening manifest failed"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED, "Cancellation signal received"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED, "Running ShellExecute failed"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION, "Cannot process manifest. The manifest version is higher than supported. Please update the client."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_DOWNLOAD_FAILED, "Downloading installer failed"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, "Cannot write to index; it is a higher schema version"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED, "The index is corrupt"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCES_INVALID, "The configured source information is corrupt"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, "The source name is already configured"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE, "The source type is invalid"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PACKAGE_IS_BUNDLE, "The MSIX file is a bundle, not a package"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING, "Data required by the source is missing"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER, "None of the installers are applicable for the current system"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH, "The installer file's hash does not match the manifest"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST, "The source name does not exist"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_ARG_ALREADY_EXISTS, "The source location is already configured under another name"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND, "No packages found"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_SOURCES_DEFINED, "No sources are configured"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND, "Multiple packages found matching the criteria"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND, "No manifest found matching the criteria"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_EXTENSION_PUBLIC_FAILED, "Failed to get Public folder from source package"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN, "Command requires administrator privileges to run"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE, "The source location is not secure"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY, "The Microsoft Store client is blocked by policy"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY, "The Microsoft Store app is blocked by policy"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED, "The feature is currently under development. It can be enabled using winget settings."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MSSTORE_INSTALL_FAILED, "Failed to install the Microsoft Store app"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD, "Failed to perform auto complete"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, "Failed to initialize YAML parser"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY, "Encountered an invalid YAML key"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, "Encountered a duplicate YAML key"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION, "Invalid YAML operation"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED, "Failed to build YAML doc"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE, "Invalid YAML emitter state"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA, "Invalid YAML data"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR, "LibYAML error"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_WARNING, "Manifest validation succeeded with warning"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_FAILURE, "Manifest validation failed"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST, "Manifest is invalid"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE, "No applicable update found"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE, "winget upgrade --all completed with failures"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED, "Installer failed security check"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH, "Download size does not match expected content length"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND, "Uninstall command not found"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED, "Running uninstall command failed"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_ICU_BREAK_ITERATOR_ERROR, "ICU break iterator error"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_ICU_CASEMAP_ERROR, "ICU casemap error"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_ICU_REGEX_ERROR, "ICU regex error"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED, "Failed to install one or more imported packages"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND, "Could not find one or more requested packages"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, "Json file is invalid"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_NOT_REMOTE, "The source location is not remote"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, "The configured rest source is not supported"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA, "Invalid data returned by rest source"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, "Operation is blocked by Group Policy"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_RESTAPI_INTERNAL_ERROR, "Rest API internal error"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, "Invalid rest source url"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_RESTAPI_UNSUPPORTED_MIME_TYPE, "Unsupported MIME type returned by rest API"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION, "Invalid rest source contract version"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE, "The source data is corrupted or tampered"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE, "Error reading from the stream"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED, "Package agreements were not agreed to"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR, "Error reading input in prompt"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST, "The search request is not supported by one or more sources"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND, "The rest API endpoint is not found."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED, "Failed to open the source."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED, "Source agreements were not agreed to"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH, "Header size exceeds the allowable limit of 1024 characters. Please reduce the size and try again."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MISSING_RESOURCE_FILE, "Missing resource file"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED, "Running MSI install failed"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT, "Arguments for msiexec are invalid"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES, "Failed to open one or more sources"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED, "Failed to validate dependencies"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MISSING_PACKAGE, "One or more package is missing"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INVALID_TABLE_COLUMN, "Invalid table column"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER, "The upgrade version is not newer than the installed version"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN, "Upgrade version is unknown and override is not specified"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_ICU_CONVERSION_ERROR, "ICU conversion error"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PORTABLE_INSTALL_FAILED, "Failed to install portable package"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PORTABLE_REPARSE_POINT_NOT_SUPPORTED, "Volume does not support reparse points"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PORTABLE_PACKAGE_ALREADY_EXISTS, "Portable package from a different source already exists."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PORTABLE_SYMLINK_PATH_IS_DIRECTORY, "Unable to create symlink, path points to a directory."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALLER_PROHIBITS_ELEVATION, "The installer cannot be run from an administrator context."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PORTABLE_UNINSTALL_FAILED, "Failed to uninstall portable package"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED, "Failed to validate DisplayVersion values against index."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT, "One or more arguments are not supported."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_BIND_WITH_EMBEDDED_NULL, "Embedded null characters are disallowed for SQLite"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND, "Failed to find the nested installer in the archive."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_EXTRACT_ARCHIVE_FAILED, "Failed to extract archive."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_INVALID_PATH, "Invalid relative file path to nested installer provided."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PINNED_CERTIFICATE_MISMATCH, "The server certificate did not match any of the expected values."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_LOCATION_REQUIRED, "Install location must be provided."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_ARCHIVE_SCAN_FAILED, "Archive malware scan failed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PACKAGE_ALREADY_INSTALLED, "Found at least one version of the package installed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PIN_ALREADY_EXISTS, "A pin already exists for the package."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PIN_DOES_NOT_EXIST, "There is no pin for the package."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_CANNOT_OPEN_PINNING_INDEX, "Unable to open the pin database."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MULTIPLE_INSTALL_FAILED, "One or more applications failed to install"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_MULTIPLE_UNINSTALL_FAILED, "One or more applications failed to uninstall"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE, "One or more queries did not return exactly one match"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PACKAGE_IS_PINNED, "The package has a pin that prevents upgrade."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB, "The package currently installed is the stub package"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED, "Application shutdown signal received"), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES, "Failed to download package dependencies."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED, "Failed to download package. Download for offline installation is prohibited."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SERVICE_UNAVAILABLE, "A required service is busy or unavailable. Try again later."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND, "The guid provided does not correspond to a valid resume state."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH, "The current client version did not match the client version of the saved state."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE, "The resume state data is invalid."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX, "Unable to open the checkpoint database."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_RESUME_LIMIT_EXCEEDED, "Exceeded max resume limit."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO, "Invalid authentication info."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED, "Authentication method not supported."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED, "Authentication failed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_AUTHENTICATION_INTERACTIVE_REQUIRED, "Authentication failed. Interactive authentication required."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_AUTHENTICATION_CANCELLED_BY_USER, "Authentication failed. User cancelled."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_AUTHENTICATION_INCORRECT_ACCOUNT, "Authentication failed. Authenticated account is not the desired account."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND, "Repair command not found."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_REPAIR_NOT_APPLICABLE, "Repair operation is not applicable."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_EXEC_REPAIR_FAILED, "Repair operation failed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_REPAIR_NOT_SUPPORTED, "The installer technology in use doesn't support repair."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED, "Repair operations involving administrator privileges are not permitted on packages installed within the user scope."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SQLITE_CONNECTION_TERMINATED, "The SQLite connection was terminated to prevent corruption."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED, "Failed to get Microsoft Store package catalog."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE, "No applicable Microsoft Store package found from Microsoft Store package catalog."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED, "Failed to get Microsoft Store package download information."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE, "No applicable Microsoft Store package download information found."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED, "Failed to retrieve Microsoft Store package license."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED, "The Microsoft Store package does not support download."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN, "Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account does not have the required privilege."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE, "Downloaded zero byte installer; ensure that your network connection is working properly."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED, "Failed installing one or more fonts."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_SUPPORTED, "Font file is not supported and cannot be installed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_FONT_ALREADY_INSTALLED, "Font package is already installed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_FOUND, "Font file not found."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED, "Font uninstall failed. The font may not be in a good state. Try uninstalling after a restart."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_FONT_VALIDATION_FAILED, "Font validation failed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_FONT_ROLLBACK_FAILED, "Font rollback failed. The font may not be in a good state. Try uninstalling after a restart."), // Install errors. WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE, "Application is currently running. Exit the application then try again."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS, "Another installation is already in progress. Try again later."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_FILE_IN_USE, "One or more file is being used. Exit the application then try again."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY, "This package has a dependency missing from your system."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_DISK_FULL, "There's no more space on your PC. Make space, then try again."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_INSUFFICIENT_MEMORY, "There's not enough memory available to install. Close other applications then try again."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_NO_NETWORK, "This application requires internet connectivity. Connect to a network then try again."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT, "This application encountered an error during installation. Contact support."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH, "Restart your PC to finish installation."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL, "Installation failed. Restart your PC then try again."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_INITIATED, "Your PC will restart to finish installation."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_CANCELLED_BY_USER, "You cancelled the installation."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_ALREADY_INSTALLED, "Another version of this application is already installed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_DOWNGRADE, "A higher version of this application is already installed."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY, "Organization policies are preventing installation. Contact your admin."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES, "Failed to install package dependencies."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE_BY_APPLICATION, "Application is currently in use by another application."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_INVALID_PARAMETER, "Invalid parameter."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED, "Package not supported by the system."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_UPGRADE_NOT_SUPPORTED, "The installer does not support upgrading an existing package."), WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_CUSTOM_ERROR, "Installation failed with a custom installer error."), // Status values for check package installed status results. // Partial success has the success bit(first bit) set to 0. WINGET_HRESULT_INFO(WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND, "The Apps and Features Entry for the package could not be found."), WINGET_HRESULT_INFO(WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND, "The install location could not be found."), WINGET_HRESULT_INFO(WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH, "The hash of the existing file did not match."), WINGET_HRESULT_INFO(WINGET_INSTALLED_STATUS_FILE_NOT_FOUND, "File not found."), WINGET_HRESULT_INFO(WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR, "The file could not be accessed."), // Configuration Errors WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE, "The configuration file is invalid."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_INVALID_YAML, "The YAML syntax is invalid."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, "A configuration field has an invalid type."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, "The configuration has an unknown version."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_SET_APPLY_FAILED, "An error occurred while applying the configuration."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, "The configuration contains a duplicate identifier."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_MISSING_DEPENDENCY, "The configuration is missing a dependency."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, "The configuration has an unsatisfied dependency."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_ASSERTION_FAILED, "An assertion for the configuration unit failed."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_MANUALLY_SKIPPED, "The configuration was manually skipped."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_WARNING_NOT_ACCEPTED, "The user declined to continue execution."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_SET_DEPENDENCY_CYCLE, "The dependency graph contains a cycle which cannot be resolved."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, "The configuration has an invalid field value."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_MISSING_FIELD, "The configuration is missing a field."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_TEST_FAILED, "Some of the configuration units failed while testing their state."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_TEST_NOT_RUN, "Configuration state was not tested."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_GET_FAILED, "The configuration unit failed getting its properties."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_HISTORY_ITEM_NOT_FOUND, "The specified configuration could not be found."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_PARAMETER_INTEGRITY_BOUNDARY, "Parameter cannot be passed across integrity boundary."), // Configuration Processor Errors WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_NOT_INSTALLED, "The configuration unit was not installed."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_NOT_FOUND_REPOSITORY, "The configuration unit could not be found."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_MULTIPLE_MATCHES, "Multiple matches were found for the configuration unit; specify the module to select the correct one."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_INVOKE_GET, "The configuration unit failed while attempting to get the current system state."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_INVOKE_TEST, "The configuration unit failed while attempting to test the current system state."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_INVOKE_SET, "The configuration unit failed while attempting to apply the desired state."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_MODULE_CONFLICT, "The module for the configuration unit is available in multiple locations with the same version."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE, "Loading the module for the configuration unit failed."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_INVOKE_INVALID_RESULT, "The configuration unit returned an unexpected result during execution."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_SETTING_CONFIG_ROOT, "A unit contains a setting that requires the config root."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE_ADMIN, "Loading the module for the configuration unit failed because it requires administrator privileges to run."), WINGET_HRESULT_INFO(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR, "Operation is not supported by the configuration processor."), // Errors without the error bit set WINGET_HRESULT_INFO(WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE, "The install location is not applicable."), WINGET_HRESULT_INFO(WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, "The file was found but the hash was not checked."), }; // Map externally defined HRESULTs to error messages we want to use here constexpr const HResultData s_externalHResultData[] = { // Changes to any of these errors require the corresponding resource string in winget.resw to be updated. HResultData{ static_cast(0x803FB103), "StoreInstall_PackageNotAvailableForCurrentSystem", "The package is not compatible with the current Windows version or platform." }, HResultData{ static_cast(0x803FB104), "StoreInstall_PackageNotAvailableForCurrentSystem", "The package is not compatible with the current Windows version or platform." }, HResultData{ static_cast(0x803FB106), "StoreInstall_PackageNotAvailableForCurrentSystem", "The package is not compatible with the current Windows version or platform." }, }; template const HResultData* FindHResultData(HRESULT value, const HResultData (&dataArray)[ArraySize]) { auto itr = std::lower_bound(std::cbegin(dataArray), std::cend(dataArray), HResultData{ value }); if (itr != std::cend(dataArray) && itr->Value == value) { return itr; } return nullptr; } const HResultData* FindWinGetHResultData(HRESULT value) { return FindHResultData(value, s_wingetHResultData); } const HResultData* FindExternalHResultData(HRESULT value) { return FindHResultData(value, s_externalHResultData); } Utility::LocIndString GetMessageForAppInstallerHR(HRESULT hr) { const HResultData* data = FindWinGetHResultData(hr); return data ? WinGetHResultInformation(*data).GetDescription() : UnknownHResultInformation(hr).GetDescription(); } void GetUserPresentableMessageForHR(std::ostringstream& strstr, HRESULT hr) { strstr << "0x" << Logging::SetHRFormat << hr << " : "; if (HRESULT_FACILITY(hr) == APPINSTALLER_CLI_ERROR_FACILITY) { strstr << GetMessageForAppInstallerHR(hr); } else { const HResultData* data = FindExternalHResultData(hr); if (data) { strstr << WinGetHResultInformation(*data).GetDescription(); } else { strstr << std::system_category().message(hr); } } } } std::string GetUserPresentableMessage(const wil::ResultException& re) { const auto& info = re.GetFailureInfo(); std::ostringstream strstr; // We assume that if the exception has a message, that message is relevant to show to the user. if (info.pszMessage) { strstr << Utility::ConvertToUTF8(info.pszMessage) << std::endl; } GetUserPresentableMessageForHR(strstr, re.GetErrorCode()); return strstr.str(); } std::string GetUserPresentableMessage(const std::exception& e) { return e.what(); } std::string GetUserPresentableMessage(HRESULT hr) { std::ostringstream strstr; GetUserPresentableMessageForHR(strstr, hr); return strstr.str(); } #ifndef WINGET_DISABLE_FOR_FUZZING std::string GetUserPresentableMessage(const winrt::hresult_error& hre) { std::ostringstream strstr; GetUserPresentableMessageForHR(strstr, hre.code()); return strstr.str(); } #endif namespace Errors { constexpr HResultInformation::HResultInformation(HRESULT value) : m_value(value) {} constexpr HResultInformation::HResultInformation(HRESULT value, std::string_view symbol) : m_value(value), m_symbol(symbol) {} HRESULT HResultInformation::Value() const { return m_value; } bool HResultInformation::operator<(const HResultInformation& other) const { return m_value < other.m_value; } Utility::LocIndView HResultInformation::Symbol() const { return Utility::LocIndView{ m_symbol }; } Utility::LocIndString HResultInformation::GetDescription() const { if (HRESULT_FACILITY(m_value) == APPINSTALLER_CLI_ERROR_FACILITY) { // In case external code attempts to construct HResultInformation directly return GetMessageForAppInstallerHR(m_value); } else { return Utility::LocIndString{ std::system_category().message(m_value) }; } } std::unique_ptr HResultInformation::Find(HRESULT value) { if (HRESULT_FACILITY(value) == APPINSTALLER_CLI_ERROR_FACILITY) { const HResultData* data = FindWinGetHResultData(value); if (data) { return std::make_unique(*data); } else { return std::make_unique(value); } } else { return std::make_unique(value); } } std::vector> HResultInformation::Find(std::string_view value) { std::vector> result; auto addToResultIf = [&](auto predicate) { for (const HResultData& data : s_wingetHResultData) { if (predicate(data) && std::none_of(result.begin(), result.end(), [&](const std::unique_ptr& info) { return info->Value() == data.Value; })) { result.emplace_back(std::make_unique(data)); } } }; addToResultIf([&](const HResultData& data) { return Utility::CaseInsensitiveEquals(data.Symbol, value); }); addToResultIf([&](const HResultData& data) { return Utility::CaseInsensitiveContainsSubstring(data.Symbol, value); }); addToResultIf([&](const HResultData& data) { return Utility::CaseInsensitiveContainsSubstring(data.Description, value); }); return result; } std::vector> GetWinGetErrors() { std::vector> result; result.reserve(ARRAYSIZE(s_wingetHResultData)); for (const HResultData& data : s_wingetHResultData) { result.emplace_back(std::make_unique(data)); } return result; } } } ================================================ FILE: src/AppInstallerSharedLib/Filesystem.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/Filesystem.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerLogging.h" #include "Public/winget/Runtime.h" using namespace std::chrono_literals; using namespace std::string_view_literals; using namespace AppInstaller::Runtime; namespace AppInstaller::Filesystem { namespace anon { constexpr std::string_view s_AppDataDir_Settings = "Settings"sv; constexpr std::string_view s_AppDataDir_State = "State"sv; constexpr std::string_view s_LocalAppDataEnvironmentVariable = "%LOCALAPPDATA%"; // Contains the information about an ACE entry for a given principal. struct ACEDetails { ACEPrincipal Principal; PSID SID; TRUSTEE_TYPE TrusteeType; }; DWORD AccessPermissionsFrom(ACEPermissions permissions) { DWORD result = 0; if (permissions == ACEPermissions::All) { result |= GENERIC_ALL; } else { if (WI_IsFlagSet(permissions, ACEPermissions::Read)) { result |= GENERIC_READ; } if (WI_IsFlagSet(permissions, ACEPermissions::Write)) { result |= GENERIC_WRITE | FILE_DELETE_CHILD; } if (WI_IsFlagSet(permissions, ACEPermissions::Execute)) { result |= GENERIC_EXECUTE; } } return result; } // Gets the path to the appdata root. // *Only used by non packaged version!* std::filesystem::path GetPathToAppDataRoot(bool anonymize) { std::filesystem::path result = anonymize ? s_LocalAppDataEnvironmentVariable : GetKnownFolderPath(FOLDERID_LocalAppData); result /= "Microsoft/WinGet"; return result; } // Gets the path to the app data relative directory. std::filesystem::path GetPathToAppDataDir(const std::filesystem::path& relative, bool anonymize) { THROW_HR_IF(E_INVALIDARG, !relative.has_relative_path()); THROW_HR_IF(E_INVALIDARG, relative.has_root_path()); THROW_HR_IF(E_INVALIDARG, !relative.has_filename()); std::filesystem::path result = GetPathToAppDataRoot(anonymize); result /= relative; return result; } } DWORD GetVolumeInformationFlagsByHandle(HANDLE anyFileHandle) { DWORD flags = 0; wchar_t fileSystemName[MAX_PATH]; THROW_LAST_ERROR_IF(!GetVolumeInformationByHandleW( anyFileHandle, /*hFile*/ NULL, /*lpVolumeNameBuffer*/ 0, /*nVolumeNameSize*/ NULL, /*lpVolumeSerialNumber*/ NULL, /*lpMaximumComponentLength*/ &flags, /*lpFileSystemFlags*/ fileSystemName, /*lpFileSystemNameBuffer*/ MAX_PATH /*nFileSystemNameSize*/)); // Vista and older does not report all flags, fix them up here if (!(flags & FILE_SUPPORTS_HARD_LINKS) && !_wcsicmp(fileSystemName, L"NTFS")) { flags |= FILE_SUPPORTS_HARD_LINKS | FILE_SUPPORTS_EXTENDED_ATTRIBUTES | FILE_SUPPORTS_OPEN_BY_FILE_ID | FILE_SUPPORTS_USN_JOURNAL; } return flags; } DWORD GetVolumeInformationFlags(const std::filesystem::path& anyPath) { wil::unique_hfile fileHandle{ CreateFileW( anyPath.c_str(), /*lpFileName*/ 0, /*dwDesiredAccess*/ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, /*dwShareMode*/ NULL, /*lpSecurityAttributes*/ OPEN_EXISTING, /*dwCreationDisposition*/ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, /*dwFlagsAndAttributes*/ NULL /*hTemplateFile*/) }; THROW_LAST_ERROR_IF(fileHandle.get() == INVALID_HANDLE_VALUE); return GetVolumeInformationFlagsByHandle(fileHandle.get()); } bool SupportsNamedStreams(const std::filesystem::path& path) { return (GetVolumeInformationFlags(path) & FILE_NAMED_STREAMS) != 0; } bool SupportsHardLinks(const std::filesystem::path& path) { return (GetVolumeInformationFlags(path) & FILE_SUPPORTS_HARD_LINKS) != 0; } bool SupportsReparsePoints(const std::filesystem::path& path) { return (GetVolumeInformationFlags(path) & FILE_SUPPORTS_REPARSE_POINTS) != 0; } bool PathEscapesBaseDirectory(const std::filesystem::path& target, const std::filesystem::path& base) { const auto& targetPath = std::filesystem::weakly_canonical(target); const auto& basePath = std::filesystem::weakly_canonical(base); auto [a, b] = std::mismatch(targetPath.begin(), targetPath.end(), basePath.begin(), basePath.end()); return (b != basePath.end()); } // Complicated rename algorithm due to somewhat arbitrary failures. // 1. First, try to rename. // 2. Then, create an empty file for the target, and attempt to rename. // 3. Then, try repeatedly for 500ms in case it is a timing thing. // 4. Attempt to use a hard link if available. // 5. Copy the file if nothing else has worked so far. void RenameFile(const std::filesystem::path& from, const std::filesystem::path& to) { // 1. First, try to rename. try { // std::filesystem::rename() handles motw correctly if applicable. std::filesystem::rename(from, to); return; } CATCH_LOG(); // 2. Then, create an empty file for the target, and attempt to rename. // This seems to fix things in certain cases, so we do it. try { { std::ofstream targetFile{ to }; } std::filesystem::rename(from, to); return; } CATCH_LOG(); // 3. Then, try repeatedly for 500ms in case it is a timing thing. for (int i = 0; i < 5; ++i) { try { std::this_thread::sleep_for(100ms); std::filesystem::rename(from, to); return; } CATCH_LOG(); } // 4. Attempt to use a hard link if available. if (SupportsHardLinks(from)) { try { // Create a hard link to the file; the installer will be left in the temp directory afterward // but it is better to succeed the operation and leave a file around than to fail. // First we have to remove the target file as the function will not overwrite. std::filesystem::remove(to); std::filesystem::create_hard_link(from, to); return; } CATCH_LOG(); } // 5. Copy the file if nothing else has worked so far. // Create a copy of the file; the installer will be left in the temp directory afterward // but it is better to succeed the operation and leave a file around than to fail. std::filesystem::copy_file(from, to, std::filesystem::copy_options::overwrite_existing); } #ifndef AICLI_DISABLE_TEST_HOOKS static bool* s_CreateSymlinkResult_TestHook_Override = nullptr; void TestHook_SetCreateSymlinkResult_Override(bool* status) { s_CreateSymlinkResult_TestHook_Override = status; } #endif bool CreateSymlink(const std::filesystem::path& target, const std::filesystem::path& link) { #ifndef AICLI_DISABLE_TEST_HOOKS if (s_CreateSymlinkResult_TestHook_Override) { return *s_CreateSymlinkResult_TestHook_Override; } #endif try { std::filesystem::create_symlink(target, link); return true; } catch (std::filesystem::filesystem_error& error) { if (error.code().value() == ERROR_PRIVILEGE_NOT_HELD) { return false; } else { throw; } } } bool VerifySymlink(const std::filesystem::path& symlink, const std::filesystem::path& target) { const std::filesystem::path& symlinkTargetPath = std::filesystem::weakly_canonical(symlink); return symlinkTargetPath == std::filesystem::weakly_canonical(target); } void AppendExtension(std::filesystem::path& target, const std::string& value) { if (target.extension() != value) { target += value; } } bool SymlinkExists(const std::filesystem::path& symlinkPath) { return std::filesystem::is_symlink(std::filesystem::symlink_status(symlinkPath)); } std::filesystem::path GetExpandedPath(const std::string& path) { std::string trimPath = path; Utility::Trim(trimPath); try { return std::filesystem::weakly_canonical(Utility::ExpandEnvironmentVariables(Utility::ConvertToUTF16(trimPath))); } catch (...) { return Utility::ConvertToUTF16(path); } } bool ReplaceCommonPathPrefix(std::filesystem::path& source, const std::filesystem::path& prefix, std::string_view replacement) { auto prefixItr = prefix.begin(); auto sourceItr = source.begin(); while (prefixItr != prefix.end() && sourceItr != source.end()) { if (!Utility::ICUCaseInsensitiveEquals(prefixItr->u8string(), sourceItr->u8string())) { break; } ++prefixItr; ++sourceItr; } // Only replace source if we found all of prefix if (prefixItr == prefix.end()) { std::filesystem::path temp{ replacement }; for (; sourceItr != source.end(); ++sourceItr) { temp /= *sourceItr; } source = std::move(temp); return true; } return false; } std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id) { wil::unique_cotaskmem_string knownFolder = nullptr; THROW_IF_FAILED(SHGetKnownFolderPath(id, KF_FLAG_NO_ALIAS | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_PACKAGE_REDIRECTION, NULL, &knownFolder)); return knownFolder.get(); } bool IsSameVolume(const std::filesystem::path& path1, const std::filesystem::path& path2) { WCHAR volumeName1[MAX_PATH]; WCHAR volumeName2[MAX_PATH]; // Note: GetVolumePathNameW will return false if the volume drive does not exist. if (!GetVolumePathNameW(path1.c_str(), volumeName1, MAX_PATH) || !GetVolumePathNameW(path2.c_str(), volumeName2, MAX_PATH)) { return false; } return Utility::ICUCaseInsensitiveEquals(Utility::ConvertToUTF8(volumeName1), Utility::ConvertToUTF8(volumeName2)); } bool IsParentPath(const std::filesystem::path& path, const std::filesystem::path& parentPath) { return std::filesystem::weakly_canonical(path.parent_path()) == std::filesystem::weakly_canonical(parentPath); } void PathDetails::SetOwner(ACEPrincipal owner) { Owner = owner; ACL[owner] = ACEPermissions::All; } bool PathDetails::ShouldApplyACL() const { // Could be expanded to actually check the current owner/ACL on the path, but isn't worth it currently return !ACL.empty(); } void PathDetails::ApplyACL() const { bool hasCurrentUser = ACL.count(ACEPrincipal::CurrentUser) != 0; bool hasSystem = ACL.count(ACEPrincipal::System) != 0; // Configuring permissions for both CurrentUser and SYSTEM while not having owner set as one of them is not valid because // below we use only the owner permissions in the case of running as SYSTEM. if ((hasCurrentUser && hasSystem) && IsRunningAsSystem() && (!Owner || (Owner.value() != ACEPrincipal::CurrentUser && Owner.value() != ACEPrincipal::System))) { THROW_HR(HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } auto userToken = wil::get_token_information(); auto adminSID = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); auto systemSID = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID); PSID ownerSID = nullptr; anon::ACEDetails aceDetails[] = { { ACEPrincipal::CurrentUser, userToken->User.Sid, TRUSTEE_IS_USER }, { ACEPrincipal::Admins, adminSID.get(), TRUSTEE_IS_WELL_KNOWN_GROUP}, { ACEPrincipal::System, systemSID.get(), TRUSTEE_IS_USER}, }; ULONG entriesCount = 0; std::array explicitAccess; // If the current user is SYSTEM, we want to take either the owner or the only configured set of permissions. // The check above should prevent us from getting into situations outside of the ones below. std::optional principalToIgnore; if (hasCurrentUser && hasSystem && EqualSid(userToken->User.Sid, systemSID.get())) { principalToIgnore = (Owner.value() == ACEPrincipal::CurrentUser ? ACEPrincipal::System : ACEPrincipal::CurrentUser); } for (const auto& ace : aceDetails) { if (principalToIgnore && principalToIgnore.value() == ace.Principal) { continue; } if (Owner && Owner.value() == ace.Principal) { ownerSID = ace.SID; } auto itr = ACL.find(ace.Principal); if (itr != ACL.end()) { EXPLICIT_ACCESS_W& entry = explicitAccess[entriesCount++]; entry = {}; entry.grfAccessPermissions = anon::AccessPermissionsFrom(itr->second); entry.grfAccessMode = SET_ACCESS; entry.grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; entry.Trustee.pMultipleTrustee = nullptr; entry.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; entry.Trustee.TrusteeForm = TRUSTEE_IS_SID; entry.Trustee.TrusteeType = ace.TrusteeType; entry.Trustee.ptstrName = reinterpret_cast(ace.SID); } } wil::unique_any acl; THROW_IF_WIN32_ERROR(SetEntriesInAclW(entriesCount, explicitAccess.data(), nullptr, &acl)); std::wstring path = Path.wstring(); SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION; if (ownerSID) { securityInformation |= OWNER_SECURITY_INFORMATION; } DWORD result = SetNamedSecurityInfoW(&path[0], SE_FILE_OBJECT, securityInformation, ownerSID, nullptr, acl.get(), nullptr); // We can be denied access attempting to set the owner when the owner is already correct. // Determine if the owner is correct; if so, try again without attempting to set the owner. if (result == ERROR_ACCESS_DENIED && ownerSID) { wil::unique_hlocal_security_descriptor securityDescriptor; PSID currentOwnerSID = nullptr; DWORD getResult = GetNamedSecurityInfoW(&path[0], SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, ¤tOwnerSID, nullptr, nullptr, nullptr, &securityDescriptor); if (SUCCEEDED_WIN32_LOG(getResult) && currentOwnerSID && EqualSid(currentOwnerSID, ownerSID)) { result = SetNamedSecurityInfoW(&path[0], SE_FILE_OBJECT, securityInformation & ~OWNER_SECURITY_INFORMATION, nullptr, nullptr, acl.get(), nullptr); } } THROW_IF_WIN32_ERROR(result); } std::filesystem::path InitializeAndGetPathTo(PathDetails&& details) { if (details.Create) { if (details.Path.is_absolute()) { if (std::filesystem::exists(details.Path) && !std::filesystem::is_directory(details.Path)) { std::filesystem::remove(details.Path); } std::filesystem::create_directories(details.Path); // Set the ACLs on the directory if needed. We do this after creating the directory because an attacker could // have created the directory beforehand so we must be able to place the correct ACL on any directory or fail // to operate. if (details.ShouldApplyACL()) { details.ApplyACL(); } } else { AICLI_LOG(Core, Warning, << "InitializeAndGetPathTo directory creation requested for path that was not absolute: " << details.Path); } } return std::move(details.Path); } PathDetails GetPathDetailsFor(PathName path, bool forDisplay) { PathDetails result; // We should not create directories by default when they are retrieved for display purposes. result.Create = !forDisplay; switch (path) { case PathName::UnpackagedLocalStateRoot: result.Path = anon::GetPathToAppDataDir(anon::s_AppDataDir_State, forDisplay); result.SetOwner(ACEPrincipal::CurrentUser); result.ACL[ACEPrincipal::System] = ACEPermissions::All; result.ACL[ACEPrincipal::Admins] = ACEPermissions::All; break; case PathName::UnpackagedSettingsRoot: result.Path = anon::GetPathToAppDataDir(anon::s_AppDataDir_Settings, forDisplay); result.SetOwner(ACEPrincipal::CurrentUser); result.ACL[ACEPrincipal::System] = ACEPermissions::All; result.ACL[ACEPrincipal::Admins] = ACEPermissions::All; break; default: THROW_HR(E_UNEXPECTED); } return result; } std::filesystem::path GetExecutablePathForProcess(HANDLE process) { wil::unique_cotaskmem_string imageName = nullptr; if (SUCCEEDED(wil::QueryFullProcessImageNameW(process, 0, imageName)) && (imageName.get() != nullptr)) { return imageName.get(); } return {}; } std::vector GetFileInfoFor(const std::filesystem::path& directory) { std::vector result; for (const auto& file : std::filesystem::directory_iterator{ directory }) { if (file.is_regular_file()) { result.emplace_back(FileInfo{ file.path(), file.last_write_time(), file.file_size() }); } } return result; } void FilterToFilesExceedingLimits(std::vector& files, const FileLimits& limits) { auto now = std::filesystem::file_time_type::clock::now(); std::chrono::hours ageLimit = limits.Age; static_assert(sizeof(uintmax_t) >= 8); uintmax_t totalSizeLimit = static_cast(limits.TotalSizeInMB) << 20; size_t countLimit = limits.Count; // Sort with oldest first so that we can work backward to find the cutoff std::sort(files.begin(), files.end(), [](const FileInfo& a, const FileInfo& b) { return a.LastWriteTime < b.LastWriteTime; }); // Walk the list backward until we find the first entry that goes over one of the limits size_t i = files.size(); uintmax_t totalSize = 0; for (; i > 0; --i) { const FileInfo& current = files[i - 1]; if (totalSizeLimit != 0) { totalSize += current.Size; if (totalSize > totalSizeLimit) { break; } } if (countLimit != 0 && (files.size() - i + 1) > countLimit) { break; } if (ageLimit != 0h && now - current.LastWriteTime > ageLimit) { break; } } files.resize(i); } } ================================================ FILE: src/AppInstallerSharedLib/GroupPolicy.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/GroupPolicy.h" #include "AppInstallerLogging.h" using namespace AppInstaller::StringResource; namespace AppInstaller::Settings { namespace { const GroupPolicy& InstanceInternal(std::optional overridePolicy = {}) { const static GroupPolicy s_groupPolicy{ Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, "Software\\Policies\\Microsoft\\Windows\\AppInstaller") }; static GroupPolicy* s_override = nullptr; if (overridePolicy.has_value()) { s_override = overridePolicy.value(); } return (s_override ? *s_override : s_groupPolicy); } std::optional GetRegistryValueObject(const Registry::Key& key, const std::string_view valueName) { if (!key) { // Key does not exist; there's nothing to return return std::nullopt; } return key[valueName]; } template std::optional().GetValue())> GetRegistryValueData(const Registry::Value& regValue, const std::string_view valueName) { auto value = regValue.TryGetValue(); if (!value.has_value()) { AICLI_LOG(Core, Warning, << "Value for policy '" << valueName << "' does not have expected type"); return std::nullopt; } return std::move(value.value()); } template std::optional().GetValue())> GetRegistryValueData(const Registry::Key& key, const std::string_view valueName) { auto regValue = GetRegistryValueObject(key, valueName); if (!regValue.has_value()) { // Value does not exist; there's nothing to return return std::nullopt; } return GetRegistryValueData(regValue.value(), valueName); } std::optional RegistryValueIsTrue(const Registry::Key& key, std::string_view valueName) { auto intValue = GetRegistryValueData(key, valueName); if (!intValue.has_value()) { return std::nullopt; } AICLI_LOG(Core, Verbose, << "Found policy '" << valueName << "', Value: " << *intValue); return (bool)*intValue; } PolicyState GetStateInternal(const Registry::Key& key, TogglePolicy::Policy policy) { // Default to not configured if there is no policy for this if (policy == TogglePolicy::Policy::None) { return PolicyState::NotConfigured; } auto togglePolicy = TogglePolicy::GetPolicy(policy); // Policies are not configured if there is no registry value. auto setting = RegistryValueIsTrue(key, togglePolicy.RegValueName()); if (!setting.has_value()) { return PolicyState::NotConfigured; } // Return flag as-is or invert depending on the policy return *setting ? PolicyState::Enabled : PolicyState::Disabled; } template void Validate(const Registry::Key& policiesKey, GroupPolicy::ValuePoliciesMap& policies) { auto value = details::ValuePolicyMapping

::ReadAndValidate(policiesKey); if (value.has_value()) { policies.Add

(std::move(*value)); } } template <> void Validate(const Registry::Key&, GroupPolicy::ValuePoliciesMap&) {}; template void ValidateAllValuePolicies( const Registry::Key& policiesKey, GroupPolicy::ValuePoliciesMap& policies, std::index_sequence) { // Use folding to call each policy validate function. (FoldHelper{}, ..., Validate(P)>(policiesKey, policies)); } // Reads a list from a Group Policy. // The list is stored in a sub-key of the policies key, and each value in that key is a list item. // Cases not considered by this function because we don't use them: // - When the list is in an arbitrary key, not a sub key. // - When the list values are mixed with other values and are identified by a prefix in their names. // - When the value names are relevant. template std::optional::value_t> ReadList(const Registry::Key& policiesKey) { using Mapping = details::ValuePolicyMapping

; auto listKey = policiesKey.SubKey(Mapping::KeyName); if (!listKey.has_value()) { return std::nullopt; } typename Mapping::value_t items; for (const auto& value : listKey->Values()) { std::optional potentialValue = value.Value(); if (potentialValue) { auto item = Mapping::ReadAndValidateItem(potentialValue.value()); if (item.has_value()) { items.emplace_back(std::move(item.value())); } else { AICLI_LOG(Core, Warning, << "Failed to read Group Policy list value. Policy [" << Mapping::KeyName << "], Value [" << value.Name() << ']'); } } else { AICLI_LOG(Core, Verbose, << "Group Policy list value not found. Policy [" << Mapping::KeyName << "], Value [" << value.Name() << ']'); } } return items; } std::optional ReadSourceFromRegistryValue(const Registry::Value& item) { auto jsonString = item.TryGetValue(); if (!jsonString.has_value()) { AICLI_LOG(Core, Warning, << "Registry value is not a string"); return std::nullopt; } int stringLength = static_cast(jsonString->length()); Json::Value sourceJson; Json::CharReaderBuilder charReaderBuilder; const std::unique_ptr jsonReader(charReaderBuilder.newCharReader()); Json::String jsonErrors; if (!jsonReader->parse(jsonString->c_str(), jsonString->c_str() + stringLength, &sourceJson, &jsonErrors)) { AICLI_LOG(Core, Warning, << "Registry value does not contain a valid JSON: " << jsonErrors); return std::nullopt; } SourceFromPolicy source; auto readSourceAttribute = [&](const std::string& name, std::string SourceFromPolicy::* member) { if (sourceJson.isMember(name) && sourceJson[name].isString()) { source.*member = sourceJson[name].asString(); return true; } else { AICLI_LOG(Core, Warning, << "Source JSON does not contain a string value for " << name); return false; } }; // All required fields should be read here. bool allRead = readSourceAttribute("Name", &SourceFromPolicy::Name) && readSourceAttribute("Arg", &SourceFromPolicy::Arg) && readSourceAttribute("Type", &SourceFromPolicy::Type) && readSourceAttribute("Data", &SourceFromPolicy::Data) && readSourceAttribute("Identifier", &SourceFromPolicy::Identifier); if (!allRead) { return std::nullopt; } #ifndef AICLI_DISABLE_TEST_HOOKS // Enable certificate pinning configuration through GP sources for testing const std::string pinningConfigurationName = "CertificatePinning"; if (sourceJson.isMember(pinningConfigurationName)) { source.PinningConfiguration = Certificates::PinningConfiguration(source.Name); if (!source.PinningConfiguration.LoadFrom(sourceJson[pinningConfigurationName])) { return std::nullopt; } } #endif // TrustLevel, Explicit, and Priority are optional policy fields with default values. const std::string trustLevelName = "TrustLevel"; if (sourceJson.isMember(trustLevelName) && sourceJson[trustLevelName].isArray()) { const Json::Value in = sourceJson[trustLevelName]; std::vector result; result.reserve(in.size()); std::transform(in.begin(), in.end(), std::back_inserter(result), [](const auto& e) { return e.asString(); }); source.TrustLevel = result; } const std::string explicitName = "Explicit"; if (sourceJson.isMember(explicitName) && sourceJson[explicitName].isBool()) { source.Explicit = sourceJson[explicitName].asBool(); } const std::string priorityName = "Priority"; if (sourceJson.isMember(priorityName) && sourceJson[priorityName].isInt()) { source.Priority = sourceJson[priorityName].asInt(); } return source; } } namespace details { #define POLICY_MAPPING_DEFAULT_READ(_policy_) \ std::optional::value_t> ValuePolicyMapping<_policy_>::ReadAndValidate(const Registry::Key& policiesKey) \ { \ using Mapping = ValuePolicyMapping<_policy_>; \ return GetRegistryValueData(policiesKey, Mapping::ValueName); \ } #define POLICY_MAPPING_DEFAULT_LIST_READ(_policy_) \ std::optional::value_t> ValuePolicyMapping<_policy_>::ReadAndValidate(const Registry::Key& policiesKey) \ { \ return ReadList<_policy_>(policiesKey); \ } POLICY_MAPPING_DEFAULT_LIST_READ(ValuePolicy::AdditionalSources); POLICY_MAPPING_DEFAULT_LIST_READ(ValuePolicy::AllowedSources); POLICY_MAPPING_DEFAULT_READ(ValuePolicy::DefaultProxy); std::nullopt_t ValuePolicyMapping::ReadAndValidate(const Registry::Key&) { return std::nullopt; } std::optional ValuePolicyMapping::ReadAndValidate(const Registry::Key& policiesKey) { // This policy used to have another name in the registry. // Try to read first with the current name, and if it's not present // check if the old name is present. using Mapping = ValuePolicyMapping; auto regValueWithCurrentName = GetRegistryValueObject(policiesKey, Mapping::ValueName); if (regValueWithCurrentName.has_value()) { // We use the current name even if it doesn't have valid data. return GetRegistryValueData(regValueWithCurrentName.value(), Mapping::ValueName); } else { return GetRegistryValueData(policiesKey, "SourceAutoUpdateIntervalInMinutes"sv); } } std::optional ValuePolicyMapping::ReadAndValidateItem(const Registry::Value& item) { return ReadSourceFromRegistryValue(item); } std::optional ValuePolicyMapping::ReadAndValidateItem(const Registry::Value& item) { return ReadSourceFromRegistryValue(item); } } TogglePolicy TogglePolicy::GetPolicy(TogglePolicy::Policy policy) { switch (policy) { case TogglePolicy::Policy::WinGet: return TogglePolicy(policy, "EnableAppInstaller"sv, String::PolicyEnableWinGet); case TogglePolicy::Policy::Settings: return TogglePolicy(policy, "EnableSettings"sv, String::PolicyEnableWingetSettings); case TogglePolicy::Policy::ExperimentalFeatures: return TogglePolicy(policy, "EnableExperimentalFeatures"sv, String::PolicyEnableExperimentalFeatures); case TogglePolicy::Policy::LocalManifestFiles: return TogglePolicy(policy, "EnableLocalManifestFiles"sv, String::PolicyEnableLocalManifests); case TogglePolicy::Policy::HashOverride: return TogglePolicy(policy, "EnableHashOverride"sv, String::PolicyEnableHashOverride); case TogglePolicy::Policy::LocalArchiveMalwareScanOverride: return TogglePolicy(policy, "EnableLocalArchiveMalwareScanOverride"sv, String::PolicyEnableLocalArchiveMalwareScanOverride); case TogglePolicy::Policy::DefaultSource: return TogglePolicy(policy, "EnableDefaultSource"sv, String::PolicyEnableDefaultSource); case TogglePolicy::Policy::MSStoreSource: return TogglePolicy(policy, "EnableMicrosoftStoreSource"sv, String::PolicyEnableMSStoreSource); case TogglePolicy::Policy::FontSource: return TogglePolicy(policy, "EnableFontSource"sv, String::PolicyEnableFontSource); case TogglePolicy::Policy::AdditionalSources: return TogglePolicy(policy, "EnableAdditionalSources"sv, String::PolicyAdditionalSources); case TogglePolicy::Policy::AllowedSources: return TogglePolicy(policy, "EnableAllowedSources"sv, String::PolicyAllowedSources); case TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore: return TogglePolicy(policy, "EnableBypassCertificatePinningForMicrosoftStore"sv, String::PolicyEnableBypassCertificatePinningForMicrosoftStore); case TogglePolicy::Policy::WinGetCommandLineInterfaces: return TogglePolicy(policy, "EnableWindowsPackageManagerCommandLineInterfaces"sv, String::PolicyEnableWindowsPackageManagerCommandLineInterfaces); case TogglePolicy::Policy::Configuration: return TogglePolicy(policy, "EnableWindowsPackageManagerConfiguration"sv, String::PolicyEnableWinGetConfiguration); case TogglePolicy::Policy::ProxyCommandLineOptions: return TogglePolicy(policy, "EnableWindowsPackageManagerProxyCommandLineOptions"sv, String::PolicyEnableProxyCommandLineOptions); case TogglePolicy::Policy::McpServer: return TogglePolicy(policy, "EnableWindowsPackageManagerMcpServer"sv, String::PolicyEnableMcpServer); default: THROW_HR(E_UNEXPECTED); } } std::vector TogglePolicy::GetAllPolicies() { using Toggle_t = std::underlying_type_t; std::vector result; // Skip "None" for (Toggle_t i = 1 + static_cast(TogglePolicy::Policy::None); i < static_cast(TogglePolicy::Policy::Max); ++i) { result.emplace_back(GetPolicy(static_cast(i))); } return result; } std::string SourceFromPolicy::ToJsonString() const { Json::Value json{ Json::ValueType::objectValue }; json["Name"] = Name; json["Type"] = Type; json["Arg"] = Arg; json["Data"] = Data; json["Identifier"] = Identifier; json["Explicit"] = Explicit; if (Priority) { json["Priority"] = Priority.value(); } // Trust level is represented as an array of trust level strings since there can be multiple flags set. int trustLevelLength = static_cast(TrustLevel.size()); for (int i = 0; i < trustLevelLength; ++i) { json["TrustLevel"][i] = TrustLevel[i]; } Json::StreamWriterBuilder writerBuilder; writerBuilder.settings_["indentation"] = ""; return Json::writeString(writerBuilder, json); } GroupPolicy::GroupPolicy(const Registry::Key& key) { ValidateAllValuePolicies(key, m_values, std::make_index_sequence(ValuePolicy::Max)>()); using Toggle_t = std::underlying_type_t; for (Toggle_t i = static_cast(TogglePolicy::Policy::None); i < static_cast(TogglePolicy::Policy::Max); ++i) { auto policy = static_cast(i); m_toggles[policy] = GetStateInternal(key, policy); } } PolicyState GroupPolicy::GetState(TogglePolicy::Policy policy) const { auto itr = m_toggles.find(policy); if (itr == m_toggles.end()) { return PolicyState::NotConfigured; } return itr->second; } bool GroupPolicy::IsEnabled(TogglePolicy::Policy policy) const { if (policy == TogglePolicy::Policy::None) { return true; } PolicyState state = GetState(policy); if (state == PolicyState::NotConfigured) { return TogglePolicy::GetPolicy(policy).DefaultIsEnabled(); } return state == PolicyState::Enabled; } GroupPolicy const& GroupPolicy::Instance() { return InstanceInternal(); } #ifndef AICLI_DISABLE_TEST_HOOKS void GroupPolicy::OverrideInstance(GroupPolicy* overridePolicy) { InstanceInternal(overridePolicy); } void GroupPolicy::ResetInstance() { InstanceInternal(nullptr); } #endif } ================================================ FILE: src/AppInstallerSharedLib/ICU/SQLiteICU.c ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include // Define values to force static linkage version of extension. // The code below the ICU header include should be changed as little as possible. #define SQLITE_CORE #define SQLITE_ENABLE_ICU #define SQLITE_PRIVATE #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_INNOCUOUS 0x000200000 #define STDCALL_FOR_INTEROP __stdcall // Adapted from the file icu.c (v 1.7 2007/12/13 21:54:11) to use the built-in Windows SQLite and ICU binaries. // This file implements an integration between the ICU library // ("International Components for Unicode", an open-source library // for handling unicode data) and SQLite. The integration uses // ICU to provide the following to SQLite: // // * An implementation of the SQL regexp() function (and hence REGEXP // operator) using the ICU uregex_XX() APIs. // // * Implementations of the SQL scalar upper() and lower() functions // for case mapping. // // * Integration of ICU and SQLite collation sequences. // // * An implementation of the LIKE operator that uses ICU to // provide case-independent matching. // Include ICU headers #include /* #include */ #include #ifndef SQLITE_CORE /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #else /* #include "sqlite3.h" */ #endif /* ** This function is called when an ICU function called from within ** the implementation of an SQL scalar function returns an error. ** ** The scalar function context passed as the first argument is ** loaded with an error message based on the following two args. */ static void icuFunctionError( sqlite3_context* pCtx, /* SQLite scalar function context */ const char* zName, /* Name of ICU function that failed */ UErrorCode e /* Error code returned by ICU function */ ) { char zBuf[128]; sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e)); zBuf[127] = '\0'; sqlite3_result_error(pCtx, zBuf, -1); } #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) /* ** Maximum length (in bytes) of the pattern in a LIKE or GLOB ** operator. */ #ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH # define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 #endif /* ** Version of sqlite3_free() that is always a function, never a macro. */ static void STDCALL_FOR_INTEROP xFree(void* p) { sqlite3_free(p); } /* ** This lookup table is used to help decode the first byte of ** a multi-byte UTF8 character. It is copied here from SQLite source ** code file utf8.c. */ static const unsigned char icuUtf8Trans1[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, }; #define SQLITE_ICU_READ_UTF8(zIn, c) \ c = *(zIn++); \ if( c>=0xc0 ){ \ c = icuUtf8Trans1[c-0xc0]; \ while( (*zIn & 0xc0)==0x80 ){ \ c = (c<<6) + (0x3f & *(zIn++)); \ } \ } #define SQLITE_ICU_SKIP_UTF8(zIn) \ assert( *zIn ); \ if( *(zIn++)>=0xc0 ){ \ while( (*zIn & 0xc0)==0x80 ){zIn++;} \ } /* ** Compare two UTF-8 strings for equality where the first string is ** a "LIKE" expression. Return true (1) if they are the same and ** false (0) if they are different. */ static int icuLikeCompare( const uint8_t* zPattern, /* LIKE pattern */ const uint8_t* zString, /* The UTF-8 string to compare against */ const UChar32 uEsc /* The escape character */ ) { static const uint32_t MATCH_ONE = (uint32_t)'_'; static const uint32_t MATCH_ALL = (uint32_t)'%'; int prevEscape = 0; /* True if the previous character was uEsc */ while (1) { /* Read (and consume) the next character from the input pattern. */ uint32_t uPattern; SQLITE_ICU_READ_UTF8(zPattern, uPattern); if (uPattern == 0) break; /* There are now 4 possibilities: ** ** 1. uPattern is an unescaped match-all character "%", ** 2. uPattern is an unescaped match-one character "_", ** 3. uPattern is an unescaped escape character, or ** 4. uPattern is to be handled as an ordinary character */ if (!prevEscape && uPattern == MATCH_ALL) { /* Case 1. */ uint8_t c; /* Skip any MATCH_ALL or MATCH_ONE characters that follow a ** MATCH_ALL. For each MATCH_ONE, skip one character in the ** test string. */ while ((c = *zPattern) == MATCH_ALL || c == MATCH_ONE) { if (c == MATCH_ONE) { if (*zString == 0) return 0; SQLITE_ICU_SKIP_UTF8(zString); } zPattern++; } if (*zPattern == 0) return 1; while (*zString) { if (icuLikeCompare(zPattern, zString, uEsc)) { return 1; } SQLITE_ICU_SKIP_UTF8(zString); } return 0; } else if (!prevEscape && uPattern == MATCH_ONE) { /* Case 2. */ if (*zString == 0) return 0; SQLITE_ICU_SKIP_UTF8(zString); } else if (!prevEscape && uPattern == (uint32_t)uEsc) { /* Case 3. */ prevEscape = 1; } else { /* Case 4. */ uint32_t uString; SQLITE_ICU_READ_UTF8(zString, uString); uString = (uint32_t)u_foldCase((UChar32)uString, U_FOLD_CASE_DEFAULT); uPattern = (uint32_t)u_foldCase((UChar32)uPattern, U_FOLD_CASE_DEFAULT); if (uString != uPattern) { return 0; } prevEscape = 0; } } return *zString == 0; } /* ** Implementation of the like() SQL function. This function implements ** the build-in LIKE operator. The first argument to the function is the ** pattern and the second argument is the string. So, the SQL statements: ** ** A LIKE B ** ** is implemented as like(B, A). If there is an escape character E, ** ** A LIKE B ESCAPE E ** ** is mapped to like(B, A, E). */ static void STDCALL_FOR_INTEROP icuLikeFunc( sqlite3_context* context, int argc, sqlite3_value** argv ) { const unsigned char* zA = sqlite3_value_text(argv[0]); const unsigned char* zB = sqlite3_value_text(argv[1]); UChar32 uEsc = 0; /* Limit the length of the LIKE or GLOB pattern to avoid problems ** of deep recursion and N*N behavior in patternCompare(). */ if (sqlite3_value_bytes(argv[0]) > SQLITE_MAX_LIKE_PATTERN_LENGTH) { sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1); return; } if (argc == 3) { /* The escape character string must consist of a single UTF-8 character. ** Otherwise, return an error. */ int nE = sqlite3_value_bytes(argv[2]); const unsigned char* zE = sqlite3_value_text(argv[2]); int i = 0; if (zE == 0) return; U8_NEXT(zE, i, nE, uEsc); if (i != nE) { sqlite3_result_error(context, "ESCAPE expression must be a single character", -1); return; } } if (zA && zB) { sqlite3_result_int(context, icuLikeCompare(zA, zB, uEsc)); } } /* ** Function to delete compiled regexp objects. Registered as ** a destructor function with sqlite3_set_auxdata(). */ static void STDCALL_FOR_INTEROP icuRegexpDelete(void* p) { URegularExpression* pExpr = (URegularExpression*)p; uregex_close(pExpr); } /* ** Implementation of SQLite REGEXP operator. This scalar function takes ** two arguments. The first is a regular expression pattern to compile ** the second is a string to match against that pattern. If either ** argument is an SQL NULL, then NULL Is returned. Otherwise, the result ** is 1 if the string matches the pattern, or 0 otherwise. ** ** SQLite maps the regexp() function to the regexp() operator such ** that the following two are equivalent: ** ** zString REGEXP zPattern ** regexp(zPattern, zString) ** ** Uses the following ICU regexp APIs: ** ** uregex_open() ** uregex_matches() ** uregex_close() */ static void STDCALL_FOR_INTEROP icuRegexpFunc(sqlite3_context* p, int nArg, sqlite3_value** apArg) { UErrorCode status = U_ZERO_ERROR; URegularExpression* pExpr; UBool res; const UChar* zString = sqlite3_value_text16(apArg[1]); (void)nArg; /* Unused parameter */ /* If the left hand side of the regexp operator is NULL, ** then the result is also NULL. */ if (!zString) { return; } pExpr = sqlite3_get_auxdata(p, 0); if (!pExpr) { const UChar* zPattern = sqlite3_value_text16(apArg[0]); if (!zPattern) { return; } pExpr = uregex_open(zPattern, -1, 0, 0, &status); if (U_SUCCESS(status)) { sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete); } else { assert(!pExpr); icuFunctionError(p, "uregex_open", status); return; } } /* Configure the text that the regular expression operates on. */ uregex_setText(pExpr, zString, -1, &status); if (!U_SUCCESS(status)) { icuFunctionError(p, "uregex_setText", status); return; } /* Attempt the match */ res = uregex_matches(pExpr, 0, &status); if (!U_SUCCESS(status)) { icuFunctionError(p, "uregex_matches", status); return; } /* Set the text that the regular expression operates on to a NULL ** pointer. This is not really necessary, but it is tidier than ** leaving the regular expression object configured with an invalid ** pointer after this function returns. */ uregex_setText(pExpr, 0, 0, &status); /* Return 1 or 0. */ sqlite3_result_int(p, res ? 1 : 0); } /* ** Implementations of scalar functions for case mapping - upper() and ** lower(). Function upper() converts its input to upper-case (ABC). ** Function lower() converts to lower-case (abc). ** ** ICU provides two types of case mapping, "general" case mapping and ** "language specific". Refer to ICU documentation for the differences ** between the two. ** ** To utilise "general" case mapping, the upper() or lower() scalar ** functions are invoked with one argument: ** ** upper('ABC') -> 'abc' ** lower('abc') -> 'ABC' ** ** To access ICU "language specific" case mapping, upper() or lower() ** should be invoked with two arguments. The second argument is the name ** of the locale to use. Passing an empty string ("") or SQL NULL value ** as the second argument is the same as invoking the 1 argument version ** of upper() or lower(). ** ** lower('I', 'en_us') -> 'i' ** lower('I', 'tr_tr') -> '\u131' (small dotless i) ** ** http://www.icu-project.org/userguide/posix.html#case_mappings */ static void STDCALL_FOR_INTEROP icuCaseFunc16(sqlite3_context* p, int nArg, sqlite3_value** apArg) { const UChar* zInput; /* Pointer to input string */ UChar* zOutput = 0; /* Pointer to output buffer */ int nInput; /* Size of utf-16 input string in bytes */ int nOut; /* Size of output buffer in bytes */ int cnt; int bToUpper; /* True for toupper(), false for tolower() */ UErrorCode status; const char* zLocale = 0; assert(nArg == 1 || nArg == 2); bToUpper = (sqlite3_user_data(p) != 0); if (nArg == 2) { zLocale = (const char*)sqlite3_value_text(apArg[1]); } zInput = sqlite3_value_text16(apArg[0]); if (!zInput) { return; } nOut = nInput = sqlite3_value_bytes16(apArg[0]); if (nOut == 0) { sqlite3_result_text16(p, "", 0, SQLITE_STATIC); return; } for (cnt = 0; cnt < 2; cnt++) { UChar* zNew = sqlite3_realloc(zOutput, nOut); if (zNew == 0) { sqlite3_free(zOutput); sqlite3_result_error_nomem(p); return; } zOutput = zNew; status = U_ZERO_ERROR; if (bToUpper) { nOut = 2 * u_strToUpper(zOutput, nOut / 2, zInput, nInput / 2, zLocale, &status); } else { nOut = 2 * u_strToLower(zOutput, nOut / 2, zInput, nInput / 2, zLocale, &status); } if (U_SUCCESS(status)) { sqlite3_result_text16(p, zOutput, nOut, xFree); } else if (status == U_BUFFER_OVERFLOW_ERROR) { assert(cnt == 0); continue; } else { icuFunctionError(p, bToUpper ? "u_strToUpper" : "u_strToLower", status); } return; } assert(0); /* Unreachable */ } #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */ /* ** Collation sequence destructor function. The pCtx argument points to ** a UCollator structure previously allocated using ucol_open(). */ static void STDCALL_FOR_INTEROP icuCollationDel(void* pCtx) { UCollator* p = (UCollator*)pCtx; ucol_close(p); } /* ** Collation sequence comparison function. The pCtx argument points to ** a UCollator structure previously allocated using ucol_open(). */ static int STDCALL_FOR_INTEROP icuCollationColl( void* pCtx, int nLeft, const void* zLeft, int nRight, const void* zRight ) { UCollationResult res; UCollator* p = (UCollator*)pCtx; res = ucol_strcoll(p, (UChar*)zLeft, nLeft / 2, (UChar*)zRight, nRight / 2); switch (res) { case UCOL_LESS: return -1; case UCOL_GREATER: return +1; case UCOL_EQUAL: return 0; } assert(!"Unexpected return value from ucol_strcoll()"); return 0; } /* ** Implementation of the scalar function icu_load_collation(). ** ** This scalar function is used to add ICU collation based collation ** types to an SQLite database connection. It is intended to be called ** as follows: ** ** SELECT icu_load_collation(, ); ** ** Where is a string containing an ICU locale identifier (i.e. ** "en_AU", "tr_TR" etc.) and is the name of the ** collation sequence to create. */ static void STDCALL_FOR_INTEROP icuLoadCollation( sqlite3_context* p, int nArg, sqlite3_value** apArg ) { sqlite3* db = (sqlite3*)sqlite3_user_data(p); UErrorCode status = U_ZERO_ERROR; const char* zLocale; /* Locale identifier - (eg. "jp_JP") */ const char* zName; /* SQL Collation sequence name (eg. "japanese") */ UCollator* pUCollator; /* ICU library collation object */ int rc; /* Return code from sqlite3_create_collation_x() */ assert(nArg == 2); (void)nArg; /* Unused parameter */ zLocale = (const char*)sqlite3_value_text(apArg[0]); zName = (const char*)sqlite3_value_text(apArg[1]); if (!zLocale || !zName) { return; } pUCollator = ucol_open(zLocale, &status); if (!U_SUCCESS(status)) { icuFunctionError(p, "ucol_open", status); return; } assert(p); rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void*)pUCollator, icuCollationColl, icuCollationDel ); if (rc != SQLITE_OK) { ucol_close(pUCollator); sqlite3_result_error(p, "Error registering collation function", -1); } } /* ** Register the ICU extension functions with database db. */ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3* db) { # define SQLITEICU_EXTRAFLAGS (SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS) static const struct IcuScalar { const char* zName; /* Function name */ unsigned char nArg; /* Number of arguments */ unsigned int enc; /* Optimal text encoding */ unsigned char iContext; /* sqlite3_user_data() context */ void (STDCALL_FOR_INTEROP *xFunc)(sqlite3_context*, int, sqlite3_value**); } scalars[] = { {"icu_load_collation",2,SQLITE_UTF8 | SQLITE_DIRECTONLY,1, icuLoadCollation}, #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) {"regexp", 2, SQLITE_ANY | SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc}, {"lower", 1, SQLITE_UTF16 | SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, {"lower", 2, SQLITE_UTF16 | SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, {"upper", 1, SQLITE_UTF16 | SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16}, {"upper", 2, SQLITE_UTF16 | SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16}, {"lower", 1, SQLITE_UTF8 | SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, {"lower", 2, SQLITE_UTF8 | SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16}, {"upper", 1, SQLITE_UTF8 | SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16}, {"upper", 2, SQLITE_UTF8 | SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16}, {"like", 2, SQLITE_UTF8 | SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc}, {"like", 3, SQLITE_UTF8 | SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc}, #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */ }; int rc = SQLITE_OK; int i; for (i = 0; rc == SQLITE_OK && i < (int)(sizeof(scalars) / sizeof(scalars[0])); i++) { const struct IcuScalar* p = &scalars[i]; rc = sqlite3_create_function( db, p->zName, p->nArg, p->enc, p->iContext ? (void*)db : (void*)0, p->xFunc, 0, 0 ); } return rc; } #ifndef SQLITE_CORE #ifdef _WIN32 __declspec(dllexport) #endif SQLITE_API int sqlite3_icu_init( sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi ) { SQLITE_EXTENSION_INIT2(pApi) return sqlite3IcuInit(db); } #endif /************** End of icu.c *************************************************/ ================================================ FILE: src/AppInstallerSharedLib/ICU/SQLiteICU.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include // Adapted from the file sqliteicu.h to use the built-in Windows SQLite and ICU binaries. extern "C" { int sqlite3IcuInit(sqlite3* db); } ================================================ FILE: src/AppInstallerSharedLib/JsonSchemaValidation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/JsonSchemaValidation.h" #include "winget/Resources.h" namespace AppInstaller::JsonSchema { Json::Value LoadSchemaDoc(std::string_view schemaStr) { Json::Value schemaJson; int schemaLength = static_cast(schemaStr.length()); Json::CharReaderBuilder charReaderBuilder; const std::unique_ptr jsonReader(charReaderBuilder.newCharReader()); std::string errorMsg; if (!jsonReader->parse(schemaStr.data(), schemaStr.data() + schemaLength, &schemaJson, &errorMsg)) { THROW_HR_MSG(E_UNEXPECTED, "Jsoncpp parser failed to parse the schema doc. Reason: %hs", errorMsg.c_str()); } return schemaJson; } Json::Value LoadResourceAsSchemaDoc(PCWSTR resourceName, PCWSTR resourceType) { return LoadSchemaDoc(Resource::GetResourceAsString(resourceName, resourceType)); } void PopulateSchema(const Json::Value& schemaJson, valijson::Schema& schema) { valijson::SchemaParser schemaParser; valijson::adapters::JsonCppAdapter jsonSchemaAdapter(schemaJson); schemaParser.populateSchema(jsonSchemaAdapter, schema); } bool Validate(const valijson::Schema& schema, const Json::Value& json, valijson::ValidationResults& results) { valijson::Validator schemaValidator; valijson::adapters::JsonCppAdapter jsonAdapter(json); return schemaValidator.validate(schema, jsonAdapter, &results); } std::string GetErrorStringFromResults(valijson::ValidationResults& results) { valijson::ValidationResults::Error error; std::stringstream ss; ss << "Schema validation failed." << std::endl; while (results.popError(error)) { std::string context; for (auto itr = error.context.begin(); itr != error.context.end(); itr++) { context += *itr; } ss << "Error context: " << context << " Description: " << error.description << std::endl; } return ss.str(); } } ================================================ FILE: src/AppInstallerSharedLib/JsonUtil.cpp ================================================ // Copyright(c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/JsonUtil.h" #include "AppInstallerStrings.h" namespace AppInstaller::JSON { template<> std::optional GetValue(const Json::Value& node) { std::optional value; if (node.isString()) { value = node.asString(); } return value; } template<> std::optional GetValue(const Json::Value& node) { std::optional value; if (node.isUInt()) { value = node.asUInt(); } return value; } template<> std::optional GetValue(const Json::Value& node) { std::optional value; if (node.isBool()) { value = node.asBool(); } return value; } template<> std::optional> GetValue(const Json::Value& node) { std::vector result; if (node.isArray()) { for (const Json::Value& entry : node) { if (!entry.isString()) { return std::nullopt; } result.emplace_back(entry.asString()); } return result; } return std::nullopt; } #ifndef WINGET_DISABLE_FOR_FUZZING utility::string_t GetUtilityString(std::string_view nodeName) { return utility::conversions::to_string_t(nodeName.data()); } web::json::value GetStringValue(std::string_view value) { return web::json::value::string(Utility::ConvertToUTF16(value)); } std::optional> GetJsonValueFromNode(const web::json::value& node, const utility::string_t& keyName) { if (node.is_null() || !node.has_field(keyName)) { return {}; } return node.at(keyName); } std::optional GetRawStringValueFromJsonValue(const web::json::value& value) { if (value.is_null() || !value.is_string()) { return {}; } return utility::conversions::to_utf8string(value.as_string()); } std::optional GetWideStringValueFromJsonValue(const web::json::value& value) { if (value.is_null() || !value.is_string()) { return {}; } return value.as_string(); } std::optional GetRawStringValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName) { std::optional> jsonValue = GetJsonValueFromNode(node, keyName); if (jsonValue) { return GetRawStringValueFromJsonValue(jsonValue.value().get()); } return {}; } std::optional GetWideStringValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName) { std::optional> jsonValue = GetJsonValueFromNode(node, keyName); if (jsonValue) { return GetWideStringValueFromJsonValue(jsonValue.value().get()); } return {}; } std::optional GetRawIntValueFromJsonValue(const web::json::value& value) { if (value.is_null() || !value.is_integer()) { return {}; } return value.as_integer(); } std::optional GetRawUInt64ValueFromJsonValue(const web::json::value& value) { if (value.is_null() || !value.is_number()) { return {}; } const web::json::number& valueNumber = value.as_number(); if (!valueNumber.is_uint64()) { return {}; } return valueNumber.to_uint64(); } std::optional GetRawIntValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName) { std::optional> jsonValue = GetJsonValueFromNode(node, keyName); if (jsonValue) { return GetRawIntValueFromJsonValue(jsonValue.value().get()); } return {}; } std::optional GetRawUInt64ValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName) { std::optional> jsonValue = GetJsonValueFromNode(node, keyName); if (jsonValue) { return GetRawUInt64ValueFromJsonValue(jsonValue.value().get()); } return {}; } std::optional GetRawBoolValueFromJsonValue(const web::json::value& value) { if (value.is_null() || !value.is_boolean()) { return {}; } return value.as_bool(); } std::optional GetRawBoolValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName) { std::optional> jsonValue = GetJsonValueFromNode(node, keyName); if (jsonValue) { return GetRawBoolValueFromJsonValue(jsonValue.value().get()); } return {}; } std::optional> GetRawJsonArrayFromJsonNode(const web::json::value& node, const utility::string_t& keyName) { std::optional> jsonValue = GetJsonValueFromNode(node, keyName); if (!jsonValue || !jsonValue.value().get().is_array()) { return {}; } return jsonValue.value().get().as_array(); } std::vector GetRawStringArrayFromJsonNode( const web::json::value& node, const utility::string_t& keyName) { std::optional> arrayValue = GetRawJsonArrayFromJsonNode(node, keyName); std::vector result; if (!arrayValue) { return result; } for (auto& value : arrayValue.value().get()) { std::optional item = GetRawStringValueFromJsonValue(value); if (item) { result.emplace_back(std::move(item.value())); } } return result; } std::set GetRawStringSetFromJsonNode( const web::json::value& node, const utility::string_t& keyName) { std::optional> arrayValue = GetRawJsonArrayFromJsonNode(node, keyName); std::set result; if (!arrayValue) { return result; } for (auto& value : arrayValue.value().get()) { std::optional item = GetRawStringValueFromJsonValue(value); if (item) { result.emplace(std::move(item.value())); } } return result; } std::string Base64Encode(const std::vector& input) { if (input.size() == 0) { return {}; } std::wstring result; DWORD resultSize = 0; CryptBinaryToStringW(input.data(), static_cast(input.size()), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, nullptr, &resultSize); THROW_LAST_ERROR_IF(resultSize == 0); result.resize(resultSize); THROW_LAST_ERROR_IF(!CryptBinaryToStringW(input.data(), static_cast(input.size()), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, result.data(), &resultSize)); // Resize to remove trailing null terminator result.resize(resultSize); return Utility::ConvertToUTF8(result); } std::vector Base64Decode(const std::string& input) { if (input.empty()) { return {}; } auto inputWide = Utility::ConvertToUTF16(input); std::vector result; DWORD resultSize = 0; CryptStringToBinaryW(inputWide.data(), static_cast(inputWide.size()), CRYPT_STRING_BASE64, nullptr, &resultSize, nullptr, nullptr); THROW_LAST_ERROR_IF(resultSize == 0); result.resize(resultSize); THROW_LAST_ERROR_IF(!CryptStringToBinaryW(inputWide.data(), static_cast(inputWide.size()), CRYPT_STRING_BASE64, result.data(), &resultSize, nullptr, nullptr)); return result; } #endif bool IsValidNonEmptyStringValue(std::optional& value) { if (Utility::IsEmptyOrWhitespace(value.value_or(""))) { return false; } return true; } bool IsValidNonEmptyStringValue(std::optional& value) { if (Utility::IsEmptyOrWhitespace(value.value_or(L""))) { return false; } return true; } } ================================================ FILE: src/AppInstallerSharedLib/ManagedFile.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/ManagedFile.h" #include "AppInstallerLogging.h" namespace AppInstaller::Utility { ManagedFile ManagedFile::CreateWriteLockedFile(const std::filesystem::path& path, DWORD desiredAccess, bool deleteOnExit) { ManagedFile file; file.m_fileHandle.reset(CreateFileW(path.c_str(), desiredAccess, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)); THROW_LAST_ERROR_IF(!file.m_fileHandle); file.m_filePath = path; file.m_deleteFileOnExit = deleteOnExit; return file; } ManagedFile ManagedFile::OpenWriteLockedFile(const std::filesystem::path& path, DWORD desiredAccess) { ManagedFile file; file.m_fileHandle.reset(CreateFileW(path.c_str(), desiredAccess, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); THROW_LAST_ERROR_IF(!file.m_fileHandle); file.m_filePath = path; return file; } ManagedFile::~ManagedFile() { if (m_deleteFileOnExit) { if (m_fileHandle) { m_fileHandle.reset(); } try { std::filesystem::remove(m_filePath); } catch (...) { AICLI_LOG(Core, Info, << "Failed to remove managed file at: " << m_filePath); } } } } ================================================ FILE: src/AppInstallerSharedLib/PropertySheet.props ================================================ ================================================ FILE: src/AppInstallerSharedLib/Public/AppInstallerDateTime.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Utility { // The individual aspects of a time point. enum class TimeFacet { None = 0x000, Millisecond = 0x001, Second = 0x002, Minute = 0x004, Hour = 0x008, Day = 0x010, Month = 0x020, Year = 0x040, // `Year - 2000` [2 digits for 75 more years] ShortYear = 0x080, // Includes unspecified time zone RFC3339 = 0x100, // Limits special character use Filename = 0x200, Default = Year | Month | Day | Hour | Minute | Second | Millisecond, ShortYearSecondPrecision = ShortYear | Month | Day | Hour | Minute | Second, }; DEFINE_ENUM_FLAG_OPERATORS(TimeFacet); // Writes the given time to the given stream. // Assumes that system_clock uses Linux epoch (as required by C++20 standard). // Time is also assumed to be after the epoch. void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, bool useRFC3339 = false); void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, TimeFacet facet); // Converts the time point to a string using OutputTimePoint. std::string TimePointToString(const std::chrono::system_clock::time_point& time, bool useRFC3339 = false); std::string TimePointToString(const std::chrono::system_clock::time_point& time, TimeFacet facet); // Gets the current time as a string. Can be used as a file name. // Tries to make things a little bit shorter when shortTime == true. std::string GetCurrentTimeForFilename(bool shortTime = false); // Gets the current date as a string to be used in the ARP registry. std::string GetCurrentDateForARP(); // Gets the current time as a unix epoch value. int64_t GetCurrentUnixEpoch(); // Converts the given unix epoch time to a system_clock::time_point. int64_t ConvertSystemClockToUnixEpoch(const std::chrono::system_clock::time_point& time); // Converts the given unix epoch time to a system_clock::time_point. std::chrono::system_clock::time_point ConvertUnixEpochToSystemClock(int64_t epoch); // Converts the given file time to a system_clock::time_point. std::chrono::system_clock::time_point ConvertFiletimeToSystemClock(const FILETIME& fileTime); // Converts the given package version into a time_point using our custom format. // Ensure that the package is expected to use this format, or you may get strange times. // If the version is not convertable, the minimum time is returned. std::chrono::system_clock::time_point GetTimePointFromVersion(const UInt64Version& version); } ================================================ FILE: src/AppInstallerSharedLib/Public/AppInstallerErrors.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include // Errors is the most ubiquitous header; including the mismatch detection in it should reach everywhere. #include #ifndef WINGET_DISABLE_FOR_FUZZING #include #endif #include #include #include #include #include #define APPINSTALLER_CLI_ERROR_FACILITY 0xA15 // Changes to any of these errors require the corresponding resource string in winget.resw to be updated. #define APPINSTALLER_CLI_ERROR_INTERNAL_ERROR ((HRESULT)0x8A150001) #define APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS ((HRESULT)0x8A150002) #define APPINSTALLER_CLI_ERROR_COMMAND_FAILED ((HRESULT)0x8A150003) #define APPINSTALLER_CLI_ERROR_MANIFEST_FAILED ((HRESULT)0x8A150004) #define APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED ((HRESULT)0x8A150005) #define APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED ((HRESULT)0x8A150006) #define APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION ((HRESULT)0x8A150007) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_FAILED ((HRESULT)0x8A150008) #define APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX ((HRESULT)0x8A150009) #define APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED ((HRESULT)0x8A15000A) #define APPINSTALLER_CLI_ERROR_SOURCES_INVALID ((HRESULT)0x8A15000B) #define APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS ((HRESULT)0x8A15000C) #define APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE ((HRESULT)0x8A15000D) #define APPINSTALLER_CLI_ERROR_PACKAGE_IS_BUNDLE ((HRESULT)0x8A15000E) #define APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING ((HRESULT)0x8A15000F) #define APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER ((HRESULT)0x8A150010) #define APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH ((HRESULT)0x8A150011) #define APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST ((HRESULT)0x8A150012) #define APPINSTALLER_CLI_ERROR_SOURCE_ARG_ALREADY_EXISTS ((HRESULT)0x8A150013) #define APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND ((HRESULT)0x8A150014) #define APPINSTALLER_CLI_ERROR_NO_SOURCES_DEFINED ((HRESULT)0x8A150015) #define APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND ((HRESULT)0x8A150016) #define APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND ((HRESULT)0x8A150017) #define APPINSTALLER_CLI_ERROR_EXTENSION_PUBLIC_FAILED ((HRESULT)0x8A150018) #define APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN ((HRESULT)0x8A150019) #define APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE ((HRESULT)0x8A15001A) #define APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY ((HRESULT)0x8A15001B) #define APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY ((HRESULT)0x8A15001C) #define APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED ((HRESULT)0x8A15001D) #define APPINSTALLER_CLI_ERROR_MSSTORE_INSTALL_FAILED ((HRESULT)0x8A15001E) #define APPINSTALLER_CLI_ERROR_COMPLETE_INPUT_BAD ((HRESULT)0x8A15001F) #define APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED ((HRESULT)0x8A150020) #define APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY ((HRESULT)0x8A150021) #define APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY ((HRESULT)0x8A150022) #define APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION ((HRESULT)0x8A150023) #define APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED ((HRESULT)0x8A150024) #define APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE ((HRESULT)0x8A150025) #define APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA ((HRESULT)0x8A150026) #define APPINSTALLER_CLI_ERROR_LIBYAML_ERROR ((HRESULT)0x8A150027) #define APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_WARNING ((HRESULT)0x8A150028) #define APPINSTALLER_CLI_ERROR_MANIFEST_VALIDATION_FAILURE ((HRESULT)0x8A150029) #define APPINSTALLER_CLI_ERROR_INVALID_MANIFEST ((HRESULT)0x8A15002A) #define APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE ((HRESULT)0x8A15002B) #define APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE ((HRESULT)0x8A15002C) #define APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED ((HRESULT)0x8A15002D) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH ((HRESULT)0x8A15002E) #define APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND ((HRESULT)0x8A15002F) #define APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED ((HRESULT)0x8A150030) #define APPINSTALLER_CLI_ERROR_ICU_BREAK_ITERATOR_ERROR ((HRESULT)0x8A150031) #define APPINSTALLER_CLI_ERROR_ICU_CASEMAP_ERROR ((HRESULT)0x8A150032) #define APPINSTALLER_CLI_ERROR_ICU_REGEX_ERROR ((HRESULT)0x8A150033) #define APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED ((HRESULT)0x8A150034) #define APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND ((HRESULT)0x8A150035) #define APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE ((HRESULT)0x8A150036) #define APPINSTALLER_CLI_ERROR_SOURCE_NOT_REMOTE ((HRESULT)0x8A150037) #define APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE ((HRESULT)0x8A150038) #define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA ((HRESULT)0x8A150039) #define APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY ((HRESULT)0x8A15003A) #define APPINSTALLER_CLI_ERROR_RESTAPI_INTERNAL_ERROR ((HRESULT)0x8A15003B) #define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL ((HRESULT)0x8A15003C) #define APPINSTALLER_CLI_ERROR_RESTAPI_UNSUPPORTED_MIME_TYPE ((HRESULT)0x8A15003D) #define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION ((HRESULT)0x8A15003E) #define APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE ((HRESULT)0x8A15003F) #define APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE ((HRESULT)0x8A150040) #define APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED ((HRESULT)0x8A150041) #define APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR ((HRESULT)0x8A150042) #define APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST ((HRESULT)0x8A150043) #define APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND ((HRESULT)0x8A150044) #define APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED ((HRESULT)0x8A150045) #define APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED ((HRESULT)0x8A150046) #define APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH ((HRESULT)0x8A150047) #define APPINSTALLER_CLI_ERROR_MISSING_RESOURCE_FILE ((HRESULT)0x8A150048) #define APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED ((HRESULT)0x8A150049) #define APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT ((HRESULT)0x8A15004A) #define APPINSTALLER_CLI_ERROR_FAILED_TO_OPEN_ALL_SOURCES ((HRESULT)0x8A15004B) #define APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED ((HRESULT)0x8A15004C) #define APPINSTALLER_CLI_ERROR_MISSING_PACKAGE ((HRESULT)0x8A15004D) #define APPINSTALLER_CLI_ERROR_INVALID_TABLE_COLUMN ((HRESULT)0x8A15004E) #define APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER ((HRESULT)0x8A15004F) #define APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN ((HRESULT)0x8A150050) #define APPINSTALLER_CLI_ERROR_ICU_CONVERSION_ERROR ((HRESULT)0x8A150051) #define APPINSTALLER_CLI_ERROR_PORTABLE_INSTALL_FAILED ((HRESULT)0x8A150052) #define APPINSTALLER_CLI_ERROR_PORTABLE_REPARSE_POINT_NOT_SUPPORTED ((HRESULT)0x8A150053) #define APPINSTALLER_CLI_ERROR_PORTABLE_PACKAGE_ALREADY_EXISTS ((HRESULT)0x8A150054) #define APPINSTALLER_CLI_ERROR_PORTABLE_SYMLINK_PATH_IS_DIRECTORY ((HRESULT)0x8A150055) #define APPINSTALLER_CLI_ERROR_INSTALLER_PROHIBITS_ELEVATION ((HRESULT)0x8A150056) #define APPINSTALLER_CLI_ERROR_PORTABLE_UNINSTALL_FAILED ((HRESULT)0x8A150057) #define APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED ((HRESULT)0x8A150058) #define APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT ((HRESULT)0x8A150059) #define APPINSTALLER_CLI_ERROR_BIND_WITH_EMBEDDED_NULL ((HRESULT)0x8A15005A) #define APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND ((HRESULT)0x8A15005B) #define APPINSTALLER_CLI_ERROR_EXTRACT_ARCHIVE_FAILED ((HRESULT)0x8A15005C) #define APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_INVALID_PATH ((HRESULT)0x8A15005D) #define APPINSTALLER_CLI_ERROR_PINNED_CERTIFICATE_MISMATCH ((HRESULT)0x8A15005E) #define APPINSTALLER_CLI_ERROR_INSTALL_LOCATION_REQUIRED ((HRESULT)0x8A15005F) #define APPINSTALLER_CLI_ERROR_ARCHIVE_SCAN_FAILED ((HRESULT)0x8A150060) #define APPINSTALLER_CLI_ERROR_PACKAGE_ALREADY_INSTALLED ((HRESULT)0x8A150061) #define APPINSTALLER_CLI_ERROR_PIN_ALREADY_EXISTS ((HRESULT)0x8A150062) #define APPINSTALLER_CLI_ERROR_PIN_DOES_NOT_EXIST ((HRESULT)0x8A150063) #define APPINSTALLER_CLI_ERROR_CANNOT_OPEN_PINNING_INDEX ((HRESULT)0x8A150064) #define APPINSTALLER_CLI_ERROR_MULTIPLE_INSTALL_FAILED ((HRESULT)0x8A150065) #define APPINSTALLER_CLI_ERROR_MULTIPLE_UNINSTALL_FAILED ((HRESULT)0x8A150066) #define APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE ((HRESULT)0x8A150067) #define APPINSTALLER_CLI_ERROR_PACKAGE_IS_PINNED ((HRESULT)0x8A150068) #define APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB ((HRESULT)0x8A150069) #define APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED ((HRESULT)0x8A15006A) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES ((HRESULT)0x8A15006B) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED ((HRESULT)0x8A15006C) #define APPINSTALLER_CLI_ERROR_SERVICE_UNAVAILABLE ((HRESULT)0x8A15006D) #define APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND ((HRESULT)0x8A15006E) #define APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH ((HRESULT)0x8A15006F) #define APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE ((HRESULT)0x8A150070) #define APPINSTALLER_CLI_ERROR_CANNOT_OPEN_CHECKPOINT_INDEX ((HRESULT)0x8A150071) #define APPINSTALLER_CLI_ERROR_RESUME_LIMIT_EXCEEDED ((HRESULT)0x8A150072) #define APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO ((HRESULT)0x8A150073) #define APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED ((HRESULT)0x8A150074) #define APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED ((HRESULT)0x8A150075) #define APPINSTALLER_CLI_ERROR_AUTHENTICATION_INTERACTIVE_REQUIRED ((HRESULT)0x8A150076) #define APPINSTALLER_CLI_ERROR_AUTHENTICATION_CANCELLED_BY_USER ((HRESULT)0x8A150077) #define APPINSTALLER_CLI_ERROR_AUTHENTICATION_INCORRECT_ACCOUNT ((HRESULT)0x8A150078) #define APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND ((HRESULT)0x8A150079) #define APPINSTALLER_CLI_ERROR_REPAIR_NOT_APPLICABLE ((HRESULT)0x8A15007A) #define APPINSTALLER_CLI_ERROR_EXEC_REPAIR_FAILED ((HRESULT)0x8A15007B) #define APPINSTALLER_CLI_ERROR_REPAIR_NOT_SUPPORTED ((HRESULT)0x8A15007C) #define APPINSTALLER_CLI_ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED ((HRESULT)0x8A15007D) #define APPINSTALLER_CLI_ERROR_SQLITE_CONNECTION_TERMINATED ((HRESULT)0x8A15007E) #define APPINSTALLER_CLI_ERROR_DISPLAYCATALOG_API_FAILED ((HRESULT)0x8A15007F) #define APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE ((HRESULT)0x8A150080) #define APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED ((HRESULT)0x8A150081) #define APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE ((HRESULT)0x8A150082) #define APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED ((HRESULT)0x8A150083) #define APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED ((HRESULT)0x8A150084) #define APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN ((HRESULT)0x8A150085) #define APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE ((HRESULT)0x8A150086) #define APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED ((HRESULT)0x8A150087) #define APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_SUPPORTED ((HRESULT)0x8A150088) #define APPINSTALLER_CLI_ERROR_FONT_ALREADY_INSTALLED ((HRESULT)0x8A150089) #define APPINSTALLER_CLI_ERROR_FONT_FILE_NOT_FOUND ((HRESULT)0x8A15008A) #define APPINSTALLER_CLI_ERROR_FONT_UNINSTALL_FAILED ((HRESULT)0x8A15008B) #define APPINSTALLER_CLI_ERROR_FONT_VALIDATION_FAILED ((HRESULT)0x8A15008C) #define APPINSTALLER_CLI_ERROR_FONT_ROLLBACK_FAILED ((HRESULT)0x8A15008D) // Install errors. #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) #define APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS ((HRESULT)0x8A150102) #define APPINSTALLER_CLI_ERROR_INSTALL_FILE_IN_USE ((HRESULT)0x8A150103) #define APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY ((HRESULT)0x8A150104) #define APPINSTALLER_CLI_ERROR_INSTALL_DISK_FULL ((HRESULT)0x8A150105) #define APPINSTALLER_CLI_ERROR_INSTALL_INSUFFICIENT_MEMORY ((HRESULT)0x8A150106) #define APPINSTALLER_CLI_ERROR_INSTALL_NO_NETWORK ((HRESULT)0x8A150107) #define APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT ((HRESULT)0x8A150108) #define APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH ((HRESULT)0x8A150109) #define APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_FOR_INSTALL ((HRESULT)0x8A15010A) #define APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_INITIATED ((HRESULT)0x8A15010B) #define APPINSTALLER_CLI_ERROR_INSTALL_CANCELLED_BY_USER ((HRESULT)0x8A15010C) #define APPINSTALLER_CLI_ERROR_INSTALL_ALREADY_INSTALLED ((HRESULT)0x8A15010D) #define APPINSTALLER_CLI_ERROR_INSTALL_DOWNGRADE ((HRESULT)0x8A15010E) #define APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY ((HRESULT)0x8A15010F) #define APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES ((HRESULT)0x8A150110) #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE_BY_APPLICATION ((HRESULT)0x8A150111) #define APPINSTALLER_CLI_ERROR_INSTALL_INVALID_PARAMETER ((HRESULT)0x8A150112) #define APPINSTALLER_CLI_ERROR_INSTALL_SYSTEM_NOT_SUPPORTED ((HRESULT)0x8A150113) #define APPINSTALLER_CLI_ERROR_INSTALL_UPGRADE_NOT_SUPPORTED ((HRESULT)0x8A150114) #define APPINSTALLER_CLI_ERROR_INSTALL_CUSTOM_ERROR ((HRESULT)0x8A150115) // Status values for check package installed status results. // Partial success has the success bit(first bit) set to 0. #define WINGET_INSTALLED_STATUS_ARP_ENTRY_FOUND S_OK #define WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND ((HRESULT)0x8A150201) #define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND S_OK #define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE ((HRESULT)0x0A150202) #define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND ((HRESULT)0x8A150203) #define WINGET_INSTALLED_STATUS_FILE_HASH_MATCH S_OK #define WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH ((HRESULT)0x8A150204) #define WINGET_INSTALLED_STATUS_FILE_NOT_FOUND ((HRESULT)0x8A150205) #define WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK ((HRESULT)0x0A150206) #define WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR ((HRESULT)0x8A150207) // Configuration Errors #define WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE ((HRESULT)0x8A15C001) #define WINGET_CONFIG_ERROR_INVALID_YAML ((HRESULT)0x8A15C002) #define WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE ((HRESULT)0x8A15C003) #define WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION ((HRESULT)0x8A15C004) #define WINGET_CONFIG_ERROR_SET_APPLY_FAILED ((HRESULT)0x8A15C005) #define WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER ((HRESULT)0x8A15C006) #define WINGET_CONFIG_ERROR_MISSING_DEPENDENCY ((HRESULT)0x8A15C007) #define WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED ((HRESULT)0x8A15C008) #define WINGET_CONFIG_ERROR_ASSERTION_FAILED ((HRESULT)0x8A15C009) #define WINGET_CONFIG_ERROR_MANUALLY_SKIPPED ((HRESULT)0x8A15C00A) #define WINGET_CONFIG_ERROR_WARNING_NOT_ACCEPTED ((HRESULT)0x8A15C00B) #define WINGET_CONFIG_ERROR_SET_DEPENDENCY_CYCLE ((HRESULT)0x8A15C00C) #define WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE ((HRESULT)0x8A15C00D) #define WINGET_CONFIG_ERROR_MISSING_FIELD ((HRESULT)0x8A15C00E) #define WINGET_CONFIG_ERROR_TEST_FAILED ((HRESULT)0x8A15C00F) #define WINGET_CONFIG_ERROR_TEST_NOT_RUN ((HRESULT)0x8A15C010) #define WINGET_CONFIG_ERROR_GET_FAILED ((HRESULT)0x8A15C011) #define WINGET_CONFIG_ERROR_HISTORY_ITEM_NOT_FOUND ((HRESULT)0x8A15C012) #define WINGET_CONFIG_ERROR_PARAMETER_INTEGRITY_BOUNDARY ((HRESULT)0x8A15C013) // Configuration Processor Errors #define WINGET_CONFIG_ERROR_UNIT_NOT_INSTALLED ((HRESULT)0x8A15C101) #define WINGET_CONFIG_ERROR_UNIT_NOT_FOUND_REPOSITORY ((HRESULT)0x8A15C102) #define WINGET_CONFIG_ERROR_UNIT_MULTIPLE_MATCHES ((HRESULT)0x8A15C103) #define WINGET_CONFIG_ERROR_UNIT_INVOKE_GET ((HRESULT)0x8A15C104) #define WINGET_CONFIG_ERROR_UNIT_INVOKE_TEST ((HRESULT)0x8A15C105) #define WINGET_CONFIG_ERROR_UNIT_INVOKE_SET ((HRESULT)0x8A15C106) #define WINGET_CONFIG_ERROR_UNIT_MODULE_CONFLICT ((HRESULT)0x8A15C107) #define WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE ((HRESULT)0x8A15C108) #define WINGET_CONFIG_ERROR_UNIT_INVOKE_INVALID_RESULT ((HRESULT)0x8A15C109) #define WINGET_CONFIG_ERROR_UNIT_SETTING_CONFIG_ROOT ((HRESULT)0x8A15C110) #define WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE_ADMIN ((HRESULT)0x8A15C111) #define WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR ((HRESULT)0x8A15C112) namespace AppInstaller { // Gets error messages that are presentable to the user. std::string GetUserPresentableMessage(const wil::ResultException& re); std::string GetUserPresentableMessage(const std::exception& e); std::string GetUserPresentableMessage(HRESULT hr); #ifndef WINGET_DISABLE_FOR_FUZZING std::string GetUserPresentableMessage(const winrt::hresult_error& hre); #endif namespace Errors { // Details about an HRESULT struct HResultInformation { constexpr HResultInformation(HRESULT value); constexpr HResultInformation(HRESULT value, std::string_view symbol); virtual ~HResultInformation() = default; HRESULT Value() const; bool operator<(const HResultInformation& other) const; // The symbol will be an empty view if not known. Utility::LocIndView Symbol() const; // Looks up the description of the HRESULT virtual Utility::LocIndString GetDescription() const; // Find information by HRESULT; this lookup is optimized. static std::unique_ptr Find(HRESULT value); // Find information by substring match with the given value. // This lookup is not optimized, as it should only be needed for the error command. static std::vector> Find(std::string_view value); private: HRESULT m_value; std::string_view m_symbol; }; // Gets all of the custom error information for our errors. std::vector> GetWinGetErrors(); } } ================================================ FILE: src/AppInstallerSharedLib/Public/AppInstallerLanguageUtilities.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace AppInstaller { // A helper type that resets itself when it is moved from. template struct ResetWhenMovedFrom { ResetWhenMovedFrom() : m_var{} {} ResetWhenMovedFrom(T t) : m_var{ t } {} // Not copyable ResetWhenMovedFrom(const ResetWhenMovedFrom&) = delete; ResetWhenMovedFrom& operator=(const ResetWhenMovedFrom&) = delete; ResetWhenMovedFrom(ResetWhenMovedFrom&& other) noexcept : m_var(std::move(other.m_var)) { other.m_var = T{}; } ResetWhenMovedFrom& operator=(ResetWhenMovedFrom&& other) noexcept { m_var = std::move(other.m_var); other.m_var = T{}; return *this; } operator T& () { return m_var; } operator const T& () const { return m_var; } private: T m_var; }; // Enables a bool to be used as a destruction indicator. // Default construction *sets the value to false!* using DestructionToken = ResetWhenMovedFrom; // Enable use of folding to execute functions across parameter packs. struct FoldHelper { template FoldHelper& operator,(T&&) { return *this; } }; // Get the integral value for an enum. template constexpr inline std::enable_if_t, std::underlying_type_t> ToIntegral(E e) { return static_cast>(e); } // Get the enum value for an integral. template constexpr inline std::enable_if_t, E> ToEnum(std::underlying_type_t ut) { return static_cast(ut); } // Enum based variant helper. // Enum must be an enum whose first member has the value 0, each subsequent member increases by 1, and the final member is named Max. // Mapping is a template type that takes one template parameter of type Enum, and whose members define value_t as the type for that enum value. template typename Mapping> struct EnumBasedVariant { private: // Used to deduce the variant type; making a variant that includes std::monostate and all Mapping types. template static inline auto Deduce(std::index_sequence) { return std::variant(I)>::value_t...>{}; } public: // Holds data of any type listed in Mapping. using variant_t = decltype(Deduce(std::make_index_sequence(Enum::Max)>())); // Gets the index into the variant for the given Data. static constexpr inline size_t Index(Enum e) { return static_cast(e) + 1; } }; // An action that can be taken on an EnumBasedVariantMap. enum class EnumBasedVariantMapAction { Add, Contains, Get, }; // A callback function that can be used for logging map actions. template using EnumBasedVariantMapActionCallback = void (*)(const void* map, Enum value, EnumBasedVariantMapAction action); // Provides a map of the Enum to the mapped types. template typename Mapping, EnumBasedVariantMapActionCallback Callback = nullptr> struct EnumBasedVariantMap { using Variant = EnumBasedVariant; template using mapping_t = typename Mapping::value_t; // Adds a value to the map, or overwrites an existing entry. // This must be used to create the initial data entry, but Get can be used to modify. template void Add(mapping_t&& v) { if constexpr (Callback) { Callback(this, E, EnumBasedVariantMapAction::Add); } m_data[E].emplace(std::move(std::forward>(v))); } template void Add(const mapping_t& v) { if constexpr (Callback) { Callback(this, E, EnumBasedVariantMapAction::Add); } m_data[E].emplace(v); } // Return a value indicating whether the given enum is stored in the map. bool Contains(Enum e) const { if constexpr (Callback) { Callback(this, e, EnumBasedVariantMapAction::Contains); } return (m_data.find(e) != m_data.end()); } // Gets the value. template mapping_t& Get() { if constexpr (Callback) { Callback(this, E, EnumBasedVariantMapAction::Get); } return std::get(GetVariant(E)); } template const mapping_t& Get() const { if constexpr (Callback) { Callback(this, E, EnumBasedVariantMapAction::Get); } return std::get(GetVariant(E)); } private: typename Variant::variant_t& GetVariant(Enum e) { auto itr = m_data.find(e); THROW_HR_IF_MSG(E_NOT_SET, itr == m_data.end(), "GetVariant(%d)", static_cast(e)); return itr->second; } const typename Variant::variant_t& GetVariant(Enum e) const { auto itr = m_data.find(e); THROW_HR_IF_MSG(E_NOT_SET, itr == m_data.cend(), "GetVariant(%d)", static_cast(e)); return itr->second; } std::map m_data; }; template std::vector GetAllSequentialEnumValues(E initialToSkip) { std::vector result; using underlying_t = std::underlying_type_t; for (underlying_t i = 1 + static_cast(initialToSkip); i < static_cast(E::Max); ++i) { result.emplace_back(static_cast(i)); } return result; } template std::vector GetAllExponentialEnumValues(E initialToSkip) { std::vector result; using underlying_t = std::underlying_type_t; for (underlying_t i = 1 + static_cast(initialToSkip); i < static_cast(E::Max); i <<= 1) { result.emplace_back(static_cast(i)); } return result; } } // Enable enums to be output generically (as their integral value). template std::enable_if_t, std::ostream&> operator<<(std::ostream& out, E e) { return out << AppInstaller::ToIntegral(e); } template struct CopyConstructibleAtomic : public std::atomic { using std::atomic::atomic; using std::atomic::operator=; CopyConstructibleAtomic(const CopyConstructibleAtomic& other) : std::atomic(other.load()) {} }; ================================================ FILE: src/AppInstallerSharedLib/Public/AppInstallerLogging.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #define AICLI_LOG_DIRECT(_logger_,_channel_,_level_,_outstream_) \ do { \ auto _aicli_log_channel = AppInstaller::Logging::Channel:: _channel_; \ auto _aicli_log_level = AppInstaller::Logging::Level:: _level_; \ auto& _aicli_log_log = _logger_; \ if (_aicli_log_log.IsEnabled(_aicli_log_channel, _aicli_log_level)) \ { \ AppInstaller::Logging::LoggingStream _aicli_log_strstr; \ _aicli_log_strstr _outstream_; \ _aicli_log_log.Write(_aicli_log_channel, _aicli_log_level, _aicli_log_strstr.str()); \ } \ } while (0, 0) #define AICLI_LOG(_channel_,_level_,_outstream_) AICLI_LOG_DIRECT(AppInstaller::Logging::Log(),_channel_,_level_,_outstream_) // Consider using this macro when the string might be larger than 4K. // The normal macro has some buffering that occurs; it can cut off larger strings and is slower. #define AICLI_LOG_LARGE_STRING(_channel_,_level_,_headerStream_,_largeString_) \ do { \ auto _aicli_log_channel = AppInstaller::Logging::Channel:: _channel_; \ auto _aicli_log_level = AppInstaller::Logging::Level:: _level_; \ auto& _aicli_log_log = AppInstaller::Logging::Log(); \ if (_aicli_log_log.IsEnabled(_aicli_log_channel, _aicli_log_level)) \ { \ AppInstaller::Logging::LoggingStream _aicli_log_strstr; \ _aicli_log_strstr _headerStream_; \ _aicli_log_log.Write(_aicli_log_channel, _aicli_log_level, _aicli_log_strstr.str()); \ _aicli_log_log.WriteDirect(_aicli_log_channel, _aicli_log_level, _largeString_); \ } \ } while (0, 0) namespace AppInstaller::Logging { // The channel that the log is from. // Channels enable large groups of logs to be enabled or disabled together. enum class Channel : uint32_t { Fail = 0x1, CLI = 0x2, SQL = 0x4, Repo = 0x8, YAML = 0x10, Core = 0x20, Test = 0x40, Config = 0x80, Workflow = 0x100, None = 0, All = 0xFFFFFFFF, Defaults = All & ~(SQL | Workflow), }; DEFINE_ENUM_FLAG_OPERATORS(Channel); // Gets the channel's name as a string. std::string_view GetChannelName(Channel channel); // Gets the channel from it's name. Channel GetChannelFromName(std::string_view channel); // Gets the maximum channel name length in characters. size_t GetMaxChannelNameLength(); // The level of the log. enum class Level { Verbose, Info, Warning, Error, Crit, }; // Indicates a location of significance in the logging stream. enum class Tag { // The initial set of logging has been completed. HeadersComplete, }; // The interface that a log target must implement. struct ILogger { virtual ~ILogger() = default; // Gets the name of the logger for internal use. virtual std::string GetName() const = 0; // Informs the logger of the given log. virtual void Write(Channel channel, Level level, std::string_view message) noexcept = 0; // Informs the logger of the given log with the intention that no buffering occurs (in winget code). virtual void WriteDirect(Channel channel, Level level, std::string_view message) noexcept = 0; // Indicates that the given tag location has occurred. virtual void SetTag(Tag) noexcept {} }; // This type contains the set of loggers that diagnostic logging will be sent to. // Each binary that leverages it must configure any loggers and filters to their // desired level, as nothing is enabled by default. struct DiagnosticLogger { DiagnosticLogger() = default; ~DiagnosticLogger() = default; DiagnosticLogger(const DiagnosticLogger&) = delete; DiagnosticLogger& operator=(const DiagnosticLogger&) = delete; DiagnosticLogger(DiagnosticLogger&&) = delete; DiagnosticLogger& operator=(DiagnosticLogger&&) = delete; // Gets the singleton instance of this type. static DiagnosticLogger& GetInstance(); // NOTE: The logger management functionality is *SINGLE THREAD SAFE*. // This includes with logging itself. // As it is not expected that adding/removing loggers is an // extremely frequent operation, no care has been made to protect // it from modifying loggers while logging may be occurring. // Adds a logger to the active set. void AddLogger(std::unique_ptr&& logger); // Determines if a logger with the given name is present. bool ContainsLogger(const std::string& name); // Removes a logger from the active set, returning it. std::unique_ptr RemoveLogger(const std::string& name); // Removes all loggers. void RemoveAllLoggers(); // Enables the given channel(s), in addition to the currently enabled channels. void EnableChannel(Channel channel); // The given channel mask will become the only enabled channels. void SetEnabledChannels(Channel channel); // Disables the given channel. void DisableChannel(Channel channel); // Sets the enabled level. // All levels above this level will be enabled. // For example; SetLevel(Verbose) will enable all logs. void SetLevel(Level level); // Gets the enabled level. Level GetLevel() const; // Checks whether a given channel and level are enabled. bool IsEnabled(Channel channel, Level level) const; // Writes a log line, if the given channel and level are enabled. void Write(Channel channel, Level level, std::string_view message); // Writes a log line, if the given channel and level are enabled. // Use to make large logs more efficient by writing directly to the output streams. void WriteDirect(Channel channel, Level level, std::string_view message); // Indicates that the given tag location has occurred. void SetTag(Tag tag); private: std::vector> m_loggers; Channel m_enabledChannels = Channel::None; Level m_enabledLevel = Level::Info; }; DiagnosticLogger& Log(); // Calls the various stream format functions to produce an 8 character hexadecimal output. std::ostream& SetHRFormat(std::ostream& out); // This type allows us to override the default behavior of output operators for logging. struct LoggingStream { // Force use of the UTF-8 string from a file path. // This should not be necessary when we move to C++20 and convert to using u8string. friend AppInstaller::Logging::LoggingStream& operator<<(AppInstaller::Logging::LoggingStream& out, const std::filesystem::path& path) { out.m_out << path.u8string(); return out; } // Enums template friend std::enable_if_t>, AppInstaller::Logging::LoggingStream&> operator<<(AppInstaller::Logging::LoggingStream& out, T t) { out.m_out << ToIntegral(t); return out; } // Everything else. template friend std::enable_if_t, std::filesystem::path>, std::is_enum>>, AppInstaller::Logging::LoggingStream&> operator<<(AppInstaller::Logging::LoggingStream& out, T&& t) { out.m_out << std::forward(t); return out; } std::string str() const { return m_out.str(); } private: std::stringstream m_out; }; } namespace std { std::ostream& operator<<(std::ostream& out, const std::chrono::system_clock::time_point& time); std::ostream& operator<<(std::ostream& out, const GUID& guid); } ================================================ FILE: src/AppInstallerSharedLib/Public/AppInstallerSHA256.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Utility { // Forward declaration of type defined within PAL struct SHA256Context; // Class used to compute SHA256 hashes over various sets of data. // Create one and Add data to it if the data is not all available, // or simply call ComputeHash if the data is all in memory. class SHA256 { public: using HashBuffer = std::vector; constexpr static size_t HashBufferSizeInBytes = 32; constexpr static size_t HashStringSizeInChars = 64; struct HashDetails { HashBuffer Hash; uint64_t SizeInBytes = 0; }; SHA256(); // Adds the next chunk of data to the hash. void Add(const uint8_t* buffer, size_t cbBuffer); inline void Add(const std::vector& buffer) { Add(buffer.data(), buffer.size()); } // Gets the hash of the data. This is a destructive action; the accumulated hash // value will be returned and the object can no longer be used. void Get(HashBuffer& hash); inline HashBuffer Get() { HashBuffer result{}; Get(result); return result; } // Computes the hash of the given buffer immediately. static HashBuffer ComputeHash(const uint8_t* buffer, std::uint32_t cbBuffer); // Computes the hash of the given buffer immediately. static HashBuffer ComputeHash(const std::vector& buffer); // Computes the hash of the given string immediately. static HashBuffer ComputeHash(std::string_view buffer); // Computes the hash from a given stream. static HashBuffer ComputeHash(std::istream& in); // Computes the hash from a given stream. static HashDetails ComputeHashDetails(std::istream& in); // Computes the hash from a given file path. static HashBuffer ComputeHashFromFile(const std::filesystem::path& path); static std::string ConvertToString(const HashBuffer& hashBuffer); static std::wstring ConvertToWideString(const HashBuffer& hashBuffer); static HashBuffer ConvertToBytes(const std::string& hashStr); static HashBuffer ConvertToBytes(const std::wstring& hashStr); // Returns a value indicating whether the two hashes are equal. static bool AreEqual(const HashBuffer& first, const HashBuffer& second); private: void EnsureNotFinished() const; struct SHA256ContextDeleter { void operator()(SHA256Context* context); }; std::unique_ptr context; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/AppInstallerStrings.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include namespace AppInstaller::Utility { // Converts the given UTF16 string to UTF8 std::string ConvertToUTF8(std::wstring_view input); // Converts the given UTF8 string to UTF16 std::wstring ConvertToUTF16(std::string_view input, UINT codePage = CP_UTF8); // Tries to convert the given UTF8 string to UTF16 std::optional TryConvertToUTF16(std::string_view input, UINT codePage = CP_UTF8); // Converts the given UTF8 string to UTF32 std::u32string ConvertToUTF32(std::string_view input); // Normalizes a UTF8 string to the given form. std::string Normalize(std::string_view input, NORM_FORM form = NORM_FORM::NormalizationKC); // Normalizes a UTF16 string to the given form. std::wstring Normalize(std::wstring_view input, NORM_FORM form = NORM_FORM::NormalizationKC); // Replaces any embedded null character found within the string. void ReplaceEmbeddedNullCharacters(std::string& s, char c = ' '); // Type to hold and force a normalized UTF8 string. // Also enables further normalization by replacing embedded null characters with spaces. template struct NormalizedUTF8 : public std::string { NormalizedUTF8() = default; template NormalizedUTF8(const char(&s)[Size]) { AssignValue(std::string_view{ s, (s[Size - 1] == '\0' ? Size - 1 : Size) }); } NormalizedUTF8(std::string_view sv) { AssignValue(sv); } NormalizedUTF8(std::string& s) { AssignValue(s); } NormalizedUTF8(const std::string& s) { AssignValue(s); } NormalizedUTF8(std::string&& s) { AssignValue(s); } NormalizedUTF8(std::wstring_view sv) { AssignValue(sv); } NormalizedUTF8(const NormalizedUTF8& other) = default; NormalizedUTF8& operator=(const NormalizedUTF8& other) = default; NormalizedUTF8(NormalizedUTF8&& other) = default; NormalizedUTF8& operator=(NormalizedUTF8&& other) = default; template NormalizedUTF8& operator=(const char(&s)[Size]) { AssignValue(std::string_view{ s, (s[Size - 1] == '\0' ? Size - 1 : Size) }); return *this; } NormalizedUTF8& operator=(std::string_view sv) { AssignValue(sv); return *this; } NormalizedUTF8& operator=(const std::string& s) { AssignValue(s); return *this; } NormalizedUTF8& operator=(std::string&& s) { AssignValue(s); return *this; } private: void AssignValue(std::string_view sv) { assign(Normalize(sv, Form)); if constexpr (ConvertEmbeddedNullToSpace) { ReplaceEmbeddedNullCharacters(*this); } } void AssignValue(std::wstring_view sv) { assign(ConvertToUTF8(Normalize(sv, Form))); if constexpr (ConvertEmbeddedNullToSpace) { ReplaceEmbeddedNullCharacters(*this); } } }; using NormalizedString = NormalizedUTF8; // Compares the two UTF8 strings in a case-insensitive manner. // Use this if one of the values is a known value, and thus ToLower is sufficient. bool CaseInsensitiveEquals(std::string_view a, std::string_view b); // Compares the two UTF16 strings in a case-insensitive manner. // Use this if one of the values is a known value, and thus ToLower is sufficient. bool CaseInsensitiveEquals(std::wstring_view a, std::wstring_view b); // Returns if a UTF8 string is contained within a vector in a case-insensitive manner. bool CaseInsensitiveContains(const std::vector& a, std::string_view b); // Determines if string a starts with string b. UTF16. bool StartsWith(std::wstring_view a, std::wstring_view b); // Determines if string a starts with string b. UTF8. // Use this if one of the values is a known value, and thus ToLower is sufficient. bool CaseInsensitiveStartsWith(std::string_view a, std::string_view b); // Determines if string a starts with string b. UTF16. // Use this if one of the values is a known value, and thus ToLower is sufficient. bool CaseInsensitiveStartsWith(std::wstring_view a, std::wstring_view b); // Determines if string a contains string b. // Use this if one of the values is a known value, and thus ToLower is sufficient. bool CaseInsensitiveContainsSubstring(std::string_view a, std::string_view b); // Determines if string a contains string b. bool ContainsSubstring(std::string_view a, std::string_view b); // Compares the two UTF8 strings in a case-insensitive manner, using ICU for case folding. bool ICUCaseInsensitiveEquals(std::string_view a, std::string_view b); // Determines if string a starts with string b, using ICU for case folding. bool ICUCaseInsensitiveStartsWith(std::string_view a, std::string_view b); // Returns the number of grapheme clusters (characters) in an UTF8-encoded string. size_t UTF8Length(std::string_view input); // Returns the number of units the UTF8-encoded string will take in terminal output. Some characters take 2 units in terminal output. size_t UTF8ColumnWidth(const NormalizedUTF8& input); // Returns a substring view in an UTF8-encoded string. Offset and count are measured in grapheme clusters (characters). std::string_view UTF8Substring(std::string_view input, size_t offset, size_t count); // Returns a substring view in an UTF8-encoded string trimmed to be at most expected length. Length is measured as units taken in terminal output. // Note the returned substring view might be less than specified length as some characters might take 2 units in terminal output. std::string UTF8TrimRightToColumnWidth(const NormalizedUTF8&, size_t expectedWidth, size_t& actualWidth); // Get the lower case version of the given std::string std::string ToLower(std::string_view in); // Get the lower case version of the given std::wstring std::wstring ToLower(std::wstring_view in); // Folds the case of the given std::string // See https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#case-folding std::string FoldCase(std::string_view input); // Folds the case of the given NormalizedString, returning it as also Normalized // See https://unicode-org.github.io/icu/userguide/transforms/casemappings.html#case-folding NormalizedString FoldCase(const NormalizedString& input); // Checks if the input string is empty or whitespace bool IsEmptyOrWhitespace(std::string_view str); bool IsEmptyOrWhitespace(std::wstring_view str); // Find token in the input string and replace with value. // Returns a value indicating whether a replacement occurred. bool FindAndReplace(std::string& inputStr, std::string_view token, std::string_view value); // Replaces the token in the input string with value while copying to a new result. std::wstring ReplaceWhileCopying(std::wstring_view input, std::wstring_view token, std::wstring_view value); // Removes whitespace from the beginning and end of the string. std::string& Trim(std::string& str); std::string Trim(const std::string& str); std::string Trim(std::string&& str); std::string_view& Trim(std::string_view& str); std::string_view Trim(std::string_view&& str); // Removes whitespace from the beginning and end of the string. std::wstring& Trim(std::wstring& str); std::wstring Trim(const std::wstring& str); std::wstring Trim(std::wstring&& str); std::wstring_view& Trim(std::wstring_view& str); std::wstring_view Trim(std::wstring_view&& str); // Reads the entire stream into a string. std::string ReadEntireStream(std::istream& stream); // Reads the entire stream into a byte array. std::vector ReadEntireStreamAsByteArray(std::istream& stream); // Expands environment variables within the input. std::wstring ExpandEnvironmentVariables(const std::wstring& input); // Converts the candidate path part into one suitable for the actual file system std::string MakeSuitablePathPart(std::string_view candidate); // Splits the file name part off of the given URI. std::pair SplitFileNameFromURI(std::string_view uri); // Gets the file name part of the given URI. std::filesystem::path GetFileNameFromURI(std::string_view uri); // Splits the string into words. std::vector SplitIntoWords(std::string_view input); // Splits the string into lines. // Drops empty lines. std::vector SplitIntoLines(std::string_view input, size_t maximum = 0); // Removes lines from the vector (and/or characters from the last line) so that it contains the maximum number of lines. // Returns true if changes were made, false if not. bool LimitOutputLines(std::vector& lines, size_t lineWidth, size_t maximum); // Converts a container to a string representation of it. template std::string ConvertContainerToString(const T& container, U toString) { std::ostringstream strstr; strstr << '['; bool firstItem = true; for (const auto& item : container) { if (firstItem) { firstItem = false; } else { strstr << ", "; } strstr << toString(item); } strstr << ']'; return strstr.str(); // We need C++20 to get std::move(strstr).str() to extract the string from inside the stream } template std::string ConvertContainerToString(const T& container) { return ConvertContainerToString(container, [](const auto& item) { return item; }); } template std::basic_string StringOrEmptyIfNull(const CharType* string) { if (string) { return { string }; } else { return {}; } } // Converts the given bytes into a hexadecimal string. std::string ConvertToHexString(const std::vector& buffer, size_t byteCount = 0); // Converts the given hexadecimal string into bytes. std::vector ParseFromHexString(const std::string& value, size_t byteCount = 0); // Join a string vector using the provided separator. LocIndString Join(LocIndView separator, const std::vector& vector); // Join a string vector using the provided separator. std::string Join(std::string_view separator, const std::vector& vector); // Splits the string using the provided separator. Entries can also be trimmed. std::vector Split(const std::string& input, char separator, bool trim = false); std::vector SplitView(const std::string& input, char separator, bool trim = false); std::vector Split(std::string_view input, char separator, bool trim = false); // Splits the string using the provided separator. Entries can also be trimmed. std::vector Split(const std::wstring& input, wchar_t separator, bool trim = false); std::vector SplitView(const std::wstring& input, wchar_t separator, bool trim = false); std::vector Split(std::wstring_view input, wchar_t separator, bool trim = false); // Format an input string by replacing placeholders {index} with provided values at corresponding indices. // Note: After upgrading to C++20, this function should be deprecated in favor of std::format. template std::string Format(std::string inputStr, T ... args) { int index = 0; (FindAndReplace(inputStr, "{" + std::to_string(index++) + "}", (std::ostringstream() << args).str()),...); return inputStr; } // Converts the given boolean value to a string. std::string_view ConvertBoolToString(bool value); // Converts the given string view into a bool. std::optional TryConvertStringToBool(const std::string_view& value); // Converts the given string view into an int32. std::optional TryConvertStringToInt32(const std::string_view& value); // Converts the given GUID value to a string. std::string ConvertGuidToString(const GUID& value); // Creates a new GUID and returns the string value. std::wstring CreateNewGuidNameWString(); // Converts the input string to a DWORD value using std::stoul and returns a boolean value based on the resulting DWORD value. bool IsDwordFlagSet(const std::string& value); // Finds the next control code index that would be replaced. // Returns std::string::npos if not found. size_t FindControlCodeToConvert(std::string_view input, size_t offset = 0); // Converts most control codes in the input to their corresponding control picture in the output. // Exempts tab, line feed, and carriage return from being replaced. std::string ConvertControlCodesToPictures(std::string_view input); // Generates a random alpha numeric string. std::string GetRandomString(size_t size = 8); } ================================================ FILE: src/AppInstallerSharedLib/Public/AppInstallerVersions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Utility { using namespace std::string_view_literals; // Creates a comparable version object from a string. // Versions are parsed by: // 1. Parse approximate comparator sign if applicable // 2. Splitting the string based on the given splitChars (or DefaultSplitChars) // 3. Parsing a leading, positive integer from each split part // 4. Saving any remaining, non-digits as a supplemental value // // Versions are compared by: // for each part in each version // if one side has no more parts, perform the remaining comparisons against an empty part // if integers not equal, return comparison of integers // else if only one side has a non-empty string part, it is less // else if string parts not equal, return comparison of strings // if each part has been compared, use approximate comparator if applicable // // Note: approximate to another approximate version is invalid. // approximate to Unknown is invalid. struct Version { // Used in approximate version to indicate the relation to the base version. enum class ApproximateComparator { None, LessThan, GreaterThan, }; // The default characters to split a version string on. constexpr static std::string_view DefaultSplitChars = "."sv; Version() = default; Version(const std::string& version, std::string_view splitChars = DefaultSplitChars) : Version(std::string(version), splitChars) {} Version(std::string&& version, std::string_view splitChars = DefaultSplitChars); // Constructing an approximate version from a base version. Version(Version baseVersion, ApproximateComparator approximateComparator); // Resets the version's value to the input. virtual void Assign(std::string version, std::string_view splitChars = DefaultSplitChars); // Gets the full version string used to construct the Version. const std::string& ToString() const { return m_version; } bool operator<(const Version& other) const; bool operator>(const Version& other) const; bool operator<=(const Version& other) const; bool operator>=(const Version& other) const; bool operator==(const Version& other) const; bool operator!=(const Version& other) const; // Determines if this version is the sentinel value defining the 'Latest' version bool IsLatest() const; // Returns a Version that will return true for IsLatest static Version CreateLatest(); // Determines if this version is the sentinel value defining an 'Unknown' version bool IsUnknown() const; // Returns a Version that will return true for IsUnknown static Version CreateUnknown(); // Gets a bool indicating whether the full version string is empty. // Does not indicate that Parts is empty; for instance when "0.0" is given, // this will be false while GetParts().empty() would be true. bool IsEmpty() const { return m_version.empty(); } // An individual version part in between split characters. struct Part { Part() = default; Part(uint64_t integer) : Integer(integer) {} Part(const std::string& part); Part(uint64_t integer, std::string other); bool operator<(const Part& other) const; bool operator==(const Part& other) const; bool operator!=(const Part& other) const; uint64_t Integer = 0; std::string Other; private: std::string m_foldedOther; }; // Gets the part breakdown for a given version. const std::vector& GetParts() const { return m_parts; } // Gets the part at the given index; or the implied zero part if past the end. const Part& PartAt(size_t index) const; // Returns if the version is an approximate version. bool IsApproximate() const { return m_approximateComparator != ApproximateComparator::None; } // Get the base version from approximate version, or return a copy if the version is not approximate. Version GetBaseVersion() const; protected: bool IsBaseVersionLatest() const; bool IsBaseVersionUnknown() const; // Called by overloaded less than operator implementation when base version already compared and equal, less than determined by approximate comparator. bool ApproximateCompareLessThan(const Version& other) const; std::string m_version; std::vector m_parts; bool m_trimPrefix = true; ApproximateComparator m_approximateComparator = ApproximateComparator::None; // Remove trailing empty parts (0 or empty) void Trim(); }; // Version that does not have leading non-digit characters trimmed struct RawVersion : protected Version { RawVersion() { m_trimPrefix = false; } RawVersion(std::string version, std::string_view splitChars = DefaultSplitChars); using Version::GetParts; }; // Four parts version number: 16-bits.16-bits.16-bits.16-bits struct UInt64Version : public Version { UInt64Version() = default; UInt64Version(UINT64 version); UInt64Version(uint16_t major, uint16_t minor, uint16_t build, uint16_t revision); UInt64Version(std::string&& version, std::string_view splitChars = DefaultSplitChars); UInt64Version(const std::string& version, std::string_view splitChars = DefaultSplitChars) : UInt64Version(std::string(version), splitChars) {} void Assign(std::string version, std::string_view splitChars = DefaultSplitChars) override; void Assign(UINT64 version); void Assign(uint16_t major, uint16_t minor, uint16_t build, uint16_t revision); UINT64 Major() const { return m_parts.size() > 0 ? m_parts[0].Integer : 0; } UINT64 Minor() const { return m_parts.size() > 1 ? m_parts[1].Integer : 0; } UINT64 Build() const { return m_parts.size() > 2 ? m_parts[2].Integer : 0; } UINT64 Revision() const { return m_parts.size() > 3 ? m_parts[3].Integer : 0; } }; // A semantic version as defined by semver.org // This is currently fairly loose in its restrictions on a version, and is largely to separate out the prelease and build metadata portions. struct SemanticVersion : public Version { SemanticVersion() = default; SemanticVersion(std::string&& version); SemanticVersion(const std::string& version) : SemanticVersion(std::string(version)) {} void Assign(std::string version, std::string_view splitChars = DefaultSplitChars) override; // Indicates that the version is pre-release bool IsPrerelease() const; // The pre-release version const Version& PrereleaseVersion() const; // Indicates that the version has build metadata bool HasBuildMetadata() const; // The build metadata version const Version& BuildMetadata() const; private: Version m_prerelease; Version m_buildMetadata; }; // Version range represented by a min version and max version, both inclusive. struct VersionRange { VersionRange() { m_isEmpty = true; }; VersionRange(Version minVersion, Version maxVersion); bool IsEmpty() const { return m_isEmpty; } // Checks if version ranges overlap. Empty version range does not overlap with any version range. bool Overlaps(const VersionRange& other) const; // Checks if the version range is effectively the same as a single version. bool IsSameAsSingleVersion(const Version& version) const; // Checks if a version is within the version range bool ContainsVersion(const Version& version) const; // < operator will thow if compared with an empty range or an overlapped range bool operator<(const VersionRange& other) const; const Version& GetMinVersion() const; const Version& GetMaxVersion() const; private: Version m_minVersion; Version m_maxVersion; bool m_isEmpty = false; }; // A range of versions indicated by a version and optionally a wildcard at the end. struct GatedVersion { GatedVersion() {} GatedVersion(Version&& version) : m_version(std::move(version)) {} GatedVersion(const Version& version) : m_version(version) {} GatedVersion(std::string_view versionString) : m_version(std::string{ versionString }) {} GatedVersion(const std::string& versionString) : m_version(versionString) {} // Determines whether a given version falls within this Gated version. // I.e., whether it matches up to the wildcard bool IsValidVersion(Version version) const; bool operator==(const GatedVersion& other) const { return m_version == other.m_version; } const std::string& ToString() const { return m_version.ToString(); } private: // Hold the version string as a Version object that makes it easy to access each of // the version's parts. The real magic is in IsValidVersion() Version m_version; }; // A channel string; existing solely to give a type. // // Compared lexicographically. struct Channel { Channel() = default; Channel(const std::string& channel) : m_channel(channel) {} Channel(std::string&& channel) : m_channel(std::move(channel)) {} const std::string& ToString() const { return m_channel; } bool operator<(const Channel& other) const; private: std::string m_channel; }; // Contains a version and channel. // These are compared by: // if channel not equal, return compare channel // else return !compare version // // The implication of this is that the default less sort will be: // 2.0, "" // 1.0, "" // 3.0, "alpha" // 2.0, "alpha" struct VersionAndChannel { VersionAndChannel() = default; VersionAndChannel(Version&& version, Channel&& channel); const Version& GetVersion() const { return m_version; } const Channel& GetChannel() const { return m_channel; } std::string ToString() const; bool operator<(const VersionAndChannel& other) const; // A convenience function to make more semantic sense at call sites over the somewhat awkward less than ordering. bool IsUpdatedBy(const VersionAndChannel& other) const; private: Version m_version; Channel m_channel; }; // Checks if there are overlaps within the list of version ranges bool HasOverlapInVersionRanges(const std::vector& ranges); // The OpenType font version. // The format of this version type is 'Version 1.234 ;567' // The only part that is of importance is the 'Major.Minor' parts. // The 'Version' string is typically found at the beginning of the version string. // Any value after a digit that is not a '.' represents some other meaning. struct OpenTypeFontVersion : Version { OpenTypeFontVersion() = default; OpenTypeFontVersion(std::string&& version); OpenTypeFontVersion(const std::string& version) : OpenTypeFontVersion(std::string(version)) {} void Assign(std::string version, std::string_view splitChars = DefaultSplitChars) override; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/Telemetry/MicrosoftTelemetry.h ================================================ /* ++ Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. Module Name: MicrosoftTelemetry.h Abstract: Macro definitions used by this project's TraceLogging ETW providers: - Configuration macros that select the ETW Provider Groups to be used by this project. - Constants for tags that are commonly used in Microsoft's TraceLogging-based ETW. Different versions of this file use different definitions for the TraceLoggingOption configuration macros. The definitions in this file are empty. As a result, providers using this configuration file will not join any ETW Provider Groups and will not be given any special treatment by group-sensitive ETW listeners. Environment: User mode or kernel mode. --*/ #pragma once // Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition // in this file configures the provider as a normal (non-telemetry) provider. #define TraceLoggingOptionMicrosoftTelemetry() \ // Empty definition for TraceLoggingOptionMicrosoftTelemetry // Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition // in this file configures the provider as a normal (non-telemetry) provider. #define TraceLoggingOptionWindowsCoreTelemetry() \ // Empty definition for TraceLoggingOptionWindowsCoreTelemetry // Event privacy tags. Use the PDT macro values for the tag parameter, e.g.: // TraceLoggingWrite(..., // TelemetryPrivacyDataTag(PDT_BrowsingHistory | PDT_ProductAndServiceUsage), // ...); #define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "PartA_PrivTags") #define PDT_BrowsingHistory 0x0000000000000002u #define PDT_DeviceConnectivityAndConfiguration 0x0000000000000800u #define PDT_InkingTypingAndSpeechUtterance 0x0000000000020000u #define PDT_ProductAndServicePerformance 0x0000000001000000u #define PDT_ProductAndServiceUsage 0x0000000002000000u #define PDT_SoftwareSetupAndInventory 0x0000000080000000u // Event categories specified via keywords, e.g.: // TraceLoggingWrite(..., // TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), // ...); #define MICROSOFT_KEYWORD_CRITICAL_DATA 0x0000800000000000 // Bit 47 #define MICROSOFT_KEYWORD_MEASURES 0x0000400000000000 // Bit 46 #define MICROSOFT_KEYWORD_TELEMETRY 0x0000200000000000 // Bit 45 #define MICROSOFT_KEYWORD_RESERVED_44 0x0000100000000000 // Bit 44 (reserved for future assignment) // Event categories specified via event tags, e.g.: // TraceLoggingWrite(..., // TraceLoggingEventTag(MICROSOFT_EVENTTAG_REALTIME_LATENCY), // ...); #define MICROSOFT_EVENTTAG_DROP_USER_IDS 0x00008000 #define MICROSOFT_EVENTTAG_AGGREGATE 0x00010000 #define MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP 0x00020000 #define MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY 0x00040000 #define MICROSOFT_EVENTTAG_CORE_DATA 0x00080000 #define MICROSOFT_EVENTTAG_INJECT_XTOKEN 0x00100000 #define MICROSOFT_EVENTTAG_REALTIME_LATENCY 0x00200000 #define MICROSOFT_EVENTTAG_NORMAL_LATENCY 0x00400000 #define MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE 0x00800000 #define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000 #define MICROSOFT_EVENTTAG_DROP_PII 0x02000000 #define MICROSOFT_EVENTTAG_HASH_PII 0x04000000 #define MICROSOFT_EVENTTAG_MARK_PII 0x08000000 // Field categories specified via field tags, e.g.: // TraceLoggingWrite(..., // TraceLoggingString(szUser, "UserName", "User's name", MICROSOFT_FIELDTAG_HASH_PII), // ...); #define MICROSOFT_FIELDTAG_DROP_PII 0x04000000 #define MICROSOFT_FIELDTAG_HASH_PII 0x08000000 ================================================ FILE: src/AppInstallerSharedLib/Public/Telemetry/WinEventLogLevels.h ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. // From winmeta.h #define WINEVENT_LEVEL_LOG_ALWAYS 0x0 #define WINEVENT_LEVEL_CRITICAL 0x1 #define WINEVENT_LEVEL_ERROR 0x2 #define WINEVENT_LEVEL_WARNING 0x3 #define WINEVENT_LEVEL_INFO 0x4 #define WINEVENT_LEVEL_VERBOSE 0x5 ================================================ FILE: src/AppInstallerSharedLib/Public/winget/AsyncTokens.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::WinRT { namespace details { // Type erasing interface for winrt cancellation token. struct AsyncCancellationTypeErasure { virtual ~AsyncCancellationTypeErasure() = default; virtual bool IsCancelled() const noexcept = 0; virtual void Callback(winrt::delegate<>&& callback) const noexcept = 0; virtual void Cancel() noexcept = 0; }; // Type containing winrt cancellation token wrapper. template struct AsyncCancellationT : public AsyncCancellationTypeErasure { using Token = winrt::impl::cancellation_token; AsyncCancellationT(Token&& token) : m_token(std::move(token)) {} bool IsCancelled() const noexcept override { return m_token(); } void Callback(winrt::delegate<>&& callback) const noexcept override { m_token.callback(std::move(callback)); } void Cancel() noexcept override { // This is a bit of a hack, but the cancellation_token provides no access to the underlying promise for the purpose of cancellation. static_assert(sizeof(Token) == sizeof(Promise*), "We expect that the cancellation_token has only 1 member and it is a Promise*"); (*reinterpret_cast(&m_token))->Cancel(); } private: Token m_token; }; } // May hold a cancellation token and provide the ability to check its status. // If empty, it will act as if it is never cancelled. struct AsyncCancellation { // Create an empty cancellation object, which will never be cancelled. AsyncCancellation() = default; // Create a cancellation object from the winrt token. template AsyncCancellation(winrt::impl::cancellation_token&& token) { m_token = std::make_shared>(std::move(token)); } // Returns true if the operation has been cancelled, false if not. bool IsCancelled() const noexcept { return m_token ? m_token->IsCancelled() : false; } // Throws the appropriate exception if the operation has been cancelled. void ThrowIfCancelled() const { if (IsCancelled()) { AICLI_LOG(Core, Warning, << "Operation cancelled"); throw winrt::hresult_canceled(); } } // Sets a callback that will be invoked on cancellation. void Callback(winrt::delegate<>&& callback) const noexcept { if (m_token) { m_token->Callback(std::move(callback)); } } std::weak_ptr GetWeak() { return m_token; } private: std::shared_ptr m_token; }; namespace details { // Type erasing interface for winrt progress token. template struct AsyncProgressTypeErasure { virtual ~AsyncProgressTypeErasure() = default; virtual void Progress(ProgressT const& progress) const = 0; virtual void Result(ResultT const& result) const = 0; }; // Type containing winrt progress token wrapper. template struct AsyncProgressT : public AsyncProgressTypeErasure { using Token = winrt::impl::progress_token; AsyncProgressT(Token&& token) : m_token(std::move(token)) {} void Progress(ProgressT const& progress) const override { m_token(progress); } void Result(ResultT const& result) const override { m_token.set_result(result); } private: Token m_token; }; // Type containing winrt EventHandler. template struct AsyncProgressEventHandlerT : public AsyncProgressTypeErasure { using Token = winrt::Windows::Foundation::EventHandler; AsyncProgressEventHandlerT(Token&& token) : m_token(std::move(token)) {} void Progress(ProgressT const& progress) const override { m_token(m_result, progress); } void Result(ResultT const& result) const override { m_result = result; } private: mutable ResultT m_result = nullptr; Token m_token; }; } // May hold a progress token and provide the ability to send progress updates. // If empty, progress will be dropped on calls here. template struct AsyncProgress : public AsyncCancellation { // Create an empty progress object. AsyncProgress() = default; // Create a progress object from the winrt token. template AsyncProgress(winrt::impl::progress_token&& progress, winrt::impl::cancellation_token&& cancellation) : AsyncCancellation(std::move(cancellation)) { m_token = std::make_unique>(std::move(progress)); } // Create a progress object from an EventHandler. template AsyncProgress(winrt::Windows::Foundation::EventHandler&& progress, winrt::impl::cancellation_token&& cancellation) : AsyncCancellation(std::move(cancellation)) { m_token = std::make_unique>(std::move(progress)); } // Create a cancellation only object. template AsyncProgress(winrt::impl::cancellation_token&& cancellation) : AsyncCancellation(std::move(cancellation)) { } // Sends progress if this object is not empty. void Progress(ProgressT const& progress) const { if (m_token) { m_token->Progress(progress); } } // Sets the result onto the progress object if it is not empty. void Result(ResultT const& result) const { if (m_token) { m_token->Result(result); } } private: std::unique_ptr> m_token; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/COMStaticStorage.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::WinRT { // Contains registration for static storage so that they can be cleared. struct COMStaticStorageStatics { // Adds a static storage key to the set of known items. static void AddStaticStorageItem(const winrt::hstring& name, const winrt::Windows::Foundation::IInspectable& item); // Removes all known static storage items. static void ResetAll(); private: COMStaticStorageStatics() = default; static COMStaticStorageStatics& Instance(); winrt::slim_mutex m_lock; std::set m_items; }; // https://devblogs.microsoft.com/oldnewthing/20210215-00/?p=104865 // Base class for an object that needs to live in the COM static store. // An object needs to use this if it has: // - static lifetime // - references to externally implemented COM objects // // Additionally, it should *not* contain references to WRL counted objects implemented by this module. // If it does, it will prevent the module from being unloaded until COM is uninitialized, which is often never. template struct COMStaticStorageBase { private: struct DataHolder : public winrt::implements { std::shared_ptr m_shared{ std::make_shared() }; }; std::weak_ptr m_weak; winrt::slim_mutex m_lock; winrt::hstring m_name; public: COMStaticStorageBase(std::wstring_view name) : m_name(name) {} std::shared_ptr Get() { { const std::shared_lock lock{ m_lock }; if (auto cached = m_weak.lock()) { return cached; } } auto value = winrt::make_self(); const winrt::slim_lock_guard lock{ m_lock }; if (auto cached = m_weak.lock()) { return cached; } COMStaticStorageStatics::AddStaticStorageItem(m_name, value.as()); m_weak = value->m_shared; return value->m_shared; } void Reset() { winrt::Windows::ApplicationModel::Core::CoreApplication::Properties().TryRemove(m_name); } }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/Certificates.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Certificates { // Defines the types of certificate pinning to perform. enum class PinningVerificationType : uint32_t { // No validation; accepts anything. None = 0x0, // Validates that the public keys match; requires a certificate to be loaded. PublicKey = 0x1, // Validates that the full subjects match; requires a certificate to be loaded. Subject = 0x2, // Validates that the full issuers match; requires a certificate to be loaded. Issuer = 0x4, // Allows for unknown certificates in the chain; will continue to consume certificates from the chain until it matches. // Requires partial chain. AnyIssuer = 0x8, // Requires that the certificate is not a leaf. RequireNonLeaf = 0x10, }; DEFINE_ENUM_FLAG_OPERATORS(PinningVerificationType); std::ostream& operator<<(std::ostream& out, PinningVerificationType value); // The position within a chain that a certificate is located. enum class CertificateChainPosition { Unknown = 0x0, // The start of the chain. Root = 0x1, // An indeterminate intermediate along the chain. Intermediate = 0x2, // The final certificate of the chain. Leaf = 0x4, }; DEFINE_ENUM_FLAG_OPERATORS(CertificateChainPosition); std::ostream& operator<<(std::ostream& out, CertificateChainPosition value); // The result of validating a single certificate. enum class CertificatePinningValidationResult { // The certificate was accepted as valid. Accepted, // The certificate was rejected as invalid. Rejected, // The next certificate in the chain should be validated against the current details. // For use by partial chain validation that does not require exacting chain configurations. Skipped, }; // Contains the specific information about a certificate to pin. struct PinningDetails { PinningDetails() = default; PinningDetails(const PinningDetails&) = default; PinningDetails& operator=(const PinningDetails&) = default; PinningDetails(PinningDetails&&) = default; PinningDetails& operator=(PinningDetails&&) = default; // Loads the certificate context. PinningDetails& LoadCertificate(int resource, int resourceType); PinningDetails& LoadCertificate(const std::vector& certificateBytes); PinningDetails& LoadCertificate(const std::pair certificateBytes); PCCERT_CONTEXT GetCertificate() const { return m_certificateContext.get(); } PinningDetails& SetPinning(PinningVerificationType type); PinningVerificationType GetPinning() const { return m_pinning; } // Validates the given certificate against the pinning information. CertificatePinningValidationResult Validate(PCCERT_CONTEXT certContext, CertificateChainPosition position) const; // Outputs a description of the pinning details. void OutputDescription(std::ostream& stream, std::string_view indent) const; // Loads the pinning details from the given JSON. [[nodiscard]] bool LoadFrom(const Json::Value& configuration); // Determines how far the certificate is through its lifespan. double GetRemainingLifetimePercentage() const; #ifndef AICLI_DISABLE_TEST_HOOKS using CustomValidationFunction = std::function; void SetCustomValidationFunction(CustomValidationFunction function) { m_customValidation = std::move(function); } private: CustomValidationFunction m_customValidation; #endif private: wil::shared_cert_context m_certificateContext; PinningVerificationType m_pinning = PinningVerificationType::None; }; // Contains the full chain of pinning details. struct PinningChain { PinningChain() = default; PinningChain(const PinningChain&) = default; PinningChain& operator=(const PinningChain&) = default; PinningChain(PinningChain&&) = default; PinningChain& operator=(PinningChain&&) = default; // A single entry in the chain. struct Node { friend PinningChain; // Access the value PinningDetails* operator->() { return &m_chain.get()[m_index]; } // Create/access the next node in the chain. Node Next(); const Node Next() const; // Drops the next node (and all subsequent nodes) from the chain. void RemoveNext(); // Indicates if there is already an existing next node. bool HasNext() const; private: Node(std::vector& chain, size_t index); std::reference_wrapper> m_chain; size_t m_index = 0; }; // Gets the root certificate pinning details in the chain. // These will correspond to the root of the certificate chain being verified. Node Root(); const Node Root() const; // A partial chain will validate success if all of its components are successful. PinningChain& PartialChain(bool isPartial = true); bool IsPartialChain() const { return m_partial; } // Validates the given certificate chain against the configuration. // Returns true to indicate that the chain meets the pinning configuration criteria. // Returns false to indicate the it does not. bool Validate(PCCERT_CHAIN_CONTEXT chainContext) const; // Gets a description of the pinning chain. std::string GetDescription() const; // Loads the pinning chain from the given JSON. [[nodiscard]] bool LoadFrom(const Json::Value& configuration); // Determines how far the certificate chain is through its lifespan (the minimum of all of its certificates). double GetRemainingLifetimePercentage() const; private: std::vector m_chain; bool m_partial = false; }; // Holds the details about how a certificate chain is to be validated (aka "pinned"). struct PinningConfiguration { PinningConfiguration(std::string identifier = {}); PinningConfiguration(const PinningConfiguration&) = default; PinningConfiguration& operator=(const PinningConfiguration&) = default; PinningConfiguration(PinningConfiguration&&) = default; PinningConfiguration& operator=(PinningConfiguration&&) = default; // Adds a possible chain to the configuration. // For a certificate to be valid, it must match only one of the configured chains. void AddChain(PinningChain chain); // Validates the given leaf certificate against the configuration. // Returns true to indicate that the certificate meets the pinning configuration criteria. // Returns false to indicate the it does not. bool Validate(PCCERT_CONTEXT certContext) const; // True if no pinning is configured. bool IsEmpty() const { return m_configuration.empty(); } // Loads the pinning configuration from the given JSON. [[nodiscard]] bool LoadFrom(const Json::Value& configuration); // Determines how far the configuration is through its lifespan (the maximum of all of its chains). double GetRemainingLifetimePercentage() const; private: // The identifier used when logging. std::string m_identifier; // The configured chains. std::vector m_configuration; // We store the last certificate that was successfully validated to speed up subsequent checks. // Only cache a single certificate under the assumption that most of the time there will // only be a single server certificate in use. mutable std::vector m_cachedCertificate; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/Compression.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::Compression { // Contains a compressor from the Windows Compression API. struct Compressor { // Create a compressor using the given algorithm (see COMPRESS_ALGORITHM_*) Compressor(DWORD algorithm); // Compresses the given data. std::vector Compress(std::string_view data); // Resets the compressor. void Reset(); // Sets compressor information values. void SetInformation(COMPRESS_INFORMATION_CLASS information, DWORD value); // Gets compressor information values. DWORD GetInformation(COMPRESS_INFORMATION_CLASS information); private: wil::unique_any m_compressor; }; // Contains a decompressor from the Windows Compression API. struct Decompressor { // Create a decompressor using the given algorithm (see COMPRESS_ALGORITHM_*) Decompressor(DWORD algorithm); // Decompresses the given data. std::vector Decompress(const std::vector& data); // Resets the decompressor. void Reset(); // Sets decompressor information values. void SetInformation(COMPRESS_INFORMATION_CLASS information, DWORD value); // Gets decompressor information values. DWORD GetInformation(COMPRESS_INFORMATION_CLASS information); private: wil::unique_any m_decompressor; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/ConfigurationSetProcessorHandlers.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Configuration { constexpr std::wstring_view PowerShellHandlerIdentifier = L"pwsh"; constexpr std::wstring_view DynamicRuntimeHandlerIdentifier = L"{73fea39f-6f4a-41c9-ba94-6fd14d633e40}"; constexpr std::wstring_view DSCv3HandlerIdentifier = L"{dbb2ac6d-1b58-4b05-9c50-b463cc434771}"; constexpr std::wstring_view DSCv3DynamicRuntimeHandlerIdentifier = L"{5f83e564-ca26-41ca-89db-36f5f0517ffd}"; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/DetectMismatch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #ifndef USE_PROD_CLSIDS #pragma detect_mismatch("USE_PROD_CLSIDS", "Not Defined") #else #pragma detect_mismatch("USE_PROD_CLSIDS", "Defined") #endif #ifndef AICLI_DISABLE_TEST_HOOKS #pragma detect_mismatch("AICLI_DISABLE_TEST_HOOKS", "Not Defined") #else #pragma detect_mismatch("AICLI_DISABLE_TEST_HOOKS", "Defined") #endif #ifndef WINGET_DISABLE_EXPERIMENTAL_FEATURES #pragma detect_mismatch("WINGET_DISABLE_EXPERIMENTAL_FEATURES", "Not Defined") #else #pragma detect_mismatch("WINGET_DISABLE_EXPERIMENTAL_FEATURES", "Defined") #endif #ifndef USE_PROD_WINGET_SERVER #pragma detect_mismatch("USE_PROD_WINGET_SERVER", "Not Defined") #else #pragma detect_mismatch("USE_PROD_WINGET_SERVER", "Defined") #endif #ifndef WINGET_ENABLE_RELEASE_BUILD #pragma detect_mismatch("WINGET_ENABLE_RELEASE_BUILD", "Not Defined") #else #pragma detect_mismatch("WINGET_ENABLE_RELEASE_BUILD", "Defined") #endif ================================================ FILE: src/AppInstallerSharedLib/Public/winget/Filesystem.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include using namespace std::chrono_literals; namespace AppInstaller::Filesystem { // Checks if the file system at path supports named streams/ADS bool SupportsNamedStreams(const std::filesystem::path& path); // Checks if the file system at path supports hard links bool SupportsHardLinks(const std::filesystem::path& path); // Checks if the file system at path support reparse points bool SupportsReparsePoints(const std::filesystem::path& path); // Checks if the canonical form of the path points to a location outside of the provided base path. bool PathEscapesBaseDirectory(const std::filesystem::path& target, const std::filesystem::path& base); // Renames the file to a new path. void RenameFile(const std::filesystem::path& from, const std::filesystem::path& to); // Creates a symlink that points to the target path. bool CreateSymlink(const std::filesystem::path& target, const std::filesystem::path& link); // Verifies that a symlink points to the target path. bool VerifySymlink(const std::filesystem::path& symlink, const std::filesystem::path& target); // Appends the .exe extension to the path if not present. void AppendExtension(std::filesystem::path& value, const std::string& extension); // Checks if the path is a symlink and exists. bool SymlinkExists(const std::filesystem::path& symlinkPath); bool CreateSymlink(const std::filesystem::path& path, const std::filesystem::path& target); // Get expanded file system path. std::filesystem::path GetExpandedPath(const std::string& path); // If `source` begins with all of `prefix`, replace that with `replacement`. // Returns true if replacement happened, false otherwise. bool ReplaceCommonPathPrefix(std::filesystem::path& source, const std::filesystem::path& prefix, std::string_view replacement); // Gets the path of a known folder. std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id); // Verifies that the paths are on the same volume. bool IsSameVolume(const std::filesystem::path& path1, const std::filesystem::path& path2); // Verifies if 'path' has parent equal to 'parentPath' bool IsParentPath(const std::filesystem::path& path, const std::filesystem::path& parentPath); // The principal that an ACE applies to. enum class ACEPrincipal : uint32_t { CurrentUser, Admins, System, }; // The permissions granted to a specific ACE. enum class ACEPermissions : uint32_t { // This is not "Deny All", but rather, "Not mentioned" None = 0x0, Read = 0x1, Write = 0x2, Execute = 0x4, ReadWrite = Read | Write, ReadExecute = Read | Execute, ReadWriteExecute = Read | Write | Execute, // All means that full control will be granted All = 0xFFFFFFFF }; DEFINE_ENUM_FLAG_OPERATORS(ACEPermissions); // Information about a path that we use and how to set it up. struct PathDetails { std::filesystem::path Path; // Default to creating the directory with inherited ownership and permissions bool Create = true; std::optional Owner; std::map ACL; // Shorthand for setting Owner and giving them ACEPermissions::All void SetOwner(ACEPrincipal owner); // Determines if the ACL should be applied. bool ShouldApplyACL() const; // Applies the ACL unconditionally. void ApplyACL() const; }; // Initializes from the given details and returns the path to it. // The path is moved out of the details. std::filesystem::path InitializeAndGetPathTo(PathDetails&& details); // Gets the path to the requested location. template std::filesystem::path GetPathTo(PathEnum path, bool forDisplay = false) { return InitializeAndGetPathTo(GetPathDetailsFor(path, forDisplay)); } // A shared path. enum class PathName { // Local state root that is specifically unpackaged (even if used from a packaged process). UnpackagedLocalStateRoot, // Local settings root that is specifically unpackaged (even if used from a packaged process). UnpackagedSettingsRoot, }; // Gets the PathDetails used for the given path. // This is exposed primarily to allow for testing, GetPathTo should be preferred. PathDetails GetPathDetailsFor(PathName path, bool forDisplay = false); // Gets the path to the executable for the given process. std::filesystem::path GetExecutablePathForProcess(HANDLE process); // Information about a specific file. struct FileInfo { std::filesystem::path Path; std::filesystem::file_time_type LastWriteTime{}; uintmax_t Size = 0; }; // Gets the FileInfo for each regular file directly under the given directory. std::vector GetFileInfoFor(const std::filesystem::path& directory); // Limitations on a set of files. // Any value that is 0 is treated as no limit. struct FileLimits { std::chrono::hours Age = 0h; uint32_t TotalSizeInMB = 0; size_t Count = 0; }; // Modifies the given files to only include those that exceed the limits that are provided. void FilterToFilesExceedingLimits(std::vector& files, const FileLimits& limits); } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/GroupPolicy.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include using namespace std::string_view_literals; namespace AppInstaller::Settings { // A policy that sets a value for some setting. // The value of the policy is a value in the registry key, or is // made up of sub-keys for settings that are lists. enum class ValuePolicy { None, SourceAutoUpdateIntervalInMinutes, AdditionalSources, AllowedSources, DefaultProxy, Max, }; // A policy that acts as a toggle to enable or disable a feature. // They are backed by a DWORD value with values 0 and 1. struct TogglePolicy { enum class Policy { None = 0, WinGet, Settings, ExperimentalFeatures, LocalManifestFiles, HashOverride, LocalArchiveMalwareScanOverride, DefaultSource, MSStoreSource, FontSource, AdditionalSources, AllowedSources, BypassCertificatePinningForMicrosoftStore, WinGetCommandLineInterfaces, Configuration, ProxyCommandLineOptions, McpServer, Max, }; TogglePolicy(Policy policy, std::string_view regValueName, StringResource::StringId policyName, bool defaultIsEnabled = true) : m_policy(policy), m_regValueName(regValueName), m_policyName(policyName), m_defaultIsEnabled(defaultIsEnabled) {} static TogglePolicy GetPolicy(Policy policy); static std::vector GetAllPolicies(); Policy GetPolicy() const { return m_policy; } std::string_view RegValueName() const { return m_regValueName; } StringResource::StringId PolicyName() const { return m_policyName; } bool DefaultIsEnabled() const { return m_defaultIsEnabled; } private: Policy m_policy; std::string_view m_regValueName; StringResource::StringId m_policyName; bool m_defaultIsEnabled; }; // Possible configuration states for a policy. enum class PolicyState { NotConfigured, Disabled, Enabled, }; // A source defined by Group Policy to be added or allowed struct SourceFromPolicy { std::string Name; std::string Arg; std::string Type; std::string Data; std::string Identifier; std::vector TrustLevel; bool Explicit = false; std::optional Priority; #ifndef AICLI_DISABLE_TEST_HOOKS Certificates::PinningConfiguration PinningConfiguration; #endif std::string ToJsonString() const; }; namespace details { template struct ValuePolicyMapping { // value_t - type of the policy // ReadAndValidate() - Function that reads the value and does semantic validation. // For simple values: // ValueName - Name of the registry value // ValueType - Type of the registry value // reg_value_t - Type returned by the registry when reading the value // For lists: // item_t - Type of each item // KeyName -- Name of the sub-key containing the list // ReadAndValidateItem() - Function that reads a single item from a subkey }; template<> struct ValuePolicyMapping { using value_t = std::monostate; using opt_value_t = std::nullopt_t; using opt_ref_value_t = std::nullopt_t; static std::nullopt_t ReadAndValidate(const Registry::Key& policiesKey); }; #define POLICY_MAPPING_SPECIALIZATION(_policy_, _type_, _extra_) \ template <> \ struct ValuePolicyMapping<_policy_> \ { \ using value_t = _type_; \ using opt_value_t = std::optional; \ using opt_ref_value_t = std::optional>; \ static std::optional ReadAndValidate(const Registry::Key& policiesKey); \ _extra_ \ } #define POLICY_MAPPING_VALUE_SPECIALIZATION(_policy_, _type_, _valueName_, _valueType_) \ POLICY_MAPPING_SPECIALIZATION(_policy_, _type_, \ static constexpr std::string_view ValueName = _valueName_; \ static constexpr Registry::Value::Type ValueType = _valueType_; \ using reg_value_t = decltype(std::declval().GetValue()); \ ) #define POLICY_MAPPING_LIST_SPECIALIZATION(_policy_, _type_, _keyName_) \ POLICY_MAPPING_SPECIALIZATION(_policy_, std::vector<_type_>, \ static constexpr std::string_view KeyName = _keyName_; \ using item_t = _type_; \ static std::optional ReadAndValidateItem(const Registry::Value& item); \ ) POLICY_MAPPING_VALUE_SPECIALIZATION(ValuePolicy::SourceAutoUpdateIntervalInMinutes, uint32_t, "SourceAutoUpdateInterval"sv, Registry::Value::Type::DWord); POLICY_MAPPING_VALUE_SPECIALIZATION(ValuePolicy::DefaultProxy, std::string, "DefaultProxy"sv, Registry::Value::Type::String); POLICY_MAPPING_LIST_SPECIALIZATION(ValuePolicy::AdditionalSources, SourceFromPolicy, "AdditionalSources"sv); POLICY_MAPPING_LIST_SPECIALIZATION(ValuePolicy::AllowedSources, SourceFromPolicy, "AllowedSources"sv); } // Representation of the policies read from the registry. struct GroupPolicy { using ValuePoliciesMap = EnumBasedVariantMap; static GroupPolicy const& Instance(); GroupPolicy(const Registry::Key& key); ~GroupPolicy() = default; GroupPolicy() = delete; GroupPolicy(const GroupPolicy&) = delete; GroupPolicy& operator=(const GroupPolicy&) = delete; GroupPolicy(GroupPolicy&&) = delete; GroupPolicy& operator=(GroupPolicy&&) = delete; template using ValueType = typename details::ValuePolicyMapping

::value_t; // Gets the policy value if it is present template typename details::ValuePolicyMapping

::opt_value_t GetValue() const { if (m_values.Contains(P)) { return m_values.Get

(); } else { return std::nullopt; } } template typename details::ValuePolicyMapping

::opt_ref_value_t GetValueRef() const { if (m_values.Contains(P)) { return std::cref(m_values.Get

()); } else { return std::nullopt; } } template<> std::nullopt_t GetValue() const { return std::nullopt; } template<> std::nullopt_t GetValueRef() const { return std::nullopt; } PolicyState GetState(TogglePolicy::Policy policy) const; // Checks whether a policy is enabled, using an appropriate default when not configured. // Should not be used when not configured means something different than enabled/disabled. bool IsEnabled(TogglePolicy::Policy policy) const; #ifndef AICLI_DISABLE_TEST_HOOKS protected: static void OverrideInstance(GroupPolicy* gp); static void ResetInstance(); #else private: #endif std::map m_toggles; ValuePoliciesMap m_values; }; inline const GroupPolicy& GroupPolicies() { return GroupPolicy::Instance(); } struct GroupPolicyException { GroupPolicyException(TogglePolicy::Policy policy) : m_policy(policy) {} const TogglePolicy::Policy& Policy() const { return m_policy; } private: TogglePolicy::Policy m_policy; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/IConfigurationStaticsInternals.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::WinRT { // Flag values for SetExperimentalState. enum class ConfigurationStaticsInternalsStateFlags : UINT32 { None = 0, All = None }; DEFINE_ENUM_FLAG_OPERATORS(ConfigurationStaticsInternalsStateFlags); MIDL_INTERFACE("C3886148-148A-4A3D-8018-9CDACDFC0B8D") IConfigurationStaticsInternals : public IUnknown { public: virtual /* [local] */ HRESULT STDMETHODCALLTYPE SetExperimentalState( UINT32 state) = 0; virtual /* [local] */ HRESULT STDMETHODCALLTYPE BlockNewWorkForShutdown() = 0; virtual /* [local] */ HRESULT STDMETHODCALLTYPE BeginShutdown() = 0; virtual /* [local] */ HRESULT STDMETHODCALLTYPE WaitForShutdown() = 0; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/ILifetimeWatcher.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::WinRT { MIDL_INTERFACE("59b5623f-d03e-41f8-b400-89ee04ea02d7") ILifetimeWatcher : public IUnknown { public: // Due to the way the winrt types are set up, this watcher will not have AddRef called. virtual /* [local] */ HRESULT STDMETHODCALLTYPE SetLifetimeWatcher( IUnknown* watcher) = 0; }; // Implements ILifetimeWatcher functionality. struct LifetimeWatcherBase { HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher) { m_lifetimeWatcher = winrt::Windows::Foundation::IUnknown(watcher, winrt::take_ownership_from_abi); return S_OK; } void PropagateLifetimeWatcher(const winrt::Windows::Foundation::IUnknown& child) { if (m_lifetimeWatcher && child) { // Require that any call to this function is for an object that implements watching to prevent // accidental assumptions about the child object and lifetime management. auto watcher = child.as(); // Create a copy of the lifetime watcher (to add_ref), then detach and pass it to the child to own. watcher->SetLifetimeWatcher(static_cast(winrt::detach_abi(winrt::Windows::Foundation::IUnknown{ m_lifetimeWatcher }))); } } private: winrt::Windows::Foundation::IUnknown m_lifetimeWatcher; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/JsonSchemaValidation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::JsonSchema { // Load schema as parsed json doc Json::Value LoadSchemaDoc(std::string_view schemaStr); // Load an embedded resource from binary and return as Json::Value Json::Value LoadResourceAsSchemaDoc(PCWSTR resourceName, PCWSTR resourceType); // Populate a valijson Schema object from a json value void PopulateSchema(const Json::Value& schemaJson, valijson::Schema& schema); // Validate a json doc with a schema // Returns whether it was successful and fills the results object bool Validate(const valijson::Schema& schema, const Json::Value& json, valijson::ValidationResults& results); // Extracts the error messages from a result into a single non-localized string std::string GetErrorStringFromResults(valijson::ValidationResults& results); } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/JsonUtil.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include // Disable dllimport for cpprest JSON in any downstream consumers #ifndef _NO_ASYNCRTIMP #define _NO_ASYNCRTIMP #endif #ifndef WINGET_DISABLE_FOR_FUZZING #include #endif #include #include #include namespace AppInstaller::JSON { // For JSON CPP Lib template std::optional GetValue(const Json::Value& node); template<> std::optional GetValue(const Json::Value& node); template<> std::optional GetValue(const Json::Value& node); template<> std::optional GetValue(const Json::Value& node); template<> std::optional> GetValue>(const Json::Value& node); #ifndef WINGET_DISABLE_FOR_FUZZING // For cpprestsdk JSON std::optional> GetJsonValueFromNode(const web::json::value& node, const utility::string_t& keyName); std::optional GetRawStringValueFromJsonValue(const web::json::value& value); std::optional GetWideStringValueFromJsonValue(const web::json::value& value); std::optional GetRawStringValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName); std::optional GetWideStringValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName); std::optional GetRawBoolValueFromJsonValue(const web::json::value& value); std::optional GetRawBoolValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName); std::optional> GetRawJsonArrayFromJsonNode(const web::json::value& node, const utility::string_t& keyName); std::vector GetRawStringArrayFromJsonNode(const web::json::value& node, const utility::string_t& keyName); std::set GetRawStringSetFromJsonNode(const web::json::value& node, const utility::string_t& keyName); std::optional GetRawIntValueFromJsonValue(const web::json::value& value); std::optional GetRawUInt64ValueFromJsonValue(const web::json::value& value); std::optional GetRawIntValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName); std::optional GetRawUInt64ValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName); utility::string_t GetUtilityString(std::string_view nodeName); web::json::value GetStringValue(std::string_view value); // Base64 encode std::string Base64Encode(const std::vector& input); // Base64 decode std::vector Base64Decode(const std::string& input); #endif bool IsValidNonEmptyStringValue(std::optional& value); bool IsValidNonEmptyStringValue(std::optional& value); } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/LocIndependent.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Utility { // "I solemnly swear that this string is indeed localization independent." // A localization independent string view. // Used as a wrapper around strings that do not need localization. struct LocIndView : public std::string_view { constexpr LocIndView() = default; explicit constexpr LocIndView(std::string_view sv) : std::string_view(sv) {} }; // "I solemnly swear that this string is indeed localization independent." // A localization independent string; either through external localization // or by virtue of not needing to be localized. // Intentionally does not allow the value to be modified to prevent accidental // reintroduction of a localization dependent value. struct LocIndString { LocIndString() = default; explicit LocIndString(std::string_view sv) : m_value(sv) {} explicit LocIndString(std::string v) : m_value(std::move(v)) {} LocIndString(const LocIndString&) = default; LocIndString& operator=(const LocIndString&) = default; LocIndString(LocIndString&&) = default; LocIndString& operator=(LocIndString&&) = default; bool empty() const { return m_value.empty(); } const std::string& get() const & { return m_value; } std::string&& get() && { return std::move(m_value); } operator const std::string& () const { return m_value; } operator std::string_view() const { return m_value; } operator LocIndView() const { return LocIndView{ static_cast(m_value) }; } const std::string* operator->() const { return &m_value; } bool operator==(std::string_view sv) const { return m_value == sv; } bool operator!=(const LocIndString& other) const { return m_value != other.m_value; } bool operator<(const LocIndString& other) const { return m_value < other.m_value; } bool operator<(const std::string& other) const { return m_value < other; } friend std::ostream& operator<<(std::ostream& out, const AppInstaller::Utility::LocIndString& lis) { return (out << lis.get()); } private: std::string m_value; }; namespace literals { // "I solemnly swear that this string is indeed localization independent." // Enable easier use of a localization independent view through literals. inline constexpr LocIndView operator ""_liv(const char* chars, size_t size) { return LocIndView{ std::string_view{ chars, size } }; } // "I solemnly swear that this string is indeed localization independent." // Enable easier use of a localization independent string through literals. inline LocIndString operator ""_lis(const char* chars, size_t size) { return LocIndString{ std::string_view{ chars, size } }; } } } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/ManagedFile.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::Utility { // Struct that holds a file handle and may perform additional operations on exit struct ManagedFile { ManagedFile() = default; ManagedFile(const ManagedFile&) = delete; ManagedFile& operator=(const ManagedFile&) = delete; ManagedFile(ManagedFile&&) = default; ManagedFile& operator=(ManagedFile&&) = default; HANDLE GetFileHandle() const { return m_fileHandle.get(); } const std::filesystem::path& GetFilePath() const { return m_filePath; } // Always creates a new write locked file at the path given. desiredAccess is passed to CreateFile call. static ManagedFile CreateWriteLockedFile(const std::filesystem::path& path, DWORD desiredAccess, bool deleteOnExit); // Always opens an existing file at the path given with write locked. desiredAccess is passed to CreateFile call. static ManagedFile OpenWriteLockedFile(const std::filesystem::path& path, DWORD desiredAccess); ~ManagedFile(); private: std::filesystem::path m_filePath; wil::unique_handle m_fileHandle; bool m_deleteFileOnExit = false; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/ModuleCountBase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::WinRT { // Implements module count interactions. struct ModuleCountBase { ModuleCountBase() { if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) { modulePtr->IncrementObjectCount(); } } ~ModuleCountBase() { if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) { modulePtr->DecrementObjectCount(); } } }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/PathTree.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::Filesystem { // Container that holds a map of items addressable by path. template struct PathTree { using value_t = Value; PathTree() = default; private: struct Node { value_t Value{}; std::map Children; }; public: // Returns the value for the given path, inserting it if necessary. value_t& FindOrInsert(const std::filesystem::path& path) { return FindNode(path, true)->Value; } // Finds the value for the given path; returns null if not found. value_t* Find(const std::filesystem::path& path) { Node* node = FindNode(path, false); return node ? &node->Value : nullptr; } // Finds the value for the given path; returns null if not found. const value_t* Find(const std::filesystem::path& path) const { const Node* node = FindNode(path); return node ? &node->Value : nullptr; } // Invokes the `visit` function for each value in the tree starting at `initialPath` (unconditionally) // and recursively continuing on to children for whom the predicate returns true. void VisitIf(const std::filesystem::path& initialPath, std::function visit, std::function predicate) const { const Node* node = FindNode(initialPath); if (node) { std::queue nodes; nodes.push(node); while (!nodes.empty()) { const Node* currentNode = nodes.front(); nodes.pop(); visit(currentNode->Value); for (const auto& child : currentNode->Children) { if (predicate(child.second.Value)) { nodes.push(&child.second); } } } } } private: // Finds the node for the given path, creating as needed if requested. Node* FindNode(const std::filesystem::path& path, bool createIfNeeded) { if (path.empty()) { if (createIfNeeded) { THROW_HR(E_INVALIDARG); } else { return nullptr; } } const auto& nodePath = std::filesystem::weakly_canonical(path); Node* currentNode = &m_rootNode; for (const auto& pathPart : nodePath) { auto& children = currentNode->Children; if (createIfNeeded) { currentNode = &children[pathPart]; } else { auto itr = children.find(pathPart); if (itr != children.end()) { currentNode = &itr->second; } else { // Not found and should not create return nullptr; } } } return currentNode; } // Finds the node for the given path; returns null if not found. const Node* FindNode(const std::filesystem::path& path) const { return const_cast(this)->FindNode(path, false); } Node m_rootNode; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/Registry.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #define AICLI_REGISTRY_UTF16_FLAG 0x08000000 namespace AppInstaller::Registry { namespace details { template constexpr bool dependent_false = false; template struct ValueTypeSpecifics { using value_t = void; static value_t Convert(const std::vector& data) { static_assert(dependent_false, "No Type specific override has been supplied"); } }; template <> struct ValueTypeSpecifics { using value_t = std::vector; static value_t Convert(const std::vector& data); }; template <> struct ValueTypeSpecifics { using value_t = std::string; static value_t Convert(const std::vector& data); }; template <> struct ValueTypeSpecifics { using value_t = std::wstring; static value_t Convert(const std::vector& data); }; template <> struct ValueTypeSpecifics { using value_t = std::string; static value_t Convert(const std::vector& data); }; template <> struct ValueTypeSpecifics { using value_t = std::wstring; static value_t Convert(const std::vector& data); }; template <> struct ValueTypeSpecifics { using value_t = std::vector; static value_t Convert(const std::vector& data); }; template <> struct ValueTypeSpecifics { using value_t = uint32_t; static value_t Convert(const std::vector& data); }; } struct Key; struct ValueList; // A registry value. struct Value { friend Key; friend ValueList; // The type of data stored in the Value. enum class Type : DWORD { None = REG_NONE, String = REG_SZ, UTF16Flag = AICLI_REGISTRY_UTF16_FLAG, UTF16String = REG_SZ | UTF16Flag, ExpandString = REG_EXPAND_SZ, UTF16ExpandString = REG_EXPAND_SZ | UTF16Flag, Binary = REG_BINARY, DWord = REG_DWORD, DWordLittleEndian = REG_DWORD_LITTLE_ENDIAN, DWordBigEndian = REG_DWORD_BIG_ENDIAN, MultiString = REG_MULTI_SZ, QWord = REG_QWORD, QWordLittleEndian = REG_QWORD_LITTLE_ENDIAN, }; Type GetType() const { return m_type; } template typename details::ValueTypeSpecifics(T)>::value_t GetValue() const { auto value = TryGetValue(); if (!value.has_value()) { THROW_HR(E_INVALIDARG); } return std::move(value.value()); } template typename std::optional(T)>::value_t> TryGetValue() const { if (HasCompatibleType(T)) { return details::ValueTypeSpecifics(T)>::Convert(m_data); } else { return std::nullopt; } } private: Value(DWORD type, std::vector&& data); bool HasCompatibleType(Type type) const; Type m_type; std::vector m_data; }; // Value iteration struct ValueList { friend Key; struct const_iterator; struct ValueRef { friend const_iterator; // Gets the name of the value. std::string Name() const; // Gets the actual value of the value. // The optional allows for the potential race with the value being removed. std::optional Value() const; private: ValueRef(wil::shared_hkey key, std::wstring&& valueName); wil::shared_hkey m_key; std::wstring m_valueName; }; struct const_iterator { friend ValueList; const_iterator& operator++(); const_iterator operator++(int); bool operator==(const const_iterator& other) const; bool operator!=(const const_iterator& other) const; const ValueRef& operator*() const; const ValueRef* operator->() const; private: // Create an iterator const_iterator(const wil::shared_hkey& key, DWORD index = 0); // Create an iterator for end const_iterator() = default; void GetValue(); // An empty handle represents the end iterator. wil::shared_hkey m_key; DWORD m_index = 0; std::optional m_value; }; const_iterator begin() const; const_iterator end() const; private: ValueList(wil::shared_hkey key); wil::shared_hkey m_key; }; // A registry key. struct Key { Key() = default; Key(HKEY key); Key(HKEY key, std::string_view subKey, DWORD options = 0, REGSAM access = KEY_READ); Key(HKEY key, const std::wstring& subKey, DWORD options = 0, REGSAM access = KEY_READ); // --== Sub-Key iteration ==-- struct const_iterator; struct SubKeyRef { friend const_iterator; // Gets the name of the subkey. std::string Name() const; // Opens the subkey. Key Open() const; operator bool() const { return m_parentKey.operator bool(); } private: // For a valid iterator SubKeyRef(const wil::shared_hkey& key, REGSAM access); // For the end iterator SubKeyRef() = default; // Enumerates the subkey of m_parentKey at the given index. void Enum(DWORD index); wil::shared_hkey m_parentKey; REGSAM m_access = KEY_READ; std::wstring m_subKeyName; }; struct const_iterator { friend Key; const_iterator& operator++(); const_iterator operator++(int); bool operator==(const const_iterator& other) const; bool operator!=(const const_iterator& other) const; const SubKeyRef& operator*() const; const SubKeyRef* operator->() const; private: // Create an iterator for begin const_iterator(const wil::shared_hkey& key, REGSAM access); // Create an iterator for end const_iterator() = default; DWORD m_index = 0; SubKeyRef m_subkey; }; const_iterator begin() const; const_iterator end() const; std::optional operator[](std::string_view name) const; std::optional operator[](const std::wstring& name) const; void DeleteValue(std::string_view name) const; void DeleteValue(const std::wstring& name) const; std::optional SubKey(std::string_view name, DWORD options = 0) const; std::optional SubKey(const std::wstring& name, DWORD options = 0) const; // Set registry values. void SetValue(const std::wstring& name, const std::wstring& value, DWORD type = REG_SZ) const; void SetValue(const std::wstring& name, const std::vector& value, DWORD type = REG_BINARY) const; void SetValue(const std::wstring& name, DWORD value) const; ValueList Values() const; operator bool() const { return m_key.operator bool(); } operator HKEY() const { return m_key.get(); } // Open a Key; will return an empty Key if the subkey does not exist. static Key OpenIfExists(HKEY key, std::string_view subKey = {}, DWORD options = 0, REGSAM access = KEY_READ); static Key OpenIfExists(HKEY key, const std::wstring& subKey = {}, DWORD options = 0, REGSAM access = KEY_READ); // Creates a new Key or returns one if it already existed. static Key Create(HKEY key, std::string_view subkey = {}, DWORD options = REG_OPTION_NON_VOLATILE, REGSAM access = KEY_ALL_ACCESS); static Key Create(HKEY key, const std::wstring& subKey = {}, DWORD options = REG_OPTION_NON_VOLATILE, REGSAM access = KEY_ALL_ACCESS); // Delete a key static bool Delete(HKEY key, std::string_view subkey, DWORD samDesired); static bool Delete(HKEY key, const std::wstring& subKey, DWORD samDesired); static bool DeleteTree(HKEY key, const std::wstring& subKey); private: // When ignoring error, returns whether the key existed bool Initialize(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access, bool ignoreErrorIfDoesNotExist); // Returns whether the key was created successfully. bool CreateAndOpen(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access); wil::shared_hkey m_key; REGSAM m_access = KEY_READ; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/Resources.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include "AppInstallerStrings.h" using namespace std::string_view_literals; namespace AppInstaller { namespace StringResource { #define WINGET_WIDE_STRINGIFY_HELP(_id_) L ## _id_ #define WINGET_WIDE_STRINGIFY(_id_) WINGET_WIDE_STRINGIFY_HELP(_id_) #define WINGET_DEFINE_RESOURCE_STRINGID(_id_) static constexpr AppInstaller::StringResource::StringId _id_ { WINGET_WIDE_STRINGIFY(#_id_) ## sv } // A resource identifier struct StringId : public std::wstring_view { explicit constexpr StringId(std::wstring_view id) : std::wstring_view(id) {} // Sets the placeholder values in the resolved string id. // Example: out << myStringId(placeholderVal1, placeholderVal2, ...) template Utility::LocIndString operator()(T ... args) const; // Creates a StringId that represents an empty resource string static StringId Empty(); private: // Resolve the string ID to its corresponding localized string // without replacing placeholders. std::string Resolve() const; }; inline StringId StringId::Empty() { return StringId{ {} }; } // Output resource identifier as localized string. std::ostream& operator<<(std::ostream& out, StringId si); // Resource string identifiers. struct String { WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableWinGet); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableWingetSettings); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableExperimentalFeatures); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableLocalManifests); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableHashOverride); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableLocalArchiveMalwareScanOverride); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableDefaultSource); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableMSStoreSource); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableFontSource); WINGET_DEFINE_RESOURCE_STRINGID(PolicyAdditionalSources); WINGET_DEFINE_RESOURCE_STRINGID(PolicyAllowedSources); WINGET_DEFINE_RESOURCE_STRINGID(PolicySourceAutoUpdateInterval); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableBypassCertificatePinningForMicrosoftStore); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableWindowsPackageManagerCommandLineInterfaces); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableWinGetConfiguration); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableProxyCommandLineOptions); WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableMcpServer); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidFieldFormat); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidFieldValue); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidValueFromPolicy); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningLoadedBackupSettings); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningParseError); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningUsingDefault); WINGET_DEFINE_RESOURCE_STRINGID(UnknownErrorCode); }; } namespace Resource { // Get an embedded resource from the binary and return as std::string_view. // Resource data is valid as long as the binary is loaded. std::string_view GetResourceAsString(int resourceName, int resourceType); std::string_view GetResourceAsString(PCWSTR resourceName, PCWSTR resourceType); // Get an embedded resource from the binary and return as std::pair. // Resource data is valid as long as the binary is loaded. std::pair GetResourceAsBytes(int resourceName, int resourceType); std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType); // A localized string struct LocString : public Utility::LocIndString { LocString() = default; LocString(StringResource::StringId id) : Utility::LocIndString(id()) {} LocString(Utility::LocIndString locIndString) : Utility::LocIndString(std::move(locIndString)) {} LocString(const LocString&) = default; LocString& operator=(const LocString&) = default; LocString(LocString&&) = default; LocString& operator=(LocString&&) = default; }; } namespace StringResource { // Tries to resolve a string, returning a nullopt if it cannot. std::optional TryResolveString(std::wstring_view resKey); } namespace details { // List of approved types for output, others are potentially not localized. template struct IsApprovedForOutput { static constexpr bool value = std::is_arithmetic::value; }; #define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) \ template <> \ struct IsApprovedForOutput<_t_> \ { \ static constexpr bool value = true; \ } // It is assumed that single char values need not be localized, as they are matched // ordinally or they are punctuation / other. WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(char); // Localized strings (and from an Id for one for convenience). WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(StringResource::StringId); WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Resource::LocString); // Strings explicitly declared as localization independent. WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndView); WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndString); // Normalized strings come from user data and should therefore already by localized // by how they are chosen (or there is no localized version). WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::NormalizedString); } template Utility::LocIndString StringResource::StringId::operator()(T ... args) const { static_assert((details::IsApprovedForOutput>::value && ...), "This type may not be localized, see comment for more information"); return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; } } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/Runtime.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include namespace AppInstaller::Runtime { // Determines whether the process is running in a packaged context or not. bool IsRunningInPackagedContext(); // Determines the current version of the client and returns it. Utility::LocIndString GetClientVersion(); // Gets the package family name of the current package (or empty string if not packaged). std::wstring GetPackageFamilyName(); // Determines the current version of the package if running in a packaged context. Utility::LocIndString GetPackageVersion(); // Gets a string representation of the OS version for debugging purposes. Utility::LocIndString GetOSVersion(); // Gets the OS region. // This can be used as the current market. std::string GetOSRegion(); // Determines whether the current OS version is >= the given one. // We treat the given Version struct as a standard 4 part Windows OS version. bool IsCurrentOSVersionGreaterThanOrEqual(const Utility::Version& version); // Determines whether the process is running with administrator privileges. bool IsRunningAsAdmin(); // Determines whether the process is running with local system context. bool IsRunningAsSystem(); // Determines whether the process is running with administrator or system privileges. bool IsRunningAsAdminOrSystem(); // Determines whether the current token can be elevated. // This only returns true for tokens that are TokenElevationTypeLimited. // Thus, it will only be true if: // 1. UAC is enabled // 2. the user is in the Administrators group // 3. the token is not already elevated bool IsRunningWithLimitedToken(); // Returns true if this is a release build; false if not. inline constexpr bool IsReleaseBuild() { #ifdef WINGET_ENABLE_RELEASE_BUILD return true; #else return false; #endif } } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/SQLiteDynamicStorage.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace AppInstaller::SQLite { // Type the allows for the schema version of the underlying storage to be changed dynamically. struct SQLiteDynamicStorage : public SQLiteStorageBase { // Creates a new database with the given schema version. SQLiteDynamicStorage(const std::string& target, const Version& version); SQLiteDynamicStorage(const std::filesystem::path& target, const Version& version); // Opens an existing database with the given disposition. SQLiteDynamicStorage(const std::string& filePath, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& file = {}); SQLiteDynamicStorage(const std::filesystem::path& filePath, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& file = {}); // Implicit conversion to a connection object for convenience. operator Connection& (); operator const Connection& () const; Connection& GetConnection(); const Connection& GetConnection() const; using SQLiteStorageBase::SetLastWriteTime; // Must be kept alive to ensure consistent schema view and exclusive use of the owned connection. struct TransactionLock { _Acquires_lock_(mutex) TransactionLock(std::mutex& mutex); _Acquires_lock_(mutex) TransactionLock(std::mutex& mutex, Connection& connection, std::string_view name, bool immediateWrite); // Abandons the transaction and any changes; releases the connection lock. void Rollback(bool throwOnError = true); // Commits the transaction and releases the connection lock. void Commit(); private: std::lock_guard m_lock; Transaction m_transaction; }; // Acquires the connection lock and begins a transaction on the database. // If the returned result is empty, the schema version has changed and the caller must handle this. std::unique_ptr TryBeginTransaction(std::string_view name, bool immediateWrite); // Locks the connection for use during the schema upgrade. std::unique_ptr LockConnection(); }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/SQLiteMetadataTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::SQLite { using namespace std::string_view_literals; static constexpr std::string_view s_MetadataValueName_DatabaseIdentifier = "databaseIdentifier"sv; static constexpr std::string_view s_MetadataValueName_MajorVersion = "majorVersion"sv; static constexpr std::string_view s_MetadataValueName_MinorVersion = "minorVersion"sv; static constexpr std::string_view s_MetadataValueName_LastWriteTime = "lastwritetime"sv; // The metadata table for the database. // Contains a fixed-schema set of named values that can be used to determine how to read the rest of the database. struct MetadataTable { static void Create(Connection& connection); // Gets the named value from the metadata table, interpreting it as the given type. template static Value GetNamedValue(const Connection& connection, std::string_view name) { Statement statement = GetNamedValueStatement(connection, name); return statement.GetColumn(0); } // Gets the named value from the metadata table, interpreting it as the given type. // Returns nullopt if the value is not present. template static std::optional TryGetNamedValue(const Connection& connection, std::string_view name) { std::optional statement = TryGetNamedValueStatement(connection, name); if (statement) { return statement->GetColumn(0); } else { return std::nullopt; } } // Sets the named value into the metadata table. template static void SetNamedValue(const Connection& connection, std::string_view name, Value&& v) { Statement statement = SetNamedValueStatement(connection, name); statement.Bind(2, std::forward(v)); statement.Execute(); } private: // Internal function that gets the named value. static Statement GetNamedValueStatement(const Connection& connection, std::string_view name); // Internal function that gets the named value, or nullopt if it is not present. static std::optional TryGetNamedValueStatement(const Connection& connection, std::string_view name); // Internal function that sets the named value. static Statement SetNamedValueStatement(const Connection& connection, std::string_view name); }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/SQLiteStatementBuilder.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include using namespace std::string_view_literals; namespace AppInstaller::SQLite::Builder { namespace details { // Sentinel types to indicate special cases to the builder. struct unbound_t {}; struct rowcount_t {}; // Class for intake from external functions. struct SubBuilder { SubBuilder(std::string&& s) : m_string(std::move(s)) {} SubBuilder(const SubBuilder&) = default; SubBuilder& operator=(const SubBuilder&) = default; SubBuilder(SubBuilder&&) noexcept = default; SubBuilder& operator=(SubBuilder&&) noexcept = default; const std::string& GetString() const { return m_string; } protected: std::string m_string; }; // Base class for all sub-builders. struct SubBuilderBase { SubBuilderBase() = default; SubBuilderBase(const SubBuilderBase&) = default; SubBuilderBase& operator=(const SubBuilderBase&) = default; SubBuilderBase(SubBuilderBase&&) noexcept = default; SubBuilderBase& operator=(SubBuilderBase&&) noexcept = default; virtual operator SubBuilder() { return { m_stream.str() }; } protected: std::ostringstream m_stream; }; } // Pass this value to indicate that the caller will bind the value later. __declspec_selectany_ details::unbound_t Unbound; // Pass this value to indicate that the number of rows is to be selected. __declspec_selectany_ details::rowcount_t RowCount; // A qualified table reference. struct QualifiedTable { std::string_view Schema; std::string_view Table; explicit constexpr QualifiedTable(std::string_view table) : Table(table) {} explicit constexpr QualifiedTable(std::string_view schema, std::string_view table) : Schema(schema), Table(table) {} }; namespace Schema { // The main database's schema table. // More info can be found at: https://www.sqlite.org/schematab.html constexpr QualifiedTable MainTable{ "main"sv, "sqlite_master"sv }; // The sqlite_schema column name for the type of the object. constexpr std::string_view TypeColumn = "type"sv; // The sqlite_schema type value for a table. constexpr std::string_view Type_Table = "table"sv; // The sqlite_schema type value for an index. constexpr std::string_view Type_Index = "index"sv; // The sqlite_schema column name for the name of the object. constexpr std::string_view NameColumn = "name"sv; } // A qualified column reference. struct QualifiedColumn { std::string_view Table; std::string_view Column; explicit QualifiedColumn(std::string_view column) : Column(column) {} explicit QualifiedColumn(std::string_view table, std::string_view column) : Table(table), Column(column) {} }; // SQLite types as an enum. enum class Type { Int, Bool = Int, Int64, RowId = Int64, Text, Blob, Integer, // Type for specifying a primary key column as a row id alias. None, // Does not declare a type }; template struct TypeInfo { }; template <> struct TypeInfo { using value_t = std::string; }; template <> struct TypeInfo { using value_t = std::optional; }; template <> struct TypeInfo { using value_t = SQLite::blob_t; }; template <> struct TypeInfo { using value_t = std::optional; }; // Aggregate functions. enum class Aggregate { Min, Max, }; // Helper to mark create an integer primary key for rowid, making it stable across vacuum. struct IntegerPrimaryKey : public details::SubBuilderBase { IntegerPrimaryKey(); IntegerPrimaryKey(const IntegerPrimaryKey&) = default; IntegerPrimaryKey& operator=(const IntegerPrimaryKey&) = default; IntegerPrimaryKey(IntegerPrimaryKey&&) noexcept = default; IntegerPrimaryKey& operator=(IntegerPrimaryKey&&) noexcept = default; // Set the column to autoincrement. SQLite recommends against using this value unless // you need to ensure that rowids are not ever reused. IntegerPrimaryKey& AutoIncrement(bool isTrue = true); }; // Helper used when creating a table. struct ColumnBuilder : public details::SubBuilderBase { // Specify the column name and type when creating the builder. ColumnBuilder(std::string_view column, Type type); ColumnBuilder(const ColumnBuilder&) = default; ColumnBuilder& operator=(const ColumnBuilder&) = default; ColumnBuilder(ColumnBuilder&&) noexcept = default; ColumnBuilder& operator=(ColumnBuilder&&) noexcept = default; // Indicate that the column is not able to be null. // Allow for data driven construction with input value. ColumnBuilder& NotNull(bool isTrue = true); // Indicate that the column is case-insensitive. // Allow for data driven construction with input value. ColumnBuilder& CollateNoCase(bool isTrue = true); // Indicate the default value for the column. // Note that a default value is not considered constant if it is bound, // so this function directly places the incoming value into the SQL statement. ColumnBuilder& Default(int64_t value); // Indicate that the column is unique. // Allow for data driven construction with input value. ColumnBuilder& Unique(bool isTrue = true); // Indicate that the column is the primary key. // Allow for data driven construction with input value. ColumnBuilder& PrimaryKey(bool isTrue = true); }; // Helper used to specify a primary key with multiple columns. struct PrimaryKeyBuilder : public details::SubBuilderBase { PrimaryKeyBuilder(); PrimaryKeyBuilder(std::initializer_list columns); PrimaryKeyBuilder(const PrimaryKeyBuilder&) = default; PrimaryKeyBuilder& operator=(const PrimaryKeyBuilder&) = default; PrimaryKeyBuilder(PrimaryKeyBuilder&&) noexcept = default; PrimaryKeyBuilder& operator=(PrimaryKeyBuilder&&) noexcept = default; virtual operator details::SubBuilder() override; // Add a column to the primary key. PrimaryKeyBuilder& Column(std::string_view column); private: bool m_isFirst = true; bool m_needsClosing = true; }; // A class that aids in building SQL statements in a more expressive manner than simple strings. struct StatementBuilder { StatementBuilder() = default; StatementBuilder(const StatementBuilder&) = default; StatementBuilder& operator=(const StatementBuilder&) = default; StatementBuilder(StatementBuilder&&) = default; StatementBuilder& operator=(StatementBuilder&&) = default; // Begin a select statement for the given columns. StatementBuilder& Select(); StatementBuilder& Select(std::string_view column); StatementBuilder& Select(std::initializer_list columns); StatementBuilder& Select(const QualifiedColumn& column); StatementBuilder& Select(std::initializer_list columns); StatementBuilder& Select(details::rowcount_t); // Indicate the table that the statement will be operating on. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& From(); StatementBuilder& From(std::string_view table); StatementBuilder& From(QualifiedTable table); StatementBuilder& From(std::initializer_list table); // Begin a filter clause on the given column. StatementBuilder& Where(std::string_view column); StatementBuilder& Where(const QualifiedColumn& column); // A full filter clause looking for an embedded null character. // Is extremely specific to consistency checks, and so a more detailed construct is not required. StatementBuilder& WhereValueContainsEmbeddedNullCharacter(std::string_view column); StatementBuilder& WhereValueContainsEmbeddedNullCharacter(const QualifiedColumn& column); // Indicate the operation of the filter clause. template StatementBuilder& Equals(const ValueType& value) { AddBindFunctor(AppendOpAndBinder(Op::Equals), value); return *this; } template StatementBuilder& Equals(const std::optional& value) { if (value) { AddBindFunctor(AppendOpAndBinder(Op::Equals), value.value()); return *this; } else { return IsNull(); } } // The optional index value can be used to specify the parameter index. StatementBuilder& Equals(details::unbound_t, std::optional index = {}); StatementBuilder& Equals(std::nullptr_t); StatementBuilder& Equals(); StatementBuilder& Equals(const QualifiedColumn& column); template StatementBuilder& IsGreaterThan(const ValueType& value) { AddBindFunctor(AppendOpAndBinder(Op::GreaterThan), value); return *this; } StatementBuilder& IsGreaterThan(details::unbound_t, std::optional index = {}); template StatementBuilder& IsGreaterThanOrEqualTo(const ValueType& value) { AddBindFunctor(AppendOpAndBinder(Op::GreaterThanOrEqualTo), value); return *this; } StatementBuilder& IsGreaterThanOrEqualTo(details::unbound_t, std::optional index = {}); StatementBuilder& LikeWithEscape(std::string_view value); StatementBuilder& Like(details::unbound_t); StatementBuilder& Escape(std::string_view escapeChar); StatementBuilder& Not(); StatementBuilder& In(); // Appends a set of value binders for the In clause. StatementBuilder& In(size_t count); // IsNull(true) means the value is null; IsNull(false) means the value is not null. StatementBuilder& IsNull(bool isNull = true); StatementBuilder& IsNotNull() { return IsNull(false); } // Operators for combining filter clauses. StatementBuilder& And(std::string_view column); StatementBuilder& And(const QualifiedColumn& column); StatementBuilder& Or(const QualifiedColumn& column); // Begin a join clause. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& Join(std::string_view table); StatementBuilder& Join(QualifiedTable table); StatementBuilder& Join(std::initializer_list table); // Begin a left outer join clause. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& LeftOuterJoin(std::string_view table); StatementBuilder& LeftOuterJoin(QualifiedTable table); StatementBuilder& LeftOuterJoin(std::initializer_list table); // Set the join constraint. StatementBuilder& On(const QualifiedColumn& column1, const QualifiedColumn& column2); // Specify the grouping to use. StatementBuilder& GroupBy(std::string_view column); StatementBuilder& GroupBy(const QualifiedColumn& column); // Specify the ordering to use. StatementBuilder& OrderBy(std::string_view column); StatementBuilder& OrderBy(const QualifiedColumn& column); StatementBuilder& OrderBy(std::initializer_list columns); // Specify the ordering behavior. StatementBuilder& Ascending(); StatementBuilder& Descending(); // Limits the result set to the given number of rows. StatementBuilder& Limit(size_t rowCount); // Begin an insert statement for the given table. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& InsertInto(std::string_view table); StatementBuilder& InsertInto(QualifiedTable table); StatementBuilder& InsertInto(std::initializer_list table); // Begin an insert or ignore statement for the given table. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& InsertOrIgnore(std::string_view table); StatementBuilder& InsertOrIgnore(QualifiedTable table); StatementBuilder& InsertOrIgnore(std::initializer_list table); // Set the columns for a statement (typically insert). StatementBuilder& Columns(std::string_view column); StatementBuilder& Columns(std::initializer_list columns); StatementBuilder& Columns(const QualifiedColumn& column); StatementBuilder& Columns(std::initializer_list columns); // Set the columns for a select or create table statement. StatementBuilder& Columns(std::initializer_list columns); StatementBuilder& BeginColumns(); StatementBuilder& Column(std::string_view column); StatementBuilder& Column(const QualifiedColumn& column); StatementBuilder& Column(Aggregate aggOp, std::string_view column); StatementBuilder& Column(Aggregate aggOp, const QualifiedColumn& column); StatementBuilder& Column(const details::SubBuilder& column); StatementBuilder& EndColumns(); // Set the columns null constraint. StatementBuilder& NotNull(bool isTrue = true); // Set the column's default value. template StatementBuilder& Default(const ValueType& value) { m_stream << " DEFAULT (" << value << ")"; return *this; } // Add the values clause for an insert statement. template StatementBuilder& Values(const ValueTypes&... values) { int bindIndexBegin = AppendValuesAndBinders(sizeof...(ValueTypes)); // Use folding to add a binder for every value, specifically in the order they were given. // Do not change this expression without understanding the implications to the bind order. // See: https://en.cppreference.com/w/cpp/language/fold for more details. (FoldHelper{}, ..., InsertValuesValueBinder(bindIndexBegin++, values)); return *this; } StatementBuilder& BeginValues(); template StatementBuilder& Value(const ValueType& value) { InsertValuesValueBinder(AppendValueAndBinder(), value); return *this; } StatementBuilder& EndValues(); // Begin a table creation statement. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& CreateTable(std::string_view table); StatementBuilder& CreateTable(QualifiedTable table); StatementBuilder& CreateTable(std::initializer_list table); // Begin an alter table statement. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& AlterTable(std::string_view table); StatementBuilder& AlterTable(QualifiedTable table); StatementBuilder& AlterTable(std::initializer_list table); // Complete an alter table statement by adding a column. StatementBuilder& Add(std::string_view column, Type type); // Begin a table deletion statement. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& DropTable(std::string_view table); StatementBuilder& DropTable(QualifiedTable table); StatementBuilder& DropTable(std::initializer_list table); // Begin a table deletion statement. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& DropTableIfExists(std::string_view table); StatementBuilder& DropTableIfExists(QualifiedTable table); StatementBuilder& DropTableIfExists(std::initializer_list table); // Begin an index creation statement. // The initializer_list form enables the index name to be constructed from multiple parts. StatementBuilder& CreateIndex(std::string_view table); StatementBuilder& CreateIndex(QualifiedTable table); StatementBuilder& CreateIndex(std::initializer_list table); // Begin an unique index creation statement. // The initializer_list form enables the index name to be constructed from multiple parts. StatementBuilder& CreateUniqueIndex(std::string_view table); StatementBuilder& CreateUniqueIndex(QualifiedTable table); StatementBuilder& CreateUniqueIndex(std::initializer_list table); // Begin an index deletion statement. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& DropIndex(std::string_view index); StatementBuilder& DropIndex(QualifiedTable index); StatementBuilder& DropIndex(std::initializer_list index); // Set index target table. StatementBuilder& On(std::string_view table); StatementBuilder& On(std::initializer_list table); // Begin a delete statement. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& DeleteFrom(std::string_view table); StatementBuilder& DeleteFrom(QualifiedTable table); StatementBuilder& DeleteFrom(std::initializer_list table); // Begin an update statement. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& Update(std::string_view table); StatementBuilder& Update(QualifiedTable table); StatementBuilder& Update(std::initializer_list table); // Begin an `update or replace` statement. // The initializer_list form enables the table name to be constructed from multiple parts. StatementBuilder& UpdateOrReplace(std::string_view table); StatementBuilder& UpdateOrReplace(QualifiedTable table); StatementBuilder& UpdateOrReplace(std::initializer_list table); // Output the set portion of an update statement. StatementBuilder& Set(); // Output the set portion of an update statement. StatementBuilder& Vacuum(); // General purpose functions to begin and end a parenthetical expression. StatementBuilder& BeginParenthetical(); StatementBuilder& EndParenthetical(); // Adds the `without rowid` clause. StatementBuilder& WithoutRowID(); // Assign an alias to the previous item. StatementBuilder& As(std::string_view alias); // Gets the last bound index. // A value of zero indicates that nothing has been bound. int GetLastBindIndex() const { return m_bindIndex - 1; } // Prepares and returns the statement, applying any bindings that were requested. Statement Prepare(const Connection& connection); // A convenience function that prepares, binds, and then executes a statement that does not return rows. void Execute(const Connection& connection); private: enum class Op { Equals, Like, Escape, Literal, GreaterThan, GreaterThanOrEqualTo, }; // Appends given the operation. // The optional index value can be used to specify the parameter index. int AppendOpAndBinder(Op op, std::optional index = {}); // Appends a set of binders for the values clause of an insert. int AppendValuesAndBinders(size_t count); // Appends a binder for the values clause of an insert. int AppendValueAndBinder(); // Adds a functor to our list that will bind the given value. template void AddBindFunctor(int binderIndex, const ValueType& value) { m_binders.emplace_back([binderIndex, value](Statement& s) { s.Bind(binderIndex, value); }); } // Helper template for binding incoming values for an insert. template StatementBuilder& InsertValuesValueBinder(int bindIndex, const ValueType& value) { AddBindFunctor(bindIndex, value); return *this; } template StatementBuilder& InsertValuesValueBinder(int bindIndex, const std::optional& value) { if (value) { AddBindFunctor(bindIndex, value.value()); } else { AddBindFunctor(bindIndex, nullptr); } return *this; } StatementBuilder& InsertValuesValueBinder(int, details::unbound_t) { return *this; } StatementBuilder& InsertValuesValueBinder(int bindIndex, std::nullptr_t) { AddBindFunctor(bindIndex, nullptr); return *this; } std::ostringstream m_stream; // Because binding values starts at 1 int m_bindIndex = 1; std::vector> m_binders; bool m_needsComma = false; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/SQLiteStorageBase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include namespace AppInstaller::SQLite { // Type that wraps the basic SQLite storage functionality; the connection and metadata like schema version. struct SQLiteStorageBase { // The disposition for opening the database. enum class OpenDisposition { // Open for read only. Read, // Open for read and write. ReadWrite, // The database will not change while in use; open for immutable read. Immutable, }; // Gets the last write time for the database. std::chrono::system_clock::time_point GetLastWriteTime() const; // Gets the identifier written to the database when it was created. std::string GetDatabaseIdentifier() const; // Gets the schema version of the database. const Version& GetVersion() const { return m_version; } // Renames the database file and any auxiliary files given the inputs. // Should only be used on an inactive database. // If overwrite is given, existing destination files will be removed first. static void RenameSQLiteDatabase(const std::filesystem::path& source, const std::filesystem::path& destination, bool overwrite = false); protected: SQLiteStorageBase(const std::string& target, const Version& version, size_t pageSize = 0); SQLiteStorageBase(const std::string& filePath, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); SQLiteStorageBase(const std::string& target, SQLiteStorageBase& source); // Sets the last write time metadata value in the database. void SetLastWriteTime(); Utility::ManagedFile m_indexFile; SQLite::Connection m_dbconn; Version m_version; std::unique_ptr m_interfaceLock = std::make_unique(); }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/SQLiteTempTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::SQLite { // The base for a class that represents a temp table. struct TempTable { TempTable(); ~TempTable(); TempTable(const TempTable&) = delete; TempTable& operator=(const TempTable&) = delete; TempTable(TempTable&&) = default; TempTable& operator=(TempTable&&) = default; protected: // Gets the qualified name of the temp table. Builder::QualifiedTable GetQualifiedName() const; // Prepares the drop table statement for use in destructor. // It needs to be run by the derived class after the table is actually created. void InitDropStatement(const Connection& connection); private: std::string m_name; Statement m_dropTableStatement; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/SQLiteVersion.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace AppInstaller::SQLite { // Represents the schema version of the database. struct Version { // The major version of the schema. // All minor changes to this major version must be backward compatible. uint32_t MajorVersion{}; // The minor version of the schema. // All changes to the schema warrant a change to the minor version. uint32_t MinorVersion{}; bool operator==(const Version& other) const { return (MajorVersion == other.MajorVersion && MinorVersion == other.MinorVersion); } bool operator!=(const Version& other) const { return !operator==(other); } bool operator>=(const Version& other) const { if (MajorVersion > other.MajorVersion) return true; if (MajorVersion < other.MajorVersion) return false; return MinorVersion >= other.MinorVersion; } bool operator<(const Version& other) const { if (MajorVersion < other.MajorVersion) return true; if (MajorVersion > other.MajorVersion) return false; return MinorVersion < other.MinorVersion; } // Gets a version that represents the latest schema known to the implementation. static Version Latest(); // Gets a version that represents the latest schema known to the implementation for the given major version. static Version LatestForMajor(uint32_t majorVersion); // Determines if this version represents the latest schema. bool IsLatest() const; // Determines if this version represents the latest schema of the given major version. bool IsLatestForMajor(uint32_t majorVersion) const; // Determines the schema version of the opened database. static Version GetSchemaVersion(Connection& connection); // Writes the current version to the given database. void SetSchemaVersion(Connection& connection) const; }; // Output the version std::ostream& operator<<(std::ostream& out, const Version& version); } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/SQLiteWrapper.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SQLITE_MEMORY_DB_CONNECTION_TARGET ":memory:" using namespace std::string_view_literals; namespace AppInstaller::SQLite { // The name of the rowid column in SQLite. extern std::string_view RowIDName; // The type of a rowid column in code. using rowid_t = int64_t; // The type to use for blob data. using blob_t = std::vector; namespace details { template constexpr bool dependent_false = false; template struct ParameterSpecificsImpl { static T& ToLog(T&&) { static_assert(dependent_false, "No type specific override has been supplied"); } static void Bind(sqlite3_stmt*, int, T&&) { static_assert(dependent_false, "No type specific override has been supplied"); } static T GetColumn(sqlite3_stmt*, int) { static_assert(dependent_false, "No type specific override has been supplied"); } }; template <> struct ParameterSpecificsImpl { inline static std::string_view ToLog(nullptr_t) { return "null"sv; } static void Bind(sqlite3_stmt* stmt, int index, nullptr_t); }; template <> struct ParameterSpecificsImpl { inline static const std::string& ToLog(const std::string& v) { return v; } static void Bind(sqlite3_stmt* stmt, int index, const std::string& v); static std::string GetColumn(sqlite3_stmt* stmt, int column); }; template <> struct ParameterSpecificsImpl { inline static const std::string_view& ToLog(const std::string_view& v) { return v; } static void Bind(sqlite3_stmt* stmt, int index, std::string_view v); }; template <> struct ParameterSpecificsImpl { inline static int ToLog(int v) { return v; } static void Bind(sqlite3_stmt* stmt, int index, int v); static int GetColumn(sqlite3_stmt* stmt, int column); }; template <> struct ParameterSpecificsImpl { inline static int64_t ToLog(int64_t v) { return v; } static void Bind(sqlite3_stmt* stmt, int index, int64_t v); static int64_t GetColumn(sqlite3_stmt* stmt, int column); }; template <> struct ParameterSpecificsImpl { inline static bool ToLog(bool v) { return v; } static void Bind(sqlite3_stmt* stmt, int index, bool v); static bool GetColumn(sqlite3_stmt* stmt, int column); }; template <> struct ParameterSpecificsImpl { static std::string ToLog(const blob_t& v); static void Bind(sqlite3_stmt* stmt, int index, const blob_t& v); static blob_t GetColumn(sqlite3_stmt* stmt, int column); }; template <> struct ParameterSpecificsImpl { static std::string ToLog(const GUID& v); static void Bind(sqlite3_stmt* stmt, int index, const GUID& v); static GUID GetColumn(sqlite3_stmt* stmt, int column); }; template struct ParameterSpecificsImpl>> { static auto ToLog(E v) { return ToIntegral(v); } static void Bind(sqlite3_stmt* stmt, int index, E v) { ParameterSpecificsImpl>::Bind(stmt, index, ToIntegral(v)); } static E GetColumn(sqlite3_stmt* stmt, int column) { return ToEnum(ParameterSpecificsImpl>::GetColumn(stmt, column)); } }; template struct ParameterSpecificsImpl> { using Optional = std::optional; static auto ToLog(const Optional& v) { std::ostringstream result; if (v) { result << ParameterSpecificsImpl::ToLog(v.value()); } else { result << "{null}"; } return std::move(result).str(); } static void Bind(sqlite3_stmt* stmt, int index, const Optional& v) { if (v) { ParameterSpecificsImpl::Bind(stmt, index, v.value()); } else { ParameterSpecificsImpl::Bind(stmt, index, nullptr); } } static Optional GetColumn(sqlite3_stmt* stmt, int column) { if (sqlite3_column_type(stmt, column) == SQLITE_NULL) { return std::nullopt; } else { return ParameterSpecificsImpl::GetColumn(stmt, column); } } }; template using ParameterSpecifics = ParameterSpecificsImpl>; // Allows the connection to be shared so that it can be closed in some circumstances. struct SharedConnection { // Disables the connection, causing an exception to be thrown by `get`. void Disable(); // Gets the connection object if active. sqlite3* Get() const; // Gets the connection object for creation. sqlite3** GetPtr(); private: std::atomic_bool m_active = true; wil::unique_any m_dbconn; }; } // A SQLite exception. struct SQLiteException : public wil::ResultException { SQLiteException(int error) : wil::ResultException(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, error)) {} }; struct Statement; // The connection to a database. struct Connection { friend Statement; // The disposition for opening a database connection. enum class OpenDisposition : int { // Open existing database for reading. ReadOnly = SQLITE_OPEN_READONLY, // Open existing database for reading and writing. ReadWrite = SQLITE_OPEN_READWRITE, // Create new database for reading and writing. Create = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, }; // Flags for opening a database connection. enum class OpenFlags : int { // No flags specified. None = 0, // Indicate that the target can be a URI. Uri = SQLITE_OPEN_URI, }; static Connection Create(const std::string& target, OpenDisposition disposition, OpenFlags flags = OpenFlags::None); Connection() = default; Connection(const Connection&) = delete; Connection& operator=(const Connection&) = delete; Connection(Connection&& other) = default; Connection& operator=(Connection&& other) = default; ~Connection() = default; // Enables the ICU integrations on this connection. void EnableICU(); // Gets the last inserted rowid to the database. rowid_t GetLastInsertRowID(); // Gets the count of changed rows for the last executed statement. int GetChanges() const; //. Gets the (fixed but arbitrary) identifier for this connection. size_t GetID() const; // Sets the busy timeout for the connection. void SetBusyTimeout(std::chrono::milliseconds timeout); // Sets the journal mode. // Returns true if successful, false if not. // Must be performed outside of a transaction. bool SetJournalMode(std::string_view mode); // Sets the page size for a new, empty database. // Must be called before the first write to take effect. // Must be a power of two between 512 and 65536 (inclusive), but we let SQLite enforce that. void SetPageSize(size_t pageSize); operator sqlite3* () const { return m_dbconn->Get(); } protected: // Gets the shared connection. std::shared_ptr GetSharedConnection() const; private: Connection(const std::string& target, OpenDisposition disposition, OpenFlags flags); size_t m_id = 0; std::shared_ptr m_dbconn; }; // A SQL statement. struct Statement { static Statement Create(const Connection& connection, const std::string& sql); static Statement Create(const Connection& connection, std::string_view sql); static Statement Create(const Connection& connection, char const* const sql); Statement() = default; Statement(const Statement&) = delete; Statement& operator=(const Statement&) = delete; Statement(Statement&& other) = default; Statement& operator=(Statement&& other) = default; operator sqlite3_stmt* () const { return m_stmt.get(); } // The state of the statement. enum class State { // The statement has been prepared, but not evaluated. Prepared = 0, // The statement has a row available for reading. HasRow = 1, // The statement has been completed. Completed = 2, // The statement has resulted in an error. Error = 3, }; // Gets the current state of the statement. State GetState() const { return m_state; } // Bind parameters to the statement. // The index is 1 based. template void Bind(int index, Value&& v) { AICLI_LOG(SQL, Verbose, << "Binding statement #" << m_connectionId << '-' << m_id << ": " << index << " => " << details::ParameterSpecifics::ToLog(std::forward(v))); details::ParameterSpecifics::Bind(m_stmt.get(), index, std::forward(v)); } // Evaluate the statement; either retrieving the next row or executing some action. // Returns true if there is a row of data, or false if there is none. // This return value is the equivalent of 'GetState() == State::HasRow' after calling Step. bool Step(bool closeConnectionOnError = false); // Equivalent to Step, but does not ever expect a result, throwing if one is retrieved. void Execute(bool closeConnectionOnError = false); // Gets a boolean value that indicates whether the specified column value is null in the current row. // The index is 0 based. bool GetColumnIsNull(int column); // Gets the value of the specified column from the current row. // The index is 0 based. template Value GetColumn(int column) { THROW_HR_IF(E_BOUNDS, m_state != State::HasRow); return details::ParameterSpecifics::GetColumn(m_stmt.get(), column); } // Gets the entire row of values from the current row. // The values requested *must* be those available starting from the first column, but trailing columns can be omitted. template std::tuple GetRow() { return GetRowImpl(std::make_integer_sequence{}); } // Resets the statement state, allowing it to be evaluated again. // Note that this does not clear data bindings. void Reset(); // Determines if the statement owns an underlying object. operator bool() const { return static_cast(m_stmt); } private: Statement(const Connection& connection, std::string_view sql); // Helper to receive the integer sequence from the public function. // This is equivalent to calling: // for (i = 0 .. count of Values types) // GetColumn(i) // Then putting them all into a tuple. template std::tuple GetRowImpl(std::integer_sequence) { THROW_HR_IF(E_BOUNDS, m_state != State::HasRow); return std::make_tuple(details::ParameterSpecifics::GetColumn(m_stmt.get(), I)...); } std::shared_ptr m_dbconn; size_t m_connectionId = 0; size_t m_id = 0; wil::unique_any m_stmt; State m_state = State::Prepared; }; // A SQLite transaction. // Use as the beginning of a transaction stack, specifically when the transaction will write // and the database is in WAL mode. struct Transaction { // Creates a transaction, beginning it. static Transaction Create(Connection& connection, std::string name, bool immediateWrite); Transaction(); Transaction(const Transaction&) = delete; Transaction& operator=(const Transaction&) = delete; Transaction(Transaction&&) = default; Transaction& operator=(Transaction&&) = default; ~Transaction(); // Rolls back the Transaction. void Rollback(bool throwOnError = true); // Commits the Transaction. void Commit(); private: Transaction(Connection& connection, std::string&& name, bool immediateWrite); std::string m_name; DestructionToken m_inProgress = true; Statement m_rollback; Statement m_commit; }; // A SQLite savepoint. struct Savepoint { // Creates a savepoint, beginning it. static Savepoint Create(Connection& connection, std::string name); Savepoint(); Savepoint(const Savepoint&) = delete; Savepoint& operator=(const Savepoint&) = delete; Savepoint(Savepoint&&) = default; Savepoint& operator=(Savepoint&&) = default; ~Savepoint(); // Rolls back the Savepoint. void Rollback(bool throwOnError = true); // Commits the Savepoint. void Commit(); private: Savepoint(Connection& connection, std::string&& name); std::string m_name; DestructionToken m_inProgress = true; Statement m_rollbackTo; Statement m_release; }; // A SQLite backup operation. struct Backup { // Creates a backup. static Backup Create(Connection& destination, const std::string& destinationName, Connection& source, const std::string& sourceName); Backup(const Backup&) = delete; Backup& operator=(const Backup&) = delete; Backup(Backup&&) = default; Backup& operator=(Backup&&) = default; // Performs some or all of the backup. // Returns true if the backup is completed, false if not. bool Step(int pages = -1); private: Backup(Connection& destination, const std::string& destinationName, Connection& source, const std::string& sourceName); wil::unique_any m_backup; }; // The escape character used in the EscapeStringForLike function. extern std::string_view EscapeCharForLike; // Escapes the given input string for passing to a like operation. std::string EscapeStringForLike(std::string_view value); } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/Security.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace AppInstaller::Security { // A Windows integrity level. enum class IntegrityLevel { Untrusted, Low, Medium, MediumPlus, High, System, ProtectedProcess, }; // Gets the integrity level for the current effective token. // Does not know how to determine MediumPlus, if that ever matters... IntegrityLevel GetEffectiveIntegrityLevel(); // Determines if the current COM caller is the same user as the current process // and is at least equal integrity level (higher will also be allowed). bool IsCOMCallerSameUserAndIntegrityLevel(); // Determines if the current COM caller is at least the minimum integrity level provided. bool IsCOMCallerIntegrityLevelAtLeast(IntegrityLevel minimumLevel); // Determines if the current integrity level is at least the minimum integrity level provided. bool IsCurrentIntegrityLevelAtLeast(IntegrityLevel minimumLevel); // Gets the string representation of the given SID. std::string ToString(PSID sid); } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/SharedThreadGlobals.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace AppInstaller::ThreadLocalStorage { struct PreviousThreadGlobals; // Interface for access to values that are stored on a per-thread object. struct ThreadGlobals { ThreadGlobals() = default; virtual ~ThreadGlobals() = default; virtual AppInstaller::Logging::DiagnosticLogger& GetDiagnosticLogger() = 0; virtual void* GetTelemetryObject() = 0; // Set Globals for Current Thread // Return RAII object with its ownership to set the AppInstaller ThreadLocalStorage back to previous state virtual std::unique_ptr SetForCurrentThread(); // Return Globals for Current Thread static ThreadGlobals* GetForCurrentThread(); }; // RAII object used to enable reverting back to the previous thread globals object. struct PreviousThreadGlobals { ~PreviousThreadGlobals(); PreviousThreadGlobals(ThreadGlobals* previous); private: ThreadGlobals* m_previous; DWORD m_threadId; }; } ================================================ FILE: src/AppInstallerSharedLib/Public/winget/Yaml.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include namespace AppInstaller::YAML { // A location within the stream. struct Mark { Mark() = default; Mark(size_t l, size_t c) : line(l), column(c) {} size_t line = 0; size_t column = 0; }; // An exception from YAML. struct Exception : public wil::ResultException { // The type of error that occurred. enum class Type { None, Memory, Reader, Scanner, Parser, Composer, Writer, Emitter, Policy, }; // Should only be used for Memory. Exception(Type type); // Should only be used for Reader. Exception(Type type, const char* problem, size_t offset, int value); // Used for Scanner, Parser, and Composer. Exception(Type type, const char* problem, const Mark& problemMark, const char* context = {}, const Mark& contextMark = {}); // Used for Writer and Emitter. Exception(Type type, const char* problem); const char* what() const noexcept override; const Mark& GetMark() const; private: std::string m_what; YAML::Mark m_mark; }; // A YAML node. struct Node { // The node's type. enum class Type { Invalid, None, Scalar, Sequence, Mapping }; // The node's tag enum class TagType { Unknown, Null, Bool, Str, Int, Float, Timestamp, Seq, Map, }; Node() : m_type(Type::Invalid), m_tagType(TagType::Unknown) {} Node(Type type, std::string tag, const Mark& mark); // Sets the scalar value of the node. void SetScalar(std::string value); void SetScalar(std::string value, bool isQuoted); // Adds a child node to the sequence. template Node& AddSequenceNode(Args&&... args) { Require(Type::Sequence); return m_sequence->emplace_back(std::forward(args)...); } // Merges sequence nodes. If both sequence have the specified key with the same value // they will get merged together. All elements in sequence must have the key. void MergeSequenceNode(Node other, std::string_view key, bool caseInsensitive = false); // Adds a child node to the mapping. template Node& AddMappingNode(Node&& key, Args&&... args) { Require(Type::Mapping); return m_mapping->emplace(std::move(key), Node(std::forward(args)...))->second; } // Merge mapping node. If both contain a node with the same key preserve this. void MergeMappingNode(Node other, bool caseInsensitive = false); bool IsDefined() const { return m_type != Type::Invalid; } bool IsNull() const { return m_type == Type::Invalid || m_type == Type::None || (m_type == Type::Scalar && m_scalar.empty()); } bool IsScalar() const { return m_type == Type::Scalar; } bool IsSequence() const { return m_type == Type::Sequence; } bool IsMap() const { return m_type == Type::Mapping; } Type GetType() const { return m_type; } TagType GetTagType() const { return m_tagType; } explicit operator bool() const { return IsDefined(); } // Gets the scalar value as the requested type. template T as() const { Require(Type::Scalar); T* t = nullptr; return as_dispatch(t); } template std::optional try_as() const { if (m_type != Type::Scalar) { return {}; } T* t = nullptr; return try_as_dispatch(t); } bool operator<(const Node& other) const; // Gets a child node from the mapping by its name. Node& operator[](std::string_view key); const Node& operator[](std::string_view key) const; // Gets a child node from the mapping by its name case-insensitive. Node& GetChildNode(std::string_view key); const Node& GetChildNode(std::string_view key) const; // Gets a child node from the sequence by its index. Node& operator[](size_t index); const Node& operator[](size_t index) const; // Gets the number of child nodes. size_t size() const; // Gets the mark for this node. const Mark& Mark() const { return m_mark; } // Gets the nodes in the sequence. const std::vector& Sequence() const; // Gets the nodes in the mapping. const std::multimap& Mapping() const; private: Node(std::string_view key) : m_type(Type::Scalar), m_scalar(key), m_tagType(TagType::Str) {} // Require certain node types to; throwing if the requirement is not met. void Require(Type type) const; // The workers for the as function. std::string as_dispatch(std::string*) const; std::optional try_as_dispatch(std::string*) const; std::wstring as_dispatch(std::wstring*) const; std::optional try_as_dispatch(std::wstring*) const; int64_t as_dispatch(int64_t*) const; std::optional try_as_dispatch(int64_t*) const; int as_dispatch(int*) const; std::optional try_as_dispatch(int*) const; bool as_dispatch(bool*) const; std::optional try_as_dispatch(bool*) const; Type m_type; std::string m_tag; TagType m_tagType; YAML::Mark m_mark; std::string m_scalar; std::optional> m_sequence; std::optional> m_mapping; }; // Loads from the input; returns the root node of the first document. Node Load(std::string_view input); Node Load(const std::string& input); Node Load(const std::filesystem::path& input); Node Load(const std::filesystem::path& input, Utility::SHA256::HashBuffer& hashOut); // Any emitter event. // Not using enum class to enable existing code to function. enum EmitterEvent { BeginSeq, EndSeq, BeginMap, EndMap, Key, Value, }; // Sets the scalar style to use for the next scalar output. enum class ScalarStyle { Any, Plain, SingleQuoted, DoubleQuoted, Literal, Folded, }; // A schema header for a document. struct DocumentSchemaHeader { DocumentSchemaHeader() = default; DocumentSchemaHeader(std::string schemaHeaderString, const Mark& mark) : SchemaHeader(std::move(schemaHeaderString)), Mark(mark) {} std::string SchemaHeader; Mark Mark; static constexpr std::string_view YamlLanguageServerKey = "yaml-language-server"; }; struct Document { Document() = default; Document(Node root, DocumentSchemaHeader schemaHeader) : m_root(std::move(root)), m_schemaHeader(std::move(schemaHeader)) {} const DocumentSchemaHeader& GetSchemaHeader() const { return m_schemaHeader; } // Return r-values for move semantics Node&& GetRoot() && { return std::move(m_root); } private: Node m_root; DocumentSchemaHeader m_schemaHeader; }; // Forward declaration to allow pImpl in this Emitter. namespace Wrapper { struct Document; } // Loads from the input; returns the root node of the first document. Document LoadDocument(std::string_view input); Document LoadDocument(const std::string& input); Document LoadDocument(const std::filesystem::path& input); Document LoadDocument(const std::filesystem::path& input, Utility::SHA256::HashBuffer& hashOut); // A YAML emitter. struct Emitter { Emitter(); Emitter(const Emitter&) = delete; Emitter& operator=(const Emitter&) = delete; Emitter(Emitter&&) noexcept; Emitter& operator=(Emitter&&) noexcept; ~Emitter(); // Emit events and values. Emitter& operator<<(EmitterEvent event); Emitter& operator<<(std::string_view value); Emitter& operator<<(int64_t value); Emitter& operator<<(int value); Emitter& operator<<(bool value); Emitter& operator<<(ScalarStyle style); // Gets the result of the emitter; can only be retrieved once. std::string str(); // Gets the result of the emitter to out stream; can only be retrieved once. void Emit(std::ostream& out); private: // Appends the given node to the current container if applicable. void AppendNode(int id); std::unique_ptr m_document; // If set, stores the last Key that was set. std::optional m_keyId; struct ContainerInfo { ContainerInfo(int id, bool map) : Id(id), IsMapping(map) {} int Id; bool IsMapping; }; // The stack of containers being emitted. std::stack m_containers; // *** State Machine *** // The type of input coming into the emitter. enum class InputType { Scalar, BeginSeq, EndSeq, BeginMap, EndMap, Key, Value, }; // If set, defines the type of the next scalar (Key or Value). std::optional m_scalarType; // If set, defines the style of the next scalar. std::optional m_scalarStyle; // Converts the input type to a bitmask value. size_t GetInputBitmask(InputType type); // Checks the state of the emitter to ensure that the incoming value is acceptable. void CheckInput(InputType type); // The currently allowed input types. size_t m_allowedInputs = 0; template void SetAllowedInputs() { m_allowedInputs = (GetInputBitmask(types) | ...); } // Sets the allowed inputs for the container on the top of the stack. void SetAllowedInputsForContainer(); }; } ================================================ FILE: src/AppInstallerSharedLib/Registry.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/Registry.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerLogging.h" namespace AppInstaller::Registry { namespace { std::wstring_view ConvertBytesToWideStringView(const std::vector& data) { // Remove any extra bytes because the data could just be dirty; better to not have a bad character than outright fail. std::wstring_view result{ reinterpret_cast(data.data()), data.size() / sizeof(wchar_t) }; // Registry values may or may not be null terminated; we will remove any trailing nulls while (!result.empty() && result.back() == L'\0') { result = result.substr(0, result.size() - 1); } return result; } std::wstring ConvertBytesToWideString(const std::vector& data) { return std::wstring{ ConvertBytesToWideStringView(data) }; } std::string ConvertBytesToString(const std::vector& data) { return Utility::ConvertToUTF8(ConvertBytesToWideStringView(data)); } uint32_t ConvertBytesToUInt32LE(const std::vector& data) { THROW_HR_IF(E_NOT_VALID_STATE, data.size() != sizeof(uint32_t)); uint32_t result = 0; uint32_t shift = 0; for (const BYTE datum : data) { result |= ((static_cast(datum) & 0xFF) << shift); shift += 8; } return result; } bool TryGetRegistryValueNameFromIndex(const wil::shared_hkey& key, DWORD index, std::wstring& valueName) { constexpr DWORD MaxNameLength = 32767; LSTATUS status = ERROR_SUCCESS; DWORD charCount = 0; valueName = L'\0'; while (valueName.size() <= MaxNameLength) { charCount = wil::safe_cast(valueName.size()); // We could also get the type and data here, but we read only the name instead // to prevent duplication with the code that gets the data from the name. status = RegEnumValueW(key.get(), index, &valueName[0], &charCount, nullptr, nullptr, nullptr, nullptr); if (status == ERROR_MORE_DATA) { // See if we can get away with the current capacity if (valueName.size() < valueName.capacity()) { valueName.resize(valueName.capacity()); } else { valueName.resize(valueName.capacity() * 2); } } else { break; } } if (status == ERROR_SUCCESS) { valueName.resize(wil::safe_cast(charCount)); return true; } else if (status == ERROR_NO_MORE_ITEMS) { return false; } else { THROW_IF_WIN32_ERROR(status); return false; } } bool TryGetRegistryValueData(const wil::shared_hkey& key, const std::wstring& valueName, DWORD& type, std::vector& data) { data.resize(64); LSTATUS status = ERROR_SUCCESS; DWORD byteCount = 0; while (data.size() < (64 << 20)) { byteCount = wil::safe_cast(data.size()); status = RegGetValueW(key.get(), nullptr, valueName.c_str(), RRF_RT_ANY | RRF_NOEXPAND, &type, data.data(), &byteCount); if (status == ERROR_MORE_DATA && byteCount > data.size()) { data.resize(byteCount); } else { break; } } if (status == ERROR_FILE_NOT_FOUND) { return false; } THROW_IF_WIN32_ERROR(status); // Resize to actual data size data.resize(byteCount); return true; } bool DeleteRegistryValueData(const wil::shared_hkey& key, const std::wstring& valueName) { LSTATUS status = RegDeleteValueW(key.get(), valueName.c_str()); if (status == ERROR_FILE_NOT_FOUND) { return false; } THROW_IF_WIN32_ERROR(status); return true; } } namespace details { ValueTypeSpecifics::value_t ValueTypeSpecifics::Convert(const std::vector& data) { return data; } ValueTypeSpecifics::value_t ValueTypeSpecifics::Convert(const std::vector& data) { return ConvertBytesToString(data); } ValueTypeSpecifics::value_t ValueTypeSpecifics::Convert(const std::vector& data) { return ConvertBytesToWideString(data); } ValueTypeSpecifics::value_t ValueTypeSpecifics::Convert(const std::vector& data) { return Utility::ConvertToUTF8(Utility::ExpandEnvironmentVariables(ConvertBytesToWideString(data))); } ValueTypeSpecifics::value_t ValueTypeSpecifics::Convert(const std::vector& data) { return ConvertBytesToWideString(data); } ValueTypeSpecifics::value_t ValueTypeSpecifics::Convert(const std::vector& data) { return data; } ValueTypeSpecifics::value_t ValueTypeSpecifics::Convert(const std::vector& data) { return ConvertBytesToUInt32LE(data); } } Value::Value(DWORD type, std::vector&& data) : m_type(static_cast(type)), m_data(std::move(data)) { } bool Value::HasCompatibleType(Type type) const { // Allow interop between String and ExpandString if ((m_type == Type::String || m_type == Type::ExpandString || m_type == Type::UTF16String || m_type == Type::UTF16ExpandString) && (type == Type::String || type == Type::ExpandString || type == Type::UTF16String || type == Type::UTF16ExpandString)) { return true; } return m_type == type; } ValueList::ValueRef::ValueRef(wil::shared_hkey key, std::wstring&& valueName) : m_key(std::move(key)), m_valueName(std::move(valueName)) {} std::string ValueList::ValueRef::Name() const { return Utility::ConvertToUTF8(m_valueName); } std::optional ValueList::ValueRef::Value() const { DWORD type; std::vector data; if (!TryGetRegistryValueData(m_key, m_valueName, type, data)) { return std::nullopt; } return Registry::Value{ type, std::move(data) }; } ValueList::const_iterator& ValueList::const_iterator::operator++() { ++m_index; GetValue(); return *this; } ValueList::const_iterator ValueList::const_iterator::operator++(int) { const_iterator result; result.m_key = m_key; result.m_index = m_index++; result.m_value = std::nullopt; std::swap(m_value, result.m_value); GetValue(); return result; } bool ValueList::const_iterator::operator==(const const_iterator& other) const { return (!m_key && !other.m_key) || (m_key.get() == other.m_key.get() && m_index == other.m_index); } bool ValueList::const_iterator::operator!=(const const_iterator& other) const { return !operator==(other); } void ValueList::const_iterator::GetValue() { std::wstring valueName; if (!TryGetRegistryValueNameFromIndex(m_key, m_index, valueName)) { m_key.reset(); return; } m_value = ValueRef{ m_key, std::move(valueName) }; } const ValueList::ValueRef& ValueList::const_iterator::operator*() const { return m_value.value(); } const ValueList::ValueRef* ValueList::const_iterator::operator->() const { return &m_value.value(); } ValueList::const_iterator::const_iterator(const wil::shared_hkey& key, DWORD index) : m_key(key), m_index(index) { GetValue(); } ValueList::const_iterator ValueList::begin() const { return { m_key }; } ValueList::const_iterator ValueList::end() const { return {}; } ValueList::ValueList(wil::shared_hkey key) : m_key(key) {} Key::Key(HKEY key) { Initialize(key, {}, 0, KEY_READ, false); } Key::Key(HKEY key, std::string_view subKey, DWORD options, REGSAM access) { Initialize(key, Utility::ConvertToUTF16(subKey), options, access, false); } Key::Key(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access) { Initialize(key, subKey, options, access, false); } std::string Key::SubKeyRef::Name() const { return Utility::ConvertToUTF8(m_subKeyName); } Key Key::SubKeyRef::Open() const { return { m_parentKey.get(), m_subKeyName, 0, m_access }; } Key::SubKeyRef::SubKeyRef(const wil::shared_hkey& key, REGSAM access) : m_parentKey(key), m_access(access), m_subKeyName(64, L'\0') { Enum(0); } void Key::SubKeyRef::Enum(DWORD index) { LSTATUS status = ERROR_SUCCESS; DWORD charCount = 0; while (m_subKeyName.size() < 4096) { charCount = wil::safe_cast(m_subKeyName.size()); status = RegEnumKeyExW(m_parentKey.get(), index, &m_subKeyName[0], &charCount, nullptr, nullptr, nullptr, nullptr); if (status == ERROR_MORE_DATA) { // See if we can get away with the current capacity if (m_subKeyName.size() < m_subKeyName.capacity()) { m_subKeyName.resize(m_subKeyName.capacity()); } else { m_subKeyName.resize(m_subKeyName.capacity() * 2); } } else { break; } } if (status == ERROR_SUCCESS) { m_subKeyName.resize(wil::safe_cast(charCount)); } else if (status == ERROR_NO_MORE_ITEMS) { m_parentKey.reset(); } else { THROW_IF_WIN32_ERROR(status); } } Key::const_iterator& Key::const_iterator::operator++() { m_subkey.Enum(++m_index); return *this; } Key::const_iterator Key::const_iterator::operator++(int) { const_iterator result = *this; m_subkey.Enum(++m_index); return result; } bool Key::const_iterator::operator==(const const_iterator& other) const { return (!m_subkey.m_parentKey && !other.m_subkey.m_parentKey) || (m_subkey.m_parentKey.get() == other.m_subkey.m_parentKey.get() && m_index == other.m_index); } bool Key::const_iterator::operator!=(const const_iterator& other) const { return !operator==(other); } const Key::SubKeyRef& Key::const_iterator::operator*() const { return m_subkey; } const Key::SubKeyRef* Key::const_iterator::operator->() const { return &m_subkey; } Key::const_iterator::const_iterator(const wil::shared_hkey& key, REGSAM access) : m_subkey(key, access) { } Key::const_iterator Key::begin() const { return { m_key, m_access }; } Key::const_iterator Key::end() const { return {}; } std::optional Key::operator[](std::string_view name) const { return operator[](Utility::ConvertToUTF16(name)); } std::optional Key::operator[](const std::wstring& name) const { DWORD type; std::vector data; if (TryGetRegistryValueData(m_key, name, type, data)) { return Value{ type, std::move(data) }; } else { return {}; } } void Key::DeleteValue(std::string_view name) const { DeleteValue(Utility::ConvertToUTF16(name)); } void Key::DeleteValue(const std::wstring& name) const { if (DeleteRegistryValueData(m_key, name)) { AICLI_LOG(Core, Verbose, << "Registry value '" << Utility::ConvertToUTF8(name) << "' deleted successfully."); } else { AICLI_LOG(Core, Verbose, << "Registry value '" << Utility::ConvertToUTF8(name) << "' does not exist."); } } std::optional Key::SubKey(std::string_view subKey, DWORD options) const { return SubKey(Utility::ConvertToUTF16(subKey), options); } std::optional Key::SubKey(const std::wstring& subKey, DWORD options) const { if (!m_key) { return std::nullopt; } Key result; if (result.Initialize(m_key.get(), subKey, options, m_access, true)) { return result; } else { return std::nullopt; } } void Key::SetValue(const std::wstring& name, const std::wstring& value, DWORD type) const { THROW_IF_WIN32_ERROR(RegSetValueExW(m_key.get(), name.c_str(), 0, type, reinterpret_cast(value.c_str()), static_cast(sizeof(wchar_t) * (value.size() + 1)))); AICLI_LOG(Core, Verbose, << "Setting '" << Utility::ConvertToUTF8(name) << "' with the value: " << Utility::ConvertToUTF8(value)); } void Key::SetValue(const std::wstring& name, const std::vector& value, DWORD type) const { THROW_IF_WIN32_ERROR(RegSetValueExW(m_key.get(), name.c_str(), 0, type, reinterpret_cast(value.data()), static_cast(value.size()))); AICLI_LOG(Core, Verbose, << "Setting '" << Utility::ConvertToUTF8(name) << "' with the value: " << ConvertBytesToString(value)); } void Key::SetValue(const std::wstring& name, DWORD value) const { THROW_IF_WIN32_ERROR(RegSetValueExW(m_key.get(), name.c_str(), 0, REG_DWORD, reinterpret_cast(&value), sizeof(DWORD))); AICLI_LOG(Core, Verbose, << "Setting '" << Utility::ConvertToUTF8(name) << "' with the value: " << value); } ValueList Key::Values() const { return { m_key }; } Key Key::OpenIfExists(HKEY key, std::string_view subKey, DWORD options, REGSAM access) { return OpenIfExists(key, Utility::ConvertToUTF16(subKey), options, access); } Key Key::OpenIfExists(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access) { Key result; result.Initialize(key, subKey, options, access, true); return result; } Key Key::Create(HKEY key, std::string_view subKey, DWORD options, REGSAM access) { return Create(key, Utility::ConvertToUTF16(subKey), options, access); } Key Key::Create(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access) { Key result; result.CreateAndOpen(key, subKey, options, access); return result; } bool Key::Delete(HKEY key, std::string_view subKey, DWORD samDesired) { return Delete(key, Utility::ConvertToUTF16(subKey), samDesired); } bool Key::Delete(HKEY key, const std::wstring& subKey, DWORD samDesired) { LSTATUS status = RegDeleteKeyExW(key, subKey.c_str(), samDesired, 0); if (status == ERROR_SUCCESS) { AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was deleted successfully."); return true; } else if (status == ERROR_FILE_NOT_FOUND) { AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was not found."); } else { THROW_IF_WIN32_ERROR(status); } return false; } bool Key::DeleteTree(HKEY key, const std::wstring& subKey) { LSTATUS status = RegDeleteTree(key, subKey.c_str()); if (status == ERROR_SUCCESS) { AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was deleted successfully."); return true; } else if (status == ERROR_FILE_NOT_FOUND) { AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was not found."); } else { THROW_IF_WIN32_ERROR(status); } return false; } bool Key::CreateAndOpen(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access) { m_access = access; LPDWORD disposition = {}; LSTATUS status = RegCreateKeyExW(key, subKey.c_str(), 0, nullptr, options, access, NULL, &m_key, disposition); if (disposition == (LPDWORD)REG_CREATED_NEW_KEY) { AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was created."); } else if (disposition == (LPDWORD)REG_OPENED_EXISTING_KEY) { AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' already existed and was opened."); } THROW_IF_WIN32_ERROR(status); return true; } bool Key::Initialize(HKEY key, const std::wstring& subKey, DWORD options, REGSAM access, bool ignoreErrorIfDoesNotExist) { m_access = access; LSTATUS status = RegOpenKeyExW(key, subKey.c_str(), options, access, &m_key); if (ignoreErrorIfDoesNotExist && status == ERROR_FILE_NOT_FOUND) { AICLI_LOG(Core, Verbose, << "Subkey '" << Utility::ConvertToUTF8(subKey) << "' was not found"); return false; } THROW_IF_WIN32_ERROR(status); return true; } } ================================================ FILE: src/AppInstallerSharedLib/Resources.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/Resources.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" #include "Public/AppInstallerErrors.h" namespace AppInstaller { namespace Resource { namespace { std::pair GetResourceData(PCWSTR resourceName, PCWSTR resourceType) { HMODULE resourceModule = nullptr; GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(GetResourceData), &resourceModule); THROW_LAST_ERROR_IF_NULL(resourceModule); HRSRC resourceInfoHandle = FindResourceW(resourceModule, resourceName, resourceType); THROW_LAST_ERROR_IF_NULL(resourceInfoHandle); HGLOBAL resourceMemoryHandle = LoadResource(resourceModule, resourceInfoHandle); THROW_LAST_ERROR_IF_NULL(resourceMemoryHandle); DWORD resourceSize = SizeofResource(resourceModule, resourceInfoHandle); THROW_LAST_ERROR_IF(resourceSize == 0); void* resourceContent = LockResource(resourceMemoryHandle); THROW_HR_IF_NULL(E_UNEXPECTED, resourceContent); return std::make_pair(resourceContent, static_cast(resourceSize)); } } std::string_view GetResourceAsString(int resourceName, int resourceType) { return GetResourceAsString(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); } std::string_view GetResourceAsString(PCWSTR resourceName, PCWSTR resourceType) { auto resourceData = GetResourceData(resourceName, resourceType); return { reinterpret_cast(resourceData.first), resourceData.second }; } std::pair GetResourceAsBytes(int resourceName, int resourceType) { return GetResourceAsBytes(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); } std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType) { auto resourceData = GetResourceData(resourceName, resourceType); return std::make_pair(reinterpret_cast(resourceData.first), resourceData.second); } // Utility class to load resources struct Loader { // Gets the singleton instance of the resource loader. static const Loader& Instance() { static Loader instance; return instance; } // Gets the string resource value. std::string ResolveString(std::wstring_view resKey) const { if (resKey.empty()) { return {}; } if (m_wingetLoader) { return Utility::ConvertToUTF8(m_wingetLoader.GetString(resKey)); } // Loader failed to load resource file, print the resource key instead. return Utility::ConvertToUTF8(resKey); } // Gets the string resource value or nothing if not present. std::optional TryResolveString(std::wstring_view resKey) const { if (!resKey.empty() && m_wingetLoader) { try { winrt::hstring result = m_wingetLoader.GetString(resKey); if (!result.empty()) { return Resource::LocString{ Utility::LocIndString{ Utility::ConvertToUTF8(result) } }; } } CATCH_LOG() } return {}; } private: winrt::Windows::ApplicationModel::Resources::ResourceLoader m_wingetLoader; Loader() : m_wingetLoader(nullptr) { try { // The default constructor of ResourceLoader throws a winrt::hresult_error exception // when resource.pri is not found. ResourceLoader::GetForViewIndependentUse also throws // a winrt::hresult_error but for reasons unknown it only gets caught when running on the // debugger. Running without a debugger will result in a crash that not even adding a // catch all will fix. To provide a good error message we call the default constructor // before calling GetForViewIndependentUse. m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader(); m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse(L"winget"); } catch (const winrt::hresult_error& hre) { // This message cannot be localized. AICLI_LOG(CLI, Error, << "Failure loading resource file with error: " << hre.code()); m_wingetLoader = nullptr; } } }; } namespace StringResource { std::string StringId::Resolve() const { return Resource::Loader::Instance().ResolveString(*this); } std::ostream& operator<<(std::ostream& out, StringId si) { return (out << Resource::LocString{ si }); } std::optional TryResolveString(std::wstring_view resKey) { return Resource::Loader::Instance().TryResolveString(resKey); } } } ================================================ FILE: src/AppInstallerSharedLib/Runtime.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "Public/winget/Runtime.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" namespace AppInstaller::Runtime { using namespace Utility; namespace { using namespace std::string_view_literals; constexpr std::string_view s_PreviewBuildSuffix = "-preview"sv; // Gets a boolean indicating whether the current process has identity. bool DoesCurrentProcessHaveIdentity() { UINT32 length = 0; LONG result = ::GetPackageFamilyName(GetCurrentProcess(), &length, nullptr); return (result != APPMODEL_ERROR_NO_PACKAGE); } std::unique_ptr GetPACKAGE_ID() { UINT32 bufferLength = 0; LONG gcpiResult = GetCurrentPackageId(&bufferLength, nullptr); THROW_HR_IF(E_UNEXPECTED, gcpiResult != ERROR_INSUFFICIENT_BUFFER); std::unique_ptr buffer = std::make_unique(bufferLength); gcpiResult = GetCurrentPackageId(&bufferLength, buffer.get()); if (FAILED_WIN32_LOG(gcpiResult)) { return {}; } return buffer; } // Gets the package name; only succeeds if running in a packaged context. std::string GetPackageName() { std::unique_ptr buffer = GetPACKAGE_ID(); if (!buffer) { return {}; } PACKAGE_ID* packageId = reinterpret_cast(buffer.get()); return Utility::ConvertToUTF8(packageId->name); } // Gets the package version; only succeeds if running in a packaged context. std::optional GetPACKAGE_VERSION() { std::unique_ptr buffer = GetPACKAGE_ID(); if (!buffer) { return {}; } PACKAGE_ID* packageId = reinterpret_cast(buffer.get()); return packageId->version; } } bool IsRunningInPackagedContext() { static bool result = DoesCurrentProcessHaveIdentity(); return result; } LocIndString GetClientVersion() { std::ostringstream strstr; strstr << VERSION_MAJOR << '.' << VERSION_MINOR << '.' << VERSION_BUILD; if (!IsReleaseBuild()) { strstr << s_PreviewBuildSuffix; } return LocIndString{ strstr.str() }; } std::wstring GetPackageFamilyName() { UINT32 length = 0; LONG returnValue = ::GetPackageFamilyName(GetCurrentProcess(), &length, nullptr); if (returnValue == APPMODEL_ERROR_NO_PACKAGE) { return {}; } if (returnValue != ERROR_INSUFFICIENT_BUFFER) { THROW_IF_WIN32_ERROR(returnValue); } std::wstring result(length, '\0'); returnValue = ::GetPackageFamilyName(GetCurrentProcess(), &length, &result[0]); THROW_IF_WIN32_ERROR(returnValue); THROW_HR_IF(E_UNEXPECTED, length == 0); result.resize(length - 1); return result; } LocIndString GetPackageVersion() { using namespace std::string_literals; if (IsRunningInPackagedContext()) { auto version = GetPACKAGE_VERSION(); if (!version) { // In the extremely unlikely event of a failure, this is merely a sentinel value // to indicated such. The only other option is to completely prevent execution, // which seems unnecessary. return LocIndString{ "error"sv }; } std::ostringstream strstr; strstr << GetPackageName() << " v" << version->Major << '.' << version->Minor << '.' << version->Build << '.' << version->Revision; return LocIndString{ strstr.str() }; } else { // Calling code should avoid calling in when this is the case. return LocIndString{ "none"sv }; } } LocIndString GetOSVersion() { winrt::Windows::System::Profile::AnalyticsInfo analyticsInfo{}; auto versionInfo = analyticsInfo.VersionInfo(); uint64_t version = std::stoull(Utility::ConvertToUTF8(versionInfo.DeviceFamilyVersion())); uint16_t parts[4]; for (size_t i = 0; i < ARRAYSIZE(parts); ++i) { parts[i] = version & 0xFFFF; version = version >> 16; } std::ostringstream strstr; strstr << Utility::ConvertToUTF8(versionInfo.DeviceFamily()) << " v" << parts[3] << '.' << parts[2] << '.' << parts[1] << '.' << parts[0]; return LocIndString{ strstr.str() }; } std::string GetOSRegion() { winrt::Windows::Globalization::GeographicRegion region; return Utility::ConvertToUTF8(region.CodeTwoLetter()); } bool IsCurrentOSVersionGreaterThanOrEqual(const Utility::Version& version) { DWORD versionParts[3] = {}; for (size_t i = 0; i < ARRAYSIZE(versionParts) && i < version.GetParts().size(); ++i) { versionParts[i] = static_cast(std::min(static_cast(std::numeric_limits::max()), version.GetParts()[i].Integer)); } OSVERSIONINFOEXW osVersionInfo{}; osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo); osVersionInfo.dwMajorVersion = versionParts[0]; osVersionInfo.dwMinorVersion = versionParts[1]; osVersionInfo.dwBuildNumber = versionParts[2]; osVersionInfo.wServicePackMajor = 0; osVersionInfo.wServicePackMinor = 0; DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; DWORDLONG conditions = 0; VER_SET_CONDITION(conditions, VER_MAJORVERSION, VER_GREATER_EQUAL); VER_SET_CONDITION(conditions, VER_MINORVERSION, VER_GREATER_EQUAL); VER_SET_CONDITION(conditions, VER_BUILDNUMBER, VER_GREATER_EQUAL); VER_SET_CONDITION(conditions, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); VER_SET_CONDITION(conditions, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); BOOL result = VerifyVersionInfoW(&osVersionInfo, mask, conditions); if (!result) { THROW_LAST_ERROR_IF(GetLastError() != ERROR_OLD_WIN_VERSION); } return !!result; } bool IsRunningAsAdmin() { return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); } bool IsRunningAsSystem() { return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID); } bool IsRunningAsAdminOrSystem() { return IsRunningAsAdmin() || IsRunningAsSystem(); } bool IsRunningWithLimitedToken() { return wil::get_token_information() == TokenElevationTypeLimited; } } ================================================ FILE: src/AppInstallerSharedLib/SHA256.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include #define WIN32_NO_STATUS #include #include "Public/AppInstallerSHA256.h" #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerStrings.h" namespace AppInstaller::Utility { struct SHA256Context { wil::unique_bcrypt_algorithm algHandle; wil::unique_bcrypt_hash hashHandle; DWORD hashLength = 0; }; SHA256::SHA256() : context(new SHA256Context{}) { BCRYPT_ALG_HANDLE algHandleT{}; BCRYPT_HASH_HANDLE hashHandleT; DWORD resultLength = 0; // Open an algorithm handle THROW_IF_NTSTATUS_FAILED_MSG(BCryptOpenAlgorithmProvider( &algHandleT, // Alg Handle pointer BCRYPT_SHA256_ALGORITHM, // Cryptographic Algorithm name (null terminated unicode string) nullptr, // Provider name; if null, the default provider is loaded 0), // Flags "failed opening SHA256 algorithm provider"); context->algHandle.reset(algHandleT); // Obtain the length of the hash THROW_IF_NTSTATUS_FAILED_MSG(BCryptGetProperty( context->algHandle.get(), // Handle to a CNG object BCRYPT_HASH_LENGTH, // Property name (null terminated unicode string) (PBYTE) & (context->hashLength), // Address of the output buffer which receives the property value sizeof(context->hashLength), // Size of the buffer in bytes &resultLength, // Number of bytes that were copied into the buffer 0), // Flags "failed getting SHA256 hash length"); if (resultLength != sizeof(context->hashLength)) { THROW_HR_MSG(E_UNEXPECTED, "failed getting SHA256 hash length"); } // Create a hash handle THROW_IF_NTSTATUS_FAILED_MSG(BCryptCreateHash( context->algHandle.get(), // Handle to an algorithm provider &hashHandleT, // A pointer to a hash handle - can be a hash or hmac object nullptr, // Pointer to the buffer that receives the hash/hmac object 0, // Size of the buffer in bytes nullptr, // A pointer to a key to use for the hash or MAC 0, // Size of the key in bytes 0), // Flags "failed creating SHA256 hash object"); context->hashHandle.reset(hashHandleT); } void SHA256::Add(const uint8_t* buffer, size_t cbBuffer) { EnsureNotFinished(); // Add the data THROW_IF_NTSTATUS_FAILED_MSG( BCryptHashData(context->hashHandle.get(), const_cast(buffer), static_cast(cbBuffer), 0), "failed adding SHA256 data"); } void SHA256::Get(HashBuffer& hash) { EnsureNotFinished(); // Size the hash buffer appropriately hash.resize(context->hashLength); // Obtain the hash of the message(s) into the hash buffer THROW_IF_NTSTATUS_FAILED_MSG(BCryptFinishHash( context->hashHandle.get(), // Handle to the hash or MAC object hash.data(), // A pointer to a buffer that receives the hash or MAC value context->hashLength, // Size of the buffer in bytes 0), // Flags "failed getting SHA256 hash"); context.reset(); } std::string SHA256::ConvertToString(const HashBuffer& hashBuffer) { return Utility::ConvertToHexString(hashBuffer, HashBufferSizeInBytes); } std::wstring SHA256::ConvertToWideString(const HashBuffer& hashBuffer) { return ConvertToUTF16(SHA256::ConvertToString(hashBuffer)); } SHA256::HashBuffer SHA256::ConvertToBytes(const std::string& hashStr) { return Utility::ParseFromHexString(hashStr, HashBufferSizeInBytes); } SHA256::HashBuffer SHA256::ConvertToBytes(const std::wstring& hashStr) { return Utility::ParseFromHexString(Utility::ConvertToUTF8(hashStr), HashBufferSizeInBytes); } SHA256::HashBuffer SHA256::ComputeHash(const std::uint8_t* buffer, std::uint32_t cbBuffer) { SHA256 hasher; hasher.Add(buffer, cbBuffer); return hasher.Get(); } SHA256::HashBuffer SHA256::ComputeHash(const std::vector& buffer) { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), buffer.size() > std::numeric_limits::max()); return ComputeHash(buffer.data(), static_cast(buffer.size())); } SHA256::HashBuffer SHA256::ComputeHash(std::string_view buffer) { return ComputeHash(reinterpret_cast(buffer.data()), static_cast(buffer.size())); } SHA256::HashBuffer SHA256::ComputeHash(std::istream& in) { return ComputeHashDetails(in).Hash; } SHA256::HashDetails SHA256::ComputeHashDetails(std::istream& in) { // Throw exceptions on badbit auto excState = in.exceptions(); auto revertExcState = wil::scope_exit([excState, &in]() { in.exceptions(excState); }); in.exceptions(std::ios_base::badbit); const int bufferSize = 1024 * 1024; // 1MB auto buffer = std::make_unique(bufferSize); SHA256 hasher; uint64_t totalSize = 0; while (in.good()) { in.read((char*)(buffer.get()), bufferSize); std::streamsize bytesRead = in.gcount(); if (bytesRead) { hasher.Add(buffer.get(), static_cast(bytesRead)); totalSize += static_cast(bytesRead); } } if (in.eof()) { HashDetails result; result.Hash = hasher.Get(); result.SizeInBytes = totalSize; return result; } else { THROW_HR(APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE); } } SHA256::HashBuffer SHA256::ComputeHashFromFile(const std::filesystem::path& path) { std::ifstream inStream{ path, std::ifstream::binary }; const Utility::SHA256::HashBuffer& targetFileHash = Utility::SHA256::ComputeHash(inStream); inStream.close(); return targetFileHash; } void SHA256::SHA256ContextDeleter::operator()(SHA256Context* context) { delete context; } bool SHA256::AreEqual(const HashBuffer& first, const HashBuffer& second) { return (first.size() == second.size() && std::equal(first.begin(), first.end(), second.begin())); } void SHA256::EnsureNotFinished() const { if (!context) { THROW_HR_MSG(E_UNEXPECTED, "The hash is already finished"); } } } ================================================ FILE: src/AppInstallerSharedLib/SQLiteDynamicStorage.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/SQLiteDynamicStorage.h" namespace AppInstaller::SQLite { SQLiteDynamicStorage::SQLiteDynamicStorage(const std::string& target, const Version& version) : SQLiteStorageBase(target, version) { version.SetSchemaVersion(m_dbconn); } SQLiteDynamicStorage::SQLiteDynamicStorage(const std::filesystem::path& target, const Version& version) : SQLiteDynamicStorage(target.u8string(), version) {} SQLiteDynamicStorage::SQLiteDynamicStorage( const std::string& filePath, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& file) : SQLiteStorageBase(filePath, disposition, std::move(file)) {} SQLiteDynamicStorage::SQLiteDynamicStorage( const std::filesystem::path& filePath, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& file) : SQLiteDynamicStorage(filePath.u8string(), disposition, std::move(file)) {} SQLiteDynamicStorage::operator Connection& () { return m_dbconn; } SQLiteDynamicStorage::operator const Connection& () const { return m_dbconn; } Connection& SQLiteDynamicStorage::GetConnection() { return m_dbconn; } const Connection& SQLiteDynamicStorage::GetConnection() const { return m_dbconn; } _Acquires_lock_(mutex) SQLiteDynamicStorage::TransactionLock::TransactionLock(std::mutex& mutex) : m_lock(mutex) { } _Acquires_lock_(mutex) SQLiteDynamicStorage::TransactionLock::TransactionLock(std::mutex& mutex, Connection& connection, std::string_view name, bool immediateWrite) : m_lock(mutex) { m_transaction = Transaction::Create(connection, std::string{ name }, immediateWrite); } void SQLiteDynamicStorage::TransactionLock::Rollback(bool throwOnError) { m_transaction.Rollback(throwOnError); } void SQLiteDynamicStorage::TransactionLock::Commit() { m_transaction.Commit(); } std::unique_ptr SQLiteDynamicStorage::TryBeginTransaction(std::string_view name, bool immediateWrite) { auto result = std::make_unique(*m_interfaceLock, m_dbconn, name, immediateWrite); Version currentVersion = Version::GetSchemaVersion(m_dbconn); if (currentVersion != m_version) { m_version = currentVersion; result.reset(); } return result; } std::unique_ptr SQLiteDynamicStorage::LockConnection() { return std::make_unique(*m_interfaceLock); } } ================================================ FILE: src/AppInstallerSharedLib/SQLiteMetadataTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/SQLiteMetadataTable.h" using namespace std::literals; namespace AppInstaller::SQLite { // Table data [note that this table is not versioned, and thus *cannot change*] static constexpr std::string_view s_MetadataTable_Table_Name = "metadata"sv; static constexpr std::string_view s_MetadataTable_Column_Name = "name"sv; static constexpr std::string_view s_MetadataTable_Column_Value = "value"sv; static constexpr std::string_view s_MetadataTable_Table_Create = R"( CREATE TABLE [metadata]( [name] TEXT PRIMARY KEY NOT NULL, [value] TEXT NOT NULL) WITHOUT ROWID )"sv; // Statements static constexpr std::string_view s_MetadataTableStmt_GetNamedValue = "select [value] from [metadata] where [name] = ?"sv; static constexpr std::string_view s_MetadataTableStmt_SetNamedValue = "insert or replace into [metadata] ([name], [value]) values (?, ?)"sv; void MetadataTable::Create(Connection& connection) { Statement create = Statement::Create(connection, s_MetadataTable_Table_Create); create.Execute(); } Statement MetadataTable::GetNamedValueStatement(const Connection& connection, std::string_view name) { std::optional result = TryGetNamedValueStatement(connection, name); THROW_HR_IF(E_NOT_SET, !result); return std::move(result).value(); } std::optional MetadataTable::TryGetNamedValueStatement(const Connection& connection, std::string_view name) { THROW_HR_IF(E_INVALIDARG, name.empty()); Statement result = Statement::Create(connection, s_MetadataTableStmt_GetNamedValue); result.Bind(1, name); if (result.Step()) { return result; } else { return std::nullopt; } } Statement MetadataTable::SetNamedValueStatement(const Connection& connection, std::string_view name) { THROW_HR_IF(E_INVALIDARG, name.empty()); Statement result = Statement::Create(connection, s_MetadataTableStmt_SetNamedValue); result.Bind(1, name); return result; } } ================================================ FILE: src/AppInstallerSharedLib/SQLiteStatementBuilder.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/SQLiteStatementBuilder.h" namespace AppInstaller::SQLite::Builder { std::ostream& operator<<(std::ostream& out, const QualifiedColumn& column) { if (!column.Table.empty()) { out << '[' << column.Table << "]."; } out << '[' << column.Column << ']'; return out; } std::ostream& operator<<(std::ostream& out, const QualifiedTable& table) { if (!table.Schema.empty()) { out << '[' << table.Schema << "]."; } out << '[' << table.Table << ']'; return out; } std::ostream& operator<<(std::ostream& out, const details::SubBuilder& column) { out << column.GetString(); return out; } namespace { void OutputColumns(std::ostream& out, std::string_view op, std::string_view column) { out << op << '[' << column << ']'; } void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list columns) { out << op; bool isFirst = true; for (const auto& c : columns) { out << (isFirst ? "[" : ", [") << c << ']'; isFirst = false; } } void OutputColumns(std::ostream& out, std::string_view op, const QualifiedColumn& column) { out << op << column; } void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list columns) { out << op; bool isFirst = true; for (const auto& c : columns) { out << (isFirst ? "" : ", ") << c; isFirst = false; } } void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list columns) { out << op; bool isFirst = true; for (const auto& c : columns) { out << (isFirst ? "" : ", ") << c; isFirst = false; } } void OutputAggregate(std::ostream& out, Aggregate op) { out << ' '; switch (op) { case Aggregate::Min: out << "MIN"; break; case Aggregate::Max: out << "MAX"; break; default: THROW_HR(E_UNEXPECTED); } } void OutputColumns(std::ostream& out, Aggregate op, std::string_view column) { OutputAggregate(out, op); out << "([" << column << "])"; } void OutputColumns(std::ostream& out, Aggregate op, const QualifiedColumn& column) { OutputAggregate(out, op); out << '(' << column << ')'; } // Use to output operation and table name, such as " FROM [table]" void OutputOperationAndTable(std::ostream& out, std::string_view op, std::string_view table) { out << op << " [" << table << ']'; } void OutputOperationAndTable(std::ostream& out, std::string_view op, QualifiedTable table) { out << op << table; } void OutputOperationAndTable(std::ostream& out, std::string_view op, std::initializer_list table) { out << op << " ["; for (std::string_view t : table) { out << t; } out << ']'; } void OutputType(std::ostream& out, Type type) { out << ' '; switch (type) { case Type::Int: out << "INT"; break; case Type::Int64: out << "INT64"; break; case Type::Text: out << "TEXT"; break; case Type::Blob: out << "BLOB"; break; case Type::Integer: out << "INTEGER"; break; case Type::None: break; default: THROW_HR(E_UNEXPECTED); } } } IntegerPrimaryKey::IntegerPrimaryKey() { m_stream << SQLite::RowIDName << " INTEGER PRIMARY KEY"; } IntegerPrimaryKey& IntegerPrimaryKey::AutoIncrement(bool isTrue) { if (isTrue) { m_stream << " AUTOINCREMENT"; } return *this; } ColumnBuilder::ColumnBuilder(std::string_view column, Type type) { OutputColumns(m_stream, "", column); OutputType(m_stream, type); } ColumnBuilder& ColumnBuilder::NotNull(bool isTrue) { if (isTrue) { m_stream << " NOT NULL"; } return *this; } ColumnBuilder& ColumnBuilder::CollateNoCase(bool isTrue) { if (isTrue) { m_stream << " COLLATE NOCASE"; } return *this; } ColumnBuilder& ColumnBuilder::Default(int64_t value) { m_stream << " DEFAULT " << value; return *this; } ColumnBuilder& ColumnBuilder::Unique(bool isTrue) { if (isTrue) { m_stream << " UNIQUE"; } return *this; } ColumnBuilder& ColumnBuilder::PrimaryKey(bool isTrue) { if (isTrue) { m_stream << " PRIMARY KEY"; } return *this; } PrimaryKeyBuilder::PrimaryKeyBuilder(std::initializer_list columns) { OutputColumns(m_stream, "PRIMARY KEY(", columns); m_stream << ')'; m_needsClosing = false; } PrimaryKeyBuilder::PrimaryKeyBuilder() { m_stream << "PRIMARY KEY("; } PrimaryKeyBuilder& PrimaryKeyBuilder::Column(std::string_view column) { if (m_isFirst) { m_isFirst = false; } else { m_stream << ", "; } OutputColumns(m_stream, "", column); return *this; } PrimaryKeyBuilder::operator details::SubBuilder() { if (m_needsClosing) { m_stream << ')'; m_needsClosing = false; } return { m_stream.str() }; } StatementBuilder& StatementBuilder::Select() { m_stream << "SELECT "; m_needsComma = false; return *this; } StatementBuilder& StatementBuilder::Select(std::string_view column) { OutputColumns(m_stream, "SELECT ", column); return *this; } StatementBuilder& StatementBuilder::Select(std::initializer_list columns) { OutputColumns(m_stream, "SELECT ", columns); return *this; } StatementBuilder& StatementBuilder::Select(const QualifiedColumn& column) { OutputColumns(m_stream, "SELECT ", column); return *this; } StatementBuilder& StatementBuilder::Select(std::initializer_list columns) { OutputColumns(m_stream, "SELECT ", columns); return *this; } StatementBuilder& StatementBuilder::Select(details::rowcount_t) { m_stream << "SELECT COUNT(*)"; return *this; } StatementBuilder& StatementBuilder::From() { m_stream << " FROM "; return *this; } StatementBuilder& StatementBuilder::From(std::string_view table) { OutputOperationAndTable(m_stream, " FROM", table); return *this; } StatementBuilder& StatementBuilder::From(QualifiedTable table) { OutputOperationAndTable(m_stream, " FROM", table); return *this; } StatementBuilder& StatementBuilder::From(std::initializer_list table) { OutputOperationAndTable(m_stream, " FROM", table); return *this; } StatementBuilder& StatementBuilder::Where(std::string_view column) { OutputColumns(m_stream, " WHERE ", column); return *this; } StatementBuilder& StatementBuilder::Where(const QualifiedColumn& column) { OutputColumns(m_stream, " WHERE ", column); return *this; } StatementBuilder& StatementBuilder::WhereValueContainsEmbeddedNullCharacter(std::string_view column) { OutputColumns(m_stream, " WHERE instr(", column); m_stream << ",char(0))>0"; return *this; } StatementBuilder& StatementBuilder::WhereValueContainsEmbeddedNullCharacter(const QualifiedColumn& column) { OutputColumns(m_stream, " WHERE instr(", column); m_stream << ",char(0))>0"; return *this; } StatementBuilder& StatementBuilder::Equals(details::unbound_t, std::optional index) { AppendOpAndBinder(Op::Equals, index); return *this; } StatementBuilder& StatementBuilder::Equals(std::nullptr_t) { // This is almost certainly not what you want. // In SQL, value = NULL is always false. // Use StatementBuilder::IsNull instead. THROW_HR(E_NOTIMPL); } StatementBuilder& StatementBuilder::Equals() { m_stream << " ="; return *this; } StatementBuilder& StatementBuilder::Equals(const QualifiedColumn& column) { OutputColumns(m_stream, " = ", column); return *this; } StatementBuilder& StatementBuilder::IsGreaterThan(details::unbound_t, std::optional index) { AppendOpAndBinder(Op::GreaterThan, index); return *this; } StatementBuilder& StatementBuilder::IsGreaterThanOrEqualTo(details::unbound_t, std::optional index) { AppendOpAndBinder(Op::GreaterThanOrEqualTo, index); return *this; } StatementBuilder& StatementBuilder::LikeWithEscape(std::string_view value) { AddBindFunctor(AppendOpAndBinder(Op::Like), EscapeStringForLike(value)); return Escape(EscapeCharForLike); } StatementBuilder& StatementBuilder::Like(details::unbound_t) { AppendOpAndBinder(Op::Like); return *this; } StatementBuilder& StatementBuilder::Escape(std::string_view escapeChar) { THROW_HR_IF(E_INVALIDARG, escapeChar.length() != 1); AddBindFunctor(AppendOpAndBinder(Op::Escape), escapeChar); return *this; } StatementBuilder& StatementBuilder::Not() { m_stream << " NOT"; return *this; } StatementBuilder& StatementBuilder::In() { m_stream << " IN"; return *this; } StatementBuilder& StatementBuilder::In(size_t count) { m_stream << " IN ("; for (size_t i = 0; i < count; ++i) { m_stream << (i == 0 ? "?" : ", ?"); } m_stream << ')'; m_bindIndex += static_cast(count); return *this; } StatementBuilder& StatementBuilder::IsNull(bool isNull) { m_stream << " IS " << (isNull ? "" : "NOT ") << "NULL"; return *this; } StatementBuilder& StatementBuilder::And(std::string_view column) { OutputColumns(m_stream, " AND ", column); return *this; } StatementBuilder& StatementBuilder::And(const QualifiedColumn& column) { OutputColumns(m_stream, " AND ", column); return *this; } StatementBuilder& StatementBuilder::Or(const QualifiedColumn& column) { OutputColumns(m_stream, " OR ", column); return *this; } StatementBuilder& StatementBuilder::Join(std::string_view table) { OutputOperationAndTable(m_stream, " JOIN", table); return *this; } StatementBuilder& StatementBuilder::Join(QualifiedTable table) { OutputOperationAndTable(m_stream, " JOIN", table); return *this; } StatementBuilder& StatementBuilder::Join(std::initializer_list table) { OutputOperationAndTable(m_stream, " JOIN", table); return *this; } StatementBuilder& StatementBuilder::LeftOuterJoin(std::string_view table) { OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); return *this; } StatementBuilder& StatementBuilder::LeftOuterJoin(QualifiedTable table) { OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); return *this; } StatementBuilder& StatementBuilder::LeftOuterJoin(std::initializer_list table) { OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); return *this; } StatementBuilder& StatementBuilder::On(const QualifiedColumn& column1, const QualifiedColumn& column2) { m_stream << " ON " << column1 << " = " << column2; return *this; } StatementBuilder& StatementBuilder::Limit(size_t rowCount) { m_stream << " LIMIT " << rowCount; return *this; } StatementBuilder& StatementBuilder::GroupBy(std::string_view column) { OutputColumns(m_stream, " GROUP BY ", column); return *this; } StatementBuilder& StatementBuilder::GroupBy(const QualifiedColumn& column) { OutputColumns(m_stream, " GROUP BY ", column); return *this; } StatementBuilder& StatementBuilder::OrderBy(std::string_view column) { OutputColumns(m_stream, " ORDER BY ", column); return *this; } StatementBuilder& StatementBuilder::OrderBy(const QualifiedColumn& column) { OutputColumns(m_stream, " ORDER BY ", column); return *this; } StatementBuilder& StatementBuilder::OrderBy(std::initializer_list columns) { OutputColumns(m_stream, " ORDER BY ", columns); return *this; } StatementBuilder& StatementBuilder::Ascending() { m_stream << " ASC"; return *this; } StatementBuilder& StatementBuilder::Descending() { m_stream << " DESC"; return *this; } StatementBuilder& StatementBuilder::InsertInto(std::string_view table) { OutputOperationAndTable(m_stream, "INSERT INTO", table); return *this; } StatementBuilder& StatementBuilder::InsertInto(QualifiedTable table) { OutputOperationAndTable(m_stream, "INSERT INTO", table); return *this; } StatementBuilder& StatementBuilder::InsertInto(std::initializer_list table) { OutputOperationAndTable(m_stream, "INSERT INTO", table); return *this; } StatementBuilder& StatementBuilder::InsertOrIgnore(std::string_view table) { OutputOperationAndTable(m_stream, "INSERT OR IGNORE INTO", table); return *this; } StatementBuilder& StatementBuilder::InsertOrIgnore(QualifiedTable table) { OutputOperationAndTable(m_stream, "INSERT OR IGNORE INTO", table); return *this; } StatementBuilder& StatementBuilder::InsertOrIgnore(std::initializer_list table) { OutputOperationAndTable(m_stream, "INSERT OR IGNORE INTO", table); return *this; } StatementBuilder& StatementBuilder::Columns(std::string_view column) { OutputColumns(m_stream, "(", column); m_stream << ')'; return *this; } StatementBuilder& StatementBuilder::Columns(std::initializer_list columns) { OutputColumns(m_stream, "(", columns); m_stream << ')'; return *this; } StatementBuilder& StatementBuilder::Columns(const QualifiedColumn& column) { OutputColumns(m_stream, "(", column); m_stream << ')'; return *this; } StatementBuilder& StatementBuilder::Columns(std::initializer_list columns) { OutputColumns(m_stream, "(", columns); m_stream << ')'; return *this; } StatementBuilder& StatementBuilder::Columns(std::initializer_list columns) { OutputColumns(m_stream, "(", columns); m_stream << ')'; return *this; } StatementBuilder& StatementBuilder::BeginColumns() { m_stream << '('; m_needsComma = false; return *this; } StatementBuilder& StatementBuilder::Column(std::string_view column) { if (m_needsComma) { m_stream << ", "; } OutputColumns(m_stream, "", column); m_needsComma = true; return *this; } StatementBuilder& StatementBuilder::Column(const QualifiedColumn& column) { if (m_needsComma) { m_stream << ", "; } OutputColumns(m_stream, "", column); m_needsComma = true; return *this; } StatementBuilder& StatementBuilder::Column(Aggregate aggOp, std::string_view column) { if (m_needsComma) { m_stream << ", "; } OutputColumns(m_stream, aggOp, column); m_needsComma = true; return *this; } StatementBuilder& StatementBuilder::Column(Aggregate aggOp, const QualifiedColumn& column) { if (m_needsComma) { m_stream << ", "; } OutputColumns(m_stream, aggOp, column); m_needsComma = true; return *this; } StatementBuilder& StatementBuilder::Column(const details::SubBuilder& column) { if (m_needsComma) { m_stream << ", "; } m_stream << column; m_needsComma = true; return *this; } StatementBuilder& StatementBuilder::EndColumns() { m_stream << ')'; m_needsComma = false; return *this; } StatementBuilder& StatementBuilder::NotNull(bool isTrue) { if (isTrue) { m_stream << " NOT NULL"; } return *this; } StatementBuilder& StatementBuilder::BeginValues() { m_stream << " VALUES ("; m_needsComma = false; return *this; } StatementBuilder& StatementBuilder::EndValues() { m_stream << ')'; m_needsComma = false; return *this; } StatementBuilder& StatementBuilder::CreateTable(std::string_view table) { OutputOperationAndTable(m_stream, "CREATE TABLE", table); return *this; } StatementBuilder& StatementBuilder::CreateTable(QualifiedTable table) { OutputOperationAndTable(m_stream, "CREATE TABLE", table); return *this; } StatementBuilder& StatementBuilder::CreateTable(std::initializer_list table) { OutputOperationAndTable(m_stream, "CREATE TABLE", table); return *this; } StatementBuilder& StatementBuilder::AlterTable(std::string_view table) { OutputOperationAndTable(m_stream, "ALTER TABLE", table); return *this; } StatementBuilder& StatementBuilder::AlterTable(QualifiedTable table) { OutputOperationAndTable(m_stream, "ALTER TABLE", table); return *this; } StatementBuilder& StatementBuilder::AlterTable(std::initializer_list table) { OutputOperationAndTable(m_stream, "ALTER TABLE", table); return *this; } StatementBuilder& StatementBuilder::Add(std::string_view column, Type type) { m_stream << " ADD " << column; OutputType(m_stream, type); return *this; } StatementBuilder& StatementBuilder::DropTable(std::string_view table) { OutputOperationAndTable(m_stream, "DROP TABLE", table); return *this; } StatementBuilder& StatementBuilder::DropTable(QualifiedTable table) { OutputOperationAndTable(m_stream, "DROP TABLE", table); return *this; } StatementBuilder& StatementBuilder::DropTable(std::initializer_list table) { OutputOperationAndTable(m_stream, "DROP TABLE", table); return *this; } StatementBuilder& StatementBuilder::DropTableIfExists(std::string_view table) { OutputOperationAndTable(m_stream, "DROP TABLE IF EXISTS", table); return *this; } StatementBuilder& StatementBuilder::DropTableIfExists(QualifiedTable table) { OutputOperationAndTable(m_stream, "DROP TABLE IF EXISTS", table); return *this; } StatementBuilder& StatementBuilder::DropTableIfExists(std::initializer_list table) { OutputOperationAndTable(m_stream, "DROP TABLE IF EXISTS", table); return *this; } StatementBuilder& StatementBuilder::CreateIndex(std::string_view table) { OutputOperationAndTable(m_stream, "CREATE INDEX", table); return *this; } StatementBuilder& StatementBuilder::CreateIndex(QualifiedTable table) { OutputOperationAndTable(m_stream, "CREATE INDEX", table); return *this; } StatementBuilder& StatementBuilder::CreateIndex(std::initializer_list table) { OutputOperationAndTable(m_stream, "CREATE INDEX", table); return *this; } StatementBuilder& StatementBuilder::CreateUniqueIndex(std::string_view table) { OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); return *this; } StatementBuilder& StatementBuilder::CreateUniqueIndex(QualifiedTable table) { OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); return *this; } StatementBuilder& StatementBuilder::CreateUniqueIndex(std::initializer_list table) { OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); return *this; } StatementBuilder& StatementBuilder::DropIndex(std::string_view index) { OutputOperationAndTable(m_stream, "DROP INDEX", index); return *this; } StatementBuilder& StatementBuilder::DropIndex(QualifiedTable index) { OutputOperationAndTable(m_stream, "DROP INDEX", index); return *this; } StatementBuilder& StatementBuilder::DropIndex(std::initializer_list index) { OutputOperationAndTable(m_stream, "DROP INDEX", index); return *this; } StatementBuilder& StatementBuilder::On(std::string_view table) { OutputOperationAndTable(m_stream, " ON", table); return *this; } StatementBuilder& StatementBuilder::On(std::initializer_list table) { OutputOperationAndTable(m_stream, " ON", table); return *this; } StatementBuilder& StatementBuilder::DeleteFrom(std::string_view table) { OutputOperationAndTable(m_stream, "DELETE FROM", table); return *this; } StatementBuilder& StatementBuilder::DeleteFrom(QualifiedTable table) { OutputOperationAndTable(m_stream, "DELETE FROM", table); return *this; } StatementBuilder& StatementBuilder::DeleteFrom(std::initializer_list table) { OutputOperationAndTable(m_stream, "DELETE FROM", table); return *this; } StatementBuilder& StatementBuilder::Update(std::string_view table) { OutputOperationAndTable(m_stream, "UPDATE", table); return *this; } StatementBuilder& StatementBuilder::Update(QualifiedTable table) { OutputOperationAndTable(m_stream, "UPDATE", table); return *this; } StatementBuilder& StatementBuilder::Update(std::initializer_list table) { OutputOperationAndTable(m_stream, "UPDATE", table); return *this; } StatementBuilder& StatementBuilder::UpdateOrReplace(std::string_view table) { OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); return *this; } StatementBuilder& StatementBuilder::UpdateOrReplace(QualifiedTable table) { OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); return *this; } StatementBuilder& StatementBuilder::UpdateOrReplace(std::initializer_list table) { OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); return *this; } StatementBuilder& StatementBuilder::Set() { m_stream << " SET "; m_needsComma = false; return *this; } StatementBuilder& StatementBuilder::Vacuum() { m_stream << "VACUUM"; return *this; } StatementBuilder& StatementBuilder::BeginParenthetical() { m_stream << '('; return *this; } StatementBuilder& StatementBuilder::EndParenthetical() { m_stream << ')'; return *this; } StatementBuilder& StatementBuilder::WithoutRowID() { m_stream << " WITHOUT ROWID"; return *this; } StatementBuilder& StatementBuilder::As(std::string_view alias) { OutputOperationAndTable(m_stream, " AS", alias); return *this; } Statement StatementBuilder::Prepare(const Connection& connection) { Statement result = Statement::Create(connection, m_stream.str()); for (const auto& f : m_binders) { f(result); } return result; } void StatementBuilder::Execute(const Connection& connection) { Prepare(connection).Execute(); } int StatementBuilder::AppendOpAndBinder(Op op, std::optional index) { switch (op) { case Op::Equals: m_stream << " = ?"; break; case Op::Like: m_stream << " LIKE ?"; break; case Op::Escape: m_stream << " ESCAPE ?"; break; case Op::Literal: m_stream << " ?"; break; case Op::GreaterThan: m_stream << " > ?"; break; case Op::GreaterThanOrEqualTo: m_stream << " >= ?"; break; default: THROW_HR(E_UNEXPECTED); } if (index) { m_stream << index.value(); } return m_bindIndex++; } int StatementBuilder::AppendValuesAndBinders(size_t count) { m_stream << " VALUES ("; for (size_t i = 0; i < count; ++i) { m_stream << (i == 0 ? "?" : ", ?"); } m_stream << ')'; int result = m_bindIndex; m_bindIndex += static_cast(count); return result; } int StatementBuilder::AppendValueAndBinder() { if (m_needsComma) { m_stream << ", "; } m_stream << '?'; m_needsComma = true; return m_bindIndex++; } } ================================================ FILE: src/AppInstallerSharedLib/SQLiteStorageBase.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/SQLiteStorageBase.h" #include "Public/winget/SQLiteMetadataTable.h" #include "AppInstallerDateTime.h" namespace AppInstaller::SQLite { namespace { static char const* const GetOpenDispositionString(SQLiteStorageBase::OpenDisposition disposition) { switch (disposition) { case SQLiteStorageBase::OpenDisposition::Read: return "Read"; case SQLiteStorageBase::OpenDisposition::ReadWrite: return "ReadWrite"; case SQLiteStorageBase::OpenDisposition::Immutable: return "ImmutableRead"; default: return "Unknown"; } } std::filesystem::path AddSuffix(const std::filesystem::path& source, std::wstring_view suffix) { std::filesystem::path result{ source }; if (!suffix.empty()) { std::wstring filename = result.filename().wstring(); filename += suffix; result.replace_filename(std::move(filename)); } return result; } } // One method for converting open disposition to proper open disposition // another method for obtaining the right flags void SQLiteStorageBase::SetLastWriteTime() { MetadataTable::SetNamedValue(m_dbconn, s_MetadataValueName_LastWriteTime, Utility::GetCurrentUnixEpoch()); } // Recording last write time based on MSDN documentation stating that time returns a POSIX epoch time and thus // should be consistent across systems. std::chrono::system_clock::time_point SQLiteStorageBase::GetLastWriteTime() const { int64_t lastWriteTime = MetadataTable::GetNamedValue(m_dbconn, s_MetadataValueName_LastWriteTime); return Utility::ConvertUnixEpochToSystemClock(lastWriteTime); } std::string SQLiteStorageBase::GetDatabaseIdentifier() const { return MetadataTable::TryGetNamedValue(m_dbconn, s_MetadataValueName_DatabaseIdentifier).value_or(std::string{}); } void SQLiteStorageBase::RenameSQLiteDatabase(const std::filesystem::path& source, const std::filesystem::path& destination, bool overwrite) { auto fileSuffixes = { L"", L"-journal", L"-wal" }; THROW_WIN32_IF(ERROR_FILE_NOT_FOUND, !std::filesystem::exists(source)); THROW_WIN32_IF(ERROR_DIRECTORY, std::filesystem::is_directory(source)); if (overwrite) { for (const auto& suffix : fileSuffixes) { std::filesystem::path target = AddSuffix(destination, suffix); if (std::filesystem::exists(target)) { std::filesystem::remove_all(target); } } } for (const auto& suffix : fileSuffixes) { std::filesystem::path target = AddSuffix(source, suffix); if (std::filesystem::exists(target)) { std::filesystem::rename(target, AddSuffix(destination, suffix)); } } } SQLiteStorageBase::SQLiteStorageBase(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& file) : m_indexFile(std::move(file)) { AICLI_LOG(Repo, Info, << "Opening database for " << GetOpenDispositionString(disposition) << " at '" << filePath << "'"); switch (disposition) { case OpenDisposition::Read: m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::None); break; case OpenDisposition::ReadWrite: m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadWrite, SQLite::Connection::OpenFlags::None); break; case OpenDisposition::Immutable: { // Following the algorithm set forth at https://sqlite.org/uri.html [3.1] to convert to a URI path // The execution order builds out the string so that it shouldn't require any moves (other than growing) std::string target; // Add an 'arbitrary' growth size to prevent the majority of needing to grow (adding 'file:/' and '?immutable=1') target.reserve(filePath.size() + 20); target += "file:"; bool wasLastCharSlash = false; if (filePath.size() >= 2 && filePath[1] == ':' && ((filePath[0] >= 'a' && filePath[0] <= 'z') || (filePath[0] >= 'A' && filePath[0] <= 'Z'))) { target += '/'; wasLastCharSlash = true; } for (char c : filePath) { bool wasThisCharSlash = false; switch (c) { case '?': target += "%3f"; break; case '#': target += "%23"; break; case '\\': case '/': { wasThisCharSlash = true; if (!wasLastCharSlash) { target += '/'; } break; } default: target += c; break; } wasLastCharSlash = wasThisCharSlash; } target += "?immutable=1"; m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::Uri); break; } default: THROW_HR(E_UNEXPECTED); } m_version = Version::GetSchemaVersion(m_dbconn); } SQLiteStorageBase::SQLiteStorageBase(const std::string& target, const Version& version, size_t pageSize) : m_dbconn(SQLite::Connection::Create(target, SQLite::Connection::OpenDisposition::Create)) { m_version = version; if (pageSize > 0) { m_dbconn.SetPageSize(pageSize); } MetadataTable::Create(m_dbconn); // Write a new identifier for this database GUID databaseIdentifier; THROW_IF_FAILED(CoCreateGuid(&databaseIdentifier)); std::ostringstream stream; stream << databaseIdentifier; MetadataTable::SetNamedValue(m_dbconn, s_MetadataValueName_DatabaseIdentifier, stream.str()); } SQLiteStorageBase::SQLiteStorageBase(const std::string& target, SQLiteStorageBase& source) : m_dbconn(SQLite::Connection::Create(target, SQLite::Connection::OpenDisposition::Create)), m_version(source.m_version) { std::string mainDatabase = "main"; Backup backup = Backup::Create(m_dbconn, mainDatabase, source.m_dbconn, mainDatabase); backup.Step(); } } ================================================ FILE: src/AppInstallerSharedLib/SQLiteTempTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/SQLiteTempTable.h" #include "AppInstallerStrings.h" namespace AppInstaller::SQLite { using namespace std::string_view_literals; TempTable::TempTable() { GUID tempName; THROW_IF_FAILED(CoCreateGuid(&tempName)); wchar_t guidAsString[MAX_PATH]; THROW_HR_IF(E_UNEXPECTED, StringFromGUID2(tempName, guidAsString, MAX_PATH) == 0); m_name = Utility::ConvertToUTF8(guidAsString); } TempTable::~TempTable() { if (m_dropTableStatement) { m_dropTableStatement.Execute(); } } Builder::QualifiedTable TempTable::GetQualifiedName() const { return Builder::QualifiedTable("temp"sv, m_name); } void TempTable::InitDropStatement(const Connection& connection) { Builder::StatementBuilder builder; builder.DropTable(m_name); m_dropTableStatement = builder.Prepare(connection); } } ================================================ FILE: src/AppInstallerSharedLib/SQLiteVersion.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/SQLiteVersion.h" #include "Public/winget/SQLiteMetadataTable.h" #include namespace AppInstaller::SQLite { Version Version::GetSchemaVersion(Connection& connection) { int major = MetadataTable::GetNamedValue(connection, s_MetadataValueName_MajorVersion); int minor = MetadataTable::GetNamedValue(connection, s_MetadataValueName_MinorVersion); return { static_cast(major), static_cast(minor) }; } void Version::SetSchemaVersion(Connection& connection) const { Savepoint savepoint = Savepoint::Create(connection, "version_setschemaversion"); MetadataTable::SetNamedValue(connection, s_MetadataValueName_MajorVersion, static_cast(MajorVersion)); MetadataTable::SetNamedValue(connection, s_MetadataValueName_MinorVersion, static_cast(MinorVersion)); savepoint.Commit(); } std::ostream& operator<<(std::ostream& out, const Version& version) { if (version.IsLatest()) { return out << "Latest"; } else if (version.IsLatestForMajor(version.MajorVersion)) { return out << version.MajorVersion << ".Latest"; } else { return out << version.MajorVersion << '.' << version.MinorVersion; } } Version Version::Latest() { return { std::numeric_limits::max(), std::numeric_limits::max() }; } Version Version::LatestForMajor(uint32_t majorVersion) { return { majorVersion, std::numeric_limits::max() }; } bool Version::IsLatest() const { return (MajorVersion == std::numeric_limits::max() && MinorVersion == std::numeric_limits::max()); } bool Version::IsLatestForMajor(uint32_t majorVersion) const { return (MajorVersion == majorVersion && MinorVersion == std::numeric_limits::max()); } } ================================================ FILE: src/AppInstallerSharedLib/SQLiteWrapper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/SQLiteWrapper.h" #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerStrings.h" #include "ICU/SQLiteICU.h" #include using namespace std::chrono_literals; using namespace std::string_view_literals; // Enable this to have all Statement constructions output the associated query plan. #define WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED 0 #if WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED #include #endif // Connection is used twice #define SQLITE_ERROR_MSG(_error_,_connection_) (_connection_ ? sqlite3_errmsg(_connection_) : sqlite3_errstr(_error_)) #define THROW_SQLITE(_error_,_connection_) \ do { \ int _ts_sqliteReturnValue = (_error_); \ sqlite3* _ts_sqliteConnection = (_connection_); \ THROW_EXCEPTION_MSG(SQLiteException(_ts_sqliteReturnValue), "%hs", SQLITE_ERROR_MSG(_ts_sqliteReturnValue, _ts_sqliteConnection)); \ } while (0,0) #define THROW_IF_SQLITE_FAILED(_statement_,_connection_) \ do { \ int _tisf_sqliteReturnValue = (_statement_); \ if (_tisf_sqliteReturnValue != SQLITE_OK) \ { \ THROW_SQLITE(_tisf_sqliteReturnValue,_connection_); \ } \ } while (0,0) namespace AppInstaller::SQLite { std::string_view RowIDName = "rowid"sv; namespace { size_t GetNextConnectionId() { static std::atomic_size_t connectionId(0); return ++connectionId; } size_t GetNextStatementId() { static std::atomic_size_t statementId(0); return ++statementId; } } namespace details { void ParameterSpecificsImpl::Bind(sqlite3_stmt* stmt, int index, nullptr_t) { THROW_IF_SQLITE_FAILED(sqlite3_bind_null(stmt, index), sqlite3_db_handle(stmt)); } void ThrowIfContainsEmbeddedNullCharacter(std::string_view v) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_BIND_WITH_EMBEDDED_NULL, v.find('\0') != std::string_view::npos); } void ParameterSpecificsImpl::Bind(sqlite3_stmt* stmt, int index, const std::string& v) { ThrowIfContainsEmbeddedNullCharacter(v); THROW_IF_SQLITE_FAILED(sqlite3_bind_text64(stmt, index, v.c_str(), v.size(), SQLITE_TRANSIENT, SQLITE_UTF8), sqlite3_db_handle(stmt)); } std::string ParameterSpecificsImpl::GetColumn(sqlite3_stmt* stmt, int column) { return reinterpret_cast(sqlite3_column_text(stmt, column)); } void ParameterSpecificsImpl::Bind(sqlite3_stmt* stmt, int index, std::string_view v) { if (v.empty()) { // An empty string_view can have it's data member return nullptr, which effectively binds a null value. // We don't want that, so instead bind an empty string, which will have a non-null data pointer. ParameterSpecificsImpl::Bind(stmt, index, {}); } else { ThrowIfContainsEmbeddedNullCharacter(v); THROW_IF_SQLITE_FAILED(sqlite3_bind_text64(stmt, index, v.data(), v.size(), SQLITE_TRANSIENT, SQLITE_UTF8), sqlite3_db_handle(stmt)); } } void ParameterSpecificsImpl::Bind(sqlite3_stmt* stmt, int index, int v) { THROW_IF_SQLITE_FAILED(sqlite3_bind_int(stmt, index, v), sqlite3_db_handle(stmt)); } int ParameterSpecificsImpl::GetColumn(sqlite3_stmt* stmt, int column) { return sqlite3_column_int(stmt, column); } void ParameterSpecificsImpl::Bind(sqlite3_stmt* stmt, int index, int64_t v) { THROW_IF_SQLITE_FAILED(sqlite3_bind_int64(stmt, index, v), sqlite3_db_handle(stmt)); } int64_t ParameterSpecificsImpl::GetColumn(sqlite3_stmt* stmt, int column) { return sqlite3_column_int64(stmt, column); } void ParameterSpecificsImpl::Bind(sqlite3_stmt* stmt, int index, bool v) { THROW_IF_SQLITE_FAILED(sqlite3_bind_int(stmt, index, (v ? 1 : 0)), sqlite3_db_handle(stmt)); } bool ParameterSpecificsImpl::GetColumn(sqlite3_stmt* stmt, int column) { return (sqlite3_column_int(stmt, column) != 0); } std::string ParameterSpecificsImpl::ToLog(const blob_t& v) { std::ostringstream strstr; strstr << "blob[" << v.size() << "]"; return strstr.str(); } void ParameterSpecificsImpl::Bind(sqlite3_stmt* stmt, int index, const blob_t& v) { THROW_IF_SQLITE_FAILED(sqlite3_bind_blob64(stmt, index, v.data(), v.size(), SQLITE_TRANSIENT), sqlite3_db_handle(stmt)); } blob_t ParameterSpecificsImpl::GetColumn(sqlite3_stmt* stmt, int column) { const blob_t::value_type* blobPtr = reinterpret_cast(sqlite3_column_blob(stmt, column)); if (blobPtr) { int blobBytes = sqlite3_column_bytes(stmt, column); return blob_t{ blobPtr, blobPtr + blobBytes }; } else { return {}; } } std::string ParameterSpecificsImpl::ToLog(const GUID& v) { std::ostringstream strstr; strstr << v; return strstr.str(); } void ParameterSpecificsImpl::Bind(sqlite3_stmt* stmt, int index, const GUID& v) { static_assert(sizeof(v) == 16); THROW_IF_SQLITE_FAILED(sqlite3_bind_blob64(stmt, index, &v, sizeof(v), SQLITE_TRANSIENT), sqlite3_db_handle(stmt)); } GUID ParameterSpecificsImpl::GetColumn(sqlite3_stmt* stmt, int column) { GUID result{}; const void* blobPtr = sqlite3_column_blob(stmt, column); if (blobPtr) { result = *reinterpret_cast(blobPtr); } return result; } void SharedConnection::Disable() { m_active = false; } sqlite3* SharedConnection::Get() const { THROW_HR_IF(APPINSTALLER_CLI_ERROR_SQLITE_CONNECTION_TERMINATED, !m_active.load()); return m_dbconn.get(); } sqlite3** SharedConnection::GetPtr() { return &m_dbconn; } } Connection::Connection(const std::string& target, OpenDisposition disposition, OpenFlags flags) { m_dbconn = std::make_shared(); m_id = GetNextConnectionId(); AICLI_LOG(SQL, Info, << "Opening SQLite connection #" << m_id << ": '" << target << "' [" << std::hex << static_cast(disposition) << ", " << std::hex << static_cast(flags) << "]"); // Always force connection serialization until we determine that there are situations where it is not needed int resultingFlags = static_cast(disposition) | static_cast(flags) | SQLITE_OPEN_FULLMUTEX; THROW_IF_SQLITE_FAILED(sqlite3_open_v2(target.c_str(), m_dbconn->GetPtr(), resultingFlags, nullptr), nullptr); } Connection Connection::Create(const std::string& target, OpenDisposition disposition, OpenFlags flags) { Connection result{ target, disposition, flags }; THROW_IF_SQLITE_FAILED(sqlite3_extended_result_codes(result.m_dbconn->Get(), 1), result.m_dbconn->Get()); result.SetBusyTimeout(250ms); return result; } void Connection::EnableICU() { AICLI_LOG(SQL, Verbose, << "Enabling ICU"); THROW_IF_SQLITE_FAILED(sqlite3IcuInit(m_dbconn->Get()), m_dbconn->Get()); } rowid_t Connection::GetLastInsertRowID() { return sqlite3_last_insert_rowid(m_dbconn->Get()); } int Connection::GetChanges() const { return sqlite3_changes(m_dbconn->Get()); } size_t Connection::GetID() const { return m_id; } void Connection::SetBusyTimeout(std::chrono::milliseconds timeout) { THROW_IF_SQLITE_FAILED(sqlite3_busy_timeout(m_dbconn->Get(), static_cast(timeout.count())), m_dbconn->Get()); } bool Connection::SetJournalMode(std::string_view mode) { using namespace AppInstaller::Utility; std::ostringstream stream; stream << "PRAGMA journal_mode=" << mode; Statement setJournalMode = Statement::Create(*this, stream.str()); THROW_HR_IF(E_UNEXPECTED, !setJournalMode.Step()); return ToLower(setJournalMode.GetColumn(0)) == ToLower(mode); } void Connection::SetPageSize(size_t pageSize) { std::ostringstream stream; stream << "PRAGMA page_size=" << pageSize; Statement setPageSize = Statement::Create(*this, stream.str()); setPageSize.Step(); } std::shared_ptr Connection::GetSharedConnection() const { return m_dbconn; } Statement::Statement(const Connection& connection, std::string_view sql) { m_dbconn = connection.GetSharedConnection(); m_connectionId = connection.GetID(); m_id = GetNextStatementId(); AICLI_LOG(SQL, Verbose, << "Preparing statement #" << m_connectionId << '-' << m_id << ": " << sql); // SQL string size should include the null terminator (https://www.sqlite.org/c3ref/prepare.html) assert(sql.data()[sql.size()] == '\0'); THROW_IF_SQLITE_FAILED(sqlite3_prepare_v2(connection, sql.data(), static_cast(sql.size() + 1), &m_stmt, nullptr), connection); } #if WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED #define WINGET_SQLITE_EXPLAIN_QUERY_PLAN(_connection_,_sql_) \ std::string _explainStatementSQL_ = "EXPLAIN QUERY PLAN "; \ _explainStatementSQL_.append(_sql_); \ try { \ Statement _explainStatement_(_connection_,_explainStatementSQL_); \ LogExplainQueryPlanResult(_sql_, _explainStatement_); \ } catch(...) {} void LogExplainQueryPlanResult(std::string_view sql, Statement& plan) { bool outputHeader = true; std::stack parents; while (plan.Step()) { if (outputHeader) { AICLI_LOG(SQL, Info, << "Query plan for: " << sql); outputHeader = false; } int id = plan.GetColumn(0); int parent = plan.GetColumn(1); while (!parents.empty() && parents.top() != parent) { parents.pop(); } AICLI_LOG(SQL, Info, << "|-" << std::string(parents.size() * 2, '-') << ' ' << plan.GetColumn(3)); parents.push(id); } } #else #define WINGET_SQLITE_EXPLAIN_QUERY_PLAN(_connection_,_sql_) #endif Statement Statement::Create(const Connection& connection, const std::string& sql) { WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); return { connection, { sql.c_str(), sql.size() } }; } Statement Statement::Create(const Connection& connection, std::string_view sql) { WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); // We need the statement to be null terminated, and the only way to guarantee that with a string_view is to construct a string copy. return Create(connection, std::string(sql)); } Statement Statement::Create(const Connection& connection, char const* const sql) { WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); return { connection, sql }; } bool Statement::Step(bool closeConnectionOnError) { AICLI_LOG(SQL, Verbose, << "Stepping statement #" << m_connectionId << '-' << m_id); int result = sqlite3_step(m_stmt.get()); if (result == SQLITE_ROW) { AICLI_LOG(SQL, Verbose, << "Statement #" << m_connectionId << '-' << m_id << " has data"); m_state = State::HasRow; return true; } else if (result == SQLITE_DONE) { AICLI_LOG(SQL, Verbose, << "Statement #" << m_connectionId << '-' << m_id << " has completed"); m_state = State::Completed; return false; } else { m_state = State::Error; if (closeConnectionOnError) { m_dbconn->Disable(); } THROW_SQLITE(result, sqlite3_db_handle(m_stmt.get())); } } void Statement::Execute(bool closeConnectionOnError) { THROW_HR_IF(E_UNEXPECTED, Step(closeConnectionOnError)); } bool Statement::GetColumnIsNull(int column) { int type = sqlite3_column_type(m_stmt.get(), column); return type == SQLITE_NULL; } void Statement::Reset() { AICLI_LOG(SQL, Verbose, << "Reset statement #" << m_connectionId << '-' << m_id); // Ignore return value from reset, as if it is an error, it was the error from the last call to step. sqlite3_reset(m_stmt.get()); m_state = State::Prepared; } Transaction::Transaction() : m_inProgress(false) {} Transaction::Transaction(Connection& connection, std::string&& name, bool immediateWrite) : m_name(std::move(name)) { using namespace std::string_literals; Statement begin = Statement::Create(connection, "BEGIN "s + (immediateWrite ? "IMMEDIATE" : "DEFERRED")); m_rollback = Statement::Create(connection, "ROLLBACK"); m_commit = Statement::Create(connection, "COMMIT"); AICLI_LOG(SQL, Verbose, << "Begin transaction: " << m_name); begin.Step(); } Transaction Transaction::Create(Connection& connection, std::string name, bool immediateWrite) { return { connection, std::move(name), immediateWrite }; } Transaction::~Transaction() { // Prevent a termination by not throwing on errors here Rollback(false); } void Transaction::Rollback(bool throwOnError) { if (m_inProgress) { // Only try rollback once m_inProgress = false; try { AICLI_LOG(SQL, Verbose, << "Roll back transaction: " << m_name); m_rollback.Step(true); } catch (...) { if (throwOnError) { throw; } LOG_CAUGHT_EXCEPTION(); } } } void Transaction::Commit() { if (m_inProgress) { AICLI_LOG(SQL, Verbose, << "Commit transaction: " << m_name); m_commit.Step(); m_inProgress = false; } } Savepoint::Savepoint() : m_inProgress(false) {} Savepoint::Savepoint(Connection& connection, std::string&& name) : m_name(std::move(name)) { using namespace std::string_literals; Statement begin = Statement::Create(connection, "SAVEPOINT ["s + m_name + "]"); m_rollbackTo = Statement::Create(connection, "ROLLBACK TO ["s + m_name + "]"); m_release = Statement::Create(connection, "RELEASE ["s + m_name + "]"); AICLI_LOG(SQL, Verbose, << "Begin savepoint: " << m_name); begin.Step(); } Savepoint Savepoint::Create(Connection& connection, std::string name) { return { connection, std::move(name) }; } Savepoint::~Savepoint() { // Prevent a termination by not throwing on errors here Rollback(false); } void Savepoint::Rollback(bool throwOnError) { if (m_inProgress) { // Only try rollback once m_inProgress = false; try { AICLI_LOG(SQL, Verbose, << "Roll back savepoint: " << m_name); m_rollbackTo.Step(true); // 'ROLLBACK TO' *DOES NOT* remove the savepoint from the transaction stack. // In order to remove it, we must RELEASE. Since we just invoked a ROLLBACK TO // this should have the effect of 'committing' nothing. m_release.Step(true); } catch (...) { if (throwOnError) { throw; } LOG_CAUGHT_EXCEPTION(); } } } void Savepoint::Commit() { if (m_inProgress) { AICLI_LOG(SQL, Verbose, << "Commit savepoint: " << m_name); m_release.Step(); m_inProgress = false; } } Backup::Backup(Connection& destination, const std::string& destinationName, Connection& source, const std::string& sourceName) { m_backup.reset(sqlite3_backup_init(destination, destinationName.c_str(), source, sourceName.c_str())); if (!m_backup) { THROW_SQLITE(sqlite3_errcode(destination), destination); } } Backup Backup::Create(Connection& destination, const std::string& destinationName, Connection& source, const std::string& sourceName) { return { destination, destinationName, source, sourceName }; } bool Backup::Step(int pages) { int stepResult = sqlite3_backup_step(m_backup.get(), pages); if (stepResult == SQLITE_OK) { // A negative number of pages should finish the operation if (pages < 0) { THROW_HR(E_UNEXPECTED); } // Success but not done return false; } else if (stepResult == SQLITE_DONE) { return true; } else { THROW_SQLITE(stepResult, nullptr); } } std::string_view EscapeCharForLike = "'"sv; std::string EscapeStringForLike(std::string_view value) { constexpr char singleChar = '_'; constexpr char multiChar = '%'; char escapeChar = EscapeCharForLike[0]; std::string result; result.reserve(value.length()); for (char c : value) { if (c == singleChar || c == multiChar || c == escapeChar) { result.append(1, escapeChar); } result.append(1, c); } return result; } } ================================================ FILE: src/AppInstallerSharedLib/Security.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "winget/Security.h" #include "AppInstallerLogging.h" #include "AppInstallerLanguageUtilities.h" namespace AppInstaller::Security { namespace { bool IsSameAuthority(const SID_IDENTIFIER_AUTHORITY& a, const SID_IDENTIFIER_AUTHORITY& b) { for (size_t i = 0; i < ARRAYSIZE(a.Value); ++i) { if (a.Value[i] != b.Value[i]) { return false; } } return true; } // Helper to impersonate the COM or RPC caller. struct ImpersonateCOMorRPCCaller { static ImpersonateCOMorRPCCaller BeginImpersonation() { return {}; } ~ImpersonateCOMorRPCCaller() { if (m_serverSecurity) { FAIL_FAST_IF_FAILED(m_serverSecurity->RevertToSelf()); } else { FAIL_FAST_IF(RpcRevertToSelf() != RPC_S_OK); } } private: ImpersonateCOMorRPCCaller() { if (SUCCEEDED_LOG(CoGetCallContext(IID_IServerSecurity, m_serverSecurity.put_void()))) { THROW_IF_FAILED(m_serverSecurity->ImpersonateClient()); } else { RPC_STATUS status = RpcImpersonateClient(nullptr); THROW_HR_IF(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_RPC, status), status != RPC_S_OK); } } wil::com_ptr m_serverSecurity; }; } IntegrityLevel GetEffectiveIntegrityLevel() { auto currentIntegrityLevel = wil::get_token_information(); PSID sid = currentIntegrityLevel->Label.Sid; THROW_HR_IF(CO_E_INVALIDSID, !IsValidSid(sid)); auto identifierAuthority = GetSidIdentifierAuthority(sid); THROW_HR_IF(E_UNEXPECTED, !IsSameAuthority(*identifierAuthority, SECURITY_MANDATORY_LABEL_AUTHORITY)); PUCHAR subAuthorityCount = GetSidSubAuthorityCount(sid); THROW_HR_IF(E_UNEXPECTED, *subAuthorityCount != 1); PDWORD subAuthority = GetSidSubAuthority(sid, 0); switch (*subAuthority) { case SECURITY_MANDATORY_UNTRUSTED_RID: return IntegrityLevel::Untrusted; case SECURITY_MANDATORY_LOW_RID: return IntegrityLevel::Low; case SECURITY_MANDATORY_MEDIUM_RID: return IntegrityLevel::Medium; case SECURITY_MANDATORY_HIGH_RID: return IntegrityLevel::High; case SECURITY_MANDATORY_SYSTEM_RID: return IntegrityLevel::System; case SECURITY_MANDATORY_PROTECTED_PROCESS_RID: return IntegrityLevel::ProtectedProcess; } THROW_HR(E_UNEXPECTED); } bool IsCOMCallerSameUserAndIntegrityLevel() { auto serverUser = wil::get_token_information(); IntegrityLevel serverIntegrityLevel = GetEffectiveIntegrityLevel(); auto impersonation = ImpersonateCOMorRPCCaller::BeginImpersonation(); auto callingUser = wil::get_token_information(); IntegrityLevel callingIntegrityLevel = GetEffectiveIntegrityLevel(); if (!EqualSid(serverUser->User.Sid, callingUser->User.Sid)) { AICLI_LOG(Core, Crit, << "Attempt to access by another user: " << ToString(callingUser->User.Sid)); return false; } if (ToIntegral(callingIntegrityLevel) < ToIntegral(serverIntegrityLevel)) { AICLI_LOG(Core, Crit, << "Attempt to access by a lower integrity process: " << callingIntegrityLevel << " < " << serverIntegrityLevel); return false; } return true; } bool IsCOMCallerIntegrityLevelAtLeast(IntegrityLevel minimumLevel) { auto impersonation = ImpersonateCOMorRPCCaller::BeginImpersonation(); return IsCurrentIntegrityLevelAtLeast(minimumLevel); } bool IsCurrentIntegrityLevelAtLeast(IntegrityLevel minimumLevel) { IntegrityLevel callingIntegrityLevel = GetEffectiveIntegrityLevel(); if (ToIntegral(callingIntegrityLevel) < ToIntegral(minimumLevel)) { AICLI_LOG(Core, Crit, << "Attempt to access by a lower integrity process than required: " << callingIntegrityLevel << " < " << minimumLevel); return false; } return true; } std::string ToString(PSID sid) { wil::unique_hlocal_ansistring result; THROW_IF_WIN32_BOOL_FALSE(ConvertSidToStringSidA(sid, &result)); return result.get(); } } ================================================ FILE: src/AppInstallerSharedLib/SharedThreadGlobals.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/winget/SharedThreadGlobals.h" namespace AppInstaller::ThreadLocalStorage { using namespace AppInstaller::Logging; static ThreadGlobals* SetOrGetThreadGlobals(bool setThreadGlobals, ThreadGlobals* pThreadGlobals = nullptr) { thread_local AppInstaller::ThreadLocalStorage::ThreadGlobals* t_pThreadGlobals = nullptr; if (setThreadGlobals) { AppInstaller::ThreadLocalStorage::ThreadGlobals* previous_pThreadGlobals = t_pThreadGlobals; t_pThreadGlobals = pThreadGlobals; return previous_pThreadGlobals; } return t_pThreadGlobals; } std::unique_ptr ThreadGlobals::SetForCurrentThread() { return std::make_unique(SetOrGetThreadGlobals(true, this)); } ThreadGlobals* ThreadGlobals::GetForCurrentThread() { return SetOrGetThreadGlobals(false); } PreviousThreadGlobals::PreviousThreadGlobals(ThreadGlobals* previous) : m_previous(previous) { m_threadId = GetCurrentThreadId(); } PreviousThreadGlobals::~PreviousThreadGlobals() { // Not remaining on the same thread is a serious issue that must be resolved FAIL_FAST_IF(GetCurrentThreadId() != m_threadId); std::ignore = SetOrGetThreadGlobals(true, m_previous); } } ================================================ FILE: src/AppInstallerSharedLib/Versions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerVersions.h" #include "Public/AppInstallerStrings.h" namespace AppInstaller::Utility { using namespace std::string_view_literals; static constexpr std::string_view s_Digit_Characters = "0123456789"sv; static constexpr std::string_view s_Version_Part_Latest = "Latest"sv; static constexpr std::string_view s_Version_Part_Unknown = "Unknown"sv; static constexpr std::string_view s_Approximate_Less_Than = "< "sv; static constexpr std::string_view s_Approximate_Greater_Than = "> "sv; Version::Version(std::string&& version, std::string_view splitChars) { Assign(std::move(version), splitChars); } RawVersion::RawVersion(std::string version, std::string_view splitChars) { m_trimPrefix = false; Assign(std::move(version), splitChars); } Version::Version(Version baseVersion, ApproximateComparator approximateComparator) : Version(std::move(baseVersion)) { if (approximateComparator == ApproximateComparator::None) { return; } THROW_HR_IF(E_INVALIDARG, this->IsApproximate() || this->IsUnknown()); m_approximateComparator = approximateComparator; if (approximateComparator == ApproximateComparator::LessThan) { m_version = std::string{ s_Approximate_Less_Than } + m_version; } else if (approximateComparator == ApproximateComparator::GreaterThan) { m_version = std::string{ s_Approximate_Greater_Than } + m_version; } } void Version::Assign(std::string version, std::string_view splitChars) { m_version = std::move(Utility::Trim(version)); // Process approximate comparator if applicable std::string baseVersion = m_version; if (CaseInsensitiveStartsWith(m_version, s_Approximate_Less_Than)) { m_approximateComparator = ApproximateComparator::LessThan; baseVersion = m_version.substr(s_Approximate_Less_Than.length(), m_version.length() - s_Approximate_Less_Than.length()); } else if (CaseInsensitiveStartsWith(m_version, s_Approximate_Greater_Than)) { m_approximateComparator = ApproximateComparator::GreaterThan; baseVersion = m_version.substr(s_Approximate_Greater_Than.length(), m_version.length() - s_Approximate_Greater_Than.length()); } // If there is a digit before the split character, or no split characters exist, trim off all leading non-digit characters size_t digitPos = baseVersion.find_first_of(s_Digit_Characters); size_t splitPos = baseVersion.find_first_of(splitChars); if (m_trimPrefix && digitPos != std::string::npos && (splitPos == std::string::npos || digitPos < splitPos)) { baseVersion.erase(0, digitPos); } // Then parse the base version size_t pos = 0; while (pos < baseVersion.length()) { size_t newPos = baseVersion.find_first_of(splitChars, pos); size_t length = (newPos == std::string::npos ? baseVersion.length() : newPos) - pos; m_parts.emplace_back(baseVersion.substr(pos, length)); pos += length + 1; } // Trim version parts Trim(); THROW_HR_IF(E_INVALIDARG, m_approximateComparator != ApproximateComparator::None && IsBaseVersionUnknown()); } void Version::Trim() { while (!m_parts.empty()) { const Part& part = m_parts.back(); if (part.Integer == 0 && part.Other.empty()) { m_parts.pop_back(); } else { return; } } } bool Version::operator<(const Version& other) const { // Sort Latest higher than any other values bool thisIsLatest = IsBaseVersionLatest(); bool otherIsLatest = other.IsBaseVersionLatest(); if (thisIsLatest && otherIsLatest) { return ApproximateCompareLessThan(other); } else if (thisIsLatest || otherIsLatest) { // If only one is latest, this can only be less than if the other is and this is not. return (otherIsLatest && !thisIsLatest); } // Sort Unknown lower than any known values bool thisIsUnknown = IsBaseVersionUnknown(); bool otherIsUnknown = other.IsBaseVersionUnknown(); if (thisIsUnknown && otherIsUnknown) { // This code path should always return false as we disable approximate version for Unknown for now return ApproximateCompareLessThan(other); } else if (thisIsUnknown || otherIsUnknown) { // If at least one is unknown, this can only be less than if it is and the other is not. return (thisIsUnknown && !otherIsUnknown); } const Part emptyPart{}; for (size_t i = 0; i < std::max(m_parts.size(), other.m_parts.size()); ++i) { // Whichever version is shorter, we need to pad it with empty parts const Part& partA = (i >= m_parts.size()) ? emptyPart : m_parts[i]; const Part& partB = (i >= other.m_parts.size()) ? emptyPart : other.m_parts[i]; if (partA < partB) { return true; } else if (partB < partA) { return false; } // else parts are equal, so continue to next part } // All parts were compared and found to be equal return ApproximateCompareLessThan(other); } bool Version::operator>(const Version& other) const { return other < *this; } bool Version::operator<=(const Version& other) const { return !(*this > other); } bool Version::operator>=(const Version& other) const { return !(*this < other); } bool Version::operator==(const Version& other) const { if (m_approximateComparator != other.m_approximateComparator) { return false; } if ((IsBaseVersionLatest() && other.IsBaseVersionLatest()) || (IsBaseVersionUnknown() && other.IsBaseVersionUnknown())) { return true; } if (m_parts.size() != other.m_parts.size()) { return false; } for (size_t i = 0; i < m_parts.size(); ++i) { if (m_parts[i] != other.m_parts[i]) { return false; } } return true; } bool Version::operator!=(const Version& other) const { return !(*this == other); } bool Version::IsLatest() const { return (m_approximateComparator != ApproximateComparator::LessThan && IsBaseVersionLatest()); } Version Version::CreateLatest() { Version result; result.m_version = s_Version_Part_Latest; result.m_parts.emplace_back(0, std::string{ s_Version_Part_Latest }); return result; } bool Version::IsUnknown() const { return IsBaseVersionUnknown(); } Version Version::CreateUnknown() { Version result; result.m_version = s_Version_Part_Unknown; result.m_parts.emplace_back(0, std::string{ s_Version_Part_Unknown }); return result; } const Version::Part& Version::PartAt(size_t index) const { static Part s_zero{}; if (index < m_parts.size()) { return m_parts[index]; } else { return s_zero; } } Version Version::GetBaseVersion() const { Version baseVersion = *this; baseVersion.m_approximateComparator = ApproximateComparator::None; if (m_approximateComparator == ApproximateComparator::LessThan) { baseVersion.m_version = m_version.substr(s_Approximate_Less_Than.size()); } else if (m_approximateComparator == ApproximateComparator::GreaterThan) { baseVersion.m_version = m_version.substr(s_Approximate_Greater_Than.size()); } return baseVersion; } bool Version::IsBaseVersionLatest() const { return (m_parts.size() == 1 && m_parts[0].Integer == 0 && Utility::CaseInsensitiveEquals(m_parts[0].Other, s_Version_Part_Latest)); } bool Version::IsBaseVersionUnknown() const { return (m_parts.size() == 1 && m_parts[0].Integer == 0 && Utility::CaseInsensitiveEquals(m_parts[0].Other, s_Version_Part_Unknown)); } bool Version::ApproximateCompareLessThan(const Version& other) const { // Only true if this is less than, other is not, OR this is none, other is greater than return (m_approximateComparator == ApproximateComparator::LessThan && other.m_approximateComparator != ApproximateComparator::LessThan) || (m_approximateComparator == ApproximateComparator::None && other.m_approximateComparator == ApproximateComparator::GreaterThan); } Version::Part::Part(const std::string& part) { std::string interimPart = Utility::Trim(part); const char* begin = interimPart.c_str(); char* end = nullptr; errno = 0; Integer = strtoull(begin, &end, 10); if (errno == ERANGE) { Integer = 0; Other = interimPart; } else if (static_cast(end - begin) != interimPart.length()) { Other = end; } m_foldedOther = Utility::FoldCase(static_cast(Other)); } Version::Part::Part(uint64_t integer, std::string other) : Integer(integer), Other(std::move(Utility::Trim(other))) { m_foldedOther = Utility::FoldCase(static_cast(Other)); } bool Version::Part::operator<(const Part& other) const { if (Integer < other.Integer) { return true; } else if (Integer > other.Integer) { return false; } else if (Other.empty()) { // If this Other is empty, it is at least >= return false; } else if (!Other.empty() && other.Other.empty()) { // If the other Other is empty and this is not, this is less. return true; } else if (m_foldedOther < other.m_foldedOther) { // Compare the folded versions return true; } // else Other >= other.Other return false; } bool Version::Part::operator==(const Part& other) const { return Integer == other.Integer && m_foldedOther == other.m_foldedOther; } bool Version::Part::operator!=(const Part& other) const { return !(*this == other); } bool Channel::operator<(const Channel& other) const { return m_channel < other.m_channel; } VersionAndChannel::VersionAndChannel(Version&& version, Channel&& channel) : m_version(std::move(version)), m_channel(std::move(channel)) {} std::string VersionAndChannel::ToString() const { std::string result; result = m_version.ToString(); if (!m_channel.ToString().empty()) { result += '['; result += m_channel.ToString(); result += ']'; } return result; } bool VersionAndChannel::operator<(const VersionAndChannel& other) const { if (m_channel < other.m_channel) { return true; } else if (other.m_channel < m_channel) { return false; } // We intentionally invert the order for version here. else if (other.m_version < m_version) { return true; } // else m_version >= other.m_version return false; } bool VersionAndChannel::IsUpdatedBy(const VersionAndChannel& other) const { // Channel crossing should not happen here. if (!Utility::ICUCaseInsensitiveEquals(m_channel.ToString(), other.m_channel.ToString())) { return false; } return m_version < other.m_version; } UInt64Version::UInt64Version(UINT64 version) { Assign(version); } UInt64Version::UInt64Version(uint16_t major, uint16_t minor, uint16_t build, uint16_t revision) { Assign(major, minor, build, revision); } void UInt64Version::Assign(UINT64 version) { constexpr UINT64 mask16 = (1 << 16) - 1; uint16_t revision = version & mask16; uint16_t build = (version >> 0x10) & mask16; uint16_t minor = (version >> 0x20) & mask16; uint16_t major = (version >> 0x30) & mask16; Assign(major, minor, build, revision); } void UInt64Version::Assign(uint16_t major, uint16_t minor, uint16_t build, uint16_t revision) { // Construct a string representation of the provided version std::stringstream ssVersion; ssVersion << major << Version::DefaultSplitChars << minor << Version::DefaultSplitChars << build << Version::DefaultSplitChars << revision; m_version = ssVersion.str(); // Construct the 4 parts m_parts = { major, minor, build, revision }; // Trim version parts Trim(); } UInt64Version::UInt64Version(std::string&& version, std::string_view splitChars) { Assign(std::move(version), splitChars); } void UInt64Version::Assign(std::string version, std::string_view splitChars) { Version::Assign(std::move(version), splitChars); // After trimming trailing parts (0 or empty), // at most 4 parts must be present THROW_HR_IF(E_INVALIDARG, m_parts.size() > 4); for (const auto& part : m_parts) { // Check for non-empty Other part THROW_HR_IF(E_INVALIDARG, !part.Other.empty()); // Check for overflow Integer part THROW_HR_IF(E_INVALIDARG, part.Integer >> 16 != 0); } } SemanticVersion::SemanticVersion(std::string&& version) { Assign(std::move(version), DefaultSplitChars); } void SemanticVersion::Assign(std::string version, std::string_view splitChars) { // Semantic versions require using the default split character THROW_HR_IF(E_INVALIDARG, splitChars != DefaultSplitChars); // First split off any trailing build metadata std::string interimVersion = Utility::Trim(version); size_t buildMetadataPos = interimVersion.find('+', 0); if (buildMetadataPos != std::string::npos) { m_buildMetadata.Assign(interimVersion.substr(buildMetadataPos + 1)); interimVersion.resize(buildMetadataPos); } // Now split off the prerelease data size_t prereleasePos = interimVersion.find('-', 0); if (prereleasePos != std::string::npos) { m_prerelease.Assign(interimVersion.substr(prereleasePos + 1)); interimVersion.resize(prereleasePos); } // Parse main version Version::Assign(std::move(interimVersion), splitChars); THROW_HR_IF(E_INVALIDARG, IsApproximate()); THROW_HR_IF(E_INVALIDARG, m_parts.size() > 3); for (size_t i = 0; i < 3; ++i) { THROW_HR_IF(E_INVALIDARG, !PartAt(i).Other.empty()); } // Put rest of version back onto Other of last part size_t otherSplit = (prereleasePos != std::string::npos ? prereleasePos : buildMetadataPos); if (otherSplit != std::string::npos) { while (m_parts.size() < 3) { m_parts.emplace_back(); } m_parts[2].Other = version.substr(otherSplit); } // Overwrite the whole version string with our whole version string m_version = std::move(version); } bool SemanticVersion::IsPrerelease() const { return !m_prerelease.IsEmpty(); } const Version& SemanticVersion::PrereleaseVersion() const { return m_prerelease; } bool SemanticVersion::HasBuildMetadata() const { return !m_buildMetadata.IsEmpty(); } const Version& SemanticVersion::BuildMetadata() const { return m_buildMetadata; } VersionRange::VersionRange(Version first, Version second) { if (first < second) { m_minVersion = std::move(first); m_maxVersion = std::move(second); } else { m_minVersion = std::move(second); m_maxVersion = std::move(first); } } bool VersionRange::Overlaps(const VersionRange& other) const { // No overlap if either is an empty range. if (IsEmpty() || other.IsEmpty()) { return false; } return m_minVersion <= other.m_maxVersion && m_maxVersion >= other.m_minVersion; } bool VersionRange::IsSameAsSingleVersion(const Version& version) const { if (IsEmpty()) { return false; } return m_minVersion == version && m_maxVersion == version; } bool VersionRange::ContainsVersion(const Version& version) const { if (IsEmpty()) { return false; } return version >= m_minVersion && version <= m_maxVersion; } bool VersionRange::operator<(const VersionRange& other) const { THROW_HR_IF(E_INVALIDARG, IsEmpty() || other.IsEmpty() || Overlaps(other)); return m_minVersion < other.m_minVersion; } const Version& VersionRange::GetMinVersion() const { THROW_HR_IF(E_NOT_VALID_STATE, IsEmpty()); return m_minVersion; } const Version& VersionRange::GetMaxVersion() const { THROW_HR_IF(E_NOT_VALID_STATE, IsEmpty()); return m_maxVersion; } bool GatedVersion::IsValidVersion(Version version) const { auto gateParts = m_version.GetParts(); if (gateParts.empty()) { return false; } if (gateParts.back() != Version::Part("*")) { // Without wildcards, revert to direct comparison return m_version == version; } auto versionParts = version.GetParts(); for (size_t i = 0; i < gateParts.size() - 1; ++i) { if (versionParts.size() > i) { if (gateParts[i] == versionParts[i]) { continue; } else { // Mismatch with the gated version return false; } } else { // Assume trailing 0s on the version if (gateParts[i] != Version::Part(0)) { return false; } } } // All version parts matched return true; } bool HasOverlapInVersionRanges(const std::vector& ranges) { for (size_t i = 0; i < ranges.size(); i++) { for (size_t j = i + 1; j < ranges.size(); j++) { if (ranges[i].Overlaps(ranges[j])) { return true; } } } return false; } OpenTypeFontVersion::OpenTypeFontVersion(std::string&& version) { Assign(std::move(version), DefaultSplitChars); } void OpenTypeFontVersion::Assign(std::string version, std::string_view splitChars) { // Open type version requires using the default split character THROW_HR_IF(E_INVALIDARG, splitChars != DefaultSplitChars); // Split on default split character. std::vector parts = Split(version, '.', true); std::string majorString; std::string minorString; // Font version must have a "major.minor" part. if (parts.size() >= 2) { // Find first digit and trim all preceding characters. std::string firstPart = parts[0]; size_t majorStartIndex = firstPart.find_first_of(s_Digit_Characters); if (majorStartIndex != std::string::npos) { firstPart.erase(0, majorStartIndex); } size_t majorEndIndex = firstPart.find_last_of(s_Digit_Characters); majorString = firstPart.substr(0, majorEndIndex + 1); // Parse and verify minor part. std::string secondPart = parts[1]; size_t endPos = secondPart.find_first_not_of(s_Digit_Characters); // If a non-digit character exists, trim off the remainder. if (endPos != std::string::npos) { secondPart.erase(endPos, secondPart.length()); } minorString = secondPart; } // Verify results. if (!majorString.empty() && !minorString.empty()) { m_parts.emplace_back(majorString); m_parts.emplace_back(minorString); m_version = Utility::Join(DefaultSplitChars, { majorString, minorString }); Trim(); } else { m_version = s_Version_Part_Unknown; m_parts.emplace_back(0, std::string{ s_Version_Part_Unknown }); } } } ================================================ FILE: src/AppInstallerSharedLib/Yaml.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include #include "winget/Yaml.h" #include "YamlWrapper.h" #include "AppInstallerErrors.h" #include "AppInstallerLogging.h" #include "AppInstallerStrings.h" namespace AppInstaller::YAML { using namespace std::string_view_literals; namespace { Node s_globalInvalidNode; static constexpr std::string_view s_nullTag = "tag:yaml.org,2002:null"sv; static constexpr std::string_view s_boolTag = "tag:yaml.org,2002:bool"sv; static constexpr std::string_view s_strTag = "tag:yaml.org,2002:str"sv; static constexpr std::string_view s_intTag = "tag:yaml.org,2002:int"sv; static constexpr std::string_view s_floatTag = "tag:yaml.org,2002:float"sv; static constexpr std::string_view s_timestampTag = "tag:yaml.org,2002:timestamp"sv; static constexpr std::string_view s_seqTag = "tag:yaml.org,2002:seq"sv; static constexpr std::string_view s_mapTag = "tag:yaml.org,2002:map"sv; std::string_view GetExceptionTypeStringView(Exception::Type type) { switch (type) { case Exception::Type::None: return "None"sv; case Exception::Type::Memory: return "Memory"sv; case Exception::Type::Reader: return "Reader"sv; case Exception::Type::Scanner: return "Scanner"sv; case Exception::Type::Parser: return "Parser"sv; case Exception::Type::Composer: return "Composer"sv; case Exception::Type::Writer: return "Writer"sv; case Exception::Type::Emitter: return "Emitter"sv; case Exception::Type::Policy: return "Policy"sv; } return "Unknown"sv; } void OutputExceptionHeader(std::ostringstream& out, Exception::Type type) { out << "[YAML:" << GetExceptionTypeStringView(type) << "] "; } void OutputMark(std::ostringstream& out, const Mark& mark) { out << "[line " << mark.line << "; col " << mark.column << ']'; } Node::TagType ConvertToTagType(const std::string& tag) { if (tag == s_strTag) { return Node::TagType::Str; } else if (tag == s_seqTag) { return Node::TagType::Seq; } else if (tag == s_mapTag) { return Node::TagType::Map; } else if (tag == s_boolTag) { return Node::TagType::Bool; } else if (tag == s_intTag) { return Node::TagType::Int; } else if (tag == s_floatTag) { return Node::TagType::Float; } else if (tag == s_timestampTag) { return Node::TagType::Timestamp; } else if (tag == s_nullTag) { return Node::TagType::Null; } return Node::TagType::Unknown; } DocumentSchemaHeader ExtractSchemaHeaderFromYaml( const std::string& yamlDocument, size_t rootNodeLine) { std::istringstream input(yamlDocument); std::string line; size_t currentLine = 1; // Search for the schema header string in the comments before the root node. while (currentLine < rootNodeLine && std::getline(input, line)) { std::string comment = Utility::Trim(line); // Check if the line is a comment if (!comment.empty() && comment[0] == '#') { size_t pos = line.find(DocumentSchemaHeader::YamlLanguageServerKey); // Check if the comment contains the schema header string if (pos != std::string::npos) { return DocumentSchemaHeader(std::move(comment), YAML::Mark{ currentLine, pos}); } } currentLine++; } return {}; } } Exception::Exception(Type type) : wil::ResultException(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR) { std::ostringstream out; OutputExceptionHeader(out, type); if (type == Type::Memory) { out << "Unable to (re)allocate memory"; } else { out << "An unknown error occurred"; } m_what = out.str(); } Exception::Exception(Type type, const char* problem, size_t offset, int value) : wil::ResultException(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR) { std::ostringstream out; OutputExceptionHeader(out, type); out << (problem ? problem : "Unexplained error"); if (value != -1) { out << " [" << value << ']'; } out << " at " << offset; m_what = out.str(); } Exception::Exception(Type type, const char* problem, const Mark& problemMark, const char* context, const Mark& contextMark) : wil::ResultException(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR), m_mark(problemMark) { std::ostringstream out; OutputExceptionHeader(out, type); if (context) { out << context << ' '; OutputMark(out, contextMark); out << ' ' << (problem ? problem : "unexplained error"); } else { out << (problem ? problem : "Unexplained error"); } out << ' '; OutputMark(out, problemMark); m_what = out.str(); } Exception::Exception(Type type, const char* problem) : wil::ResultException(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR) { std::ostringstream out; OutputExceptionHeader(out, type); out << (problem ? problem : "Unexplained error"); m_what = out.str(); } const char* Exception::what() const noexcept { return m_what.c_str(); } const Mark& Exception::GetMark() const { return m_mark; } Node::Node(Type type, std::string tag, const YAML::Mark& mark) : m_type(type), m_tag(std::move(tag)), m_mark(mark) { if (m_type == Type::Sequence) { m_sequence = decltype(m_sequence)::value_type{}; } else if (m_type == Type::Mapping) { m_mapping = decltype(m_mapping)::value_type{}; } m_tagType = ConvertToTagType(m_tag); } void Node::SetScalar(std::string value) { Require(Type::Scalar); m_scalar = std::move(value); } void Node::SetScalar(std::string value, bool isQuoted) { this->SetScalar(value); // For untagged scalar nodes, libyaml always assigns the generic string // tag. Here we just try our best and assume that if the value is unquoted // then is not necessarily a string. // TODO: handle float and timestamps if (!isQuoted && this->GetTagType() == TagType::Str) { // Integer // 0 | -? [1-9] [0-9]* auto tryInt = this->try_as(); if (tryInt.has_value()) { m_tagType = TagType::Int; return; } // Boolean. Either 'true' or 'false' auto tryBool = this->try_as(); if (tryBool.has_value()) { m_tagType = TagType::Bool; } } } bool Node::operator<(const Node& other) const { Require(Type::Scalar); other.Require(Type::Scalar); return this->m_scalar < other.m_scalar; } Node& Node::operator[](std::string_view key) { Require(Type::Mapping); auto itrs = m_mapping->equal_range(key); if (itrs.first == itrs.second) { return s_globalInvalidNode; } Node& result = itrs.first->second; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, ++itrs.first != itrs.second); return result; } const Node& Node::operator[](std::string_view key) const { Require(Type::Mapping); auto itrs = m_mapping->equal_range(key); if (itrs.first == itrs.second) { return s_globalInvalidNode; } const Node& result = itrs.first->second; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, ++itrs.first != itrs.second); return result; } // Gets a child node from the mapping by its name. Node& Node::GetChildNode(std::string_view key) { Require(Type::Mapping); auto itr = m_mapping->begin(); for (; itr != m_mapping->end(); itr++) { if (Utility::CaseInsensitiveEquals(itr->first.m_scalar, key)) { break; } } if (itr == m_mapping->end()) { return s_globalInvalidNode; } auto firstFound = itr; for (++itr; itr != m_mapping->end(); itr++) { if (Utility::CaseInsensitiveEquals(itr->first.m_scalar, key)) { break; } } THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, itr != m_mapping->end()); Node& result = firstFound->second; return result; } const Node& Node::GetChildNode(std::string_view key) const { Require(Type::Mapping); auto itr = m_mapping->begin(); for (; itr != m_mapping->end(); itr++) { if (Utility::CaseInsensitiveEquals(itr->first.m_scalar, key)) { break; } } if (itr == m_mapping->end()) { return s_globalInvalidNode; } auto firstFound = itr; for (++itr; itr != m_mapping->end(); itr++) { if (Utility::CaseInsensitiveEquals(itr->first.m_scalar, key)) { break; } } THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, itr != m_mapping->end()); const Node& result = firstFound->second; return result; } Node& Node::operator[](size_t index) { Require(Type::Sequence); return m_sequence.value()[index]; } const Node& Node::operator[](size_t index) const { Require(Type::Sequence); return m_sequence.value()[index]; } size_t Node::size() const { switch (m_type) { case Type::Invalid: case Type::None: case Type::Scalar: return 0; case Type::Sequence: return m_sequence->size(); case Type::Mapping: return m_mapping->size(); } THROW_HR(E_UNEXPECTED); } const std::vector& Node::Sequence() const { Require(Type::Sequence); return m_sequence.value(); } const std::multimap& Node::Mapping() const { Require(Type::Mapping); return m_mapping.value(); } void Node::Require(Type type) const { THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION, m_type != type); } std::string Node::as_dispatch(std::string*) const { return m_scalar; } std::optional Node::try_as_dispatch(std::string*) const { return std::optional{ m_scalar }; } std::wstring Node::as_dispatch(std::wstring*) const { return Utility::ConvertToUTF16(m_scalar); } std::optional Node::try_as_dispatch(std::wstring*) const { return Utility::TryConvertToUTF16(m_scalar); } int64_t Node::as_dispatch(int64_t*) const { return std::stoll(m_scalar); } std::optional Node::try_as_dispatch(int64_t*) const { if (m_scalar.empty()) { return {}; } const char* begin = m_scalar.c_str(); char* end = nullptr; errno = 0; int64_t result = static_cast(strtoll(begin, &end, 0)); if (errno == ERANGE || static_cast(end - begin) != m_scalar.length()) { return {}; } return result; } int Node::as_dispatch(int*) const { // To allow HResult representation return static_cast(std::stoll(m_scalar, 0, 0)); } std::optional Node::try_as_dispatch(int*) const { try { return std::optional{ static_cast(std::stoll(m_scalar, 0, 0)) }; } catch (...) { return {}; } } bool Node::as_dispatch(bool*) const { bool* t = nullptr; auto tryToBool = this->try_as_dispatch(t); if (tryToBool.has_value()) { return tryToBool.value(); } else { THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA); } } std::optional Node::try_as_dispatch(bool*) const { if (Utility::CaseInsensitiveEquals(m_scalar, "true")) { return std::optional{ true }; } else if (Utility::CaseInsensitiveEquals(m_scalar, "false")) { return std::optional{ false }; } return {}; } void Node::MergeSequenceNode(Node other, std::string_view key, bool caseInsensitive) { Require(Type::Sequence); other.Require(Type::Sequence); auto getKeyValue = [&](const YAML::Node& node) { auto keyNode = caseInsensitive ? node.GetChildNode(key) : node[key]; if (keyNode.IsNull()) { THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA); } auto keyValue = keyNode.as(); return caseInsensitive ? std::string{ Utility::FoldCase(std::string_view{keyValue}) } : keyValue; }; std::map newSequenceMap; for (Node& node : m_sequence.value()) { node.Require(Type::Mapping); auto keyValue = getKeyValue(node); newSequenceMap.emplace(std::move(keyValue), std::move(node)); } for (Node& node : other.m_sequence.value()) { node.Require(Type::Mapping); auto keyValue = getKeyValue(node); if (newSequenceMap.find(keyValue) == newSequenceMap.end()) { newSequenceMap.emplace(std::move(keyValue), std::move(node)); } else { newSequenceMap[keyValue].MergeMappingNode(node, caseInsensitive); } } m_sequence.reset(); std::vector newSequence; for (const auto& keyValuePair : newSequenceMap) { newSequence.push_back(keyValuePair.second); } m_sequence = std::move(newSequence); } void Node::MergeMappingNode(Node other, bool caseInsensitive) { Require(Type::Mapping); other.Require(Type::Mapping); std::multimap uniques; for (auto& keyValuePair : other.m_mapping.value()) { if (caseInsensitive) { auto node = GetChildNode(keyValuePair.first.as()); if (node.IsNull()) { uniques.emplace(std::move(keyValuePair)); } } else { if (m_mapping->count(keyValuePair.first) == 0) { uniques.emplace(std::move(keyValuePair)); } } } m_mapping->merge(uniques); } Node Load(std::string_view input) { Wrapper::Parser parser(input); Wrapper::Document document = parser.Load(); if (document.HasRoot()) { return document.GetRoot(); } else { return {}; } } Node Load(const std::string& input) { return Load(static_cast(input)); } Node Load(std::istream& input, Utility::SHA256::HashBuffer* hashOut) { Wrapper::Parser parser(input, hashOut); Wrapper::Document document = parser.Load(); if (document.HasRoot()) { return document.GetRoot(); } else { return {}; } } Node Load(const std::filesystem::path& input, Utility::SHA256::HashBuffer* hashOut) { std::ifstream stream(input, std::ios_base::in | std::ios_base::binary); THROW_LAST_ERROR_IF(stream.fail()); return Load(stream, hashOut); } Node Load(const std::filesystem::path& input) { return Load(input, nullptr); } Node Load(const std::filesystem::path& input, Utility::SHA256::HashBuffer& hashOut) { return Load(input, &hashOut); } Document LoadDocument(std::string_view input) { Wrapper::Parser parser(input); Wrapper::Document document = parser.Load(); if (document.HasRoot()) { const Node root = document.GetRoot(); const DocumentSchemaHeader schemaHeader = ExtractSchemaHeaderFromYaml(parser.GetEncodedInput(), root.Mark().line); return { root, schemaHeader }; } else { // Return an empty root and schema header. return {}; } } Document LoadDocument(const std::string& input) { return LoadDocument(static_cast(input)); } Document LoadDocument(std::istream& input, Utility::SHA256::HashBuffer* hashOut) { Wrapper::Parser parser(input, hashOut); Wrapper::Document document = parser.Load(); if (document.HasRoot()) { const Node root = document.GetRoot(); const DocumentSchemaHeader schemaHeader = ExtractSchemaHeaderFromYaml(parser.GetEncodedInput(), root.Mark().line); return { root, schemaHeader }; } else { // Return an empty root and schema header. return {}; } } Document LoadDocument(const std::filesystem::path& input, Utility::SHA256::HashBuffer* hashOut) { std::ifstream stream(input, std::ios_base::in | std::ios_base::binary); THROW_LAST_ERROR_IF(stream.fail()); return LoadDocument(stream, hashOut); } Document LoadDocument(const std::filesystem::path& input) { return LoadDocument(input, nullptr); } Document LoadDocument(const std::filesystem::path& input, Utility::SHA256::HashBuffer& hashOut) { return LoadDocument(input, &hashOut); } Emitter::Emitter() : m_document(std::make_unique(true)) { SetAllowedInputs(); } Emitter::Emitter(Emitter&&) noexcept = default; Emitter& Emitter::operator=(Emitter&&) noexcept = default; Emitter::~Emitter() = default; Emitter& Emitter::operator<<(EmitterEvent event) { switch (event) { case AppInstaller::YAML::BeginSeq: { CheckInput(InputType::BeginSeq); int id = m_document->AddSequence(); AppendNode(id); m_containers.emplace(id, false); SetAllowedInputsForContainer(); break; } case AppInstaller::YAML::EndSeq: CheckInput(InputType::EndSeq); m_containers.pop(); SetAllowedInputsForContainer(); break; case AppInstaller::YAML::BeginMap: { CheckInput(InputType::BeginMap); int id = m_document->AddMapping(); AppendNode(id); m_containers.emplace(id, true); SetAllowedInputsForContainer(); break; } case AppInstaller::YAML::EndMap: CheckInput(InputType::EndMap); m_containers.pop(); SetAllowedInputsForContainer(); break; case AppInstaller::YAML::Key: CheckInput(InputType::Key); m_scalarType = InputType::Key; SetAllowedInputs(); break; case AppInstaller::YAML::Value: CheckInput(InputType::Value); m_scalarType = InputType::Value; SetAllowedInputs(); break; default: THROW_HR(E_UNEXPECTED); } return *this; } Emitter& Emitter::operator<<(std::string_view value) { CheckInput(InputType::Scalar); int id = m_document->AddScalar(value, m_scalarStyle.value_or(ScalarStyle::Any)); m_scalarStyle = std::nullopt; if (!m_scalarType) { // Part of a sequence AppendNode(id); // No change to allowed inputs } else if (m_scalarType.value() == InputType::Key) { m_keyId = id; m_scalarType = std::nullopt; SetAllowedInputs(); } else if (m_scalarType.value() == InputType::Value) { // Mapping pair complete AppendNode(id); m_scalarType = std::nullopt; SetAllowedInputsForContainer(); } else { THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE); } return *this; } Emitter& Emitter::operator<<(int64_t value) { std::ostringstream stream; stream << value; return operator<<(stream.str()); } Emitter& Emitter::operator<<(int value) { std::ostringstream stream; stream << value; return operator<<(stream.str()); } Emitter& Emitter::operator<<(bool value) { return operator<<(value ? "true"sv : "false"sv); } Emitter& Emitter::operator<<(ScalarStyle style) { m_scalarStyle = style; // Because without this you get a C26815... (void)0; return *this; } std::string Emitter::str() { std::ostringstream stream; Wrapper::Emitter emitter(stream); emitter.Dump(*m_document); emitter.Flush(); return stream.str(); } void Emitter::Emit(std::ostream& out) { Wrapper::Emitter emitter(out); emitter.Dump(*m_document); emitter.Flush(); } void Emitter::AppendNode(int id) { if (!m_containers.empty()) { ContainerInfo& ci = m_containers.top(); if (ci.IsMapping) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE, !m_keyId); m_document->AppendMappingPair(ci.Id, m_keyId.value(), id); m_keyId = std::nullopt; } else { m_document->AppendSequenceItem(ci.Id, id); } } } size_t Emitter::GetInputBitmask(InputType type) { return static_cast(1) << static_cast(type); } void Emitter::CheckInput(InputType type) { if ((m_allowedInputs & GetInputBitmask(type)) == 0) { AICLI_LOG(YAML, Error, << "Invalid emitter input [0x" << std::hex << std::setw(2) << std::setfill('0') << GetInputBitmask(type) << "], expected one of [0x" << std::hex << std::setw(2) << std::setfill('0') << m_allowedInputs << "]"); THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE); } } void Emitter::SetAllowedInputsForContainer() { if (m_containers.empty()) { m_allowedInputs = 0; } else { if (m_containers.top().IsMapping) { SetAllowedInputs(); } else { SetAllowedInputs(); } } } } ================================================ FILE: src/AppInstallerSharedLib/YamlWrapper.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include #include "YamlWrapper.h" #include "AppInstallerErrors.h" #include "AppInstallerLogging.h" #include "AppInstallerStrings.h" namespace AppInstaller::YAML::Wrapper { namespace { Node::Type ConvertNodeType(yaml_node_type_t type) { switch (type) { case YAML_NO_NODE: return Node::Type::None; case YAML_SCALAR_NODE: return Node::Type::Scalar; case YAML_SEQUENCE_NODE: return Node::Type::Sequence; case YAML_MAPPING_NODE: return Node::Type::Mapping; } THROW_HR(E_UNEXPECTED); } Exception::Type ConvertErrorType(yaml_error_type_t type) { switch (type) { case YAML_NO_ERROR: return Exception::Type::None; case YAML_MEMORY_ERROR: return Exception::Type::Memory; case YAML_READER_ERROR: return Exception::Type::Reader; case YAML_SCANNER_ERROR: return Exception::Type::Scanner; case YAML_PARSER_ERROR: return Exception::Type::Parser; case YAML_COMPOSER_ERROR: return Exception::Type::Composer; case YAML_WRITER_ERROR: return Exception::Type::Writer; case YAML_EMITTER_ERROR: return Exception::Type::Emitter; } THROW_HR(E_UNEXPECTED); } Mark ConvertMark(const yaml_mark_t& mark) { return { mark.line + 1, mark.column + 1 }; } std::string ConvertYamlString(yaml_char_t* string, const yaml_mark_t& mark, size_t length = std::string::npos) { std::string_view resultView; if (length == std::string::npos) { resultView = { reinterpret_cast(string) }; } else { resultView = { reinterpret_cast(string), length }; } size_t invalidCharacter = Utility::FindControlCodeToConvert(resultView); if (invalidCharacter != std::string::npos) { THROW_EXCEPTION(Exception(Exception::Type::Policy, "unsupported control character", ConvertMark(mark))); } return std::string{ resultView }; } std::string ConvertScalarToString(yaml_node_t* node, const yaml_mark_t& mark) { return ConvertYamlString(node->data.scalar.value, mark, node->data.scalar.length); } yaml_scalar_style_t ConvertStyle(ScalarStyle style) { switch (style) { case ScalarStyle::Any: return yaml_scalar_style_t::YAML_ANY_SCALAR_STYLE; case ScalarStyle::Plain: return yaml_scalar_style_t::YAML_PLAIN_SCALAR_STYLE; case ScalarStyle::SingleQuoted: return yaml_scalar_style_t::YAML_SINGLE_QUOTED_SCALAR_STYLE; case ScalarStyle::DoubleQuoted: return yaml_scalar_style_t::YAML_DOUBLE_QUOTED_SCALAR_STYLE; case ScalarStyle::Literal: return yaml_scalar_style_t::YAML_LITERAL_SCALAR_STYLE; case ScalarStyle::Folded: return yaml_scalar_style_t::YAML_FOLDED_SCALAR_STYLE; default: THROW_HR(E_UNEXPECTED); } } } Document::Document(bool init) : m_token(true) { if (init) { // Initialize with no version directive or tags, and implicit start and end. if (!yaml_document_initialize(&m_document, NULL, NULL, NULL, 1, 1)) { THROW_HR(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED); } } else { memset(&m_document, 0, sizeof(m_document)); } } Document::~Document() { if (m_token) { yaml_document_delete(&m_document); } } bool Document::HasRoot() { return yaml_document_get_root_node(&m_document) != nullptr; } Node Document::GetRoot() { yaml_node_t* root = yaml_document_get_root_node(&m_document); if (!root) { return {}; } Node result(ConvertNodeType(root->type), ConvertYamlString(root->tag, root->start_mark), ConvertMark(root->start_mark)); struct StackItem { StackItem(yaml_node_t* yn, Node* n) : yamlNode(yn), node(n) {} yaml_node_t* yamlNode = nullptr; Node* node = nullptr; size_t childOffset = 0; }; static int YAML_DOCUMENT_NEST_LEVEL_LIMIT = 100; int nestLevel = 0; std::stack resultStack; resultStack.emplace(root, &result); while (!resultStack.empty()) { StackItem& stackItem = resultStack.top(); bool pop = false; switch (stackItem.yamlNode->type) { case YAML_NO_NODE: pop = true; break; case YAML_SCALAR_NODE: stackItem.node->SetScalar( ConvertScalarToString(stackItem.yamlNode, stackItem.yamlNode->start_mark), stackItem.yamlNode->data.scalar.style == YAML_SINGLE_QUOTED_SCALAR_STYLE || stackItem.yamlNode->data.scalar.style == YAML_DOUBLE_QUOTED_SCALAR_STYLE); pop = true; break; case YAML_SEQUENCE_NODE: { if (stackItem.childOffset == 0) { // We've entered the sequence. nestLevel++; } yaml_node_item_t* child = stackItem.yamlNode->data.sequence.items.start + stackItem.childOffset++; if (child < stackItem.yamlNode->data.sequence.items.top) { yaml_node_t* childYamlNode = GetNode(*child); Node& childNode = stackItem.node->AddSequenceNode(ConvertNodeType(childYamlNode->type), ConvertYamlString(childYamlNode->tag, childYamlNode->start_mark), ConvertMark(childYamlNode->start_mark)); resultStack.emplace(childYamlNode, &childNode); } else { // We've reached the end of the sequence pop = true; nestLevel--; } break; } case YAML_MAPPING_NODE: { if (stackItem.childOffset == 0) { // We've entered the mapping. nestLevel++; } yaml_node_pair_t* child = stackItem.yamlNode->data.mapping.pairs.start + stackItem.childOffset++; if (child < stackItem.yamlNode->data.mapping.pairs.top) { yaml_node_t* keyYamlNode = GetNode(child->key); THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY, keyYamlNode->type != YAML_SCALAR_NODE); Node keyNode(ConvertNodeType(keyYamlNode->type), ConvertYamlString(keyYamlNode->tag, keyYamlNode->start_mark), ConvertMark(keyYamlNode->start_mark)); keyNode.SetScalar(ConvertScalarToString(keyYamlNode, keyYamlNode->start_mark)); yaml_node_t* valueYamlNode = GetNode(child->value); Node& childNode = stackItem.node->AddMappingNode(std::move(keyNode), ConvertNodeType(valueYamlNode->type), ConvertYamlString(valueYamlNode->tag, valueYamlNode->start_mark), ConvertMark(valueYamlNode->start_mark)); resultStack.emplace(valueYamlNode, &childNode); } else { // We've reached the end of the mapping pop = true; nestLevel--; } break; } } if (pop) { resultStack.pop(); } THROW_HR_IF_MSG(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED, nestLevel > YAML_DOCUMENT_NEST_LEVEL_LIMIT, "Too many layers of nested nodes."); } return result; } int Document::AddScalar(std::string_view value, ScalarStyle style) { int result = yaml_document_add_scalar(&m_document, NULL, reinterpret_cast(value.data()), static_cast(value.size()), ConvertStyle(style)); THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED, result == 0); return result; } int Document::AddSequence() { int result = yaml_document_add_sequence(&m_document, NULL, YAML_ANY_SEQUENCE_STYLE); THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED, result == 0); return result; } int Document::AddMapping() { int result = yaml_document_add_mapping(&m_document, NULL, YAML_ANY_MAPPING_STYLE); THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED, result == 0); return result; } void Document::AppendSequenceItem(int sequence, int item) { if (!yaml_document_append_sequence_item(&m_document, sequence, item)) { THROW_HR(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED); } } void Document::AppendMappingPair(int mapping, int key, int value) { if (!yaml_document_append_mapping_pair(&m_document, mapping, key, value)) { THROW_HR(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED); } } yaml_node_t* Document::GetNode(yaml_node_item_t index) { yaml_node_t* result = yaml_document_get_node(&m_document, index); THROW_HR_IF(E_BOUNDS, !result); return result; } Parser::Parser(std::string_view input) : m_token(true), m_input(input) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_parser_initialize(&m_parser)); PrepareInput(); yaml_parser_set_input_string(&m_parser, reinterpret_cast(m_input.c_str()), m_input.size()); } Parser::Parser(std::istream& input, Utility::SHA256::HashBuffer* hashOut) : m_token(true) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_parser_initialize(&m_parser)); m_input = Utility::ReadEntireStream(input); if (hashOut) { *hashOut = Utility::SHA256::ComputeHash(reinterpret_cast(m_input.data()), static_cast(m_input.size())); } PrepareInput(); yaml_parser_set_input_string(&m_parser, reinterpret_cast(m_input.c_str()), m_input.size()); } Parser::~Parser() { if (m_token) { yaml_parser_delete(&m_parser); } } Document Parser::Load() { Document result; if (!yaml_parser_load(&m_parser, &result)) { Exception::Type type = ConvertErrorType(m_parser.error); switch (type) { case Exception::Type::Memory: THROW_EXCEPTION(Exception(type)); case Exception::Type::Reader: THROW_EXCEPTION(Exception(type, m_parser.problem, m_parser.problem_offset, m_parser.problem_value)); case Exception::Type::Scanner: case Exception::Type::Parser: case Exception::Type::Composer: THROW_EXCEPTION(Exception(type, m_parser.problem, ConvertMark(m_parser.problem_mark), m_parser.context, ConvertMark(m_parser.context_mark))); default: THROW_EXCEPTION(Exception(type, "An unexpected error type occurred in Parser::Load")); } } return result; } void Parser::PrepareInput() { constexpr char c_utf16LEBOM[2] = { static_cast(0xFF), static_cast(0xFE) }; constexpr char c_utf16BEBOM[2] = { static_cast(0xFE), static_cast(0xFF) }; constexpr char c_utf8BOM[3] = { static_cast(0xEF), static_cast(0xBB), static_cast(0xBF) }; // If input has a BOM, we want to remove it to prevent errors with checking for comments within the input document. // Check for UTF-16 BOMs if (m_input.size() >= sizeof(c_utf16LEBOM) && std::memcmp(m_input.data(), c_utf16LEBOM, sizeof(c_utf16LEBOM)) == 0) { AICLI_LOG(YAML, Verbose, << "Found UTF-16 LE BOM"); yaml_parser_set_encoding(&m_parser, YAML_UTF16LE_ENCODING); // Without the BOM, the encoding must be explicitly set m_input.erase(0, sizeof(c_utf16LEBOM)); // Remove the BOM from the input return; } if (m_input.size() >= sizeof(c_utf16BEBOM) && std::memcmp(m_input.data(), c_utf16BEBOM, sizeof(c_utf16BEBOM)) == 0) { AICLI_LOG(YAML, Verbose, << "Found UTF-16 BE BOM"); yaml_parser_set_encoding(&m_parser, YAML_UTF16BE_ENCODING); // Without the BOM, the encoding must be explicitly set m_input.erase(0, sizeof(c_utf16BEBOM)); // Remove the BOM from the input return; } // Check for UTF-8 BOM if (m_input.size() >= sizeof(c_utf8BOM) && std::memcmp(m_input.data(), c_utf8BOM, sizeof(c_utf8BOM)) == 0) { AICLI_LOG(YAML, Verbose, << "Found UTF-8 BOM"); yaml_parser_set_encoding(&m_parser, YAML_UTF8_ENCODING); // Without the BOM, the encoding must be explicitly set m_input.erase(0, sizeof(c_utf8BOM)); // Remove the BOM from the input return; } // Check for BOM-less UTF-16 LE INT expectedTests = IS_TEXT_UNICODE_ASCII16 | IS_TEXT_UNICODE_STATISTICS | IS_TEXT_UNICODE_CONTROLS; INT testResults = expectedTests; if (IsTextUnicode(m_input.data(), wil::safe_cast(m_input.size()), &testResults) || testResults == expectedTests) { AICLI_LOG(YAML, Verbose, << "Detected UTF-16 LE"); yaml_parser_set_encoding(&m_parser, YAML_UTF16LE_ENCODING); return; } // Check for BOM-less UTF-16 BE expectedTests = IS_TEXT_UNICODE_REVERSE_ASCII16 | IS_TEXT_UNICODE_REVERSE_STATISTICS | IS_TEXT_UNICODE_REVERSE_CONTROLS; testResults = expectedTests; if (IsTextUnicode(m_input.data(), wil::safe_cast(m_input.size()), &testResults) || testResults == expectedTests) { AICLI_LOG(YAML, Verbose, << "Detected UTF-16 BE"); yaml_parser_set_encoding(&m_parser, YAML_UTF16BE_ENCODING); return; } // Check for BOM-less UTF-8 UINT nChars = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, m_input.data(), wil::safe_cast(m_input.size()), NULL, 0); if (nChars > 0 || GetLastError() != ERROR_NO_UNICODE_TRANSLATION) { AICLI_LOG(YAML, Verbose, << "Detected UTF-8"); yaml_parser_set_encoding(&m_parser, YAML_UTF8_ENCODING); return; } // Must be ANSI (Windows-1252 assumed), convert to UTF-8 AICLI_LOG(YAML, Verbose, << "Assuming ANSI Windows-1252"); std::wstring utf16 = Utility::ConvertToUTF16(m_input, 1252); m_input = Utility::ConvertToUTF8(utf16); yaml_parser_set_encoding(&m_parser, YAML_UTF8_ENCODING); } Event::~Event() { if (m_token) { yaml_event_delete(&m_event); } } Event Event::StreamStart() { Event result; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_stream_start_event_initialize(&result, YAML_UTF8_ENCODING)); result.m_token = true; return result; } Event Event::StreamEnd() { Event result; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_stream_end_event_initialize(&result)); result.m_token = true; return result; } Event Event::DocumentStart() { Event result; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_document_start_event_initialize(&result, NULL, NULL, NULL, 1)); result.m_token = true; return result; } Event Event::DocumentEnd() { Event result; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_document_end_event_initialize(&result, 1)); result.m_token = true; return result; } Event Event::SequenceStart() { Event result; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_sequence_start_event_initialize(&result, NULL, NULL, 1, YAML_ANY_SEQUENCE_STYLE)); result.m_token = true; return result; } Event Event::SequenceEnd() { Event result; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_sequence_end_event_initialize(&result)); result.m_token = true; return result; } Event Event::MappingStart() { Event result; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_mapping_start_event_initialize(&result, NULL, NULL, 1, YAML_ANY_MAPPING_STYLE)); result.m_token = true; return result; } Event Event::MappingEnd() { Event result; THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_mapping_end_event_initialize(&result)); result.m_token = true; return result; } Emitter::Emitter(std::ostream& output) : m_token(true), m_outputStream(&output) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED, !yaml_emitter_initialize(&m_emitter)); yaml_emitter_set_output(&m_emitter, StreamWriteHandler, this); yaml_emitter_set_encoding(&m_emitter, YAML_UTF8_ENCODING); } Emitter::~Emitter() { if (m_token) { yaml_emitter_delete(&m_emitter); } } void Emitter::Emit(Event& event) { event.Detach(); if (!yaml_emitter_emit(&m_emitter, &event)) { ThrowError(); } } void Emitter::Emit(Event&& event) { event.Detach(); if (!yaml_emitter_emit(&m_emitter, &event)) { ThrowError(); } } void Emitter::Dump(Document& document) { document.Detach(); if (!yaml_emitter_dump(&m_emitter, &document)) { ThrowError(); } } void Emitter::Flush() { if (!yaml_emitter_flush(&m_emitter)) { ThrowError(); } } int Emitter::StreamWriteHandler( void* data, unsigned char* buffer, size_t size) { Emitter& emitter = *reinterpret_cast(data); try { emitter.m_outputStream->write(reinterpret_cast(buffer), size); } catch (...) { LOG_CAUGHT_EXCEPTION(); return 0; } return 1; } void Emitter::ThrowError() { Exception::Type type = ConvertErrorType(m_emitter.error); switch (type) { case Exception::Type::Memory: THROW_EXCEPTION(Exception(type)); case Exception::Type::Emitter: case Exception::Type::Writer: THROW_EXCEPTION(Exception(type, m_emitter.problem)); default: THROW_EXCEPTION(Exception(type, "An unexpected error type occurred in Emitter")); } } } ================================================ FILE: src/AppInstallerSharedLib/YamlWrapper.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "winget/Yaml.h" #include "AppInstallerLanguageUtilities.h" #include "AppInstallerSHA256.h" #include #include #include namespace AppInstaller::YAML::Wrapper { // A libyaml yaml_document_t. // A parsed document, created by the Parser. struct Document { // Initializes the document. Document(bool init = false); Document(const Document&) = delete; Document& operator=(const Document&) = delete; Document(Document&&) noexcept = default; Document& operator=(Document&&) noexcept = delete; ~Document(); yaml_document_t* operator&() { return &m_document; } // Indicates that the document should not be deleted, as // it has been handed off to the emitter. void Detach() { m_token = false; } // Determines whether the document has a root node. bool HasRoot(); // Gets the root node of the document, if it has one. Node GetRoot(); // Adds a scalar node to the document. int AddScalar(std::string_view value, ScalarStyle style = ScalarStyle::Any); // Adds a sequence node to the document. int AddSequence(); // Adds a mapping node to the document. int AddMapping(); // Appends a node to the end of the sequence. void AppendSequenceItem(int sequence, int item); // Adds a pair to the mapping. void AppendMappingPair(int mapping, int key, int value); private: // Gets the node referenced by the index. yaml_node_t* GetNode(yaml_node_item_t index); DestructionToken m_token; yaml_document_t m_document; }; // A libyaml yaml_parser_t. // The core parser construct for reading bytes directly. struct Parser { Parser(std::string_view input); Parser(std::istream& input, Utility::SHA256::HashBuffer* hashOut = nullptr); Parser(const Parser&) = delete; Parser& operator=(const Parser&) = delete; Parser(Parser&&) noexcept = default; Parser& operator=(Parser&&) noexcept = delete; ~Parser(); yaml_parser_t* operator&() { return &m_parser; } // Loads the next document from the input, if one exists. Document Load(); // Retrieves the input that was used to create the parser with the correct encoding scheme. const std::string& GetEncodedInput() const { return m_input; } private: // Determines the type of encoding in use, transforming the input as necessary. void PrepareInput(); DestructionToken m_token; yaml_parser_t m_parser; std::string m_input; }; // A libyaml yaml_event_t. // The generic event type for. struct Event { Event(const Event&) = delete; Event& operator=(const Event&) = delete; Event(Event&&) noexcept = default; Event& operator=(Event&&) noexcept = delete; ~Event(); yaml_event_t* operator&() { return &m_event; } // Indicates that the event should not be deleted, as // it has been handed off to the emitter. void Detach() { m_token = false; } // Event creation functions. static Event StreamStart(); static Event StreamEnd(); static Event DocumentStart(); static Event DocumentEnd(); static Event SequenceStart(); static Event SequenceEnd(); static Event MappingStart(); static Event MappingEnd(); private: Event() = default; DestructionToken m_token; yaml_event_t m_event = {}; }; // A libyaml yaml_emitter_t. // Allows YAML to be written out. struct Emitter { Emitter(std::ostream& output); Emitter(const Emitter&) = delete; Emitter& operator=(const Emitter&) = delete; Emitter(Emitter&&) noexcept = default; Emitter& operator=(Emitter&&) noexcept = delete; ~Emitter(); yaml_emitter_t* operator&() { return &m_emitter; } // Emits and event. void Emit(Event& event); void Emit(Event&& event); // Dumps a document to the emitter. void Dump(Document& document); // Flushes the emitter. void Flush(); private: static int StreamWriteHandler( void* data, unsigned char* buffer, size_t size); void ThrowError(); DestructionToken m_token; yaml_emitter_t m_emitter; std::ostream* m_outputStream = nullptr; }; } ================================================ FILE: src/AppInstallerSharedLib/packages.config ================================================  ================================================ FILE: src/AppInstallerSharedLib/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/AppInstallerSharedLib/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include #include #include #include #include #include #include #include #define YAML_DECLARE_STATIC #include // TODO: See if we can get down to having just one JSON parser... #include #pragma warning( push ) #pragma warning ( disable : 4458 4100 4702 6031 26439 ) #include #include #include #include #pragma warning( pop ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma warning( push ) #pragma warning ( disable : 6001 6285 6287 6340 6387 6388 26495 28196 ) #include #include #include #include #include #include #include #pragma warning( pop ) #include #include #include #include #include #include #include ================================================ FILE: src/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.vcxproj ================================================ 15.0 {6CB84692-5994-407D-B9BD-9216AF77FE83} Win32Proj AppInstallerTestExeInstaller 10.0.26100.0 10.0.17763.0 true Debug Win32 Release Win32 Debug x64 Release x64 Debug ARM64 Release ARM64 Application true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ NotUsing pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /bigobj /Zi Disabled _DEBUG;%(PreprocessorDefinitions) true true stdcpp17 stdcpp17 MultiThreadedDebugDLL Console false WIN32;%(PreprocessorDefinitions) true stdcpp17 MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) true true true stdcpp17 stdcpp17 stdcpp17 Console true true false ================================================ FILE: src/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files ================================================ FILE: src/AppInstallerTestExeInstaller/main.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include #include #include #include #include #include #include #include using namespace std::filesystem; std::wstring_view RegistrySubkey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"; std::wstring_view DefaultProductID = L"{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}"; std::wstring_view DefaultDisplayName = L"AppInstallerTestExeInstaller"; std::wstring_view DefaultDisplayVersion = L"1.0.0.0"; std::wstring_view DscSubDirectoryName = L"SubDirectory"; void WriteModifyRepairScript(std::wofstream& script, const path& repairCompletedTextFilePath, bool isModifyScript) { std::wstring scriptName = isModifyScript ? L"Modify" : L"Uninstaller"; script << L" if /I \"%%A\"==\"/repair\" (\n" << L" ECHO " << scriptName << L" Repair operation for AppInstallerTestExeInstaller.exe completed successfully > \"" << repairCompletedTextFilePath.wstring() << "\"\n" << L" ECHO " << scriptName << L" Repair operation for AppInstallerTestExeInstaller.exe completed successfully\n" << L" EXIT /B 0\n" << L" ) else if /I \"%%A\"==\"/r\" (\n" << L" ECHO " << scriptName << L" Repair operation for AppInstallerTestExeInstaller.exe completed successfully > \"" << repairCompletedTextFilePath.wstring() << "\"\n" << L" ECHO " << scriptName << L" Repair operation for AppInstallerTestExeInstaller.exe completed successfully\n" << L" EXIT /B 0\n" << L" )"; } void WriteModifyUninstallScript(std::wofstream& script) { script << L" else if /I \"%%A\"==\"/uninstall\" (\n" << L" call UninstallTestExe.bat\n" << L" EXIT /B 0\n" << L" ) else if /I \"%%A\"==\"/X\" (\n" << L" call UninstallTestExe.bat\n" << L" EXIT /B 0\n" << L" )\n"; } void WriteModifyInvalidOperationScript(std::wofstream& script) { script << L"echo Invalid operation\n" << L"EXIT /B 1\n"; } void WriteUninstallerScript( std::wofstream& uninstallerScript, const path& uninstallerOutputTextFilePath, const std::wstring& registryKey, std::initializer_list paths) { uninstallerScript << "ECHO. >" << uninstallerOutputTextFilePath << "\n"; uninstallerScript << "ECHO AppInstallerTestExeInstaller.exe uninstalled successfully.\n"; uninstallerScript << "REG DELETE " << registryKey << " /f\n"; for (const auto& path : paths) { std::wstring pathString = path.wstring(); uninstallerScript << "if exist \"" << pathString << "\" del \"" << pathString << "\"\n"; } } path GenerateUninstaller(std::wostream& out, const path& installDirectory, const std::wstring& productID, bool useHKLM) { path uninstallerPath = installDirectory; uninstallerPath /= "UninstallTestExe.bat"; out << "Uninstaller located at path: " << uninstallerPath << std::endl; path uninstallerOutputTextFilePath = installDirectory; uninstallerOutputTextFilePath /= "TestExeUninstalled.txt"; path repairCompletedTextFilePath = installDirectory; repairCompletedTextFilePath /= "TestExeRepairCompleted.txt"; std::wstring registryKey{ useHKLM ? L"HKEY_LOCAL_MACHINE\\" : L"HKEY_CURRENT_USER\\" }; registryKey += RegistrySubkey; if (!productID.empty()) { registryKey += productID; } else { registryKey += DefaultProductID; } std::wofstream uninstallerScript(uninstallerPath); uninstallerScript << "@echo off\n"; uninstallerScript << L"for %%A in (%*) do (\n"; WriteModifyRepairScript(uninstallerScript, repairCompletedTextFilePath, false /*isModifyScript*/); uninstallerScript << ")\n"; WriteUninstallerScript(uninstallerScript, uninstallerOutputTextFilePath, registryKey, { installDirectory / "ModifyTestExe.bat", repairCompletedTextFilePath, installDirectory / "AppInstallerTestResource.exe", installDirectory / "AppInstallerTest.dsc.resource.json", installDirectory / DscSubDirectoryName / "AppInstallerTestResource.exe", installDirectory / DscSubDirectoryName / "AppInstallerTest.dsc.resource.json", }); uninstallerScript.close(); return uninstallerPath; } path GenerateModifyPath(const path& installDirectory) { path modifyScriptPath = installDirectory; modifyScriptPath /= "ModifyTestExe.bat"; path repairCompletedTextFilePath = installDirectory; repairCompletedTextFilePath /= "TestExeRepairCompleted.txt"; std::wofstream modifyScript(modifyScriptPath); modifyScript << L"@echo off\n"; modifyScript << L"for %%A in (%*) do (\n"; WriteModifyRepairScript(modifyScript, repairCompletedTextFilePath, true /*isModifyScript*/); WriteModifyUninstallScript(modifyScript); modifyScript << L")\n"; WriteModifyInvalidOperationScript(modifyScript); modifyScript.close(); return modifyScriptPath; } void GenerateDSCv3ProviderFiles(const path& installDirectory, const std::wstring_view subDirectory) { path dscResourceExecutablePath = installDirectory; if (!subDirectory.empty()) { dscResourceExecutablePath /= subDirectory; std::filesystem::create_directories(dscResourceExecutablePath); } dscResourceExecutablePath /= "AppInstallerTestResource.exe"; WCHAR currentExecutable[MAX_PATH]; GetModuleFileName(nullptr, currentExecutable, MAX_PATH); path currentExecutablePath{ currentExecutable }; copy_file(currentExecutablePath, dscResourceExecutablePath); path dscResourceManifestPath = installDirectory; if (!subDirectory.empty()) { dscResourceManifestPath /= subDirectory; } dscResourceManifestPath /= "AppInstallerTest.dsc.resource.json"; std::wstring DscResourceJsonContent = LR"( { "$schema" : "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json", "description" : "AppInstallerTest dsc Resource.", "export" : { "args" : [ "/DscExport" ], "executable" : "AppInstallerTestResource.exe" }, "get" : { "args" : [ "/DscGet" ], "executable" : "AppInstallerTestResource.exe", "input" : "stdin" }, "set" : { "args" : [ "/DscSet" ] , "executable" : "AppInstallerTestResource.exe", "handlesExist" : true, "implementsPretest" : true, "input" : "stdin", "return" : "state" }, "test" : { "args" : [ "/DscTest" ] , "executable" : "AppInstallerTestResource.exe", "input" : "stdin", "return" : "state" }, "schema": { "embedded": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "AppInstallerTestResource", "description": "App Installer Test Resource", "type": "object", "required": [], "additionalProperties": false, "properties": { "_inDesiredState": { "description": "Indicates whether an instance is in the desired state.", "type": "boolean" }, "data": { "type": "string", "description": "Test data." } } } }, "type" : "AppInstallerTest/TestResource)"; if (!subDirectory.empty()) { DscResourceJsonContent += '.'; DscResourceJsonContent += subDirectory; } DscResourceJsonContent += LR"(", "version" : "1.0.0" } )"; std::wofstream dscResourceJson(dscResourceManifestPath); dscResourceJson << DscResourceJsonContent; dscResourceJson.close(); } void WriteToUninstallRegistry( std::wostream& out, const std::wstring& productID, const path& uninstallerPath, const path& modifyPath, const std::wstring& displayName, const std::wstring& displayVersion, const std::wstring& installLocation, bool useHKLM, bool noRepair, bool noModify) { HKEY hkey; LONG lReg; // String inputs to registry must be of wide char type const wchar_t* publisher = L"Microsoft Corporation"; std::wstring uninstallString = uninstallerPath.wstring(); std::wstring modifyPathString = modifyPath.wstring(); DWORD version = 1; std::wstring registryKey{ RegistrySubkey }; if (!productID.empty()) { registryKey += productID; out << "Product Code overridden to: " << registryKey << std::endl; } else { registryKey += DefaultProductID; out << "Default Product Code used: " << registryKey << std::endl; } lReg = RegCreateKeyEx( useHKLM ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, registryKey.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); if (lReg == ERROR_SUCCESS) { out << "Successfully opened registry key" << std::endl; // Set Display Name Property Value if (LONG res = RegSetValueEx(hkey, L"DisplayName", NULL, REG_SZ, (LPBYTE)displayName.c_str(), (DWORD)(displayName.length() + 1) * sizeof(wchar_t)) != ERROR_SUCCESS) { out << "Failed to write DisplayName value. Error Code: " << res << std::endl; } // Set Display Version Property Value if (LONG res = RegSetValueEx(hkey, L"DisplayVersion", NULL, REG_SZ, (LPBYTE)displayVersion.c_str(), (DWORD)(displayVersion.length() + 1) * sizeof(wchar_t)) != ERROR_SUCCESS) { out << "Failed to write DisplayVersion value. Error Code: " << res << std::endl; } // Set Publisher Property Value if (LONG res = RegSetValueEx(hkey, L"Publisher", NULL, REG_SZ, (LPBYTE)publisher, (DWORD)(wcslen(publisher) + 1) * sizeof(wchar_t)) != ERROR_SUCCESS) { out << "Failed to write Publisher value. Error Code: " << res << std::endl; } // Set UninstallString Property Value if (LONG res = RegSetValueEx(hkey, L"UninstallString", NULL, REG_EXPAND_SZ, (LPBYTE)uninstallString.c_str(), (DWORD)(uninstallString.length() + 1) * sizeof(wchar_t)) != ERROR_SUCCESS) { out << "Failed to write UninstallString value. Error Code: " << res << std::endl; } // Set Version Property Value if (LONG res = RegSetValueEx(hkey, L"Version", NULL, REG_DWORD, (LPBYTE)&version, sizeof(version)) != ERROR_SUCCESS) { out << "Failed to write Version value. Error Code: " << res << std::endl; } // Set InstallLocation Property Value if (LONG res = RegSetValueEx(hkey, L"InstallLocation", NULL, REG_SZ, (LPBYTE)installLocation.c_str(), (DWORD)(installLocation.length() + 1) * sizeof(wchar_t)) != ERROR_SUCCESS) { out << "Failed to write InstallLocation value. Error Code: " << res << std::endl; } // Set ModifyPath Property Value if (LONG res = RegSetValueEx(hkey, L"ModifyPath", NULL, REG_EXPAND_SZ, (LPBYTE)modifyPathString.c_str(), (DWORD)(modifyPathString.length() + 1) * sizeof(wchar_t)) != ERROR_SUCCESS) { out << "Failed to write ModifyPath value. Error Code: " << res << std::endl; } if(noRepair) { // Set NoRepair Property Value DWORD noRepairValue = 1; if (LONG res = RegSetValueEx(hkey, L"NoRepair", NULL, REG_DWORD, (LPBYTE)&noRepairValue, sizeof(noRepairValue)) != ERROR_SUCCESS) { out << "Failed to write NoRepair value. Error Code: " << res << std::endl; } } if(noModify) { // Set NoModify Property Value DWORD noModifyValue = 1; if (LONG res = RegSetValueEx(hkey, L"NoModify", NULL, REG_DWORD, (LPBYTE)&noModifyValue, sizeof(noModifyValue)) != ERROR_SUCCESS) { out << "Failed to write NoModify value. Error Code: " << res << std::endl; } } out << "Write to registry key completed" << std::endl; } else { out << "Key Creation Failed" << std::endl; } RegCloseKey(hkey); } void WriteToFile(const path& filePath, const std::wstringstream& content) { std::wofstream file(filePath, std::ofstream::out); file << content.str(); file.close(); } void HandleRepairOperation(const std::wstring& productID, const std::wstringstream& outContent, bool useHKLM) { path installDirectory; // Open the registry key HKEY hKey; std::wstring registryPath = std::wstring(RegistrySubkey); if (!productID.empty()) { registryPath += productID; } else { registryPath += DefaultProductID; } LONG lReg = RegOpenKeyEx(useHKLM ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, registryPath.c_str(), 0, KEY_READ, &hKey); if (lReg == ERROR_SUCCESS) { // Query the value of the InstallLocation wchar_t regInstallLocation[MAX_PATH]; DWORD bufferSize = sizeof(regInstallLocation); lReg = RegQueryValueEx(hKey, L"InstallLocation", NULL, NULL, (LPBYTE)regInstallLocation, &bufferSize); if (lReg == ERROR_SUCCESS) { // Convert the InstallLocation to a path installDirectory = std::wstring(regInstallLocation); } // Close the registry key RegCloseKey(hKey); if(installDirectory.empty()) { // We could not find the install location, so we cannot repair return; } } else { // We could not find the uninstall APR registry key, so we cannot repair return; } path outFilePath = installDirectory; outFilePath /= "TestExeRepairCompleted.txt"; WriteToFile(outFilePath, outContent); } void HandleInstallationOperation( std::wostream& out, const path& installDirectory, const std::wstringstream& outContent, const std::wstring& productCode, bool useHKLM, const std::wstring& displayName, const std::wstring& displayVersion, bool noRepair, bool noModify, bool generateDscResourceFiles) { path outFilePath = installDirectory; outFilePath /= "TestExeInstalled.txt"; std::wofstream file(outFilePath, std::ofstream::out); file << outContent.str(); file.close(); if (generateDscResourceFiles) { GenerateDSCv3ProviderFiles(installDirectory, {}); GenerateDSCv3ProviderFiles(installDirectory, DscSubDirectoryName); } path uninstallerPath = GenerateUninstaller(out, installDirectory, productCode, useHKLM); path modifyPath = GenerateModifyPath(installDirectory); WriteToUninstallRegistry(out, productCode, uninstallerPath, modifyPath, displayName, displayVersion, installDirectory.wstring(), useHKLM, noRepair, noModify); } // The installer prints all args to an output file and writes to the Uninstall registry key int wmain(int argc, const wchar_t** argv) { path installDirectory = temp_directory_path(); std::wstringstream outContent; std::wstring productCode; std::wstring displayName; std::wstring displayVersion; std::wstring aliasToExecute; std::wstring aliasArguments; bool useHKLM = false; bool noOperation = false; int exitCode = 0; bool isRepair = false; bool noRepair = false; bool noModify = false; bool generateDscResourceFiles = false; // Output to cout by default, but swap to a file if requested std::wostream* out = &std::wcout; std::wofstream logFile; for (int i = 1; i < argc; i++) { outContent << argv[i] << ' '; // Supports custom install path. if (_wcsicmp(argv[i], L"/InstallDir") == 0) { if (++i < argc) { installDirectory = argv[i]; std::filesystem::create_directories(installDirectory); outContent << argv[i] << ' '; } } // Supports custom exit code else if (_wcsicmp(argv[i], L"/ExitCode") == 0) { if (++i < argc) { exitCode = static_cast(std::stoll(argv[i], 0, 0)); outContent << argv[i] << ' '; } } // Supports custom product code ID else if (_wcsicmp(argv[i], L"/ProductID") == 0) { if (++i < argc) { productCode = argv[i]; outContent << argv[i] << ' '; } } // Supports custom DisplayName else if (_wcsicmp(argv[i], L"/DisplayName") == 0) { if (++i < argc) { displayName = argv[i]; outContent << argv[i] << ' '; } } // Supports custom version else if (_wcsicmp(argv[i], L"/Version") == 0) { if (++i < argc) { displayVersion = argv[i]; outContent << argv[i] << ' '; } } // Supports log file else if (_wcsicmp(argv[i], L"/LogFile") == 0) { if (++i < argc) { logFile = std::wofstream(argv[i], std::wofstream::out | std::wofstream::trunc); out = &logFile; outContent << argv[i] << ' '; } } // Writes to HKLM else if (_wcsicmp(argv[i], L"/UseHKLM") == 0) { useHKLM = true; } // Executes a command alias during installation else if (_wcsicmp(argv[i], L"/AliasToExecute") == 0) { if (++i < argc) { aliasToExecute = argv[i]; outContent << argv[i] << ' '; } } // Additional arguments to include when executing the command alias during installation else if (_wcsicmp(argv[i], L"/AliasArguments") == 0) { if (++i < argc) { aliasArguments = argv[i]; outContent << argv[i] << ' '; } } // Supports /repair and /r to emulate repair operation using installer. else if (_wcsicmp(argv[i], L"/repair") == 0 || _wcsicmp(argv[i], L"/r") == 0) { isRepair = true; } else if (_wcsicmp(argv[i], L"/NoRepair") == 0) { noRepair = true; } else if (_wcsicmp(argv[i], L"/NoModify") == 0) { noModify = true; } // Returns the success exit code to emulate being invoked by another caller. else if (_wcsicmp(argv[i], L"/NoOperation") == 0) { noOperation = true; } // Also output dsc resource files else if (_wcsicmp(argv[i], L"/GenerateDscResourceFiles") == 0) { generateDscResourceFiles = true; } // Dsc resource get else if (_wcsicmp(argv[i], L"/DscGet") == 0) { std::cout << R"({"data":"TestData"})" << std::endl; return 0; } // Dsc resource set else if (_wcsicmp(argv[i], L"/DscSet") == 0) { std::cout << R"({"_inDesiredState":true})" << std::endl; return 0; } // Dsc resource test else if (_wcsicmp(argv[i], L"/DscTest") == 0) { std::cout << R"({"_inDesiredState":true})" << std::endl; return 0; } // Dsc resource export else if (_wcsicmp(argv[i], L"/DscExport") == 0) { std::cout << R"({"data":"TestData"})" << std::endl; return 0; } } if (noOperation) { return exitCode; } if (!aliasToExecute.empty()) { SHELLEXECUTEINFOW execInfo = { 0 }; execInfo.cbSize = sizeof(execInfo); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS; execInfo.lpFile = aliasToExecute.c_str(); if (!aliasArguments.empty()) { execInfo.lpParameters = aliasArguments.c_str(); } execInfo.nShow = SW_SHOW; if (!ShellExecuteExW(&execInfo) || !execInfo.hProcess) { return -1; } } if (displayName.empty()) { displayName = DefaultDisplayName; } if (displayVersion.empty()) { displayVersion = DefaultDisplayVersion; } path outFilePath = installDirectory; if (isRepair) { outContent << L"\nInstaller Repair operation for AppInstallerTestExeInstaller.exe completed successfully."; HandleRepairOperation(productCode, outContent, useHKLM); } else { HandleInstallationOperation(*out, installDirectory, outContent, productCode, useHKLM, displayName, displayVersion, noRepair, noModify, generateDscResourceFiles); } return exitCode; } ================================================ FILE: src/AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.vdproj ================================================ "DeployProject" { "VSVersion" = "3:800" "ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}" "IsWebType" = "8:FALSE" "ProjectName" = "8:AppInstallerTestMsiInstaller" "LanguageId" = "3:1033" "CodePage" = "3:1252" "UILanguageId" = "3:1033" "SccProjectName" = "8:" "SccLocalPath" = "8:" "SccAuxPath" = "8:" "SccProvider" = "8:" "Hierarchy" { } "Configurations" { "Debug" { "DisplayName" = "8:Debug" "IsDebugOnly" = "11:TRUE" "IsReleaseOnly" = "11:FALSE" "OutputFilename" = "8:Debug\\AppInstallerTestMsiInstaller.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" "Compression" = "3:2" "SignOutput" = "11:FALSE" "CertificateFile" = "8:" "PrivateKeyFile" = "8:" "TimeStampServer" = "8:" "InstallerBootstrapper" = "3:2" "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" { "Enabled" = "11:TRUE" "PromptEnabled" = "11:TRUE" "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" "Items" { "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" { "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" "ProductCode" = "8:.NETFramework,Version=v4.7.2" } } } } "Release" { "DisplayName" = "8:Release" "IsDebugOnly" = "11:FALSE" "IsReleaseOnly" = "11:TRUE" "OutputFilename" = "8:Release\\AppInstallerTestMsiInstaller.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" "Compression" = "3:2" "SignOutput" = "11:FALSE" "CertificateFile" = "8:" "PrivateKeyFile" = "8:" "TimeStampServer" = "8:" "InstallerBootstrapper" = "3:2" "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" { "Enabled" = "11:TRUE" "PromptEnabled" = "11:TRUE" "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" "Items" { "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" { "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" "ProductCode" = "8:.NETFramework,Version=v4.7.2" } } } } } "Deployable" { "CustomAction" { } "DefaultFeature" { "Name" = "8:DefaultFeature" "Title" = "8:" "Description" = "8:" } "ExternalPersistence" { "LaunchCondition" { } } "File" { } "FileType" { } "Folder" { "{3C67513D-01DD-4637-8A68-80971EB9504F}:_3FD954FA64934D1C9D4F66157C4603AF" { "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]" "Name" = "8:#1925" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:TARGETDIR" "Folders" { } } "{1525181F-901A-416C-8A58-119130FE478E}:_7CE3351BC05D486393C4720064B75896" { "Name" = "8:#1916" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:DesktopFolder" "Folders" { } } "{1525181F-901A-416C-8A58-119130FE478E}:_9537533CAD3E4467B320694E22A39C4C" { "Name" = "8:#1919" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:ProgramMenuFolder" "Folders" { } } } "LaunchCondition" { } "Locator" { } "MsiBootstrapper" { "LangId" = "3:1033" "RequiresElevation" = "11:FALSE" } "Product" { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:AppInstallerTestMsiInstaller" "ProductCode" = "8:{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}" "PackageCode" = "8:{B21B22D8-5E23-401C-84FA-EE074DB23F8F}" "UpgradeCode" = "8:{B9CF9DD5-D46F-4CE0-BFC9-633BF9D3A6F4}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:FALSE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:FALSE" "ProductVersion" = "8:1.0.0" "Manufacturer" = "8:AppInstallerCLI" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" "Title" = "8:AppInstallerTestMsiInstaller" "Subject" = "8:" "ARPCONTACT" = "8:AppInstallerCLI" "Keywords" = "8:" "ARPCOMMENTS" = "8:" "ARPURLINFOABOUT" = "8:" "ARPPRODUCTICON" = "8:" "ARPIconIndex" = "3:0" "SearchPath" = "8:" "UseSystemSearchPath" = "11:TRUE" "TargetPlatform" = "3:0" "PreBuildEvent" = "8:" "PostBuildEvent" = "8:" "RunPostBuildEvent" = "3:0" } "Registry" { "HKLM" { "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_951AD5F7C8424CCAB911C2469F382BA0" { "Name" = "8:Software" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_1245A15ED08E4D31AC11FE6C6AF43AA6" { "Name" = "8:[Manufacturer]" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { } "Values" { } } } "Values" { } } } } "HKCU" { "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_A0E8E016B459429AB33F8A8041B409CF" { "Name" = "8:Software" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_6F792619739B49D49348459164023C26" { "Name" = "8:[Manufacturer]" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { } "Values" { } } } "Values" { } } } } "HKCR" { "Keys" { } } "HKU" { "Keys" { } } "HKPU" { "Keys" { } } } "Sequences" { } "Shortcut" { } "UserInterface" { "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_07F5BD380D034D2788D0E26EC943AF9E" { "Name" = "8:#1900" "Sequence" = "3:2" "Attributes" = "3:1" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_0F2C662EB54446EEBB450AB41783B804" { "Sequence" = "3:300" "DisplayName" = "8:Confirm Installation" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminConfirmDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_5B669F9D2835440A97450ED9A579EBB0" { "Sequence" = "3:100" "DisplayName" = "8:Welcome" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminWelcomeDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "CopyrightWarning" { "Name" = "8:CopyrightWarning" "DisplayName" = "8:#1002" "Description" = "8:#1102" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1202" "DefaultValue" = "8:#1202" "UsePlugInResources" = "11:TRUE" } "Welcome" { "Name" = "8:Welcome" "DisplayName" = "8:#1003" "Description" = "8:#1103" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1203" "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_A1B6CF1DE99744E9BA298D9516F4FBBB" { "Sequence" = "3:200" "DisplayName" = "8:Installation Folder" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminFolderDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_31DFC87DB7EB49BBBE91080D4E17EBC3" { "Name" = "8:#1901" "Sequence" = "3:2" "Attributes" = "3:2" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_B0FC86DC72004463B28043005E93933D" { "Sequence" = "3:100" "DisplayName" = "8:Progress" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminProgressDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "ShowProgress" { "Name" = "8:ShowProgress" "DisplayName" = "8:#1009" "Description" = "8:#1109" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_49F7C7303D464C18981485552AA18870" { "Name" = "8:#1901" "Sequence" = "3:1" "Attributes" = "3:2" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_78EF5C34DBDB4BC88F2789CBE1F17043" { "Sequence" = "3:100" "DisplayName" = "8:Progress" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdProgressDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "ShowProgress" { "Name" = "8:ShowProgress" "DisplayName" = "8:#1009" "Description" = "8:#1109" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_68474A3513F54E56B4D451594D7639DC" { "Name" = "8:#1902" "Sequence" = "3:2" "Attributes" = "3:3" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_3EC2CCC643CA4EEB9DAFF092520273EE" { "Sequence" = "3:100" "DisplayName" = "8:Finished" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminFinishedDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_81A3A282324C416DB8EA6A5FABEAD776" { "UseDynamicProperties" = "11:FALSE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdUserInterface.wim" } "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_869386ADC36F4DAE92F694770A481917" { "UseDynamicProperties" = "11:FALSE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdBasicDialogs.wim" } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_9E39D7A2A2BB4185AE8290AF159CBDE8" { "Name" = "8:#1902" "Sequence" = "3:1" "Attributes" = "3:3" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_18EEDC13BA92450EA46E5CFAD31164FE" { "Sequence" = "3:100" "DisplayName" = "8:Finished" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdFinishedDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "UpdateText" { "Name" = "8:UpdateText" "DisplayName" = "8:#1058" "Description" = "8:#1158" "Type" = "3:15" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1258" "DefaultValue" = "8:#1258" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_A1D7AFF21DC048769B063F0ABB6BAD93" { "Name" = "8:#1900" "Sequence" = "3:1" "Attributes" = "3:1" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_6AFCB2A470844F02AC97138D2C4255B5" { "Sequence" = "3:300" "DisplayName" = "8:Confirm Installation" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdConfirmDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_73B000DF31AF44D789DC52D2EB3AC63B" { "Sequence" = "3:200" "DisplayName" = "8:Installation Folder" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdFolderDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "InstallAllUsersVisible" { "Name" = "8:InstallAllUsersVisible" "DisplayName" = "8:#1059" "Description" = "8:#1159" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_E411887483814552AF4BF33BBB4F1B1D" { "Sequence" = "3:100" "DisplayName" = "8:Welcome" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdWelcomeDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "CopyrightWarning" { "Name" = "8:CopyrightWarning" "DisplayName" = "8:#1002" "Description" = "8:#1102" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1202" "DefaultValue" = "8:#1202" "UsePlugInResources" = "11:TRUE" } "Welcome" { "Name" = "8:Welcome" "DisplayName" = "8:#1003" "Description" = "8:#1103" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1203" "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } } } } } } "MergeModule" { } "ProjectOutput" { "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_9892A03006D742BB8C58FDF406605C8B" { "SourcePath" = "8:..\\x86\\Release\\AppInstallerTestExeInstaller\\AppInstallerTestExeInstaller.exe" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_3FD954FA64934D1C9D4F66157C4603AF" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" "ProjectOutputGroupRegister" = "3:1" "OutputConfiguration" = "8:Release|Win32" "OutputGroupCanonicalName" = "8:Built" "OutputProjectGuid" = "8:{6CB84692-5994-407D-B9BD-9216AF77FE83}" "ShowKeyOutput" = "11:TRUE" "ExcludeFilters" { } } } } } ================================================ FILE: src/AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.wapproj ================================================ 15.0 Debug x86 Release x86 Debug x64 Release x64 Debug ARM64 Release ARM64 Debug AnyCPU Release AnyCPU $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\ 3e2cba31-ceba-4d63-bf52-49c0718e19ea 10.0.26100.0 10.0.17763.0 en-US false ..\AppInstallerTestExeInstaller\AppInstallerTestExeInstaller.vcxproj Always Always Always Always Always Always Always Always Designer ================================================ FILE: src/AppInstallerTestMsixInstaller/Package.appxmanifest ================================================  AppInstallerTestMsixInstaller AppInstallerTest Images\StoreLogo.png ================================================ FILE: src/COMServer/COMServer.vcxitems ================================================  $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true {409cd681-22a4-469d-88ae-cb5e4836e07a} __WRL_DISABLE_STATIC_INITIALIZE__;%(PreprocessorDefinitions) ================================================ FILE: src/CertificateResources/CertificateResources.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define CERTIFICATE_RESOURCE_TYPE 400 #define IDX_CERTIFICATE_STORE_ROOT_1 401 #define IDX_CERTIFICATE_STORE_INTERMEDIATE_1 402 #define IDX_CERTIFICATE_STORE_LEAF_1 403 #define IDX_CERTIFICATE_STORE_ROOT_2 404 #define IDX_CERTIFICATE_STORE_INTERMEDIATE_2 405 #define IDX_CERTIFICATE_STORE_LEAF_2 406 #define IDX_CERTIFICATE_MS_TLS_ECC_ROOT_G2 407 #define IDX_CERTIFICATE_MS_TLS_RSA_ROOT_G2 408 ================================================ FILE: src/CertificateResources/CertificateResources.rc ================================================ // Microsoft Visual C++ generated resource script. // #include "resource.h" #include "CertificateResources.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE 9, 1 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Packages schema // IDX_CERTIFICATE_STORE_ROOT_1 CERTIFICATE_RESOURCE_TYPE "StoreRoot1.cer" IDX_CERTIFICATE_STORE_INTERMEDIATE_1 CERTIFICATE_RESOURCE_TYPE "StoreIntermediate1.cer" IDX_CERTIFICATE_STORE_LEAF_1 CERTIFICATE_RESOURCE_TYPE "StoreLeaf1.cer" IDX_CERTIFICATE_STORE_ROOT_2 CERTIFICATE_RESOURCE_TYPE "StoreRoot2.cer" IDX_CERTIFICATE_STORE_INTERMEDIATE_2 CERTIFICATE_RESOURCE_TYPE "StoreIntermediate2.cer" IDX_CERTIFICATE_STORE_LEAF_2 CERTIFICATE_RESOURCE_TYPE "StoreLeaf2.cer" IDX_CERTIFICATE_MS_TLS_ECC_ROOT_G2 CERTIFICATE_RESOURCE_TYPE "Microsoft_TLS_ECC_Root_G2.crt" IDX_CERTIFICATE_MS_TLS_RSA_ROOT_G2 CERTIFICATE_RESOURCE_TYPE "Microsoft_TLS_RSA_Root_G2.crt" ================================================ FILE: src/CertificateResources/CertificateResources.vcxitems ================================================  $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true {b0bbbd92-943b-408f-b2b2-dbbab4a22d23} %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) ================================================ FILE: src/CertificateResources/CertificateResources.vcxitems.filters ================================================  {ad622b6a-16bb-4e32-921c-f1d6505fc0ed} Certificates Certificates Certificates Certificates Certificates Certificates Certificates Certificates ================================================ FILE: src/CertificateResources/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by CertificateResources.rc // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: src/CodeAnalysis.ruleset ================================================  ================================================ FILE: src/ComInprocTestbed/ComInprocTestbed.manifest ================================================ ================================================ FILE: src/ComInprocTestbed/ComInprocTestbed.vcxproj ================================================ 15.0 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94} Win32Proj ComInprocTestbed 10.0.26100.0 10.0.17763.0 true Debug Win32 Release Win32 Debug x64 Release x64 Debug ARM64 Release ARM64 Application true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false true true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false true true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ false true false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ false true false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false true false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ false true Use pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /bigobj /Zi Disabled _DEBUG;%(PreprocessorDefinitions) true true stdcpp17 stdcpp17 MultiThreadedDebugDLL Console false ComInprocTestbed.manifest ComInprocTestbed.manifest WIN32;%(PreprocessorDefinitions) true stdcpp17 ComInprocTestbed.manifest MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) true true true stdcpp17 stdcpp17 stdcpp17 Console true true false ComInprocTestbed.manifest ComInprocTestbed.manifest ComInprocTestbed.manifest Create Create Create Create Create Create {9ac3c6a4-1875-4d3e-bf9c-c31e81eff6b4} false false true {1cc41a9a-ae66-459d-9210-1e572dd7be69} {2046b5af-666d-4ce8-8d3e-c32c57908a56} false false true true true This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/ComInprocTestbed/ComInprocTestbed.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Source Files Header Files Header Files Header Files ================================================ FILE: src/ComInprocTestbed/PackageManager.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageManager.h" using namespace winrt::Microsoft::Management::Deployment; // Work around the assertions about waiting on an STA thread from C++/WinRT template auto WaitForResult(Operation&& operation) { std::promise promise; auto future = promise.get_future(); operation.Completed([&](const auto&, const auto&) { promise.set_value(); }); future.wait(); return operation.GetResults(); } PackageCatalog Connect(const PackageCatalogReference& reference, std::string_view name) { auto connectResult = reference.Connect(); if (connectResult.Status() != ConnectResultStatus::Ok) { std::cout << "Connecting to " << name << " got: " << static_cast(connectResult.Status()) << " [" << connectResult.ExtendedErrorCode() << "]\n"; return nullptr; } return connectResult.PackageCatalog(); } PackageCatalog ConnectComposite(const PackageManager& packageManager, const TestParameters& testParameters, CompositeSearchBehavior searchBehavior, PackageInstallScope scope = PackageInstallScope::Any) { CreateCompositePackageCatalogOptions options = testParameters.CreateCreateCompositePackageCatalogOptions(); options.InstalledScope(scope); options.CompositeSearchBehavior(searchBehavior); auto sourceName = winrt::to_hstring(testParameters.SourceName); for (PackageCatalogReference catalogRef : packageManager.GetPackageCatalogs()) { if (sourceName.empty() || catalogRef.Info().Name() == sourceName) { options.Catalogs().Append(catalogRef); } } // Inputs are provided for a source that we did not find; add it. if (!sourceName.empty() && !testParameters.SourceURL.empty() && options.Catalogs().Size() == 0) { AddPackageCatalogOptions addPackageCatalogOptions = testParameters.CreateAddPackageCatalogOptions(); addPackageCatalogOptions.Name(sourceName); addPackageCatalogOptions.SourceUri(winrt::to_hstring(testParameters.SourceURL)); addPackageCatalogOptions.TrustLevel(PackageCatalogTrustLevel::Trusted); auto addCatalogResult = WaitForResult(packageManager.AddPackageCatalogAsync(addPackageCatalogOptions)); if (addCatalogResult.Status() != AddPackageCatalogStatus::Ok) { std::cout << "Adding catalog `" << testParameters.SourceName << "` [`" << testParameters.SourceURL << "`] got: " << static_cast(addCatalogResult.Status()) << " [" << addCatalogResult.ExtendedErrorCode() << "]\n"; return nullptr; } // Get the new catalog options.Catalogs().Append(packageManager.GetPackageCatalogByName(sourceName)); } return Connect(packageManager.CreateCompositePackageCatalog(options), "Composite Catalog"); } CatalogPackage FindPackage(const PackageCatalog& compositeCatalog, const TestParameters& testParameters) { PackageMatchFilter filter = testParameters.CreatePackageMatchFilter(); filter.Field(PackageMatchField::Id); filter.Option(PackageFieldMatchOption::EqualsCaseInsensitive); filter.Value(winrt::to_hstring(testParameters.PackageName)); FindPackagesOptions findOptions = testParameters.CreateFindPackagesOptions(); findOptions.Filters().Append(filter); auto findResult = compositeCatalog.FindPackages(findOptions); if (findResult.Status() != FindPackagesResultStatus::Ok) { std::cout << "Finding package " << testParameters.PackageName << " got: " << static_cast(findResult.Status()) << " [" << findResult.ExtendedErrorCode() << "]\n"; return nullptr; } if (findResult.Matches().Size() != 1) { std::cout << "Finding package " << testParameters.PackageName << " got " << findResult.Matches().Size() << " results.\n"; return nullptr; } return findResult.Matches().GetAt(0).CatalogPackage(); } bool UsePackageManager(const TestParameters& testParameters) { PackageManager packageManager = testParameters.CreatePackageManager(); // Force installed cache to be created auto installedCatalogRef = packageManager.GetLocalPackageCatalog(LocalPackageCatalog::InstalledPackages); auto installedCatalog = Connect(installedCatalogRef, "Installed Catalog"); if (!installedCatalog) { return false; } // Force TerminationSignalHandler to be created auto compositeCatalog = ConnectComposite(packageManager, testParameters, CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs); if (!compositeCatalog) { return false; } auto package = FindPackage(compositeCatalog, testParameters); if (!package) { return false; } DownloadOptions downloadOptions = testParameters.CreateDownloadOptions(); auto downloadResult = WaitForResult(packageManager.DownloadPackageAsync(package, downloadOptions)); if (downloadResult.Status() != DownloadResultStatus::Ok) { std::cout << "Downloading package " << testParameters.PackageName << " got: " << static_cast(downloadResult.Status()) << "\n"; return false; } return true; } void InitializePackageManagerGlobals() { PackageManagerSettings settings; settings.SetCallerIdentifier(L"ComInprocTestbed"); settings.SetStateIdentifier(L"ComInprocTestbed"); } void SetUnloadPreference(bool value) { PackageManagerSettings settings; settings.CanUnloadPreference(value); } void SetDisableTerminationSignals(bool value) { PackageManagerSettings settings; settings.TerminationSignalMonitoring(!value); } bool DetectForSystem(const TestParameters& testParameters) { PackageManager packageManager = testParameters.CreatePackageManager(); auto compositeCatalog = ConnectComposite(packageManager, testParameters, CompositeSearchBehavior::RemotePackagesFromAllCatalogs, PackageInstallScope::SystemOrUnknown); if (!compositeCatalog) { winrt::throw_hresult(HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } auto package = FindPackage(compositeCatalog, testParameters); if (!package) { winrt::throw_hresult(HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } return package.DefaultInstallVersion() && package.InstalledVersion(); } bool InstallForSystem(const TestParameters& testParameters) { PackageManager packageManager = testParameters.CreatePackageManager(); auto compositeCatalog = ConnectComposite(packageManager, testParameters, CompositeSearchBehavior::RemotePackagesFromAllCatalogs, PackageInstallScope::SystemOrUnknown); if (!compositeCatalog) { winrt::throw_hresult(HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } auto package = FindPackage(compositeCatalog, testParameters); if (!package) { winrt::throw_hresult(HRESULT_FROM_WIN32(ERROR_INVALID_STATE)); } InstallOptions options = testParameters.CreateInstallOptions(); options.AcceptPackageAgreements(true); options.BypassIsStoreClientBlockedPolicyCheck(true); options.Force(true); options.PackageInstallScope(PackageInstallScope::SystemOrUnknown); auto installResult = WaitForResult(packageManager.InstallPackageAsync(package, options)); if (installResult.Status() != InstallResultStatus::Ok) { std::cout << "Installing package " << testParameters.PackageName << " got: " << static_cast(installResult.Status()) << " [" << installResult.ExtendedErrorCode() << "] [" << installResult.InstallerErrorCode() << "]\n"; return false; } return true; } ================================================ FILE: src/ComInprocTestbed/PackageManager.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include "Tests.h" // Attempts to instantiate all static objects bool UsePackageManager(const TestParameters& testParameters); // Sets up the globals for the test caller. void InitializePackageManagerGlobals(); // Sets the module to prevent it from unloading. void SetUnloadPreference(bool value); // Sets the module to prevent it from listening to termination signals. void SetDisableTerminationSignals(bool value); // Attempts to detect the target package as installed for the system. bool DetectForSystem(const TestParameters& testParameters); // Installs the target package for the system. bool InstallForSystem(const TestParameters& testParameters); ================================================ FILE: src/ComInprocTestbed/Tests.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Tests.h" #include "PackageManager.h" using namespace std::string_view_literals; using namespace winrt::Microsoft::Management::Deployment; namespace { #define ADVANCE_ARG_PARAMETER if (++i >= argc) { winrt::throw_hresult(E_INVALIDARG); } std::string ToLower(std::string_view in) { std::string result(in); std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return result; } #define BEGIN_ENUM_PARSE_FUNC(_type_) \ _type_ Parse ## _type_(const char* input) \ { \ auto lower = ToLower(input); \ if (lower.empty()) {} #define ITEM_ENUM_PARSE_FUNC(_type_, _value_) \ else if (ToLower(#_value_) == lower) \ { \ return _type_ ## :: ## _value_; \ } #define END_ENUM_PARSE_FUNC \ winrt::throw_hresult(E_INVALIDARG); \ } #define BEGIN_ENUM_NAME_FUNC(_type_) \ std::string_view ToString(_type_); \ std::ostream& operator<<(std::ostream& o, _type_ input) { return (o << ToString(input)); } \ std::string_view ToString(_type_ input) \ { \ switch (input) { #define ITEM_ENUM_NAME_FUNC(_type_, _value_) \ case _type_ ## :: ## _value_: return #_value_; #define END_ENUM_NAME_FUNC \ } \ return "Unknown"; \ } BEGIN_ENUM_PARSE_FUNC(ComInitializationType) ITEM_ENUM_PARSE_FUNC(ComInitializationType, STA) ITEM_ENUM_PARSE_FUNC(ComInitializationType, MTA) END_ENUM_PARSE_FUNC BEGIN_ENUM_NAME_FUNC(ComInitializationType) ITEM_ENUM_NAME_FUNC(ComInitializationType, STA) ITEM_ENUM_NAME_FUNC(ComInitializationType, MTA) END_ENUM_NAME_FUNC BEGIN_ENUM_PARSE_FUNC(UnloadBehavior) ITEM_ENUM_PARSE_FUNC(UnloadBehavior, Allow) ITEM_ENUM_PARSE_FUNC(UnloadBehavior, AtUninitialize) ITEM_ENUM_PARSE_FUNC(UnloadBehavior, Never) END_ENUM_PARSE_FUNC BEGIN_ENUM_NAME_FUNC(UnloadBehavior) ITEM_ENUM_NAME_FUNC(UnloadBehavior, Allow) ITEM_ENUM_NAME_FUNC(UnloadBehavior, AtUninitialize) ITEM_ENUM_NAME_FUNC(UnloadBehavior, Never) END_ENUM_NAME_FUNC BEGIN_ENUM_PARSE_FUNC(ActivationType) ITEM_ENUM_PARSE_FUNC(ActivationType, ClassName) ITEM_ENUM_PARSE_FUNC(ActivationType, CoCreateInstance) END_ENUM_PARSE_FUNC BEGIN_ENUM_NAME_FUNC(ActivationType) ITEM_ENUM_NAME_FUNC(ActivationType, ClassName) ITEM_ENUM_NAME_FUNC(ActivationType, CoCreateInstance) END_ENUM_NAME_FUNC BOOL CALLBACK CheckForWinGetWindow(HWND hwnd, LPARAM param) { bool* result = reinterpret_cast(param); DWORD windowProcessId = 0; GetWindowThreadProcessId(hwnd, &windowProcessId); if (GetCurrentProcessId() == windowProcessId) { int textLength = GetWindowTextLengthW(hwnd) + 1; std::wstring windowText(textLength, '\0'); textLength = GetWindowTextW(hwnd, &windowText[0], textLength); windowText.resize(textLength); if (L"WingetMessageOnlyWindow"sv == windowText) { *result = true; return FALSE; } } return TRUE; } // Look for the set of well known objects that should be present after we have spun everything up. // Returns true if all well known objects are found in the expected state. bool SearchForWellKnownObjects(bool expectExist, const Snapshot& snapshot) { bool result = true; // Known modules in snapshot bool knownModulesLoaded = snapshot.MicrosoftManagementDeploymentInProcLoaded && snapshot.WindowsPackageManagerLoaded; if (knownModulesLoaded != expectExist) { std::cout << "Known modules were not in expected state [" << (expectExist ? "loaded" : "unloaded") << "]\n"; result = false; } auto coreApplicationProperties = winrt::Windows::ApplicationModel::Core::CoreApplication::Properties(); // COM statics for (std::wstring_view item : { L"WindowsPackageManager.CachedInstalledIndex"sv, L"WindowsPackageManager.TerminationSignalHandler"sv, }) { bool present = coreApplicationProperties.HasKey(item); if (present != expectExist) { std::cout << "CoreApplication property `" << winrt::to_string(item) << "` was not in expected state [" << (expectExist ? "should exist" : "should not exist") << "]\n"; result = false; } } return result; } // Look for the set of termination signal monitoring objects that should be present after we have spun everything up. // Returns true if all objects are found in the expected state. bool SearchForTerminationSignalObjects(bool expectExist) { bool result = true; // Shutdown monitoring window bool foundWindow = false; EnumWindows(CheckForWinGetWindow, reinterpret_cast(&foundWindow)); if (foundWindow != expectExist) { std::cout << "WinGet Window `WingetMessageOnlyWindow` was not in expected state [" << (expectExist ? "should exist" : "should not exist") << "]\n"; result = false; } return result; } std::string GetBytesString(SIZE_T bytes) { constexpr SIZE_T s_kilo = 1024; constexpr std::string_view s_sizes = "BKMG"sv; size_t i = 0; while ((i + 1) < s_sizes.size() && bytes > s_kilo) { bytes /= s_kilo; ++i; } return std::to_string(bytes) + s_sizes[i]; } const CLSID CLSID_PackageManager = { 0x2DDE4456, 0x64D9, 0x4673, 0x8F, 0x7E, 0xA4, 0xF1, 0x9A, 0x2E, 0x6C, 0xC3 }; // 2DDE4456-64D9-4673-8F7E-A4F19A2E6CC3 const CLSID CLSID_FindPackagesOptions = { 0x96B9A53A, 0x9228, 0x4DA0, 0xB0, 0x13, 0xBB, 0x1B, 0x20, 0x31, 0xAB, 0x3D }; // 96B9A53A-9228-4DA0-B013-BB1B2031AB3D const CLSID CLSID_CreateCompositePackageCatalogOptions = { 0x768318A6, 0x2EB5, 0x400D, 0x84, 0xD0, 0xDF, 0x35, 0x34, 0xC3, 0x0F, 0x5D }; // 768318A6-2EB5-400D-84D0-DF3534C30F5D const CLSID CLSID_InstallOptions = { 0xE2AF3BA8, 0x8A88, 0x4766, 0x9D, 0xDA, 0xAE, 0x40, 0x13, 0xAD, 0xE2, 0x86 }; // E2AF3BA8-8A88-4766-9DDA-AE4013ADE286 const CLSID CLSID_UninstallOptions = { 0x869CB959, 0xEB54, 0x425C, 0xA1, 0xE4, 0x1A, 0x1C, 0x29, 0x1C, 0x64, 0xE9 }; // 869CB959-EB54-425C-A1E4-1A1C291C64E9 const CLSID CLSID_PackageMatchFilter = { 0x57DC8962, 0x7343, 0x42CD, 0xB9, 0x1C, 0x04, 0xF6, 0xA2, 0x5D, 0xB1, 0xD0 }; // 57DC8962-7343-42CD-B91C-04F6A25DB1D0 const CLSID CLSID_PackageManagerSettings = { 0x80CF9D63, 0x5505, 0x4342, 0xB9, 0xB4, 0xBB, 0x87, 0x89, 0x5C, 0xA8, 0xBB }; // 80CF9D63-5505-4342-B9B4-BB87895CA8BB const CLSID CLSID_DownloadOptions = { 0x4288DF96, 0xFDC9, 0x4B68, 0xB4, 0x03, 0x19, 0x3D, 0xBB, 0xF5, 0x6A, 0x24 }; // 4288DF96-FDC9-4B68-B403-193DBBF56A24 const CLSID CLSID_AuthenticationArguments = { 0x8D593114, 0x1CF1, 0x43B9, 0x87, 0x22, 0x4D, 0xBB, 0x30, 0x10, 0x32, 0x96 }; // 8D593114-1CF1-43B9-8722-4DBB30103296 const CLSID CLSID_RepairOptions = { 0x30c024c4, 0x852c, 0x4dd4, 0x98, 0x10, 0x13, 0x48, 0xc5, 0x1e, 0xf9, 0xbb }; // {30C024C4-852C-4DD4-9810-1348C51EF9BB} const CLSID CLSID_AddPackageCatalogOptions = { 0x24e6f1fa, 0xe4c3, 0x4acd, 0x96, 0x5d, 0xdf, 0x21, 0x3f, 0xd5, 0x8f, 0x15 }; // {24E6F1FA-E4C3-4ACD-965D-DF213FD58F15} const CLSID CLSID_RemovePackageCatalogOptions = { 0x1125d3a6, 0xe2ce, 0x479a, 0x91, 0xd5, 0x71, 0xa3, 0xf6, 0xf8, 0xb0, 0xb }; // {1125D3A6-E2CE-479A-91D5-71A3F6F8B00B} template T CreatePackageManagerObject(ActivationType activationType, const CLSID& clsid) { if (ActivationType::ClassName == activationType) { return T{}; } else if (ActivationType::CoCreateInstance == activationType) { return winrt::create_instance(clsid); } winrt::throw_hresult(E_UNEXPECTED); } } TestParameters::TestParameters(int argc, const char** argv) { for (int i = 0; i < argc; ++i) { if ("-test"sv == argv[i]) { ADVANCE_ARG_PARAMETER TestToRun = ToLower(argv[i]); } else if ("-com"sv == argv[i]) { ADVANCE_ARG_PARAMETER ComInit = ParseComInitializationType(argv[i]); } else if ("-leak-com"sv == argv[i]) { LeakCOM = true; } else if ("-itr"sv == argv[i]) { ADVANCE_ARG_PARAMETER Iterations = atoi(argv[i]); } else if ("-pkg"sv == argv[i]) { ADVANCE_ARG_PARAMETER PackageName = argv[i]; } else if ("-src"sv == argv[i]) { ADVANCE_ARG_PARAMETER SourceName = argv[i]; } else if ("-url"sv == argv[i]) { ADVANCE_ARG_PARAMETER SourceURL = argv[i]; } else if ("-unload"sv == argv[i]) { ADVANCE_ARG_PARAMETER UnloadBehavior = ParseUnloadBehavior(argv[i]); } else if ("-activation"sv == argv[i]) { ADVANCE_ARG_PARAMETER ActivationType = ParseActivationType(argv[i]); } else if ("-keep-factories"sv == argv[i]) { SkipClearFactories = true; } else if ("-work-test-sleep"sv == argv[i]) { ADVANCE_ARG_PARAMETER WorkTestSleepInterval = atoi(argv[i]); } else if ("-no-term"sv == argv[i]) { DisableTerminationSignals = true; } } } void TestParameters::OutputDetails() const { std::cout << "Running inproc testbed with:\n" " COM Init : " << ComInit << "\n" " Activate : " << ActivationType << "\n" " Clear : " << std::boolalpha << !SkipClearFactories << "\n" " Leak COM : " << std::boolalpha << LeakCOM << "\n" " Unload : " << UnloadBehavior << "\n" " Expect : " << std::boolalpha << UnloadExpected() << "\n" " Test : " << TestToRun << "\n" " Sleep : " << WorkTestSleepInterval << "\n" " Package : " << PackageName << "\n" " Source : " << SourceName << "\n" " URL : " << SourceURL << "\n" " Passes : " << Iterations << std::endl; } bool TestParameters::InitializeTestState() const { HRESULT hr = S_OK; if (ComInitializationType::STA == ComInit) { hr = RoInitialize(RO_INIT_SINGLETHREADED); } else if (ComInitializationType::MTA == ComInit) { hr = RoInitialize(RO_INIT_MULTITHREADED); } if (FAILED(hr)) { std::cout << "RoInitialize returned " << hr << std::endl; return false; } if (UnloadBehavior::Never == UnloadBehavior || UnloadBehavior::AtUninitialize == UnloadBehavior) { SetUnloadPreference(false); } return true; } bool TestParameters::InitializeIterationState() const { InitializePackageManagerGlobals(); if (DisableTerminationSignals) { SetDisableTerminationSignals(true); } return true; } std::unique_ptr TestParameters::CreateTest() const { if ("unload_check"sv == TestToRun) { return std::make_unique(*this); } else if ("install_detect"sv == TestToRun) { return std::make_unique(*this); } return {}; } void TestParameters::UninitializeTestState() const { if (UnloadBehavior::AtUninitialize == UnloadBehavior) { SetUnloadPreference(true); } if (!LeakCOM) { RoUninitialize(); } } bool TestParameters::UnloadExpected() const { bool shouldUnload = true; if (UnloadBehavior::Never == UnloadBehavior || UnloadBehavior::AtUninitialize == UnloadBehavior || (ActivationType::ClassName == ActivationType && SkipClearFactories)) { shouldUnload = false; } return shouldUnload; } PackageManager TestParameters::CreatePackageManager() const { return CreatePackageManagerObject(ActivationType, CLSID_PackageManager); } CreateCompositePackageCatalogOptions TestParameters::CreateCreateCompositePackageCatalogOptions() const { return CreatePackageManagerObject(ActivationType, CLSID_CreateCompositePackageCatalogOptions); } PackageMatchFilter TestParameters::CreatePackageMatchFilter() const { return CreatePackageManagerObject(ActivationType, CLSID_PackageMatchFilter); } FindPackagesOptions TestParameters::CreateFindPackagesOptions() const { return CreatePackageManagerObject(ActivationType, CLSID_FindPackagesOptions); } DownloadOptions TestParameters::CreateDownloadOptions() const { return CreatePackageManagerObject(ActivationType, CLSID_DownloadOptions); } AddPackageCatalogOptions TestParameters::CreateAddPackageCatalogOptions() const { return CreatePackageManagerObject(ActivationType, CLSID_AddPackageCatalogOptions); } InstallOptions TestParameters::CreateInstallOptions() const { return CreatePackageManagerObject(ActivationType, CLSID_InstallOptions); } Snapshot::Snapshot() { const DWORD processId = GetCurrentProcessId(); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE, processId); // Count threads in this process THREADENTRY32 threadEntry{}; threadEntry.dwSize = sizeof(threadEntry); if (Thread32First(snapshot, &threadEntry)) { do { if (processId == threadEntry.th32OwnerProcessID) { ++ThreadCount; } } while (Thread32Next(snapshot, &threadEntry)); } // Count modules MODULEENTRY32 moduleEntry{}; moduleEntry.dwSize = sizeof(moduleEntry); if (Module32First(snapshot, &moduleEntry)) { do { if (moduleEntry.szModule == L"Microsoft.Management.Deployment.InProc.dll"sv) { MicrosoftManagementDeploymentInProcLoaded = true; } else if (moduleEntry.szModule == L"WindowsPackageManager.dll"sv) { WindowsPackageManagerLoaded = true; } ++ModuleCount; } while (Module32Next(snapshot, &moduleEntry)); } // Get memory stats GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&Memory), sizeof(Memory)); CloseHandle(snapshot); } UnloadAndCheckForLeaks::UnloadAndCheckForLeaks(const TestParameters& parameters) : m_parameters(parameters) { } bool UnloadAndCheckForLeaks::RunIterationWork() { std::cout << "UnloadAndCheckForLeaks::RunIterationWork\n"; return UsePackageManager(m_parameters); } bool UnloadAndCheckForLeaks::RunIterationTest() { std::cout << "UnloadAndCheckForLeaks::RunIterationTest\n"; Snapshot beforeUnload; if (!SearchForWellKnownObjects(true, beforeUnload) || !SearchForTerminationSignalObjects(!m_parameters.DisableTerminationSignals)) { return false; } CoFreeUnusedLibrariesEx(0, 0); Snapshot afterUnload; m_iterationSnapshots.emplace_back(beforeUnload, afterUnload); if (!SearchForWellKnownObjects(!m_parameters.UnloadExpected(), afterUnload) || !SearchForTerminationSignalObjects(!m_parameters.UnloadExpected() && !m_parameters.DisableTerminationSignals)) { return false; } return true; } bool UnloadAndCheckForLeaks::RunFinal() { constexpr std::streamsize s_columnWidth = 5; bool result = true; std::cout << "--- UnloadAndCheckForLeaks results ---\n"; std::cout << std::setfill(' '); // --- Threads --- std::cout << "Thread Count [Initial: " << m_initialSnapshot.ThreadCount << "]\n"; std::cout << "Iteration "; for (size_t i = 0; i < m_iterationSnapshots.size(); ++i) { std::cout << std::setw(s_columnWidth) << (i + 1); } std::cout << '\n'; std::cout << "Pre Unload "; for (const auto& snapshot : m_iterationSnapshots) { std::cout << std::setw(s_columnWidth) << snapshot.first.ThreadCount; } std::cout << '\n'; std::cout << "Post Unload"; for (const auto& snapshot : m_iterationSnapshots) { std::cout << std::setw(s_columnWidth) << snapshot.second.ThreadCount; } std::cout << '\n'; // Look for consistent increase in measured values if (m_iterationSnapshots.size() > 1) { size_t previousValue = m_iterationSnapshots[0].second.ThreadCount; bool consistentIncrease = true; for (size_t i = 1; i < m_iterationSnapshots.size(); ++i) { size_t currentValue = m_iterationSnapshots[i].second.ThreadCount; if (currentValue > previousValue) { previousValue = currentValue; } else { consistentIncrease = false; break; } } if (consistentIncrease) { std::cout << "Post unload thread count shows consistent increase; failing test.\n"; result = false; } } // --- Modules --- std::cout << "Module Count [Initial: " << m_initialSnapshot.ModuleCount << "]\n"; std::cout << "Iteration "; for (size_t i = 0; i < m_iterationSnapshots.size(); ++i) { std::cout << std::setw(s_columnWidth) << (i + 1); } std::cout << '\n'; std::cout << "Pre Unload "; for (const auto& snapshot : m_iterationSnapshots) { std::cout << std::setw(s_columnWidth) << snapshot.first.ModuleCount; } std::cout << '\n'; std::cout << "Post Unload"; for (const auto& snapshot : m_iterationSnapshots) { std::cout << std::setw(s_columnWidth) << snapshot.second.ModuleCount; } std::cout << '\n'; // Look for modules not unloading if (m_parameters.UnloadExpected() && m_iterationSnapshots.size() > 1) { bool noUnloadFound = false; for (size_t i = 0; i < m_iterationSnapshots.size(); ++i) { if (m_iterationSnapshots[i].first.ModuleCount == m_iterationSnapshots[i].second.ModuleCount) { noUnloadFound = true; } } if (noUnloadFound) { std::cout << "Module count did not decrease during at least one iteration; failing test.\n"; result = false; } } // --- Memory --- std::cout << "Private Usage [Initial: " << GetBytesString(m_initialSnapshot.Memory.PrivateUsage) << "]\n"; std::cout << "Iteration "; for (size_t i = 0; i < m_iterationSnapshots.size(); ++i) { std::cout << std::setw(s_columnWidth) << (i + 1); } std::cout << '\n'; std::cout << "Pre Unload "; for (const auto& snapshot : m_iterationSnapshots) { std::cout << std::setw(s_columnWidth) << GetBytesString(snapshot.first.Memory.PrivateUsage); } std::cout << '\n'; std::cout << "Post Unload"; for (const auto& snapshot : m_iterationSnapshots) { std::cout << std::setw(s_columnWidth) << GetBytesString(snapshot.second.Memory.PrivateUsage); } std::cout << '\n'; return result; } InstallForSystem_DetectPresence::InstallForSystem_DetectPresence(const TestParameters& parameters) : m_parameters(parameters) { } bool InstallForSystem_DetectPresence::RunIterationWork() { std::cout << "Before installing, the detection state was: " << std::boolalpha << DetectForSystem(m_parameters) << '\n'; return InstallForSystem(m_parameters); } bool InstallForSystem_DetectPresence::RunIterationTest() { bool result = DetectForSystem(m_parameters); std::cout << "After installing, the detection state was: " << std::boolalpha << result << '\n'; return result; } bool InstallForSystem_DetectPresence::RunFinal() { return true; } ================================================ FILE: src/ComInprocTestbed/Tests.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include // Represents a test that will be performed. struct ITest { virtual ~ITest() = default; // Runs an iteration of the work. Performed at the beginning of the iteration. virtual bool RunIterationWork() = 0; // Runs an iteration of the test. Performed at the end of the iteration. virtual bool RunIterationTest() = 0; // Performs the final test validation. virtual bool RunFinal() = 0; }; enum class ComInitializationType { STA, MTA, }; enum class UnloadBehavior { Allow, AtUninitialize, Never, }; enum class ActivationType { ClassName, CoCreateInstance, }; // Test parameters from command line struct TestParameters { TestParameters(int argc, const char** argv); void OutputDetails() const; bool InitializeTestState() const; bool InitializeIterationState() const; std::unique_ptr CreateTest() const; void UninitializeTestState() const; // Determines if we expect COM to unload the module based on inputs. bool UnloadExpected() const; winrt::Microsoft::Management::Deployment::PackageManager CreatePackageManager() const; winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions() const; winrt::Microsoft::Management::Deployment::PackageMatchFilter CreatePackageMatchFilter() const; winrt::Microsoft::Management::Deployment::FindPackagesOptions CreateFindPackagesOptions() const; winrt::Microsoft::Management::Deployment::DownloadOptions CreateDownloadOptions() const; winrt::Microsoft::Management::Deployment::AddPackageCatalogOptions CreateAddPackageCatalogOptions() const; winrt::Microsoft::Management::Deployment::InstallOptions CreateInstallOptions() const; std::string TestToRun; ComInitializationType ComInit = ComInitializationType::MTA; bool LeakCOM = false; int Iterations = 1; std::string PackageName = "Microsoft.Edit"; std::string SourceName = "winget"; std::string SourceURL; UnloadBehavior UnloadBehavior = UnloadBehavior::Allow; ActivationType ActivationType = ActivationType::ClassName; bool SkipClearFactories = false; DWORD WorkTestSleepInterval = 0; bool DisableTerminationSignals = false; }; // Captures a snapshot of current resource usage. struct Snapshot { Snapshot(); size_t ThreadCount = 0; size_t ModuleCount = 0; bool MicrosoftManagementDeploymentInProcLoaded = false; bool WindowsPackageManagerLoaded = false; PROCESS_MEMORY_COUNTERS_EX2 Memory{}; }; // A test that unloads the COM module and looks for resources that were not released. struct UnloadAndCheckForLeaks : public ITest { UnloadAndCheckForLeaks(const TestParameters& parameters); bool RunIterationWork() override; bool RunIterationTest() override; bool RunFinal() override; private: const TestParameters& m_parameters; Snapshot m_initialSnapshot; std::vector> m_iterationSnapshots; }; // A test that installs the package machine wide and then attempts to detect that it is installed. struct InstallForSystem_DetectPresence : public ITest { InstallForSystem_DetectPresence(const TestParameters& parameters); bool RunIterationWork() override; bool RunIterationTest() override; bool RunFinal() override; private: const TestParameters& m_parameters; }; ================================================ FILE: src/ComInprocTestbed/main.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageManager.h" #include "Tests.h" using namespace std::string_view_literals; int main(int argc, const char** argv) try { const TestParameters testParameters(argc, argv); testParameters.OutputDetails(); if (!testParameters.InitializeTestState()) { return 2; } auto test = testParameters.CreateTest(); for (int i = 0; i < testParameters.Iterations; ++i) { std::cout << "Begin iteration " << (i + 1) << std::endl; if (!testParameters.InitializeIterationState()) { return 2; } if (test && !test->RunIterationWork()) { return 3; } if (!testParameters.SkipClearFactories) { winrt::clear_factory_cache(); } if (testParameters.WorkTestSleepInterval) { Sleep(testParameters.WorkTestSleepInterval); } if (test && !test->RunIterationTest()) { return 4; } std::cout << "Iteration " << (i + 1) << " completed" << std::endl; } if (test && !test->RunFinal()) { return 5; } testParameters.UninitializeTestState(); std::cout << "Tests completed" << std::endl; return 0; } catch (const std::exception& e) { std::cout << "Caught std exception: " << e.what() << std::endl; return 1; } catch (const winrt::hresult_error& hre) { std::cout << "Caught winrt exception: " << winrt::to_string(hre.message()) << std::endl; return 1; } catch (...) { std::cout << "Caught unknown exception" << std::endl; return 1; } ================================================ FILE: src/ComInprocTestbed/packages.config ================================================  ================================================ FILE: src/ComInprocTestbed/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/ComInprocTestbed/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ================================================ FILE: src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj ================================================ Exe net8.0-windows10.0.26100.0 enable enable 10.0.17763.0 x64;x86;arm64 $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ true win-x64;win-x86;win-arm64 win-x64 win-x86 win-arm64 ================================================ FILE: src/ConfigurationRemotingServer/EnvironmentChangeListener.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Runtime.InteropServices; using System.Threading; namespace ConfigurationRemotingServer { internal class EnvironmentChangeListener { // Window message constants private const int WM_SETTINGCHANGE = 0x001A; private const int WM_QUIT = 0x0012; // User32.dll imports [DllImport("user32.dll")] private static extern IntPtr CreateWindowEx( uint dwExStyle, string lpClassName, string lpWindowName, uint dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam); [DllImport("user32.dll")] private static extern bool DestroyWindow(IntPtr hWnd); [DllImport("user32.dll")] private static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] private static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); [DllImport("user32.dll")] private static extern bool PostMessageW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] private static extern bool TranslateMessage(ref MSG lpMsg); [DllImport("user32.dll")] private static extern IntPtr DispatchMessage(ref MSG lpMsg); [DllImport("user32.dll")] private static extern ushort RegisterClass(ref WNDCLASS lpWndClass); [DllImport("kernel32.dll")] private static extern IntPtr GetModuleHandle(string? lpModuleName); // Windows message structure [StructLayout(LayoutKind.Sequential)] public struct MSG { public IntPtr hwnd; public uint message; public IntPtr wParam; public IntPtr lParam; public uint time; public POINT pt; public uint lPrivate; } [StructLayout(LayoutKind.Sequential)] public struct POINT { public int x; public int y; } // Window class structure [StructLayout(LayoutKind.Sequential)] public struct WNDCLASS { public uint style; public IntPtr lpfnWndProc; public int cbClsExtra; public int cbWndExtra; public IntPtr hInstance; public IntPtr hIcon; public IntPtr hCursor; public IntPtr hBackground; [MarshalAs(UnmanagedType.LPStr)] public string? lpszMenuName; [MarshalAs(UnmanagedType.LPStr)] public string lpszClassName; } // Window procedure delegate private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); // Keep a reference to the delegate to prevent it from being garbage collected private static WndProcDelegate? _wndProcDelegate; private IntPtr _hwnd; private Thread _thread; public delegate void EnvironmentChangedEventHandler(); public static event EnvironmentChangedEventHandler? EnvironmentChanged; public EnvironmentChangeListener() { _thread = new Thread(MessageLoop); _thread.Start(); } public void Stop() { if (PostMessageW(_hwnd, WM_QUIT, IntPtr.Zero, IntPtr.Zero)) { _thread.Join(); } } private void MessageLoop() { // Register window class string className = "EnvironmentMonitorClass"; _wndProcDelegate = WndProc; WNDCLASS wndClass = new WNDCLASS { style = 0, lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), cbClsExtra = 0, cbWndExtra = 0, hInstance = GetModuleHandle(null), hIcon = IntPtr.Zero, hCursor = IntPtr.Zero, hBackground = IntPtr.Zero, lpszMenuName = "", lpszClassName = className }; RegisterClass(ref wndClass); // Create hidden window const uint WS_OVERLAPPED = 0x00000000; const uint WS_EX_TOOL_WINDOW = 0x00000080; _hwnd = CreateWindowEx( WS_EX_TOOL_WINDOW, className, "Environment Monitor", WS_OVERLAPPED, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, GetModuleHandle(null), IntPtr.Zero); if (_hwnd == IntPtr.Zero) { return; } MSG msg; while (GetMessage(out msg, IntPtr.Zero, 0, 0)) { TranslateMessage(ref msg); DispatchMessage(ref msg); } // Clean up DestroyWindow(_hwnd); } private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { switch (msg) { case WM_SETTINGCHANGE: // Check if this is an environment variable change notification string? msgInfo = Marshal.PtrToStringAnsi(lParam); if (msgInfo == "Environment") { if (EnvironmentChanged != null) { EnvironmentChanged.Invoke(); } } break; } return DefWindowProc(hWnd, msg, wParam, lParam); } } } ================================================ FILE: src/ConfigurationRemotingServer/Program.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Loader; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor; using Microsoft.Management.Configuration.Processor.Helpers; using WinRT; using IConfigurationSetProcessorFactory = global::Microsoft.Management.Configuration.IConfigurationSetProcessorFactory; namespace ConfigurationRemotingServer { ///

/// Custom assembly load context. /// internal class NativeAssemblyLoadContext : AssemblyLoadContext { private static readonly string PackageRootPath; private static readonly NativeAssemblyLoadContext NativeALC = new(); static NativeAssemblyLoadContext() { var self = typeof(NativeAssemblyLoadContext).Assembly; PackageRootPath = Path.Combine( Path.GetDirectoryName(self.Location)!, ".."); } private NativeAssemblyLoadContext() : base("NativeAssemblyLoadContext", isCollectible: false) { } /// /// Handler to resolve unmanaged assemblies. /// /// Assembly load context. /// Assembly name. /// The assembly, null if not in our assembly location. internal static IntPtr ResolvingUnmanagedHandler(Assembly context, string name) { if (name.Equals("WindowsPackageManager.dll", StringComparison.OrdinalIgnoreCase)) { return NativeALC.LoadUnmanagedDll(name); } return IntPtr.Zero; } /// protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { string path = Path.Combine(PackageRootPath, unmanagedDllName); if (File.Exists(path)) { return this.LoadUnmanagedDllFromPath(path); } return IntPtr.Zero; } } internal class Program { private const string CommandLineSectionSeparator = "~~~~~~"; private const string ExternalModulesName = "ExternalModules"; static int Main(string[] args) { // Remove any attached console to prevent modules (or their actions) from writing to our console. FreeConsole(); // Help find WindowsPackageManager.dll AssemblyLoadContext.Default.ResolvingUnmanagedDll += NativeAssemblyLoadContext.ResolvingUnmanagedHandler; string staticsCallback = args[1]; // Listen for setting change message and update PATH if needed. EnvironmentChangeListener.EnvironmentChanged += OnEnvironmentChanged; EnvironmentChangeListener environmentChangeListener = new EnvironmentChangeListener(); try { string completionEventName = args[2]; uint parentProcessId = uint.Parse(args[3]); string processorEngine = args[4]; ConfigurationSet? limitationSet = null; LimitationSetMetadata? limitationSetMetadata = null; // Parse limitation set if applicable. // The format will be: // ~~~~~~ ~~~~~~ // Metadata json format: // { // "path": "C:\full\file\path.yaml" // } // If a limitation set is provided, the processor will be limited // to only work on units defined inside the limitation set. var commandPtr = GetCommandLineW(); var commandStr = Marshal.PtrToStringUni(commandPtr) ?? string.Empty; // In case the limitation set content contains the separator, we'll not use Split method. var firstSeparatorIndex = commandStr.IndexOf(CommandLineSectionSeparator); if (firstSeparatorIndex > 0) { var secondSeparatorIndex = commandStr.IndexOf(CommandLineSectionSeparator, firstSeparatorIndex + CommandLineSectionSeparator.Length); if (secondSeparatorIndex <= 0) { throw new ArgumentException("The input command contains only one separator string."); } // Parse limitation set. byte[] limitationSetBytes = Encoding.UTF8.GetBytes(commandStr.Substring(secondSeparatorIndex + CommandLineSectionSeparator.Length)); MemoryStream memoryStream = new MemoryStream(); memoryStream.Write(limitationSetBytes); memoryStream.Flush(); memoryStream.Seek(0, SeekOrigin.Begin); ConfigurationProcessor processor = new ConfigurationProcessor((IConfigurationSetProcessorFactory?)null); var limitationSetResult = processor.OpenConfigurationSet(memoryStream.AsInputStream()); memoryStream.Close(); if (limitationSetResult.ResultCode != null) { throw limitationSetResult.ResultCode; } limitationSet = limitationSetResult.Set; if (limitationSet == null) { throw new ArgumentException("The limitation set cannot be parsed."); } // Now parse metadata json and update the limitation set limitationSetMetadata = JsonSerializer.Deserialize(commandStr.Substring( firstSeparatorIndex + CommandLineSectionSeparator.Length, secondSeparatorIndex - firstSeparatorIndex - CommandLineSectionSeparator.Length)); if (limitationSetMetadata != null) { limitationSet.Path = limitationSetMetadata.Path; } } IConfigurationSetProcessorFactory factory = CreateFactory(processorEngine, limitationSet, limitationSetMetadata); IObjectReference factoryInterface = MarshalInterface.CreateMarshaler(factory); return WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(0, factoryInterface.ThisPtr, staticsCallback, completionEventName, parentProcessId); } catch (Exception ex) { WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(ex.HResult, IntPtr.Zero, staticsCallback, null, 0); return ex.HResult; } finally { environmentChangeListener.Stop(); } } private static void OnEnvironmentChanged() { PathEnvironmentVariableHandler.UpdatePath(); } private class LimitationSetMetadata { [JsonPropertyName("path")] public string Path { get; set; } = string.Empty; [JsonPropertyName("modulePath")] public string? ModulePath { get; set; } = null; [JsonPropertyName("processorPath")] public string? ProcessorPath { get; set; } = null; } private static IConfigurationSetProcessorFactory CreateFactory(string processorEngine, ConfigurationSet? limitationSet, LimitationSetMetadata? limitationSetMetadata) { switch (processorEngine) { case "pwsh": return CreatePowerShellFactory(limitationSet, limitationSetMetadata); case "dscv3": return CreateDSCv3Factory(limitationSet, limitationSetMetadata); } throw new NotImplementedException($"Processor engine unknown: {processorEngine}"); } private static IConfigurationSetProcessorFactory CreatePowerShellFactory(ConfigurationSet? limitationSet, LimitationSetMetadata? limitationSetMetadata) { PowerShellConfigurationSetProcessorFactory factory = new PowerShellConfigurationSetProcessorFactory(); // Set default properties. var externalModulesPath = GetExternalModulesPath(); if (string.IsNullOrWhiteSpace(externalModulesPath)) { throw new DirectoryNotFoundException("Failed to get ExternalModules."); } // Set as implicit module paths so it will be always included in AdditionalModulePaths factory.ImplicitModulePaths = new List() { externalModulesPath }; factory.ProcessorType = PowerShellConfigurationProcessorType.Hosted; if (limitationSetMetadata != null) { if (limitationSetMetadata.ModulePath != null) { PowerShellConfigurationProcessorLocation parsedLocation = PowerShellConfigurationProcessorLocation.Default; if (Enum.TryParse(limitationSetMetadata.ModulePath, out parsedLocation)) { factory.Location = parsedLocation; } else { factory.Location = PowerShellConfigurationProcessorLocation.Custom; factory.CustomLocation = limitationSetMetadata.ModulePath; } } } // Apply limitation set and thereby disable changing properties. if (limitationSet != null) { factory.LimitationSet = limitationSet; } return factory; } private static IConfigurationSetProcessorFactory CreateDSCv3Factory(ConfigurationSet? limitationSet, LimitationSetMetadata? limitationSetMetadata) { DSCv3ConfigurationSetProcessorFactory factory = new DSCv3ConfigurationSetProcessorFactory(); if (limitationSetMetadata != null) { if (limitationSetMetadata.ProcessorPath != null) { factory.DscExecutablePath = limitationSetMetadata.ProcessorPath; } else { // Require that the path to the DSC executable be presented to the user in limitation mode. // This helps prevent path attacks against an elevated process (as long as the user checks the value). throw new ArgumentNullException("The path to the DSC executable must be supplied in limitation mode."); } } // Apply limitation set and thereby disable changing properties. if (limitationSet != null) { factory.LimitationSet = limitationSet; } return factory; } private static string GetExternalModulesPath() { var currentAssemblyDirectoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (currentAssemblyDirectoryPath != null) { var packageRootPath = Directory.GetParent(currentAssemblyDirectoryPath)?.FullName; if (packageRootPath != null) { var externalModulesPath = Path.Combine(packageRootPath, ExternalModulesName); if (Directory.Exists(externalModulesPath)) { return externalModulesPath; } } } return string.Empty; } [DllImport("WindowsPackageManager.dll")] private static extern int WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization( int result, IntPtr factory, [MarshalAs(UnmanagedType.LPWStr)]string staticsCallback, [MarshalAs(UnmanagedType.LPWStr)]string? completionEventName, uint parentProcessId); [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr GetCommandLineW(); [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool FreeConsole(); } } ================================================ FILE: src/Directory.Build.props ================================================ USE_PROD_CLSIDS;$(WinGetMacros) AICLI_DISABLE_TEST_HOOKS;$(WinGetMacros) WINGET_DISABLE_EXPERIMENTAL_FEATURES;$(WinGetMacros) USE_PROD_WINGET_SERVER;$(WinGetMacros) WINGET_ENABLE_RELEASE_BUILD;$(WinGetMacros) v142 v143 v145 Unicode $(WinGetMacros)%(PreprocessorDefinitions) true %(IgnoreSpecificDefaultLibraries);libucrt.lib %(AdditionalOptions) /defaultlib:ucrt.lib $(WinGetMacros)$(DefineConstants) ================================================ FILE: src/Directory.Packages.props ================================================ true 10.0.26100.53 ================================================ FILE: src/Directory.Solution.props ================================================ ================================================ FILE: src/Get-VcxprojNugetPackageVersions.ps1 ================================================ #Requires -Version 5.1 <# .SYNOPSIS Recursively searches for .vcxproj files and extracts NuGet package versions from associated packages.config files. .DESCRIPTION This script searches directories below the script location for Visual Studio C++ project files (.vcxproj) and their corresponding packages.config files. It extracts all NuGet package references with their versions and generates a comprehensive report. .PARAMETER OutputFormat Specifies the output format: Table, CSV, JSON, or Object. Default is Table. Object format returns PackageInfo objects without printing anything. .PARAMETER ExportPath Optional path to export the results to a file. Not applicable for Object format. .PARAMETER PackageFilter Optional filter to search for specific package names. Supports wildcards. If specified, only packages matching this filter will be included in the results. .EXAMPLE .\Get-VcxprojNugetPackageVersions.ps1 .EXAMPLE .\Get-VcxprojNugetPackageVersions.ps1 -OutputFormat CSV -ExportPath "packages-report.csv" .EXAMPLE .\Get-VcxprojNugetPackageVersions.ps1 -PackageFilter "Microsoft*" .EXAMPLE .\Get-VcxprojNugetPackageVersions.ps1 -PackageFilter "Newtonsoft.Json" -OutputFormat JSON .EXAMPLE $packages = .\Get-VcxprojNugetPackageVersions.ps1 -OutputFormat Object #> [CmdletBinding()] param( [ValidateSet("Table", "CSV", "JSON", "Object")] [string]$OutputFormat = "Table", [string]$ExportPath, [string]$PackageFilter ) # Custom class to hold package information class PackageInfo { [string]$ProjectFile [string]$ProjectName [string]$PackageId [string]$Version [string]$TargetFramework [string]$PackagesConfigPath [bool]$PackagesConfigExists } function Get-VcxprojFiles { param( [string]$SearchPath, [bool]$SuppressOutput = $false ) Write-Verbose "Searching for .vcxproj files in: $SearchPath" try { $vcxprojFiles = Get-ChildItem -Path $SearchPath -Filter "*.vcxproj" -Recurse -ErrorAction SilentlyContinue if (-not $SuppressOutput) { Write-Host "Found $($vcxprojFiles.Count) .vcxproj files" -ForegroundColor Green } return $vcxprojFiles } catch { Write-Warning "Error searching for .vcxproj files: $($_.Exception.Message)" return @() } } function Get-PackagesFromConfig { param( [string]$PackagesConfigPath, [string]$ProjectFile, [string]$ProjectName, [string]$PackageFilter ) $packages = @() if (-not (Test-Path $PackagesConfigPath)) { Write-Verbose "No packages.config found for project: $ProjectName" # If a package filter is specified and there's no packages.config, don't include this project if ($PackageFilter) { Write-Verbose "Package filter specified but no packages.config exists for project: $ProjectName. Excluding from results." return @() } # Return empty package info to indicate no packages.config $packageInfo = [PackageInfo]::new() $packageInfo.ProjectFile = $ProjectFile $packageInfo.ProjectName = $ProjectName $packageInfo.PackageId = "N/A" $packageInfo.Version = "N/A" $packageInfo.TargetFramework = "N/A" $packageInfo.PackagesConfigPath = $PackagesConfigPath $packageInfo.PackagesConfigExists = $false return @($packageInfo) } try { Write-Verbose "Processing packages.config: $PackagesConfigPath" [xml]$packagesXml = Get-Content $PackagesConfigPath -ErrorAction Stop $packageNodes = $packagesXml.packages.package if ($null -eq $packageNodes) { Write-Verbose "No package nodes found in packages.config for project: $ProjectName" return @() } # Handle both single package and multiple packages scenarios if ($packageNodes -is [System.Xml.XmlElement]) { # Single package $packageNodes = @($packageNodes) } foreach ($package in $packageNodes) { # Apply package filter if specified if ($PackageFilter -and $package.id -notlike $PackageFilter) { Write-Verbose "Package '$($package.id)' does not match filter '$PackageFilter', skipping" continue } $packageInfo = [PackageInfo]::new() $packageInfo.ProjectFile = $ProjectFile $packageInfo.ProjectName = $ProjectName $packageInfo.PackageId = $package.id $packageInfo.Version = $package.version $packageInfo.TargetFramework = $package.targetFramework $packageInfo.PackagesConfigPath = $PackagesConfigPath $packageInfo.PackagesConfigExists = $true $packages += $packageInfo } Write-Verbose "Found $($packages.Count) packages in $ProjectName" } catch { Write-Warning "Error parsing packages.config for project '$ProjectName': $($_.Exception.Message)" } return $packages } function Get-ProjectName { param([string]$VcxprojPath) try { [xml]$projectXml = Get-Content $VcxprojPath -ErrorAction Stop # Try to get project name from PropertyGroup $projectNameNode = $projectXml.Project.PropertyGroup.ProjectName | Select-Object -First 1 if ($projectNameNode) { return $projectNameNode } # Fallback to filename without extension return [System.IO.Path]::GetFileNameWithoutExtension($VcxprojPath) } catch { Write-Verbose "Could not parse project file for name, using filename: $($_.Exception.Message)" return [System.IO.Path]::GetFileNameWithoutExtension($VcxprojPath) } } function Export-Results { param( [array]$Results, [string]$Format, [string]$Path ) if (-not $Path) { return } try { switch ($Format) { "CSV" { $Results | Export-Csv -Path $Path -NoTypeInformation Write-Host "Results exported to CSV: $Path" -ForegroundColor Green } "JSON" { $Results | ConvertTo-Json -Depth 3 | Out-File -FilePath $Path -Encoding utf8 Write-Host "Results exported to JSON: $Path" -ForegroundColor Green } default { $Results | Format-Table -AutoSize | Out-File -FilePath $Path -Encoding utf8 Write-Host "Results exported to text file: $Path" -ForegroundColor Green } } } catch { Write-Warning "Failed to export results to '$Path': $($_.Exception.Message)" } } function Show-Summary { param([array]$AllPackages) $projectsWithPackages = $AllPackages | Where-Object { $_.PackagesConfigExists } | Group-Object ProjectFile $projectsWithoutPackages = $AllPackages | Where-Object { -not $_.PackagesConfigExists } | Group-Object ProjectFile Write-Host "`n=== SUMMARY ===" -ForegroundColor Cyan Write-Host "Total .vcxproj files found: $($projectsWithPackages.Count + $projectsWithoutPackages.Count)" Write-Host "Projects with packages.config: $($projectsWithPackages.Count)" -ForegroundColor Green Write-Host "Projects without packages.config: $($projectsWithoutPackages.Count)" -ForegroundColor Yellow Write-Host "Total package references: $(($AllPackages | Where-Object { $_.PackagesConfigExists }).Count)" -ForegroundColor Green } # Main execution try { $scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path # Suppress output messages for Object format if ($OutputFormat -ne "Object") { Write-Host "Starting search from: $scriptPath" -ForegroundColor Cyan Write-Host "Output format: $OutputFormat" -ForegroundColor Cyan if ($PackageFilter) { Write-Host "Package filter: $PackageFilter" -ForegroundColor Cyan } if ($ExportPath) { Write-Host "Export path: $ExportPath" -ForegroundColor Cyan } } # Find all .vcxproj files $vcxprojFiles = Get-VcxprojFiles -SearchPath $scriptPath -SuppressOutput ($OutputFormat -eq "Object") if ($vcxprojFiles.Count -eq 0) { if ($OutputFormat -ne "Object") { Write-Warning "No .vcxproj files found in the search directory." } return @() # Return empty array for Object format } # Process each .vcxproj file $allPackages = @() $processedCount = 0 foreach ($vcxproj in $vcxprojFiles) { $processedCount++ Write-Progress -Activity "Processing .vcxproj files" -Status "$processedCount of $($vcxprojFiles.Count)" -PercentComplete (($processedCount / $vcxprojFiles.Count) * 100) $projectName = Get-ProjectName -VcxprojPath $vcxproj.FullName $packagesConfigPath = Join-Path $vcxproj.DirectoryName "packages.config" $packages = Get-PackagesFromConfig -PackagesConfigPath $packagesConfigPath -ProjectFile $vcxproj.FullName -ProjectName $projectName -PackageFilter $PackageFilter $allPackages += $packages } Write-Progress -Activity "Processing .vcxproj files" -Completed # Display results if ($allPackages.Count -gt 0) { switch ($OutputFormat) { "CSV" { if ($ExportPath) { Export-Results -Results $allPackages -Format "CSV" -Path $ExportPath } else { $allPackages | ConvertTo-Csv -NoTypeInformation | Write-Output } } "JSON" { if ($ExportPath) { Export-Results -Results $allPackages -Format "JSON" -Path $ExportPath } else { $allPackages | ConvertTo-Json -Depth 3 | Write-Output } } "Object" { # Return only packages where PackagesConfigExists is true $packagesWithConfig = $allPackages | Where-Object { $_.PackagesConfigExists } return $packagesWithConfig } default { # Table format Write-Host "`n=== PACKAGE DETAILS ===" -ForegroundColor Cyan # Show the set of packages and versions found across all projects $uniquePackages = $allPackages | Where-Object { $_.PackagesConfigExists } | Group-Object PackageId, Version if ($uniquePackages.Count -gt 0) { $uniquePackages | Format-Table -Property Name, Count -AutoSize } # Output the URL of each package on nuget.org Write-Host "`n=== PACKAGE URLS ===" -ForegroundColor Cyan $uniquePackages = $allPackages | Where-Object { $_.PackagesConfigExists } | Group-Object PackageId foreach ($packageGroup in $uniquePackages) { $packageId = $packageGroup.Name $url = "https://www.nuget.org/packages/$packageId#versions-body-tab" Write-Host " $packageId : $url" -ForegroundColor Yellow } Write-Host "`n=== PROJECT DETAILS ===" -ForegroundColor Cyan # Show projects with packages $packagesWithConfig = $allPackages | Where-Object { $_.PackagesConfigExists } if ($packagesWithConfig.Count -gt 0) { $packagesWithConfig | Format-Table -Property ProjectName, PackageId, Version, TargetFramework -AutoSize } if ($ExportPath) { Export-Results -Results $allPackages -Format $OutputFormat -Path $ExportPath } } } # Show summary (but not for Object format) if ($OutputFormat -ne "Object") { Show-Summary -AllPackages $allPackages } } else { if ($OutputFormat -ne "Object") { Write-Host "No packages found in any .vcxproj files." -ForegroundColor Yellow } } } catch { Write-Error "An error occurred during execution: $($_.Exception.Message)" Write-Error $_.ScriptStackTrace } if ($OutputFormat -ne "Object") { Write-Host "`nScript completed." -ForegroundColor Green } ================================================ FILE: src/IndexCreationTool/IndexCreationTool.csproj ================================================ Exe net8.0 $(SolutionDir)$(Platform)\$(Configuration)\IndexCreationTool\ x64;x86 Content PreserveNewest True ================================================ FILE: src/IndexCreationTool/Program.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace IndexCreationTool { using Microsoft.WinGetSourceCreator; using System; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; using WinGetSourceCreator.Model; class Program { private static void PrintUsage() { Console.WriteLine("Usage: IndexCreationTool.exe -f "); } static int Main(string[] args) { try { string inputFile = string.Empty; for (int i = 0; i < args.Length; i++) { if (args[i] == "-f" && ++i < args.Length) { inputFile = args[i]; } } if (string.IsNullOrEmpty(inputFile) || !File.Exists(inputFile)) { PrintUsage(); throw new ArgumentException("Missing input file"); } WinGetLocalSource.CreateFromLocalSourceFile(inputFile); } catch (Exception e) { PrintUsage(); Console.WriteLine(e.Message); return -1; } return 0; } } } ================================================ FILE: src/LocalhostWebServer/LocalhostWebServer.csproj ================================================ net8.0 $(SolutionDir)$(Platform)\$(Configuration)\LocalhostWebServer\ x64;x86 Content PreserveNewest True ================================================ FILE: src/LocalhostWebServer/Program.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace LocalhostWebServer { using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using System; using System.IO; using Microsoft.Extensions.Configuration; using System.Security.Cryptography.X509Certificates; using System.Text.Json.Serialization; using System.Text.Json; using WinGetSourceCreator.Model; using Microsoft.WinGetSourceCreator; using System.Runtime.InteropServices; public class Program { const string CertificateProviderString = "Microsoft.PowerShell.Security\\Certificate::"; const string StoreLocationCurrentUser = "CurrentUser"; const string StoreLocationLocalMachine = "LocalMachine"; static void Main(string[] args) { IConfiguration config = new ConfigurationBuilder() .AddCommandLine(args) .Build(); Startup.StaticFileRoot = config.GetValue("StaticFileRoot"); Startup.CertPath = config.GetValue("CertPath"); Startup.CertPassword = config.GetValue("CertPassword"); Startup.Port = config.GetValue("Port", 5001); Startup.OutCertFile = config.GetValue("OutCertFile"); Startup.LocalSourceJson = config.GetValue("LocalSourceJson"); Startup.TestDataPath = config.GetValue("TestDataPath"); Startup.ExitBeforeRun = config.GetValue("ExitBeforeRun"); if (string.IsNullOrEmpty(Startup.StaticFileRoot) || string.IsNullOrEmpty(Startup.CertPath)) { Console.WriteLine("Usage: LocalhostWebServer.exe StaticFileRoot= " + "CertPath= CertPassword= "); return; } Directory.CreateDirectory(Startup.StaticFileRoot); if (Startup.CertPath.StartsWith(CertificateProviderString)) { string certPath = Startup.CertPath.Substring(CertificateProviderString.Length); string[] pathParts = certPath.Split('\\'); if (pathParts.Length != 3) { throw new InvalidDataException($"Don't know how to handle: {Startup.CertPath}"); } StoreLocation storeLocation = StoreLocation.CurrentUser; if (pathParts[0] == StoreLocationCurrentUser) { // The default } else if (pathParts[0] == StoreLocationLocalMachine) { storeLocation = StoreLocation.LocalMachine; } else { throw new InvalidDataException($"Unknown store scope: {Startup.CertPath}"); } X509Store x509Store = new X509Store(pathParts[1], storeLocation); x509Store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Certificate2Collection collection = x509Store.Certificates; if (collection.Count == 0) { throw new InvalidDataException($"Found {collection.Count} certificates in store '{pathParts[0]}' [{storeLocation}] \\ '{pathParts[1]}': {Startup.CertPath}"); } X509Certificate2Collection results = collection.Find(X509FindType.FindByThumbprint, pathParts[2], true); if (results.Count != 1) { throw new InvalidDataException($"Found {results.Count} matches for '{pathParts[2]}': {Startup.CertPath}"); } ServerCertificate = results[0]; } else { ServerCertificate = new X509Certificate2(Startup.CertPath, Startup.CertPassword); } if (!string.IsNullOrEmpty(Startup.OutCertFile)) { string parent = Path.GetDirectoryName(Startup.OutCertFile); if (!string.IsNullOrEmpty(parent)) { Directory.CreateDirectory(parent); } File.WriteAllBytes(Startup.OutCertFile, ServerCertificate.Export(X509ContentType.Cert)); } if (!string.IsNullOrEmpty(Startup.LocalSourceJson)) { if (!File.Exists(Startup.LocalSourceJson)) { throw new FileNotFoundException(Startup.LocalSourceJson); } 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); } if (Startup.ExitBeforeRun) { return; } CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseKestrel(opt => { opt.ListenAnyIP(Startup.Port, listOpt => { listOpt.UseHttps(ServerCertificate); }); }); webBuilder.UseContentRoot(Startup.StaticFileRoot); webBuilder.UseStartup(); }); public static X509Certificate2 ServerCertificate { get; private set; } 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, overwrite: true); } string[] directories = Directory.GetDirectories(sourceDir); foreach (string dir in directories) { string dest = Path.Combine(destDir, Path.GetFileName(dir)); CopyDirectoryRecursive(dir, dest); } } } } ================================================ FILE: src/LocalhostWebServer/Run-LocalhostWebServer.ps1 ================================================ <# .SYNOPSIS Runs the LocalhostWebServer. .PARAMETER BuildRoot The root of the build output directory for LocalhostWebServer .PARAMETER StaticFileRoot The root of the static files to be served through Localhost .PARAMETER CertPath Path to HTTPS Development Certificate File (pfx) .PARAMETER CertPassword Secure Password for HTTPS Certificate .PARAMETER OutCertFile Export cert location. .PARAMETER LocalSourceJson Local source json definition .PARAMETER SourceCert The certificate of the source package. #> param( [Parameter(Mandatory=$true)] [string]$BuildRoot, [Parameter(Mandatory=$true)] [string]$StaticFileRoot, [Parameter(Mandatory=$true)] [string]$CertPath, [Parameter()] [string]$CertPassword, [Parameter()] [string]$OutCertFile, [Parameter()] [string]$LocalSourceJson, [Parameter()] [string]$SourceCert, [Parameter()] [string]$TestDataPath, [Parameter()] [switch]$ExitBeforeRun ) if (-not [System.String]::IsNullOrEmpty($sourceCert)) { # Requires admin & certutil.exe -addstore -f "TRUSTEDPEOPLE" $sourceCert } Push-Location $BuildRoot $startProcessArguments = @{ FilePath = Join-Path $BuildRoot "LocalhostWebServer.exe" ArgumentList = "StaticFileRoot=$StaticFileRoot CertPath=$CertPath CertPassword=$CertPassword OutCertFile=$OutCertFile LocalSourceJson=$LocalSourceJson TestDataPath=$TestDataPath ExitBeforeRun=$ExitBeforeRun" PassThru = $true } if (-not [System.string]::IsNullOrEmpty($env:artifactsDir)) { $startProcessArguments.RedirectStandardOutput = Join-Path $env:artifactsDir "LocalhostWebServer.out" $startProcessArguments.RedirectStandardError = Join-Path $env:artifactsDir "LocalhostWebServer.err" } $Local:process = Start-Process @startProcessArguments if ($ExitBeforeRun) { Wait-Process -InputObject $Local:process } Pop-Location ================================================ FILE: src/LocalhostWebServer/Startup.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace LocalhostWebServer { using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; public class Startup { public const string StaticFileRequestPath = "/TestKit"; public static string StaticFileRoot { get; set; } public static string CertPath { get; set; } public static string CertPassword { get; set; } public static string OutCertFile { get; set; } public static int Port { get; set; } public static string LocalSourceJson { get; set; } public static string TestDataPath { get; set; } public static bool ExitBeforeRun { get; set; } public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); } public IConfiguration Configuration { get; } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); //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"; provider.Mappings[".msi"] = "application/msi"; provider.Mappings[".appx"] = "application/vns.ms-appx"; provider.Mappings[".appxbundle"] = "application/vns.ms-appx"; provider.Mappings[".mszyml"] = "application/x-ms-zip-yaml"; provider.Mappings[".ttf"] = "application/x-font-ttf"; //Enable static file serving app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(StaticFileRoot), RequestPath = StaticFileRequestPath, ContentTypeProvider = provider, ServeUnknownFileTypes = true, DefaultContentType = "application/octet-stream" }); app.UseDirectoryBrowser(new DirectoryBrowserOptions { FileProvider = new PhysicalFileProvider(StaticFileRoot), RequestPath = StaticFileRequestPath }); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } } } ================================================ FILE: src/ManifestSchema/ManifestSchema.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once // 0 is reserved to represent when there is no resource. #define MANIFESTSCHEMA_NO_RESOURCE 0 #define MANIFESTSCHEMA_RESOURCE_TYPE 200 #define IDX_MANIFEST_SCHEMA_PREVIEW 201 #define IDX_MANIFEST_SCHEMA_V1_SINGLETON 202 #define IDX_MANIFEST_SCHEMA_V1_VERSION 203 #define IDX_MANIFEST_SCHEMA_V1_INSTALLER 204 #define IDX_MANIFEST_SCHEMA_V1_DEFAULTLOCALE 205 #define IDX_MANIFEST_SCHEMA_V1_LOCALE 206 #define IDX_MANIFEST_SCHEMA_V1_1_SINGLETON 207 #define IDX_MANIFEST_SCHEMA_V1_1_VERSION 208 #define IDX_MANIFEST_SCHEMA_V1_1_INSTALLER 209 #define IDX_MANIFEST_SCHEMA_V1_1_DEFAULTLOCALE 210 #define IDX_MANIFEST_SCHEMA_V1_1_LOCALE 211 #define IDX_MANIFEST_SCHEMA_V1_2_SINGLETON 212 #define IDX_MANIFEST_SCHEMA_V1_2_VERSION 213 #define IDX_MANIFEST_SCHEMA_V1_2_INSTALLER 214 #define IDX_MANIFEST_SCHEMA_V1_2_DEFAULTLOCALE 215 #define IDX_MANIFEST_SCHEMA_V1_2_LOCALE 216 #define IDX_MANIFEST_SCHEMA_V1_4_SINGLETON 217 #define IDX_MANIFEST_SCHEMA_V1_4_VERSION 218 #define IDX_MANIFEST_SCHEMA_V1_4_INSTALLER 219 #define IDX_MANIFEST_SCHEMA_V1_4_DEFAULTLOCALE 220 #define IDX_MANIFEST_SCHEMA_V1_4_LOCALE 221 #define IDX_MANIFEST_SCHEMA_V1_5_SINGLETON 222 #define IDX_MANIFEST_SCHEMA_V1_5_VERSION 223 #define IDX_MANIFEST_SCHEMA_V1_5_INSTALLER 224 #define IDX_MANIFEST_SCHEMA_V1_5_DEFAULTLOCALE 225 #define IDX_MANIFEST_SCHEMA_V1_5_LOCALE 226 #define IDX_MANIFEST_SCHEMA_V1_6_SINGLETON 227 #define IDX_MANIFEST_SCHEMA_V1_6_VERSION 228 #define IDX_MANIFEST_SCHEMA_V1_6_INSTALLER 229 #define IDX_MANIFEST_SCHEMA_V1_6_DEFAULTLOCALE 230 #define IDX_MANIFEST_SCHEMA_V1_6_LOCALE 231 #define IDX_MANIFEST_SCHEMA_V1_7_SINGLETON 232 #define IDX_MANIFEST_SCHEMA_V1_7_VERSION 233 #define IDX_MANIFEST_SCHEMA_V1_7_INSTALLER 234 #define IDX_MANIFEST_SCHEMA_V1_7_DEFAULTLOCALE 235 #define IDX_MANIFEST_SCHEMA_V1_7_LOCALE 236 #define IDX_MANIFEST_SCHEMA_V1_9_SINGLETON 237 #define IDX_MANIFEST_SCHEMA_V1_9_VERSION 238 #define IDX_MANIFEST_SCHEMA_V1_9_INSTALLER 239 #define IDX_MANIFEST_SCHEMA_V1_9_DEFAULTLOCALE 240 #define IDX_MANIFEST_SCHEMA_V1_9_LOCALE 241 #define IDX_MANIFEST_SCHEMA_V1_10_SINGLETON 242 #define IDX_MANIFEST_SCHEMA_V1_10_VERSION 243 #define IDX_MANIFEST_SCHEMA_V1_10_INSTALLER 244 #define IDX_MANIFEST_SCHEMA_V1_10_DEFAULTLOCALE 245 #define IDX_MANIFEST_SCHEMA_V1_10_LOCALE 246 #define IDX_MANIFEST_SCHEMA_V1_12_SINGLETON 247 #define IDX_MANIFEST_SCHEMA_V1_12_VERSION 248 #define IDX_MANIFEST_SCHEMA_V1_12_INSTALLER 249 #define IDX_MANIFEST_SCHEMA_V1_12_DEFAULTLOCALE 250 #define IDX_MANIFEST_SCHEMA_V1_12_LOCALE 251 #define IDX_MANIFEST_SCHEMA_V1_28_SINGLETON 252 #define IDX_MANIFEST_SCHEMA_V1_28_VERSION 253 #define IDX_MANIFEST_SCHEMA_V1_28_INSTALLER 254 #define IDX_MANIFEST_SCHEMA_V1_28_DEFAULTLOCALE 255 #define IDX_MANIFEST_SCHEMA_V1_28_LOCALE 256 // Packages schema starts at 300 // Certificates start at 400 // If we get to 300, either move the others or skip over them ================================================ FILE: src/ManifestSchema/ManifestSchema.rc ================================================ // Microsoft Visual C++ generated resource script. // #include "resource.h" #include "ManifestSchema.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE 9, 1 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Manifest schema // IDX_MANIFEST_SCHEMA_PREVIEW MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\preview\\manifest.0.1.0.json" IDX_MANIFEST_SCHEMA_V1_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.0.0\\manifest.singleton.1.0.0.json" IDX_MANIFEST_SCHEMA_V1_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.0.0\\manifest.version.1.0.0.json" IDX_MANIFEST_SCHEMA_V1_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.0.0\\manifest.installer.1.0.0.json" IDX_MANIFEST_SCHEMA_V1_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.0.0\\manifest.defaultLocale.1.0.0.json" IDX_MANIFEST_SCHEMA_V1_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.0.0\\manifest.locale.1.0.0.json" IDX_MANIFEST_SCHEMA_V1_1_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.1.0\\manifest.singleton.1.1.0.json" IDX_MANIFEST_SCHEMA_V1_1_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.1.0\\manifest.version.1.1.0.json" IDX_MANIFEST_SCHEMA_V1_1_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.1.0\\manifest.installer.1.1.0.json" IDX_MANIFEST_SCHEMA_V1_1_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.1.0\\manifest.defaultLocale.1.1.0.json" IDX_MANIFEST_SCHEMA_V1_1_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.1.0\\manifest.locale.1.1.0.json" IDX_MANIFEST_SCHEMA_V1_2_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.2.0\\manifest.singleton.1.2.0.json" IDX_MANIFEST_SCHEMA_V1_2_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.2.0\\manifest.version.1.2.0.json" IDX_MANIFEST_SCHEMA_V1_2_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.2.0\\manifest.installer.1.2.0.json" IDX_MANIFEST_SCHEMA_V1_2_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.2.0\\manifest.defaultLocale.1.2.0.json" IDX_MANIFEST_SCHEMA_V1_2_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.2.0\\manifest.locale.1.2.0.json" IDX_MANIFEST_SCHEMA_V1_4_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.4.0\\manifest.singleton.1.4.0.json" IDX_MANIFEST_SCHEMA_V1_4_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.4.0\\manifest.version.1.4.0.json" IDX_MANIFEST_SCHEMA_V1_4_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.4.0\\manifest.installer.1.4.0.json" IDX_MANIFEST_SCHEMA_V1_4_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.4.0\\manifest.defaultLocale.1.4.0.json" IDX_MANIFEST_SCHEMA_V1_4_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.4.0\\manifest.locale.1.4.0.json" IDX_MANIFEST_SCHEMA_V1_5_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.5.0\\manifest.singleton.1.5.0.json" IDX_MANIFEST_SCHEMA_V1_5_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.5.0\\manifest.version.1.5.0.json" IDX_MANIFEST_SCHEMA_V1_5_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.5.0\\manifest.installer.1.5.0.json" IDX_MANIFEST_SCHEMA_V1_5_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.5.0\\manifest.defaultLocale.1.5.0.json" IDX_MANIFEST_SCHEMA_V1_5_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.5.0\\manifest.locale.1.5.0.json" IDX_MANIFEST_SCHEMA_V1_6_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.6.0\\manifest.singleton.1.6.0.json" IDX_MANIFEST_SCHEMA_V1_6_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.6.0\\manifest.version.1.6.0.json" IDX_MANIFEST_SCHEMA_V1_6_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.6.0\\manifest.installer.1.6.0.json" IDX_MANIFEST_SCHEMA_V1_6_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.6.0\\manifest.defaultLocale.1.6.0.json" IDX_MANIFEST_SCHEMA_V1_6_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.6.0\\manifest.locale.1.6.0.json" IDX_MANIFEST_SCHEMA_V1_7_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.7.0\\manifest.singleton.1.7.0.json" IDX_MANIFEST_SCHEMA_V1_7_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.7.0\\manifest.version.1.7.0.json" IDX_MANIFEST_SCHEMA_V1_7_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.7.0\\manifest.installer.1.7.0.json" IDX_MANIFEST_SCHEMA_V1_7_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.7.0\\manifest.defaultLocale.1.7.0.json" IDX_MANIFEST_SCHEMA_V1_7_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.7.0\\manifest.locale.1.7.0.json" IDX_MANIFEST_SCHEMA_V1_9_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.9.0\\manifest.singleton.1.9.0.json" IDX_MANIFEST_SCHEMA_V1_9_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.9.0\\manifest.version.1.9.0.json" IDX_MANIFEST_SCHEMA_V1_9_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.9.0\\manifest.installer.1.9.0.json" IDX_MANIFEST_SCHEMA_V1_9_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.9.0\\manifest.defaultLocale.1.9.0.json" IDX_MANIFEST_SCHEMA_V1_9_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.9.0\\manifest.locale.1.9.0.json" IDX_MANIFEST_SCHEMA_V1_10_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.10.0\\manifest.singleton.1.10.0.json" IDX_MANIFEST_SCHEMA_V1_10_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.10.0\\manifest.version.1.10.0.json" IDX_MANIFEST_SCHEMA_V1_10_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.10.0\\manifest.installer.1.10.0.json" IDX_MANIFEST_SCHEMA_V1_10_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.10.0\\manifest.defaultLocale.1.10.0.json" IDX_MANIFEST_SCHEMA_V1_10_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.10.0\\manifest.locale.1.10.0.json" IDX_MANIFEST_SCHEMA_V1_12_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.12.0\\manifest.singleton.1.12.0.json" IDX_MANIFEST_SCHEMA_V1_12_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.12.0\\manifest.version.1.12.0.json" IDX_MANIFEST_SCHEMA_V1_12_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.12.0\\manifest.installer.1.12.0.json" IDX_MANIFEST_SCHEMA_V1_12_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.12.0\\manifest.defaultLocale.1.12.0.json" IDX_MANIFEST_SCHEMA_V1_12_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\v1.12.0\\manifest.locale.1.12.0.json" IDX_MANIFEST_SCHEMA_V1_28_SINGLETON MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\latest\\manifest.singleton.latest.json" IDX_MANIFEST_SCHEMA_V1_28_VERSION MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\latest\\manifest.version.latest.json" IDX_MANIFEST_SCHEMA_V1_28_INSTALLER MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\latest\\manifest.installer.latest.json" IDX_MANIFEST_SCHEMA_V1_28_DEFAULTLOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\latest\\manifest.defaultLocale.latest.json" IDX_MANIFEST_SCHEMA_V1_28_LOCALE MANIFESTSCHEMA_RESOURCE_TYPE "..\\..\\schemas\\JSON\\manifests\\latest\\manifest.locale.latest.json" ================================================ FILE: src/ManifestSchema/ManifestSchema.vcxitems ================================================  $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true {7d05f64d-ce5a-42aa-a2c1-e91458f061cf} %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) ================================================ FILE: src/ManifestSchema/ManifestSchema.vcxitems.filters ================================================  {d6998b42-8ce1-440a-ad12-8b505a284d7b} {f392b73a-3389-4d29-86d9-539a98e2bf3b} {fe494b58-5e5c-415a-922f-c6e10725e06a} {74616d1e-e987-4c28-bd85-7bbd205ee813} {36190723-3948-462f-97c2-47fa1b770826} {fb3524f4-5541-468d-a33c-ddcdad90484d} {422a18e9-7735-412d-b16b-98d9d53a1632} {27ca448e-4d6f-4039-bc55-46a7df7c69e2} {d14a3093-eaa9-49d0-9fc4-fbc596afa673} {040a7f05-fd31-466c-bb71-0d59e4cdf1c4} {f7069050-9235-44e0-ab07-72e4addfd765} {9c132cfd-cf4c-48d0-a32b-c52213f742fe} {99217106-7710-40c7-a2df-e5b69c758e5a} schema\latest schema\latest schema\latest schema\latest schema\latest schema\preview schema\v1.0.0 schema\v1.0.0 schema\v1.0.0 schema\v1.0.0 schema\v1.0.0 schema\v1.1.0 schema\v1.1.0 schema\v1.1.0 schema\v1.1.0 schema\v1.1.0 schema\v1.2.0 schema\v1.2.0 schema\v1.2.0 schema\v1.2.0 schema\v1.2.0 schema\v1.4.0 schema\v1.4.0 schema\v1.4.0 schema\v1.4.0 schema\v1.4.0 schema\v1.5.0 schema\v1.5.0 schema\v1.5.0 schema\v1.5.0 schema\v1.5.0 schema\v1.6.0 schema\v1.6.0 schema\v1.6.0 schema\v1.6.0 schema\v1.6.0 schema\v1.7.0 schema\v1.7.0 schema\v1.7.0 schema\v1.7.0 schema\v1.7.0 schema\v1.9.0 schema\v1.9.0 schema\v1.9.0 schema\v1.9.0 schema\v1.9.0 schema\v1.10.0 schema\v1.10.0 schema\v1.10.0 schema\v1.10.0 schema\v1.10.0 schema\v1.12.0 schema\v1.12.0 schema\v1.12.0 schema\v1.12.0 schema\v1.12.0 ================================================ FILE: src/ManifestSchema/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by ManifestSchema.rc // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: src/Microsoft.Management.Configuration/ApplyConfigurationSetResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ApplyConfigurationSetResult.h" #include "ApplyConfigurationSetResult.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { ApplyConfigurationSetResult::ApplyConfigurationSetResult() : m_unitResults(multi_threaded_vector()) {} Windows::Foundation::Collections::IVectorView ApplyConfigurationSetResult::UnitResults() const { return m_unitResults.GetView(); } const Windows::Foundation::Collections::IVector& ApplyConfigurationSetResult::UnitResultsVector() { return m_unitResults; } hresult ApplyConfigurationSetResult::ResultCode() const { return m_resultCode; } void ApplyConfigurationSetResult::ResultCode(hresult value) { m_resultCode = value; } } ================================================ FILE: src/Microsoft.Management.Configuration/ApplyConfigurationSetResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ApplyConfigurationSetResult.g.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ApplyConfigurationSetResult : ApplyConfigurationSetResultT { using ApplyConfigurationUnitResult = Configuration::ApplyConfigurationUnitResult; ApplyConfigurationSetResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) const Windows::Foundation::Collections::IVector& UnitResultsVector(); void ResultCode(hresult value); #endif Windows::Foundation::Collections::IVectorView UnitResults() const; hresult ResultCode() const; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: Windows::Foundation::Collections::IVector m_unitResults; hresult m_resultCode; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ApplyConfigurationUnitResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ApplyConfigurationUnitResult.h" #include "ApplyConfigurationUnitResult.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { ApplyConfigurationUnitResult::ApplyConfigurationUnitResult() : m_resultInformation(make_self>()) { } void ApplyConfigurationUnitResult::Initialize(const IApplySettingsResult& result) { m_unit = result.Unit(); THROW_HR_IF(E_POINTER, !m_unit); m_resultInformation->Initialize(result.ResultInformation()); } void ApplyConfigurationUnitResult::Initialize(const IApplyGroupMemberSettingsResult& unitResult) { m_unit = unitResult.Unit(); THROW_HR_IF(E_POINTER, !m_unit); m_resultInformation->Initialize(unitResult.ResultInformation()); m_state = unitResult.State(); m_previouslyInDesiredState = unitResult.PreviouslyInDesiredState(); m_rebootRequired = unitResult.RebootRequired(); } ConfigurationUnit ApplyConfigurationUnitResult::Unit() { return m_unit; } void ApplyConfigurationUnitResult::Unit(ConfigurationUnit value) { m_unit = std::move(value); } ConfigurationUnitState ApplyConfigurationUnitResult::State() const { return m_state.load(); } void ApplyConfigurationUnitResult::State(ConfigurationUnitState value) { m_state.store(value); } bool ApplyConfigurationUnitResult::PreviouslyInDesiredState() const { return m_previouslyInDesiredState; } void ApplyConfigurationUnitResult::PreviouslyInDesiredState(bool value) { m_previouslyInDesiredState = value; } bool ApplyConfigurationUnitResult::RebootRequired() const { return m_rebootRequired; } void ApplyConfigurationUnitResult::RebootRequired(bool value) { m_rebootRequired = value; } IConfigurationUnitResultInformation ApplyConfigurationUnitResult::ResultInformation() { return *m_resultInformation; } void ApplyConfigurationUnitResult::ResultInformation(const Configuration::IConfigurationUnitResultInformation& value) { m_resultInformation->Initialize(value); } ApplyConfigurationUnitResult::ResultInformationT ApplyConfigurationUnitResult::ResultInformationInternal() { return m_resultInformation; } } ================================================ FILE: src/Microsoft.Management.Configuration/ApplyConfigurationUnitResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ApplyConfigurationUnitResult.g.h" #include "ConfigurationUnitResultInformation.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ApplyConfigurationUnitResult : ApplyConfigurationUnitResultT { using ConfigurationUnit = Configuration::ConfigurationUnit; using ResultInformationT = decltype(make_self>()); ApplyConfigurationUnitResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(const IApplySettingsResult& result); void Initialize(const IApplyGroupMemberSettingsResult& unitResult); void Unit(ConfigurationUnit value); void State(ConfigurationUnitState value); void PreviouslyInDesiredState(bool value); void RebootRequired(bool value); void ResultInformation(const Configuration::IConfigurationUnitResultInformation& value); ResultInformationT ResultInformationInternal(); #endif ConfigurationUnit Unit(); ConfigurationUnitState State() const; bool PreviouslyInDesiredState() const; bool RebootRequired() const; IConfigurationUnitResultInformation ResultInformation(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ConfigurationUnit m_unit = nullptr; std::atomic m_state = ConfigurationUnitState::Pending; bool m_previouslyInDesiredState = false; bool m_rebootRequired = false; ResultInformationT m_resultInformation; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ApplyGroupSettingsResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ApplyGroupSettingsResult.h" namespace winrt::Microsoft::Management::Configuration::implementation { ApplyGroupSettingsResult::ApplyGroupSettingsResult() : m_resultInformation(make_self>()), m_unitResults(winrt::multi_threaded_vector()) {} void ApplyGroupSettingsResult::Group(const Windows::Foundation::IInspectable& value) { m_group = value; } void ApplyGroupSettingsResult::RebootRequired(bool value) { m_rebootRequired = value; } ApplyGroupSettingsResult::ResultInformationPtr ApplyGroupSettingsResult::ResultInformationInternal() { return m_resultInformation; } Windows::Foundation::IInspectable ApplyGroupSettingsResult::Group() { return m_group; } bool ApplyGroupSettingsResult::RebootRequired() { return m_rebootRequired; } IConfigurationUnitResultInformation ApplyGroupSettingsResult::ResultInformation() { return *m_resultInformation; } Windows::Foundation::Collections::IVector ApplyGroupSettingsResult::UnitResults() { return m_unitResults; } } ================================================ FILE: src/Microsoft.Management.Configuration/ApplyGroupSettingsResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" #include "ConfigurationUnitResultInformation.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct ApplyGroupSettingsResult : winrt::implements { ApplyGroupSettingsResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) using ResultInformationPtr = decltype(make_self>()); void Group(const Windows::Foundation::IInspectable& value); void RebootRequired(bool value); ResultInformationPtr ResultInformationInternal(); #endif Windows::Foundation::IInspectable Group(); bool RebootRequired(); IConfigurationUnitResultInformation ResultInformation(); Windows::Foundation::Collections::IVector UnitResults(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: Windows::Foundation::IInspectable m_group; bool m_rebootRequired = false; ResultInformationPtr m_resultInformation; Windows::Foundation::Collections::IVector m_unitResults; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ArgumentValidation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include #include "ArgumentValidation.h" namespace winrt::Microsoft::Management::Configuration::implementation { void EnsureSupportedType(Windows::Foundation::PropertyType type) { switch (type) { case Windows::Foundation::PropertyType::UInt8: case Windows::Foundation::PropertyType::Int16: case Windows::Foundation::PropertyType::UInt16: case Windows::Foundation::PropertyType::Int32: case Windows::Foundation::PropertyType::UInt32: case Windows::Foundation::PropertyType::Int64: case Windows::Foundation::PropertyType::UInt64: case Windows::Foundation::PropertyType::Single: case Windows::Foundation::PropertyType::Double: case Windows::Foundation::PropertyType::Char16: case Windows::Foundation::PropertyType::Boolean: case Windows::Foundation::PropertyType::String: case Windows::Foundation::PropertyType::Inspectable: case Windows::Foundation::PropertyType::DateTime: case Windows::Foundation::PropertyType::TimeSpan: case Windows::Foundation::PropertyType::Guid: case Windows::Foundation::PropertyType::UInt8Array: case Windows::Foundation::PropertyType::Int16Array: case Windows::Foundation::PropertyType::UInt16Array: case Windows::Foundation::PropertyType::Int32Array: case Windows::Foundation::PropertyType::UInt32Array: case Windows::Foundation::PropertyType::Int64Array: case Windows::Foundation::PropertyType::UInt64Array: case Windows::Foundation::PropertyType::SingleArray: case Windows::Foundation::PropertyType::DoubleArray: case Windows::Foundation::PropertyType::Char16Array: case Windows::Foundation::PropertyType::BooleanArray: case Windows::Foundation::PropertyType::StringArray: case Windows::Foundation::PropertyType::InspectableArray: case Windows::Foundation::PropertyType::DateTimeArray: case Windows::Foundation::PropertyType::TimeSpanArray: case Windows::Foundation::PropertyType::GuidArray: return; } THROW_HR(E_INVALIDARG); } bool IsValidObjectType(Windows::Foundation::IInspectable const& value, Windows::Foundation::PropertyType type) { auto propertyValue = value.try_as(); // If the type is an object, it is acceptable for the value to be a ValueSet directly if (type == Windows::Foundation::PropertyType::Inspectable && (propertyValue || value.try_as())) { return true; } // If it wasn't an object type and a ValueSet, it must be an IPropertyValue if (!propertyValue) { return false; } // If it is an IPropertyValue, it must have the required type return (propertyValue.Type() == type); } void EnsureObjectType(Windows::Foundation::IInspectable const& value, Windows::Foundation::PropertyType type) { THROW_HR_IF(E_INVALIDARG, !IsValidObjectType(value, type)); } bool IsComparableType(Windows::Foundation::PropertyType type) { switch (type) { case Windows::Foundation::PropertyType::UInt8: case Windows::Foundation::PropertyType::Int16: case Windows::Foundation::PropertyType::UInt16: case Windows::Foundation::PropertyType::Int32: case Windows::Foundation::PropertyType::UInt32: case Windows::Foundation::PropertyType::Int64: case Windows::Foundation::PropertyType::UInt64: case Windows::Foundation::PropertyType::Single: case Windows::Foundation::PropertyType::Double: case Windows::Foundation::PropertyType::Char16: case Windows::Foundation::PropertyType::DateTime: case Windows::Foundation::PropertyType::TimeSpan: return true; } return false; } void EnsureComparableType(Windows::Foundation::PropertyType type) { THROW_HR_IF(E_INVALIDARG, !IsComparableType(type)); } bool IsLengthType(Windows::Foundation::PropertyType type) { switch (type) { case Windows::Foundation::PropertyType::String: case Windows::Foundation::PropertyType::UInt8Array: case Windows::Foundation::PropertyType::Int16Array: case Windows::Foundation::PropertyType::UInt16Array: case Windows::Foundation::PropertyType::Int32Array: case Windows::Foundation::PropertyType::UInt32Array: case Windows::Foundation::PropertyType::Int64Array: case Windows::Foundation::PropertyType::UInt64Array: case Windows::Foundation::PropertyType::SingleArray: case Windows::Foundation::PropertyType::DoubleArray: case Windows::Foundation::PropertyType::Char16Array: case Windows::Foundation::PropertyType::BooleanArray: case Windows::Foundation::PropertyType::StringArray: case Windows::Foundation::PropertyType::InspectableArray: case Windows::Foundation::PropertyType::DateTimeArray: case Windows::Foundation::PropertyType::TimeSpanArray: case Windows::Foundation::PropertyType::GuidArray: case Windows::Foundation::PropertyType::PointArray: case Windows::Foundation::PropertyType::SizeArray: case Windows::Foundation::PropertyType::RectArray: case Windows::Foundation::PropertyType::OtherTypeArray: return true; } return false; } void EnsureLengthType(Windows::Foundation::PropertyType type) { THROW_HR_IF(E_INVALIDARG, !IsLengthType(type)); } bool IsStringableType(Windows::Foundation::PropertyType type) { switch (type) { case Windows::Foundation::PropertyType::UInt8: case Windows::Foundation::PropertyType::Int16: case Windows::Foundation::PropertyType::UInt16: case Windows::Foundation::PropertyType::Int32: case Windows::Foundation::PropertyType::UInt32: case Windows::Foundation::PropertyType::Int64: case Windows::Foundation::PropertyType::UInt64: case Windows::Foundation::PropertyType::Single: case Windows::Foundation::PropertyType::Double: case Windows::Foundation::PropertyType::Char16: case Windows::Foundation::PropertyType::Boolean: case Windows::Foundation::PropertyType::String: return true; } return false; } hstring ToString(Windows::Foundation::IPropertyValue value) { Windows::Foundation::PropertyType type = value.Type(); if (type == Windows::Foundation::PropertyType::String) { return value.GetString(); } std::wostringstream stream; switch (value.Type()) { case Windows::Foundation::PropertyType::UInt8: stream << value.GetUInt8(); break; case Windows::Foundation::PropertyType::Int16: stream << value.GetInt16(); break; case Windows::Foundation::PropertyType::UInt16: stream << value.GetUInt16(); break; case Windows::Foundation::PropertyType::Int32: stream << value.GetInt32(); break; case Windows::Foundation::PropertyType::UInt32: stream << value.GetUInt32(); break; case Windows::Foundation::PropertyType::Int64: stream << value.GetInt64(); break; case Windows::Foundation::PropertyType::UInt64: stream << value.GetUInt64(); break; case Windows::Foundation::PropertyType::Single: stream << value.GetSingle(); break; case Windows::Foundation::PropertyType::Double: stream << value.GetDouble(); break; case Windows::Foundation::PropertyType::Char16: stream << value.GetChar16(); break; case Windows::Foundation::PropertyType::Boolean: stream << value.GetBoolean() ? L"true" : L"false"; break; } return hstring{ stream.str() }; } } ================================================ FILE: src/Microsoft.Management.Configuration/ArgumentValidation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace winrt::Microsoft::Management::Configuration::implementation { // Ensures that the given type is supported. void EnsureSupportedType(Windows::Foundation::PropertyType type); // Ensures that the value object matches the expected property type. bool IsValidObjectType(Windows::Foundation::IInspectable const& value, Windows::Foundation::PropertyType type); // Ensures that the value object matches the expected property type. void EnsureObjectType(Windows::Foundation::IInspectable const& value, Windows::Foundation::PropertyType type); // Determines if the given type supports comparison. bool IsComparableType(Windows::Foundation::PropertyType type); // Ensures that the given type supports comparison. void EnsureComparableType(Windows::Foundation::PropertyType type); // Determines if the given type supports length restrictions. bool IsLengthType(Windows::Foundation::PropertyType type); // Ensures that the given type supports length restrictions. void EnsureLengthType(Windows::Foundation::PropertyType type); // Determines if the given type is a scalar that can be converted to a reasonable string representation. bool IsStringableType(Windows::Foundation::PropertyType type); // Gets the string version of the given property, if it is stringable. hstring ToString(Windows::Foundation::IPropertyValue value); } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigThreadGlobals.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigThreadGlobals.h" namespace winrt::Microsoft::Management::Configuration::implementation { AppInstaller::Logging::DiagnosticLogger& ConfigThreadGlobals::GetDiagnosticLogger() { return m_logger; } void* ConfigThreadGlobals::GetTelemetryObject() { return &m_telemetry; } TelemetryTraceLogger& ConfigThreadGlobals::GetTelemetryLogger() { return m_telemetry; } const TelemetryTraceLogger& ConfigThreadGlobals::GetTelemetryLogger() const { return m_telemetry; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigThreadGlobals.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Interface for access to values that are stored on a per-thread object. struct ConfigThreadGlobals : public AppInstaller::ThreadLocalStorage::ThreadGlobals { ConfigThreadGlobals() = default; virtual ~ConfigThreadGlobals() = default; AppInstaller::Logging::DiagnosticLogger& GetDiagnosticLogger() override; void* GetTelemetryObject() override; TelemetryTraceLogger& GetTelemetryLogger(); const TelemetryTraceLogger& GetTelemetryLogger() const; protected: AppInstaller::Logging::DiagnosticLogger m_logger; TelemetryTraceLogger m_telemetry; }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationChangeData.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationChangeData.h" #include "ConfigurationChangeData.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { void ConfigurationChangeData::Initialize(ConfigurationChangeEventType change, guid instanceIdentifier, ConfigurationSetState state) { m_change = change; m_instanceIdentifier = instanceIdentifier; m_state = state; } ConfigurationChangeEventType ConfigurationChangeData::Change() { return m_change; } guid ConfigurationChangeData::InstanceIdentifier() { return m_instanceIdentifier; } ConfigurationSetState ConfigurationChangeData::State() { return m_state; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationChangeData.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationChangeData.g.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationChangeData : ConfigurationChangeDataT { ConfigurationChangeData() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(ConfigurationChangeEventType change, guid instanceIdentifier, ConfigurationSetState state); #endif ConfigurationChangeEventType Change(); guid InstanceIdentifier(); ConfigurationSetState State(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ConfigurationChangeEventType m_change{}; guid m_instanceIdentifier{}; ConfigurationSetState m_state{}; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationConflict.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationConflict.h" #include "ConfigurationConflict.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { void ConfigurationConflict::Initialize(ConfigurationConflictType conflict) { m_conflict = conflict; } ConfigurationConflictType ConfigurationConflict::Conflict() { return m_conflict; } ConfigurationSet ConfigurationConflict::FirstSet() { return m_firstSet; } ConfigurationSet ConfigurationConflict::SecondSet() { return m_secondSet; } ConfigurationUnit ConfigurationConflict::FirstUnit() { return m_firstUnit; } ConfigurationUnit ConfigurationConflict::SecondUnit() { return m_secondUnit; } Windows::Foundation::Collections::IVectorView ConfigurationConflict::Settings() { if (m_settings) { return m_settings.GetView(); } else { return nullptr; } } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationConflict.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationConflict.g.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationConflict : ConfigurationConflictT { ConfigurationConflict() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(ConfigurationConflictType conflict); #endif ConfigurationConflictType Conflict(); ConfigurationSet FirstSet(); ConfigurationSet SecondSet(); ConfigurationUnit FirstUnit(); ConfigurationUnit SecondUnit(); Windows::Foundation::Collections::IVectorView Settings(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ConfigurationConflictType m_conflict{}; ConfigurationSet m_firstSet = nullptr; ConfigurationSet m_secondSet = nullptr; ConfigurationUnit m_firstUnit = nullptr; ConfigurationUnit m_secondUnit = nullptr; Windows::Foundation::Collections::IVector m_settings = nullptr; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationConflictSetting.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationConflictSetting.h" #include "ConfigurationConflictSetting.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { void ConfigurationConflictSetting::Initialize(std::wstring_view name, const IInspectable& firstValue, const IInspectable& secondValue) { m_name = name; m_firstValue = firstValue; m_secondValue = secondValue; } hstring ConfigurationConflictSetting::Name() { return m_name; } ConfigurationConflictSetting::IInspectable ConfigurationConflictSetting::FirstValue() { return m_firstValue; } ConfigurationConflictSetting::IInspectable ConfigurationConflictSetting::SecondValue() { return m_secondValue; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationConflictSetting.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationConflictSetting.g.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationConflictSetting : ConfigurationConflictSettingT { ConfigurationConflictSetting() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(std::wstring_view name, const IInspectable& firstValue, const IInspectable& secondValue); #endif hstring Name(); IInspectable FirstValue(); IInspectable SecondValue(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: hstring m_name; IInspectable m_firstValue = nullptr; IInspectable m_secondValue = nullptr; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationEnvironment.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationEnvironment.h" #include "ConfigurationEnvironment.g.cpp" #include "ArgumentValidation.h" namespace winrt::Microsoft::Management::Configuration::implementation { namespace { template void DeepCopyEnvironmentFrom(implementation::ConfigurationEnvironment& self, const T& toDeepCopy) { self.Context(toDeepCopy.Context()); self.ProcessorIdentifier(toDeepCopy.ProcessorIdentifier()); self.ProcessorProperties(toDeepCopy.ProcessorProperties()); } } ConfigurationEnvironment::ConfigurationEnvironment() : m_processorProperties(multi_threaded_map()) {} ConfigurationEnvironment::ConfigurationEnvironment(SecurityContext context, const std::wstring& processorIdentifier, std::map&& processorProperties) : m_context(context), m_processorIdentifier(processorIdentifier), m_processorProperties(multi_threaded_map(std::move(processorProperties))) {} ConfigurationEnvironment::ConfigurationEnvironment(const implementation::ConfigurationEnvironment& toDeepCopy) { DeepCopy(toDeepCopy); } ConfigurationEnvironment::ConfigurationEnvironment(const Configuration::ConfigurationEnvironment& toDeepCopy) { DeepCopyEnvironmentFrom(*this, toDeepCopy); } SecurityContext ConfigurationEnvironment::Context() const { return m_context; } void ConfigurationEnvironment::Context(SecurityContext value) { m_context = value; } hstring ConfigurationEnvironment::ProcessorIdentifier() const { return m_processorIdentifier; } void ConfigurationEnvironment::ProcessorIdentifier(const hstring& value) { m_processorIdentifier = value; } Windows::Foundation::Collections::IMap ConfigurationEnvironment::ProcessorProperties() const { return m_processorProperties; } void ConfigurationEnvironment::DeepCopy(const implementation::ConfigurationEnvironment& toDeepCopy) { DeepCopyEnvironmentFrom(*this, toDeepCopy); } void ConfigurationEnvironment::ProcessorProperties(const Windows::Foundation::Collections::IMap& values) { std::map properties; for (const auto& property : values) { properties.emplace(property.Key(), property.Value()); } m_processorProperties = multi_threaded_map(std::move(properties)); } void ConfigurationEnvironment::ProcessorProperties(const Windows::Foundation::Collections::ValueSet& values) { std::map properties; for (const auto& value : values) { Windows::Foundation::IPropertyValue property = value.Value().try_as(); if (property && IsStringableType(property.Type())) { properties.emplace(value.Key(), ToString(property)); } } m_processorProperties = multi_threaded_map(std::move(properties)); } bool ConfigurationEnvironment::IsDefault() const { return m_context == SecurityContext::Current && m_processorIdentifier.empty() && m_processorProperties.Size() == 0; } com_ptr ConfigurationEnvironment::CalculateCommonEnvironment(const std::vector& environments) { com_ptr result; if (environments.empty()) { result = make_self(); } else { result = make_self(environments.front()); for (size_t i = 1; i < environments.size(); ++i) { const Configuration::ConfigurationEnvironment& environment = environments[i]; if (result->m_context != environment.Context()) { result->m_context = SecurityContext::Current; } if (result->m_processorIdentifier != environment.ProcessorIdentifier()) { result->m_processorIdentifier.clear(); } if (!AreEqual(result->m_processorProperties, environment.ProcessorProperties())) { result->m_processorProperties = single_threaded_map(); } // Check if we have already found everything to be different if (result->IsDefault()) { break; } } } return result; } bool ConfigurationEnvironment::AreEqual(const Windows::Foundation::Collections::IMap& a, const Windows::Foundation::Collections::IMap& b) { uint32_t a_size = a ? a.Size() : 0; uint32_t b_size = b ? b.Size() : 0; if (a_size == 0 && b_size == 0) { return true; } else if (a_size != b_size) { return false; } for (const auto& entry : a) { hstring key = entry.Key(); if (!b.HasKey(key) || entry.Value() != b.Lookup(key)) { return false; } } return true; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationEnvironment.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationEnvironment.g.h" #include #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationEnvironment : ConfigurationEnvironmentT, AppInstaller::WinRT::ModuleCountBase { ConfigurationEnvironment(); ConfigurationEnvironment(SecurityContext context, const std::wstring& processorIdentifier, std::map&& processorProperties); ConfigurationEnvironment(const implementation::ConfigurationEnvironment& toDeepCopy); ConfigurationEnvironment(const Configuration::ConfigurationEnvironment& toDeepCopy); SecurityContext Context() const; void Context(SecurityContext value); hstring ProcessorIdentifier() const; void ProcessorIdentifier(const hstring& value); Windows::Foundation::Collections::IMap ProcessorProperties() const; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) // Copies the values from the given environment, including making a new map of the properties. void DeepCopy(const implementation::ConfigurationEnvironment& toDeepCopy); // Copies the properties from the given map. void ProcessorProperties(const Windows::Foundation::Collections::IMap& values); // Copies the scalar properties from the given ValueSet. // Ignores all values that cannot be converted to a string. void ProcessorProperties(const Windows::Foundation::Collections::ValueSet& values); // Determines if this environment represents the default environment. bool IsDefault() const; // Creates an environment, setting only fields that are identical between all given environments. static com_ptr CalculateCommonEnvironment(const std::vector& environments); // Checks if the two given properties maps are equal. static bool AreEqual(const Windows::Foundation::Collections::IMap& a, const Windows::Foundation::Collections::IMap& b); private: SecurityContext m_context = SecurityContext::Current; hstring m_processorIdentifier; Windows::Foundation::Collections::IMap m_processorProperties; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationParameter.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationParameter.h" #include "ConfigurationParameter.g.cpp" #include "ArgumentValidation.h" namespace winrt::Microsoft::Management::Configuration::implementation { hstring ConfigurationParameter::Name() const { return m_name; } void ConfigurationParameter::Name(hstring const& value) { m_name = value; } hstring ConfigurationParameter::Description() const { return m_description; } void ConfigurationParameter::Description(hstring const& value) { m_description = value; } Windows::Foundation::Collections::ValueSet ConfigurationParameter::Metadata() const { return m_metadata; } void ConfigurationParameter::Metadata(const Windows::Foundation::Collections::ValueSet& value) { THROW_HR_IF(E_POINTER, !value); m_metadata = value; } bool ConfigurationParameter::IsSecure() const { return m_isSecure; } void ConfigurationParameter::IsSecure(bool value) { m_isSecure = value; } Windows::Foundation::PropertyType ConfigurationParameter::Type() const { return m_type; } void ConfigurationParameter::Type(Windows::Foundation::PropertyType value) { EnsureSupportedType(value); m_type = value; } Windows::Foundation::IInspectable ConfigurationParameter::DefaultValue() const { return m_defaultValue; } void ConfigurationParameter::DefaultValue(Windows::Foundation::IInspectable const& value) { if (value) { EnsureObjectType(value, m_type); } m_defaultValue = value; } Windows::Foundation::Collections::IVector ConfigurationParameter::AllowedValues() const { return m_allowedValues; } void ConfigurationParameter::AllowedValues(Windows::Foundation::Collections::IVector const& value) { if (value) { for (const auto& item : value) { if (item) { EnsureObjectType(item, m_type); } } } m_allowedValues = value; } void ConfigurationParameter::AllowedValues(std::vector&& value) { m_allowedValues = winrt::multi_threaded_vector(std::move(value)); } uint32_t ConfigurationParameter::MinimumLength() const { return m_minimumLength; } void ConfigurationParameter::MinimumLength(uint32_t value) { EnsureLengthType(m_type); m_minimumLength = value; } uint32_t ConfigurationParameter::MaximumLength() const { return m_maximumLength; } void ConfigurationParameter::MaximumLength(uint32_t value) { EnsureLengthType(m_type); m_maximumLength = value; } Windows::Foundation::IInspectable ConfigurationParameter::MinimumValue() const { return m_minimumValue; } void ConfigurationParameter::MinimumValue(Windows::Foundation::IInspectable const& value) { if (value) { EnsureObjectType(value, m_type); EnsureComparableType(m_type); } m_minimumValue = value; } Windows::Foundation::IInspectable ConfigurationParameter::MaximumValue() const { return m_maximumValue; } void ConfigurationParameter::MaximumValue(Windows::Foundation::IInspectable const& value) { if (value) { EnsureObjectType(value, m_type); EnsureComparableType(m_type); } m_maximumValue = value; } Windows::Foundation::IInspectable ConfigurationParameter::ProvidedValue() const { return m_providedValue; } void ConfigurationParameter::ProvidedValue(Windows::Foundation::IInspectable const& value) { if (value) { EnsureObjectType(value, m_type); } m_providedValue = value; } HRESULT STDMETHODCALLTYPE ConfigurationParameter::SetLifetimeWatcher(IUnknown* watcher) { return AppInstaller::WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher); } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationParameter.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationParameter.g.h" #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationParameter : ConfigurationParameterT>, AppInstaller::WinRT::LifetimeWatcherBase { ConfigurationParameter() = default; hstring Name() const; void Name(hstring const& value); hstring Description() const; void Description(hstring const& value); Windows::Foundation::Collections::ValueSet Metadata() const; void Metadata(const Windows::Foundation::Collections::ValueSet& value); bool IsSecure() const; void IsSecure(bool value); Windows::Foundation::PropertyType Type() const; void Type(Windows::Foundation::PropertyType value); Windows::Foundation::IInspectable DefaultValue() const; void DefaultValue(Windows::Foundation::IInspectable const& value); Windows::Foundation::Collections::IVector AllowedValues() const; void AllowedValues(Windows::Foundation::Collections::IVector const& value); uint32_t MinimumLength() const; void MinimumLength(uint32_t value); uint32_t MaximumLength() const; void MaximumLength(uint32_t value); Windows::Foundation::IInspectable MinimumValue() const; void MinimumValue(Windows::Foundation::IInspectable const& value); Windows::Foundation::IInspectable MaximumValue() const; void MaximumValue(Windows::Foundation::IInspectable const& value); Windows::Foundation::IInspectable ProvidedValue() const; void ProvidedValue(Windows::Foundation::IInspectable const& value); HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void AllowedValues(std::vector&& value); private: hstring m_name; hstring m_description; Windows::Foundation::Collections::ValueSet m_metadata; bool m_isSecure = false; Windows::Foundation::PropertyType m_type = Windows::Foundation::PropertyType::Inspectable; Windows::Foundation::IInspectable m_defaultValue; Windows::Foundation::Collections::IVector m_allowedValues; uint32_t m_minimumLength = 0; uint32_t m_maximumLength = std::numeric_limits::max(); Windows::Foundation::IInspectable m_minimumValue; Windows::Foundation::IInspectable m_maximumValue; Windows::Foundation::IInspectable m_providedValue; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Configuration::factory_implementation { struct ConfigurationParameter : ConfigurationParameterT { }; } #endif ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationProcessor.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationProcessor.h" #include "ConfigurationProcessor.g.cpp" #include "ConfigurationSet.h" #include "OpenConfigurationSetResult.h" #include "ConfigurationSetParser.h" #include "DiagnosticInformationInstance.h" #include "ApplyConfigurationSetResult.h" #include "ApplyConfigurationUnitResult.h" #include "TestConfigurationSetResult.h" #include "TestConfigurationUnitResult.h" #include "ConfigurationUnitResultInformation.h" #include "GetConfigurationUnitSettingsResult.h" #include "GetAllConfigurationUnitSettingsResult.h" #include "GetAllConfigurationUnitsResult.h" #include "ExceptionResultHelpers.h" #include "ConfigurationSetChangeData.h" #include "GetConfigurationUnitDetailsResult.h" #include "GetConfigurationSetDetailsResult.h" #include "DefaultSetGroupProcessor.h" #include "ConfigurationSequencer.h" #include "ConfigurationStatus.h" #include #include #include #include using namespace std::chrono_literals; namespace winrt::Microsoft::Management::Configuration::implementation { namespace { AppInstaller::Logging::Level ConvertLevel(DiagnosticLevel level) { switch (level) { case DiagnosticLevel::Verbose: return AppInstaller::Logging::Level::Verbose; case DiagnosticLevel::Informational: return AppInstaller::Logging::Level::Info; case DiagnosticLevel::Warning: return AppInstaller::Logging::Level::Warning; case DiagnosticLevel::Error: return AppInstaller::Logging::Level::Error; case DiagnosticLevel::Critical: return AppInstaller::Logging::Level::Crit; default: return AppInstaller::Logging::Level::Warning; } } DiagnosticLevel ConvertLevel(AppInstaller::Logging::Level level) { switch (level) { case AppInstaller::Logging::Level::Verbose: return DiagnosticLevel::Verbose; case AppInstaller::Logging::Level::Info: return DiagnosticLevel::Informational; case AppInstaller::Logging::Level::Warning: return DiagnosticLevel::Warning; case AppInstaller::Logging::Level::Error: return DiagnosticLevel::Error; case AppInstaller::Logging::Level::Crit: return DiagnosticLevel::Critical; default: return DiagnosticLevel::Warning; } } // ILogger that sends data back to the Diagnostics event of the ConfigurationProcessor. struct ConfigurationProcessorDiagnosticsLogger : public AppInstaller::Logging::ILogger { ConfigurationProcessorDiagnosticsLogger(ConfigurationProcessor& processor) : m_processor(processor) {} std::string GetName() const override { return "ConfigurationProcessorDiagnosticsLogger"; } void Write(AppInstaller::Logging::Channel channel, AppInstaller::Logging::Level level, std::string_view message) noexcept override try { std::ostringstream strstr; strstr << '[' << AppInstaller::Logging::GetChannelName(channel) << "] " << message; m_processor.SendDiagnostics(ConvertLevel(level), strstr.str()); } catch (...) {} void WriteDirect(AppInstaller::Logging::Channel, AppInstaller::Logging::Level level, std::string_view message) noexcept override try { m_processor.SendDiagnostics(ConvertLevel(level), message); } catch (...) {} private: ConfigurationProcessor& m_processor; }; // Helper to ensure a one-time callback attach struct AttachWilFailureCallback { AttachWilFailureCallback() { wil::SetResultLoggingCallback(wilResultLoggingCallback); } ~AttachWilFailureCallback() = default; static void __stdcall wilResultLoggingCallback(const wil::FailureInfo& info) noexcept { AICLI_LOG(Fail, Error, << [&]() { wchar_t message[2048]; GetFailureLogString(message, ARRAYSIZE(message), info); return AppInstaller::Utility::ConvertToUTF8(message); }()); } static void Ensure() { static AttachWilFailureCallback s_callbackAttach; } }; } ConfigurationProcessor::ConfigurationProcessor() { THROW_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::WinGet)); THROW_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::Configuration)); AppInstaller::Logging::DiagnosticLogger& logger = m_threadGlobals.GetDiagnosticLogger(); logger.SetEnabledChannels(AppInstaller::Logging::Channel::All); logger.SetLevel(AppInstaller::Logging::Level::Verbose); logger.AddLogger(std::make_unique(*this)); } ConfigurationProcessor::ConfigurationProcessor(const IConfigurationSetProcessorFactory& factory) : ConfigurationProcessor() { ConfigurationSetProcessorFactory(factory); } event_token ConfigurationProcessor::Diagnostics(const Windows::Foundation::EventHandler& handler) { AttachWilFailureCallback::Ensure(); return m_diagnostics.add(handler); } void ConfigurationProcessor::Diagnostics(const event_token& token) noexcept { m_diagnostics.remove(token); } DiagnosticLevel ConfigurationProcessor::MinimumLevel() { return m_minimumLevel; } void ConfigurationProcessor::MinimumLevel(DiagnosticLevel value) { m_minimumLevel = value; m_threadGlobals.GetDiagnosticLogger().SetLevel(ConvertLevel(value)); if (m_factory) { m_factory.MinimumLevel(value); } } hstring ConfigurationProcessor::Caller() const { return hstring{ AppInstaller::Utility::ConvertToUTF16(m_threadGlobals.GetTelemetryLogger().GetCaller()) }; } void ConfigurationProcessor::Caller(hstring value) { m_threadGlobals.GetTelemetryLogger().SetCaller(AppInstaller::Utility::ConvertToUTF8(value)); } guid ConfigurationProcessor::ActivityIdentifier() { return *m_threadGlobals.GetTelemetryLogger().GetActivityId(); } void ConfigurationProcessor::ActivityIdentifier(const guid& value) { m_threadGlobals.GetTelemetryLogger().SetActivityId(value); } bool ConfigurationProcessor::GenerateTelemetryEvents() { return m_threadGlobals.GetTelemetryLogger().IsEnabled(); } void ConfigurationProcessor::GenerateTelemetryEvents(bool value) { std::ignore = m_threadGlobals.GetTelemetryLogger().EnableRuntime(value); } event_token ConfigurationProcessor::ConfigurationChange(const Windows::Foundation::TypedEventHandler& handler) { if (!m_configurationChange) { auto status = ConfigurationStatus::Instance(); std::atomic_store(&m_changeRegistration, status->RegisterForChange(*this)); } return m_configurationChange.add(handler); } void ConfigurationProcessor::ConfigurationChange(const event_token& token) noexcept { m_configurationChange.remove(token); if (!m_configurationChange) { std::atomic_store(&m_changeRegistration, {}); } } void ConfigurationProcessor::ConfigurationChange(const Configuration::ConfigurationSet& set, const Configuration::ConfigurationChangeData& data) try { m_configurationChange(set, data); } CATCH_LOG(); Windows::Foundation::Collections::IVector ConfigurationProcessor::GetConfigurationHistory() { return GetConfigurationHistoryImpl(); } Windows::Foundation::IAsyncOperation> ConfigurationProcessor::GetConfigurationHistoryAsync() { auto strong_this{ get_strong() }; co_await winrt::resume_background(); co_return GetConfigurationHistoryImpl({ co_await winrt::get_cancellation_token() }); } Configuration::OpenConfigurationSetResult ConfigurationProcessor::OpenConfigurationSet(const Windows::Storage::Streams::IInputStream& stream) { return OpenConfigurationSetAsync(stream).get(); } Windows::Foundation::IAsyncOperation ConfigurationProcessor::OpenConfigurationSetAsync(const Windows::Storage::Streams::IInputStream& stream) { auto strong_this{ get_strong() }; Windows::Storage::Streams::IInputStream localStream = stream; co_await winrt::resume_background(); auto cancellation = co_await winrt::get_cancellation_token(); auto threadGlobals = m_threadGlobals.SetForCurrentThread(); auto result = make_self>(); if (!localStream) { result->Initialize(E_POINTER, {}); co_return *result; } try { // Read the entire file into memory as we expect them to be small and // our YAML parser doesn't support streaming at this time. // This is done here to enable easy cancellation propagation to the stream reads. uint32_t bufferSize = 1 << 20; Windows::Storage::Streams::Buffer buffer(bufferSize); // Memory stream in mixed elevation does not support InputStreamOptions as flags. Windows::Storage::Streams::InputStreamOptions readOptions = Windows::Storage::Streams::InputStreamOptions::Partial; std::string inputString; for (;;) { auto asyncOperation = localStream.ReadAsync(buffer, bufferSize, readOptions); // Manually poll status and propagate cancellation to stay on this thread for thread globals while (asyncOperation.Status() == Windows::Foundation::AsyncStatus::Started) { if (cancellation()) { asyncOperation.Cancel(); } std::this_thread::sleep_for(100ms); } Windows::Storage::Streams::IBuffer readBuffer = asyncOperation.GetResults(); size_t readSize = static_cast(readBuffer.Length()); if (readSize) { static_assert(sizeof(char) == sizeof(*readBuffer.data())); inputString.append(reinterpret_cast(readBuffer.data()), readSize); } else { break; } } std::unique_ptr parser = ConfigurationSetParser::Create(inputString); if (FAILED(parser->Result())) { result->Initialize(parser->Result(), parser->Field(), parser->Value(), parser->Line(), parser->Column()); co_return *result; } parser->Parse(); if (FAILED(parser->Result())) { result->Initialize(parser->Result(), parser->Field(), parser->Value(), parser->Line(), parser->Column()); co_return *result; } auto configurationSet = parser->GetConfigurationSet(); PropagateLifetimeWatcher(configurationSet.as()); configurationSet->SetInputHash(AppInstaller::Utility::SHA256::ConvertToString(AppInstaller::Utility::SHA256::ComputeHash(inputString))); result->Initialize(*configurationSet); } catch (const wil::ResultException& resultException) { result->Initialize(resultException.GetErrorCode()); } catch (...) { result->Initialize(WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE); LOG_CAUGHT_EXCEPTION(); } co_return *result; } Windows::Foundation::Collections::IVector ConfigurationProcessor::CheckForConflicts( const Windows::Foundation::Collections::IVectorView& configurationSets, bool includeConfigurationHistory) { UNREFERENCED_PARAMETER(configurationSets); UNREFERENCED_PARAMETER(includeConfigurationHistory); THROW_HR(E_NOTIMPL); } Windows::Foundation::IAsyncOperation> ConfigurationProcessor::CheckForConflictsAsync( const Windows::Foundation::Collections::IVectorView& configurationSets, bool includeConfigurationHistory) { co_return CheckForConflicts(configurationSets, includeConfigurationHistory); } Configuration::GetConfigurationSetDetailsResult ConfigurationProcessor::GetSetDetails(const Configuration::ConfigurationSet& configurationSet, ConfigurationUnitDetailFlags detailFlags) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return GetSetDetailsImpl(configurationSet, detailFlags); } Windows::Foundation::IAsyncOperationWithProgress ConfigurationProcessor::GetSetDetailsAsync( const Configuration::ConfigurationSet& configurationSet, ConfigurationUnitDetailFlags detailFlags) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; Configuration::ConfigurationSet localSet = configurationSet; co_await winrt::resume_background(); co_return GetSetDetailsImpl(localSet, detailFlags, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token()}); } Windows::Foundation::Collections::IVector ConfigurationProcessor::GetConfigurationHistoryImpl(ShutdownAwareAsyncCancellation cancellation) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); m_database.EnsureOpened(false); cancellation.ThrowIfCancelled(); std::vector result; for (const auto& set : m_database.GetSetHistory()) { PropagateLifetimeWatcher(*set); result.emplace_back(*set); } return multi_threaded_vector(std::move(result)); } Configuration::GetConfigurationSetDetailsResult ConfigurationProcessor::GetSetDetailsImpl( const Configuration::ConfigurationSet& configurationSet, ConfigurationUnitDetailFlags detailFlags, ShutdownAwareAsyncProgress progress) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(configurationSet); auto result = make_self>(); progress.Result(*result); for (const auto& unit : configurationSet.Units()) { progress.ThrowIfCancelled(); auto unitResult = make_self>(); auto unitResultInformation = make_self>(); unitResult->Unit(unit); unitResult->ResultInformation(*unitResultInformation); try { IConfigurationUnitProcessorDetails details = setProcessor.GetUnitProcessorDetails(unit, detailFlags); unitResult->Details(details); get_self(unit)->Details(std::move(details)); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResultInformation); } result->UnitResultsVector().Append(*unitResult); progress.Progress(*unitResult); } return *result; } Configuration::GetConfigurationUnitDetailsResult ConfigurationProcessor::GetUnitDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return GetUnitDetailsImpl(unit, detailFlags); } Windows::Foundation::IAsyncOperation ConfigurationProcessor::GetUnitDetailsAsync(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; ConfigurationUnit localUnit = unit; co_await winrt::resume_background(); co_return GetUnitDetailsImpl(localUnit, detailFlags); } Configuration::GetConfigurationUnitDetailsResult ConfigurationProcessor::GetUnitDetailsImpl(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); auto unitResult = make_self>(); auto unitResultInformation = make_self>(); unitResult->Unit(unit); unitResult->ResultInformation(*unitResultInformation); try { IConfigurationUnitProcessorDetails details = setProcessor.GetUnitProcessorDetails(unit, detailFlags); unitResult->Details(details); get_self(unit)->Details(std::move(details)); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResultInformation); } return *unitResult; } Configuration::ApplyConfigurationSetResult ConfigurationProcessor::ApplySet(const Configuration::ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return ApplySetImpl(configurationSet, flags); } Windows::Foundation::IAsyncOperationWithProgress ConfigurationProcessor::ApplySetAsync( const Configuration::ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; Configuration::ConfigurationSet localSet = configurationSet; co_await winrt::resume_background(); co_return ApplySetImpl(localSet, flags, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token() }); } Configuration::ApplyConfigurationSetResult ConfigurationProcessor::ApplySetImpl( const Configuration::ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags, ShutdownAwareAsyncProgress progress) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationGroupProcessor groupProcessor; bool recordHistoryAndStatus = false; if (WI_IsFlagSet(flags, ApplyConfigurationSetFlags::PerformConsistencyCheckOnly)) { // If performing a consistency check, always use the default processor and let it know as well auto defaultGroupProcessor = make_self>(); defaultGroupProcessor->Initialize(configurationSet, nullptr, m_threadGlobals, true); groupProcessor = *defaultGroupProcessor; } else { groupProcessor = GetSetGroupProcessor(configurationSet); // Write this set to the database history // This is a somewhat arbitrary time to write it, but it should not be done if PerformConsistencyCheckOnly is passed, so this is convenient. recordHistoryAndStatus = true; m_database.EnsureOpened(); progress.ThrowIfCancelled(); m_database.WriteSetHistory(configurationSet, WI_IsFlagSet(flags, ApplyConfigurationSetFlags::DoNotOverwriteMatchingOriginSet)); } auto result = make_self>(); // Build out the unit results and a map to find them quickly using UnitResultType = decltype(make_self>()); std::map unitResultMap; std::function&)> createUnitResults = [&](const winrt::Windows::Foundation::Collections::IVector& units) { for (const Configuration::ConfigurationUnit& unit : units) { // Add to result UnitResultType applyUnitResult = make_self>(); applyUnitResult->Unit(unit); result->UnitResultsVector().Append(*applyUnitResult); // Add to map unitResultMap.emplace(unit.InstanceIdentifier(), applyUnitResult); // Handle members if present if (unit.IsGroup()) { createUnitResults(unit.Units()); } } }; createUnitResults(configurationSet.Units()); progress.Result(*result); try { ConfigurationSequencer sequencer{ m_database }; auto status = ConfigurationStatus::Instance(); guid setInstanceIdentifier = configurationSet.InstanceIdentifier(); auto updateState = [&](ConfigurationSetState state) { try { progress.Progress(implementation::ConfigurationSetChangeData::Create(state)); } CATCH_LOG(); if (recordHistoryAndStatus) { status->UpdateSetState(setInstanceIdentifier, state); } }; if (!WI_IsFlagSet(flags, ApplyConfigurationSetFlags::PerformConsistencyCheckOnly)) { if (sequencer.Enqueue(configurationSet)) { updateState(ConfigurationSetState::Pending); sequencer.Wait(progress.GetCancellation()); } } progress.ThrowIfCancelled(); updateState(ConfigurationSetState::InProgress); // Forward unit result progress to caller auto applyOperation = groupProcessor.ApplyGroupSettingsAsync([&](const auto&, const IApplyGroupMemberSettingsResult& unitResult) { auto itr = unitResultMap.find(unitResult.Unit().InstanceIdentifier()); if (itr != unitResultMap.end()) { itr->second->Initialize(unitResult); } // Create progress object auto applyResult = make_self(); applyResult->Initialize(unitResult); progress.Progress(*applyResult); if (recordHistoryAndStatus) { status->UpdateUnitState(setInstanceIdentifier, applyResult); } }); // Cancel the inner operation if we are cancelled progress.Callback([applyOperation]() { applyOperation.Cancel(); }); IApplyGroupSettingsResult applyResult = applyOperation.get(); // Place all results from the processor into our result if (applyResult.ResultInformation()) { result->ResultCode(applyResult.ResultInformation().ResultCode()); } for (const IApplyGroupMemberSettingsResult& unitResult : applyResult.UnitResults()) { // Update overall result auto itr = unitResultMap.find(unitResult.Unit().InstanceIdentifier()); if (itr == unitResultMap.end()) { continue; } itr->second->Initialize(unitResult); m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate( configurationSet.InstanceIdentifier(), itr->second->Unit(), ConfigurationUnitIntent::Apply, TelemetryTraceLogger::ApplyAction, itr->second->ResultInformation()); } updateState(ConfigurationSetState::Completed); m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForApply(*winrt::get_self(configurationSet), *result); return *result; } catch (...) { m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForApplyException( *winrt::get_self(configurationSet), LOG_CAUGHT_EXCEPTION(), *result); throw; } } Configuration::TestConfigurationSetResult ConfigurationProcessor::TestSet(const Configuration::ConfigurationSet& configurationSet) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return TestSetImpl(configurationSet); } Windows::Foundation::IAsyncOperationWithProgress ConfigurationProcessor::TestSetAsync(const Configuration::ConfigurationSet& configurationSet) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; Configuration::ConfigurationSet localSet = configurationSet; co_await winrt::resume_background(); co_return TestSetImpl(localSet, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token() }); } Configuration::TestConfigurationSetResult ConfigurationProcessor::TestSetImpl( const Configuration::ConfigurationSet& configurationSet, ShutdownAwareAsyncProgress progress) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationGroupProcessor groupProcessor = GetSetGroupProcessor(configurationSet); auto result = make_self>(); result->TestResult(ConfigurationTestResult::NotRun); progress.Result(*result); try { // Forward unit result progress to caller auto testOperation = groupProcessor.TestGroupSettingsAsync([&](const auto&, const ITestSettingsResult& unitResult) { auto testResult = make_self>(); testResult->Initialize(unitResult); result->AppendUnitResult(*testResult); progress.Progress(*testResult); }); // Cancel the inner operation if we are cancelled progress.Callback([testOperation]() { testOperation.Cancel(); }); ITestGroupSettingsResult testResult = testOperation.get(); // Send telemetry for all results for (const ITestSettingsResult& unitResult : testResult.UnitResults()) { auto testUnitResult = make_self>(); testUnitResult->Initialize(unitResult); m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate( configurationSet.InstanceIdentifier(), testUnitResult->Unit(), ConfigurationUnitIntent::Assert, TelemetryTraceLogger::TestAction, testUnitResult->ResultInformation()); } m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForTest(*winrt::get_self(configurationSet), *result); return *result; } catch (...) { m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForTestException( *winrt::get_self(configurationSet), LOG_CAUGHT_EXCEPTION(), *result); throw; } } Configuration::GetConfigurationUnitSettingsResult ConfigurationProcessor::GetUnitSettings(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return GetUnitSettingsImpl(unit); } Windows::Foundation::IAsyncOperation ConfigurationProcessor::GetUnitSettingsAsync(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; ConfigurationUnit localUnit = unit; co_await winrt::resume_background(); co_return GetUnitSettingsImpl(localUnit, { co_await winrt::get_cancellation_token() }); } Configuration::GetConfigurationUnitSettingsResult ConfigurationProcessor::GetUnitSettingsImpl( const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); auto result = make_self>(); auto unitResult = make_self>(); result->ResultInformation(*unitResult); cancellation.ThrowIfCancelled(); IConfigurationUnitProcessor unitProcessor; try { unitProcessor = setProcessor.CreateUnitProcessor(unit); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } cancellation.ThrowIfCancelled(); if (unitProcessor) { try { IGetSettingsResult settingsResult = unitProcessor.GetSettings(); result->Settings(settingsResult.Settings()); result->ResultInformation(settingsResult.ResultInformation()); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::GetAction, result->ResultInformation()); } return *result; } Configuration::GetAllConfigurationUnitSettingsResult ConfigurationProcessor::GetAllUnitSettings(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return GetAllUnitSettingsImpl(unit); } Windows::Foundation::IAsyncOperation ConfigurationProcessor::GetAllUnitSettingsAsync(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; ConfigurationUnit localUnit = unit; co_await winrt::resume_background(); co_return GetAllUnitSettingsImpl(localUnit, { co_await winrt::get_cancellation_token() }); } Configuration::GetAllConfigurationUnitSettingsResult ConfigurationProcessor::GetAllUnitSettingsImpl( const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); auto result = make_self>(); auto unitResult = make_self>(); result->ResultInformation(*unitResult); cancellation.ThrowIfCancelled(); IConfigurationUnitProcessor unitProcessor; try { unitProcessor = setProcessor.CreateUnitProcessor(unit); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } cancellation.ThrowIfCancelled(); IGetAllSettingsConfigurationUnitProcessor getAllSettingsUnitProcessor; if (unitProcessor.try_as(getAllSettingsUnitProcessor)) { cancellation.ThrowIfCancelled(); try { IGetAllSettingsResult allSettingsResult = getAllSettingsUnitProcessor.GetAllSettings(); result->Settings(allSettingsResult.Settings()); result->ResultInformation(allSettingsResult.ResultInformation()); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::ExportAction, result->ResultInformation()); } else { AICLI_LOG(Config, Error, << "Unit Processor does not support GetAllSettings operation"); unitResult->Initialize(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR, hstring{}); } return *result; } Configuration::GetAllConfigurationUnitsResult ConfigurationProcessor::GetAllUnits(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return GetAllUnitsImpl(unit); } Windows::Foundation::IAsyncOperation ConfigurationProcessor::GetAllUnitsAsync(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; ConfigurationUnit localUnit = unit; co_await winrt::resume_background(); co_return GetAllUnitsImpl(localUnit, { co_await winrt::get_cancellation_token() }); } Configuration::GetAllConfigurationUnitsResult ConfigurationProcessor::GetAllUnitsImpl( const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); auto result = make_self>(); auto unitResult = make_self>(); result->ResultInformation(*unitResult); cancellation.ThrowIfCancelled(); IConfigurationUnitProcessor unitProcessor; try { unitProcessor = setProcessor.CreateUnitProcessor(unit); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } cancellation.ThrowIfCancelled(); IGetAllUnitsConfigurationUnitProcessor getAllUnitsUnitProcessor; IGetAllSettingsConfigurationUnitProcessor getAllSettingsUnitProcessor; if (unitProcessor.try_as(getAllUnitsUnitProcessor)) { cancellation.ThrowIfCancelled(); try { IGetAllUnitsResult allUnitsResult = getAllUnitsUnitProcessor.GetAllUnits(); result->Units(allUnitsResult.Units()); result->ResultInformation(allUnitsResult.ResultInformation()); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::ExportAction, result->ResultInformation()); } else if (unitProcessor.try_as(getAllSettingsUnitProcessor)) { cancellation.ThrowIfCancelled(); try { IGetAllSettingsResult allSettingsResult = getAllSettingsUnitProcessor.GetAllSettings(); auto allSettings = allSettingsResult.Settings(); if (allSettings) { std::vector units; size_t index = 0; auto currentType = unit.Type(); auto currentDetails = unit.Details(); for (const auto& settings : allSettings) { auto newUnit = make_self(); newUnit->Type(currentType); newUnit->Settings(settings); newUnit->Details(currentDetails); std::wostringstream identifierStream; identifierStream << static_cast(currentType) << L'-' << index++; newUnit->Identifier(hstring{ identifierStream.str() }); units.push_back(*newUnit); } result->Units(single_threaded_vector(std::move(units))); } result->ResultInformation(allSettingsResult.ResultInformation()); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::ExportAction, result->ResultInformation()); } else { AICLI_LOG(Config, Error, << "Unit Processor does not support GetAllUnits or GetAllSettings operation"); unitResult->Initialize(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR, hstring{}); } return *result; } Windows::Foundation::Collections::IVector ConfigurationProcessor::FindUnitProcessors(const FindUnitProcessorsOptions& findOptions) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return FindUnitProcessorsImpl(findOptions); } Windows::Foundation::IAsyncOperation> ConfigurationProcessor::FindUnitProcessorsAsync(const FindUnitProcessorsOptions& findOptions) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; FindUnitProcessorsOptions localOptions = findOptions; co_await winrt::resume_background(); co_return FindUnitProcessorsImpl(localOptions, { co_await winrt::get_cancellation_token() }); } Windows::Foundation::Collections::IVector ConfigurationProcessor::FindUnitProcessorsImpl( const FindUnitProcessorsOptions& findOptions, ShutdownAwareAsyncCancellation cancellation) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); cancellation.ThrowIfCancelled(); IFindUnitProcessorsSetProcessor findUnitProcessorsSetProcessor; if (setProcessor.try_as(findUnitProcessorsSetProcessor)) { return findUnitProcessorsSetProcessor.FindUnitProcessors(findOptions); } else { AICLI_LOG(Config, Error, << "Set Processor does not support FindUnitProcessors operation"); THROW_HR(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR); } } Configuration::ApplyConfigurationUnitResult ConfigurationProcessor::ApplyUnit(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return ApplyUnitImpl(unit); } Windows::Foundation::IAsyncOperation ConfigurationProcessor::ApplyUnitAsync(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; ConfigurationUnit localUnit = unit; co_await winrt::resume_background(); co_return ApplyUnitImpl(localUnit, { co_await winrt::get_cancellation_token() }); } Configuration::ApplyConfigurationUnitResult ConfigurationProcessor::ApplyUnitImpl( const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); auto result = make_self>(); auto unitResult = make_self>(); result->Unit(unit); result->ResultInformation(*unitResult); cancellation.ThrowIfCancelled(); IConfigurationUnitProcessor unitProcessor; try { unitProcessor = setProcessor.CreateUnitProcessor(unit); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } cancellation.ThrowIfCancelled(); if (unitProcessor) { try { auto applyResult = unitProcessor.ApplySettings(); result->Unit(applyResult.Unit()); result->State(Configuration::ConfigurationUnitState::Completed); result->ResultInformation(applyResult.ResultInformation()); result->RebootRequired(applyResult.RebootRequired()); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Apply, TelemetryTraceLogger::ApplyAction, result->ResultInformation()); } return *result; } Configuration::TestConfigurationUnitResult ConfigurationProcessor::TestUnit(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); return TestUnitImpl(unit); } Windows::Foundation::IAsyncOperation ConfigurationProcessor::TestUnitAsync(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); auto strong_this{ get_strong() }; ConfigurationUnit localUnit = unit; co_await winrt::resume_background(); co_return TestUnitImpl(localUnit, { co_await winrt::get_cancellation_token() }); } Configuration::TestConfigurationUnitResult ConfigurationProcessor::TestUnitImpl( const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation) { auto threadGlobals = m_threadGlobals.SetForCurrentThread(); IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); auto result = make_self>(); auto unitResult = make_self>(); result->Unit(unit); result->ResultInformation(*unitResult); cancellation.ThrowIfCancelled(); IConfigurationUnitProcessor unitProcessor; try { unitProcessor = setProcessor.CreateUnitProcessor(unit); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } cancellation.ThrowIfCancelled(); if (unitProcessor) { try { auto testResult = unitProcessor.TestSettings(); result->Unit(testResult.Unit()); result->TestResult(testResult.TestResult()); result->ResultInformation(testResult.ResultInformation()); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Assert, TelemetryTraceLogger::TestAction, result->ResultInformation()); } return *result; } IConfigurationGroupProcessor ConfigurationProcessor::GetSetGroupProcessor(const Configuration::ConfigurationSet& configurationSet) { IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(configurationSet); IConfigurationGroupProcessor result = setProcessor.try_as(); if (!result) { auto groupProcessor = make_self>(); groupProcessor->Initialize(configurationSet, setProcessor, m_threadGlobals); result = *groupProcessor; } return result; } HRESULT STDMETHODCALLTYPE ConfigurationProcessor::SetLifetimeWatcher(IUnknown* watcher) { return AppInstaller::WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher); } void ConfigurationProcessor::ConfigurationSetProcessorFactory(const IConfigurationSetProcessorFactory& value) { m_factory = value; if (m_factory) { m_factoryDiagnosticsEventRevoker = m_factory.Diagnostics(winrt::auto_revoke, [weak_this{ get_weak() }](const IInspectable&, const IDiagnosticInformation& information) { if (auto strong_this{ weak_this.get() }) { strong_this->SendDiagnostics(information); } }); } } void ConfigurationProcessor::SendDiagnostics(DiagnosticLevel level, std::string_view message) try { if (level >= m_minimumLevel) { auto diagnostics = make_self>(); diagnostics->Initialize(level, AppInstaller::Utility::ConvertToUTF16(message)); SendDiagnosticsImpl(*diagnostics); } } // While diagnostics can be important, a failure to send them should not cause additional issues. catch (...) {} void ConfigurationProcessor::SendDiagnostics(const IDiagnosticInformation& information) try { if (information.Level() >= m_minimumLevel) { SendDiagnosticsImpl(information); } } // While diagnostics can be important, a failure to send them should not cause additional issues. catch (...) {} void ConfigurationProcessor::SendDiagnosticsImpl(const IDiagnosticInformation& information) { std::lock_guard lock{ m_diagnosticsMutex }; // Prevent a winrt/wil error recursion here by detecting that this thread failed to send a previous message. if (m_isHandlingDiagnostics) { std::wstring debugMessage = L"An error occurred while trying to send a previous diagnostics message:\n"; debugMessage.append(information.Message()); OutputDebugStringW(debugMessage.c_str()); return; } m_isHandlingDiagnostics = true; auto notHandling = wil::scope_exit([&] { m_isHandlingDiagnostics = false; }); m_diagnostics(*this, information); } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationProcessor.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationProcessor.g.h" #include #include #include #include "ConfigThreadGlobals.h" #include "ConfigurationStatus.h" #include "Database/ConfigurationDatabase.h" #include #include "ShutdownSynchronization.h" #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationProcessor : ConfigurationProcessorT>, AppInstaller::WinRT::LifetimeWatcherBase { using ConfigurationSetChangeData = Configuration::ConfigurationSetChangeData; using ConfigurationUnit = Configuration::ConfigurationUnit; using ApplyConfigurationSetResult = Configuration::ApplyConfigurationSetResult; using TestConfigurationSetResult = Configuration::TestConfigurationSetResult; using TestConfigurationUnitResult = Configuration::TestConfigurationUnitResult; using GetConfigurationUnitSettingsResult = Configuration::GetConfigurationUnitSettingsResult; using GetAllConfigurationUnitSettingsResult = Configuration::GetAllConfigurationUnitSettingsResult; using GetConfigurationSetDetailsResult = Configuration::GetConfigurationSetDetailsResult; using GetConfigurationUnitDetailsResult = Configuration::GetConfigurationUnitDetailsResult; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) ConfigurationProcessor(); #endif ConfigurationProcessor(const IConfigurationSetProcessorFactory& factory); event_token Diagnostics(const Windows::Foundation::EventHandler& handler); void Diagnostics(const event_token& token) noexcept; DiagnosticLevel MinimumLevel(); void MinimumLevel(DiagnosticLevel value); hstring Caller() const; void Caller(hstring value); guid ActivityIdentifier(); void ActivityIdentifier(const guid& value); bool GenerateTelemetryEvents(); void GenerateTelemetryEvents(bool value); event_token ConfigurationChange(const Windows::Foundation::TypedEventHandler& handler); void ConfigurationChange(const event_token& token) noexcept; Windows::Foundation::Collections::IVector GetConfigurationHistory(); Windows::Foundation::IAsyncOperation> GetConfigurationHistoryAsync(); Configuration::OpenConfigurationSetResult OpenConfigurationSet(const Windows::Storage::Streams::IInputStream& stream); Windows::Foundation::IAsyncOperation OpenConfigurationSetAsync(const Windows::Storage::Streams::IInputStream& stream); Windows::Foundation::Collections::IVector CheckForConflicts( const Windows::Foundation::Collections::IVectorView& configurationSets, bool includeConfigurationHistory); Windows::Foundation::IAsyncOperation> CheckForConflictsAsync( const Windows::Foundation::Collections::IVectorView& configurationSets, bool includeConfigurationHistory); GetConfigurationSetDetailsResult GetSetDetails(const Configuration::ConfigurationSet& configurationSet, ConfigurationUnitDetailFlags detailFlags); Windows::Foundation::IAsyncOperationWithProgress GetSetDetailsAsync(const Configuration::ConfigurationSet& configurationSet, ConfigurationUnitDetailFlags detailFlags); GetConfigurationUnitDetailsResult GetUnitDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags); Windows::Foundation::IAsyncOperation GetUnitDetailsAsync(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags); ApplyConfigurationSetResult ApplySet(const Configuration::ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags); Windows::Foundation::IAsyncOperationWithProgress ApplySetAsync(const Configuration::ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags); TestConfigurationSetResult TestSet(const Configuration::ConfigurationSet& configurationSet); Windows::Foundation::IAsyncOperationWithProgress TestSetAsync(const Configuration::ConfigurationSet& configurationSet); GetConfigurationUnitSettingsResult GetUnitSettings(const ConfigurationUnit& unit); Windows::Foundation::IAsyncOperation GetUnitSettingsAsync(const ConfigurationUnit& unit); GetAllConfigurationUnitSettingsResult GetAllUnitSettings(const ConfigurationUnit& unit); Windows::Foundation::IAsyncOperation GetAllUnitSettingsAsync(const ConfigurationUnit& unit); Configuration::GetAllConfigurationUnitsResult GetAllUnits(const ConfigurationUnit& unit); Windows::Foundation::IAsyncOperation GetAllUnitsAsync(const ConfigurationUnit& unit); Windows::Foundation::Collections::IVector FindUnitProcessors(const Configuration::FindUnitProcessorsOptions& findOptions); Windows::Foundation::IAsyncOperation> FindUnitProcessorsAsync(const Configuration::FindUnitProcessorsOptions& findOptions); Configuration::ApplyConfigurationUnitResult ApplyUnit(const ConfigurationUnit& unit); Windows::Foundation::IAsyncOperation ApplyUnitAsync(const ConfigurationUnit& unit); Configuration::TestConfigurationUnitResult TestUnit(const ConfigurationUnit& unit); Windows::Foundation::IAsyncOperation TestUnitAsync(const ConfigurationUnit& unit); HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void ConfigurationSetProcessorFactory(const IConfigurationSetProcessorFactory& value); // Sends diagnostics objects to the event. void SendDiagnostics(DiagnosticLevel level, std::string_view message); // Sends diagnostics objects to the event. void SendDiagnostics(const IDiagnosticInformation& information); // Indicate a configuration change occurred. void ConfigurationChange(const Configuration::ConfigurationSet& set, const Configuration::ConfigurationChangeData& data); private: Windows::Foundation::Collections::IVector GetConfigurationHistoryImpl(ShutdownAwareAsyncCancellation cancellation = {}); GetConfigurationSetDetailsResult GetSetDetailsImpl( const Configuration::ConfigurationSet& configurationSet, ConfigurationUnitDetailFlags detailFlags, ShutdownAwareAsyncProgress progress = {}); GetConfigurationUnitDetailsResult GetUnitDetailsImpl(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags); ApplyConfigurationSetResult ApplySetImpl( const Configuration::ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags, ShutdownAwareAsyncProgress progress = {}); TestConfigurationSetResult TestSetImpl( const Configuration::ConfigurationSet& configurationSet, ShutdownAwareAsyncProgress progress = {}); GetConfigurationUnitSettingsResult GetUnitSettingsImpl(const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation = {}); GetAllConfigurationUnitSettingsResult GetAllUnitSettingsImpl(const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation = {}); Configuration::GetAllConfigurationUnitsResult GetAllUnitsImpl(const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation = {}); Windows::Foundation::Collections::IVector FindUnitProcessorsImpl(const Configuration::FindUnitProcessorsOptions& findOptions, ShutdownAwareAsyncCancellation cancellation = {}); Configuration::ApplyConfigurationUnitResult ApplyUnitImpl(const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation = {}); Configuration::TestConfigurationUnitResult TestUnitImpl(const ConfigurationUnit& unit, ShutdownAwareAsyncCancellation cancellation = {}); IConfigurationGroupProcessor GetSetGroupProcessor(const Configuration::ConfigurationSet& configurationSet); void SendDiagnosticsImpl(const IDiagnosticInformation& information); IConfigurationSetProcessorFactory m_factory = nullptr; event> m_diagnostics; event> m_configurationChange; ConfigThreadGlobals m_threadGlobals; IConfigurationSetProcessorFactory::Diagnostics_revoker m_factoryDiagnosticsEventRevoker; DiagnosticLevel m_minimumLevel = DiagnosticLevel::Informational; std::recursive_mutex m_diagnosticsMutex; ConfigurationDatabase m_database; bool m_isHandlingDiagnostics = false; std::shared_ptr m_changeRegistration; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Configuration::factory_implementation { struct ConfigurationProcessor : ConfigurationProcessorT { }; } #endif ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSequencer.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSequencer.h" #include "ConfigurationStatus.h" #include using namespace std::chrono_literals; namespace winrt::Microsoft::Management::Configuration::implementation { ConfigurationSequencer::ConfigurationSequencer(ConfigurationDatabase& database) : m_database(database) {} ConfigurationSequencer::~ConfigurationSequencer() { // Best effort attempt to remove our queue row try { m_database.RemoveQueueItem(m_queueItemObjectName); auto status = ConfigurationStatus::Instance(); status->UpdateSetState(m_setInstanceIdentifier, false); } CATCH_LOG(); } // This function creates necessary objects and records this operation into the table. // It then performs the equivalent of `Wait` with a timeout of 0. bool ConfigurationSequencer::Enqueue(const Configuration::ConfigurationSet& configurationSet) { m_setInstanceIdentifier = configurationSet.InstanceIdentifier(); // Create an arbitrarily named object std::wstring objectName = L"WinGetConfigQueue_" + AppInstaller::Utility::CreateNewGuidNameWString(); m_queueItemObjectName = AppInstaller::Utility::ConvertToUTF8(objectName); m_queueItemObject.create(wil::EventOptions::None, objectName.c_str()); m_database.AddQueueItem(configurationSet, m_queueItemObjectName); auto statusInstance = ConfigurationStatus::Instance(); statusInstance->UpdateSetState(m_setInstanceIdentifier, true); // Create shared mutex constexpr PCWSTR applyMutexName = L"WinGetConfigQueueApplyMutex"; for (int i = 0; !m_applyMutex && i < 2; ++i) { if (!m_applyMutex.try_create(applyMutexName, 0, SYNCHRONIZE)) { m_applyMutex.try_open(applyMutexName, SYNCHRONIZE); } } THROW_LAST_ERROR_IF(!m_applyMutex); // Probe for an empty queue DWORD status = 0; m_applyMutexScope = m_applyMutex.acquire(&status, 0); THROW_LAST_ERROR_IF(status == WAIT_FAILED); if (status == WAIT_TIMEOUT) { return true; } if (GetQueuePosition() == 0) { m_database.SetActiveQueueItem(m_queueItemObjectName); return false; } else { m_applyMutexScope.reset(); return true; } } // The configuration queue consists of a table in the shared database and cooperative handling of said table. // At any moment, the active processor must be holding a common named mutex. // Each active queue entry also holds their own arbitrarily named object, recorded in the table. // // The general mechanism to wait is: // 1. Wait on common named mutex // 2. Check if first in queue, including probing arbitrary named objects of entries ahead of us // 3. If not first, wait for X * queue position, where X is sufficiently high to prevent contention on main mutex void ConfigurationSequencer::Wait(AppInstaller::WinRT::AsyncCancellation& cancellation) { THROW_HR_IF(E_NOT_VALID_STATE, !m_applyMutex); wil::unique_event cancellationEvent; cancellationEvent.create(); HANDLE waitHandles[2]; waitHandles[0] = cancellationEvent.get(); waitHandles[1] = m_applyMutex.get(); cancellation.Callback([&]() { cancellationEvent.SetEvent(); }); auto clearCancelCallback = wil::scope_exit([&cancellation]() { cancellation.Callback([]() {}); }); for (;;) { DWORD waitResult = WaitForMultipleObjects(ARRAYSIZE(waitHandles), waitHandles, FALSE, INFINITE); THROW_LAST_ERROR_IF(waitResult == WAIT_FAILED); if (waitResult == WAIT_OBJECT_0) { // Cancellation break; } else if (waitResult == WAIT_OBJECT_0 + 1 || waitResult == WAIT_ABANDONED_0 + 1) { // We now hold the apply mutex wil::mutex_release_scope_exit applyMutexScope{ m_applyMutex.get() }; size_t queuePosition = GetQueuePosition(); if (queuePosition == 0) { m_applyMutexScope = std::move(applyMutexScope); m_database.SetActiveQueueItem(m_queueItemObjectName); break; } else { applyMutexScope.reset(); std::this_thread::sleep_for(queuePosition * 100ms); } } } } size_t ConfigurationSequencer::GetQueuePosition() { auto queueItems = m_database.GetQueueItems(); // If we get no queue items at all, we assume that the database doesn't support queueing. if (queueItems.empty()) { return 0; } size_t result = 0; bool found = false; for (const auto& item : queueItems) { if (item.ObjectName == m_queueItemObjectName) { found = true; break; } std::wstring objectName = AppInstaller::Utility::ConvertToUTF16(item.ObjectName); QueueObjectType itemObject; if (itemObject.try_open(objectName.c_str(), SYNCHRONIZE)) { ++result; } else { // Best effort attempt to remove the dead queue row try { m_database.RemoveQueueItem(item.ObjectName); } CATCH_LOG(); } } THROW_HR_IF(E_NOT_SET, !found); return result; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSequencer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Database/ConfigurationDatabase.h" #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Allows for sequencing of configuration set applications. struct ConfigurationSequencer { ConfigurationSequencer(ConfigurationDatabase& database); ConfigurationSequencer(const ConfigurationSequencer&) = delete; ConfigurationSequencer& operator=(const ConfigurationSequencer&) = delete; ConfigurationSequencer(ConfigurationSequencer&&) = delete; ConfigurationSequencer& operator=(ConfigurationSequencer&&) = delete; ~ConfigurationSequencer(); // Enters the current sequencer into the queue of operations. // Returns true to indicate that this operation has been queued and must wait. // Returns false to indicate that this operation is able to proceed (queued directly to the front). bool Enqueue(const Configuration::ConfigurationSet& configurationSet); // Waits for this operation to reach the front of the queue. // Registers a cancellation callback so that we can halt our waiting. void Wait(AppInstaller::WinRT::AsyncCancellation& cancellation); private: // Determines the effective queue position of this operation; removing queue entries that are not longer active. // 0 is the front of the queue. size_t GetQueuePosition(); using QueueObjectType = wil::unique_event; ConfigurationDatabase& m_database; guid m_setInstanceIdentifier{}; std::string m_queueItemObjectName; QueueObjectType m_queueItemObject; wil::unique_mutex m_applyMutex; wil::mutex_release_scope_exit m_applyMutexScope; }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSet.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSet.h" #include "ConfigurationSet.g.cpp" #include "ConfigurationSetParser.h" #include "ConfigurationSetSerializer.h" #include "Database/ConfigurationDatabase.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { namespace impl { struct EnvironmentData { EnvironmentData(Configuration::ConfigurationEnvironment environment) : Environment(environment), Context(environment.Context()), Identifier(environment.ProcessorIdentifier()), Properties(environment.ProcessorProperties()) { } EnvironmentData(EnvironmentData&&) = default; bool operator==(const EnvironmentData& other) const { return Context == other.Context && Identifier == other.Identifier && ConfigurationEnvironment::AreEqual(Properties, other.Properties); } Configuration::ConfigurationEnvironment Environment; SecurityContext Context; hstring Identifier; Windows::Foundation::Collections::IMap Properties; }; bool ContainsEnvironment(const std::vector& uniqueEnvironments, const EnvironmentData& data) { for (const EnvironmentData& item : uniqueEnvironments) { if (item == data) { return true; } } return false; } void ComputeUniqueEnvironments(std::vector& uniqueEnvironments, const Windows::Foundation::Collections::IVector& units) { for (const Configuration::ConfigurationUnit& unit : units) { if (unit.IsActive()) { EnvironmentData data{ unit.Environment() }; if (!ContainsEnvironment(uniqueEnvironments, data)) { uniqueEnvironments.emplace_back(std::move(data)); } if (unit.IsGroup()) { ComputeUniqueEnvironments(uniqueEnvironments, unit.Units()); } } } } } ConfigurationSet::ConfigurationSet() { GUID instanceIdentifier; THROW_IF_FAILED(CoCreateGuid(&instanceIdentifier)); m_instanceIdentifier = instanceIdentifier; std::tie(m_schemaVersion, m_schemaUri) = ConfigurationSetParser::LatestVersion(); } ConfigurationSet::ConfigurationSet(const guid& instanceIdentifier) : m_instanceIdentifier(instanceIdentifier) { } void ConfigurationSet::Units(std::vector&& units) { m_units = winrt::multi_threaded_vector(std::move(units)); } void ConfigurationSet::Parameters(std::vector&& value) { m_parameters = winrt::multi_threaded_vector(std::move(value)); } hstring ConfigurationSet::Name() { return m_name; } void ConfigurationSet::Name(const hstring& value) { m_name = value; } hstring ConfigurationSet::Origin() { return m_origin; } void ConfigurationSet::Origin(const hstring& value) { m_origin = value; } hstring ConfigurationSet::Path() { return m_path; } void ConfigurationSet::Path(const hstring& value) { m_path = value; } guid ConfigurationSet::InstanceIdentifier() const { return m_instanceIdentifier; } ConfigurationSetState ConfigurationSet::State() { auto status = ConfigurationStatus::Instance(); return status->GetSetState(m_instanceIdentifier); } clock::time_point ConfigurationSet::FirstApply() { auto status = ConfigurationStatus::Instance(); return status->GetSetFirstApply(m_instanceIdentifier); } clock::time_point ConfigurationSet::ApplyBegun() { auto status = ConfigurationStatus::Instance(); return status->GetSetApplyBegun(m_instanceIdentifier); } clock::time_point ConfigurationSet::ApplyEnded() { auto status = ConfigurationStatus::Instance(); return status->GetSetApplyEnded(m_instanceIdentifier); } Windows::Foundation::Collections::IVector ConfigurationSet::Units() { return m_units; } void ConfigurationSet::Units(const Windows::Foundation::Collections::IVector& value) { THROW_HR_IF(E_POINTER, !value); m_units = value; } hstring ConfigurationSet::SchemaVersion() { return m_schemaVersion; } void ConfigurationSet::SchemaVersion(const hstring& value) { THROW_HR_IF(E_INVALIDARG, !ConfigurationSetParser::IsRecognizedSchemaVersion(value)); m_schemaUri = ConfigurationSetParser::GetSchemaUriForVersion(value); m_schemaVersion = value; } void ConfigurationSet::ConfigurationSetChange(com_ptr& data, const std::optional& unitInstanceIdentifier) try { if (unitInstanceIdentifier) { Windows::Foundation::Collections::IVector comUnits = m_units; std::vector units{ comUnits.Size() }; units.resize(comUnits.GetMany(0, units)); for (const ConfigurationUnit& unit : units) { if (unit.InstanceIdentifier() == unitInstanceIdentifier.value()) { data->Unit(unit); break; } } } m_configurationSetChange(*get_strong(), *data); } CATCH_LOG(); event_token ConfigurationSet::ConfigurationSetChange(const Windows::Foundation::TypedEventHandler& handler) { if (!m_configurationSetChange) { auto status = ConfigurationStatus::Instance(); std::atomic_store(&m_setChangeRegistration, status->RegisterForSetChange(*this)); } return m_configurationSetChange.add(handler); } void ConfigurationSet::ConfigurationSetChange(const event_token& token) noexcept { m_configurationSetChange.remove(token); if (!m_configurationSetChange) { std::atomic_store(&m_setChangeRegistration, {}); } } void ConfigurationSet::Serialize(const Windows::Storage::Streams::IOutputStream& stream) { std::unique_ptr serializer = ConfigurationSetSerializer::CreateSerializer(m_schemaVersion); hstring result = serializer->Serialize(this); auto resultUtf8 = winrt::to_string(result); std::vector bytes(resultUtf8.begin(), resultUtf8.end()); Windows::Storage::Streams::DataWriter dataWriter{ stream }; dataWriter.WriteBytes(bytes); dataWriter.StoreAsync().get(); dataWriter.DetachStream(); } void ConfigurationSet::Remove() { ConfigurationDatabase database; database.EnsureOpened(false); database.RemoveSetHistory(*get_strong()); } Windows::Foundation::Collections::ValueSet ConfigurationSet::Metadata() { return m_metadata; } void ConfigurationSet::Metadata(const Windows::Foundation::Collections::ValueSet& value) { THROW_HR_IF(E_POINTER, !value); m_metadata = value; } Windows::Foundation::Collections::IVector ConfigurationSet::Parameters() { return m_parameters; } void ConfigurationSet::Parameters(const Windows::Foundation::Collections::IVector& value) { THROW_HR_IF(E_POINTER, !value); m_parameters = value; } Windows::Foundation::Collections::ValueSet ConfigurationSet::Variables() { return m_variables; } void ConfigurationSet::Variables(const Windows::Foundation::Collections::ValueSet& value) { THROW_HR_IF(E_POINTER, !value); m_variables = value; } Windows::Foundation::Uri ConfigurationSet::SchemaUri() { return m_schemaUri; } void ConfigurationSet::SchemaUri(const Windows::Foundation::Uri& value) { THROW_HR_IF(E_INVALIDARG, !ConfigurationSetParser::IsRecognizedSchemaUri(value)); m_schemaVersion = ConfigurationSetParser::GetSchemaVersionForUri(value); m_schemaUri = value; } Configuration::ConfigurationEnvironment ConfigurationSet::Environment() { return *m_environment; } implementation::ConfigurationEnvironment& ConfigurationSet::EnvironmentInternal() { return *m_environment; } std::vector ConfigurationSet::GetUnitEnvironmentsInternal() { std::vector uniqueEnvironments; ComputeUniqueEnvironments(uniqueEnvironments, m_units); std::vector result; for (const impl::EnvironmentData& data : uniqueEnvironments) { result.emplace_back(*make_self(data.Environment)); } return result; } Windows::Foundation::Collections::IVector ConfigurationSet::GetUnitEnvironments() { return single_threaded_vector(GetUnitEnvironmentsInternal()); } HRESULT STDMETHODCALLTYPE ConfigurationSet::SetLifetimeWatcher(IUnknown* watcher) { return AppInstaller::WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher); } void ConfigurationSet::SetInputHash(std::string inputHash) { m_inputHash = std::move(inputHash); } const std::string& ConfigurationSet::GetInputHash() const { return m_inputHash; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSet.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSet.g.h" #include "ConfigurationEnvironment.h" #include "ConfigurationSetChangeData.h" #include "ConfigurationStatus.h" #include #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationSet : ConfigurationSetT>, AppInstaller::WinRT::LifetimeWatcherBase, AppInstaller::WinRT::ModuleCountBase { using WinRT_Self = ::winrt::Microsoft::Management::Configuration::ConfigurationSet; using ConfigurationUnit = ::winrt::Microsoft::Management::Configuration::ConfigurationUnit; using ConfigurationParameter = ::winrt::Microsoft::Management::Configuration::ConfigurationParameter; ConfigurationSet(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) ConfigurationSet(const guid& instanceIdentifier); void Units(std::vector&& units); void Parameters(std::vector&& value); void ConfigurationSetChange(com_ptr& data, const std::optional& unitInstanceIdentifier); implementation::ConfigurationEnvironment& EnvironmentInternal(); std::vector GetUnitEnvironmentsInternal(); #endif hstring Name(); void Name(const hstring& value); hstring Origin(); void Origin(const hstring& value); hstring Path(); void Path(const hstring& value); guid InstanceIdentifier() const; ConfigurationSetState State(); clock::time_point FirstApply(); clock::time_point ApplyBegun(); clock::time_point ApplyEnded(); Windows::Foundation::Collections::IVector Units(); void Units(const Windows::Foundation::Collections::IVector& value); hstring SchemaVersion(); void SchemaVersion(const hstring& value); event_token ConfigurationSetChange(const Windows::Foundation::TypedEventHandler& handler); void ConfigurationSetChange(const event_token& token) noexcept; void Serialize(const Windows::Storage::Streams::IOutputStream& stream); void Remove(); Windows::Foundation::Collections::ValueSet Metadata(); void Metadata(const Windows::Foundation::Collections::ValueSet& value); Windows::Foundation::Collections::IVector Parameters(); void Parameters(const Windows::Foundation::Collections::IVector& value); Windows::Foundation::Collections::ValueSet Variables(); void Variables(const Windows::Foundation::Collections::ValueSet& value); Windows::Foundation::Uri SchemaUri(); void SchemaUri(const Windows::Foundation::Uri& value); Configuration::ConfigurationEnvironment Environment(); Windows::Foundation::Collections::IVector GetUnitEnvironments(); HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void SetInputHash(std::string inputHash); const std::string& GetInputHash() const; private: hstring m_name; hstring m_origin; hstring m_path; guid m_instanceIdentifier; clock::time_point m_firstApply{}; Windows::Foundation::Collections::IVector m_units{ winrt::multi_threaded_vector() }; hstring m_schemaVersion; winrt::event> m_configurationSetChange; Windows::Foundation::Collections::ValueSet m_metadata; Windows::Foundation::Collections::IVector m_parameters{ winrt::multi_threaded_vector() }; Windows::Foundation::Collections::ValueSet m_variables; Windows::Foundation::Uri m_schemaUri = nullptr; std::string m_inputHash; std::shared_ptr m_setChangeRegistration; com_ptr m_environment{ make_self() }; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Configuration::factory_implementation { struct ConfigurationSet : ConfigurationSetT { }; } #endif ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetApplyProcessor.h" #include "ConfigurationSetChangeData.h" #include "ExceptionResultHelpers.h" #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { namespace { constexpr std::wstring_view s_ResourceType_RunCommandOnSet = L"Microsoft.DSC.Transitional/RunCommandOnSet"; std::string GetNormalizedIdentifier(hstring identifier) { using namespace AppInstaller::Utility; return FoldCase(NormalizedString{ identifier }); } bool AssertFilter(ConfigurationUnitIntent intent) { return intent == ConfigurationUnitIntent::Assert; } bool InformFilter(ConfigurationUnitIntent intent) { return intent == ConfigurationUnitIntent::Inform; } bool ApplyFilter(ConfigurationUnitIntent intent) { return intent == ConfigurationUnitIntent::Apply || intent == ConfigurationUnitIntent::Unknown; } // Check if a unit should always be applied. No TestSettings is needed. bool ShouldApplyAlways(const Configuration::ConfigurationUnit& unit) { if (AppInstaller::Utility::CaseInsensitiveEquals(s_ResourceType_RunCommandOnSet, unit.Type())) { return true; } return false; } } ConfigurationSetApplyProcessor::ConfigurationSetApplyProcessor( const Configuration::ConfigurationSet& configurationSet, IConfigurationSetProcessor setProcessor, progress_type&& progress) : m_configurationSet(configurationSet), m_setProcessor(std::move(setProcessor)), m_result(make_self>()), m_progress(std::move(progress)) { // Create a copy of the set of configuration units auto unitsView = configurationSet.Units(); std::vector unitsToProcess{ unitsView.Size() }; unitsView.GetMany(0, unitsToProcess); // Create the unit info vector from these units for (const auto& unit : unitsToProcess) { m_unitInfo.emplace_back(unit); m_result->UnitResults().Append(*m_unitInfo.back().Result); } m_progress.Result(*m_result); } void ConfigurationSetApplyProcessor::Process(bool preProcessOnly) { if (PreProcess() && !preProcessOnly) { ProcessInternal(HasProcessedSuccessfully, &ConfigurationSetApplyProcessor::ProcessUnit, true); } } IApplyGroupSettingsResult ConfigurationSetApplyProcessor::Result() const { return *m_result; } ConfigurationSetApplyProcessor::UnitInfo::UnitInfo(const Configuration::ConfigurationUnit& unit) : Unit(unit), Result(make_self>()) { Result->Unit(unit); ResultInformation = Result->ResultInformationInternal(); } bool ConfigurationSetApplyProcessor::PreProcess() { bool result = true; for (size_t i = 0; i < m_unitInfo.size(); ++i) { if (!AddUnitToMap(m_unitInfo[i], i)) { result = false; } } if (!result) { // This is the only error that adding to the map can produce m_result->ResultInformationInternal()->ResultCode(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER); return false; } for (UnitInfo& unitInfo : m_unitInfo) { for (hstring dependencyHstring : unitInfo.Unit.Dependencies()) { // Throw out empty dependency strings if (dependencyHstring.empty()) { continue; } std::string dependency = GetNormalizedIdentifier(dependencyHstring); auto itr = m_idToUnitInfoIndex.find(dependency); if (itr == m_idToUnitInfoIndex.end()) { AICLI_LOG(Config, Error, << "Found missing dependency: " << dependency); unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_MISSING_DEPENDENCY, ConfigurationUnitResultSource::ConfigurationSet); unitInfo.ResultInformation->Details(dependencyHstring); SendProgress(ConfigurationUnitState::Completed, unitInfo); result = false; // TODO: Consider collecting all missing dependencies, for now just the first break; } else { unitInfo.DependencyIndices.emplace_back(itr->second); } } } if (!result) { // This is the only error that adding to the map can produce m_result->ResultInformationInternal()->ResultCode(WINGET_CONFIG_ERROR_MISSING_DEPENDENCY); return false; } if (!ProcessInternal(HasPreprocessed, &ConfigurationSetApplyProcessor::MarkPreprocessed)) { // The preprocessing simulates processing as if every unit run was successful. // If it fails, this means that there are unit definitions whose dependencies cannot be satisfied. // The only reason for that is a cycle in the dependency graph somewhere. m_result->ResultInformationInternal()->ResultCode(WINGET_CONFIG_ERROR_SET_DEPENDENCY_CYCLE); return false; } return true; } bool ConfigurationSetApplyProcessor::AddUnitToMap(UnitInfo& unitInfo, size_t unitInfoIndex) { hstring originalIdentifier = unitInfo.Unit.Identifier(); if (originalIdentifier.empty()) { return true; } std::string identifier = GetNormalizedIdentifier(originalIdentifier); auto itr = m_idToUnitInfoIndex.find(identifier); if (itr != m_idToUnitInfoIndex.end()) { AICLI_LOG(Config, Error, << "Found duplicate identifier: " << identifier); // Found a duplicate identifier, mark both as such m_unitInfo[itr->second].ResultInformation->Initialize(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, ConfigurationUnitResultSource::ConfigurationSet); SendProgressIfNotComplete(ConfigurationUnitState::Completed, m_unitInfo[itr->second]); unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, ConfigurationUnitResultSource::ConfigurationSet); SendProgress(ConfigurationUnitState::Completed, unitInfo); return false; } else { m_idToUnitInfoIndex.emplace(std::move(identifier), unitInfoIndex); return true; } } bool ConfigurationSetApplyProcessor::ProcessInternal(CheckDependencyPtr checkDependencyFunction, ProcessUnitPtr processUnitFunction, bool sendProgress) { // Create the set of units that need to be processed std::vector unitsToProcess; for (size_t i = 0, size = m_unitInfo.size(); i < size; ++i) { unitsToProcess.emplace_back(i); } // Always process all ConfigurationUnitIntent::Assert first if (!ProcessIntentInternal( unitsToProcess, checkDependencyFunction, processUnitFunction, AssertFilter, WINGET_CONFIG_ERROR_ASSERTION_FAILED, WINGET_CONFIG_ERROR_ASSERTION_FAILED, sendProgress)) { return false; } // Then all ConfigurationUnitIntent::Inform if (!ProcessIntentInternal( unitsToProcess, checkDependencyFunction, processUnitFunction, InformFilter, WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, sendProgress)) { return false; } // Then all ConfigurationUnitIntent::Apply return ProcessIntentInternal( unitsToProcess, checkDependencyFunction, processUnitFunction, ApplyFilter, E_FAIL, // This should not happen as there are no other intents left WINGET_CONFIG_ERROR_SET_APPLY_FAILED, sendProgress); } bool ConfigurationSetApplyProcessor::ProcessIntentInternal( std::vector& unitsToProcess, CheckDependencyPtr checkDependencyFunction, ProcessUnitPtr processUnitFunction, IntentFilterPtr intentFilter, hresult errorForOtherIntents, hresult errorForFailures, bool sendProgress) { // Always process the first item in the list that is available to be processed bool hasProcessed = true; bool hasFailure = false; while (hasProcessed) { hasProcessed = false; for (auto itr = unitsToProcess.begin(), end = unitsToProcess.end(); itr != end; ++itr) { UnitInfo& unitInfo = m_unitInfo[*itr]; if (HasIntentAndSatisfiedDependencies(unitInfo, intentFilter, checkDependencyFunction)) { if (!(this->*processUnitFunction)(unitInfo)) { hasFailure = true; } unitsToProcess.erase(itr); hasProcessed = true; break; } } } // Mark all remaining items with intent as failed due to dependency bool hasRemainingDependencies = false; for (size_t index : unitsToProcess) { UnitInfo& unitInfo = m_unitInfo[index]; if (intentFilter(unitInfo.Unit.Intent())) { hasRemainingDependencies = true; unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, ConfigurationUnitResultSource::Precondition); if (sendProgress) { SendProgress(ConfigurationUnitState::Skipped, unitInfo); } } } // Any failures are fatal, mark all other units as failed due to that if (hasFailure || hasRemainingDependencies) { for (size_t index : unitsToProcess) { UnitInfo& unitInfo = m_unitInfo[index]; if (!intentFilter(unitInfo.Unit.Intent())) { unitInfo.ResultInformation->Initialize(errorForOtherIntents, ConfigurationUnitResultSource::Precondition); if (sendProgress) { SendProgress(ConfigurationUnitState::Skipped, unitInfo); } } } if (hasFailure) { m_result->ResultInformationInternal()->ResultCode(errorForFailures); } else // hasRemainingDependencies { m_result->ResultInformationInternal()->ResultCode(WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED); } return false; } return true; } bool ConfigurationSetApplyProcessor::HasIntentAndSatisfiedDependencies( const UnitInfo& unitInfo, IntentFilterPtr intentFilter, CheckDependencyPtr checkDependencyFunction) const { bool result = false; if (intentFilter(unitInfo.Unit.Intent())) { result = true; for (size_t dependencyIndex : unitInfo.DependencyIndices) { if (!checkDependencyFunction(m_unitInfo[dependencyIndex])) { result = false; break; } } } return result; } bool ConfigurationSetApplyProcessor::HasPreprocessed(const UnitInfo& unitInfo) { return unitInfo.PreProcessed; } bool ConfigurationSetApplyProcessor::MarkPreprocessed(UnitInfo& unitInfo) { unitInfo.PreProcessed = true; return true; } bool ConfigurationSetApplyProcessor::HasProcessedSuccessfully(const UnitInfo& unitInfo) { return unitInfo.Processed && SUCCEEDED(unitInfo.ResultInformation->ResultCode()); } bool ConfigurationSetApplyProcessor::ProcessUnit(UnitInfo& unitInfo) { m_progress.ThrowIfCancelled(); IConfigurationUnitProcessor unitProcessor; // Once we get this far, consider the unit processed even if we fail to create the actual processor. unitInfo.Processed = true; if (!unitInfo.Unit.IsActive()) { // If the unit is requested to be skipped, we mark it with a failure to prevent any dependency from running. // But we return true from this function to indicate a successful "processing". unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_MANUALLY_SKIPPED, ConfigurationUnitResultSource::Precondition); SendProgress(ConfigurationUnitState::Skipped, unitInfo); return true; } // Send a progress event that we are starting, and prepare one for completion when we exit the function SendProgress(ConfigurationUnitState::InProgress, unitInfo); auto sendCompletedProgress = wil::scope_exit([this, &unitInfo]() { SendProgress(ConfigurationUnitState::Completed, unitInfo); }); try { unitProcessor = m_setProcessor.CreateUnitProcessor(unitInfo.Unit); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitInfo.ResultInformation); return false; } // As the process of creating the unit processor could take a while, check for cancellation again m_progress.ThrowIfCancelled(); bool result = false; try { switch (unitInfo.Unit.Intent()) { case ConfigurationUnitIntent::Assert: { ITestSettingsResult settingsResult = unitProcessor.TestSettings(); if (settingsResult.TestResult() == ConfigurationTestResult::Positive) { result = true; } else if (settingsResult.TestResult() == ConfigurationTestResult::Negative) { unitInfo.ResultInformation->Initialize(WINGET_CONFIG_ERROR_ASSERTION_FAILED, ConfigurationUnitResultSource::Precondition); } else if (settingsResult.TestResult() == ConfigurationTestResult::Failed) { unitInfo.ResultInformation->Initialize(settingsResult.ResultInformation()); } else { unitInfo.ResultInformation->Initialize(E_UNEXPECTED, ConfigurationUnitResultSource::Internal); } } break; case ConfigurationUnitIntent::Inform: { // Force the processor to retrieve the settings IGetSettingsResult settingsResult = unitProcessor.GetSettings(); if (SUCCEEDED(settingsResult.ResultInformation().ResultCode())) { result = true; } else { unitInfo.ResultInformation->Initialize(settingsResult.ResultInformation()); } } break; case ConfigurationUnitIntent::Apply: case ConfigurationUnitIntent::Unknown: { // Check for a group processor and let it do the work if present IConfigurationGroupProcessor groupProcessor = unitProcessor.try_as(); if (groupProcessor) { auto applyOperation = groupProcessor.ApplyGroupSettingsAsync([&](const auto&, const IApplyGroupMemberSettingsResult& unitResult) { m_progress.Progress(unitResult); }); // Cancel the inner operation if we are cancelled m_progress.Callback([applyOperation]() { applyOperation.Cancel(); }); IApplyGroupSettingsResult groupResult = applyOperation.get(); // Put all of the group's unit results in our unit results bool groupPreviouslyInDesiredState = true; for (const auto& groupUnitResult : groupResult.UnitResults()) { m_result->UnitResults().Append(groupUnitResult); groupPreviouslyInDesiredState = groupPreviouslyInDesiredState && groupUnitResult.PreviouslyInDesiredState(); } // Copy the group result into the existing unit result for the group unitInfo.Result->PreviouslyInDesiredState(groupPreviouslyInDesiredState); unitInfo.ResultInformation->Initialize(groupResult.ResultInformation()); if (SUCCEEDED(unitInfo.ResultInformation->ResultCode())) { unitInfo.Result->RebootRequired(groupResult.RebootRequired()); result = true; } } else { ITestSettingsResult testSettingsResult = nullptr; bool applyAlways = ShouldApplyAlways(unitProcessor.Unit()); if (!applyAlways) { testSettingsResult = unitProcessor.TestSettings(); } if (applyAlways || testSettingsResult.TestResult() == ConfigurationTestResult::Negative) { // Just in case testing took a while, check for cancellation before moving on to applying m_progress.ThrowIfCancelled(); IApplySettingsResult applySettingsResult = unitProcessor.ApplySettings(); if (SUCCEEDED(applySettingsResult.ResultInformation().ResultCode())) { unitInfo.Result->RebootRequired(applySettingsResult.RebootRequired()); result = true; } else { unitInfo.ResultInformation->Initialize(applySettingsResult.ResultInformation()); } } else if (testSettingsResult.TestResult() == ConfigurationTestResult::Positive) { unitInfo.Result->PreviouslyInDesiredState(true); result = true; } else if (testSettingsResult.TestResult() == ConfigurationTestResult::Failed) { unitInfo.ResultInformation->Initialize(testSettingsResult.ResultInformation()); } else { unitInfo.ResultInformation->Initialize(E_UNEXPECTED, ConfigurationUnitResultSource::Internal); } } } break; default: unitInfo.ResultInformation->Initialize(E_UNEXPECTED, ConfigurationUnitResultSource::Internal); break; } } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitInfo.ResultInformation); } return result; } void ConfigurationSetApplyProcessor::SendProgress(ConfigurationUnitState state, const UnitInfo& unitInfo) { unitInfo.Result->State(state); try { m_progress.Progress(*unitInfo.Result); } CATCH_LOG(); } void ConfigurationSetApplyProcessor::SendProgressIfNotComplete(ConfigurationUnitState state, const UnitInfo& unitInfo) { if (unitInfo.Result->State() != ConfigurationUnitState::Completed) { SendProgress(state, unitInfo); } } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSet.h" #include "ConfigurationUnit.h" #include "ApplyGroupSettingsResult.h" #include "ApplyConfigurationUnitResult.h" #include "ConfigurationUnitResultInformation.h" #include "ShutdownSynchronization.h" #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // A helper to better organize the configuration set Apply. struct ConfigurationSetApplyProcessor { using ConfigurationSet = Configuration::ConfigurationSet; using ConfigurationUnit = Configuration::ConfigurationUnit; using ConfigurationSetChangeData = Configuration::ConfigurationSetChangeData; using result_type = decltype(make_self>()); using progress_type = ShutdownAwareAsyncProgress; ConfigurationSetApplyProcessor(const ConfigurationSet& configurationSet, IConfigurationSetProcessor setProcessor, progress_type&& progress); // Processes the apply for the configuration set. void Process(bool preProcessOnly = false); // Gets the result object. IApplyGroupSettingsResult Result() const; private: // Contains all of the relevant data for a configuration unit. struct UnitInfo { UnitInfo(const ConfigurationUnit& unit); ConfigurationUnit Unit; std::vector DependencyIndices; decltype(make_self>()) Result; decltype(make_self>()) ResultInformation; bool PreProcessed = false; bool Processed = false; }; // Builds out some data used during processing and validates the set along the way. bool PreProcess(); // Adds the given unit to the identifier to unit info index map. bool AddUnitToMap(UnitInfo& unitInfo, size_t unitInfoIndex); // Checks the dependency; returns true to indicate that the dependency is satisfied, false if not. using CheckDependencyPtr = bool (*)(const UnitInfo&); // Processes the unit; returns true if successful, false if not. using ProcessUnitPtr = bool (ConfigurationSetApplyProcessor::*)(UnitInfo&); // Return true to process these intents, false to skip them. using IntentFilterPtr = bool (*)(ConfigurationUnitIntent); // Runs the processing using the given functions. bool ProcessInternal(CheckDependencyPtr checkDependencyFunction, ProcessUnitPtr processUnitFunction, bool sendProgress = false); // Processes one of the non-writing intent types, which are fatal if not all successful bool ProcessIntentInternal( std::vector& unitsToProcess, CheckDependencyPtr checkDependencyFunction, ProcessUnitPtr processUnitFunction, IntentFilterPtr intentFilter, hresult errorForOtherIntents, hresult errorForFailures, bool sendProgress); // Determines if the given unit has the given intent and all of its dependencies are satisfied bool HasIntentAndSatisfiedDependencies( const UnitInfo& unitInfo, IntentFilterPtr intentFilter, CheckDependencyPtr checkDependencyFunction) const; // Checks a dependency for preprocessing. static bool HasPreprocessed(const UnitInfo& unitInfo); // Marks a unit as preprocessed. bool MarkPreprocessed(UnitInfo& unitInfo); // Checks a dependency for having processed successfully. static bool HasProcessedSuccessfully(const UnitInfo& unitInfo); // Processes a configuration unit per its intent. bool ProcessUnit(UnitInfo& unitInfo); // Sends progress // TODO: Eventually these functions/call sites will be used for history void SendProgress(ConfigurationUnitState state, const UnitInfo& unitInfo); void SendProgressIfNotComplete(ConfigurationUnitState state, const UnitInfo& unitInfo); ConfigurationSet m_configurationSet; IConfigurationSetProcessor m_setProcessor; result_type m_result; progress_type m_progress; std::vector m_unitInfo; std::map m_idToUnitInfoIndex; hresult m_resultCode; }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetChangeData.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetChangeData.h" #include "ConfigurationSetChangeData.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { Configuration::ConfigurationSetChangeData ConfigurationSetChangeData::Create(ConfigurationSetState state) { auto result = make_self(); result->Initialize(state); return *result; } Configuration::ConfigurationSetChangeData ConfigurationSetChangeData::Create(ConfigurationUnitState state, IConfigurationUnitResultInformation resultInformation, ConfigurationUnit unit) { auto result = make_self(); result->Initialize(state, resultInformation, unit); return *result; } void ConfigurationSetChangeData::Initialize(ConfigurationSetState state) { m_change = ConfigurationSetChangeEventType::SetStateChanged; m_setState = state; } void ConfigurationSetChangeData::Initialize(ConfigurationUnitState state, IConfigurationUnitResultInformation resultInformation, ConfigurationUnit unit) { m_change = ConfigurationSetChangeEventType::UnitStateChanged; m_setState = ConfigurationSetState::InProgress; m_unitState = state; m_resultInformation = resultInformation; m_unit = unit; } void ConfigurationSetChangeData::Initialize(const IApplyGroupMemberSettingsResult& unitResult) { m_change = ConfigurationSetChangeEventType::UnitStateChanged; m_setState = ConfigurationSetState::InProgress; m_unitState = unitResult.State(); m_resultInformation = unitResult.ResultInformation(); m_unit = unitResult.Unit(); } ConfigurationSetChangeEventType ConfigurationSetChangeData::Change() { return m_change; } ConfigurationSetState ConfigurationSetChangeData::SetState() { return m_setState; } ConfigurationUnitState ConfigurationSetChangeData::UnitState() { return m_unitState; } IConfigurationUnitResultInformation ConfigurationSetChangeData::ResultInformation() { return m_resultInformation; } ConfigurationUnit ConfigurationSetChangeData::Unit() { return m_unit; } void ConfigurationSetChangeData::Unit(const ConfigurationUnit& unit) { m_unit = unit; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetChangeData.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetChangeData.g.h" #include "ConfigurationUnitResultInformation.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationSetChangeData : ConfigurationSetChangeDataT, AppInstaller::WinRT::ModuleCountBase { using ConfigurationUnit = Configuration::ConfigurationUnit; ConfigurationSetChangeData() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) static Configuration::ConfigurationSetChangeData Create(ConfigurationSetState state); static Configuration::ConfigurationSetChangeData Create(ConfigurationUnitState state, IConfigurationUnitResultInformation resultInformation, ConfigurationUnit unit); void Initialize(ConfigurationSetState state); void Initialize(ConfigurationUnitState state, IConfigurationUnitResultInformation resultInformation, ConfigurationUnit unit); void Initialize(const IApplyGroupMemberSettingsResult& unitResult); void Unit(const ConfigurationUnit& unit); #endif ConfigurationSetChangeEventType Change(); ConfigurationSetState SetState(); ConfigurationUnitState UnitState(); IConfigurationUnitResultInformation ResultInformation(); ConfigurationUnit Unit(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ConfigurationSetChangeEventType m_change = ConfigurationSetChangeEventType::Unknown; ConfigurationSetState m_setState = ConfigurationSetState::Unknown; ConfigurationUnitState m_unitState = ConfigurationUnitState::Unknown; IConfigurationUnitResultInformation m_resultInformation; ConfigurationUnit m_unit = nullptr; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetParser.h" #include "ParsingMacros.h" #include "ArgumentValidation.h" #include #include #include #include #include "ConfigurationSetUtilities.h" #include "ConfigurationSetParserError.h" #include "ConfigurationSetParser_0_1.h" #include "ConfigurationSetParser_0_2.h" #include "ConfigurationSetParser_0_3.h" using namespace AppInstaller::Utility; using namespace AppInstaller::YAML; namespace winrt::Microsoft::Management::Configuration::implementation { namespace { struct SchemaVersionAndUri { std::string_view Version; std::wstring_view VersionWide; std::string_view Uri; std::wstring_view UriWide; }; #define SCHEMA_VERSION_MAP_ITEM(_version_,_uri_) _version_, TEXT(_version_), _uri_, TEXT(_uri_) // Please keep in sorted order with the highest version last. // Duplicate URIs are supported, but duplicate versions are not. The highest version for a URI will be the one mapped to, the lower versions will be aliases. SchemaVersionAndUri SchemaVersionAndUriMap[] = { { SCHEMA_VERSION_MAP_ITEM("0.1", "") }, { SCHEMA_VERSION_MAP_ITEM("0.2", "") }, { SCHEMA_VERSION_MAP_ITEM("0.3", "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json") }, }; Windows::Foundation::IInspectable GetIInspectableFromNode(const Node& node); // Fills the ValueSet from the given node, which is assumed to be a map. void FillValueSetFromMap(const Node& mapNode, const Windows::Foundation::Collections::ValueSet& valueSet) { for (const auto& mapItem : mapNode.Mapping()) { // Insert returns true if it replaces an existing key, and that indicates an invalid map. THROW_HR_IF(WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE, valueSet.Insert(mapItem.first.as(), GetIInspectableFromNode(mapItem.second))); } } // Returns the appropriate IPropertyValue for the given node, which is assumed to be a scalar. Windows::Foundation::IInspectable GetPropertyValueFromScalar(const Node& node) { ::winrt::Windows::Foundation::IInspectable result; switch (node.GetTagType()) { case Node::TagType::Null: return Windows::Foundation::PropertyValue::CreateEmpty(); case Node::TagType::Bool: return Windows::Foundation::PropertyValue::CreateBoolean(node.as()); case Node::TagType::Str: return Windows::Foundation::PropertyValue::CreateString(node.as()); case Node::TagType::Int: return Windows::Foundation::PropertyValue::CreateInt64(node.as()); case Node::TagType::Float: THROW_HR(E_NOTIMPL); case Node::TagType::Timestamp: THROW_HR(E_NOTIMPL); default: THROW_HR(E_UNEXPECTED); } } // Returns the appropriate IPropertyValue for the given node, which is assumed to be a scalar. Windows::Foundation::IInspectable GetPropertyValueFromSequence(const Node& sequenceNode) { Windows::Foundation::Collections::ValueSet result; size_t index = 0; for (const Node& sequenceItem : sequenceNode.Sequence()) { std::wostringstream strstr; strstr << index++; result.Insert(strstr.str(), GetIInspectableFromNode(sequenceItem)); } result.Insert(L"treatAsArray", Windows::Foundation::PropertyValue::CreateBoolean(true)); return result; } // Returns the appropriate IInspectable for the given node. Windows::Foundation::IInspectable GetIInspectableFromNode(const Node& node) { ::winrt::Windows::Foundation::IInspectable result; switch (node.GetType()) { case Node::Type::Invalid: case Node::Type::None: // Leave value as null break; case Node::Type::Scalar: result = GetPropertyValueFromScalar(node); break; case Node::Type::Sequence: result = GetPropertyValueFromSequence(node); break; case Node::Type::Mapping: { Windows::Foundation::Collections::ValueSet subset; FillValueSetFromMap(node, subset); result = std::move(subset); } break; default: THROW_HR(E_UNEXPECTED); } return result; } // Contains the qualified resource name information. struct QualifiedResourceName { QualifiedResourceName(hstring input) { std::wstring_view inputView = input; size_t pos = inputView.find('/'); if (pos != std::wstring_view::npos) { Module = inputView.substr(0, pos); Resource = inputView.substr(pos + 1); } else { Resource = input; } } hstring Module; hstring Resource; }; } std::unique_ptr ConfigurationSetParser::Create(std::string_view input) { AICLI_LOG_LARGE_STRING(Config, Verbose, << "Parsing configuration set:", input); Node document; std::string documentError; Mark documentErrorMark; try { document = Load(input); } catch (const Exception& exc) { documentError = exc.what(); documentErrorMark = exc.GetMark(); } CATCH_LOG(); if (!document.IsMap()) { AICLI_LOG(Config, Error, << "Invalid YAML: " << documentError << " at [line " << documentErrorMark.line << ", col " << documentErrorMark.column << "]"); return std::make_unique(WINGET_CONFIG_ERROR_INVALID_YAML, documentError, documentErrorMark); } // The schema version for parsing the rest of the document std::string schemaUriString; std::string schemaVersionString; Node& schemaNode = document[GetConfigurationFieldName(ConfigurationField::Schema)]; if (schemaNode.IsScalar()) { schemaUriString = schemaNode.as(); schemaVersionString = GetSchemaVersionForUri(schemaUriString); AICLI_LOG(Config, Verbose, << "Configuration schema `" << schemaNode.as() << "` mapped to version `" << schemaVersionString << "`."); } // If we recognize the schema, use that version. // If we didn't recognize it, try using the older format. if (schemaVersionString.empty()) { std::unique_ptr oldFormatError = GetSchemaVersionFromOldFormat(document, schemaVersionString); // We have no schema version at all... if (oldFormatError) { // If the schema was provided and we didn't recognize it, make that the error. if (schemaNode.IsScalar()) { AICLI_LOG(Config, Error, << "Unknown configuration schema: " << schemaUriString); return std::make_unique(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, GetConfigurationFieldName(ConfigurationField::Schema), schemaUriString); } else { // Otherwise, this is an older format file (or neither). The proper error came back from that function. return oldFormatError; } } } // Create the parser based on the version selected auto result = CreateForSchemaVersion(std::move(schemaVersionString)); result->SetDocument(std::move(document)); return result; } std::unique_ptr ConfigurationSetParser::CreateForSchemaVersion(std::string input) { SemanticVersion schemaVersion(std::move(input)); // TODO: Consider having the version/uri/type information all together in the future if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 1) { return std::make_unique(); } else if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 2) { return std::make_unique(); } else if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 3) { return std::make_unique(); } AICLI_LOG(Config, Error, << "Unknown configuration version: " << schemaVersion.ToString()); return std::make_unique(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion), schemaVersion.ToString()); } bool ConfigurationSetParser::IsRecognizedSchemaVersion(hstring value) try { SemanticVersion schemaVersion(ConvertToUTF8(value)); for (const auto& item : SchemaVersionAndUriMap) { if (schemaVersion == SemanticVersion{ std::string{ item.Version } }) { return true; } } return false; } catch (...) { LOG_CAUGHT_EXCEPTION(); return false; } bool ConfigurationSetParser::IsRecognizedSchemaUri(const Windows::Foundation::Uri& value) { return !GetSchemaVersionForUri(value).empty(); } Windows::Foundation::Uri ConfigurationSetParser::GetSchemaUriForVersion(hstring value) { for (const auto& item : SchemaVersionAndUriMap) { if (value == item.VersionWide) { return item.Uri.empty() ? nullptr : Windows::Foundation::Uri{ item.UriWide }; } } return nullptr; } hstring ConfigurationSetParser::GetSchemaVersionForUri(Windows::Foundation::Uri value) { // Do a reverse search in order to give the highest version back for a given URI. auto itr = std::rbegin(SchemaVersionAndUriMap); auto end = std::rend(SchemaVersionAndUriMap); for (; itr != end; ++itr) { const auto& item = *itr; if (!item.Uri.empty()) { Windows::Foundation::Uri uri{ item.UriWide }; if (value.Equals(uri)) { return hstring{ item.VersionWide }; } } } return {}; } std::string ConfigurationSetParser::GetSchemaVersionForUri(std::string_view value) { // Do a reverse search in order to give the highest version back for a given URI. auto itr = std::rbegin(SchemaVersionAndUriMap); auto end = std::rend(SchemaVersionAndUriMap); for (; itr != end; ++itr) { const auto& item = *itr; if (!item.Uri.empty()) { if (item.Uri == value) { return std::string{ item.Version }; } } } return {}; } std::pair ConfigurationSetParser::LatestVersion() { auto latest = std::rbegin(SchemaVersionAndUriMap); return { hstring{ latest->VersionWide }, Windows::Foundation::Uri{ latest->UriWide } }; } Windows::Foundation::Collections::ValueSet ConfigurationSetParser::ParseValueSet(std::string_view input) { Windows::Foundation::Collections::ValueSet result; FillValueSetFromMap(Load(input), result); return result; } std::vector ConfigurationSetParser::ParseStringArray(std::string_view input) { std::vector result; ParseSequence(Load(input), "string_array", Node::Type::Scalar, [&](const AppInstaller::YAML::Node& item) { result.emplace_back(item.as()); }); return result; } void ConfigurationSetParser::SetError(hresult result, std::string_view field, std::string_view value, uint32_t line, uint32_t column) { AICLI_LOG(Config, Error, << "ConfigurationSetParser error: " << AppInstaller::Logging::SetHRFormat << result << " for " << field << " with value `" << value << "` at [line " << line << ", col " << column << "]"); m_result = result; m_field = ConvertToUTF16(field); m_value = ConvertToUTF16(value); m_line = line; m_column = column; } void ConfigurationSetParser::SetError(hresult result, std::string_view field, const Mark& mark, std::string_view value) { SetError(result, field, value, static_cast(mark.line), static_cast(mark.column)); } const Node& ConfigurationSetParser::GetAndEnsureField(const Node& parent, ConfigurationField field, bool required, std::optional type) { const Node& fieldNode = parent[GetConfigurationFieldName(field)]; if (fieldNode) { if (type && fieldNode.GetType() != type.value()) { SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(field), fieldNode.Mark()); } } else if (required) { SetError(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(field)); } return fieldNode; } void ConfigurationSetParser::EnsureFieldAbsent(const Node& parent, ConfigurationField field) { const Node& fieldNode = parent[GetConfigurationFieldName(field)]; if (fieldNode) { SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, GetConfigurationFieldName(field), fieldNode.Mark(), fieldNode.as()); } } void ConfigurationSetParser::ParseValueSet(const Node& node, ConfigurationField field, bool required, const Windows::Foundation::Collections::ValueSet& valueSet) { const Node& mapNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Mapping)); if (mapNode) { FillValueSetFromMap(mapNode, valueSet); } } void ConfigurationSetParser::ParseMapping(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, AppInstaller::YAML::Node::Type elementType, std::function operation) { const Node& mapNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Mapping)); if (!mapNode) { return; } std::ostringstream strstr; strstr << GetConfigurationFieldName(field); size_t index = 0; for (const auto& mapItem : mapNode.Mapping()) { std::string name = mapItem.first.as(); if (name.empty()) { strstr << '[' << index << ']'; FIELD_VALUE_ERROR(strstr.str(), name, mapItem.first.Mark()); } if (mapItem.second.GetType() != elementType) { strstr << '[' << index << ']'; FIELD_TYPE_ERROR(strstr.str(), mapItem.second.Mark()); } index++; CHECK_ERROR(operation(std::move(name), mapItem.second)); } } void ConfigurationSetParser::ParseSequence(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, std::optional elementType, std::function operation) { const Node& sequenceNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Sequence)); if (!sequenceNode) { return; } ParseSequence(sequenceNode, GetConfigurationFieldName(field), elementType, operation); } void ConfigurationSetParser::ParseSequence(const AppInstaller::YAML::Node& node, std::string_view nameForErrors, std::optional elementType, std::function operation) { std::ostringstream strstr; strstr << nameForErrors; size_t index = 0; for (const Node& item : node.Sequence()) { if (elementType && item.GetType() != elementType.value()) { strstr << '[' << index << ']'; FIELD_TYPE_ERROR(strstr.str(), item.Mark()); } index++; CHECK_ERROR(operation(item)); } } std::unique_ptr ConfigurationSetParser::GetSchemaVersionFromOldFormat(AppInstaller::YAML::Node& document, std::string& schemaVersionString) { Node& propertiesNode = document[GetConfigurationFieldName(ConfigurationField::Properties)]; if (!propertiesNode) { AICLI_LOG(Config, Error, << "No properties"); // Even though this is for the "older" format, if there is no properties entry then give an error for the newer format since this is probably neither. return std::make_unique(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(ConfigurationField::Schema)); } else if (!propertiesNode.IsMap()) { AICLI_LOG(Config, Error, << "Invalid properties type"); return std::make_unique(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(ConfigurationField::Properties), propertiesNode.Mark()); } Node& versionNode = propertiesNode[GetConfigurationFieldName(ConfigurationField::ConfigurationVersion)]; if (!versionNode) { AICLI_LOG(Config, Error, << "No configuration version"); return std::make_unique(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion)); } else if (!versionNode.IsScalar()) { AICLI_LOG(Config, Error, << "Invalid configuration version type"); return std::make_unique(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion), versionNode.Mark()); } schemaVersionString = versionNode.as(); return {}; } void ConfigurationSetParser::GetStringValueForUnit(const Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value)) { const Node& valueNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Scalar)); if (valueNode) { hstring value{ valueNode.as() }; FIELD_MISSING_ERROR_IF(value.empty() && required, GetConfigurationFieldName(field)); (unit->*propertyFunction)(std::move(value)); } } void ConfigurationSetParser::GetStringArrayForUnit(const Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector&& value)) { std::vector arrayValue; CHECK_ERROR(ParseSequence(node, field, required, Node::Type::Scalar, [&](const AppInstaller::YAML::Node& item) { arrayValue.emplace_back(item.as()); })); if (!arrayValue.empty()) { (unit->*propertyFunction)(std::move(arrayValue)); } } void ConfigurationSetParser::ValidateType(ConfigurationUnit* unit, const Node& unitNode, ConfigurationField typeField, bool moveModuleNameToMetadata, bool moduleNameRequiredInType) { QualifiedResourceName qualifiedName{ unit->Type() }; const Node& typeNode = CHECK_ERROR(GetAndEnsureField(unitNode, typeField, true, Node::Type::Scalar)); FIELD_VALUE_ERROR_IF(qualifiedName.Resource.empty(), GetConfigurationFieldName(typeField), ConvertToUTF8(unit->Type()), typeNode.Mark()); if (!qualifiedName.Module.empty()) { // If the module is provided in both the resource name and the directives, ensure that it matches hstring moduleDirectiveFieldName = GetConfigurationFieldNameHString(ConfigurationField::ModuleDirective); auto moduleDirective = unit->Metadata().TryLookup(moduleDirectiveFieldName); if (moduleDirective) { auto moduleProperty = moduleDirective.try_as(); FIELD_TYPE_ERROR_IF(!moduleProperty, GetConfigurationFieldName(ConfigurationField::ModuleDirective), unitNode.Mark()); FIELD_TYPE_ERROR_IF(moduleProperty.Type() != Windows::Foundation::PropertyType::String, GetConfigurationFieldName(ConfigurationField::ModuleDirective), unitNode.Mark()); hstring moduleValue = moduleProperty.GetString(); FIELD_VALUE_ERROR_IF(qualifiedName.Module != moduleValue, GetConfigurationFieldName(ConfigurationField::ModuleDirective), ConvertToUTF8(moduleValue), unitNode.Mark()); } else if (moveModuleNameToMetadata) { unit->Metadata().Insert(moduleDirectiveFieldName, Windows::Foundation::PropertyValue::CreateString(qualifiedName.Module)); } if (moveModuleNameToMetadata) { // Set the unit name to be just the resource portion unit->Type(qualifiedName.Resource); } } else if (moduleNameRequiredInType) { FIELD_VALUE_ERROR(GetConfigurationFieldName(typeField), ConvertToUTF8(unit->Type()), typeNode.Mark()); } } void ConfigurationSetParser::ParseObject(const Node& node, ConfigurationField fieldForErrors, Windows::Foundation::PropertyType type, Windows::Foundation::IInspectable& result) { try { Windows::Foundation::IInspectable object = GetIInspectableFromNode(node); FIELD_VALUE_ERROR_IF(!IsValidObjectType(object, type), GetConfigurationFieldName(fieldForErrors), node.as(), node.Mark()); result = std::move(object); } catch (...) { LOG_CAUGHT_EXCEPTION(); FIELD_VALUE_ERROR(GetConfigurationFieldName(fieldForErrors), node.as(), node.Mark()); } } void ConfigurationSetParser::ExtractSecurityContext(implementation::ConfigurationUnit* unit, SecurityContext defaultContext) { THROW_HR_IF_NULL(E_POINTER, unit); ExtractSecurityContext(unit->Metadata(), unit->EnvironmentInternal(), defaultContext); } void ConfigurationSetParser::ExtractSecurityContext(Windows::Foundation::Collections::ValueSet metadata, implementation::ConfigurationEnvironment& environment, SecurityContext defaultContext) { SecurityContext computedContext = defaultContext; auto securityContext = TryLookupProperty(metadata, ConfigurationField::SecurityContextMetadata, Windows::Foundation::PropertyType::String); if (securityContext) { TryParseSecurityContext(securityContext.GetString(), computedContext); metadata.Remove(GetConfigurationFieldNameHString(ConfigurationField::SecurityContextMetadata)); } environment.Context(computedContext); } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParser.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Interface for parsing a configuration set stream. struct ConfigurationSetParser { // Create a parser from the given bytes (the encoding is detected). static std::unique_ptr Create(std::string_view input); // Create a parser for the given schema version. static std::unique_ptr CreateForSchemaVersion(std::string schemaVersion); // Determines if the given value is a recognized schema version. // This will only return true for a version that we fully recognize. static bool IsRecognizedSchemaVersion(hstring value); // Determines if the given value is a recognized schema URI. // This will only return true for a URI that we fully recognize. static bool IsRecognizedSchemaUri(const Windows::Foundation::Uri& value); // Gets the schema URI associated with the given version, or null if there is not one. static Windows::Foundation::Uri GetSchemaUriForVersion(hstring value); // Gets the schema version associated with the given URI, or null if there is not one. static hstring GetSchemaVersionForUri(Windows::Foundation::Uri value); // Gets the schema version associated with the given URI, or null if there is not one. static std::string GetSchemaVersionForUri(std::string_view value); // Gets the latest schema version. static std::pair LatestVersion(); virtual ~ConfigurationSetParser() noexcept = default; ConfigurationSetParser(const ConfigurationSetParser&) = delete; ConfigurationSetParser& operator=(const ConfigurationSetParser&) = delete; ConfigurationSetParser(ConfigurationSetParser&&) = default; ConfigurationSetParser& operator=(ConfigurationSetParser&&) = default; // Parse the full document. virtual void Parse() = 0; // Retrieves the schema version of the parser. virtual hstring GetSchemaVersion() = 0; // Extracts (and removes) the environment information from the given metadata. virtual void ExtractEnvironmentFromMetadata(Windows::Foundation::Collections::ValueSet valueSet, implementation::ConfigurationEnvironment& environment) = 0; using ConfigurationSetPtr = winrt::com_ptr; // Retrieve the configuration set from the parser. ConfigurationSetPtr GetConfigurationSet() const { return m_configurationSet; } // The latest result code from the parser. hresult Result() const { return m_result; } // The field related to the result code. hstring Field() const { return m_field; } // The value of the field. hstring Value() const { return m_value; } // The line related to the result code. uint32_t Line() const { return m_line; } // The column related to the result code. uint32_t Column() const { return m_column; } // Parse a ValueSet from the given input. Windows::Foundation::Collections::ValueSet ParseValueSet(std::string_view input); // Parse a string array from the given input. std::vector ParseStringArray(std::string_view input); protected: ConfigurationSetParser() = default; // Sets (or resets) the document to parse. virtual void SetDocument(AppInstaller::YAML::Node&& document) = 0; // Set the error state void SetError(hresult result, std::string_view field = {}, std::string_view value = {}, uint32_t line = 0, uint32_t column = 0); void SetError(hresult result, std::string_view field, const AppInstaller::YAML::Mark& mark, std::string_view value = {}); ConfigurationSetPtr m_configurationSet; hresult m_result; hstring m_field; hstring m_value; uint32_t m_line = 0; uint32_t m_column = 0; // Gets the given `field` from the `parent` node, checking against the requirement and type. const AppInstaller::YAML::Node& GetAndEnsureField(const AppInstaller::YAML::Node& parent, ConfigurationField field, bool required, std::optional type); // Errors if the given `field` is present. void EnsureFieldAbsent(const AppInstaller::YAML::Node& parent, ConfigurationField field); // Parse the ValueSet named `field` from the given `node`. void ParseValueSet(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, const Windows::Foundation::Collections::ValueSet& valueSet); // Parse the mapping named `field` from the given `node`. void ParseMapping(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, AppInstaller::YAML::Node::Type elementType, std::function operation); // Parse the sequence named `field` from the given `node`. void ParseSequence(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, std::optional elementType, std::function operation); // Parse the sequence from the given `node`. void ParseSequence(const AppInstaller::YAML::Node& node, std::string_view nameForErrors, std::optional elementType, std::function operation); // Gets the string value in `field` from the given `node`, setting this value on `unit` using the `propertyFunction`. void GetStringValueForUnit(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value)); // Gets the string array in `field` from the given `node`, setting this value on `unit` using the `propertyFunction`. void GetStringArrayForUnit(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector&& value)); // Validates the unit's Type property for correctness and consistency with the metadata. Should be called after parsing the Metadata value. void ValidateType(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode, ConfigurationField typeField, bool moveModuleNameToMetadata, bool moduleNameRequiredInType); // Parses an object from the given node, attempting to treat it as the requested type if possible. void ParseObject(const AppInstaller::YAML::Node& node, ConfigurationField fieldForErrors, Windows::Foundation::PropertyType type, Windows::Foundation::IInspectable& result); // Extracts the security context from the metadata in the given unit; if not present use `defaultContext`. void ExtractSecurityContext(implementation::ConfigurationUnit* unit, SecurityContext defaultContext = SecurityContext::Current); void ExtractSecurityContext(Windows::Foundation::Collections::ValueSet metadata, implementation::ConfigurationEnvironment& environment, SecurityContext defaultContext = SecurityContext::Current); private: // Support older schema parsing. static std::unique_ptr GetSchemaVersionFromOldFormat(AppInstaller::YAML::Node& document, std::string& schemaVersionString); }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParserError.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetParser.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { // Parser object that only indicates an error occurred. struct ConfigurationSetParserError : public ConfigurationSetParser { ConfigurationSetParserError(hresult result, std::string_view field = {}, std::string_view value = {}) { SetError(result, field, value); } ConfigurationSetParserError(hresult result, std::string_view field, const AppInstaller::YAML::Mark& mark) { SetError(result, field, mark); } void Parse() override {} hstring GetSchemaVersion() override { return {}; } void ExtractEnvironmentFromMetadata(Windows::Foundation::Collections::ValueSet, implementation::ConfigurationEnvironment&) override {} protected: void SetDocument(AppInstaller::YAML::Node&&) override {} }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetParser_0_1.h" #include "ParsingMacros.h" #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { using namespace AppInstaller::YAML; void ConfigurationSetParser_0_1::Parse() { std::vector units; const Node& properties = m_document[GetConfigurationFieldName(ConfigurationField::Properties)]; ParseConfigurationUnitsFromField(properties, ConfigurationField::Assertions, ConfigurationUnitIntent::Assert, units); ParseConfigurationUnitsFromField(properties, ConfigurationField::Parameters, ConfigurationUnitIntent::Inform, units); ParseConfigurationUnitsFromField(properties, ConfigurationField::Resources, ConfigurationUnitIntent::Apply, units); m_configurationSet = make_self(); m_configurationSet->Units(std::move(units)); m_configurationSet->SchemaVersion(GetSchemaVersion()); } hstring ConfigurationSetParser_0_1::GetSchemaVersion() { static hstring s_schemaVersion{ L"0.1" }; return s_schemaVersion; } void ConfigurationSetParser_0_1::ExtractEnvironmentFromMetadata(Windows::Foundation::Collections::ValueSet, implementation::ConfigurationEnvironment&) { } void ConfigurationSetParser_0_1::SetDocument(AppInstaller::YAML::Node&& document) { m_document = std::move(document); } void ConfigurationSetParser_0_1::ParseConfigurationUnitsFromField(const Node& document, ConfigurationField field, ConfigurationUnitIntent intent, std::vector& result) { ParseSequence(document, field, false, Node::Type::Mapping, [&](const Node& item) { auto configurationUnit = make_self(); ParseConfigurationUnit(configurationUnit.get(), item, intent); result.emplace_back(*configurationUnit); }); } void ConfigurationSetParser_0_1::ParseConfigurationUnit(ConfigurationUnit* unit, const Node& unitNode, ConfigurationUnitIntent intent) { CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Resource, true, unit, &ConfigurationUnit::Type)); CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Id, false, unit, &ConfigurationUnit::Identifier)); unit->Intent(intent); CHECK_ERROR(GetStringArrayForUnit(unitNode, ConfigurationField::DependsOn, false, unit, &ConfigurationUnit::Dependencies)); CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Directives, false, unit->Metadata())); CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Settings, false, unit->Settings())); } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetParser.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { // Parser for schema version 0.1 struct ConfigurationSetParser_0_1 : public ConfigurationSetParser { ConfigurationSetParser_0_1() = default; virtual ~ConfigurationSetParser_0_1() noexcept = default; ConfigurationSetParser_0_1(const ConfigurationSetParser_0_1&) = delete; ConfigurationSetParser_0_1& operator=(const ConfigurationSetParser_0_1&) = delete; ConfigurationSetParser_0_1(ConfigurationSetParser_0_1&&) = default; ConfigurationSetParser_0_1& operator=(ConfigurationSetParser_0_1&&) = default; void Parse() override; // Retrieves the schema version of the parser. hstring GetSchemaVersion() override; void ExtractEnvironmentFromMetadata(Windows::Foundation::Collections::ValueSet valueSet, implementation::ConfigurationEnvironment& environment) override; protected: // Sets (or resets) the document to parse. void SetDocument(AppInstaller::YAML::Node&& document) override; void ParseConfigurationUnitsFromField(const AppInstaller::YAML::Node& document, ConfigurationField field, ConfigurationUnitIntent intent, std::vector& result); virtual void ParseConfigurationUnit(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode, ConfigurationUnitIntent intent); AppInstaller::YAML::Node m_document; }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParser_0_2.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetParser_0_2.h" #include "ParsingMacros.h" #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { using namespace AppInstaller::YAML; hstring ConfigurationSetParser_0_2::GetSchemaVersion() { static hstring s_schemaVersion{ L"0.2" }; return s_schemaVersion; } void ConfigurationSetParser_0_2::ExtractEnvironmentFromMetadata(Windows::Foundation::Collections::ValueSet valueSet, implementation::ConfigurationEnvironment& environment) { ExtractSecurityContext(valueSet, environment); } void ConfigurationSetParser_0_2::SetDocument(AppInstaller::YAML::Node&& document) { m_document = std::move(document); } void ConfigurationSetParser_0_2::ParseConfigurationUnit(ConfigurationUnit* unit, const Node& unitNode, ConfigurationUnitIntent intent) { CHECK_ERROR(ConfigurationSetParser_0_1::ParseConfigurationUnit(unit, unitNode, intent)); ValidateType(unit, unitNode, ConfigurationField::Resource, true, false); ExtractSecurityContext(unit); } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParser_0_2.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetParser_0_1.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { // Parser for schema version 0.2 struct ConfigurationSetParser_0_2 : public ConfigurationSetParser_0_1 { ConfigurationSetParser_0_2() = default; virtual ~ConfigurationSetParser_0_2() noexcept = default; ConfigurationSetParser_0_2(const ConfigurationSetParser_0_2&) = delete; ConfigurationSetParser_0_2& operator=(const ConfigurationSetParser_0_2&) = delete; ConfigurationSetParser_0_2(ConfigurationSetParser_0_2&&) = default; ConfigurationSetParser_0_2& operator=(ConfigurationSetParser_0_2&&) = default; // Retrieves the schema version of the parser. hstring GetSchemaVersion() override; void ExtractEnvironmentFromMetadata(Windows::Foundation::Collections::ValueSet valueSet, implementation::ConfigurationEnvironment& environment) override; protected: // Sets (or resets) the document to parse. void SetDocument(AppInstaller::YAML::Node&& document) override; void ParseConfigurationUnit(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode, ConfigurationUnitIntent intent) override; }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetParser_0_3.h" #include "ParsingMacros.h" #include "ArgumentValidation.h" #include #include #include using namespace AppInstaller::YAML; using namespace winrt::Windows::Foundation; namespace winrt::Microsoft::Management::Configuration::implementation { using IInspectable = winrt::Windows::Foundation::IInspectable; void ConfigurationSetParser_0_3::Parse() { auto result = make_self(); CHECK_ERROR(ParseValueSet(m_document, ConfigurationField::Metadata, false, result->Metadata())); CHECK_ERROR(ExtractEnvironmentFromMetadata(result->Metadata(), result->EnvironmentInternal())); CHECK_ERROR(ParseParameters(result)); CHECK_ERROR(ParseValueSet(m_document, ConfigurationField::Variables, false, result->Variables())); std::vector units; CHECK_ERROR(ParseConfigurationUnitsFromField(m_document, ConfigurationField::Resources, units)); result->Units(std::move(units)); result->SchemaVersion(GetSchemaVersion()); m_configurationSet = std::move(result); } hstring ConfigurationSetParser_0_3::GetSchemaVersion() { static hstring s_schemaVersion{ L"0.3" }; return s_schemaVersion; } void ConfigurationSetParser_0_3::SetDocument(AppInstaller::YAML::Node&& document) { m_document = std::move(document); } void ConfigurationSetParser_0_3::ParseParameters(ConfigurationSetParser::ConfigurationSetPtr& set) { std::vector parameters; ParseMapping(m_document, ConfigurationField::Parameters, false, Node::Type::Mapping, [&](std::string name, const Node& item) { auto parameter = make_self>(); CHECK_ERROR(ParseParameter(parameter.get(), item)); parameter->Name(hstring{ AppInstaller::Utility::ConvertToUTF16(name) }); parameters.emplace_back(*parameter); }); set->Parameters(std::move(parameters)); } void ConfigurationSetParser_0_3::ParseParameter(ConfigurationParameter* parameter, const AppInstaller::YAML::Node& node) { CHECK_ERROR(ParseParameterType(parameter, node)); CHECK_ERROR(ParseValueSet(node, ConfigurationField::Metadata, false, parameter->Metadata())); CHECK_ERROR(GetStringValueForParameter(node, ConfigurationField::Description, parameter, &ConfigurationParameter::Description)); PropertyType parameterType = parameter->Type(); CHECK_ERROR(ParseObjectValueForParameter(node, ConfigurationField::DefaultValue, parameterType, parameter, &ConfigurationParameter::DefaultValue)); std::vector allowedValues; CHECK_ERROR(ParseSequence(node, ConfigurationField::AllowedValues, false, std::nullopt, [&](const Node& item) { IInspectable object; CHECK_ERROR(ParseObject(item, ConfigurationField::AllowedValues, parameterType, object)); allowedValues.emplace_back(std::move(object)); })); if (!allowedValues.empty()) { parameter->AllowedValues(std::move(allowedValues)); } if (IsLengthType(parameterType)) { CHECK_ERROR(GetUInt32ValueForParameter(node, ConfigurationField::MinimumLength, parameter, &ConfigurationParameter::MinimumLength)); CHECK_ERROR(GetUInt32ValueForParameter(node, ConfigurationField::MaximumLength, parameter, &ConfigurationParameter::MaximumLength)); } else { CHECK_ERROR(EnsureFieldAbsent(node, ConfigurationField::MinimumLength)); CHECK_ERROR(EnsureFieldAbsent(node, ConfigurationField::MaximumLength)); } if (IsComparableType(parameterType)) { CHECK_ERROR(ParseObjectValueForParameter(node, ConfigurationField::MinimumValue, parameterType, parameter, &ConfigurationParameter::MinimumValue)); CHECK_ERROR(ParseObjectValueForParameter(node, ConfigurationField::MaximumValue, parameterType, parameter, &ConfigurationParameter::MaximumValue)); } else { CHECK_ERROR(EnsureFieldAbsent(node, ConfigurationField::MinimumValue)); CHECK_ERROR(EnsureFieldAbsent(node, ConfigurationField::MaximumValue)); } } void ConfigurationSetParser_0_3::ParseParameterType(ConfigurationParameter* parameter, const AppInstaller::YAML::Node& node) { const Node& typeNode = CHECK_ERROR(GetAndEnsureField(node, ConfigurationField::Type, true, Node::Type::Scalar)); std::string typeValue = typeNode.as(); auto parsedType = ParseWindowsFoundationPropertyType(typeValue); if (parsedType) { parameter->Type(parsedType->first); parameter->IsSecure(parsedType->second); } else { FIELD_VALUE_ERROR(GetConfigurationFieldName(ConfigurationField::Type), typeValue, typeNode.Mark()); } } void ConfigurationSetParser_0_3::GetStringValueForParameter( const Node& node, ConfigurationField field, ConfigurationParameter* parameter, void(ConfigurationParameter::* propertyFunction)(const hstring& value)) { const Node& valueNode = CHECK_ERROR(GetAndEnsureField(node, field, false, Node::Type::Scalar)); if (valueNode) { (parameter->*propertyFunction)(hstring{ valueNode.as() }); } } void ConfigurationSetParser_0_3::GetUInt32ValueForParameter( const AppInstaller::YAML::Node& node, ConfigurationField field, ConfigurationParameter* parameter, void(ConfigurationParameter::* propertyFunction)(uint32_t value)) { const Node& valueNode = CHECK_ERROR(GetAndEnsureField(node, field, false, Node::Type::Scalar)); if (valueNode) { int64_t value = valueNode.as(); if (value < 0 || value > static_cast(std::numeric_limits::max())) { FIELD_VALUE_ERROR(GetConfigurationFieldName(field), valueNode.as(), valueNode.Mark()); } (parameter->*propertyFunction)(static_cast(value)); } } void ConfigurationSetParser_0_3::ParseObjectValueForParameter( const AppInstaller::YAML::Node& node, ConfigurationField field, PropertyType type, ConfigurationParameter* parameter, void(ConfigurationParameter::* propertyFunction)(const IInspectable& value)) { const Node& valueNode = CHECK_ERROR(GetAndEnsureField(node, field, false, std::nullopt)); if (valueNode) { IInspectable valueObject; CHECK_ERROR(ParseObject(valueNode, field, type, valueObject)); (parameter->*propertyFunction)(valueObject); } } void ConfigurationSetParser_0_3::ParseConfigurationUnitsFromField(const Node& document, ConfigurationField field, std::vector& result) { ParseSequence(document, field, false, Node::Type::Mapping, [&](const Node& item) { auto configurationUnit = make_self(); ParseConfigurationUnit(configurationUnit.get(), item); result.emplace_back(*configurationUnit); }); } void ConfigurationSetParser_0_3::ParseConfigurationUnit(ConfigurationUnit* unit, const Node& unitNode) { // Set unknown intent as the new schema doesn't express it directly unit->Intent(ConfigurationUnitIntent::Unknown); CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Name, true, unit, &ConfigurationUnit::Identifier)); CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Type, true, unit, &ConfigurationUnit::Type)); CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Metadata, false, unit->Metadata())); CHECK_ERROR(ExtractEnvironmentForUnit(unit)); CHECK_ERROR(ValidateType(unit, unitNode, ConfigurationField::Type, false, true)); CHECK_ERROR(GetStringArrayForUnit(unitNode, ConfigurationField::DependsOn, false, unit, &ConfigurationUnit::Dependencies)); // Regardless of being a group or not, parse the settings. CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Properties, false, unit->Settings())); if (ShouldConvertToGroup(unit)) { unit->IsGroup(true); // TODO: The PS DSC v3 POR looks like it supports each group defining a new schema to be used for its group items. // Consider supporting that in the future; but for now just use the same schema for everything. const Node& propertiesNode = GetAndEnsureField(unitNode, ConfigurationField::Properties, false, Node::Type::Mapping); if (propertiesNode) { std::vector units; CHECK_ERROR(ParseConfigurationUnitsFromField(propertiesNode, ConfigurationField::Resources, units)); unit->Units(std::move(units)); } } } bool ConfigurationSetParser_0_3::ShouldConvertToGroup(ConfigurationUnit* unit) { // Allow the metadata to inform us that we should treat it as a group, including preventing a known type from being treated as one. auto isGroupObject = unit->Metadata().TryLookup(GetConfigurationFieldNameHString(ConfigurationField::IsGroupMetadata)); if (isGroupObject) { auto isGroupProperty = isGroupObject.try_as(); if (isGroupProperty && isGroupProperty.Type() == PropertyType::Boolean) { return isGroupProperty.GetBoolean(); } } // TODO: Check for known types return false; } void ConfigurationSetParser_0_3::ExtractEnvironmentFromMetadata(Collections::ValueSet metadata, ConfigurationEnvironment& targetEnvironment) { auto root = TryLookupValueSet(metadata, ConfigurationField::WingetMetadataRoot); if (root) { // Get security context auto securityContext = TryLookupProperty(root, ConfigurationField::SecurityContextMetadata, PropertyType::String); if (securityContext) { SecurityContext computedContext = SecurityContext::Current; if (TryParseSecurityContext(securityContext.GetString(), computedContext)) { targetEnvironment.Context(computedContext); } root.Remove(GetConfigurationFieldNameHString(ConfigurationField::SecurityContextMetadata)); } // Get processor hstring processorFieldName = GetConfigurationFieldNameHString(ConfigurationField::ProcessorMetadata); IInspectable processor = root.TryLookup(processorFieldName); Collections::ValueSet processorValueSet = processor.try_as(); if (processorValueSet) { targetEnvironment.ProcessorIdentifier({}); targetEnvironment.ProcessorProperties().Clear(); IPropertyValue identifier = TryLookupProperty(processorValueSet, ConfigurationField::ProcessorIdentifierMetadata, PropertyType::String); if (identifier) { targetEnvironment.ProcessorIdentifier(identifier.GetString()); Collections::ValueSet processorSettings = TryLookupValueSet(processorValueSet, ConfigurationField::ProcessorPropertiesMetadata); if (processorSettings) { targetEnvironment.ProcessorProperties(processorSettings); } } root.Remove(processorFieldName); } else { IPropertyValue processorProperty = processor.try_as(); if (processorProperty) { targetEnvironment.ProcessorIdentifier(processorProperty.GetString()); targetEnvironment.ProcessorProperties().Clear(); root.Remove(processorFieldName); } } if (root.Size() == 0) { metadata.Remove(GetConfigurationFieldNameHString(ConfigurationField::WingetMetadataRoot)); } } } void ConfigurationSetParser_0_3::ExtractEnvironmentForUnit(ConfigurationUnit* unit) { // Get unnested security context ExtractSecurityContext(unit); // Get nested environment ExtractEnvironmentFromMetadata(unit->Metadata(), unit->EnvironmentInternal()); } std::optional> ParseWindowsFoundationPropertyType(std::string_view value) { if (value == "string") { return std::make_pair(PropertyType::String, false); } else if (value == "securestring") { return std::make_pair(PropertyType::String, true); } else if (value == "int") { return std::make_pair(PropertyType::Int64, false); } else if (value == "bool") { return std::make_pair(PropertyType::Boolean, false); } else if (value == "object") { return std::make_pair(PropertyType::Inspectable, false); } else if (value == "secureobject") { return std::make_pair(PropertyType::Inspectable, true); } else if (value == "array") { return std::make_pair(PropertyType::InspectableArray, false); } // TODO: Consider supporting an expanded set of type strings return std::nullopt; } std::string_view ToString(PropertyType value, bool isSecure) { switch (value) { case PropertyType::Int16: case PropertyType::Int32: case PropertyType::Int64: return "int"sv; case PropertyType::Boolean: return "bool"sv; case PropertyType::String: return isSecure ? "securestring"sv : "string"sv; case PropertyType::Inspectable: return isSecure ? "secureobject"sv : "object"sv; case PropertyType::InspectableArray: return "array"sv; default: return {}; } } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetParser.h" #include "ConfigurationParameter.h" #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Parser for schema version 0.3 struct ConfigurationSetParser_0_3 : public ConfigurationSetParser { ConfigurationSetParser_0_3() = default; virtual ~ConfigurationSetParser_0_3() noexcept = default; ConfigurationSetParser_0_3(const ConfigurationSetParser_0_3&) = delete; ConfigurationSetParser_0_3& operator=(const ConfigurationSetParser_0_3&) = delete; ConfigurationSetParser_0_3(ConfigurationSetParser_0_3&&) = default; ConfigurationSetParser_0_3& operator=(ConfigurationSetParser_0_3&&) = default; // Retrieve the configuration units from the parser. void Parse() override; // Retrieves the schema version of the parser. hstring GetSchemaVersion() override; // Extracts the environment configuration from the given metadata. // This only examines the winget subnode. void ExtractEnvironmentFromMetadata(Windows::Foundation::Collections::ValueSet valueSet, implementation::ConfigurationEnvironment& environment) override; protected: // Sets (or resets) the document to parse. void SetDocument(AppInstaller::YAML::Node&& document) override; void ParseParameters(ConfigurationSetParser::ConfigurationSetPtr& set); void ParseParameter(ConfigurationParameter* parameter, const AppInstaller::YAML::Node& node); void ParseParameterType(ConfigurationParameter* parameter, const AppInstaller::YAML::Node& node); void GetStringValueForParameter( const AppInstaller::YAML::Node& node, ConfigurationField field, ConfigurationParameter* parameter, void(ConfigurationParameter::* propertyFunction)(const hstring& value)); void GetUInt32ValueForParameter( const AppInstaller::YAML::Node& node, ConfigurationField field, ConfigurationParameter* parameter, void(ConfigurationParameter::* propertyFunction)(uint32_t value)); void ParseObjectValueForParameter( const AppInstaller::YAML::Node& node, ConfigurationField field, Windows::Foundation::PropertyType type, ConfigurationParameter* parameter, void(ConfigurationParameter::* propertyFunction)(const Windows::Foundation::IInspectable& value)); void ParseConfigurationUnitsFromField(const AppInstaller::YAML::Node& document, ConfigurationField field, std::vector& result); virtual void ParseConfigurationUnit(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode); // Determines if the given unit should be converted to a group. bool ShouldConvertToGroup(ConfigurationUnit* unit); // Extracts the environment for a unit. void ExtractEnvironmentForUnit(ConfigurationUnit* unit); AppInstaller::YAML::Node m_document; }; std::optional> ParseWindowsFoundationPropertyType(std::string_view value); std::string_view ToString(Windows::Foundation::PropertyType value, bool isSecure); } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetSerializer.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include "ConfigurationSetSerializer.h" #include "ConfigurationSetSerializer_0_2.h" #include "ConfigurationSetSerializer_0_3.h" #include "ConfigurationSetUtilities.h" using namespace AppInstaller::Utility; using namespace AppInstaller::YAML; using namespace winrt::Windows::Foundation; namespace winrt::Microsoft::Management::Configuration::implementation { namespace anon { static constexpr std::string_view s_nullValue = "null"; struct ValueSetWriter { ValueSetWriter(const Windows::Foundation::Collections::ValueSet& valueSet, const std::vector>& overrides) : m_valueSet(valueSet), m_overrides(overrides) { // Create a sorted list of the field names to exclude for (const auto & override : m_overrides) { m_exclusionStrings.push_back(GetConfigurationFieldNameHString(override.first)); } std::sort(m_exclusionStrings.begin(), m_exclusionStrings.end()); } bool IsResultEmpty() { size_t nullOverrides = 0; for (const auto& override : m_overrides) { // A non-null override will always be output if (override.second) { return false; } else { ++nullOverrides; } } if (m_valueSet) { // If there are more values than null overrides, something will be output if (static_cast(m_valueSet.Size()) > nullOverrides) { return false; } // Check for a value that we would output for (const auto& [key, value] : m_valueSet) { if (value != nullptr && !std::binary_search(m_exclusionStrings.begin(), m_exclusionStrings.end(), key)) { return false; } } } return true; } void Write(AppInstaller::YAML::Emitter& emitter, void(* WriteYamlValue)(AppInstaller::YAML::Emitter& emitter, const winrt::Windows::Foundation::IInspectable& value)) { emitter << BeginMap; WriteValues(emitter, WriteYamlValue); emitter << EndMap; } void WriteValues(AppInstaller::YAML::Emitter& emitter, void(*WriteYamlValue)(AppInstaller::YAML::Emitter& emitter, const winrt::Windows::Foundation::IInspectable& value)) { if (m_valueSet) { for (const auto& [key, value] : m_valueSet) { if (value != nullptr && !std::binary_search(m_exclusionStrings.begin(), m_exclusionStrings.end(), key)) { std::string keyName = winrt::to_string(key); emitter << Key << keyName << Value; WriteYamlValue(emitter, value); } } } for (const auto & override : m_overrides) { if (override.second != nullptr) { std::string_view keyName = GetConfigurationFieldName(override.first); emitter << Key << keyName << Value; WriteYamlValue(emitter, override.second); } } } private: const Windows::Foundation::Collections::ValueSet& m_valueSet; const std::vector>& m_overrides; std::vector m_exclusionStrings; }; } std::unique_ptr ConfigurationSetSerializer::CreateSerializer(hstring version, bool strictVersionMatching) { // Create the parser based on the version selected SemanticVersion schemaVersion(std::move(winrt::to_string(version))); // TODO: Consider having the version/uri/type information all together in the future if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 1) { // Remove this once the 0.1 serializer is implemented. THROW_HR_IF(E_NOTIMPL, strictVersionMatching); return std::make_unique(); } else if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 2) { return std::make_unique(); } else if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 3) { return std::make_unique(); } else { AICLI_LOG(Config, Error, << "Unknown configuration version: " << schemaVersion.ToString()); THROW_HR(E_UNEXPECTED); } } std::string ConfigurationSetSerializer::SerializeValueSet(const Windows::Foundation::Collections::ValueSet& valueSet) { Emitter emitter; WriteYamlValueSet(emitter, valueSet); return emitter.str(); } std::string ConfigurationSetSerializer::SerializeStringArray(const Windows::Foundation::Collections::IVector& stringArray) { Emitter emitter; WriteYamlStringArray(emitter, stringArray); return emitter.str(); } void ConfigurationSetSerializer::WriteYamlValueSetIfNotEmpty(AppInstaller::YAML::Emitter& emitter, ConfigurationField key, const Windows::Foundation::Collections::ValueSet& valueSet, const OverrideMap& overrides) { anon::ValueSetWriter writer{ valueSet, overrides }; if (!writer.IsResultEmpty()) { emitter << Key << GetConfigurationFieldName(key); writer.Write(emitter, WriteYamlValue); } } void ConfigurationSetSerializer::WriteYamlValueSet(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet, const OverrideMap& overrides) { anon::ValueSetWriter writer{ valueSet, overrides }; writer.Write(emitter, WriteYamlValue); } void ConfigurationSetSerializer::WriteYamlValueSetValues(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet, const OverrideMap& overrides) { anon::ValueSetWriter writer{ valueSet, overrides }; writer.WriteValues(emitter, WriteYamlValue); } void ConfigurationSetSerializer::WriteYamlStringArray(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::IVector& values) { emitter << BeginSeq; for (const auto& value : values) { emitter << ConvertToUTF8(value); } emitter << EndSeq; } void ConfigurationSetSerializer::WriteYamlValue(AppInstaller::YAML::Emitter& emitter, const winrt::Windows::Foundation::IInspectable& value) { if (value == nullptr) { emitter << anon::s_nullValue; } else { const auto& currentValueSet = value.try_as(); if (currentValueSet) { if (currentValueSet.HasKey(L"treatAsArray")) { WriteYamlValueSetAsArray(emitter, currentValueSet); } else { WriteYamlValueSet(emitter, currentValueSet); } } else { IPropertyValue property = value.as(); auto type = property.Type(); if (type == PropertyType::Boolean) { emitter << property.GetBoolean(); } else if (type == PropertyType::String) { emitter << ScalarStyle::DoubleQuoted << ConvertToUTF8(property.GetString()); } else if (type == PropertyType::Int64) { emitter << property.GetInt64(); } else { THROW_HR(E_NOTIMPL); } } } } void ConfigurationSetSerializer::WriteYamlValueIfNotEmpty(AppInstaller::YAML::Emitter& emitter, ConfigurationField key, const winrt::Windows::Foundation::IInspectable& value) { if (value != nullptr) { emitter << Key << GetConfigurationFieldName(key) << Value; WriteYamlValue(emitter, value); } } void ConfigurationSetSerializer::WriteYamlStringValueIfNotEmpty(AppInstaller::YAML::Emitter& emitter, ConfigurationField key, hstring value) { if (!value.empty()) { emitter << Key << GetConfigurationFieldName(key) << Value << ConvertToUTF8(value); } } void ConfigurationSetSerializer::WriteYamlValueSetAsArray(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSetArray) { std::vector> arrayValues; for (const auto& arrayValue : valueSetArray) { if (arrayValue.Key() != L"treatAsArray") { arrayValues.emplace_back(std::make_pair(std::stoi(arrayValue.Key().c_str()), arrayValue.Value())); } } std::sort( arrayValues.begin(), arrayValues.end(), [](const std::pair& a, const std::pair& b) { return a.first < b.first; }); emitter << BeginSeq; for (const auto& arrayValue : arrayValues) { WriteYamlValue(emitter, arrayValue.second); } emitter << EndSeq; } std::wstring_view ConfigurationSetSerializer::GetSchemaVersionCommentPrefix() { return L"# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/"sv; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetSerializer.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetSerializer.h" #include "ConfigurationSetUtilities.h" #include "ConfigurationSet.h" #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationSetSerializer { using OverrideMap = std::vector>; static std::unique_ptr CreateSerializer(hstring version, bool strictVersionMatching = false); virtual ~ConfigurationSetSerializer() noexcept = default; ConfigurationSetSerializer(const ConfigurationSetSerializer&) = delete; ConfigurationSetSerializer& operator=(const ConfigurationSetSerializer&) = delete; ConfigurationSetSerializer(ConfigurationSetSerializer&&) = default; ConfigurationSetSerializer& operator=(ConfigurationSetSerializer&&) = default; // Serializes a configuration set to the original yaml string. virtual hstring Serialize(ConfigurationSet*) = 0; // Serialize the metadata with the given environment. virtual std::string SerializeMetadataWithEnvironment(const Windows::Foundation::Collections::ValueSet& metadata, const Configuration::ConfigurationEnvironment& environment) = 0; // Serializes a value set only. std::string SerializeValueSet(const Windows::Foundation::Collections::ValueSet& valueSet); // Serializes a value set only. std::string SerializeStringArray(const Windows::Foundation::Collections::IVector& stringArray); protected: ConfigurationSetSerializer() = default; static void WriteYamlValueSet(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet, const OverrideMap& overrides = {}); static void WriteYamlValueSetValues(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet, const OverrideMap& overrides = {}); static void WriteYamlValueSetIfNotEmpty(AppInstaller::YAML::Emitter& emitter, ConfigurationField key, const Windows::Foundation::Collections::ValueSet& valueSet, const OverrideMap& overrides = {}); static void WriteYamlValueSetAsArray(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSetArray); static void WriteYamlStringArray(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::IVector& values); static void WriteYamlValue(AppInstaller::YAML::Emitter& emitter, const winrt::Windows::Foundation::IInspectable& value); static void WriteYamlValueIfNotEmpty(AppInstaller::YAML::Emitter& emitter, ConfigurationField key, const winrt::Windows::Foundation::IInspectable& value); static void WriteYamlStringValueIfNotEmpty(AppInstaller::YAML::Emitter& emitter, ConfigurationField key, hstring value); std::wstring_view GetSchemaVersionCommentPrefix(); }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_2.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetSerializer_0_2.h" #include "ConfigurationSetUtilities.h" #include using namespace AppInstaller::Utility; using namespace AppInstaller::YAML; using namespace winrt::Windows::Foundation; namespace winrt::Microsoft::Management::Configuration::implementation { hstring ConfigurationSetSerializer_0_2::Serialize(ConfigurationSet* configurationSet) { std::vector assertions; std::vector resources; for (auto unit : configurationSet->Units()) { ConfigurationUnitIntent unitIntent = unit.Intent(); if (unitIntent == ConfigurationUnitIntent::Assert) { assertions.emplace_back(unit); } else if (unitIntent == ConfigurationUnitIntent::Apply) { resources.emplace_back(unit); } } Emitter emitter; emitter << BeginMap; emitter << Key << GetConfigurationFieldName(ConfigurationField::Properties); emitter << BeginMap; emitter << Key << GetConfigurationFieldName(ConfigurationField::ConfigurationVersion) << Value << ConvertToUTF8(configurationSet->SchemaVersion()); if (!assertions.empty()) { emitter << Key << GetConfigurationFieldName(ConfigurationField::Assertions); WriteYamlConfigurationUnits(emitter, assertions); } if (!resources.empty()) { emitter << Key << GetConfigurationFieldName(ConfigurationField::Resources); WriteYamlConfigurationUnits(emitter, resources); } emitter << EndMap; emitter << EndMap; std::wostringstream result; result << GetSchemaVersionCommentPrefix() << static_cast(configurationSet->SchemaVersion()) << L"\n" << ConvertToUTF16(emitter.str()); return hstring{ std::move(result).str() }; } std::string ConfigurationSetSerializer_0_2::SerializeMetadataWithEnvironment(const Windows::Foundation::Collections::ValueSet& metadata, const Configuration::ConfigurationEnvironment& environment) { Emitter emitter; WriteYamlValueSet(emitter, metadata, GetMetadataWithEnvironmentOverrides(false, environment.Context())); return emitter.str(); } void ConfigurationSetSerializer_0_2::WriteYamlConfigurationUnits(AppInstaller::YAML::Emitter& emitter, const std::vector& units) { emitter << BeginSeq; for (const auto& unit : units) { // Resource emitter << BeginMap; emitter << Key << GetConfigurationFieldName(ConfigurationField::Resource) << Value << AppInstaller::Utility::ConvertToUTF8(GetResourceName(unit)); // Id if (!unit.Identifier().empty()) { emitter << Key << GetConfigurationFieldName(ConfigurationField::Id) << Value << AppInstaller::Utility::ConvertToUTF8(unit.Identifier()); } // Dependencies if (unit.Dependencies().Size() > 0) { emitter << Key << GetConfigurationFieldName(ConfigurationField::DependsOn); emitter << BeginSeq; for (const auto& dependency : unit.Dependencies()) { emitter << AppInstaller::Utility::ConvertToUTF8(dependency); } emitter << EndSeq; } // Directives WriteResourceDirectives(emitter, unit); // Settings const auto& settings = unit.Settings(); emitter << Key << GetConfigurationFieldName(ConfigurationField::Settings); WriteYamlValueSet(emitter, settings); emitter << EndMap; } emitter << EndSeq; } winrt::hstring ConfigurationSetSerializer_0_2::GetResourceName(const ConfigurationUnit& unit) { const auto& metadata = unit.Metadata(); const auto moduleKey = GetConfigurationFieldNameHString(ConfigurationField::ModuleDirective); if (metadata.HasKey(moduleKey)) { auto object = metadata.Lookup(moduleKey); auto property = object.try_as(); if (property && property.Type() == PropertyType::String) { return property.GetString() + '/' + unit.Type(); } } return unit.Type(); } void ConfigurationSetSerializer_0_2::WriteResourceDirectives(AppInstaller::YAML::Emitter& emitter, const ConfigurationUnit& unit) { WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Directives, unit.Metadata(), GetMetadataWithEnvironmentOverrides(true, unit.Environment().Context())); } ConfigurationSetSerializer::OverrideMap ConfigurationSetSerializer_0_2::GetMetadataWithEnvironmentOverrides(bool includeModuleOverride, SecurityContext securityContext) { ConfigurationSetSerializer::OverrideMap result { { ConfigurationField::SecurityContextMetadata, (securityContext != SecurityContext::Current ? PropertyValue::CreateString(ToWString(securityContext)) : nullptr)} }; if (includeModuleOverride) { result.emplace_back(ConfigurationField::ModuleDirective, nullptr); } return result; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_2.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetSerializer.h" namespace winrt::Microsoft::Management::Configuration::implementation { // Serializer for schema version 0.2 struct ConfigurationSetSerializer_0_2 : public ConfigurationSetSerializer { ConfigurationSetSerializer_0_2() {} virtual ~ConfigurationSetSerializer_0_2() noexcept = default; ConfigurationSetSerializer_0_2(const ConfigurationSetSerializer_0_2&) = delete; ConfigurationSetSerializer_0_2& operator=(const ConfigurationSetSerializer_0_2&) = delete; ConfigurationSetSerializer_0_2(ConfigurationSetSerializer_0_2&&) = default; ConfigurationSetSerializer_0_2& operator=(ConfigurationSetSerializer_0_2&&) = default; hstring Serialize(ConfigurationSet* configurationSet) override; std::string SerializeMetadataWithEnvironment(const Windows::Foundation::Collections::ValueSet& metadata, const Configuration::ConfigurationEnvironment& environment) override; protected: void WriteYamlConfigurationUnits(AppInstaller::YAML::Emitter& emitter, const std::vector& units); virtual winrt::hstring GetResourceName(const ConfigurationUnit& unit); virtual void WriteResourceDirectives(AppInstaller::YAML::Emitter& emitter, const ConfigurationUnit& unit); static ConfigurationSetSerializer::OverrideMap GetMetadataWithEnvironmentOverrides(bool includeModuleOverride, SecurityContext securityContext); }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_3.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetSerializer_0_3.h" #include "ArgumentValidation.h" #include "ConfigurationSetParser_0_3.h" #include "ConfigurationSetUtilities.h" #include "ConfigurationEnvironment.h" #include #include using namespace AppInstaller::Utility; using namespace AppInstaller::YAML; using namespace winrt::Windows::Foundation; namespace winrt::Microsoft::Management::Configuration::implementation { namespace { Windows::Foundation::Collections::ValueSet GetWingetProcessorMetadataValueSet(Windows::Foundation::Collections::ValueSet& metadata) { Windows::Foundation::Collections::ValueSet result = nullptr; hstring processorMetadataKey = GetConfigurationFieldNameHString(ConfigurationField::ProcessorMetadata); if (metadata) { Windows::Foundation::IInspectable processorMetadataObject = metadata.TryLookup(processorMetadataKey); if (processorMetadataObject) { result = processorMetadataObject.try_as(); THROW_HR_IF(WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, !result); } } else { metadata = Collections::ValueSet{}; } if (!result) { result = Collections::ValueSet{}; metadata.Insert(processorMetadataKey, result); } return result; } Windows::Foundation::Collections::ValueSet CreateValueSetFromStringMap(const Windows::Foundation::Collections::IMap& map) { Windows::Foundation::Collections::ValueSet result; if (map) { for (const auto& item : map) { result.Insert(item.Key(), PropertyValue::CreateString(item.Value())); } } return result; } void AddEnvironmentToMetadata( Windows::Foundation::Collections::ValueSet& metadata, SecurityContext context, hstring processor, Windows::Foundation::Collections::IMap properties, SecurityContext defaultContext = SecurityContext::Current, hstring defaultProcessor = {}, Windows::Foundation::Collections::IMap defaultProperties = nullptr) { if (context != defaultContext) { if (!metadata) { metadata = Collections::ValueSet{}; } metadata.Insert(GetConfigurationFieldNameHString(ConfigurationField::SecurityContextMetadata), PropertyValue::CreateString(ToWString(context))); } Windows::Foundation::Collections::ValueSet processorValueSet{ nullptr }; if (processor != defaultProcessor) { if (!processorValueSet) { processorValueSet = GetWingetProcessorMetadataValueSet(metadata); } processorValueSet.Insert(GetConfigurationFieldNameHString(ConfigurationField::ProcessorIdentifierMetadata), PropertyValue::CreateString(processor)); } if (!ConfigurationEnvironment::AreEqual(properties, defaultProperties)) { if (!processorValueSet) { processorValueSet = GetWingetProcessorMetadataValueSet(metadata); } processorValueSet.Insert(GetConfigurationFieldNameHString(ConfigurationField::ProcessorPropertiesMetadata), CreateValueSetFromStringMap(properties)); } } void AddEnvironmentToMetadata( Windows::Foundation::Collections::ValueSet& metadata, const com_ptr& environment) { if (environment) { AddEnvironmentToMetadata(metadata, environment->Context(), environment->ProcessorIdentifier(), environment->ProcessorProperties()); } } void AddEnvironmentToMetadata( Windows::Foundation::Collections::ValueSet& metadata, const Configuration::ConfigurationEnvironment& environment) { AddEnvironmentToMetadata(metadata, environment.Context(), environment.ProcessorIdentifier(), environment.ProcessorProperties()); } } hstring ConfigurationSetSerializer_0_3::Serialize(ConfigurationSet* configurationSet) { Emitter emitter; emitter << BeginMap; emitter << Key << GetConfigurationFieldName(ConfigurationField::Schema) << Value << ConvertToUTF8(configurationSet->SchemaUri().ToString()); // Prepare an override if necessary Collections::ValueSet wingetMetadataOverride = nullptr; AddEnvironmentToMetadata(wingetMetadataOverride, configurationSet->EnvironmentInternal()); WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Metadata, configurationSet->Metadata(), { { ConfigurationField::WingetMetadataRoot, wingetMetadataOverride }, { ConfigurationField::SecurityContextMetadata, nullptr }, }); WriteYamlParameters(emitter, configurationSet->Parameters()); WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Variables, configurationSet->Variables()); WriteYamlConfigurationUnits(emitter, configurationSet->Units()); emitter << EndMap; std::wostringstream result; result << GetSchemaVersionCommentPrefix() << static_cast(configurationSet->SchemaVersion()) << L"\n" << ConvertToUTF16(emitter.str()); return hstring{ std::move(result).str() }; } std::string ConfigurationSetSerializer_0_3::SerializeMetadataWithEnvironment(const Windows::Foundation::Collections::ValueSet& metadata, const Configuration::ConfigurationEnvironment& environment) { Emitter emitter; Collections::ValueSet wingetMetadataOverride = nullptr; AddEnvironmentToMetadata(wingetMetadataOverride, environment); WriteYamlValueSet(emitter, metadata, { { ConfigurationField::WingetMetadataRoot, wingetMetadataOverride }, { ConfigurationField::SecurityContextMetadata, nullptr }, }); return emitter.str(); } void ConfigurationSetSerializer_0_3::WriteYamlParameters(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::IVector& values) { if (!values || values.Size() == 0) { return; } emitter << Key << GetConfigurationFieldName(ConfigurationField::Parameters); emitter << BeginMap; for (const Configuration::ConfigurationParameter& parameter : values) { emitter << Key << ConvertToUTF8(parameter.Name()); emitter << BeginMap; auto type = parameter.Type(); emitter << Key << GetConfigurationFieldName(ConfigurationField::Type) << Value << ToString(type, parameter.IsSecure()); WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Metadata, parameter.Metadata()); WriteYamlStringValueIfNotEmpty(emitter, ConfigurationField::Description, parameter.Description()); WriteYamlValueIfNotEmpty(emitter, ConfigurationField::DefaultValue, parameter.DefaultValue()); auto allowedValues = parameter.AllowedValues(); if (allowedValues && allowedValues.Size() != 0) { emitter << Key << GetConfigurationFieldName(ConfigurationField::AllowedValues); emitter << BeginSeq; for (const auto& value : allowedValues) { emitter << Value; WriteYamlValue(emitter, value); } emitter << EndSeq; } if (IsLengthType(type)) { uint32_t minimumLength = parameter.MinimumLength(); if (minimumLength != 0) { emitter << Key << GetConfigurationFieldName(ConfigurationField::MinimumLength) << Value << static_cast(minimumLength); } uint32_t maximumLength = parameter.MaximumLength(); if (maximumLength != std::numeric_limits::max()) { emitter << Key << GetConfigurationFieldName(ConfigurationField::MaximumLength) << Value << static_cast(maximumLength); } } if (IsComparableType(type)) { WriteYamlValueIfNotEmpty(emitter, ConfigurationField::MinimumValue, parameter.MinimumValue()); WriteYamlValueIfNotEmpty(emitter, ConfigurationField::MaximumValue, parameter.MaximumValue()); } emitter << EndMap; } emitter << EndMap; } void ConfigurationSetSerializer_0_3::WriteYamlConfigurationUnits( AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::IVector& values) { emitter << Key << GetConfigurationFieldName(ConfigurationField::Resources); emitter << BeginSeq; for (const Configuration::ConfigurationUnit& unit : values) { emitter << BeginMap; hstring identifier = unit.Identifier(); THROW_HR_IF(WINGET_CONFIG_ERROR_MISSING_FIELD, identifier.empty()); emitter << Key << GetConfigurationFieldName(ConfigurationField::Name) << Value << ConvertToUTF8(identifier); hstring type = unit.Type(); THROW_HR_IF(WINGET_CONFIG_ERROR_MISSING_FIELD, type.empty()); emitter << Key << GetConfigurationFieldName(ConfigurationField::Type) << Value << ConvertToUTF8(type); // Prepare an override if necessary Collections::ValueSet wingetMetadataOverride = nullptr; Configuration::ConfigurationEnvironment unitEnvironment = unit.Environment(); AddEnvironmentToMetadata(wingetMetadataOverride, unitEnvironment); WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Metadata, unit.Metadata(), { { ConfigurationField::WingetMetadataRoot, wingetMetadataOverride } }); auto dependencies = unit.Dependencies(); if (dependencies && dependencies.Size() != 0) { emitter << Key << GetConfigurationFieldName(ConfigurationField::DependsOn); emitter << BeginSeq; for (const auto& value : dependencies) { emitter << ConvertToUTF8(value); } emitter << EndSeq; } // If this unit is a group, write the units directly if (unit.IsGroup()) { auto groupUnits = unit.Units(); if (groupUnits.Size() != 0) { emitter << Key << GetConfigurationFieldName(ConfigurationField::Properties); emitter << BeginMap; // Write everything but the resources WriteYamlValueSetValues(emitter, unit.Settings(), { { ConfigurationField::Resources, nullptr } }); // Write the resources from the individual units WriteYamlConfigurationUnits(emitter, groupUnits); emitter << EndMap; } } else { WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Properties, unit.Settings()); } emitter << EndMap; } emitter << EndSeq; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_3.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetSerializer.h" #include "ConfigurationEnvironment.h" namespace winrt::Microsoft::Management::Configuration::implementation { // Serializer for schema version 0.3 struct ConfigurationSetSerializer_0_3 : public ConfigurationSetSerializer { ConfigurationSetSerializer_0_3() {} virtual ~ConfigurationSetSerializer_0_3() noexcept = default; ConfigurationSetSerializer_0_3(const ConfigurationSetSerializer_0_3&) = delete; ConfigurationSetSerializer_0_3& operator=(const ConfigurationSetSerializer_0_3&) = delete; ConfigurationSetSerializer_0_3(ConfigurationSetSerializer_0_3&&) = default; ConfigurationSetSerializer_0_3& operator=(ConfigurationSetSerializer_0_3&&) = default; hstring Serialize(ConfigurationSet* configurationSet) override; std::string SerializeMetadataWithEnvironment(const Windows::Foundation::Collections::ValueSet& metadata, const Configuration::ConfigurationEnvironment& environment) override; protected: void WriteYamlParameters(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::IVector& values); void WriteYamlConfigurationUnits( AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::IVector& values); }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetUtilities.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationSetUtilities.h" #include using namespace std::string_view_literals; namespace winrt::Microsoft::Management::Configuration::implementation { std::string_view GetConfigurationFieldName(ConfigurationField fieldName) { switch (fieldName) { case ConfigurationField::ConfigurationVersion: return "configurationVersion"sv; case ConfigurationField::Properties: return "properties"sv; case ConfigurationField::Resource: return "resource"sv; case ConfigurationField::Directives: return "directives"sv; case ConfigurationField::Settings: return "settings"sv; case ConfigurationField::Assertions: return "assertions"sv; case ConfigurationField::Id: return "id"sv; case ConfigurationField::DependsOn: return "dependsOn"sv; case ConfigurationField::Resources: return "resources"sv; case ConfigurationField::ModuleDirective: return "module"sv; case ConfigurationField::SecurityContextMetadata: return "securityContext"sv; case ConfigurationField::Schema: return "$schema"sv; case ConfigurationField::Metadata: return "metadata"sv; case ConfigurationField::Parameters: return "parameters"sv; case ConfigurationField::Variables: return "variables"sv; case ConfigurationField::Type: return "type"sv; case ConfigurationField::Description: return "description"sv; case ConfigurationField::Name: return "name"sv; case ConfigurationField::IsGroupMetadata: return "isGroup"sv; case ConfigurationField::DefaultValue: return "defaultValue"sv; case ConfigurationField::AllowedValues: return "allowedValues"sv; case ConfigurationField::MinimumLength: return "minLength"sv; case ConfigurationField::MaximumLength: return "maxLength"sv; case ConfigurationField::MinimumValue: return "minValue"sv; case ConfigurationField::MaximumValue: return "maxValue"sv; case ConfigurationField::WingetMetadataRoot: return "winget"sv; case ConfigurationField::ProcessorMetadata: return "processor"sv; case ConfigurationField::ProcessorIdentifierMetadata: return "identifier"sv; case ConfigurationField::ProcessorPropertiesMetadata: return "properties"sv; } THROW_HR(E_UNEXPECTED); } hstring GetConfigurationFieldNameHString(ConfigurationField fieldName) { return hstring{ AppInstaller::Utility::ConvertToUTF16(GetConfigurationFieldName(fieldName)) }; } bool TryParseSecurityContext(const hstring& value, SecurityContext& result) { std::wstring securityContextLower = AppInstaller::Utility::ToLower(value); if (securityContextLower == L"elevated") { result = SecurityContext::Elevated; } else if (securityContextLower == L"restricted") { result = SecurityContext::Restricted; } else if (securityContextLower == L"current") { result = SecurityContext::Current; } else { return false; } return true; } SecurityContext ParseSecurityContext(const hstring& value) { SecurityContext result = SecurityContext::Current; THROW_HR_IF(E_INVALIDARG, !TryParseSecurityContext(value, result)); return result; } std::string_view ToString(SecurityContext value) { switch (value) { case SecurityContext::Current: return "current"; case SecurityContext::Restricted: return "restricted"; case SecurityContext::Elevated: return "elevated"; } THROW_HR(E_INVALIDARG); } std::wstring_view ToWString(SecurityContext value) { switch (value) { case SecurityContext::Current: return L"current"; case SecurityContext::Restricted: return L"restricted"; case SecurityContext::Elevated: return L"elevated"; } THROW_HR(E_INVALIDARG); } Windows::Foundation::Collections::ValueSet TryLookupValueSet(const Windows::Foundation::Collections::ValueSet& valueSet, ConfigurationField field) { Windows::Foundation::IInspectable value = valueSet.TryLookup(GetConfigurationFieldNameHString(field)); if (value) { return value.try_as(); } return nullptr; } Windows::Foundation::IPropertyValue TryLookupProperty(const Windows::Foundation::Collections::ValueSet& valueSet, ConfigurationField field, Windows::Foundation::PropertyType type) { Windows::Foundation::IInspectable value = valueSet.TryLookup(GetConfigurationFieldNameHString(field)); if (value) { Windows::Foundation::IPropertyValue property = value.try_as(); if (property && (type == Windows::Foundation::PropertyType::Empty || property.Type() == type)) { return property; } } return nullptr; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationSetUtilities.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // The various configuration fields that are used in parsing/serialization. enum class ConfigurationField { // v0.1 and v0.2 ConfigurationVersion, Properties, Resource, Directives, Settings, Assertions, Id, DependsOn, // Universal Resources, ModuleDirective, SecurityContextMetadata, // v0.3 Schema, Metadata, Parameters, Variables, Type, Description, Name, IsGroupMetadata, DefaultValue, AllowedValues, MinimumLength, MaximumLength, MinimumValue, MaximumValue, WingetMetadataRoot, ProcessorMetadata, ProcessorIdentifierMetadata, ProcessorPropertiesMetadata, }; // Gets the name value of the configuration field. std::string_view GetConfigurationFieldName(ConfigurationField fieldName); winrt::hstring GetConfigurationFieldNameHString(ConfigurationField fieldName); // Attempts to parse a security context from a string. // Returns true if successful; false otherwise. bool TryParseSecurityContext(const hstring& value, SecurityContext& result); // Parses a security context from a string. SecurityContext ParseSecurityContext(const hstring& value); // Gets the string representation of a security context. std::string_view ToString(SecurityContext value); // Gets the string representation of a security context. std::wstring_view ToWString(SecurityContext value); // Tries to get the field value from the given value set; only if it is a value set. Windows::Foundation::Collections::ValueSet TryLookupValueSet(const Windows::Foundation::Collections::ValueSet& valueSet, ConfigurationField field); // Tries to get the field value from the given value set; only if it is a value set. Windows::Foundation::IPropertyValue TryLookupProperty(const Windows::Foundation::Collections::ValueSet& valueSet, ConfigurationField field, Windows::Foundation::PropertyType type = Windows::Foundation::PropertyType::Empty); } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationStaticFunctions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationStaticFunctions.h" #include "ConfigurationStaticFunctions.g.cpp" #include "ConfigurationUnit.h" #include "ConfigurationSet.h" #include "ConfigurationProcessor.h" #include "ConfigurationParameter.h" #include "FindUnitProcessorsOptions.h" #include "ShutdownSynchronization.h" #include #include namespace winrt::Microsoft::Management::Configuration::implementation { Configuration::ConfigurationUnit ConfigurationStaticFunctions::CreateConfigurationUnit() { return *make_self(); } Configuration::ConfigurationSet ConfigurationStaticFunctions::CreateConfigurationSet() { return *make_self(); } Windows::Foundation::IAsyncOperation ConfigurationStaticFunctions::CreateConfigurationSetProcessorFactoryAsync(hstring const& handler) { std::wstring lowerHandler = AppInstaller::Utility::ToLower(handler); if (lowerHandler == AppInstaller::Configuration::PowerShellHandlerIdentifier) { THROW_HR(E_NOTIMPL); } AICLI_LOG(Config, Error, << "Unknown handler in CreateConfigurationSetProcessorFactory: " << AppInstaller::Utility::ConvertToUTF8(handler)); THROW_HR(E_NOT_SET); } Configuration::ConfigurationProcessor ConfigurationStaticFunctions::CreateConfigurationProcessor(IConfigurationSetProcessorFactory const& factory) { auto result = make_self>(); result->ConfigurationSetProcessorFactory(factory); return *result; } Windows::Foundation::IAsyncActionWithProgress ConfigurationStaticFunctions::EnsureConfigurationAvailableAsync() { THROW_HR(E_NOTIMPL); } Configuration::ConfigurationParameter ConfigurationStaticFunctions::CreateConfigurationParameter() { return *make_self>(); } Configuration::FindUnitProcessorsOptions ConfigurationStaticFunctions::CreateFindUnitProcessorsOptions() { return *make_self>(); } HRESULT STDMETHODCALLTYPE ConfigurationStaticFunctions::SetExperimentalState(UINT32 state) { m_state = static_cast(state); return S_OK; } HRESULT STDMETHODCALLTYPE ConfigurationStaticFunctions::BlockNewWorkForShutdown() { ShutdownSynchronization::Instance().BlockNewWork(); return S_OK; } HRESULT STDMETHODCALLTYPE ConfigurationStaticFunctions::BeginShutdown() { ShutdownSynchronization::Instance().CancelAllWork(); return S_OK; } HRESULT STDMETHODCALLTYPE ConfigurationStaticFunctions::WaitForShutdown() { ShutdownSynchronization::Instance().Wait(); return S_OK; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationStaticFunctions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationStaticFunctions.g.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationStaticFunctions : ConfigurationStaticFunctionsT> { ConfigurationStaticFunctions() = default; Configuration::ConfigurationUnit CreateConfigurationUnit(); Configuration::ConfigurationSet CreateConfigurationSet(); Windows::Foundation::IAsyncOperation CreateConfigurationSetProcessorFactoryAsync(hstring const& handler); Configuration::ConfigurationProcessor CreateConfigurationProcessor(IConfigurationSetProcessorFactory const& factory); bool IsConfigurationAvailable() { return true; } Windows::Foundation::IAsyncActionWithProgress EnsureConfigurationAvailableAsync(); Configuration::ConfigurationParameter CreateConfigurationParameter(); Configuration::FindUnitProcessorsOptions CreateFindUnitProcessorsOptions(); // IConfigurationStaticsInternals HRESULT STDMETHODCALLTYPE SetExperimentalState(UINT32 state); HRESULT STDMETHODCALLTYPE BlockNewWorkForShutdown(); HRESULT STDMETHODCALLTYPE BeginShutdown(); HRESULT STDMETHODCALLTYPE WaitForShutdown(); private: // By default, enable all state so that in-proc usage contains it. AppInstaller::WinRT::ConfigurationStaticsInternalsStateFlags m_state = AppInstaller::WinRT::ConfigurationStaticsInternalsStateFlags::All; }; } namespace winrt::Microsoft::Management::Configuration::factory_implementation { struct ConfigurationStaticFunctions : ConfigurationStaticFunctionsT { }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationStatus.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationStatus.h" #include "ConfigurationChangeData.h" #include "ConfigurationProcessor.h" #include "ConfigurationSet.h" #include "ConfigurationUnitResultInformation.h" #include #include namespace winrt::Microsoft::Management::Configuration::implementation { namespace details { // Implements the consuming side of the status signaling. struct ChangeListener { struct SetStatusItem { ConfigurationDatabase::StatusItem Status; com_ptr Set; }; ChangeListener(ConfigurationStatus& status) : m_status(status) { ConfigurationDatabase::StatusBaseline baseline = m_status.Database().GetStatusBaseline(); m_changeIdentifier = baseline.ChangeIdentifier; for (const auto& item : baseline.SetStatus) { m_lastSetStatus.emplace(item.SetInstanceIdentifier, SetStatusItem{ item }); } std::wstring objectName = L"WinGetConfigListener_" + AppInstaller::Utility::CreateNewGuidNameWString(); m_listenerEventName = AppInstaller::Utility::ConvertToUTF8(objectName); m_listenerEvent.create(wil::EventOptions::None, objectName.c_str()); m_status.Database().AddListener(m_listenerEventName); m_threadPoolWait.reset(CreateThreadpoolWait(StaticWaitCallback, this, nullptr)); THROW_LAST_ERROR_IF(!m_threadPoolWait); SetThreadpoolWait(m_threadPoolWait.get(), m_listenerEvent.get(), NULL); } ~ChangeListener() { try { m_status.Database().RemoveListener(m_listenerEventName); } CATCH_LOG(); } private: static void NTAPI StaticWaitCallback(PTP_CALLBACK_INSTANCE, void* context, TP_WAIT*, TP_WAIT_RESULT) { reinterpret_cast(context)->WaitCallback(); } void WaitCallback() try { std::vector changes = m_status.Database().GetStatusSince(m_changeIdentifier); // Convert status items to relevant change information for (const auto& change : changes) { if (change.UnitInstanceIdentifier) { if (m_status.HasSetChangeRegistration(change.SetInstanceIdentifier)) { // A unit status change ConfigurationUnitState state = AppInstaller::ToEnum(change.State); decltype(make_self>()) resultInformation; if (change.ResultCode) { resultInformation = make_self>(); resultInformation->ResultCode(change.ResultCode.value()); resultInformation->Description(hstring{ AppInstaller::Utility::ConvertToUTF16(change.ResultDescription) }); resultInformation->Details(hstring{ AppInstaller::Utility::ConvertToUTF16(change.ResultDetails) }); resultInformation->ResultSource(change.ResultSource); } auto changeData = make_self(); changeData->Initialize(state, *resultInformation, nullptr); m_status.SetChangeDetected(change.SetInstanceIdentifier, changeData, change.UnitInstanceIdentifier); } } else { // A set status change ConfigurationSetState state = AppInstaller::ToEnum(change.State); ConfigurationChangeEventType changeType = ConfigurationChangeEventType::Unknown; SetStatusItem* setStatusItem = nullptr; auto itr = m_lastSetStatus.find(change.SetInstanceIdentifier); if (itr != m_lastSetStatus.end()) { setStatusItem = &itr->second; } if (!setStatusItem) { changeType = ConfigurationChangeEventType::SetAdded; std::tie(itr, std::ignore) = m_lastSetStatus.emplace(change.SetInstanceIdentifier, SetStatusItem{ change }); setStatusItem = &itr->second; } else { changeType = (change.InQueue ? ConfigurationChangeEventType::SetStateChanged : ConfigurationChangeEventType::SetRemoved); } if (m_status.HasChangeRegistrations()) { if (!setStatusItem->Set) { setStatusItem->Set = m_status.Database().GetSet(change.SetInstanceIdentifier); } auto changeData = make_self>(); changeData->Initialize(changeType, change.SetInstanceIdentifier, state); m_status.ChangeDetected(*setStatusItem->Set, *changeData); } auto setChangeData = make_self(); setChangeData->Initialize(state); m_status.SetChangeDetected(change.SetInstanceIdentifier, setChangeData, std::nullopt); } m_changeIdentifier = change.ChangeIdentifier; } SetThreadpoolWait(m_threadPoolWait.get(), m_listenerEvent.get(), NULL); } CATCH_LOG_MSG("ChangeListener::WaitCallback exception"); ConfigurationStatus& m_status; int64_t m_changeIdentifier; std::map m_lastSetStatus; wil::unique_event m_listenerEvent; std::string m_listenerEventName; // Keep last to destroy first wil::unique_threadpool_wait m_threadPoolWait; }; } ConfigurationStatus::ConfigurationStatus(private_construction) {} ConfigurationStatus::~ConfigurationStatus() = default; std::shared_ptr ConfigurationStatus::Instance() { static std::shared_ptr s_instance; std::shared_ptr result = std::atomic_load(&s_instance); if (!result) { result = std::make_shared(private_construction{}); std::shared_ptr empty; if (!std::atomic_compare_exchange_strong(&s_instance, &empty, result)) { result = empty; } } return result; } ConfigurationSetState ConfigurationStatus::GetSetState(const winrt::guid& instanceIdentifier) { m_database.EnsureOpened(false); return m_database.GetSetState(instanceIdentifier); } clock::time_point ConfigurationStatus::GetSetFirstApply(const winrt::guid& instanceIdentifier) { m_database.EnsureOpened(false); return clock::from_sys(m_database.GetSetFirstApply(instanceIdentifier)); } clock::time_point ConfigurationStatus::GetSetApplyBegun(const winrt::guid& instanceIdentifier) { using system_clock = std::chrono::system_clock; m_database.EnsureOpened(false); system_clock::time_point result = m_database.GetSetApplyBegun(instanceIdentifier); return (result == system_clock::time_point{} ? clock::time_point{} : clock::from_sys(result)); } clock::time_point ConfigurationStatus::GetSetApplyEnded(const winrt::guid& instanceIdentifier) { using system_clock = std::chrono::system_clock; m_database.EnsureOpened(false); system_clock::time_point result = m_database.GetSetApplyEnded(instanceIdentifier); return (result == system_clock::time_point{} ? clock::time_point{} : clock::from_sys(result)); } ConfigurationUnitState ConfigurationStatus::GetUnitState(const winrt::guid& instanceIdentifier) { m_database.EnsureOpened(false); return m_database.GetUnitState(instanceIdentifier); } IConfigurationUnitResultInformation ConfigurationStatus::GetUnitResultInformation(const winrt::guid& instanceIdentifier) { m_database.EnsureOpened(false); return m_database.GetUnitResultInformation(instanceIdentifier); } void ConfigurationStatus::UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state) { m_database.EnsureOpened(); m_database.UpdateSetState(setInstanceIdentifier, state); SignalChangeListeners(); } void ConfigurationStatus::UpdateSetState(const guid& setInstanceIdentifier, bool inQueue) { m_database.EnsureOpened(); m_database.UpdateSetInQueue(setInstanceIdentifier, inQueue); SignalChangeListeners(); } void ConfigurationStatus::UpdateUnitState(const guid& setInstanceIdentifier, const com_ptr& changeData) { m_database.EnsureOpened(); m_database.UpdateUnitState(setInstanceIdentifier, changeData); SignalChangeListeners(); } ConfigurationStatus::SetChangeRegistration::SetChangeRegistration(const winrt::guid& instanceIdentifier, ConfigurationSet* configurationSet) : m_status(Instance()), m_instanceIdentifier(instanceIdentifier), m_configurationSet(configurationSet) {} ConfigurationStatus::SetChangeRegistration::~SetChangeRegistration() { m_status->RemoveSetChangeRegistration(m_instanceIdentifier, m_configurationSet); } std::shared_ptr ConfigurationStatus::RegisterForSetChange(ConfigurationSet& set) { m_database.EnsureOpened(); winrt::guid instanceIdentifier = set.InstanceIdentifier(); { std::lock_guard lock{ m_changeRegistrationsMutex }; m_setChangeRegistrations.emplace(instanceIdentifier, &set); EnableChangeListeningIfNeeded(); } return std::make_shared(instanceIdentifier, &set); } void ConfigurationStatus::RemoveSetChangeRegistration(const winrt::guid& instanceIdentifier, ConfigurationSet* configurationSet) noexcept { std::lock_guard lock{ m_changeRegistrationsMutex }; auto [begin, end] = m_setChangeRegistrations.equal_range(instanceIdentifier); for (; begin != end; ++begin) { if (begin->second == configurationSet) { m_setChangeRegistrations.erase(begin); break; } } DisableChangeListeningIfNeeded(); } ConfigurationStatus::ChangeRegistration::ChangeRegistration(const winrt::guid& instanceIdentifier) : m_status(Instance()), m_instanceIdentifier(instanceIdentifier) {} ConfigurationStatus::ChangeRegistration::~ChangeRegistration() { m_status->RemoveChangeRegistration(m_instanceIdentifier); } std::shared_ptr ConfigurationStatus::RegisterForChange(ConfigurationProcessor& processor) { m_database.EnsureOpened(); GUID instanceIdentifier; std::ignore = CoCreateGuid(&instanceIdentifier); { std::lock_guard lock{ m_changeRegistrationsMutex }; m_changeRegistrations.emplace_back(instanceIdentifier, &processor); EnableChangeListeningIfNeeded(); } return std::make_shared(instanceIdentifier); } void ConfigurationStatus::RemoveChangeRegistration(const winrt::guid& instanceIdentifier) noexcept { std::lock_guard lock{ m_changeRegistrationsMutex }; for (auto itr = m_changeRegistrations.begin(); itr != m_changeRegistrations.end(); ++itr) { if (itr->first == instanceIdentifier) { m_changeRegistrations.erase(itr); DisableChangeListeningIfNeeded(); return; } } } void ConfigurationStatus::EnableChangeListeningIfNeeded() { if (!m_changeListener) { m_changeListener = std::make_unique(*this); } } void ConfigurationStatus::DisableChangeListeningIfNeeded() { if (m_changeListener && m_setChangeRegistrations.empty() && m_changeRegistrations.empty()) { m_changeListener.reset(); } } void ConfigurationStatus::SignalChangeListeners() { std::vector changeListeners = m_database.GetChangeListeners(); for (const auto& listener : changeListeners) { std::wstring objectName = AppInstaller::Utility::ConvertToUTF16(listener.ObjectName); wil::unique_event listenerEvent; if (listenerEvent.try_open(objectName.c_str(), EVENT_MODIFY_STATE)) { listenerEvent.SetEvent(); } else { m_database.RemoveListener(listener.ObjectName); } } } ConfigurationDatabase& ConfigurationStatus::Database() { return m_database; } bool ConfigurationStatus::HasSetChangeRegistration(const guid& setInstanceIdentifier) { std::lock_guard lock{ m_changeRegistrationsMutex }; auto [begin, end] = m_setChangeRegistrations.equal_range(setInstanceIdentifier); return begin != end; } bool ConfigurationStatus::HasChangeRegistrations() { std::lock_guard lock{ m_changeRegistrationsMutex }; return !m_changeRegistrations.empty(); } void ConfigurationStatus::SetChangeDetected(const winrt::guid& setInstanceIdentifier, com_ptr& data, const std::optional& unitInstanceIdentifier) { std::vector setChangeRegistrations; { std::lock_guard lock{ m_changeRegistrationsMutex }; auto [begin, end] = m_setChangeRegistrations.equal_range(setInstanceIdentifier); for (; begin != end; ++begin) { setChangeRegistrations.emplace_back(begin->second); } } for (ConfigurationSet* set : setChangeRegistrations) { set->ConfigurationSetChange(data, unitInstanceIdentifier); } } void ConfigurationStatus::ChangeDetected(const Configuration::ConfigurationSet& set, const Configuration::ConfigurationChangeData& data) { std::vector> changeRegistrations; { std::lock_guard lock{ m_changeRegistrationsMutex }; changeRegistrations = m_changeRegistrations; } for (const auto& registration : changeRegistrations) { registration.second->ConfigurationChange(set, data); } } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationStatus.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Database/ConfigurationDatabase.h" #include "ConfigurationSetChangeData.h" #include #include #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Forward declarations struct ConfigurationProcessor; struct ConfigurationSet; struct ConfigurationSetChangeData; namespace details { struct ChangeListener; } // Provides access to overall configuration status information. struct ConfigurationStatus { private: struct private_construction {}; public: friend details::ChangeListener; ConfigurationStatus(private_construction); ConfigurationStatus(const ConfigurationStatus&) = delete; ConfigurationStatus& operator=(const ConfigurationStatus&) = delete; ConfigurationStatus(ConfigurationStatus&&) = delete; ConfigurationStatus& operator=(ConfigurationStatus&&) = delete; ~ConfigurationStatus(); // Gets the singleton instance. static std::shared_ptr Instance(); // Get various set state information ConfigurationSetState GetSetState(const guid& instanceIdentifier); clock::time_point GetSetFirstApply(const guid& instanceIdentifier); clock::time_point GetSetApplyBegun(const guid& instanceIdentifier); clock::time_point GetSetApplyEnded(const guid& instanceIdentifier); ConfigurationUnitState GetUnitState(const guid& instanceIdentifier); IConfigurationUnitResultInformation GetUnitResultInformation(const guid& instanceIdentifier); // Record state changes void UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state); void UpdateSetState(const guid& setInstanceIdentifier, bool inQueue); void UpdateUnitState(const guid& setInstanceIdentifier, const com_ptr& changeData); // Keeps data for a set change listener. struct SetChangeRegistration { SetChangeRegistration(const guid& instanceIdentifier, ConfigurationSet* configurationSet); SetChangeRegistration(const SetChangeRegistration&) = delete; SetChangeRegistration& operator=(const SetChangeRegistration&) = delete; SetChangeRegistration(SetChangeRegistration&&) = delete; SetChangeRegistration& operator=(SetChangeRegistration&&) = delete; ~SetChangeRegistration(); private: std::shared_ptr m_status; guid m_instanceIdentifier; ConfigurationSet* m_configurationSet; }; std::shared_ptr RegisterForSetChange(ConfigurationSet& set); void RemoveSetChangeRegistration(const guid& instanceIdentifier, ConfigurationSet* configurationSet) noexcept; // Keeps data for a change listener. struct ChangeRegistration { ChangeRegistration(const guid& instanceIdentifier); ChangeRegistration(const ChangeRegistration&) = delete; ChangeRegistration& operator=(const ChangeRegistration&) = delete; ChangeRegistration(ChangeRegistration&&) = delete; ChangeRegistration& operator=(ChangeRegistration&&) = delete; ~ChangeRegistration(); private: std::shared_ptr m_status; guid m_instanceIdentifier; }; std::shared_ptr RegisterForChange(ConfigurationProcessor& processor); void RemoveChangeRegistration(const guid& instanceIdentifier) noexcept; private: void EnableChangeListeningIfNeeded(); void DisableChangeListeningIfNeeded(); void SignalChangeListeners(); ConfigurationDatabase& Database(); bool HasSetChangeRegistration(const guid& setInstanceIdentifier); bool HasChangeRegistrations(); void SetChangeDetected(const guid& setInstanceIdentifier, com_ptr& data, const std::optional& unitInstanceIdentifier); void ChangeDetected(const Configuration::ConfigurationSet& set, const Configuration::ConfigurationChangeData& data); ConfigurationDatabase m_database; std::mutex m_changeRegistrationsMutex; std::multimap m_setChangeRegistrations; std::vector> m_changeRegistrations; // Keep this last to ensure it is destroyed first std::unique_ptr m_changeListener; }; } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationUnit.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationUnit.h" #include "ConfigurationUnit.g.cpp" #include "ConfigurationSetParser.h" #include "ConfigurationStatus.h" namespace winrt::Microsoft::Management::Configuration::implementation { namespace { using ValueSet = Windows::Foundation::Collections::ValueSet; ValueSet Clone(const ValueSet& source) { ValueSet result; for (const auto& entry : source) { ValueSet child = entry.Value().try_as(); if (child) { result.Insert(entry.Key(), Clone(child)); } else { result.Insert(entry.Key(), entry.Value()); } } return result; } Windows::Foundation::Collections::IVector Clone(const Windows::Foundation::Collections::IVector& value) { std::vector temp{ value.Size() }; value.GetMany(0, temp); return winrt::multi_threaded_vector(std::move(temp)); } } ConfigurationUnit::ConfigurationUnit() { GUID instanceIdentifier; THROW_IF_FAILED(CoCreateGuid(&instanceIdentifier)); m_instanceIdentifier = instanceIdentifier; } ConfigurationUnit::ConfigurationUnit(const guid& instanceIdentifier) : m_instanceIdentifier(instanceIdentifier) { } hstring ConfigurationUnit::Type() { return m_type; } void ConfigurationUnit::Type(const hstring& value) { m_type = value; } guid ConfigurationUnit::InstanceIdentifier() { return m_instanceIdentifier; } hstring ConfigurationUnit::Identifier() { return m_identifier; } void ConfigurationUnit::Identifier(const hstring& value) { m_identifier = value; } ConfigurationUnitIntent ConfigurationUnit::Intent() { return m_intent; } void ConfigurationUnit::Intent(ConfigurationUnitIntent value) { m_intent = value; } Windows::Foundation::Collections::IVector ConfigurationUnit::Dependencies() { return m_dependencies; } void ConfigurationUnit::Dependencies(const Windows::Foundation::Collections::IVector& value) { THROW_HR_IF(E_POINTER, !value); m_dependencies = value; } void ConfigurationUnit::Dependencies(std::vector&& value) { m_dependencies = winrt::multi_threaded_vector(std::move(value)); } Windows::Foundation::Collections::ValueSet ConfigurationUnit::Metadata() { return m_metadata; } void ConfigurationUnit::Metadata(const Windows::Foundation::Collections::ValueSet& value) { THROW_HR_IF(E_POINTER, !value); m_metadata = value; } Windows::Foundation::Collections::ValueSet ConfigurationUnit::Settings() { return m_settings; } void ConfigurationUnit::Settings(const Windows::Foundation::Collections::ValueSet& value) { THROW_HR_IF(E_POINTER, !value); m_settings = value; } IConfigurationUnitProcessorDetails ConfigurationUnit::Details() { return m_details; } void ConfigurationUnit::Details(IConfigurationUnitProcessorDetails details) { m_details = std::move(details); } ConfigurationUnitState ConfigurationUnit::State() { auto status = ConfigurationStatus::Instance(); return status->GetUnitState(m_instanceIdentifier); } IConfigurationUnitResultInformation ConfigurationUnit::ResultInformation() { auto status = ConfigurationStatus::Instance(); return status->GetUnitResultInformation(m_instanceIdentifier); } bool ConfigurationUnit::IsActive() { return m_isActive; } void ConfigurationUnit::IsActive(bool value) { m_isActive = value; } Configuration::ConfigurationUnit ConfigurationUnit::Copy() { auto result = make_self(); result->m_type = m_type; result->m_intent = m_intent; result->m_dependencies = Clone(m_dependencies); result->m_metadata = Clone(m_metadata); result->m_settings = Clone(m_settings); result->m_details = m_details; result->m_environment = make_self(*m_environment); return *result; } bool ConfigurationUnit::IsGroup() { return m_isGroup; } void ConfigurationUnit::IsGroup(bool value) { m_isGroup = value; if (value) { if (!m_units) { m_units = winrt::multi_threaded_vector(); } } } Windows::Foundation::Collections::IVector ConfigurationUnit::Units() { return m_units; } void ConfigurationUnit::Units(const Windows::Foundation::Collections::IVector& value) { if (m_isGroup) { THROW_HR_IF(E_POINTER, !value); } else if (value) { m_isGroup = true; } m_units = value; } void ConfigurationUnit::Units(std::vector&& value) { m_units = winrt::multi_threaded_vector(std::move(value)); } Configuration::ConfigurationEnvironment ConfigurationUnit::Environment() { return *m_environment; } implementation::ConfigurationEnvironment& ConfigurationUnit::EnvironmentInternal() { return *m_environment; } HRESULT STDMETHODCALLTYPE ConfigurationUnit::SetLifetimeWatcher(IUnknown* watcher) { return AppInstaller::WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher); } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationUnit.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationUnit.g.h" #include "ConfigurationEnvironment.h" #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationUnit : ConfigurationUnitT>, AppInstaller::WinRT::LifetimeWatcherBase, AppInstaller::WinRT::ModuleCountBase { ConfigurationUnit(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) ConfigurationUnit(const guid& instanceIdentifier); implementation::ConfigurationEnvironment& EnvironmentInternal(); #endif hstring Type(); void Type(const hstring& value); guid InstanceIdentifier(); hstring Identifier(); void Identifier(const hstring& value); ConfigurationUnitIntent Intent(); void Intent(ConfigurationUnitIntent value); Windows::Foundation::Collections::IVector Dependencies(); void Dependencies(const Windows::Foundation::Collections::IVector& value); Windows::Foundation::Collections::ValueSet Metadata(); void Metadata(const Windows::Foundation::Collections::ValueSet& value); Windows::Foundation::Collections::ValueSet Settings(); void Settings(const Windows::Foundation::Collections::ValueSet& value); IConfigurationUnitProcessorDetails Details(); ConfigurationUnitState State(); IConfigurationUnitResultInformation ResultInformation(); bool IsActive(); void IsActive(bool value); Configuration::ConfigurationUnit Copy(); bool IsGroup(); void IsGroup(bool value); Windows::Foundation::Collections::IVector Units(); void Units(const Windows::Foundation::Collections::IVector& value); Configuration::ConfigurationEnvironment Environment(); HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Dependencies(std::vector&& value); void Details(IConfigurationUnitProcessorDetails details); void Units(std::vector&& value); private: hstring m_type; guid m_instanceIdentifier; hstring m_identifier; ConfigurationUnitIntent m_intent = ConfigurationUnitIntent::Apply; Windows::Foundation::Collections::IVector m_dependencies{ winrt::multi_threaded_vector() }; Windows::Foundation::Collections::ValueSet m_metadata; Windows::Foundation::Collections::ValueSet m_settings; IConfigurationUnitProcessorDetails m_details{ nullptr }; bool m_isActive = true; bool m_isGroup = false; Windows::Foundation::Collections::IVector m_units = nullptr; com_ptr m_environment{ make_self() }; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Configuration::factory_implementation { struct ConfigurationUnit : ConfigurationUnitT { }; } #endif ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationUnitResultInformation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConfigurationUnitResultInformation.h" #include "AppInstallerErrors.h" #include "AppInstallerStrings.h" namespace winrt::Microsoft::Management::Configuration::implementation { namespace { ConfigurationUnitResultSource FromHRESULT(hresult resultCode) { switch (resultCode) { case WINGET_CONFIG_ERROR_UNIT_NOT_INSTALLED: case WINGET_CONFIG_ERROR_UNIT_NOT_FOUND_REPOSITORY: case WINGET_CONFIG_ERROR_UNIT_MULTIPLE_MATCHES: case WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE: case WINGET_CONFIG_ERROR_UNIT_SETTING_CONFIG_ROOT: case WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE_ADMIN: return ConfigurationUnitResultSource::ConfigurationSet; case WINGET_CONFIG_ERROR_UNIT_MODULE_CONFLICT: return ConfigurationUnitResultSource::SystemState; } return ConfigurationUnitResultSource::Internal; } hstring SanitizeString(std::wstring_view value) { using namespace AppInstaller::Utility; return hstring{ ConvertToUTF16(ConvertControlCodesToPictures(ConvertToUTF8(value))) }; } } void ConfigurationUnitResultInformation::Initialize(const Configuration::IConfigurationUnitResultInformation& other) { if (other) { m_resultCode = other.ResultCode(); m_description = SanitizeString(other.Description()); m_details = SanitizeString(other.Details()); m_resultSource = other.ResultSource(); } } void ConfigurationUnitResultInformation::Initialize(hresult resultCode, std::wstring_view description) { m_resultCode = resultCode; m_description = SanitizeString(description); m_resultSource = FromHRESULT(resultCode); } void ConfigurationUnitResultInformation::Initialize(hresult resultCode, hstring description) { m_resultCode = resultCode; m_description = SanitizeString(description); m_resultSource = FromHRESULT(resultCode); } void ConfigurationUnitResultInformation::Initialize(hresult resultCode, ConfigurationUnitResultSource resultSource) { m_resultCode = resultCode; m_resultSource = resultSource; } void ConfigurationUnitResultInformation::Initialize(hresult resultCode, std::wstring_view description, std::wstring_view details, ConfigurationUnitResultSource resultSource) { m_resultCode = resultCode; m_description = SanitizeString(description); m_details = SanitizeString(details); m_resultSource = resultSource; } hresult ConfigurationUnitResultInformation::ResultCode() const { return m_resultCode; } void ConfigurationUnitResultInformation::ResultCode(hresult resultCode) { m_resultCode = resultCode; } hstring ConfigurationUnitResultInformation::Description() { return m_description; } void ConfigurationUnitResultInformation::Description(hstring value) { m_description = value; } hstring ConfigurationUnitResultInformation::Details() { return m_details; } void ConfigurationUnitResultInformation::Details(hstring value) { m_details = value; } ConfigurationUnitResultSource ConfigurationUnitResultInformation::ResultSource() const { return m_resultSource; } void ConfigurationUnitResultInformation::ResultSource(ConfigurationUnitResultSource value) { m_resultSource = value; } } ================================================ FILE: src/Microsoft.Management.Configuration/ConfigurationUnitResultInformation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct ConfigurationUnitResultInformation : winrt::implements { ConfigurationUnitResultInformation() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(const Configuration::IConfigurationUnitResultInformation& other); void Initialize(hresult resultCode, std::wstring_view description); void Initialize(hresult resultCode, hstring description); void Initialize(hresult resultCode, ConfigurationUnitResultSource resultSource); void Initialize(hresult resultCode, std::wstring_view description, std::wstring_view details, ConfigurationUnitResultSource resultSource); #endif hresult ResultCode() const; void ResultCode(hresult resultCode); hstring Description(); void Description(hstring value); hstring Details(); void Details(hstring value); ConfigurationUnitResultSource ResultSource() const; void ResultSource(ConfigurationUnitResultSource value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: hresult m_resultCode; hstring m_description; hstring m_details; ConfigurationUnitResultSource m_resultSource = ConfigurationUnitResultSource::None; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/ConfigurationDatabase.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Database/ConfigurationDatabase.h" #include "Database/Schema/IConfigurationDatabase.h" #include "ConfigurationUnitResultInformation.h" #include #include #include "Filesystem.h" using namespace AppInstaller::SQLite; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation { namespace { // Use an alternate location for the dev build history. #ifdef AICLI_DISABLE_TEST_HOOKS constexpr std::string_view s_Database_DirectoryName = "History"sv; #else constexpr std::string_view s_Database_DirectoryName = "DevHistory"sv; #endif constexpr std::string_view s_Database_FileName = "config.db"sv; #define s_Database_MutexName L"WindowsPackageManager_Configuration_DatabaseMutex" std::vector ConvertStatusItems(const std::vector& input) { std::vector result; for (const auto& item : input) { ConfigurationDatabase::StatusItem statusItem{}; std::tie( statusItem.ChangeIdentifier, statusItem.ChangeTime, statusItem.SetInstanceIdentifier, statusItem.InQueue, statusItem.UnitInstanceIdentifier, statusItem.State, statusItem.ResultCode, statusItem.ResultDescription, statusItem.ResultDetails, statusItem.ResultSource) = item; result.emplace_back(std::move(statusItem)); } return result; } } ConfigurationDatabase::ConfigurationDatabase() = default; ConfigurationDatabase::ConfigurationDatabase(ConfigurationDatabase&&) = default; ConfigurationDatabase& ConfigurationDatabase::operator=(ConfigurationDatabase&&) = default; ConfigurationDatabase::~ConfigurationDatabase() = default; void ConfigurationDatabase::EnsureOpened(bool createIfNeeded) { #ifdef AICLI_DISABLE_TEST_HOOKS // While under development, treat errors escaping this function as a test hook. try { #endif if (!std::atomic_load(&m_database)) { std::filesystem::path databaseDirectory = AppInstaller::Filesystem::GetPathTo(PathName::LocalState) / s_Database_DirectoryName; std::filesystem::path databaseFile = databaseDirectory / s_Database_FileName; { wil::unique_mutex databaseMutex; databaseMutex.create(s_Database_MutexName); auto databaseLock = databaseMutex.acquire(); if (!std::filesystem::is_regular_file(databaseFile) && createIfNeeded) { if (std::filesystem::exists(databaseFile)) { std::filesystem::remove_all(databaseDirectory); } std::filesystem::create_directories(databaseDirectory); auto connection = std::make_shared(databaseFile, IConfigurationDatabase::GetLatestVersion()); auto database = std::shared_ptr{ IConfigurationDatabase::CreateFor(connection) }; database->InitializeDatabase(); std::atomic_store(&m_connection, connection); std::atomic_store(&m_database, database); } } if (!std::atomic_load(&m_connection)) { std::shared_ptr empty; auto connection = std::make_shared(databaseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); std::atomic_compare_exchange_strong(&m_connection, &empty, connection); } if (!std::atomic_load(&m_database)) { std::shared_ptr empty; auto database = std::shared_ptr{ IConfigurationDatabase::CreateFor(std::atomic_load(&m_connection), true) }; std::atomic_compare_exchange_strong(&m_database, &empty, database); } } #ifdef AICLI_DISABLE_TEST_HOOKS } CATCH_LOG(); #endif } template auto ConfigurationDatabase::ExecuteReadOperation(std::string_view operationName, OperationT&& operation, bool requireDatabase) const { using ResultT = decltype(operation(std::declval&>())); ResultT result{}; #ifdef AICLI_DISABLE_TEST_HOOKS // While under development, treat errors escaping this function as a test hook. try { #endif auto database = std::atomic_load(&m_database); if (database) { auto transaction = BeginTransaction(operationName, false, database); result = operation(database); } else if (requireDatabase) { THROW_HR(E_NOT_VALID_STATE); } #ifdef AICLI_DISABLE_TEST_HOOKS } CATCH_LOG(); #endif return result; } template void ConfigurationDatabase::ExecuteWriteOperation(std::string_view operationName, OperationT&& operation, bool silentlyIgnoreNoDatabase) { #ifdef AICLI_DISABLE_TEST_HOOKS // While under development, treat errors escaping this function as a test hook. try { #endif auto database = std::atomic_load(&m_database); if (!database) { THROW_HR_IF(E_NOT_VALID_STATE, !silentlyIgnoreNoDatabase); return; } auto transaction = BeginTransaction(operationName, true, database); operation(database); std::atomic_load(&m_connection)->SetLastWriteTime(); transaction->Commit(); #ifdef AICLI_DISABLE_TEST_HOOKS } CATCH_LOG(); #endif } std::vector ConfigurationDatabase::GetSetHistory() const { return ExecuteReadOperation("GetSetHistory", [&](std::shared_ptr& database) { return database->GetSets(); }); } ConfigurationDatabase::ConfigurationSetPtr ConfigurationDatabase::GetSet(const GUID& instanceIdentifier) const { return ExecuteReadOperation("GetSet", [&](std::shared_ptr& database) { return database->GetSet(instanceIdentifier); }); } void ConfigurationDatabase::WriteSetHistory(const Configuration::ConfigurationSet& configurationSet, bool preferNewHistory) { THROW_HR_IF_NULL(E_POINTER, configurationSet); ExecuteWriteOperation("WriteSetHistory", [&](std::shared_ptr& database) { std::optional setRowId = database->GetSetRowId(configurationSet.InstanceIdentifier()); if (!setRowId && !preferNewHistory) { // TODO: Use conflict detection code to check for a matching set } if (setRowId) { database->UpdateSet(setRowId.value(), configurationSet); } else { database->AddSet(configurationSet); } }); } void ConfigurationDatabase::RemoveSetHistory(const Configuration::ConfigurationSet& configurationSet) { THROW_HR_IF_NULL(E_POINTER, configurationSet); ExecuteWriteOperation("RemoveSetHistory", [&](std::shared_ptr& database) { std::optional setRowId = database->GetSetRowId(configurationSet.InstanceIdentifier()); if (!setRowId) { // TODO: Use conflict detection code to check for a matching set } if (setRowId) { database->RemoveSet(setRowId.value()); std::atomic_load(&m_connection)->SetLastWriteTime(); } }, true); } void ConfigurationDatabase::AddQueueItem(const Configuration::ConfigurationSet& configurationSet, const std::string& objectName) { THROW_HR_IF_NULL(E_POINTER, configurationSet); ExecuteWriteOperation("AddQueueItem", [&](std::shared_ptr& database) { database->AddQueueItem(configurationSet.InstanceIdentifier(), objectName); }); } void ConfigurationDatabase::SetActiveQueueItem(const std::string& objectName) { ExecuteWriteOperation("SetActiveQueueItem", [&](std::shared_ptr& database) { database->SetActiveQueueItem(objectName); }); } std::vector ConfigurationDatabase::GetQueueItems() const { return ExecuteReadOperation("GetQueueItems", [&](std::shared_ptr& database) { std::vector result; auto queueItems = database->GetQueueItems(); result.reserve(queueItems.size()); for (const auto& item : queueItems) { QueueItem resultItem; std::tie(resultItem.SetInstanceIdentifier, resultItem.ObjectName, resultItem.QueuedAt, resultItem.ProcessId, resultItem.Active) = item; result.emplace_back(std::move(resultItem)); } return result; }, true); } void ConfigurationDatabase::RemoveQueueItem(const std::string& objectName) { ExecuteWriteOperation("RemoveQueueItem", [&](std::shared_ptr& database) { database->RemoveQueueItem(objectName); }); } std::vector ConfigurationDatabase::GetStatusSince(int64_t changeIdentifier) const { return ExecuteReadOperation("GetStatusSince", [&](std::shared_ptr& database) { return ConvertStatusItems(database->GetStatusSince(changeIdentifier)); }); } ConfigurationDatabase::StatusBaseline ConfigurationDatabase::GetStatusBaseline() const { return ExecuteReadOperation("GetStatusBaseline", [&](std::shared_ptr& database) { auto [changeIdentifier, setStatus] = database->GetStatusBaseline(); StatusBaseline result{}; result.ChangeIdentifier = changeIdentifier; result.SetStatus = ConvertStatusItems(setStatus); return result; }); } void ConfigurationDatabase::AddListener(const std::string& objectName) { ExecuteWriteOperation("AddListener", [&](std::shared_ptr& database) { database->AddListener(objectName); }); } void ConfigurationDatabase::RemoveListener(const std::string& objectName) { ExecuteWriteOperation("RemoveListener", [&](std::shared_ptr& database) { database->RemoveListener(objectName); }); } std::vector ConfigurationDatabase::GetChangeListeners() const { return ExecuteReadOperation("GetChangeListeners", [&](std::shared_ptr& database) { std::vector result; for (const auto& item : database->GetChangeListeners()) { StatusChangeListener listener{}; std::tie(listener.ObjectName, listener.Started, listener.ProcessId) = item; result.emplace_back(std::move(listener)); } return result; }); } void ConfigurationDatabase::UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state) { ExecuteWriteOperation("UpdateSetState", [&](std::shared_ptr& database) { database->UpdateSetState(setInstanceIdentifier, state); }); } void ConfigurationDatabase::UpdateSetInQueue(const guid& setInstanceIdentifier, bool inQueue) { ExecuteWriteOperation("UpdateSetInQueue", [&](std::shared_ptr& database) { database->UpdateSetInQueue(setInstanceIdentifier, inQueue); }); } void ConfigurationDatabase::UpdateUnitState(const guid& setInstanceIdentifier, const com_ptr& changeData) { ExecuteWriteOperation("UpdateUnitState", [&](std::shared_ptr& database) { database->UpdateUnitState(setInstanceIdentifier, changeData); }); } ConfigurationSetState ConfigurationDatabase::GetSetState(const guid& instanceIdentifier) { return ExecuteReadOperation("GetSetState", [&](std::shared_ptr& database) { return database->GetSetState(instanceIdentifier); }); } std::chrono::system_clock::time_point ConfigurationDatabase::GetSetFirstApply(const guid& instanceIdentifier) { return ExecuteReadOperation("GetSetFirstApply", [&](std::shared_ptr& database) { return database->GetSetFirstApply(instanceIdentifier); }); } std::chrono::system_clock::time_point ConfigurationDatabase::GetSetApplyBegun(const guid& instanceIdentifier) { return ExecuteReadOperation("GetSetApplyBegun", [&](std::shared_ptr& database) { return database->GetSetApplyBegun(instanceIdentifier); }); } std::chrono::system_clock::time_point ConfigurationDatabase::GetSetApplyEnded(const guid& instanceIdentifier) { return ExecuteReadOperation("GetSetApplyEnded", [&](std::shared_ptr& database) { return database->GetSetApplyEnded(instanceIdentifier); }); } ConfigurationUnitState ConfigurationDatabase::GetUnitState(const guid& instanceIdentifier) { return ExecuteReadOperation("GetUnitState", [&](std::shared_ptr& database) { return database->GetUnitState(instanceIdentifier); }); } IConfigurationUnitResultInformation ConfigurationDatabase::GetUnitResultInformation(const guid& instanceIdentifier) { return ExecuteReadOperation("GetUnitResultInformation", [&](std::shared_ptr& database) { com_ptr> result; auto resultInformation = database->GetUnitResultInformation(instanceIdentifier); if (resultInformation) { result = make_self>(); result->Initialize( std::get<0>(resultInformation.value()), ConvertToUTF16(std::get<1>(resultInformation.value())), ConvertToUTF16(std::get<2>(resultInformation.value())), std::get<3>(resultInformation.value())); } IConfigurationUnitResultInformation actualResult; if (result) { actualResult = *result; } return actualResult; }); } ConfigurationDatabase::TransactionLock ConfigurationDatabase::BeginTransaction(std::string_view name, bool forWrite, std::shared_ptr& database) const { auto connection = std::atomic_load(&m_connection); THROW_HR_IF_NULL(E_NOT_VALID_STATE, connection); TransactionLock result = connection->TryBeginTransaction(name, forWrite); while (!result) { { auto connectionLock = connection->LockConnection(); auto newDatabase = std::shared_ptr{ IConfigurationDatabase::CreateFor(connection) }; if (std::atomic_compare_exchange_strong(&m_database, &database, newDatabase)) { database = newDatabase; } } result = connection->TryBeginTransaction(name, forWrite); } return result; } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/ConfigurationDatabase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConfigurationSetChangeData.h" #include #include #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Forward declarations struct ConfigurationSet; struct IConfigurationDatabase; // Allows access to the configuration database. struct ConfigurationDatabase { using ConfigurationSetPtr = winrt::com_ptr; ConfigurationDatabase(); ConfigurationDatabase(const ConfigurationDatabase&) = delete; ConfigurationDatabase& operator=(const ConfigurationDatabase&) = delete; ConfigurationDatabase(ConfigurationDatabase&&); ConfigurationDatabase& operator=(ConfigurationDatabase&&); ~ConfigurationDatabase(); // Ensures that the database connection is established and the schema interface is created appropriately. // If `createIfNeeded` is false, this function will not create the database if it does not exist. // If not connected, any read methods will return empty results and any write methods will throw. void EnsureOpened(bool createIfNeeded = true); // Gets all of the configuration sets from the database. std::vector GetSetHistory() const; // Gets the set with the given identifier. ConfigurationSetPtr GetSet(const GUID& instanceIdentifier) const; // Writes the given set to the database history, attempting to merge with a matching set if one exists unless preferNewHistory is true. void WriteSetHistory(const Configuration::ConfigurationSet& configurationSet, bool preferNewHistory); // Removes the given set from the database history if it is present. void RemoveSetHistory(const Configuration::ConfigurationSet& configurationSet); // Adds a new queue item for the given configuration set and object name. void AddQueueItem(const Configuration::ConfigurationSet& configurationSet, const std::string& objectName); // Sets the queue item with the given object name as active. void SetActiveQueueItem(const std::string& objectName); // Data about a queue item. struct QueueItem { GUID SetInstanceIdentifier{}; std::string ObjectName; std::chrono::system_clock::time_point QueuedAt; DWORD ProcessId{}; bool Active = false; }; // Gets all queue items in queue order (item at index 0 is active/next). std::vector GetQueueItems() const; // Removes the queue item with the given object name. void RemoveQueueItem(const std::string& objectName); // A status line item. struct StatusItem { int64_t ChangeIdentifier; std::chrono::system_clock::time_point ChangeTime; GUID SetInstanceIdentifier; bool InQueue; std::optional UnitInstanceIdentifier; int32_t State; std::optional ResultCode; std::string ResultDescription; std::string ResultDetails; ConfigurationUnitResultSource ResultSource; }; // Gets all changed status items after the given change identifier. std::vector GetStatusSince(int64_t changeIdentifier) const; // The status baseline data. struct StatusBaseline { int64_t ChangeIdentifier = 0; std::vector SetStatus; }; // Gets the current status baseline. StatusBaseline GetStatusBaseline() const; // Data about a status change listener. struct StatusChangeListener { std::string ObjectName; std::chrono::system_clock::time_point Started; DWORD ProcessId{}; }; // Adds a listener to the database. void AddListener(const std::string& objectName); // Removes a listener from the database. void RemoveListener(const std::string& objectName); // Gets all listeners in the database. std::vector GetChangeListeners() const; // Updates the set state in the database. void UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state); // Updates the set "in queue" state in the database. void UpdateSetInQueue(const guid& setInstanceIdentifier, bool inQueue); // Updates the unit state in the database. void UpdateUnitState(const guid& setInstanceIdentifier, const com_ptr& changeData); // Read various status values. ConfigurationSetState GetSetState(const guid& instanceIdentifier); std::chrono::system_clock::time_point GetSetFirstApply(const guid& instanceIdentifier); std::chrono::system_clock::time_point GetSetApplyBegun(const guid& instanceIdentifier); std::chrono::system_clock::time_point GetSetApplyEnded(const guid& instanceIdentifier); ConfigurationUnitState GetUnitState(const guid& instanceIdentifier); IConfigurationUnitResultInformation GetUnitResultInformation(const guid& instanceIdentifier); private: std::shared_ptr m_connection; mutable std::shared_ptr m_database; using TransactionLock = decltype(m_connection->TryBeginTransaction({}, true)); // Begins a transaction, which may require upgrading to a newer schema version. TransactionLock BeginTransaction(std::string_view name, bool forWrite, std::shared_ptr& database) const; // Performs the boilerplate setup for a read, then executes the given operation. template auto ExecuteReadOperation(std::string_view operationName, OperationT&& operation, bool requireDatabase = false) const; // Performs the boilerplate setup for a write, then executes the given operation. template void ExecuteWriteOperation(std::string_view operationName, OperationT&& operation, bool silentlyIgnoreNoDatabase = false); }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_1/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Database/Schema/IConfigurationDatabase.h" namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_1 { struct Interface : public IConfigurationDatabase { Interface(std::shared_ptr storage); const AppInstaller::SQLite::Version& GetSchemaVersion() override; // Version 0.1 void InitializeDatabase() override; void AddSet(const Configuration::ConfigurationSet& configurationSet) override; void UpdateSet(AppInstaller::SQLite::rowid_t target, const Configuration::ConfigurationSet& configurationSet) override; void RemoveSet(AppInstaller::SQLite::rowid_t target) override; std::vector GetSets() override; std::optional GetSetRowId(const GUID& instanceIdentifier) override; // Version 0.2 bool MigrateFrom(IConfigurationDatabase* current) override; // Version 0.3 ConfigurationSetPtr GetSet(const GUID& instanceIdentifier) override; protected: std::shared_ptr m_storage; }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_1/Interface_0_1.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Interface.h" #include "SetInfoTable.h" #include "UnitInfoTable.h" using namespace AppInstaller::SQLite; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_1 { static constexpr AppInstaller::SQLite::Version s_InterfaceVersion{ 0, 1 }; Interface::Interface(std::shared_ptr storage) : m_storage(std::move(storage)) {} const AppInstaller::SQLite::Version& Interface::GetSchemaVersion() { return s_InterfaceVersion; } void Interface::InitializeDatabase() { // Must enable WAL mode outside of a transaction THROW_HR_IF(E_UNEXPECTED, !m_storage->GetConnection().SetJournalMode("WAL")); Savepoint savepoint = Savepoint::Create(*m_storage, "InitializeDatabase_0_1"); SetInfoTable setInfoTable(*m_storage); setInfoTable.Create(); UnitInfoTable unitInfoTable(*m_storage); unitInfoTable.Create(); savepoint.Commit(); } void Interface::AddSet(const Configuration::ConfigurationSet& configurationSet) { Savepoint savepoint = Savepoint::Create(*m_storage, "AddSet_0_1"); SetInfoTable setInfoTable(*m_storage); setInfoTable.Add(configurationSet); savepoint.Commit(); } void Interface::UpdateSet(rowid_t target, const Configuration::ConfigurationSet& configurationSet) { Savepoint savepoint = Savepoint::Create(*m_storage, "UpdateSet_0_1"); SetInfoTable setInfoTable(*m_storage); setInfoTable.Update(target, configurationSet); savepoint.Commit(); } void Interface::RemoveSet(rowid_t target) { Savepoint savepoint = Savepoint::Create(*m_storage, "RemoveSet_0_1"); SetInfoTable setInfoTable(*m_storage); setInfoTable.Remove(target); savepoint.Commit(); } std::vector Interface::GetSets() { SetInfoTable setInfoTable(*m_storage); return setInfoTable.GetAllSets(); } std::optional Interface::GetSetRowId(const GUID& instanceIdentifier) { SetInfoTable setInfoTable(*m_storage); return setInfoTable.GetSetRowId(instanceIdentifier); } bool Interface::MigrateFrom(IConfigurationDatabase* current) { return current->GetSchemaVersion() == s_InterfaceVersion; } Interface::ConfigurationSetPtr Interface::GetSet(const GUID& instanceIdentifier) { SetInfoTable setInfoTable(*m_storage); return setInfoTable.GetSet(instanceIdentifier); } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_1/SetInfoTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "SetInfoTable.h" #include "UnitInfoTable.h" #include "ConfigurationSetSerializer.h" #include "ConfigurationSetParser.h" #include #include #include using namespace AppInstaller::SQLite; using namespace AppInstaller::SQLite::Builder; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_1 { namespace { constexpr std::string_view s_SetInfoTable_Table = "set_info"sv; constexpr std::string_view s_SetInfoTable_Column_InstanceIdentifier = "instance_identifier"sv; constexpr std::string_view s_SetInfoTable_Column_Name = "name"sv; constexpr std::string_view s_SetInfoTable_Column_Origin = "origin"sv; constexpr std::string_view s_SetInfoTable_Column_Path = "path"sv; constexpr std::string_view s_SetInfoTable_Column_FirstApply = "first_apply"sv; constexpr std::string_view s_SetInfoTable_Column_SchemaVersion = "schema_version"sv; constexpr std::string_view s_SetInfoTable_Column_Metadata = "metadata"sv; constexpr std::string_view s_SetInfoTable_Column_Parameters = "parameters"sv; constexpr std::string_view s_SetInfoTable_Column_Variables = "variables"sv; void BuildBaseSetSelectStatement(StatementBuilder& builder) { builder.Select({ RowIDName, // 0 s_SetInfoTable_Column_InstanceIdentifier, // 1 s_SetInfoTable_Column_Name, // 2 s_SetInfoTable_Column_Origin, // 3 s_SetInfoTable_Column_Path, // 4 s_SetInfoTable_Column_SchemaVersion, // 5 s_SetInfoTable_Column_Metadata, // 6 s_SetInfoTable_Column_Parameters, // 7 s_SetInfoTable_Column_Variables, // 8 }).From(s_SetInfoTable_Table); } IConfigurationDatabase::ConfigurationSetPtr GetSetFromStatement(Statement& statement, UnitInfoTable& unitInfoTable) { auto configurationSet = make_self(statement.GetColumn(1)); configurationSet->Name(hstring{ ConvertToUTF16(statement.GetColumn(2)) }); configurationSet->Origin(hstring{ ConvertToUTF16(statement.GetColumn(3)) }); configurationSet->Path(hstring{ ConvertToUTF16(statement.GetColumn(4)) }); std::string schemaVersion = statement.GetColumn(5); configurationSet->SchemaVersion(hstring{ ConvertToUTF16(schemaVersion) }); auto parser = ConfigurationSetParser::CreateForSchemaVersion(schemaVersion); configurationSet->Metadata(parser->ParseValueSet(statement.GetColumn(6))); parser->ExtractEnvironmentFromMetadata(configurationSet->Metadata(), configurationSet->EnvironmentInternal()); THROW_HR_IF(E_NOTIMPL, !statement.GetColumn(7).empty()); configurationSet->Variables(parser->ParseValueSet(statement.GetColumn(8))); std::vector winrtUnits; for (const auto& unit : unitInfoTable.GetAllUnitsForSet(statement.GetColumn(0), schemaVersion)) { winrtUnits.emplace_back(*unit); } configurationSet->Units(std::move(winrtUnits)); return configurationSet; } } SetInfoTable::SetInfoTable(Connection& connection) : m_connection(connection) {} std::string_view SetInfoTable::TableName() { return s_SetInfoTable_Table; } std::string_view SetInfoTable::InstanceIdentifierColumn() { return s_SetInfoTable_Column_InstanceIdentifier; } void SetInfoTable::Create() { Savepoint savepoint = Savepoint::Create(m_connection, "SetInfoTable_Create_0_1"); StatementBuilder tableBuilder; tableBuilder.CreateTable(s_SetInfoTable_Table).Columns({ IntegerPrimaryKey(), ColumnBuilder(s_SetInfoTable_Column_InstanceIdentifier, Type::Blob).Unique().NotNull(), ColumnBuilder(s_SetInfoTable_Column_Name, Type::Text).NotNull(), ColumnBuilder(s_SetInfoTable_Column_Origin, Type::Text).NotNull(), ColumnBuilder(s_SetInfoTable_Column_Path, Type::Text).NotNull(), ColumnBuilder(s_SetInfoTable_Column_FirstApply, Type::Int64).NotNull(), ColumnBuilder(s_SetInfoTable_Column_SchemaVersion, Type::Text).NotNull(), ColumnBuilder(s_SetInfoTable_Column_Metadata, Type::Text).NotNull(), ColumnBuilder(s_SetInfoTable_Column_Parameters, Type::Text).NotNull(), ColumnBuilder(s_SetInfoTable_Column_Variables, Type::Text).NotNull(), }); tableBuilder.Execute(m_connection); savepoint.Commit(); } rowid_t SetInfoTable::Add(const Configuration::ConfigurationSet& configurationSet) { THROW_HR_IF(E_NOTIMPL, configurationSet.Parameters().Size() > 0); Savepoint savepoint = Savepoint::Create(m_connection, "SetInfoTable_Add_0_1"); hstring schemaVersion = configurationSet.SchemaVersion(); auto serializer = ConfigurationSetSerializer::CreateSerializer(schemaVersion); StatementBuilder builder; builder.InsertInto(s_SetInfoTable_Table).Columns({ s_SetInfoTable_Column_InstanceIdentifier, s_SetInfoTable_Column_Name, s_SetInfoTable_Column_Origin, s_SetInfoTable_Column_Path, s_SetInfoTable_Column_FirstApply, s_SetInfoTable_Column_SchemaVersion, s_SetInfoTable_Column_Metadata, s_SetInfoTable_Column_Parameters, s_SetInfoTable_Column_Variables, }).Values( static_cast(configurationSet.InstanceIdentifier()), ConvertToUTF8(configurationSet.Name()), ConvertToUTF8(configurationSet.Origin()), ConvertToUTF8(configurationSet.Path()), GetCurrentUnixEpoch(), ConvertToUTF8(schemaVersion), serializer->SerializeMetadataWithEnvironment(configurationSet.Metadata(), configurationSet.Environment()), std::string{}, // Parameters serializer->SerializeValueSet(configurationSet.Variables()) ); builder.Execute(m_connection); rowid_t result = m_connection.GetLastInsertRowID(); UnitInfoTable unitInfoTable(m_connection); auto winrtUnits = configurationSet.Units(); std::vector units{ winrtUnits.Size() }; winrtUnits.GetMany(0, units); for (const auto& unit : units) { unitInfoTable.Add(unit, result, schemaVersion); } savepoint.Commit(); return result; } void SetInfoTable::Update(rowid_t target, const Configuration::ConfigurationSet& configurationSet) { THROW_HR_IF(E_NOTIMPL, configurationSet.Parameters().Size() > 0); Savepoint savepoint = Savepoint::Create(m_connection, "SetInfoTable_Update_0_1"); hstring schemaVersion = configurationSet.SchemaVersion(); auto serializer = ConfigurationSetSerializer::CreateSerializer(schemaVersion); StatementBuilder builder; builder.Update(s_SetInfoTable_Table).Set(). Column(s_SetInfoTable_Column_Name).Equals(ConvertToUTF8(configurationSet.Name())). Column(s_SetInfoTable_Column_Origin).Equals(ConvertToUTF8(configurationSet.Origin())). Column(s_SetInfoTable_Column_Path).Equals(ConvertToUTF8(configurationSet.Path())). Column(s_SetInfoTable_Column_SchemaVersion).Equals(ConvertToUTF8(schemaVersion)). Column(s_SetInfoTable_Column_Metadata).Equals(serializer->SerializeMetadataWithEnvironment(configurationSet.Metadata(), configurationSet.Environment())). Column(s_SetInfoTable_Column_Variables).Equals(serializer->SerializeValueSet(configurationSet.Variables())). Where(RowIDName).Equals(target); builder.Execute(m_connection); UnitInfoTable unitInfoTable(m_connection); unitInfoTable.UpdateForSet(target, configurationSet.Units(), schemaVersion); savepoint.Commit(); } void SetInfoTable::Remove(rowid_t target) { Savepoint savepoint = Savepoint::Create(m_connection, "SetInfoTable_Remove_0_1"); StatementBuilder builder; builder.DeleteFrom(s_SetInfoTable_Table).Where(RowIDName).Equals(target); builder.Execute(m_connection); UnitInfoTable unitInfoTable(m_connection); unitInfoTable.RemoveForSet(target); savepoint.Commit(); } std::vector SetInfoTable::GetAllSets() { std::vector result; StatementBuilder builder; BuildBaseSetSelectStatement(builder); Statement getAllSets = builder.Prepare(m_connection); UnitInfoTable unitInfoTable(m_connection); while (getAllSets.Step()) { result.emplace_back(GetSetFromStatement(getAllSets, unitInfoTable)); } return result; } std::optional SetInfoTable::GetSetRowId(const GUID& instanceIdentifier) { StatementBuilder builder; builder.Select(RowIDName).From(s_SetInfoTable_Table).Where(s_SetInfoTable_Column_InstanceIdentifier).Equals(instanceIdentifier); Statement select = builder.Prepare(m_connection); if (select.Step()) { return select.GetColumn(0); } return std::nullopt; } IConfigurationDatabase::ConfigurationSetPtr SetInfoTable::GetSet(const GUID& instanceIdentifier) { IConfigurationDatabase::ConfigurationSetPtr result; StatementBuilder builder; BuildBaseSetSelectStatement(builder); builder.Where(s_SetInfoTable_Column_InstanceIdentifier).Equals(instanceIdentifier); Statement getSet = builder.Prepare(m_connection); if (getSet.Step()) { UnitInfoTable unitInfoTable(m_connection); result = GetSetFromStatement(getSet, unitInfoTable); } return result; } std::chrono::system_clock::time_point SetInfoTable::GetSetFirstApply(const GUID& instanceIdentifier) { StatementBuilder builder; builder.Select(s_SetInfoTable_Column_FirstApply).From(s_SetInfoTable_Table).Where(s_SetInfoTable_Column_InstanceIdentifier).Equals(instanceIdentifier); Statement statement = builder.Prepare(m_connection); return (statement.Step() ? ConvertUnixEpochToSystemClock(statement.GetColumn(0)) : std::chrono::system_clock::time_point{}); } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_1/SetInfoTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" #include "Database/Schema/IConfigurationDatabase.h" #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_1 { struct SetInfoTable { SetInfoTable(AppInstaller::SQLite::Connection& connection); static std::string_view TableName(); static std::string_view InstanceIdentifierColumn(); // Creates the set info table. void Create(); // Adds the given configuration set to the table. // Returns the row id of the added set. AppInstaller::SQLite::rowid_t Add(const Configuration::ConfigurationSet& configurationSet); // Updates the set with the target row id using the given set. void Update(AppInstaller::SQLite::rowid_t target, const Configuration::ConfigurationSet& configurationSet); // Removes the set with the target row id. void Remove(AppInstaller::SQLite::rowid_t target); // Gets all of the sets from the table. std::vector GetAllSets(); // Gets the row id of the set with the given instance identifier. std::optional GetSetRowId(const GUID& instanceIdentifier); // Gets the set with the given instance identifier. IConfigurationDatabase::ConfigurationSetPtr GetSet(const GUID& instanceIdentifier); // Gets a set's first apply time. std::chrono::system_clock::time_point GetSetFirstApply(const GUID& instanceIdentifier); private: AppInstaller::SQLite::Connection& m_connection; }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_1/UnitInfoTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "UnitInfoTable.h" #include "ConfigurationUnit.h" #include "ConfigurationSetParser.h" #include "ConfigurationSetSerializer.h" #include #include #include using namespace AppInstaller::SQLite; using namespace AppInstaller::SQLite::Builder; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_1 { namespace { constexpr std::string_view s_UnitInfoTable_Table = "unit_info"sv; constexpr std::string_view s_UnitInfoTable_SetRowIdIndex = "unit_info_set_idx"sv; constexpr std::string_view s_UnitInfoTable_Column_SetRowId = "set_rowid"sv; constexpr std::string_view s_UnitInfoTable_Column_ParentRowId = "parent_rowid"sv; constexpr std::string_view s_UnitInfoTable_Column_InstanceIdentifier = "instance_identifier"sv; constexpr std::string_view s_UnitInfoTable_Column_Type = "type"sv; constexpr std::string_view s_UnitInfoTable_Column_Identifier = "identifier"sv; constexpr std::string_view s_UnitInfoTable_Column_Intent = "intent"sv; constexpr std::string_view s_UnitInfoTable_Column_Dependencies = "dependencies"sv; constexpr std::string_view s_UnitInfoTable_Column_Metadata = "metadata"sv; constexpr std::string_view s_UnitInfoTable_Column_Settings = "settings"sv; constexpr std::string_view s_UnitInfoTable_Column_IsActive = "is_active"sv; constexpr std::string_view s_UnitInfoTable_Column_IsGroup = "is_group"sv; } UnitInfoTable::UnitInfoTable(Connection& connection) : m_connection(connection) {} void UnitInfoTable::Create() { Savepoint savepoint = Savepoint::Create(m_connection, "UnitInfoTable_Create_0_1"); StatementBuilder tableBuilder; tableBuilder.CreateTable(s_UnitInfoTable_Table).Columns({ IntegerPrimaryKey(), ColumnBuilder(s_UnitInfoTable_Column_SetRowId, Type::RowId).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_ParentRowId, Type::RowId), ColumnBuilder(s_UnitInfoTable_Column_InstanceIdentifier, Type::Blob).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_Type, Type::Text).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_Identifier, Type::Text).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_Intent, Type::Int).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_Dependencies, Type::Text).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_Metadata, Type::Text).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_Settings, Type::Text).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_IsActive, Type::Bool).NotNull(), ColumnBuilder(s_UnitInfoTable_Column_IsGroup, Type::Bool).NotNull(), }); tableBuilder.Execute(m_connection); StatementBuilder indexBuilder; indexBuilder.CreateIndex(s_UnitInfoTable_SetRowIdIndex).On(s_UnitInfoTable_Table).Columns(s_UnitInfoTable_Column_SetRowId); indexBuilder.Execute(m_connection); savepoint.Commit(); } void UnitInfoTable::Add(const Configuration::ConfigurationUnit& configurationUnit, AppInstaller::SQLite::rowid_t setRowId, hstring schemaVersion) { Savepoint savepoint = Savepoint::Create(m_connection, "UnitInfoTable_Add_0_1"); StatementBuilder builder; builder.InsertInto(s_UnitInfoTable_Table).Columns({ s_UnitInfoTable_Column_SetRowId, s_UnitInfoTable_Column_ParentRowId, s_UnitInfoTable_Column_InstanceIdentifier, s_UnitInfoTable_Column_Type, s_UnitInfoTable_Column_Identifier, s_UnitInfoTable_Column_Intent, s_UnitInfoTable_Column_Dependencies, s_UnitInfoTable_Column_Metadata, s_UnitInfoTable_Column_Settings, s_UnitInfoTable_Column_IsActive, s_UnitInfoTable_Column_IsGroup, }).Values( Unbound, Unbound, Unbound, Unbound, Unbound, Unbound, Unbound, Unbound, Unbound, Unbound, Unbound ); Statement insertStatement = builder.Prepare(m_connection); struct UnitsToInsert { std::optional Parent; Configuration::ConfigurationUnit Unit; }; std::queue unitsToInsert; unitsToInsert.emplace(UnitsToInsert{ std::nullopt, configurationUnit }); auto serializer = ConfigurationSetSerializer::CreateSerializer(schemaVersion); while (!unitsToInsert.empty()) { const auto& current = unitsToInsert.front(); insertStatement.Reset(); bool isGroup = current.Unit.IsGroup(); insertStatement.Bind(1, setRowId); insertStatement.Bind(2, current.Parent); insertStatement.Bind(3, static_cast(current.Unit.InstanceIdentifier())); insertStatement.Bind(4, ConvertToUTF8(current.Unit.Type())); insertStatement.Bind(5, ConvertToUTF8(current.Unit.Identifier())); insertStatement.Bind(6, AppInstaller::ToIntegral(current.Unit.Intent())); insertStatement.Bind(7, serializer->SerializeStringArray(current.Unit.Dependencies())); insertStatement.Bind(8, serializer->SerializeMetadataWithEnvironment(current.Unit.Metadata(), current.Unit.Environment())); insertStatement.Bind(9, serializer->SerializeValueSet(current.Unit.Settings())); insertStatement.Bind(10, current.Unit.IsActive()); insertStatement.Bind(11, isGroup); insertStatement.Execute(); if (isGroup) { rowid_t currentRowId = m_connection.GetLastInsertRowID(); auto winrtUnits = current.Unit.Units(); std::vector units{ winrtUnits.Size() }; winrtUnits.GetMany(0, units); for (const auto& unit : units) { unitsToInsert.emplace(UnitsToInsert{ currentRowId, unit }); } } unitsToInsert.pop(); } savepoint.Commit(); } void UnitInfoTable::UpdateForSet(AppInstaller::SQLite::rowid_t target, const Windows::Foundation::Collections::IVector& winrtUnits, hstring schemaVersion) { Savepoint savepoint = Savepoint::Create(m_connection, "UnitInfoTable_UpdateForSet_0_1"); RemoveForSet(target); std::vector units{ winrtUnits.Size() }; winrtUnits.GetMany(0, units); for (const auto& unit : units) { Add(unit, target, schemaVersion); } savepoint.Commit(); } void UnitInfoTable::RemoveForSet(AppInstaller::SQLite::rowid_t target) { StatementBuilder builder; builder.DeleteFrom(s_UnitInfoTable_Table).Where(s_UnitInfoTable_Column_SetRowId).Equals(target); builder.Execute(m_connection); } std::vector UnitInfoTable::GetAllUnitsForSet(AppInstaller::SQLite::rowid_t setRowId, std::string_view schemaVersion) { StatementBuilder builder; builder.Select({ RowIDName, // 0 s_UnitInfoTable_Column_ParentRowId, // 1 s_UnitInfoTable_Column_InstanceIdentifier, // 2 s_UnitInfoTable_Column_Type, // 3 s_UnitInfoTable_Column_Identifier, // 4 s_UnitInfoTable_Column_Intent, // 5 s_UnitInfoTable_Column_Dependencies, // 6 s_UnitInfoTable_Column_Metadata, // 7 s_UnitInfoTable_Column_Settings, // 8 s_UnitInfoTable_Column_IsActive, // 9 s_UnitInfoTable_Column_IsGroup, // 10 }).From(s_UnitInfoTable_Table).Where(s_UnitInfoTable_Column_SetRowId).Equals(setRowId); Statement statement = builder.Prepare(m_connection); std::vector result; std::map rowToUnitMap; auto parser = ConfigurationSetParser::CreateForSchemaVersion(std::string{ schemaVersion }); while (statement.Step()) { auto unit = make_self(statement.GetColumn(2)); unit->Type(hstring{ ConvertToUTF16(statement.GetColumn(3)) }); unit->Identifier(hstring{ ConvertToUTF16(statement.GetColumn(4)) }); unit->Intent(statement.GetColumn(5)); unit->Dependencies(parser->ParseStringArray(statement.GetColumn(6))); unit->Metadata(parser->ParseValueSet(statement.GetColumn(7))); unit->Settings(parser->ParseValueSet(statement.GetColumn(8))); unit->IsActive(statement.GetColumn(9)); unit->IsGroup(statement.GetColumn(10)); parser->ExtractEnvironmentFromMetadata(unit->Metadata(), unit->EnvironmentInternal()); if (statement.GetColumnIsNull(1)) { result.emplace_back(unit); } else { rowToUnitMap.at(statement.GetColumn(1))->Units().Append(*unit); } rowToUnitMap.emplace(statement.GetColumn(0), unit); } return result; } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_1/UnitInfoTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" #include "Database/Schema/IConfigurationDatabase.h" #include namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_1 { struct UnitInfoTable { UnitInfoTable(AppInstaller::SQLite::Connection& connection); // Creates the unit info table. void Create(); // Adds the given configuration unit to the table. void Add(const Configuration::ConfigurationUnit& configurationUnit, AppInstaller::SQLite::rowid_t setRowId, hstring schemaVersion); // Updates the units for the target set. void UpdateForSet(AppInstaller::SQLite::rowid_t target, const Windows::Foundation::Collections::IVector& units, hstring schemaVersion); // Removes the units from the target set. void RemoveForSet(AppInstaller::SQLite::rowid_t target); // Gets all of the units for the given set. std::vector GetAllUnitsForSet(AppInstaller::SQLite::rowid_t setRowId, std::string_view schemaVersion); private: AppInstaller::SQLite::Connection& m_connection; }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_2/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Database/Schema/IConfigurationDatabase.h" #include "Database/Schema/0_1/Interface.h" namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_2 { struct Interface : public V0_1::Interface { using V0_1::Interface::Interface; const AppInstaller::SQLite::Version& GetSchemaVersion() override; // Version 0.1 void InitializeDatabase() override; // Version 0.2 bool MigrateFrom(IConfigurationDatabase* current) override; void AddQueueItem(const GUID& instanceIdentifier, const std::string& objectName) override; void SetActiveQueueItem(const std::string& objectName) override; std::vector> GetQueueItems() override; void RemoveQueueItem(const std::string& objectName) override; private: // Unconditionally attempts to migrate from the 0.1 base. void MigrateFrom0_1(); }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_2/Interface_0_2.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Interface.h" #include "QueueTable.h" #include using namespace AppInstaller::SQLite; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_2 { static constexpr AppInstaller::SQLite::Version s_InterfaceVersion{ 0, 2 }; const AppInstaller::SQLite::Version& Interface::GetSchemaVersion() { return s_InterfaceVersion; } void Interface::InitializeDatabase() { V0_1::Interface::InitializeDatabase(); Savepoint savepoint = Savepoint::Create(*m_storage, "InitializeDatabase_0_2"); MigrateFrom0_1(); savepoint.Commit(); } bool Interface::MigrateFrom(IConfigurationDatabase* current) { auto currentSchemaVersion = current->GetSchemaVersion(); if (currentSchemaVersion < s_InterfaceVersion) { if (V0_1::Interface::MigrateFrom(current)) { Savepoint savepoint = Savepoint::Create(*m_storage, "MigrateFrom0_1"); MigrateFrom0_1(); s_InterfaceVersion.SetSchemaVersion(*m_storage); savepoint.Commit(); return true; } } else if (currentSchemaVersion == s_InterfaceVersion) { return true; } return false; } void Interface::AddQueueItem(const GUID& instanceIdentifier, const std::string& objectName) { QueueTable queueTable(*m_storage); queueTable.AddQueueItemWithoutProcess(instanceIdentifier, objectName); } void Interface::SetActiveQueueItem(const std::string& objectName) { QueueTable queueTable(*m_storage); queueTable.SetActiveQueueItem(objectName); } std::vector> Interface::GetQueueItems() { QueueTable queueTable(*m_storage); return queueTable.GetQueueItemsWithoutProcess(); } void Interface::RemoveQueueItem(const std::string& objectName) { QueueTable queueTable(*m_storage); queueTable.RemoveQueueItem(objectName); } void Interface::MigrateFrom0_1() { QueueTable queueTable(*m_storage); queueTable.Create(); } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_2/QueueTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "QueueTable.h" #include #include using namespace AppInstaller::SQLite; using namespace AppInstaller::SQLite::Builder; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_2 { namespace { constexpr std::string_view s_QueueTable_Table = "queue"sv; constexpr std::string_view s_QueueTable_Column_SetInstanceIdentifier = "set_instance_identifier"sv; constexpr std::string_view s_QueueTable_Column_ObjectName = "object_name"sv; constexpr std::string_view s_QueueTable_Column_QueuedAt = "queued_at"sv; constexpr std::string_view s_QueueTable_Column_Active = "active"sv; constexpr std::string_view s_QueueTable_Column_Process = "process"sv; } QueueTable::QueueTable(Connection& connection) : m_connection(connection) {} void QueueTable::Create() { Savepoint savepoint = Savepoint::Create(m_connection, "QueueTable_Create_0_2"); StatementBuilder tableBuilder; tableBuilder.CreateTable(s_QueueTable_Table).Columns({ IntegerPrimaryKey(), ColumnBuilder(s_QueueTable_Column_SetInstanceIdentifier, Type::Blob).NotNull(), ColumnBuilder(s_QueueTable_Column_ObjectName, Type::Text).Unique().NotNull(), ColumnBuilder(s_QueueTable_Column_QueuedAt, Type::Int64).NotNull(), ColumnBuilder(s_QueueTable_Column_Active, Type::Bool).NotNull(), }); tableBuilder.Execute(m_connection); savepoint.Commit(); } void QueueTable::AddProcessColumn() { Savepoint savepoint = Savepoint::Create(m_connection, "QueueTable_AddProcessColumn_0_3"); StatementBuilder builder; builder.AlterTable(s_QueueTable_Table).Add(s_QueueTable_Column_Process, Type::Int64).NotNull().Default(0); builder.Execute(m_connection); savepoint.Commit(); } void QueueTable::AddQueueItemWithoutProcess(const GUID& instanceIdentifier, const std::string& objectName) { StatementBuilder builder; builder.InsertInto(s_QueueTable_Table).Columns({ s_QueueTable_Column_SetInstanceIdentifier, s_QueueTable_Column_ObjectName, s_QueueTable_Column_QueuedAt, s_QueueTable_Column_Active }).Values( instanceIdentifier, objectName, GetCurrentUnixEpoch(), false ); builder.Execute(m_connection); } void QueueTable::AddQueueItemWithProcess(const GUID& instanceIdentifier, const std::string& objectName) { StatementBuilder builder; builder.InsertInto(s_QueueTable_Table).Columns({ s_QueueTable_Column_SetInstanceIdentifier, s_QueueTable_Column_ObjectName, s_QueueTable_Column_QueuedAt, s_QueueTable_Column_Active, s_QueueTable_Column_Process }).Values( instanceIdentifier, objectName, GetCurrentUnixEpoch(), false, static_cast(GetCurrentProcessId()) ); builder.Execute(m_connection); } void QueueTable::SetActiveQueueItem(const std::string& objectName) { StatementBuilder builder; builder.Update(s_QueueTable_Table).Set().Column(s_QueueTable_Column_Active).Equals(true).Where(s_QueueTable_Column_ObjectName).Equals(objectName); builder.Execute(m_connection); } std::vector> QueueTable::GetQueueItemsWithoutProcess() { StatementBuilder builder; builder.Select({ s_QueueTable_Column_SetInstanceIdentifier, s_QueueTable_Column_ObjectName, s_QueueTable_Column_QueuedAt, s_QueueTable_Column_Active }).From(s_QueueTable_Table).OrderBy({ s_QueueTable_Column_QueuedAt, RowIDName }); Statement statement = builder.Prepare(m_connection); std::vector> result; while (statement.Step()) { result.emplace_back(std::make_tuple(statement.GetColumn(0), statement.GetColumn(1), ConvertUnixEpochToSystemClock(statement.GetColumn(2)), 0, statement.GetColumn(3))); } return result; } std::vector> QueueTable::GetQueueItemsWithProcess() { StatementBuilder builder; builder.Select({ s_QueueTable_Column_SetInstanceIdentifier, s_QueueTable_Column_ObjectName, s_QueueTable_Column_QueuedAt, s_QueueTable_Column_Active, s_QueueTable_Column_Process }).From(s_QueueTable_Table).OrderBy({ s_QueueTable_Column_QueuedAt, RowIDName }); Statement statement = builder.Prepare(m_connection); std::vector> result; while (statement.Step()) { result.emplace_back(std::make_tuple(statement.GetColumn(0), statement.GetColumn(1), ConvertUnixEpochToSystemClock(statement.GetColumn(2)), static_cast(statement.GetColumn(4)), statement.GetColumn(3))); } return result; } void QueueTable::RemoveQueueItem(const std::string& objectName) { StatementBuilder builder; builder.DeleteFrom(s_QueueTable_Table).Where(s_QueueTable_Column_ObjectName).Equals(objectName); builder.Execute(m_connection); } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_2/QueueTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_2 { struct QueueTable { QueueTable(AppInstaller::SQLite::Connection& connection); // Creates the queue table. void Create(); // Adds the process column to the table. void AddProcessColumn(); // Adds a new queue item for the given configuration set and object name. void AddQueueItemWithoutProcess(const GUID& instanceIdentifier, const std::string& objectName); // Adds a new queue item for the given configuration set and object name. void AddQueueItemWithProcess(const GUID& instanceIdentifier, const std::string& objectName); // Sets the queue item with the given object name as active. void SetActiveQueueItem(const std::string& objectName); // Gets all queue items in queue order (item at index 0 is active/next). std::vector> GetQueueItemsWithoutProcess(); // Gets all queue items in queue order (item at index 0 is active/next). std::vector> GetQueueItemsWithProcess(); // Removes the queue item with the given object name. void RemoveQueueItem(const std::string& objectName); private: AppInstaller::SQLite::Connection& m_connection; }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_3/ChangeListenerTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ChangeListenerTable.h" #include #include using namespace AppInstaller::SQLite; using namespace AppInstaller::SQLite::Builder; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_3 { namespace { constexpr std::string_view s_ChangeListenerTable_Table = "change_listeners"sv; constexpr std::string_view s_ChangeListenerTable_Column_ObjectName = "object_name"sv; constexpr std::string_view s_ChangeListenerTable_Column_StartedAt = "started_at"sv; constexpr std::string_view s_ChangeListenerTable_Column_Process = "process"sv; } ChangeListenerTable::ChangeListenerTable(Connection& connection) : m_connection(connection) {} void ChangeListenerTable::Create() { Savepoint savepoint = Savepoint::Create(m_connection, "ChangeListenerTable_Create_0_3"); StatementBuilder tableBuilder; tableBuilder.CreateTable(s_ChangeListenerTable_Table).Columns({ IntegerPrimaryKey(), ColumnBuilder(s_ChangeListenerTable_Column_ObjectName, Type::Text).Unique().NotNull(), ColumnBuilder(s_ChangeListenerTable_Column_StartedAt, Type::Int64).NotNull(), ColumnBuilder(s_ChangeListenerTable_Column_Process, Type::Int64).NotNull(), }); tableBuilder.Execute(m_connection); savepoint.Commit(); } void ChangeListenerTable::AddChangeListener(const std::string& objectName) { StatementBuilder builder; builder.InsertInto(s_ChangeListenerTable_Table).Columns({ s_ChangeListenerTable_Column_ObjectName, s_ChangeListenerTable_Column_StartedAt, s_ChangeListenerTable_Column_Process }).Values( objectName, GetCurrentUnixEpoch(), static_cast(GetCurrentProcessId()) ); builder.Execute(m_connection); } void ChangeListenerTable::RemoveChangeListener(const std::string& objectName) { StatementBuilder builder; builder.DeleteFrom(s_ChangeListenerTable_Table).Where(s_ChangeListenerTable_Column_ObjectName).Equals(objectName); builder.Execute(m_connection); } std::vector> ChangeListenerTable::GetChangeListeners() { StatementBuilder builder; builder.Select({ s_ChangeListenerTable_Column_ObjectName, s_ChangeListenerTable_Column_StartedAt, s_ChangeListenerTable_Column_Process }).From(s_ChangeListenerTable_Table); Statement statement = builder.Prepare(m_connection); std::vector> result; while (statement.Step()) { result.emplace_back(std::make_tuple(statement.GetColumn(0), ConvertUnixEpochToSystemClock(statement.GetColumn(1)), static_cast(statement.GetColumn(2)))); } return result; } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_3/ChangeListenerTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_3 { struct ChangeListenerTable { ChangeListenerTable(AppInstaller::SQLite::Connection& connection); // Creates the table. void Create(); // Adds a new change listener to the table. void AddChangeListener(const std::string& objectName); // Removes the change listener with the given name from the table. void RemoveChangeListener(const std::string& objectName); // Gets all change listeners. std::vector> GetChangeListeners(); private: AppInstaller::SQLite::Connection& m_connection; }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_3/Interface.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Database/Schema/IConfigurationDatabase.h" #include "Database/Schema/0_2/Interface.h" namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_3 { struct Interface : public V0_2::Interface { using V0_2::Interface::Interface; const AppInstaller::SQLite::Version& GetSchemaVersion() override; // Version 0.1 void InitializeDatabase() override; void RemoveSet(AppInstaller::SQLite::rowid_t target) override; // Version 0.2 bool MigrateFrom(IConfigurationDatabase* current) override; void AddQueueItem(const GUID& instanceIdentifier, const std::string& objectName) override; std::vector> GetQueueItems() override; // Version 0.3 std::vector GetStatusSince(int64_t changeIdentifier) override; std::tuple> GetStatusBaseline() override; void AddListener(const std::string& objectName) override; void RemoveListener(const std::string& objectName) override; std::vector> GetChangeListeners() override; void UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state) override; void UpdateSetInQueue(const guid& setInstanceIdentifier, bool inQueue) override; void UpdateUnitState(const guid& setInstanceIdentifier, const ConfigurationSetChangeDataPtr& changeData) override; ConfigurationSetState GetSetState(const guid& instanceIdentifier) override; std::chrono::system_clock::time_point GetSetFirstApply(const guid& instanceIdentifier) override; std::chrono::system_clock::time_point GetSetApplyBegun(const guid& instanceIdentifier) override; std::chrono::system_clock::time_point GetSetApplyEnded(const guid& instanceIdentifier) override; ConfigurationUnitState GetUnitState(const guid& instanceIdentifier) override; std::optional> GetUnitResultInformation(const guid& instanceIdentifier) override; private: // Unconditionally attempts to migrate from the 0.2 base. void MigrateFrom0_2(); }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_3/Interface_0_3.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Interface.h" #include "Database/Schema/0_1/SetInfoTable.h" #include "Database/Schema/0_2/QueueTable.h" #include "StatusItemTable.h" #include "ChangeListenerTable.h" #include using namespace AppInstaller::SQLite; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_3 { static constexpr AppInstaller::SQLite::Version s_InterfaceVersion{ 0, 3 }; const AppInstaller::SQLite::Version& Interface::GetSchemaVersion() { return s_InterfaceVersion; } void Interface::InitializeDatabase() { V0_2::Interface::InitializeDatabase(); Savepoint savepoint = Savepoint::Create(*m_storage, "InitializeDatabase_0_3"); MigrateFrom0_2(); savepoint.Commit(); } void Interface::RemoveSet(AppInstaller::SQLite::rowid_t target) { Savepoint savepoint = Savepoint::Create(*m_storage, "RemoveSet_0_3"); V0_2::Interface::RemoveSet(target); StatusItemTable statusItemTable(*m_storage); statusItemTable.RemoveForSet(target); savepoint.Commit(); } bool Interface::MigrateFrom(IConfigurationDatabase* current) { auto currentSchemaVersion = current->GetSchemaVersion(); if (currentSchemaVersion < s_InterfaceVersion) { if (V0_2::Interface::MigrateFrom(current)) { Savepoint savepoint = Savepoint::Create(*m_storage, "MigrateFrom0_2"); MigrateFrom0_2(); s_InterfaceVersion.SetSchemaVersion(*m_storage); savepoint.Commit(); return true; } } else if (currentSchemaVersion == s_InterfaceVersion) { return true; } return false; } void Interface::AddQueueItem(const GUID& instanceIdentifier, const std::string& objectName) { V0_2::QueueTable queueTable(*m_storage); queueTable.AddQueueItemWithProcess(instanceIdentifier, objectName); } std::vector> Interface::GetQueueItems() { V0_2::QueueTable queueTable(*m_storage); return queueTable.GetQueueItemsWithProcess(); } void Interface::MigrateFrom0_2() { V0_2::QueueTable queueTable(*m_storage); queueTable.AddProcessColumn(); ChangeListenerTable changeListenerTable(*m_storage); changeListenerTable.Create(); StatusItemTable statusItemTable(*m_storage); statusItemTable.Create(); } std::vector Interface::GetStatusSince(int64_t changeIdentifier) { StatusItemTable statusItemTable(*m_storage); return statusItemTable.GetStatusSince(changeIdentifier); } std::tuple> Interface::GetStatusBaseline() { StatusItemTable statusItemTable(*m_storage); return statusItemTable.GetStatusBaseline(); } void Interface::AddListener(const std::string& objectName) { ChangeListenerTable changeListenerTable(*m_storage); changeListenerTable.AddChangeListener(objectName); } void Interface::RemoveListener(const std::string& objectName) { ChangeListenerTable changeListenerTable(*m_storage); changeListenerTable.RemoveChangeListener(objectName); } std::vector> Interface::GetChangeListeners() { ChangeListenerTable changeListenerTable(*m_storage); return changeListenerTable.GetChangeListeners(); } void Interface::UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state) { StatusItemTable statusItemTable(*m_storage); statusItemTable.UpdateSetState(setInstanceIdentifier, state); } void Interface::UpdateSetInQueue(const guid& setInstanceIdentifier, bool inQueue) { StatusItemTable statusItemTable(*m_storage); statusItemTable.UpdateSetInQueue(setInstanceIdentifier, inQueue); } void Interface::UpdateUnitState(const guid& setInstanceIdentifier, const ConfigurationSetChangeDataPtr& changeData) { StatusItemTable statusItemTable(*m_storage); statusItemTable.UpdateUnitState(setInstanceIdentifier, changeData); } ConfigurationSetState Interface::GetSetState(const guid& instanceIdentifier) { StatusItemTable statusItemTable(*m_storage); return statusItemTable.GetSetState(instanceIdentifier); } std::chrono::system_clock::time_point Interface::GetSetFirstApply(const guid& instanceIdentifier) { V0_1::SetInfoTable setInfoTable(*m_storage); return setInfoTable.GetSetFirstApply(instanceIdentifier); } std::chrono::system_clock::time_point Interface::GetSetApplyBegun(const guid& instanceIdentifier) { StatusItemTable statusItemTable(*m_storage); return statusItemTable.GetSetApplyBegun(instanceIdentifier); } std::chrono::system_clock::time_point Interface::GetSetApplyEnded(const guid& instanceIdentifier) { StatusItemTable statusItemTable(*m_storage); return statusItemTable.GetSetApplyEnded(instanceIdentifier); } ConfigurationUnitState Interface::GetUnitState(const guid& instanceIdentifier) { StatusItemTable statusItemTable(*m_storage); return statusItemTable.GetUnitState(instanceIdentifier); } std::optional> Interface::GetUnitResultInformation(const guid& instanceIdentifier) { StatusItemTable statusItemTable(*m_storage); return statusItemTable.GetUnitResultInformation(instanceIdentifier); } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_3/StatusItemTable.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "StatusItemTable.h" #include "Database/Schema/0_1/SetInfoTable.h" #include #include #include #include using namespace AppInstaller::SQLite; using namespace AppInstaller::SQLite::Builder; using namespace AppInstaller::Utility; namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_3 { namespace { constexpr std::string_view s_StatusItemTable_Table = "status_items"sv; constexpr std::string_view s_StatusItemTable_ChangeIdentifierIndex = "status_items_change_idx"sv; constexpr std::string_view s_StatusItemTable_SetRowIdIndex = "status_items_set_idx"sv; constexpr std::string_view s_StatusItemTable_UnitInstanceIndex = "status_items_unit_idx"sv; constexpr std::string_view s_StatusItemTable_Column_ChangeIdentifier = "change_identifier"sv; constexpr std::string_view s_StatusItemTable_Column_ChangeTimeInitial = "change_time_initial"sv; constexpr std::string_view s_StatusItemTable_Column_ChangeTimeLatest = "change_time_latest"sv; constexpr std::string_view s_StatusItemTable_Column_SetRowId = "set_rowid"sv; constexpr std::string_view s_StatusItemTable_Column_InQueue = "in_queue"sv; constexpr std::string_view s_StatusItemTable_Column_UnitInstanceIdentifier = "unit_instance_identifier"sv; constexpr std::string_view s_StatusItemTable_Column_State = "state"sv; constexpr std::string_view s_StatusItemTable_Column_ResultCode = "result_code"sv; constexpr std::string_view s_StatusItemTable_Column_ResultDescription = "result_description"sv; constexpr std::string_view s_StatusItemTable_Column_ResultDetails = "result_details"sv; constexpr std::string_view s_StatusItemTable_Column_ResultSource = "result_source"sv; void BuildBaseStatusSelectStatement(StatementBuilder& builder) { builder.Select({ s_StatusItemTable_Column_ChangeIdentifier, // 0 s_StatusItemTable_Column_ChangeTimeLatest, // 1 V0_1::SetInfoTable::InstanceIdentifierColumn(), // 2 s_StatusItemTable_Column_InQueue, // 3 s_StatusItemTable_Column_UnitInstanceIdentifier, // 4 s_StatusItemTable_Column_State, // 5 s_StatusItemTable_Column_ResultCode, // 6 s_StatusItemTable_Column_ResultDescription, // 7 s_StatusItemTable_Column_ResultDetails, // 8 s_StatusItemTable_Column_ResultSource, // 9 }).From(s_StatusItemTable_Table).LeftOuterJoin(V0_1::SetInfoTable::TableName()).On(QualifiedColumn{ s_StatusItemTable_Table, s_StatusItemTable_Column_SetRowId }, QualifiedColumn{ V0_1::SetInfoTable::TableName(), RowIDName }); } IConfigurationDatabase::StatusItemTuple GetTupleFromStatement(Statement& statement) { return std::make_tuple( statement.GetColumn(0), ConvertUnixEpochToSystemClock(statement.GetColumn(1)), statement.GetColumn(2), statement.GetColumn(3), statement.GetColumnIsNull(4) ? std::nullopt : std::make_optional(statement.GetColumn(4)), statement.GetColumn(5), statement.GetColumnIsNull(6) ? std::nullopt : std::make_optional(statement.GetColumn(6)), statement.GetColumnIsNull(7) ? std::string{} : statement.GetColumn(7), statement.GetColumnIsNull(8) ? std::string{} : statement.GetColumn(8), statement.GetColumnIsNull(9) ? ConfigurationUnitResultSource::None : statement.GetColumn(9) ); } int64_t GetLatestChangeIdentifier(Connection& connection) { StatementBuilder getLatestChangeBuilder; getLatestChangeBuilder.Select().Column(Aggregate::Max, s_StatusItemTable_Column_ChangeIdentifier).From(s_StatusItemTable_Table); Statement getLatestChange = getLatestChangeBuilder.Prepare(connection); return (getLatestChange.Step() ? getLatestChange.GetColumn(0) : 0); } int64_t GetNextChangeIdentifier(Connection& connection) { return GetLatestChangeIdentifier(connection) + 1; } void UpdateStatus( Connection& connection, const GUID& setInstanceIdentifier, const std::optional& state, const std::optional& inQueue, const std::optional& unitInstanceIdentifier = std::nullopt, const std::optional& resultCode = std::nullopt, const std::optional& resultDescription = std::nullopt, const std::optional& resultDetails = std::nullopt, const std::optional& resultSource = std::nullopt) { static constexpr std::string_view s_alias = "sub_expression"; int64_t changeIdentifier = GetNextChangeIdentifier(connection); int64_t changeTime = GetCurrentUnixEpoch(); // Statement like: // Update status_items set state = 1 from (Select rowid from set_info where instance_identifier = "foo") as sub where status_items.set_rowid = sub.rowid StatementBuilder updateBuilder; updateBuilder.Update(s_StatusItemTable_Table).Set(); if (state) { updateBuilder.Column(s_StatusItemTable_Column_State).Equals(state.value()); } if (inQueue) { updateBuilder.Column(s_StatusItemTable_Column_InQueue).Equals(inQueue.value()); } if (resultCode) { updateBuilder.Column(s_StatusItemTable_Column_ResultCode).Equals(resultCode.value()); } if (resultDescription) { updateBuilder.Column(s_StatusItemTable_Column_ResultDescription).Equals(resultDescription.value()); } if (resultDetails) { updateBuilder.Column(s_StatusItemTable_Column_ResultDetails).Equals(resultDetails.value()); } if (resultSource) { updateBuilder.Column(s_StatusItemTable_Column_ResultSource).Equals(resultSource.value()); } updateBuilder. Column(s_StatusItemTable_Column_ChangeIdentifier).Equals(changeIdentifier). Column(s_StatusItemTable_Column_ChangeTimeLatest).Equals(changeTime). From().BeginParenthetical(). Select(RowIDName).From(V0_1::SetInfoTable::TableName()).Where(V0_1::SetInfoTable::InstanceIdentifierColumn()).Equals(setInstanceIdentifier). EndParenthetical().As(s_alias).Where(QualifiedColumn{ s_StatusItemTable_Table, s_StatusItemTable_Column_SetRowId }).Equals(QualifiedColumn{ s_alias, RowIDName }). And(QualifiedColumn{ s_StatusItemTable_Table, s_StatusItemTable_Column_UnitInstanceIdentifier }).Equals(unitInstanceIdentifier); updateBuilder.Execute(connection); if (connection.GetChanges() == 0) { // No change; we need to insert the status row StatementBuilder insertBuilder; insertBuilder.InsertInto(s_StatusItemTable_Table).Columns({ s_StatusItemTable_Column_ChangeIdentifier, s_StatusItemTable_Column_ChangeTimeInitial, s_StatusItemTable_Column_ChangeTimeLatest, s_StatusItemTable_Column_SetRowId, s_StatusItemTable_Column_InQueue, s_StatusItemTable_Column_UnitInstanceIdentifier, s_StatusItemTable_Column_State, s_StatusItemTable_Column_ResultCode, s_StatusItemTable_Column_ResultDescription, s_StatusItemTable_Column_ResultDetails, s_StatusItemTable_Column_ResultSource, }).Select(). Value(changeIdentifier). Value(changeTime). Value(changeTime). Column(QualifiedColumn{ V0_1::SetInfoTable::TableName(), RowIDName }). Value(inQueue.value_or(false)). Value(unitInstanceIdentifier). Value(state.value_or(0)). Value(resultCode). Value(resultDescription). Value(resultDetails). Value(resultSource). From(V0_1::SetInfoTable::TableName()).Where(QualifiedColumn{ V0_1::SetInfoTable::TableName(), V0_1::SetInfoTable::InstanceIdentifierColumn() }).Equals(setInstanceIdentifier); insertBuilder.Execute(connection); } } Statement PrepareSelectStatusValues(Connection& connection, const std::optional& setInstanceIdentifier, const std::optional& unitInstanceIdentifier, std::initializer_list columns) { THROW_HR_IF(E_INVALIDARG, (setInstanceIdentifier && unitInstanceIdentifier) || (!setInstanceIdentifier && !unitInstanceIdentifier)); StatementBuilder builder; builder.Select(columns).From(s_StatusItemTable_Table); if (setInstanceIdentifier) { builder.Join(V0_1::SetInfoTable::TableName()).On(QualifiedColumn{ s_StatusItemTable_Table, s_StatusItemTable_Column_SetRowId }, QualifiedColumn{ V0_1::SetInfoTable::TableName(), RowIDName }). Where(QualifiedColumn{ V0_1::SetInfoTable::TableName(), V0_1::SetInfoTable::InstanceIdentifierColumn() }).Equals(setInstanceIdentifier). And(s_StatusItemTable_Column_UnitInstanceIdentifier).IsNull(); } else { builder.Where(s_StatusItemTable_Column_UnitInstanceIdentifier).Equals(unitInstanceIdentifier.value()); } return builder.Prepare(connection); } } StatusItemTable::StatusItemTable(Connection& connection) : m_connection(connection) {} void StatusItemTable::Create() { Savepoint savepoint = Savepoint::Create(m_connection, "StatusItemTable_Create_0_3"); StatementBuilder tableBuilder; tableBuilder.CreateTable(s_StatusItemTable_Table).Columns({ IntegerPrimaryKey(), ColumnBuilder(s_StatusItemTable_Column_ChangeIdentifier, Type::Int64).NotNull(), ColumnBuilder(s_StatusItemTable_Column_ChangeTimeInitial, Type::Int64).NotNull(), ColumnBuilder(s_StatusItemTable_Column_ChangeTimeLatest, Type::Int64).NotNull(), ColumnBuilder(s_StatusItemTable_Column_SetRowId, Type::RowId).NotNull(), ColumnBuilder(s_StatusItemTable_Column_InQueue, Type::Bool).NotNull(), ColumnBuilder(s_StatusItemTable_Column_UnitInstanceIdentifier, Type::Blob), ColumnBuilder(s_StatusItemTable_Column_State, Type::Int).NotNull(), ColumnBuilder(s_StatusItemTable_Column_ResultCode, Type::Int), ColumnBuilder(s_StatusItemTable_Column_ResultDescription, Type::Text), ColumnBuilder(s_StatusItemTable_Column_ResultDetails, Type::Text), ColumnBuilder(s_StatusItemTable_Column_ResultSource, Type::Int), }); tableBuilder.Execute(m_connection); { StatementBuilder indexBuilder; indexBuilder.CreateIndex(s_StatusItemTable_ChangeIdentifierIndex).On(s_StatusItemTable_Table).Columns(s_StatusItemTable_Column_ChangeIdentifier); indexBuilder.Execute(m_connection); } { StatementBuilder indexBuilder; indexBuilder.CreateIndex(s_StatusItemTable_SetRowIdIndex).On(s_StatusItemTable_Table).Columns(s_StatusItemTable_Column_SetRowId); indexBuilder.Execute(m_connection); } { StatementBuilder indexBuilder; indexBuilder.CreateUniqueIndex(s_StatusItemTable_UnitInstanceIndex).On(s_StatusItemTable_Table).Columns(s_StatusItemTable_Column_UnitInstanceIdentifier); indexBuilder.Execute(m_connection); } savepoint.Commit(); } void StatusItemTable::RemoveForSet(AppInstaller::SQLite::rowid_t target) { StatementBuilder builder; builder.DeleteFrom(s_StatusItemTable_Table).Where(s_StatusItemTable_Column_SetRowId).Equals(target); builder.Execute(m_connection); } std::vector StatusItemTable::GetStatusSince(int64_t changeIdentifier) { StatementBuilder builder; BuildBaseStatusSelectStatement(builder); builder.Where(s_StatusItemTable_Column_ChangeIdentifier).IsGreaterThan(changeIdentifier).OrderBy(s_StatusItemTable_Column_ChangeIdentifier); Statement statement = builder.Prepare(m_connection); std::vector result; while (statement.Step()) { result.emplace_back(GetTupleFromStatement(statement)); } return result; } std::tuple> StatusItemTable::GetStatusBaseline() { int64_t latestChange = GetLatestChangeIdentifier(m_connection); std::vector setStatus; StatementBuilder builder; BuildBaseStatusSelectStatement(builder); builder.Where(s_StatusItemTable_Column_UnitInstanceIdentifier).IsNull(); Statement statement = builder.Prepare(m_connection); while (statement.Step()) { setStatus.emplace_back(GetTupleFromStatement(statement)); } return std::make_tuple(latestChange, std::move(setStatus)); } void StatusItemTable::UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state) { UpdateStatus(m_connection, setInstanceIdentifier, AppInstaller::ToIntegral(state), std::nullopt); } void StatusItemTable::UpdateSetInQueue(const guid& setInstanceIdentifier, bool inQueue) { UpdateStatus(m_connection, setInstanceIdentifier, std::nullopt, inQueue); } void StatusItemTable::UpdateUnitState(const guid& setInstanceIdentifier, const IConfigurationDatabase::ConfigurationSetChangeDataPtr& changeData) { const auto& resultInformation = changeData->ResultInformation(); std::optional resultCode; std::optional resultDescription; std::optional resultDetails; std::optional resultSource; if (resultInformation) { resultCode = resultInformation.ResultCode(); resultDescription = ConvertToUTF8(resultInformation.Description()); resultDetails = ConvertToUTF8(resultInformation.Details()); resultSource = resultInformation.ResultSource(); } UpdateStatus(m_connection, setInstanceIdentifier, AppInstaller::ToIntegral(changeData->UnitState()), std::nullopt, changeData->Unit().InstanceIdentifier(), resultCode, resultDescription, resultDetails, resultSource); } ConfigurationSetState StatusItemTable::GetSetState(const guid& instanceIdentifier) { Statement statement = PrepareSelectStatusValues(m_connection, instanceIdentifier, std::nullopt, { s_StatusItemTable_Column_State }); return (statement.Step() ? statement.GetColumn(0) : ConfigurationSetState::Unknown); } std::chrono::system_clock::time_point StatusItemTable::GetSetApplyBegun(const GUID& instanceIdentifier) { Statement statement = PrepareSelectStatusValues(m_connection, instanceIdentifier, std::nullopt, { s_StatusItemTable_Column_ChangeTimeInitial }); return (statement.Step() ? ConvertUnixEpochToSystemClock(statement.GetColumn(0)) : std::chrono::system_clock::time_point{}); } std::chrono::system_clock::time_point StatusItemTable::GetSetApplyEnded(const GUID& instanceIdentifier) { Statement statement = PrepareSelectStatusValues(m_connection, instanceIdentifier, std::nullopt, { s_StatusItemTable_Column_ChangeTimeLatest, s_StatusItemTable_Column_InQueue }); // Only return the end time if no longer in the queue if (statement.Step() && !statement.GetColumn(1)) { return ConvertUnixEpochToSystemClock(statement.GetColumn(0)); } return std::chrono::system_clock::time_point{}; } ConfigurationUnitState StatusItemTable::GetUnitState(const guid& instanceIdentifier) { Statement statement = PrepareSelectStatusValues(m_connection, std::nullopt, instanceIdentifier, { s_StatusItemTable_Column_State }); return (statement.Step() ? statement.GetColumn(0) : ConfigurationUnitState::Unknown); } std::optional> StatusItemTable::GetUnitResultInformation(const guid& instanceIdentifier) { Statement statement = PrepareSelectStatusValues(m_connection, std::nullopt, instanceIdentifier, { s_StatusItemTable_Column_ResultCode, s_StatusItemTable_Column_ResultDescription, s_StatusItemTable_Column_ResultDetails, s_StatusItemTable_Column_ResultSource }); if (statement.Step() && !statement.GetColumnIsNull(0)) { return statement.GetRow(); } return std::nullopt; } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/0_3/StatusItemTable.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Database/Schema/IConfigurationDatabase.h" #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_3 { struct StatusItemTable { StatusItemTable(AppInstaller::SQLite::Connection& connection); // Creates the table. void Create(); // Removes the status for the target set. void RemoveForSet(AppInstaller::SQLite::rowid_t target); // Gets all status items that have changed since the given change identifier, in order of changes (last item is the new latest change to pass next). std::vector GetStatusSince(int64_t changeIdentifier); // Gets the latest change identifier and the set status items. std::tuple> GetStatusBaseline(); // Updates a set's state. void UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state); // Updates whether a set is in the queue or not. void UpdateSetInQueue(const guid& setInstanceIdentifier, bool inQueue); // Updates a unit's state. void UpdateUnitState(const guid& setInstanceIdentifier, const IConfigurationDatabase::ConfigurationSetChangeDataPtr& changeData); // Gets a set's state. ConfigurationSetState GetSetState(const guid& instanceIdentifier); // Gets a set's latest apply begin time. std::chrono::system_clock::time_point GetSetApplyBegun(const GUID& instanceIdentifier); // Gets a set's latest apply end time. std::chrono::system_clock::time_point GetSetApplyEnded(const GUID& instanceIdentifier); // Gets a unit's state. ConfigurationUnitState GetUnitState(const guid& instanceIdentifier); // Gets a unit's latest result information. std::optional> GetUnitResultInformation(const guid& instanceIdentifier); private: AppInstaller::SQLite::Connection& m_connection; }; } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/IConfigurationDatabase.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Database/Schema/IConfigurationDatabase.h" #include "Database/Schema/0_1/Interface.h" #include "Database/Schema/0_2/Interface.h" #include "Database/Schema/0_3/Interface.h" namespace winrt::Microsoft::Management::Configuration::implementation { namespace { std::unique_ptr CreateForVersion(const AppInstaller::SQLite::Version& version, const std::shared_ptr& storage) { using StorageT = std::shared_ptr; if (version.MajorVersion == 0) { constexpr std::array(*)(const StorageT& s), 3> versionCreatorMap = { [](const StorageT& s) { return std::unique_ptr(std::make_unique(s)); }, [](const StorageT& s) { return std::unique_ptr(std::make_unique(s)); }, [](const StorageT& s) { return std::unique_ptr(std::make_unique(s)); }, }; size_t minorVersion = static_cast(version.MinorVersion); if (minorVersion >= 1 && minorVersion <= versionCreatorMap.size()) { return versionCreatorMap[minorVersion - 1](storage); } } // We do not have the capacity to operate on this schema version THROW_WIN32(ERROR_NOT_SUPPORTED); } } AppInstaller::SQLite::Version IConfigurationDatabase::GetLatestVersion() { return { 0, 3 }; } std::unique_ptr IConfigurationDatabase::CreateFor(const std::shared_ptr& storage, bool allowMigration) { using StorageT = std::shared_ptr; const AppInstaller::SQLite::Version& version = storage->GetVersion(); std::unique_ptr result = CreateForVersion(version, storage); AppInstaller::SQLite::Version latestVersion = GetLatestVersion(); if (allowMigration && version < latestVersion) { // Always migrate to the latest version until a reason comes along to not do that std::unique_ptr latest = CreateForVersion(latestVersion, storage); THROW_WIN32_IF(ERROR_NOT_SUPPORTED, !latest->MigrateFrom(result.get())); result = std::move(latest); } return result; } void IConfigurationDatabase::AddQueueItem(const GUID&, const std::string&) { } void IConfigurationDatabase::SetActiveQueueItem(const std::string&) { } std::vector> IConfigurationDatabase::GetQueueItems() { return {}; } void IConfigurationDatabase::RemoveQueueItem(const std::string&) { } std::vector IConfigurationDatabase::GetStatusSince(int64_t) { return {}; } std::tuple> IConfigurationDatabase::GetStatusBaseline() { return { 0, {} }; } void IConfigurationDatabase::AddListener(const std::string&) { } void IConfigurationDatabase::RemoveListener(const std::string&) { } std::vector> IConfigurationDatabase::GetChangeListeners() { return {}; } void IConfigurationDatabase::UpdateSetState(const guid&, ConfigurationSetState) { } void IConfigurationDatabase::UpdateSetInQueue(const guid&, bool) { } void IConfigurationDatabase::UpdateUnitState(const guid&, const ConfigurationSetChangeDataPtr&) { } ConfigurationSetState IConfigurationDatabase::GetSetState(const guid&) { return ConfigurationSetState::Unknown; } std::chrono::system_clock::time_point IConfigurationDatabase::GetSetFirstApply(const guid&) { return {}; } std::chrono::system_clock::time_point IConfigurationDatabase::GetSetApplyBegun(const guid&) { return {}; } std::chrono::system_clock::time_point IConfigurationDatabase::GetSetApplyEnded(const guid&) { return {}; } ConfigurationUnitState IConfigurationDatabase::GetUnitState(const guid&) { return ConfigurationUnitState::Unknown; } std::optional> IConfigurationDatabase::GetUnitResultInformation(const guid&) { return std::nullopt; } } ================================================ FILE: src/Microsoft.Management.Configuration/Database/Schema/IConfigurationDatabase.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" #include "ConfigurationSet.h" #include "ConfigurationUnit.h" #include "ConfigurationSetChangeData.h" #include #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Interface for interacting with the configuration database. struct IConfigurationDatabase { using ConfigurationSetPtr = winrt::com_ptr; using ConfigurationUnitPtr = winrt::com_ptr; using ConfigurationSetChangeDataPtr = winrt::com_ptr; virtual ~IConfigurationDatabase() = default; // Gets the latest schema version for the configuration database. static AppInstaller::SQLite::Version GetLatestVersion(); // Creates the version appropriate database object for the given storage. static std::unique_ptr CreateFor(const std::shared_ptr& storage, bool allowMigration = false); // Gets the schema version from the current interface. virtual const AppInstaller::SQLite::Version& GetSchemaVersion() = 0; // Version 0.1 // Acts on a database that has been created but contains no tables beyond metadata. virtual void InitializeDatabase() = 0; // Adds the given set to the database. virtual void AddSet(const Configuration::ConfigurationSet& configurationSet) = 0; // Updates the set with the given row id using the given set. virtual void UpdateSet(AppInstaller::SQLite::rowid_t target, const Configuration::ConfigurationSet& configurationSet) = 0; // Removes the set with the given row id from the database. virtual void RemoveSet(AppInstaller::SQLite::rowid_t target) = 0; // Gets all of the sets in the database. virtual std::vector GetSets() = 0; // Gets the row id of the set with the given instance identifier, if present. virtual std::optional GetSetRowId(const GUID& instanceIdentifier) = 0; // Version 0.2 // Migrates from the current interface given. // Returns true if supported (or is already same schema version); false if not. // Throws on errors that occur during an attempted migration. virtual bool MigrateFrom(IConfigurationDatabase* current) = 0; // Adds a new queue item for the given configuration set and object name. virtual void AddQueueItem(const GUID& instanceIdentifier, const std::string& objectName); // Sets the queue item with the given object name as active. virtual void SetActiveQueueItem(const std::string& objectName); // Gets all queue items in queue order (item at index 0 is active/next). virtual std::vector> GetQueueItems(); // Removes the queue item with the given object name. virtual void RemoveQueueItem(const std::string& objectName); // Version 0.3 // Gets a set from the database. virtual ConfigurationSetPtr GetSet(const GUID& instanceIdentifier) = 0; // The tuple returned for status items. using StatusItemTuple = std::tuple< int64_t, std::chrono::system_clock::time_point, GUID, bool, std::optional, int32_t, std::optional, std::string, std::string, ConfigurationUnitResultSource>; // Gets all status items that have changed since the given change identifier, in order of changes (last item is the new latest change to pass next). virtual std::vector GetStatusSince(int64_t changeIdentifier); // Gets the latest change identifier and the set status items. virtual std::tuple> GetStatusBaseline(); // Adds a new listener to the database. virtual void AddListener(const std::string& objectName); // Removes a listener from the database. virtual void RemoveListener(const std::string& objectName); // Gets all of the change listeners in the database. virtual std::vector> GetChangeListeners(); // Updates a set's state. virtual void UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state); // Updates whether a set is in the queue or not. virtual void UpdateSetInQueue(const guid& setInstanceIdentifier, bool inQueue); // Updates a unit's state. virtual void UpdateUnitState(const guid& setInstanceIdentifier, const ConfigurationSetChangeDataPtr& changeData); // Gets a set's state. virtual ConfigurationSetState GetSetState(const guid& instanceIdentifier); // Gets a set's first apply time. virtual std::chrono::system_clock::time_point GetSetFirstApply(const guid& instanceIdentifier); // Gets a set's latest apply begin time. virtual std::chrono::system_clock::time_point GetSetApplyBegun(const guid& instanceIdentifier); // Gets a set's latest apply end time. virtual std::chrono::system_clock::time_point GetSetApplyEnded(const guid& instanceIdentifier); // Gets a unit's state. virtual ConfigurationUnitState GetUnitState(const guid& instanceIdentifier); // Gets a unit's latest result information. virtual std::optional> GetUnitResultInformation(const guid& instanceIdentifier); }; } ================================================ FILE: src/Microsoft.Management.Configuration/DefaultSetGroupProcessor.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DefaultSetGroupProcessor.h" #include "ConfigurationSetApplyProcessor.h" #include "ExceptionResultHelpers.h" #include "TestGroupSettingsResult.h" #include "TestSettingsResult.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { namespace { // Determines whether a configuration unit should be tested. bool ShouldTestDuringTest(const Configuration::ConfigurationUnit& unit) { return unit.IsActive() && unit.Intent() != ConfigurationUnitIntent::Inform; } } void DefaultSetGroupProcessor::Initialize(const ConfigurationSet& set, const IConfigurationSetProcessor& setProcessor, ConfigThreadGlobals& threadGlobals, bool consistencyCheckOnly) { m_set = set; m_setProcessor = setProcessor; m_threadGlobals = &threadGlobals; m_consistencyCheckOnly = consistencyCheckOnly; } Windows::Foundation::IInspectable DefaultSetGroupProcessor::Group() { return m_set; } Windows::Foundation::IAsyncOperation DefaultSetGroupProcessor::TestGroupSettingsAsync(Windows::Foundation::EventHandler progressHandler) { auto strongThis = get_strong(); co_await resume_background(); auto cancellation = co_await get_cancellation_token(); cancellation.enable_propagation(); auto result = make_self>(); result->Group(m_set); try { for (const auto& unit : m_set.Units()) { ThrowIf(cancellation()); AICLI_LOG_DIRECT(m_threadGlobals->GetDiagnosticLogger(), Config, Info, << "Testing configuration unit: `" << AppInstaller::Utility::ConvertToUTF8(unit.Identifier()) << "` [" << AppInstaller::Utility::ConvertToUTF8(unit.Type()) << ']'); ITestSettingsResult settingsResult; ConfigurationTestResult testResult = ConfigurationTestResult::Unknown; auto unitResult = make_self>(); if (ShouldTestDuringTest(unit)) { IConfigurationUnitProcessor unitProcessor; try { unitProcessor = m_setProcessor.CreateUnitProcessor(unit); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } // Check again as creating the unit processor could take time ThrowIf(cancellation()); if (unitProcessor) { IConfigurationGroupProcessor groupProcessor = unitProcessor.try_as(); if (groupProcessor) { auto testOperation = groupProcessor.TestGroupSettingsAsync([&](const auto&, const ITestSettingsResult& unitResult) { progressHandler(nullptr, unitResult); }); ITestGroupSettingsResult groupResult = co_await testOperation; // Put all of the group's unit results in our unit results for (const auto& groupUnitResult : groupResult.UnitResults()) { result->AppendUnitResult(groupUnitResult); } // Convert group result into a unit result for the group auto testSettingsResult = make_self>(); testSettingsResult->Unit(unit); testSettingsResult->TestResult(groupResult.TestResult()); testSettingsResult->ResultInformation(groupResult.ResultInformation()); settingsResult = *testSettingsResult; } else { try { settingsResult = unitProcessor.TestSettings(); } catch (...) { ExtractUnitResultInformation(std::current_exception(), unitResult); } } } } else { testResult = ConfigurationTestResult::NotRun; } if (FAILED(unitResult->ResultCode())) { testResult = ConfigurationTestResult::Failed; } // Check if we need to construct our own result object if (!settingsResult) { auto testSettingsResult = make_self>(); testSettingsResult->Unit(unit); testSettingsResult->TestResult(testResult); testSettingsResult->ResultInformation(*unitResult); settingsResult = *testSettingsResult; } result->AppendUnitResult(settingsResult); try { progressHandler(nullptr, settingsResult); } CATCH_LOG(); } co_return *result; } catch (...) { ExtractUnitResultInformation(std::current_exception(), result->ResultInformationInternal()); throw; } } Windows::Foundation::IAsyncOperation DefaultSetGroupProcessor::ApplyGroupSettingsAsync(Windows::Foundation::EventHandler progressHandler) { auto strongThis = get_strong(); co_await resume_background(); ConfigurationSetApplyProcessor applyProcessor{ m_set, m_setProcessor, { std::move(progressHandler), co_await winrt::get_cancellation_token() } }; applyProcessor.Process(m_consistencyCheckOnly); co_return applyProcessor.Result(); } void DefaultSetGroupProcessor::ThrowIf(bool cancellation) { if (cancellation) { AICLI_LOG_DIRECT(m_threadGlobals->GetDiagnosticLogger(), Config, Warning, << "Operation cancelled"); throw winrt::hresult_canceled(); } } } ================================================ FILE: src/Microsoft.Management.Configuration/DefaultSetGroupProcessor.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" #include "ConfigThreadGlobals.h" namespace winrt::Microsoft::Management::Configuration::implementation { // Implements the default group processing for configuration sets when the set processor doesn't handle it. struct DefaultSetGroupProcessor : winrt::implements { using ConfigurationSet = Configuration::ConfigurationSet; DefaultSetGroupProcessor() = default; void Initialize(const ConfigurationSet& set, const IConfigurationSetProcessor& setProcessor, ConfigThreadGlobals& threadGlobals, bool consistencyCheckOnly = false); IInspectable Group(); Windows::Foundation::IAsyncOperation TestGroupSettingsAsync(Windows::Foundation::EventHandler progressHandler); Windows::Foundation::IAsyncOperation ApplyGroupSettingsAsync(Windows::Foundation::EventHandler progressHandler); private: void ThrowIf(bool cancellation); ConfigurationSet m_set = nullptr; IConfigurationSetProcessor m_setProcessor; ConfigThreadGlobals* m_threadGlobals = nullptr; bool m_consistencyCheckOnly = false; }; } ================================================ FILE: src/Microsoft.Management.Configuration/DiagnosticInformationInstance.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DiagnosticInformationInstance.h" namespace winrt::Microsoft::Management::Configuration::implementation { void DiagnosticInformationInstance::Initialize(DiagnosticLevel level, std::wstring_view message) { m_level = level; m_message = message; } DiagnosticLevel DiagnosticInformationInstance::Level() { return m_level; } void DiagnosticInformationInstance::Level(DiagnosticLevel value) { m_level = value; } hstring DiagnosticInformationInstance::Message() { return m_message; } void DiagnosticInformationInstance::Message(const hstring& value) { m_message = value; } } ================================================ FILE: src/Microsoft.Management.Configuration/DiagnosticInformationInstance.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct DiagnosticInformationInstance : winrt::implements { DiagnosticInformationInstance() = default; void Initialize(DiagnosticLevel level, std::wstring_view message); DiagnosticLevel Level(); void Level(DiagnosticLevel value); hstring Message(); void Message(const hstring& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: DiagnosticLevel m_level = DiagnosticLevel::Verbose; hstring m_message; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ExceptionResultHelpers.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { template void ExtractUnitResultInformation(std::exception_ptr exc, const UnitResult& unitResult) { try { std::rethrow_exception(exc); } catch (const winrt::hresult_error& hre) { unitResult->Initialize(hre.code(), hre.message()); } catch (const std::exception& ex) { unitResult->Initialize(E_FAIL, AppInstaller::Utility::ConvertToUTF16(ex.what())); } catch (...) { unitResult->Initialize(E_FAIL, hstring{}); } AICLI_LOG(Config, Error, << "Unit Processor exception: " << AppInstaller::Logging::SetHRFormat << unitResult->ResultCode() << " [" << AppInstaller::Utility::ConvertToUTF8(unitResult->Description()) << "]"); } } ================================================ FILE: src/Microsoft.Management.Configuration/Filesystem.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Filesystem.h" using namespace std::string_view_literals; namespace winrt::Microsoft::Management::Configuration::implementation { namespace anon { constexpr std::string_view s_Configuration_LocalState = "Configuration"sv; } AppInstaller::Filesystem::PathDetails GetPathDetailsFor(PathName path, bool forDisplay) { AppInstaller::Filesystem::PathDetails result; // We should not create directories by default when they are retrieved for display purposes. result.Create = !forDisplay; switch (path) { case PathName::LocalState: result = GetPathDetailsFor(AppInstaller::Filesystem::PathName::UnpackagedLocalStateRoot, forDisplay); result.Path /= anon::s_Configuration_LocalState; break; default: THROW_HR(E_UNEXPECTED); } return result; } } ================================================ FILE: src/Microsoft.Management.Configuration/Filesystem.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include namespace winrt::Microsoft::Management::Configuration::implementation { // Paths used by configuration. enum class PathName { // Local state root for configuration. LocalState, }; // Gets the PathDetails used for the given path. // This is exposed primarily to allow for testing, GetPathTo should be preferred. AppInstaller::Filesystem::PathDetails GetPathDetailsFor(PathName path, bool forDisplay = false); } ================================================ FILE: src/Microsoft.Management.Configuration/FindUnitProcessorsOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "FindUnitProcessorsOptions.h" #include "FindUnitProcessorsOptions.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { hstring FindUnitProcessorsOptions::SearchPaths() const { return m_searchPaths; } void FindUnitProcessorsOptions::SearchPaths(hstring const& value) { m_searchPaths = value; } bool FindUnitProcessorsOptions::SearchPathsExclusive() const { return m_searchPathsExclusive; } void FindUnitProcessorsOptions::SearchPathsExclusive(bool value) { m_searchPathsExclusive = value; } Microsoft::Management::Configuration::ConfigurationUnitDetailFlags FindUnitProcessorsOptions::UnitDetailFlags() const { return m_detailFlags; } void FindUnitProcessorsOptions::UnitDetailFlags(Microsoft::Management::Configuration::ConfigurationUnitDetailFlags value) { m_detailFlags = value; } HRESULT STDMETHODCALLTYPE FindUnitProcessorsOptions::SetLifetimeWatcher(IUnknown* watcher) { return AppInstaller::WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher); } } ================================================ FILE: src/Microsoft.Management.Configuration/FindUnitProcessorsOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "FindUnitProcessorsOptions.g.h" #include #include namespace winrt::Microsoft::Management::Configuration::implementation { struct FindUnitProcessorsOptions : FindUnitProcessorsOptionsT>, AppInstaller::WinRT::LifetimeWatcherBase { FindUnitProcessorsOptions() = default; hstring SearchPaths() const; void SearchPaths(hstring const& value); bool SearchPathsExclusive() const; void SearchPathsExclusive(bool value); Microsoft::Management::Configuration::ConfigurationUnitDetailFlags UnitDetailFlags() const; void UnitDetailFlags(Microsoft::Management::Configuration::ConfigurationUnitDetailFlags value); HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: hstring m_searchPaths; bool m_searchPathsExclusive = false; Microsoft::Management::Configuration::ConfigurationUnitDetailFlags m_detailFlags = Microsoft::Management::Configuration::ConfigurationUnitDetailFlags::None; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Configuration::factory_implementation { struct FindUnitProcessorsOptions : FindUnitProcessorsOptionsT { }; } #endif ================================================ FILE: src/Microsoft.Management.Configuration/GetAllConfigurationUnitSettingsResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "GetAllConfigurationUnitSettingsResult.h" #include "ConfigurationUnitResultInformation.h" namespace winrt::Microsoft::Management::Configuration::implementation { GetAllConfigurationUnitSettingsResult::GetAllConfigurationUnitSettingsResult() : m_resultInformation(*make_self>()) { } void GetAllConfigurationUnitSettingsResult::ResultInformation(const IConfigurationUnitResultInformation& resultInformation) { m_resultInformation = resultInformation; } IConfigurationUnitResultInformation GetAllConfigurationUnitSettingsResult::ResultInformation() const { return m_resultInformation; } Windows::Foundation::Collections::IVector GetAllConfigurationUnitSettingsResult::Settings() { return m_settings; } void GetAllConfigurationUnitSettingsResult::Settings(Windows::Foundation::Collections::IVector&& value) { m_settings = std::move(value); } } ================================================ FILE: src/Microsoft.Management.Configuration/GetAllConfigurationUnitSettingsResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "GetAllConfigurationUnitSettingsResult.g.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct GetAllConfigurationUnitSettingsResult : GetAllConfigurationUnitSettingsResultT { GetAllConfigurationUnitSettingsResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void ResultInformation(const IConfigurationUnitResultInformation& resultInformation); void Settings(Windows::Foundation::Collections::IVector&& value); #endif IConfigurationUnitResultInformation ResultInformation() const; Windows::Foundation::Collections::IVector Settings(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: IConfigurationUnitResultInformation m_resultInformation; Windows::Foundation::Collections::IVector m_settings; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/GetAllConfigurationUnitsResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "GetAllConfigurationUnitsResult.h" #include "ConfigurationUnitResultInformation.h" namespace winrt::Microsoft::Management::Configuration::implementation { GetAllConfigurationUnitsResult::GetAllConfigurationUnitsResult() : m_resultInformation(*make_self>()) { } void GetAllConfigurationUnitsResult::ResultInformation(const IConfigurationUnitResultInformation& resultInformation) { m_resultInformation = resultInformation; } IConfigurationUnitResultInformation GetAllConfigurationUnitsResult::ResultInformation() const { return m_resultInformation; } Windows::Foundation::Collections::IVector GetAllConfigurationUnitsResult::Units() { return m_units; } void GetAllConfigurationUnitsResult::Units(Windows::Foundation::Collections::IVector&& value) { m_units = std::move(value); } } ================================================ FILE: src/Microsoft.Management.Configuration/GetAllConfigurationUnitsResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "GetAllConfigurationUnitsResult.g.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct GetAllConfigurationUnitsResult : GetAllConfigurationUnitsResultT { GetAllConfigurationUnitsResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void ResultInformation(const IConfigurationUnitResultInformation& resultInformation); void Units(Windows::Foundation::Collections::IVector&& value); #endif IConfigurationUnitResultInformation ResultInformation() const; Windows::Foundation::Collections::IVector Units(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: IConfigurationUnitResultInformation m_resultInformation; Windows::Foundation::Collections::IVector m_units; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/GetConfigurationSetDetailsResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "GetConfigurationSetDetailsResult.h" #include "GetConfigurationSetDetailsResult.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { GetConfigurationSetDetailsResult::GetConfigurationSetDetailsResult() : m_unitResults(multi_threaded_vector()) {}; const Windows::Foundation::Collections::IVector& GetConfigurationSetDetailsResult::UnitResultsVector() { return m_unitResults; } Windows::Foundation::Collections::IVectorView GetConfigurationSetDetailsResult::UnitResults() { return m_unitResults.GetView(); } } ================================================ FILE: src/Microsoft.Management.Configuration/GetConfigurationSetDetailsResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "GetConfigurationSetDetailsResult.g.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct GetConfigurationSetDetailsResult : GetConfigurationSetDetailsResultT { using GetConfigurationUnitDetailsResult = Configuration::GetConfigurationUnitDetailsResult; GetConfigurationSetDetailsResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) const Windows::Foundation::Collections::IVector& UnitResultsVector(); #endif Windows::Foundation::Collections::IVectorView UnitResults(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: Windows::Foundation::Collections::IVector m_unitResults; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/GetConfigurationUnitDetailsResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "GetConfigurationUnitDetailsResult.h" #include "GetConfigurationUnitDetailsResult.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { void GetConfigurationUnitDetailsResult::Unit(ConfigurationUnit value) { m_unit = std::move(value); } void GetConfigurationUnitDetailsResult::Details(IConfigurationUnitProcessorDetails value) { m_details = std::move(value); } void GetConfigurationUnitDetailsResult::ResultInformation(IConfigurationUnitResultInformation value) { m_resultInformation = std::move(value); } ConfigurationUnit GetConfigurationUnitDetailsResult::Unit() { return m_unit; } IConfigurationUnitProcessorDetails GetConfigurationUnitDetailsResult::Details() { return m_details; } IConfigurationUnitResultInformation GetConfigurationUnitDetailsResult::ResultInformation() { return m_resultInformation; } } ================================================ FILE: src/Microsoft.Management.Configuration/GetConfigurationUnitDetailsResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "GetConfigurationUnitDetailsResult.g.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct GetConfigurationUnitDetailsResult : GetConfigurationUnitDetailsResultT { using ConfigurationUnit = Configuration::ConfigurationUnit; GetConfigurationUnitDetailsResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Unit(ConfigurationUnit value); void Details(IConfigurationUnitProcessorDetails value); void ResultInformation(IConfigurationUnitResultInformation value); #endif ConfigurationUnit Unit(); IConfigurationUnitProcessorDetails Details(); IConfigurationUnitResultInformation ResultInformation(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ConfigurationUnit m_unit = nullptr; IConfigurationUnitProcessorDetails m_details; IConfigurationUnitResultInformation m_resultInformation; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/GetConfigurationUnitSettingsResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "GetConfigurationUnitSettingsResult.h" #include "GetConfigurationUnitSettingsResult.g.cpp" #include "ConfigurationUnitResultInformation.h" namespace winrt::Microsoft::Management::Configuration::implementation { GetConfigurationUnitSettingsResult::GetConfigurationUnitSettingsResult() : m_resultInformation(*make_self>()) { } void GetConfigurationUnitSettingsResult::ResultInformation(const IConfigurationUnitResultInformation& resultInformation) { m_resultInformation = resultInformation; } IConfigurationUnitResultInformation GetConfigurationUnitSettingsResult::ResultInformation() const { return m_resultInformation; } Windows::Foundation::Collections::ValueSet GetConfigurationUnitSettingsResult::Settings() { return m_settings; } void GetConfigurationUnitSettingsResult::Settings(Windows::Foundation::Collections::ValueSet&& value) { m_settings = std::move(value); } } ================================================ FILE: src/Microsoft.Management.Configuration/GetConfigurationUnitSettingsResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "GetConfigurationUnitSettingsResult.g.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct GetConfigurationUnitSettingsResult : GetConfigurationUnitSettingsResultT { GetConfigurationUnitSettingsResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void ResultInformation(const IConfigurationUnitResultInformation& resultInformation); void Settings(Windows::Foundation::Collections::ValueSet&& value); #endif IConfigurationUnitResultInformation ResultInformation() const; Windows::Foundation::Collections::ValueSet Settings(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: IConfigurationUnitResultInformation m_resultInformation; Windows::Foundation::Collections::ValueSet m_settings; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Configuration { [contractversion(4)] apicontract Contract{}; // The current state of a configuration set. [contract(Microsoft.Management.Configuration.Contract, 1)] enum ConfigurationSetState { // The state of the configuration set is unknown. Unknown, // The configuration set is in the queue to be applied. Pending, // The configuration set is actively being applied. InProgress, // The configuration set has completed being applied. Completed, }; // The current state of a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 1)] enum ConfigurationUnitState { // The state of the configuration unit is unknown. Unknown, // The configuration unit is in the queue to be applied. Pending, // The configuration unit is actively being applied. InProgress, // The configuration unit has completed being applied. Completed, // The configuration unit was not applied due to external factors. Skipped, }; // Defines the type of detail probing that is allowed about a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 1)] [flags] enum ConfigurationUnitDetailFlags { // For completeness. None = 0, // Only reads details from local data. Local = 0x1, // Can query the catalog information for details, but will not download any modules. Catalog = 0x2, // Can only read data, not write or execute. ReadOnly = Local | Catalog, // Can download modules, but not load them. Download = 0x4, // Can load modules for details. Load = 0x8, }; // The source of a result; for instance, the part of the system that generated a failure. [contract(Microsoft.Management.Configuration.Contract, 1)] enum ConfigurationUnitResultSource { // The source is not known, or more likely, there was no failure. None, // The result came from inside the configuration system; this is likely a bug. Internal, // The configuration set was ill formed. For instance, referencing a configuration unit // that does not exist or a dependency that is not present. ConfigurationSet, // The external module that processes the configuration unit generated the result. UnitProcessing, // The system state is causing the error. SystemState, // The configuration unit was not run due to a precondition not being met. // For example, if a dependency fails to be applied, this will be set. Precondition, }; // A security context; typically used to define the context in which a configuration unit should be processed. [contract(Microsoft.Management.Configuration.Contract, 3)] enum SecurityContext { // The default value; indicates that the configuration unit should be processed at the caller's current level. Current, // A standard user without administrator privileges. Restricted, // A user with administrator privileges. Elevated, }; // Information about the environment in which a configuration is processed. [contract(Microsoft.Management.Configuration.Contract, 3)] runtimeclass ConfigurationEnvironment { // The security context in which a configuration is processed. SecurityContext Context; // A string that identifies the processor that should be used for the configuration. String ProcessorIdentifier; // Processor specific properties. Windows.Foundation.Collections.IMap ProcessorProperties{ get; }; }; // Information on a result for a single unit of configuration. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IConfigurationUnitResultInformation { // The error code of the failure. HRESULT ResultCode{ get; }; // The short description of the failure. String Description{ get; }; // A more detailed error message appropriate for diagnosing the root cause of an error. String Details{ get; }; // The source of the result. ConfigurationUnitResultSource ResultSource{ get; }; } // Provides information for a specific configuration unit setting. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IConfigurationUnitSettingDetails { // The name of the setting. String Identifier{ get; }; // A brief description of the setting. String Title{ get; }; // The detailed description of the setting. String Description{ get; }; // Whether the setting is a key. This is used to determine if different settings are in conflict. Boolean IsKey{ get; }; // Whether a non-empty value for the setting is required. Boolean IsRequired{ get; }; // Whether the setting should be serialized in order to be applied on another system. // When the current settings are retrieved from the system, this can be used to exclude settings that are not relevant to a future application of the unit of configuration. Boolean IsInformational{ get; }; // The data type for the value of this setting. Windows.Foundation.PropertyType Type{ get; }; // The schema fragment to be used for this setting. // Using a single entry of the `properties` object from a JSON schema is suggested, for instance: // { // "$schema": "https://json-schema.org/draft/2020-12/schema", // "scope": { // "title": "Target scope", // "description": "The scope at which the configuration should be applied.", // "type": "string", // "enum": [ // "machine", // "user" // ] // } // } String Schema{ get; }; } // Provides information for a specific configuration unit within the runtime. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IConfigurationUnitProcessorDetails { // The type of configuration unit. String UnitType{ get; }; // A description of the unit of configuration. String UnitDescription{ get; }; // The URI of the documentation for the unit of configuration. Windows.Foundation.Uri UnitDocumentationUri{ get; }; // The URI of the icon for the unit of configuration. Windows.Foundation.Uri UnitIconUri{ get; }; // The name of the module containing the unit of configuration. String ModuleName{ get; }; // The type of the module containing the unit of configuration. String ModuleType{ get; }; // The source of the module containing the unit of configuration. String ModuleSource{ get; }; // The description of the module containing the unit of configuration. String ModuleDescription{ get; }; // The URI of the documentation for the module containing the unit of configuration. Windows.Foundation.Uri ModuleDocumentationUri{ get; }; // The URI for the published module containing the unit of configuration. Windows.Foundation.Uri PublishedModuleUri{ get; }; // The version of the module containing the unit of configuration. String Version{ get; }; // The publishing date of the module containing the unit of configuration. Windows.Foundation.DateTime PublishedDate{ get; }; // Whether the module is already present on the system. Boolean IsLocal{ get; }; // The author of the module containing the unit of configuration. String Author{ get; }; // The publisher of the module containing the unit of configuration. String Publisher{ get; }; // The signing information of the module files containing the unit of configuration. // May contain Windows.Security.Cryptography.Certificates.Certificate or Windows.Security.Cryptography.Certificates.CertificateChain. Windows.Foundation.Collections.IVectorView SigningInformation{ get; }; // The settings information for the unit of configuration. Windows.Foundation.Collections.IVectorView Settings{ get; }; // Does it comes from a public repository Boolean IsPublic{ get; }; } // Provides information for a specific configuration unit within the runtime. [contract(Microsoft.Management.Configuration.Contract, 2)] interface IConfigurationUnitProcessorDetails2 requires IConfigurationUnitProcessorDetails { // Determines if this configuration unit should be treated as a group. Boolean IsGroup{ get; }; } // Provides information for a specific configuration unit within the runtime. [contract(Microsoft.Management.Configuration.Contract, 4)] interface IConfigurationUnitProcessorDetails3 requires IConfigurationUnitProcessorDetails2 { // The path of the resource. String Path{ get; }; } // Defines how the configuration unit is to be used within the configuration system. [contract(Microsoft.Management.Configuration.Contract, 1)] enum ConfigurationUnitIntent { // The configuration unit will only be used to Test the current system state. Assert, // The configuration unit will only be used to Get the current system state. Inform, // The configuration unit will be used to Apply the current system state. // The configuration unit will be used to Test and Get the current system state as part of that process. Apply, // The configuration unit's intent is unknown. // Currently not supported. Unknown, }; // A single unit of configuration. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ConfigurationUnit { // Creates an empty configuration unit for authoring purposes. ConfigurationUnit(); // The type of the unit being configured; not a name for this instance. String Type; // An identifier used to uniquely identify the instance of a configuration unit on the system. // May change upon applying a configuration unit from a historical context if some part of the set changed. Guid InstanceIdentifier{ get; }; // The identifier name of this instance within the set. String Identifier; // Describes how this configuration unit will be used. ConfigurationUnitIntent Intent; // The `Identifier` values of the configuration units that this unit depends on. Windows.Foundation.Collections.IVector Dependencies; // The metadata properties associated with the configuration unit. // TODO: This is being used to drive processing decisions when it should not. // There is ongoing discussion about this at https://github.com/PowerShell/DSC/issues/47 and https://github.com/PowerShell/DSC/issues/92. Windows.Foundation.Collections.ValueSet Metadata; // Contains the values that are for use by the configuration unit itself. Windows.Foundation.Collections.ValueSet Settings; // Contains information on the origin of the configuration unit. // May be null if ConfigurationProcessor.GetDetailsAsync has not been called yet. IConfigurationUnitProcessorDetails Details{ get; }; // The current state of the configuration unit. ConfigurationUnitState State{ get; }; // Contains information on the result of the latest attempt to apply the configuration unit. IConfigurationUnitResultInformation ResultInformation{ get; }; // Controls whether this unit will be processed when part of a set. Boolean IsActive; // Creates a copy of this configuration unit, with the following notes: // InstanceIdentifier will be a new value // Identifier will be empty // Dependencies, Metadata, and Settings will by new containers with identical values inside // Details will be the same value (not a copy, just another reference) // State, ResultInformation, and ShouldApply will be their default constructed state // IsGroup will be false and child units will not be copied // Environment will be copied ConfigurationUnit Copy(); [contract(Microsoft.Management.Configuration.Contract, 2)] { // Determines if this configuration unit should be treated as a group. // A configuration unit group treats its `Settings` as the definition of child units. Boolean IsGroup; // The configuration units that are part of this unit (if IsGroup is true). Windows.Foundation.Collections.IVector Units; } [contract(Microsoft.Management.Configuration.Contract, 3)] { // The environment in which to process the configuration unit. // This defines the initial processing environment state used by the configuration system, // and may be overridden by the processor later. ConfigurationEnvironment Environment{ get; }; } } // The change event type that has occurred for a configuration set change. [contract(Microsoft.Management.Configuration.Contract, 1)] enum ConfigurationSetChangeEventType { Unknown, // The change event was for the set state. Only ConfigurationSetChangeData.SetState is valid. SetStateChanged, // The change event was for the unit state. All ConfigurationSetChangeData properties are valid. UnitStateChanged, }; // The change data sent about changes to a specific set. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ConfigurationSetChangeData { // The change event type that occurred. ConfigurationSetChangeEventType Change{ get; }; // The state of the configuration set for this event (the ConfigurationSet can be used to get the current state, which may be different). ConfigurationSetState SetState{ get; }; // The state of the configuration unit for this event (the ConfigurationUnit can be used to get the current state, which may be different). ConfigurationUnitState UnitState{ get; }; // Contains information on the result of the attempt to apply the configuration unit. IConfigurationUnitResultInformation ResultInformation{ get; }; // The configuration unit whose state changed. ConfigurationUnit Unit{ get; }; } // The definition of a configuration parameter; a value that may be provided to alter the processing of a configuration set. [contract(Microsoft.Management.Configuration.Contract, 2)] runtimeclass ConfigurationParameter { ConfigurationParameter(); // The name of the parameter. String Name; // The description of the parameter. String Description; // The metadata properties associated with the configuration parameter. Windows.Foundation.Collections.ValueSet Metadata; // The value of the parameter should be treated as a secret; not logged our output. Boolean IsSecure; // The type of the parameter. Windows.Foundation.PropertyType Type; // The default value; may be null if no default is provided. Object DefaultValue; // The set of allowed values; a null container indicates that the values are not restricted. Windows.Foundation.Collections.IVector AllowedValues; // The minimum length for a parameter type with the concept (string, array, etc.). UInt32 MinimumLength; // The maximum length for a parameter type with the concept (string, array, etc.). UInt32 MaximumLength; // For comparable parameter types, the minimum value allowed (integrals, DateTime, etc.). Object MinimumValue; // For comparable parameter types, the maximum value allowed (integrals, DateTime, etc.). Object MaximumValue; // The input value; may be null if no value is provided. Object ProvidedValue; } // A configuration set contains a collection of configuration units and details about the set. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ConfigurationSet { // Creates an empty configuration set for authoring purposes. ConfigurationSet(); // The name of the set; if from a file this could be the file name. String Name; // The origin of the set; if it came from a repository it could be the remote URL (ex. https://github.com/microsoft/winget-cli.git). String Origin; // The location of the configuration set on the local filesystem. // If this set is from history, the file may no longer exist or it's contents may have been changed. String Path; // An identifier used to uniquely identify the instance of a configuration set on the system. // May change upon applying a configuration set from a historical context if some part of the set changed. Guid InstanceIdentifier{ get; }; // The state that the set is in. ConfigurationSetState State{ get; }; // The time that this set was recorded with intent to apply. Windows.Foundation.DateTime FirstApply{ get; }; // The time that this set was last started to be applied. Windows.Foundation.DateTime ApplyBegun{ get; }; // The time that this set was last finished being applied (does not indicate success). Windows.Foundation.DateTime ApplyEnded{ get; }; // The configuration units that are part of this set. Windows.Foundation.Collections.IVector Units; // The schema version to use for the set. // Will be set to the schema version when read in, and default to the latest if created manually. // Setting SchemaVersion to a different value will change SchemaUri. String SchemaVersion; // Only changes for this set are sent to this event. // This includes things like: start/stop of the entire set for application, start/stop of a unit for application. event Windows.Foundation.TypedEventHandler ConfigurationSetChange; // Writes the configuration set to the given stream. void Serialize(Windows.Storage.Streams.IOutputStream stream); // Removes the configuration set from the recorded history, if present. void Remove(); [contract(Microsoft.Management.Configuration.Contract, 2)] { // The metadata properties associated with the configuration set. Windows.Foundation.Collections.ValueSet Metadata; // The parameters that this configuration set supports. Windows.Foundation.Collections.IVector Parameters; // The variables that this configuration set uses. Windows.Foundation.Collections.ValueSet Variables; // The schema URI to use for the set. // Will be set to the schema version when read in, and default to the latest if created manually. // Setting SchemaUri to a different value will change SchemaVersion. Windows.Foundation.Uri SchemaUri; } [contract(Microsoft.Management.Configuration.Contract, 3)] { // The environment in which to process the configuration set. // This defines the initial processing environment state used by the configuration system, // and may be overridden by the processor later. ConfigurationEnvironment Environment{ get; }; // Gets the union of environments as defined by all of the active units within the set. Windows.Foundation.Collections.IVector GetUnitEnvironments(); } } // The result of applying the settings with an IConfigurationUnitProcessor. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IApplySettingsResult { // The configuration unit. ConfigurationUnit Unit{ get; }; // Indicates whether a reboot is required after the settings were applied. Boolean RebootRequired{ get; }; // The result of applying the configuration unit. IConfigurationUnitResultInformation ResultInformation{ get; }; } // Informs the caller of the result of running a Test. [contract(Microsoft.Management.Configuration.Contract, 1)] enum ConfigurationTestResult { // The result is unknown. Unknown, // The system is in the state described by the configuration. Positive, // The system is not in the state described by the configuration. Negative, // Running the test failed. Failed, // The test was not run because it was not applicable. NotRun, }; // The result of testing the settings with an IConfigurationUnitProcessor. [contract(Microsoft.Management.Configuration.Contract, 1)] interface ITestSettingsResult { // The configuration unit. ConfigurationUnit Unit{ get; }; // The result (if any) of running Test on the configuration unit. ConfigurationTestResult TestResult{ get; }; // The result of testing the configuration unit. // This is not the response for the test, but rather contains information about the actual attempt to run the test. IConfigurationUnitResultInformation ResultInformation{ get; }; } // The result of getting the settings with an IConfigurationUnitProcessor. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IGetSettingsResult { // The configuration unit. ConfigurationUnit Unit{ get; }; // The current state of the system for the configuration unit. Windows.Foundation.Collections.ValueSet Settings{ get; }; // The result of getting the configuration unit settings. // This is not the response for the retrieval, but rather contains information about the actual attempt to retrieve the settings. IConfigurationUnitResultInformation ResultInformation{ get; }; } // The result of getting the settings for all instances of a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 2)] interface IGetAllSettingsResult { // The configuration unit. ConfigurationUnit Unit{ get; }; // The current state of the system for all the instances of the configuration unit. Windows.Foundation.Collections.IVector Settings{ get; }; // The result of getting the configuration unit settings. // This is not the response for the retrieval, but rather contains information about the actual attempt to retrieve the settings. IConfigurationUnitResultInformation ResultInformation{ get; }; } // The result of getting all of the units for a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 4)] interface IGetAllUnitsResult { // The configuration unit. ConfigurationUnit Unit{ get; }; // The units retrieved for the given unit. Windows.Foundation.Collections.IVector Units{ get; }; // The result of getting the configuration units. // This is not the response for the retrieval, but rather contains information about the actual attempt to retrieve the settings. IConfigurationUnitResultInformation ResultInformation{ get; }; } // Provides access to a specific configuration unit within the runtime. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IConfigurationUnitProcessor { // The configuration unit that the processor was created for. ConfigurationUnit Unit{ get; }; // Determines if the system is already in the state described by the configuration unit. ITestSettingsResult TestSettings(); // Gets the current system state for the configuration unit. IGetSettingsResult GetSettings(); // Applies the state described in the configuration unit. IApplySettingsResult ApplySettings(); } [contract(Microsoft.Management.Configuration.Contract, 2)] interface IGetAllSettingsConfigurationUnitProcessor requires IConfigurationUnitProcessor { // Gets the current system state for all the instances of the configuration unit, each of which has their own settings. IGetAllSettingsResult GetAllSettings(); } [contract(Microsoft.Management.Configuration.Contract, 4)] interface IGetAllUnitsConfigurationUnitProcessor requires IConfigurationUnitProcessor { // Gets all units for the configuration unit. IGetAllUnitsResult GetAllUnits(); } // Controls the lifetime of operations for a single configuration set. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IConfigurationSetProcessor { // Gets the configuration unit processor details for the given unit. IConfigurationUnitProcessorDetails GetUnitProcessorDetails(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags); // Creates a configuration unit processor for the given unit. IConfigurationUnitProcessor CreateUnitProcessor(ConfigurationUnit unit); } // The definition of find unit processors option. [contract(Microsoft.Management.Configuration.Contract, 4)] runtimeclass FindUnitProcessorsOptions { FindUnitProcessorsOptions(); // Search paths for finding configuration unit processors. The value has the same format as // PATH environment variable. Paths are absolute paths separated by semicolons(;). String SearchPaths; // If the search paths provided are exclusive. Boolean SearchPathsExclusive; // Defines the type of detail probing that is allowed when finding unit processors. ConfigurationUnitDetailFlags UnitDetailFlags; } // Find unit processors. [contract(Microsoft.Management.Configuration.Contract, 4)] interface IFindUnitProcessorsSetProcessor requires IConfigurationSetProcessor { // Find unit processors. Windows.Foundation.Collections.IVector FindUnitProcessors(FindUnitProcessorsOptions findOptions); } // The result of applying an individual unit settings with an IConfigurationGroupProcessor. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IApplyGroupMemberSettingsResult { // The configuration unit. ConfigurationUnit Unit{ get; }; // The state of the unit. // Properties other than `Unit` are not valid unless this value is `Completed`. ConfigurationUnitState State{ get; }; // Will be true if the configuration unit was in the desired state (Test returns true) prior to the apply action. Boolean PreviouslyInDesiredState{ get; }; // Indicates whether a reboot is required after the settings were applied. Boolean RebootRequired{ get; }; // The result of applying the configuration unit. IConfigurationUnitResultInformation ResultInformation{ get; }; } // The result of applying the settings with an IConfigurationGroupProcessor. [contract(Microsoft.Management.Configuration.Contract, 2)] interface IApplyGroupSettingsResult { // The configuration group object (set or unit). Object Group{ get; }; // Indicates whether a reboot is required after the settings were applied. Boolean RebootRequired{ get; }; // The result of applying the configuration unit group. IConfigurationUnitResultInformation ResultInformation{ get; }; // Results for each configuration unit in the group. Windows.Foundation.Collections.IVector UnitResults{ get; }; } // The result of testing the settings with an IConfigurationGroupProcessor. [contract(Microsoft.Management.Configuration.Contract, 2)] interface ITestGroupSettingsResult { // The configuration group object (set or unit). Object Group{ get; }; // The result (if any) of running Test on the configuration unit group. ConfigurationTestResult TestResult{ get; }; // The result of testing the configuration unit group. // This is not the response for the test, but rather contains information about the actual attempt to run the test. IConfigurationUnitResultInformation ResultInformation{ get; }; // Results for each configuration unit in the group. Windows.Foundation.Collections.IVector UnitResults{ get; }; } // Provides access to a specific configuration unit group within the runtime. // An object returned by `CreateUnitProcessor` or `CreateSetProcessor` can implement this interface to indicate that it can take // the responsibility of executing the units that it contains. [contract(Microsoft.Management.Configuration.Contract, 2)] interface IConfigurationGroupProcessor { // The configuration group object (set or unit). Object Group{ get; }; // Determines if the system is already in the state described by the configuration unit group. // Progress is expected for every descendant unit and finally the group unit itself. Windows.Foundation.IAsyncOperation TestGroupSettingsAsync(Windows.Foundation.EventHandler progressHandler); // Applies the state described in the configuration unit group. // Progress is expected for every descendant unit and finally the group unit itself. Windows.Foundation.IAsyncOperation ApplyGroupSettingsAsync(Windows.Foundation.EventHandler progressHandler); } // The level of the diagnostic information. [contract(Microsoft.Management.Configuration.Contract, 1)] enum DiagnosticLevel { Verbose, Informational, Warning, Error, Critical, }; // Enables diagnostic information from the configuration system to be inspected/stored by callers. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IDiagnosticInformation { // Indicates the importance of the diagnostic information. DiagnosticLevel Level{ get; }; // The diagnostic message. String Message{ get; }; } // Allows different runtimes to provide specialized handling of configuration processing. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IConfigurationSetProcessorFactory { // Creates a configuration set processor for the given set. IConfigurationSetProcessor CreateSetProcessor(ConfigurationSet configurationSet); // Diagnostics event; useful for logging and/or verbose output. event Windows.Foundation.EventHandler Diagnostics; // Indicates the minimum importance desired for diagnostics. DiagnosticLevel MinimumLevel; } // The change event type that has occurred. [contract(Microsoft.Management.Configuration.Contract, 1)] enum ConfigurationChangeEventType { Unknown, SetAdded, SetStateChanged, SetRemoved, }; // The change data sent about changes to sets. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ConfigurationChangeData { // The change event type that occurred. ConfigurationChangeEventType Change{ get; }; // The identifier used to uniquely identify the instance of a configuration set on the system. Guid InstanceIdentifier{ get; }; // The state of the configuration set for this event (the ConfigurationSet can be used to get the current state, which may be different). ConfigurationSetState State{ get; }; } // The result of calling OpenConfigurationSet, containing either the set or details about the failure. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass OpenConfigurationSetResult { // The configuration set if successful; null otherwise. ConfigurationSet Set{ get; }; // The result from opening the set. HRESULT ResultCode{ get; }; // The field that is missing/invalid, if appropriate for the specific ResultCode. String Field{ get; }; // The value of the field, if appropriate for the specific ResultCode. String Value{ get; }; // The line number for the failure reason, if determined. UInt32 Line{ get; }; // The column number for the failure reason, if determined. UInt32 Column{ get; }; } // The type of conflict between configuration sets that was detected. [contract(Microsoft.Management.Configuration.Contract, 1)] enum ConfigurationConflictType { Unknown, // Indicates that the first configuration set has a matching name and origin to the second, which has already been applied. // This is likely an update to the existing set, and should be applied as such, rather than an entirely new set. MatchingOrigin, // Indicates that the first configuration set is identical to the second, which has already been applied. // This is based solely on the configuration unit settings. IdenticalSetApplied, // Indicates a conflict between the settings of two configuration units. SettingsConflict, }; // Describes a conflict between a setting of two configuration units. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ConfigurationConflictSetting { // The name of the setting. String Name{ get; }; // The value from the first configuration unit. // These are the values from a `ValueSet`, and are thus required to be a `PropertyValue` or a 'ValueSet`. Object FirstValue{ get; }; // The value from the second configuration unit. // These are the values from a `ValueSet`, and are thus required to be a `PropertyValue` or a 'ValueSet`. Object SecondValue{ get; }; } // Describes a conflict between two configuration sets. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ConfigurationConflict { // The type of conflict detected. ConfigurationConflictType Conflict{ get; }; // The first of the configuration sets involved in the conflict. ConfigurationSet FirstSet{ get; }; // The second of the configuration sets involved in the conflict. ConfigurationSet SecondSet{ get; }; // The first of the configuration units involved in the conflict. ConfigurationUnit FirstUnit{ get; }; // The second of the configuration units involved in the conflict. ConfigurationUnit SecondUnit{ get; }; // Contains information about the particular settings that are conflicting. Windows.Foundation.Collections.IVectorView Settings{ get; }; } // The result of getting the configuration unit details. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass GetConfigurationUnitDetailsResult { // The configuration unit whose details were retrieved. ConfigurationUnit Unit{ get; }; // The details, if they were able to be acquired successfully. IConfigurationUnitProcessorDetails Details{ get; }; // The result of getting the configuration unit details. IConfigurationUnitResultInformation ResultInformation{ get; }; } // The result of getting the configuration set details. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass GetConfigurationSetDetailsResult { // The configuration unit whose details were retrieved. Windows.Foundation.Collections.IVectorView UnitResults{ get; }; } // Flags to control how a configuration set should be applied to the system. [contract(Microsoft.Management.Configuration.Contract, 1)] [flags] enum ApplyConfigurationSetFlags { None = 0x0, // Forces a new configuration set instance to be recorded when the set being applied matches a previous set's origin. // The default behavior is to assume that the incoming set is an update to the existing set and overwrite it. DoNotOverwriteMatchingOriginSet = 0x1, // Does not apply the configuration set, only checks the configuration set for internal consistency. PerformConsistencyCheckOnly = 0x2, }; // The result of applying the settings for a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ApplyConfigurationUnitResult { // The configuration unit that was applied. ConfigurationUnit Unit{ get; }; // The state of the configuration unit with regards to the current execution of ApplySet. ConfigurationUnitState State{ get; }; // Will be true if the configuration unit was in the desired state (Test returns true) prior to the apply action. Boolean PreviouslyInDesiredState{ get; }; // Indicates whether a reboot is required after the configuration unit was applied. Boolean RebootRequired{ get; }; // The result of applying the configuration unit. IConfigurationUnitResultInformation ResultInformation{ get; }; } // The result of applying the settings for a configuration set. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ApplyConfigurationSetResult { // Results for each configuration unit in the set. Windows.Foundation.Collections.IVectorView UnitResults{ get; }; // The overall result from applying the configuration set. HRESULT ResultCode{ get; }; } // The result of testing the settings for a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass TestConfigurationUnitResult { // The configuration unit that was tested. ConfigurationUnit Unit{ get; }; // The result of testing the configuration unit. // This is not the response for the test, but rather contains information about the actual attempt to run the test. IConfigurationUnitResultInformation ResultInformation{ get; }; // The result (if any) of running Test on the configuration unit. ConfigurationTestResult TestResult{ get; }; } // The result of testing the settings for a configuration set. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass TestConfigurationSetResult { // Results for each configuration unit in the set. Windows.Foundation.Collections.IVectorView UnitResults{ get; }; // The result (if any) of running Test on the configuration set. // If this value is NotRun, every unit result will be NotRun. // If this value is Positive, every unit result will be Positive (or NotRun with at least one being Positive). // Any Negative result for a unit will result in this value being Negative. // Any Failed result for a unit will result in this value being Failed (overriding the Negative statement above). ConfigurationTestResult TestResult{ get; }; } // The result of getting the settings for a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass GetConfigurationUnitSettingsResult { // The result of getting the configuration unit settings. // This is not the response for the retrieval, but rather contains information about the actual attempt to retrieve the settings. IConfigurationUnitResultInformation ResultInformation{ get; }; // The current state of the system for the configuration unit. Windows.Foundation.Collections.ValueSet Settings { get; }; } // The result of getting the settings for all the instances of a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 2)] runtimeclass GetAllConfigurationUnitSettingsResult { // The result of getting the settings for all the instances of the configuration unit. // This is not the response for the retrieval, but rather contains information about the actual attempt to retrieve the settings. IConfigurationUnitResultInformation ResultInformation{ get; }; // The current state of the system for all the instances of the configuration unit. Windows.Foundation.Collections.IVector Settings { get; }; } // The result of getting the all units for a configuration unit. [contract(Microsoft.Management.Configuration.Contract, 4)] runtimeclass GetAllConfigurationUnitsResult { // The result of getting the all units for a configuration unit. // This is not the response for the retrieval, but rather contains information about the actual attempt to retrieve the settings. IConfigurationUnitResultInformation ResultInformation{ get; }; // The units retrieved for the given unit. Windows.Foundation.Collections.IVector Units { get; }; } // The configuration processor is responsible for the interactions with the system. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ConfigurationProcessor { ConfigurationProcessor(IConfigurationSetProcessorFactory factory); // Diagnostics event; useful for logging and/or verbose output. event Windows.Foundation.EventHandler Diagnostics; // Indicates the minimum importance desired for diagnostics. DiagnosticLevel MinimumLevel; // Set the caller to used to identify the usage in telemetry events. String Caller; // The identifier for the current activity, enabling multiple calls into the processor to be correlated. Guid ActivityIdentifier; // If true, ETW events will be generated. Some of those events may be sent to Microsoft depending on the system settings. Boolean GenerateTelemetryEvents; // Only top level configuration changes are sent to this event. // This includes things like: creation of a new set for intent to run, start/stop of a set for application, deletion of a not started set. event Windows.Foundation.TypedEventHandler ConfigurationChange; // Gets the configuration sets that have already been applied or with the intent to be applied (this may include in progress sets or those that are waiting on others). Windows.Foundation.Collections.IVector GetConfigurationHistory(); Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVector > GetConfigurationHistoryAsync(); // Loads a configuration set from the given stream. OpenConfigurationSetResult OpenConfigurationSet(Windows.Storage.Streams.IInputStream stream); Windows.Foundation.IAsyncOperation OpenConfigurationSetAsync(Windows.Storage.Streams.IInputStream stream); // Checks for conflicts amongst the configuration sets provided, optionally including the configuration sets already applied to the system. Windows.Foundation.Collections.IVector CheckForConflicts(Windows.Foundation.Collections.IVectorView configurationSets, Boolean includeConfigurationHistory); Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVector > CheckForConflictsAsync(Windows.Foundation.Collections.IVectorView configurationSets, Boolean includeConfigurationHistory); // Gets the details for all configuration units in a set. GetConfigurationSetDetailsResult GetSetDetails(ConfigurationSet configurationSet, ConfigurationUnitDetailFlags detailFlags); Windows.Foundation.IAsyncOperationWithProgress GetSetDetailsAsync(ConfigurationSet configurationSet, ConfigurationUnitDetailFlags detailFlags); // Gets the details for a configuration unit. GetConfigurationUnitDetailsResult GetUnitDetails(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags); Windows.Foundation.IAsyncOperation GetUnitDetailsAsync(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags); // Applies the configuration set state. ApplyConfigurationSetResult ApplySet(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags); Windows.Foundation.IAsyncOperationWithProgress ApplySetAsync(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags); // Tests the configuration set state. TestConfigurationSetResult TestSet(ConfigurationSet configurationSet); Windows.Foundation.IAsyncOperationWithProgress TestSetAsync(ConfigurationSet configurationSet); // Gets the current configuration unit settings. GetConfigurationUnitSettingsResult GetUnitSettings(ConfigurationUnit unit); Windows.Foundation.IAsyncOperation GetUnitSettingsAsync(ConfigurationUnit unit); [contract(Microsoft.Management.Configuration.Contract, 2)] { // Gets the current settings for all the instances of a configuration unit. GetAllConfigurationUnitSettingsResult GetAllUnitSettings(ConfigurationUnit unit); Windows.Foundation.IAsyncOperation GetAllUnitSettingsAsync(ConfigurationUnit unit); } [contract(Microsoft.Management.Configuration.Contract, 4)] { // Gets all configuration units for the given unit type. // Returned units may be of types other than the one passed in. GetAllConfigurationUnitsResult GetAllUnits(ConfigurationUnit unit); Windows.Foundation.IAsyncOperation GetAllUnitsAsync(ConfigurationUnit unit); // Find unit processors. Windows.Foundation.Collections.IVector FindUnitProcessors(FindUnitProcessorsOptions findOptions); Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVector > FindUnitProcessorsAsync(FindUnitProcessorsOptions findOptions); // Apply the current configuration unit. ApplyConfigurationUnitResult ApplyUnit(ConfigurationUnit unit); Windows.Foundation.IAsyncOperation ApplyUnitAsync(ConfigurationUnit unit); // Test the current configuration unit. TestConfigurationUnitResult TestUnit(ConfigurationUnit unit); Windows.Foundation.IAsyncOperation TestUnitAsync(ConfigurationUnit unit); } } // Top level entry point for configuration, enabling easier usage in out-of-process scenarios. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IConfigurationStatics { // Creates an empty configuration unit. ConfigurationUnit CreateConfigurationUnit(); // Creates an empty configuration set. ConfigurationSet CreateConfigurationSet(); // Creates a processor factory for the given handler. Windows.Foundation.IAsyncOperation CreateConfigurationSetProcessorFactoryAsync(String handler); // Creates a processor from the given factory. ConfigurationProcessor CreateConfigurationProcessor(IConfigurationSetProcessorFactory factory); // Whether configuration is enabled. Boolean IsConfigurationAvailable{ get; }; // Enables configuration. Requires store access. Windows.Foundation.IAsyncActionWithProgress EnsureConfigurationAvailableAsync(); } // Top level entry point for configuration, enabling easier usage in out-of-process scenarios. [contract(Microsoft.Management.Configuration.Contract, 2)] interface IConfigurationStatics2 requires IConfigurationStatics { // Creates an empty configuration parameter. ConfigurationParameter CreateConfigurationParameter(); } // Top level entry point for configuration, enabling easier usage in out-of-process scenarios. [contract(Microsoft.Management.Configuration.Contract, 4)] interface IConfigurationStatics3 requires IConfigurationStatics2 { // Creates an empty configuration parameter. FindUnitProcessorsOptions CreateFindUnitProcessorsOptions(); } // Top level entry point for configuration, enabling easier usage in out-of-process scenarios. [contract(Microsoft.Management.Configuration.Contract, 1)] runtimeclass ConfigurationStaticFunctions : [default]IConfigurationStatics, IConfigurationStatics2, IConfigurationStatics3 { ConfigurationStaticFunctions(); } /// Force midl3 to generate vector marshalling info. declare { // Due to the way that metadata (WinMD) based marshalling works, in order for any of these to be IIterable, they need to be // included in the manifest of the package. Update the DumpProxyStubRegistrationsCommand to add any new types to make it easier // to iterate over these collections, especially in C#. interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVector; } // Provides a way to centralize the distribution of interfaces relevant to specific implementations of IConfigurationSetProcessorFactory. namespace SetProcessorFactory { // The same as PowerShell ExecutionPolicy: // https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies enum PwshConfigurationProcessorPolicy { Unrestricted = 0, RemoteSigned = 1, AllSigned = 2, Restricted = 3, Bypass = 4, Undefined = 5, Default = RemoteSigned, }; enum PwshConfigurationProcessorLocation { CurrentUser = 0, AllUsers = 1, WinGetModulePath = 2, Custom = 3, Default = WinGetModulePath, }; // The properties provided by the "pwsh" processor factory. interface IPwshConfigurationSetProcessorFactoryProperties { // The module paths to add to the processor. // This will be in addition to any paths added by the processor and those inherent to PowerShell. Windows.Foundation.Collections.IVectorView AdditionalModulePaths; // The execution policy to apply; must be set before taking actions with the processor. PwshConfigurationProcessorPolicy Policy; // The location to install modules. Default is WinGetModulePath PwshConfigurationProcessorLocation Location; // The custom location. Only applicable for Scope.Custom String CustomLocation; }; } } ================================================ FILE: src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj ================================================ true true true true {CA460806-5E41-4E97-9A3D-1D74B433B663} Microsoft.Management.Configuration Microsoft.Management.Configuration en-US 14.0 10.0 10.0.26100.0 10.0.17763.0 true Debug ARM64 Debug Win32 Debug x64 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Release x64 DynamicLibrary false true true true false true false Spectre false true false Spectre $(VC_IncludePath);$(WindowsSDK_IncludePath); $(SolutionDir)$(PlatformTarget)\$(Configuration)\$(ProjectName)\ $(PlatformTarget)\$(Configuration)\ $(RootNamespace) true true ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch Level4 %(AdditionalOptions) /bigobj true true _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) $(ProjectDir);$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) Console false Microsoft_Management_Configuration.def $(OutDir)$(ProjectName).winmd Advapi32.lib;icuuc.lib;icuin.lib;onecoreuap.lib;winsqlite3.lib;%(AdditionalDependencies) _DEBUG;%(PreprocessorDefinitions) false false false false false false true true NDEBUG;%(PreprocessorDefinitions) false false false Guard Guard Guard true true /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full %(AdditionalOptions) /debug:full %(AdditionalOptions) /debug:full %(AdditionalOptions) true true NDEBUG;%(PreprocessorDefinitions) false false false MultiThreaded MultiThreaded MultiThreaded Guard Guard Guard true true true true Create {f3f6e699-bc5d-4950-8a05-e49dd9eb0d51} This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters ================================================  API Source API Source API Source API Source API Source API Source API Source API Source API Source API Source API Source API Source Parser Parser API Source Internals Internals API Source API Source Telemetry Telemetry Parser Internals API Source Internals API Source API Source Internals Parser Internals Internals Internals Internals Parser Parser Parser Database Database\Schema Database\Schema\0_1 Internals Database\Schema\0_1 Database\Schema\0_1 Internals Database\Schema\0_2 Database\Schema\0_2 Internals Database\Schema\0_3 Database\Schema\0_3 Database\Schema\0_3 Parser API Source API Source API Source Internals API Headers API Headers API Headers API Headers API Headers API Headers API Headers API Headers API Headers API Headers API Headers API Headers Parser Parser API Headers Parser Internals Internals Internals API Headers API Headers Telemetry Telemetry Parser Internals API Headers Internals API Headers API Headers Internals Parser Parser Internals Internals Internals Internals Parser Parser Parser Database Database\Schema Database\Schema\0_1 Internals Database\Schema\0_1 Database\Schema\0_1 Internals Database\Schema\0_2 Database\Schema\0_2 Internals Database\Schema\0_3 Database\Schema\0_3 Database\Schema\0_3 Parser API Headers API Headers API Headers Internals {0826fc0e-120c-4e31-bde7-ce0a1278b1b1} {6dbfe76d-646d-43b0-b162-e1dedad10ab8} {c5f2f74e-de80-4235-abbd-bacd6771eaf2} {b31f8336-b4d8-4c05-b08d-6b82c550a30b} {5a02f1a5-14f3-4a28-8bed-212f3e6b1a00} {c82c1df2-4ef3-4d54-9c18-a13ade2ab16a} {6f544d8a-2c3f-4d26-9b53-84dbd2144d43} {efb71f71-31e4-42db-9105-f10c2e89e1d5} {f214d0f3-3e9c-469b-91ae-213315d39a69} {d059436d-cd54-4cf2-96bc-4db53c617537} ================================================ FILE: src/Microsoft.Management.Configuration/Microsoft_Management_Configuration.def ================================================ EXPORTS DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE ================================================ FILE: src/Microsoft.Management.Configuration/OpenConfigurationSetResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "OpenConfigurationSetResult.h" #include "OpenConfigurationSetResult.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { void OpenConfigurationSetResult::Initialize(Configuration::ConfigurationSet configurationSet) { m_set = std::move(configurationSet); } void OpenConfigurationSetResult::Initialize(hresult resultCode, hstring field, hstring value, uint32_t line, uint32_t column) { m_resultCode = resultCode; m_field = field; m_value = value; m_line = line; m_column = column; } Configuration::ConfigurationSet OpenConfigurationSetResult::Set() { return m_set; } hresult OpenConfigurationSetResult::ResultCode() { return m_resultCode; } hstring OpenConfigurationSetResult::Field() { return m_field; } hstring OpenConfigurationSetResult::Value() { return m_value; } uint32_t OpenConfigurationSetResult::Line() { return m_line; } uint32_t OpenConfigurationSetResult::Column() { return m_column; } } ================================================ FILE: src/Microsoft.Management.Configuration/OpenConfigurationSetResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "OpenConfigurationSetResult.g.h" #include "ConfigurationSet.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct OpenConfigurationSetResult : OpenConfigurationSetResultT { OpenConfigurationSetResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(Configuration::ConfigurationSet configurationSet); void Initialize(hresult resultCode, hstring field = {}, hstring value = {}, uint32_t line = 0, uint32_t column = 0); #endif Configuration::ConfigurationSet Set(); hresult ResultCode(); hstring Field(); hstring Value(); uint32_t Line(); uint32_t Column(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: Configuration::ConfigurationSet m_set = nullptr; hresult m_resultCode; hstring m_field; hstring m_value; uint32_t m_line = 0; uint32_t m_column = 0; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/ParsingMacros.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #define CHECK_ERROR(_op_) (_op_); if (FAILED(m_result)) { return; } #define FIELD_TYPE_ERROR(_field_,_mark_) SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, (_field_), (_mark_)); return #define FIELD_TYPE_ERROR_IF(_condition_,_field_,_mark_) if (_condition_) { FIELD_TYPE_ERROR(_field_,_mark_); } #define FIELD_MISSING_ERROR(_field_) SetError(WINGET_CONFIG_ERROR_MISSING_FIELD, (_field_)); return #define FIELD_MISSING_ERROR_IF(_condition_,_field_) if (_condition_) { FIELD_MISSING_ERROR(_field_); } #define FIELD_VALUE_ERROR(_field_,_value_,_mark_) SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, (_field_), (_mark_), (_value_)); return #define FIELD_VALUE_ERROR_IF(_condition_,_field_,_value_,_mark_) if (_condition_) { FIELD_VALUE_ERROR(_field_,_value_,_mark_); } ================================================ FILE: src/Microsoft.Management.Configuration/PropertySheet.props ================================================ ================================================ FILE: src/Microsoft.Management.Configuration/ShutdownSynchronization.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ShutdownSynchronization.h" namespace winrt::Microsoft::Management::Configuration::implementation { ShutdownAwareAsyncCancellation::ShutdownAwareAsyncCancellation() { m_defaultPromise = std::make_unique(); m_cancellation = std::make_unique(winrt::impl::cancellation_token{ m_defaultPromise.get() }); RegisterWithShutdownSynchronization(); } ShutdownAwareAsyncCancellation::~ShutdownAwareAsyncCancellation() { if (m_cancellation) { ShutdownSynchronization::Instance().RegisterWorkEnd(m_cancellation->GetWeak()); } } bool ShutdownAwareAsyncCancellation::IsCancelled() const noexcept { return m_cancellation->IsCancelled(); } void ShutdownAwareAsyncCancellation::ThrowIfCancelled() const { m_cancellation->ThrowIfCancelled(); } void ShutdownAwareAsyncCancellation::Callback(winrt::delegate<>&& callback) const noexcept { m_cancellation->Callback(std::move(callback)); } void ShutdownAwareAsyncCancellation::RegisterWithShutdownSynchronization() { ShutdownSynchronization::Instance().RegisterWorkBegin(m_cancellation->GetWeak()); } Windows::Foundation::AsyncStatus ShutdownAwareAsyncCancellationPromise::Status() noexcept { return m_status.load(std::memory_order_acquire); } void ShutdownAwareAsyncCancellationPromise::cancellation_callback(winrt::delegate<>&& cancel) noexcept { { slim_lock_guard const guard(m_lock); if (m_status.load(std::memory_order_relaxed) != Windows::Foundation::AsyncStatus::Canceled) { m_cancel = std::move(cancel); return; } } if (cancel) { cancel(); } } bool ShutdownAwareAsyncCancellationPromise::enable_cancellation_propagation(bool) noexcept { THROW_HR(E_NOTIMPL); } void ShutdownAwareAsyncCancellationPromise::Cancel() noexcept { winrt::delegate<> cancel; { slim_lock_guard const guard(m_lock); if (m_status.load(std::memory_order_relaxed) == Windows::Foundation::AsyncStatus::Started) { m_status.store(Windows::Foundation::AsyncStatus::Canceled, std::memory_order_relaxed); cancel = std::move(m_cancel); } } if (cancel) { cancel(); } } ShutdownSynchronization& ShutdownSynchronization::Instance() { static ShutdownSynchronization s_instance; return s_instance; } void ShutdownSynchronization::BlockNewWork() { m_disabled = true; } void ShutdownSynchronization::RegisterWorkBegin(CancellableWeakPtr&& ptr) { if (m_disabled) { THROW_HR(E_ABORT); } std::lock_guard lock{ m_workLock }; m_work.emplace(std::move(ptr)); m_noActiveWork.ResetEvent(); } void ShutdownSynchronization::RegisterWorkEnd(CancellableWeakPtr&& ptr) { std::lock_guard lock{ m_workLock }; auto itr = m_work.find(ptr); if (itr != m_work.end()) { m_work.erase(itr); if (m_work.empty()) { m_noActiveWork.SetEvent(); } } } void ShutdownSynchronization::CancelAllWork() { std::lock_guard lock{ m_workLock }; for (auto itr = m_work.begin(); itr != m_work.end(); ++itr) { if (auto locked = itr->lock()) { locked->Cancel(); } else { m_work.erase(itr); } } if (m_work.empty()) { m_noActiveWork.SetEvent(); } } void ShutdownSynchronization::Wait() { for (;;) { { std::lock_guard lock{ m_workLock }; // Check for any inactive work before waiting for (auto itr = m_work.begin(); itr != m_work.end(); ++itr) { if (!itr->lock()) { m_work.erase(itr); } } if (m_work.empty()) { break; } } if (m_noActiveWork.wait(250)) { break; } } } } ================================================ FILE: src/Microsoft.Management.Configuration/ShutdownSynchronization.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Promise type implementation for cancellation_token struct ShutdownAwareAsyncCancellationPromise { Windows::Foundation::AsyncStatus Status() noexcept; void cancellation_callback(winrt::delegate<>&& cancel) noexcept; bool enable_cancellation_propagation(bool value) noexcept; void Cancel() noexcept; private: slim_mutex m_lock; winrt::delegate<> m_cancel; std::atomic m_status{ Windows::Foundation::AsyncStatus::Started }; }; // An AsyncCancellation that registers with ShutdownSynchronization. struct ShutdownAwareAsyncCancellation { // Creates a cancellable object without an external cancellation token. ShutdownAwareAsyncCancellation(); // Create a cancellation object from the winrt token. template ShutdownAwareAsyncCancellation(winrt::impl::cancellation_token&& token) { m_cancellation = std::make_unique(std::move(token)); RegisterWithShutdownSynchronization(); } // Removes the shutdown registration. ~ShutdownAwareAsyncCancellation(); // Returns true if the operation has been cancelled, false if not. bool IsCancelled() const noexcept; // Throws the appropriate exception if the operation has been cancelled. void ThrowIfCancelled() const; // Sets a callback that will be invoked on cancellation. void Callback(winrt::delegate<>&& callback) const noexcept; protected: void RegisterWithShutdownSynchronization(); std::unique_ptr m_defaultPromise; std::unique_ptr m_cancellation; }; struct ShutdownSynchronization { using CancellableWeakPtr = std::weak_ptr; ShutdownSynchronization() = default; static ShutdownSynchronization& Instance(); // Signals that new work should be blocked. void BlockNewWork(); // Call to register the begin and end of work. void RegisterWorkBegin(CancellableWeakPtr&& ptr); void RegisterWorkEnd(CancellableWeakPtr&& ptr); // Cancels all currently registered work. void CancelAllWork(); // Waits for outstanding work to be completed. void Wait(); private: std::atomic_bool m_disabled{ false }; std::mutex m_workLock; std::set> m_work; wil::slim_event_manual_reset m_noActiveWork{ true }; }; // An AsyncProgress that registers with ShutdownSynchronization. template struct ShutdownAwareAsyncProgress { // Creates a cancellable object without an external cancellation token. ShutdownAwareAsyncProgress() { m_defaultPromise = std::make_unique(); m_progress = std::make_unique>(winrt::impl::cancellation_token{ m_defaultPromise.get() }); RegisterWithShutdownSynchronization(); } // Create a progress object from the winrt token. template ShutdownAwareAsyncProgress(winrt::impl::progress_token&& progress, winrt::impl::cancellation_token&& cancellation) { m_progress = std::make_unique>(std::move(progress), std::move(cancellation)); RegisterWithShutdownSynchronization(); } // Create a progress object from an EventHandler. template ShutdownAwareAsyncProgress(winrt::Windows::Foundation::EventHandler&& progress, winrt::impl::cancellation_token&& cancellation) { m_progress = std::make_unique>(std::move(progress), std::move(cancellation)); RegisterWithShutdownSynchronization(); } ShutdownAwareAsyncProgress(const ShutdownAwareAsyncProgress&) = delete; ShutdownAwareAsyncProgress& operator=(const ShutdownAwareAsyncProgress&) = delete; ShutdownAwareAsyncProgress(ShutdownAwareAsyncProgress&&) = default; ShutdownAwareAsyncProgress& operator=(ShutdownAwareAsyncProgress&&) = default; // Removes the shutdown registration. ~ShutdownAwareAsyncProgress() { if (m_progress) { ShutdownSynchronization::Instance().RegisterWorkEnd(m_progress->GetWeak()); } } AppInstaller::WinRT::AsyncCancellation& GetCancellation() { return *m_progress; } // Returns true if the operation has been cancelled, false if not. bool IsCancelled() const noexcept { return m_progress->IsCancelled(); } // Throws the appropriate exception if the operation has been cancelled. void ThrowIfCancelled() const { m_progress->ThrowIfCancelled(); } // Sets a callback that will be invoked on cancellation. void Callback(winrt::delegate<>&& callback) const noexcept { m_progress->Callback(std::move(callback)); } // Sends progress if this object is not empty. void Progress(ProgressT const& progress) const { m_progress->Progress(progress); } // Sets the result onto the progress object if it is not empty. void Result(ResultT const& result) const { m_progress->Result(result); } protected: void RegisterWithShutdownSynchronization() { ShutdownSynchronization::Instance().RegisterWorkBegin(m_progress->GetWeak()); } std::unique_ptr m_defaultPromise; std::unique_ptr> m_progress; }; } ================================================ FILE: src/Microsoft.Management.Configuration/Telemetry/Telemetry.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Telemetry.h" #include "TraceLogging.h" #include #include #include #include #define AICLI_TraceLoggingStringView(_sv_,_name_) TraceLoggingCountedUtf8String(_sv_.data(), static_cast(_sv_.size()), _name_) #define AICLI_TraceLoggingWStringView(_sv_,_name_) TraceLoggingCountedWideString(_sv_.data(), static_cast(_sv_.size()), _name_) #define AICLI_TraceLoggingProcessingSummaryForIntent(_forIntent_,_name_,_pluralName_) \ TraceLoggingUInt32(_forIntent_.Count, _name_ ## "Count"), \ TraceLoggingUInt32(_forIntent_.Run, _pluralName_ ## "Run"), \ TraceLoggingUInt32(_forIntent_.Failed, _pluralName_ ## "Failed") #define AICLI_TraceLoggingWriteActivity(_eventName_,...) TraceLoggingWriteActivity(\ g_hTraceProvider,\ _eventName_,\ GetActivityId(),\ nullptr,\ TraceLoggingCountedUtf8String(m_version.c_str(), static_cast(m_version.size()), "CodeVersion"),\ TraceLoggingCountedUtf8String(m_caller.c_str(), static_cast(m_caller.size()), "Caller"),\ __VA_ARGS__) #ifdef AICLI_DISABLE_TEST_HOOKS #define WinGet_EventItem(_value_,_name_) #define WinGet_SummaryForIntentItem(_forIntent_,_name_,_pluralName_) #define WinGet_WriteEventToDiagnostics(_eventName_,...) #else struct WinGetAbsorbVA_ARGSCommas { WinGetAbsorbVA_ARGSCommas(int, int) {} }; inline std::ostream& operator<<(std::ostream& out, const WinGetAbsorbVA_ARGSCommas&) { return out; } inline std::ostream& operator<<(std::ostream& out, std::wstring_view value) { out << AppInstaller::Utility::ConvertToUTF8(value); return out; } #define WinGet_EventItem(_value_,_name_) \ 0) << (_name_) << ": " << (_value_) << '\n' << WinGetAbsorbVA_ARGSCommas(0 #define WinGet_SummaryForIntentItem(_forIntent_,_name_,_pluralName_) \ WinGet_EventItem(_forIntent_.Count, _name_ ## "Count"), \ WinGet_EventItem(_forIntent_.Run, _pluralName_ ## "Run"), \ WinGet_EventItem(_forIntent_.Failed, _pluralName_ ## "Failed") #define WinGet_WriteEventToDiagnostics(_eventName_,...) \ { \ std::ostringstream _debugEventStream; \ _debugEventStream << \ "#DebugEventStream\n" << \ "Event: " << (_eventName_) << '\n' << \ "ActivityID: " << *GetActivityId() << '\n' << \ "CodeVersion: " << m_version << '\n' << \ "Caller: " << m_caller << '\n' \ << WinGetAbsorbVA_ARGSCommas(0, __VA_ARGS__ ,0) \ ; \ AICLI_LOG_LARGE_STRING(Config, Verbose, , _debugEventStream.str()); \ } #endif using namespace std::string_view_literals; namespace winrt::Microsoft::Management::Configuration::implementation { namespace { // The data collected from running through a set of results. struct ConfigRunSummaryData { hresult Result = S_OK; ConfigurationUnitResultSource FailurePoint = ConfigurationUnitResultSource::None; TelemetryTraceLogger::ProcessingSummaryForIntent AssertSummary{ ConfigurationUnitIntent::Assert }; TelemetryTraceLogger::ProcessingSummaryForIntent InformSummary{ ConfigurationUnitIntent::Inform }; TelemetryTraceLogger::ProcessingSummaryForIntent ApplySummary{ ConfigurationUnitIntent::Apply }; }; size_t GetPriority(ConfigurationUnitResultSource source) { switch (source) { case ConfigurationUnitResultSource::Internal: return 0; case ConfigurationUnitResultSource::UnitProcessing: return 100; case ConfigurationUnitResultSource::SystemState: return 200; case ConfigurationUnitResultSource::ConfigurationSet: return 300; case ConfigurationUnitResultSource::Precondition: return 400; default: return 500; case ConfigurationUnitResultSource::None: return 600; } } bool FirstHasPriority(ConfigurationUnitResultSource first, ConfigurationUnitResultSource second) { return GetPriority(first) < GetPriority(second); } void ProcessUnitResult(const Configuration::ConfigurationUnit unit, const IConfigurationUnitResultInformation& resultInformation, ConfigRunSummaryData& result) { hresult resultCode = resultInformation.ResultCode(); if (FAILED(resultCode)) { if (result.Result == S_OK || result.Result == resultCode) { result.Result = resultCode; } else { result.Result = WINGET_CONFIG_ERROR_SET_APPLY_FAILED; } } ConfigurationUnitResultSource unitFailurePoint = resultInformation.ResultSource(); if (FirstHasPriority(unitFailurePoint, result.FailurePoint)) { result.FailurePoint = unitFailurePoint; } TelemetryTraceLogger::ProcessingSummaryForIntent* summaryItem = nullptr; switch (unit.Intent()) { case ConfigurationUnitIntent::Assert: summaryItem = &result.AssertSummary; break; case ConfigurationUnitIntent::Inform: summaryItem = &result.InformSummary; break; case ConfigurationUnitIntent::Apply: case ConfigurationUnitIntent::Unknown: summaryItem = &result.ApplySummary; break; default: return; } summaryItem->Count++; ConfigurationUnitResultSource resultSource = resultInformation.ResultSource(); if (resultSource != ConfigurationUnitResultSource::Precondition && resultSource != ConfigurationUnitResultSource::ConfigurationSet) { summaryItem->Run++; } if (FAILED(resultCode)) { summaryItem->Failed++; } } // Runs through a set of results, summarizing them. template ConfigRunSummaryData ProcessRunResult(const Enumerable& results) { ConfigRunSummaryData result; for (const auto& item : results) { ProcessUnitResult(item.Unit(), item.ResultInformation(), result); } return result; } } TelemetryTraceLogger::TelemetryTraceLogger() { std::ignore = CoCreateGuid(&m_activityId); m_version = AppInstaller::Runtime::GetClientVersion(); } void TelemetryTraceLogger::SetActivityId(const guid& value) { m_activityId = value; } const GUID* TelemetryTraceLogger::GetActivityId() const { return &m_activityId; } bool TelemetryTraceLogger::EnableRuntime(bool value) { return m_isRuntimeEnabled.exchange(value); } bool TelemetryTraceLogger::IsEnabled() const { return m_isRuntimeEnabled; } void TelemetryTraceLogger::SetCaller(std::string_view caller) { m_caller = caller; } std::string_view TelemetryTraceLogger::GetCaller() const { return m_caller; } void TelemetryTraceLogger::LogConfigUnitRun( const guid& setIdentifier, const guid& unitIdentifier, hstring unitName, hstring moduleName, ConfigurationUnitIntent unitIntent, ConfigurationUnitIntent runIntent, std::string_view action, hresult result, ConfigurationUnitResultSource failurePoint, std::wstring_view settingNames) const noexcept try { // Change unknown to Apply for telemetry, as it will have been treated that way if (unitIntent == ConfigurationUnitIntent::Unknown) { unitIntent = ConfigurationUnitIntent::Apply; } if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "ConfigUnitRun", TraceLoggingGuid(setIdentifier, "SetID"), TraceLoggingGuid(unitIdentifier, "UnitID"), AICLI_TraceLoggingWStringView(unitName, "UnitName"), AICLI_TraceLoggingWStringView(moduleName, "ModuleName"), TraceLoggingInt32(static_cast(unitIntent), "UnitIntent"), TraceLoggingInt32(static_cast(runIntent), "RunIntent"), AICLI_TraceLoggingStringView(action, "Action"), TraceLoggingHResult(result, "Result"), TraceLoggingInt32(static_cast(failurePoint), "FailurePoint"), AICLI_TraceLoggingWStringView(settingNames, "SettingsProvided"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); // Keep in sync with above event! WinGet_WriteEventToDiagnostics( "ConfigUnitRun", WinGet_EventItem(setIdentifier, "SetID"), WinGet_EventItem(unitIdentifier, "UnitID"), WinGet_EventItem(unitName, "UnitName"), WinGet_EventItem(moduleName, "ModuleName"), WinGet_EventItem(static_cast(unitIntent), "UnitIntent"), WinGet_EventItem(static_cast(runIntent), "RunIntent"), WinGet_EventItem(action, "Action"), WinGet_EventItem(result, "Result"), WinGet_EventItem(static_cast(failurePoint), "FailurePoint"), WinGet_EventItem(settingNames, "SettingsProvided")); } } CATCH_LOG(); void TelemetryTraceLogger::LogConfigUnitRunIfAppropriate( const guid& setIdentifier, const Configuration::ConfigurationUnit& unit, ConfigurationUnitIntent runIntent, std::string_view action, const IConfigurationUnitResultInformation& resultInformation) const noexcept try { if (!IsTelemetryEnabled()) { return; } // We only want to send telemetry for publicly available units. IConfigurationUnitProcessorDetails details = unit.Details(); if (!details || !details.IsPublic()) { return; } // Create a single string from the set of top level setting names, ex. "a|b|c". const winrt::Windows::Foundation::Collections::ValueSet& settings = unit.Settings(); std::wostringstream strstr; for (const auto& setting : settings) { strstr << static_cast(setting.Key()) << L'|'; } std::wstring allSettingsNames = strstr.str(); if (!allSettingsNames.empty()) { allSettingsNames.pop_back(); } LogConfigUnitRun(setIdentifier, unit.InstanceIdentifier(), unit.Type(), details.ModuleName(), unit.Intent(), runIntent, action, resultInformation.ResultCode(), resultInformation.ResultSource(), allSettingsNames); } CATCH_LOG(); void TelemetryTraceLogger::LogConfigProcessingSummary( const guid& setIdentifier, std::string_view inputHash, ConfigurationUnitIntent runIntent, hresult result, ConfigurationUnitResultSource failurePoint, const ProcessingSummaryForIntent& assertSummary, const ProcessingSummaryForIntent& informSummary, const ProcessingSummaryForIntent& applySummary) const noexcept try { if (IsTelemetryEnabled()) { AICLI_TraceLoggingWriteActivity( "ConfigProcessingSummary", TraceLoggingGuid(setIdentifier, "SetID"), AICLI_TraceLoggingStringView(inputHash, "InputHash"), TraceLoggingBool(false, "FromHistory"), // deprecated TraceLoggingInt32(static_cast(runIntent), "RunIntent"), TraceLoggingHResult(result, "Result"), TraceLoggingInt32(static_cast(failurePoint), "FailurePoint"), AICLI_TraceLoggingProcessingSummaryForIntent(assertSummary, "Assert", "Asserts"), AICLI_TraceLoggingProcessingSummaryForIntent(informSummary, "Inform", "Informs"), AICLI_TraceLoggingProcessingSummaryForIntent(applySummary, "Apply", "Applies"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); // Keep in sync with above event! WinGet_WriteEventToDiagnostics( "ConfigProcessingSummary", WinGet_EventItem(setIdentifier, "SetID"), WinGet_EventItem(inputHash, "InputHash"), WinGet_EventItem(false, "FromHistory"), // deprecated WinGet_EventItem(static_cast(runIntent), "RunIntent"), WinGet_EventItem(result, "Result"), WinGet_EventItem(static_cast(failurePoint), "FailurePoint"), WinGet_SummaryForIntentItem(assertSummary, "Assert", "Asserts"), WinGet_SummaryForIntentItem(informSummary, "Inform", "Informs"), WinGet_SummaryForIntentItem(applySummary, "Apply", "Applies")); } } CATCH_LOG(); void TelemetryTraceLogger::LogConfigProcessingSummaryForTest( const ConfigurationSet& configurationSet, const TestConfigurationSetResult& result) const noexcept try { if (!IsTelemetryEnabled()) { return; } ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.GetInputHash(), ConfigurationUnitIntent::Assert, summaryData.Result, summaryData.FailurePoint, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); } CATCH_LOG(); void TelemetryTraceLogger::LogConfigProcessingSummaryForTestException( const ConfigurationSet& configurationSet, hresult error, const TestConfigurationSetResult& result) const noexcept try { if (!IsTelemetryEnabled()) { return; } ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.GetInputHash(), ConfigurationUnitIntent::Assert, error, ConfigurationUnitResultSource::Internal, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); } CATCH_LOG(); void TelemetryTraceLogger::LogConfigProcessingSummaryForApply( const ConfigurationSet& configurationSet, const ApplyConfigurationSetResult& result) const noexcept try { if (!IsTelemetryEnabled()) { return; } ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.GetInputHash(), ConfigurationUnitIntent::Apply, result.ResultCode(), summaryData.FailurePoint, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); } CATCH_LOG(); void TelemetryTraceLogger::LogConfigProcessingSummaryForApplyException( const ConfigurationSet& configurationSet, hresult error, const ApplyConfigurationSetResult& result) const noexcept try { if (!IsTelemetryEnabled()) { return; } ConfigRunSummaryData summaryData = ProcessRunResult(result.UnitResults()); LogConfigProcessingSummary(configurationSet.InstanceIdentifier(), configurationSet.GetInputHash(), ConfigurationUnitIntent::Apply, error, ConfigurationUnitResultSource::Internal, summaryData.AssertSummary, summaryData.InformSummary, summaryData.ApplySummary); } CATCH_LOG(); bool TelemetryTraceLogger::IsTelemetryEnabled() const noexcept { #ifdef AICLI_DISABLE_TEST_HOOKS return g_IsTelemetryProviderEnabled && m_isRuntimeEnabled; #else // For testing, only use the local enable state. return m_isRuntimeEnabled; #endif } } ================================================ FILE: src/Microsoft.Management.Configuration/Telemetry/Telemetry.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include "ConfigurationUnitResultInformation.h" #include "ConfigurationSet.h" #include "TestConfigurationSetResult.h" #include "ApplyConfigurationSetResult.h" #include #include #include #include namespace winrt::Microsoft::Management::Configuration::implementation { // Provides the ability to write telemetry events. struct TelemetryTraceLogger { TelemetryTraceLogger(); TelemetryTraceLogger(const TelemetryTraceLogger&) = default; TelemetryTraceLogger& operator=(const TelemetryTraceLogger&) = default; TelemetryTraceLogger(TelemetryTraceLogger&&) = default; TelemetryTraceLogger& operator=(TelemetryTraceLogger&&) = default; // Control whether this trace logger is enabled at runtime. // Returns the previous value. bool EnableRuntime(bool value); // Returns a value indicating whether the logger is enabled. bool IsEnabled() const; // Sets the current activity identifier. void SetActivityId(const guid& value); // Return address of m_activityId const GUID* GetActivityId() const; // Store the passed in name of the caller void SetCaller(std::string_view caller); // Get the current caller value std::string_view GetCaller() const; static constexpr std::string_view GetAction = "get"; static constexpr std::string_view ApplyAction = "apply"; static constexpr std::string_view TestAction = "test"; static constexpr std::string_view ExportAction = "export"; // Logs information about running a configuration unit. // The caller is expected to only call this for failures from publicly available units. void LogConfigUnitRun( const guid& setIdentifier, const guid& unitIdentifier, hstring unitName, hstring moduleName, ConfigurationUnitIntent unitIntent, ConfigurationUnitIntent runIntent, std::string_view action, hresult result, ConfigurationUnitResultSource failurePoint, std::wstring_view settingNames) const noexcept; // Logs information about running a configuration unit in the appropriate conditions. void LogConfigUnitRunIfAppropriate( const guid& setIdentifier, const Configuration::ConfigurationUnit& unit, ConfigurationUnitIntent runIntent, std::string_view action, const IConfigurationUnitResultInformation& resultInformation) const noexcept; // The summary information for a specific unit intent. struct ProcessingSummaryForIntent { ConfigurationUnitIntent Intent; uint32_t Count; uint32_t Run; uint32_t Failed; }; // Logs a processing summary event for a configuration set. void LogConfigProcessingSummary( const guid& setIdentifier, std::string_view inputHash, ConfigurationUnitIntent runIntent, hresult result, ConfigurationUnitResultSource failurePoint, const ProcessingSummaryForIntent& assertSummary, const ProcessingSummaryForIntent& informSummary, const ProcessingSummaryForIntent& applySummary) const noexcept; // Logs a processing summary event for a configuration set test run. void LogConfigProcessingSummaryForTest( const ConfigurationSet& configurationSet, const TestConfigurationSetResult& result) const noexcept; // Logs a processing summary event for a configuration set test run exception. void LogConfigProcessingSummaryForTestException( const ConfigurationSet& configurationSet, hresult error, const TestConfigurationSetResult& result) const noexcept; // Logs a processing summary event for a configuration set apply run. void LogConfigProcessingSummaryForApply( const ConfigurationSet& configurationSet, const ApplyConfigurationSetResult& result) const noexcept; // Logs a processing summary event for a configuration set apply run exception. void LogConfigProcessingSummaryForApplyException( const ConfigurationSet& configurationSet, hresult error, const ApplyConfigurationSetResult& result) const noexcept; protected: bool IsTelemetryEnabled() const noexcept; CopyConstructibleAtomic m_isRuntimeEnabled{ true }; GUID m_activityId = GUID_NULL; std::string m_version; std::string m_caller; }; } ================================================ FILE: src/Microsoft.Management.Configuration/Telemetry/TraceLogging.cpp ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. #include "pch.h" #include "TraceLogging.h" // GUID for Microsoft.Management.Configuration : {9be929c4-3582-4629-aaa2-f427a5032b33} TRACELOGGING_DEFINE_PROVIDER( g_hTraceProvider, "Microsoft.Management.Configuration", (0x9be929c4, 0x3582, 0x4629, 0xaa, 0xa2, 0xf4, 0x27, 0xa5, 0x03, 0x2b, 0x33), TraceLoggingOptionMicrosoftTelemetry()); bool g_IsTelemetryProviderEnabled{}; UCHAR g_TelemetryProviderLevel{}; ULONGLONG g_TelemetryProviderMatchAnyKeyword{}; struct TraceProvider { TraceProvider(); ~TraceProvider(); }; TraceProvider g_TraceProvider{}; void WINAPI TelemetryProviderEnabledCallback( _In_ LPCGUID /*sourceId*/, _In_ ULONG isEnabled, _In_ UCHAR level, _In_ ULONGLONG matchAnyKeyword, _In_ ULONGLONG /*matchAllKeywords*/, _In_opt_ PEVENT_FILTER_DESCRIPTOR /*filterData*/, _In_opt_ PVOID /*callbackContext*/) { g_IsTelemetryProviderEnabled = !!isEnabled; g_TelemetryProviderLevel = level; g_TelemetryProviderMatchAnyKeyword = matchAnyKeyword; } TraceProvider::TraceProvider() { TraceLoggingRegisterEx(g_hTraceProvider, TelemetryProviderEnabledCallback, nullptr); } TraceProvider::~TraceProvider() { TraceLoggingUnregister(g_hTraceProvider); } ================================================ FILE: src/Microsoft.Management.Configuration/Telemetry/TraceLogging.h ================================================ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. #pragma once #include #include #include // Keywords #define KEYWORD_REPEATER 0x0000000000000001 #define KEYWORD_SCROLLER 0x0000000000000002 #define KEYWORD_PTR 0x0000000000000004 #define KEYWORD_SCROLLVIEWER 0x0000000000000008 #define KEYWORD_SWIPECONTROL 0x0000000000000010 #define KEYWORD_COMMANDBARFLYOUT 0x0000000000000020 // Common output formats #define TRACE_MSG_METH L"%s[0x%p]()\n" #define TRACE_MSG_METH_DBL L"%s[0x%p](%lf)\n" #define TRACE_MSG_METH_DBL_DBL L"%s[0x%p](%lf, %lf)\n" #define TRACE_MSG_METH_DBL_INT L"%s[0x%p](%lf, %d)\n" #define TRACE_MSG_METH_DBL_DBL_INT L"%s[0x%p](%lf, %lf, %d)\n" #define TRACE_MSG_METH_DBL_DBL_FLT L"%s[0x%p](%lf, %lf, %f)\n" #define TRACE_MSG_METH_DBL_DBL_STR L"%s[0x%p](%lf, %lf, %s)\n" #define TRACE_MSG_METH_FLT L"%s[0x%p](%f)\n" #define TRACE_MSG_METH_FLT_FLT L"%s[0x%p](%f, %f)\n" #define TRACE_MSG_METH_FLT_FLT_FLT L"%s[0x%p](%f, %f, %f)\n" #define TRACE_MSG_METH_FLT_FLT_FLT_FLT L"%s[0x%p](%f, %f, %f, %f)\n" #define TRACE_MSG_METH_FLT_FLT_STR_INT L"%s[0x%p](%f, %f, %s, %d)\n" #define TRACE_MSG_METH_INT L"%s[0x%p](%d)\n" #define TRACE_MSG_METH_INT_INT L"%s[0x%p](%d, %d)\n" #define TRACE_MSG_METH_PTR L"%s[0x%p](0x%p)\n" #define TRACE_MSG_METH_PTR_PTR L"%s[0x%p](0x%p, 0x%p)\n" #define TRACE_MSG_METH_PTR_DBL L"%s[0x%p](0x%p, %lf)\n" #define TRACE_MSG_METH_PTR_INT L"%s[0x%p](0x%p, %d)\n" #define TRACE_MSG_METH_PTR_STR L"%s[0x%p](0x%p, %s)\n" #define TRACE_MSG_METH_STR L"%s[0x%p](%s)\n" #define TRACE_MSG_METH_STR_STR L"%s[0x%p](%s, %s)\n" #define TRACE_MSG_METH_STR_DBL L"%s[0x%p](%s, %lf)\n" #define TRACE_MSG_METH_STR_FLT L"%s[0x%p](%s, %f)\n" #define TRACE_MSG_METH_STR_INT L"%s[0x%p](%s, %d)\n" #define TRACE_MSG_METH_STR_STR_STR L"%s[0x%p](%s, %s, %s)\n" #define TRACE_MSG_METH_STR_INT_INT L"%s[0x%p](%s, %d, %d)\n" #define TRACE_MSG_METH_STR_FLT_FLT L"%s[0x%p](%s, %f, %f)\n" #define TRACE_MSG_METH_STR_STR_FLT L"%s[0x%p](%s, %s, %f)\n" #define TRACE_MSG_METH_STR_STR_INT_INT L"%s[0x%p](%s, %s, %d, %d)\n" #define TRACE_MSG_METH_METH L"%s[0x%p] - calls %s()\n" #define TRACE_MSG_METH_METH_INT L"%s[0x%p] - calls %s(%d)\n" #define TRACE_MSG_METH_METH_STR L"%s[0x%p] - calls %s(%s)\n" #define TRACE_MSG_METH_METH_STR_STR L"%s[0x%p] - calls %s(%s, %s)\n" #define TRACE_MSG_METH_METH_FLT_STR L"%s[0x%p] - calls %s(%f, %s)\n" #define TRACE_MSG_METH_METH_FLT_FLT_FLT L"%s[0x%p] - calls %s(%f, %f, %f)\n" // Current method name #define METH_NAME StringUtil::Utf8ToUtf16(__FUNCTION__).c_str() // TraceLogging provider name for telemetry. #define TELEMETRY_PROVIDER_NAME "Microsoft.Management.Configuration" TRACELOGGING_DECLARE_PROVIDER(g_hTraceProvider); extern bool g_IsTelemetryProviderEnabled; extern UCHAR g_TelemetryProviderLevel; extern ULONGLONG g_TelemetryProviderMatchAnyKeyword; ================================================ FILE: src/Microsoft.Management.Configuration/TestConfigurationSetResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestConfigurationSetResult.h" #include "TestConfigurationSetResult.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { TestConfigurationSetResult::TestConfigurationSetResult() : m_unitResults(multi_threaded_vector()) { } void TestConfigurationSetResult::AppendUnitResult(const TestConfigurationUnitResult& unitResult) { m_unitResults.Append(unitResult); // Also aggregate the result of this incoming test into the overall result m_testResult = FoldInTestResult(m_testResult, unitResult.TestResult()); } ConfigurationTestResult TestConfigurationSetResult::FoldInTestResult(ConfigurationTestResult current, ConfigurationTestResult incoming) { switch (current) { case ConfigurationTestResult::Unknown: case ConfigurationTestResult::NotRun: // In these "default" cases, just take the unit result return incoming; break; case ConfigurationTestResult::Positive: if (incoming == ConfigurationTestResult::Negative || incoming == ConfigurationTestResult::Failed) { return incoming; } break; case ConfigurationTestResult::Negative: if (incoming == ConfigurationTestResult::Failed) { return incoming; } break; case ConfigurationTestResult::Failed: // If a unit failed, the set failed break; default: THROW_HR(E_UNEXPECTED); } return current; } Windows::Foundation::Collections::IVectorView TestConfigurationSetResult::UnitResults() const { return m_unitResults.GetView(); } ConfigurationTestResult TestConfigurationSetResult::TestResult() const { return m_testResult; } void TestConfigurationSetResult::TestResult(ConfigurationTestResult value) { m_testResult = value; } } ================================================ FILE: src/Microsoft.Management.Configuration/TestConfigurationSetResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "TestConfigurationSetResult.g.h" #include namespace winrt::Microsoft::Management::Configuration::implementation { struct TestConfigurationSetResult : TestConfigurationSetResultT { TestConfigurationSetResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void AppendUnitResult(const TestConfigurationUnitResult& unitResult); void TestResult(ConfigurationTestResult value); static ConfigurationTestResult FoldInTestResult(ConfigurationTestResult current, ConfigurationTestResult incoming); #endif Windows::Foundation::Collections::IVectorView UnitResults() const; ConfigurationTestResult TestResult() const; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: Windows::Foundation::Collections::IVector m_unitResults = nullptr; ConfigurationTestResult m_testResult = ConfigurationTestResult::Unknown; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/TestConfigurationUnitResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestConfigurationUnitResult.h" #include "TestConfigurationUnitResult.g.cpp" namespace winrt::Microsoft::Management::Configuration::implementation { void TestConfigurationUnitResult::Initialize(const ITestSettingsResult& result) { m_unit = result.Unit(); THROW_HR_IF(E_POINTER, !m_unit); m_testResult = result.TestResult(); m_resultInformation = result.ResultInformation(); THROW_HR_IF(E_POINTER, !m_resultInformation); } void TestConfigurationUnitResult::Unit(const ConfigurationUnit& unit) { m_unit = unit; } ConfigurationUnit TestConfigurationUnitResult::Unit() { return m_unit; } IConfigurationUnitResultInformation TestConfigurationUnitResult::ResultInformation() { return m_resultInformation; } void TestConfigurationUnitResult::ResultInformation(const IConfigurationUnitResultInformation& value) { m_resultInformation = value; } ConfigurationTestResult TestConfigurationUnitResult::TestResult() { return m_testResult; } void TestConfigurationUnitResult::TestResult(ConfigurationTestResult value) { m_testResult = value; } } ================================================ FILE: src/Microsoft.Management.Configuration/TestConfigurationUnitResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "TestConfigurationUnitResult.g.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct TestConfigurationUnitResult : TestConfigurationUnitResultT { using ConfigurationUnit = Configuration::ConfigurationUnit; TestConfigurationUnitResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(const ITestSettingsResult& result); void Unit(const ConfigurationUnit& unit); void ResultInformation(const IConfigurationUnitResultInformation& value); void TestResult(ConfigurationTestResult value); #endif ConfigurationUnit Unit(); IConfigurationUnitResultInformation ResultInformation(); ConfigurationTestResult TestResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ConfigurationUnit m_unit = nullptr; IConfigurationUnitResultInformation m_resultInformation; ConfigurationTestResult m_testResult = ConfigurationTestResult::Unknown; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/TestGroupSettingsResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestGroupSettingsResult.h" #include "TestConfigurationSetResult.h" namespace winrt::Microsoft::Management::Configuration::implementation { TestGroupSettingsResult::TestGroupSettingsResult() : m_resultInformation(make_self>()), m_unitResults(winrt::multi_threaded_vector()) {} void TestGroupSettingsResult::Group(const Windows::Foundation::IInspectable& value) { m_group = value; } void TestGroupSettingsResult::TestResult(ConfigurationTestResult value) { m_testResult = value; } TestGroupSettingsResult::ResultInformationPtr TestGroupSettingsResult::ResultInformationInternal() { return m_resultInformation; } void TestGroupSettingsResult::AppendUnitResult(const ITestSettingsResult& value) { m_unitResults.Append(value); // Also aggregate the result of this incoming test into the overall result m_testResult = TestConfigurationSetResult::FoldInTestResult(m_testResult, value.TestResult()); } Windows::Foundation::IInspectable TestGroupSettingsResult::Group() { return m_group; } ConfigurationTestResult TestGroupSettingsResult::TestResult() { return m_testResult; } IConfigurationUnitResultInformation TestGroupSettingsResult::ResultInformation() { return *m_resultInformation; } Windows::Foundation::Collections::IVector TestGroupSettingsResult::UnitResults() { return m_unitResults; } } ================================================ FILE: src/Microsoft.Management.Configuration/TestGroupSettingsResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" #include "ConfigurationUnitResultInformation.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct TestGroupSettingsResult : winrt::implements { TestGroupSettingsResult(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) using ResultInformationPtr = decltype(make_self>()); void Group(const Windows::Foundation::IInspectable& value); void TestResult(ConfigurationTestResult value); ResultInformationPtr ResultInformationInternal(); void AppendUnitResult(const ITestSettingsResult& value); #endif Windows::Foundation::IInspectable Group(); ConfigurationTestResult TestResult(); IConfigurationUnitResultInformation ResultInformation(); Windows::Foundation::Collections::IVector UnitResults(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: Windows::Foundation::IInspectable m_group; ConfigurationTestResult m_testResult = ConfigurationTestResult::Unknown; ResultInformationPtr m_resultInformation; Windows::Foundation::Collections::IVector m_unitResults; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/TestSettingsResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "TestSettingsResult.h" #include "ConfigurationUnitResultInformation.h" namespace winrt::Microsoft::Management::Configuration::implementation { void TestSettingsResult::Unit(const ConfigurationUnit& value) { m_unit = value; } void TestSettingsResult::TestResult(ConfigurationTestResult value) { m_testResult = value; } void TestSettingsResult::ResultInformation(const IConfigurationUnitResultInformation& value) { m_resultInformation = value; } ConfigurationUnit TestSettingsResult::Unit() { return m_unit; } ConfigurationTestResult TestSettingsResult::TestResult() { return m_testResult; } IConfigurationUnitResultInformation TestSettingsResult::ResultInformation() { return m_resultInformation; } } ================================================ FILE: src/Microsoft.Management.Configuration/TestSettingsResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "winrt/Microsoft.Management.Configuration.h" namespace winrt::Microsoft::Management::Configuration::implementation { struct TestSettingsResult : winrt::implements { using ConfigurationUnit = Configuration::ConfigurationUnit; TestSettingsResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Unit(const ConfigurationUnit& value); void TestResult(ConfigurationTestResult value); void ResultInformation(const IConfigurationUnitResultInformation& value); #endif ConfigurationUnit Unit(); ConfigurationTestResult TestResult(); IConfigurationUnitResultInformation ResultInformation(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ConfigurationUnit m_unit = nullptr; ConfigurationTestResult m_testResult = ConfigurationTestResult::Unknown; IConfigurationUnitResultInformation m_resultInformation; #endif }; } ================================================ FILE: src/Microsoft.Management.Configuration/packages.config ================================================  ================================================ FILE: src/Microsoft.Management.Configuration/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/Microsoft.Management.Configuration/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include #include #include #include #pragma warning( push ) #pragma warning ( disable : 4324 4467 6388 ) // 4324 Structure was padded due to alignment specifier // 4467 Allow use of uuid attribute for com object creation. // 6388 Allow CreateInstance. #include #include #pragma warning( pop ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/Collect-ConfigurationOOPTests.ps1 ================================================ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. [CmdletBinding()] param( [string]$TargetLocation ) $Local:settingsExport = ConvertFrom-Json (wingetdev.exe settings export) $Local:logsFilePath = Join-Path (Split-Path $Local:settingsExport.userSettingsFile -Parent) "DiagOutputDir" Get-AppxPackage WinGetDevCLI | Remove-AppxPackage Copy-Item $Local:logsFilePath $TargetLocation -Recurse -Force -ErrorAction Ignore ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/Factory.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Factory.h" #include #include #include namespace Microsoft::Management::Configuration::OutOfProc { namespace { const CLSID& GetConfigurationStaticsCLSID() { #if USE_PROD_CLSIDS static const CLSID CLSID_ConfigurationStatics = { 0x73d763b7,0x2937,0x432f,{0xa9,0x7a,0xd9,0x8a,0x4a,0x59,0x61,0x26} }; // 73D763B7-2937-432F-A97A-D98A4A596126 #else static const CLSID CLSID_ConfigurationStatics = { 0xc9ed7917,0x66ab,0x4e31,{0xa9,0x2a,0xf6,0x5f,0x18,0xef,0x79,0x33} }; // C9ED7917-66AB-4E31-A92A-F65F18EF7933 #endif return CLSID_ConfigurationStatics; } winrt::Microsoft::Management::Configuration::IConfigurationStatics CreateOOPStaticsObject() { bool isAdmin = AppInstaller::Runtime::IsRunningAsAdmin(); try { return winrt::create_instance(GetConfigurationStaticsCLSID(), CLSCTX_LOCAL_SERVER | CLSCTX_NO_CODE_DOWNLOAD); } catch (const winrt::hresult_error& hre) { // We only want to fall through to trying the manual activation if we are running as admin and couldn't find the registration. if (!(isAdmin && hre.code() == REGDB_E_CLASSNOTREG)) { throw; } } winrt::com_ptr<::IUnknown> result; THROW_IF_FAILED(WinGetServerManualActivation_CreateInstance(GetConfigurationStaticsCLSID(), winrt::guid_of(), 0, result.put_void())); return result.as(); } } Factory::Factory() { IncrementRefCount(); } Factory::~Factory() { DecrementRefCount(); } bool Factory::HasReferences() { return s_referenceCount.load() != 0; } void Factory::Terminate() { WinGetServerManualActivation_Terminate(); } bool Factory::IsCLSID(const GUID& clsid) { if (clsid == GetConfigurationStaticsCLSID()) { return true; } return false; } bool Factory::IsCLSID(HSTRING clsid) { constexpr std::wstring_view s_ClassName = L"Microsoft.Management.Configuration.ConfigurationStaticFunctions"; UINT32 length = 0; PCWSTR buffer = WindowsGetStringRawBuffer(clsid, &length); if (std::wstring_view{ buffer, length } == s_ClassName) { return true; } return false; } winrt::Windows::Foundation::IInspectable Factory::ActivateInstance() { return CreateOOPStaticsObject().as(); } HRESULT STDMETHODCALLTYPE Factory::CreateInstance(::IUnknown* pUnkOuter, REFIID riid, void** ppvObject) try { RETURN_HR_IF(E_POINTER, !ppvObject); *ppvObject = nullptr; RETURN_HR_IF(CLASS_E_NOAGGREGATION, pUnkOuter != nullptr); return CreateOOPStaticsObject().as(riid, ppvObject); } CATCH_RETURN(); HRESULT STDMETHODCALLTYPE Factory::LockServer(BOOL fLock) { if (fLock) { IncrementRefCount(); } else { DecrementRefCount(); } return S_OK; } void Factory::IncrementRefCount() { ++s_referenceCount; } void Factory::DecrementRefCount() { --s_referenceCount; } std::atomic Factory::s_referenceCount = ATOMIC_VAR_INIT(0); } ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/Factory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace Microsoft::Management::Configuration::OutOfProc { struct Factory : winrt::implements { Factory(); ~Factory(); // Returns true if the reference count is not 0; false if it is. static bool HasReferences(); // Forcibly destroys any static objects. static void Terminate(); // Determines if the given CLSID is the CLSID for the factory. static bool IsCLSID(const GUID& clsid); // Determines if the given CLSID is the CLSID for the factory. static bool IsCLSID(HSTRING clsid); // IActivationFactory winrt::Windows::Foundation::IInspectable ActivateInstance(); // IClassFactory HRESULT STDMETHODCALLTYPE CreateInstance(::IUnknown *pUnkOuter, REFIID riid, void **ppvObject); HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock); private: static void IncrementRefCount(); static void DecrementRefCount(); static std::atomic s_referenceCount; }; } ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj ================================================ true true true 15.0 Win32Proj MicrosoftManagementConfigurationOutOfProc 10.0.26100.0 10.0.17763.0 true false {2268D5AD-7F2A-485A-8C4B-C574497514C9} Debug ARM64 Debug Win32 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Debug x64 Release x64 DynamicLibrary true true false true false false true false Spectre Spectre Spectre Spectre Spectre Spectre true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING Disabled _DEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) true true false false stdcpp17 stdcpp17 true true true true 6001 6001 false false false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) WIN32;%(PreprocessorDefinitions) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) true false stdcpp17 true true 6001 false $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) true MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) true true true Guard Guard Guard stdcpp17 stdcpp17 stdcpp17 true true true false false false 6001 6001 6001 false false false true true false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) true true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) true true true Guard Guard Guard stdcpp17 stdcpp17 stdcpp17 true true true false false false MultiThreaded MultiThreaded MultiThreaded 6001 6001 6001 false false false true true false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) true true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing Create {f3f6e699-bc5d-4950-8a05-e49dd9eb0d51} {ca460806-5e41-4e97-9a3d-1d74b433b663} This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {6017fd94-3eb1-40bc-964f-5dd571077d3c} Header Files Header Files Source Files Source Files Source Files WinGetServerManualActivation WinGetServerManualActivation WinGetServerManualActivation Source Files ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/Prepare-ConfigurationOOPTests.ps1 ================================================ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. [CmdletBinding()] param( [string]$BuildOutputPath, [string]$PackageLayoutPath ) # Copy the winmd into the unit test directory since it will be needed for marshalling $Local:winmdSourcePath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration\Microsoft.Management.Configuration.winmd" $Local:winmdTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net8.0-windows10.0.26100.0\Microsoft.Management.Configuration.winmd" Copy-Item $Local:winmdSourcePath $Local:winmdTargetPath -Force # Copy the OOP helper dll into the unit test directory to make activation look the same as in-proc $Local:dllSourcePath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.OutOfProc\Microsoft.Management.Configuration.OutOfProc.dll" $Local:dllTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net8.0-windows10.0.26100.0\Microsoft.Management.Configuration.dll" Copy-Item $Local:dllSourcePath $Local:dllTargetPath -Force # Register the package if (-not [System.String]::IsNullOrEmpty($PackageLayoutPath)) { $Local:packageManifestPath = Join-Path $PackageLayoutPath "AppxManifest.xml" Add-AppxPackage -ForceApplicationShutdown -Register $Local:packageManifestPath # Configure crash dump and log file settings $Local:settingsExport = ConvertFrom-Json (wingetdev.exe settings export) $Local:settingsFilePath = $Local:settingsExport.userSettingsFile $Local:settingsFileContent = ConvertTo-Json @{ debugging= @{ enableSelfInitiatedMinidump=$true ; keepAllLogFiles=$true } ; experimentalFeatures= @{ configuration03=$true } } Set-Content -Path $Local:settingsFilePath -Value $Local:settingsFileContent } ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/PropertySheet.props ================================================ ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/Source.def ================================================ EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllGetActivationFactory PRIVATE ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/dllmain.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Factory.h" #include using namespace Microsoft::Management::Configuration::OutOfProc; EXTERN_C BOOL WINAPI DllMain( HMODULE /* hModule */, DWORD reason, LPVOID /* lpReserved */) { switch (reason) { case DLL_PROCESS_DETACH: Factory::Terminate(); break; } return TRUE; } _Check_return_ STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) try { RETURN_HR_IF(E_POINTER, !ppv); *ppv = nullptr; winrt::Windows::Foundation::IUnknown result; if (Factory::IsCLSID(rclsid)) { result = winrt::make().as(); } if (result) { return result.as(riid, ppv); } return REGDB_E_CLASSNOTREG; } CATCH_RETURN(); __control_entrypoint(DllExport) STDAPI DllCanUnloadNow() { return Factory::HasReferences() ? S_FALSE : S_OK; } STDAPI DllGetActivationFactory(HSTRING classId, void** factory) try { RETURN_HR_IF(E_POINTER, !factory); *factory = nullptr; winrt::Windows::Foundation::IUnknown result; if (Factory::IsCLSID(classId)) { result = winrt::make().as(); } if (result) { return result.as(winrt::guid_of(), factory); } return REGDB_E_CLASSNOTREG; } CATCH_RETURN(); ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/packages.config ================================================  ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/Microsoft.Management.Configuration.OutOfProc/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Constants/DirectiveConstants.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Constants { /// /// Directives. /// internal static class DirectiveConstants { #pragma warning disable SA1600 // ElementsMustBeDocumented public const string MaxVersion = "maxVersion"; public const string MinVersion = "minVersion"; public const string Module = "module"; public const string ModuleGuid = "moduleGuid"; public const string Repository = "repository"; public const string Version = "version"; public const string AllowPrerelease = "allowPrerelease"; #pragma warning restore SA1600 // ElementsMustBeDocumented } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/FindDscPackageStateMachine.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { using System; /// /// Provides the state machine that decides which DSC package to use. /// internal class FindDscPackageStateMachine { private const string StableDscPackageFamilyName = "Microsoft.DesiredStateConfiguration_8wekyb3d8bbwe"; private const string PreviewDscPackageFamilyName = "Microsoft.DesiredStateConfiguration-Preview_8wekyb3d8bbwe"; private readonly Version minimumStableVersion = new Version(3, 1); private readonly Version minimumPreviewVersion = new Version(3, 1, 7); private State currentState = State.Initial; private string? dscExecutablePath; /// /// A state of the state machine. /// public enum State { /// /// The initial state. /// Initial, /// /// A stable installation attempt has been made. /// StableInstallAttempted, /// /// A preview installation attempt has been made. /// PreviewInstallAttempted, /// /// The state machine is terminated. /// Terminated, } /// /// A transition of the state machine. /// public enum Transition { /// /// Transition to a terminated state with DSC being found. /// Found, /// /// Attempt to install the stable version of DSC. /// InstallStable, /// /// Attempt to install the preview version of DSC. /// InstallPreview, /// /// Transition to a terminated state with DSC *not* being found. /// NotFound, } /// /// Gets the file path of the DSC (Desired State Configuration) executable. /// public string? DscExecutablePath { get { if (this.currentState == State.Terminated) { return this.dscExecutablePath; } else { PackageInformation stableInformation = new PackageInformation(StableDscPackageFamilyName); if (stableInformation.IsInstalled && stableInformation.Version >= this.minimumStableVersion) { return stableInformation.AliasPath; } else { PackageInformation previewInformation = new PackageInformation(PreviewDscPackageFamilyName); if (previewInformation.IsInstalled && previewInformation.Version >= this.minimumPreviewVersion) { return previewInformation.AliasPath; } else { return null; } } } } } /// /// Determines the next state transition based on the current context or conditions. /// /// /// A string representing the name of the next transition. /// public Transition DetermineNextTransition() { switch (this.currentState) { case State.Initial: { PackageInformation stableInformation = new PackageInformation(StableDscPackageFamilyName); if (stableInformation.IsInstalled && stableInformation.Version >= this.minimumStableVersion) { return this.Found(stableInformation); } else { this.currentState = State.StableInstallAttempted; return Transition.InstallStable; } } case State.StableInstallAttempted: { PackageInformation stableInformation = new PackageInformation(StableDscPackageFamilyName); if (stableInformation.IsInstalled && stableInformation.Version >= this.minimumStableVersion) { return this.Found(stableInformation); } else { PackageInformation previewInformation = new PackageInformation(PreviewDscPackageFamilyName); if (previewInformation.IsInstalled && previewInformation.Version >= this.minimumPreviewVersion) { return this.Found(previewInformation); } else { this.currentState = State.PreviewInstallAttempted; return Transition.InstallPreview; } } } case State.PreviewInstallAttempted: { PackageInformation previewInformation = new PackageInformation(PreviewDscPackageFamilyName); if (previewInformation.IsInstalled && previewInformation.Version >= this.minimumPreviewVersion) { return this.Found(previewInformation); } else { this.currentState = State.Terminated; return Transition.NotFound; } } case State.Terminated: return this.DscExecutablePath == null ? Transition.NotFound : Transition.Found; default: throw new InvalidOperationException($"Unexpected state: {this.currentState}"); } } private Transition Found(PackageInformation packageInformation) { this.dscExecutablePath = packageInformation.AliasPath; this.currentState = State.Terminated; return Transition.Found; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/IDiagnosticsSink.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { /// /// Defines the interface for a diagnostics sink. /// internal interface IDiagnosticsSink { /// /// Sends a diagnostic message. /// /// The level of the message. /// The message. public void OnDiagnostics(DiagnosticLevel level, string message); } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/PackageInformation.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { using System; using System.IO; using Windows.Management.Deployment; /// /// Contains information about a package. /// internal class PackageInformation { private const string DscExecutableFileName = "dsc.exe"; /// /// Initializes a new instance of the class. /// /// The package family name. public PackageInformation(string familyName) { PackageManager packageManager = new PackageManager(); var packages = packageManager.FindPackagesForUserWithPackageTypes(null, familyName, PackageTypes.Main); if (packages != null) { foreach (var package in packages) { var packageVersion = package.Id.Version; Version version = new Version(packageVersion.Major, packageVersion.Minor, packageVersion.Build, packageVersion.Revision); if (this.Version == null || version > this.Version) { this.Version = version; } } } string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); string aliasPath = Path.Combine(localAppData, "Microsoft\\WindowsApps", familyName, DscExecutableFileName); if (Path.Exists(aliasPath)) { this.AliasPath = aliasPath; } if (this.AliasPath != null && this.Version != null) { this.IsInstalled = true; } } /// /// Gets a value indicating whether the package is installed or not. /// public bool IsInstalled { get; private set; } /// /// Gets the path to the dsc.exe alias. /// public string? AliasPath { get; private set; } /// /// Gets the version of the package. /// public Version? Version { get; private set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecution.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Threading; using Microsoft.Management.Configuration.Processor.Helpers; /// /// Wrapper for a single process execution and its output. /// internal class ProcessExecution { private List outputLines = new List(); private List errorLines = new List(); /// /// Initializes a new instance of the class. /// public ProcessExecution() { } /// /// An event that receives the output lines as they are delivered. /// public event EventHandler? OutputLineReceived; /// /// An event that receives the error lines as they are delivered. /// public event EventHandler? ErrorLineReceived; /// /// Gets the executable path. /// required public string ExecutablePath { get; init; } /// /// Gets the arguments to use for the process. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public IEnumerable Arguments { get; init; } = []; /// /// Gets the data to write to standard input of the process. /// public string? Input { get; init; } = null; /// /// Gets the list of custom environment variables to use for the process. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public IEnumerable EnvironmentVariables { get; init; } = []; /// /// Gets the argument string passed to the process. /// public string SerializedArguments { get { StringBuilder processArguments = new StringBuilder(); foreach (string arg in this.Arguments) { if (processArguments.Length != 0) { processArguments.Append(' '); } processArguments.Append(arg); } return processArguments.ToString(); } } /// /// Gets the full command line that the process should see. /// public string CommandLine { get { return $"{this.ExecutablePath} {this.SerializedArguments}"; } } /// /// Gets the current set of output lines. /// Not thread safe, use OutputLineReceived for async flows. /// public IReadOnlyCollection Output { get { return this.outputLines; } } /// /// Gets the current set of error lines. /// Not thread safe, use ErrorLineReceived for async flows. /// public IReadOnlyCollection Error { get { return this.errorLines; } } /// /// Gets the exit code of the process. /// Will be null until the process exits. /// public int? ExitCode { get; private set; } = null; /// /// Gets or sets the process object; null until Start called. /// private Process? Process { get; set; } /// /// Starts the process. /// /// This object. /// Thrown if Start has already been called. public ProcessExecution Start() { if (this.Process != null) { throw new InvalidOperationException("Process has already been started."); } ProcessStartInfo startInfo; lock (PathEnvironmentVariableHandler.Lock) { startInfo = new ProcessStartInfo(this.ExecutablePath, this.SerializedArguments); } this.Process = new Process() { StartInfo = startInfo }; startInfo.UseShellExecute = false; startInfo.WindowStyle = ProcessWindowStyle.Hidden; startInfo.StandardOutputEncoding = Encoding.UTF8; startInfo.RedirectStandardOutput = true; this.Process.OutputDataReceived += (sender, args) => { string? output = args.Data; if (output != null) { this.outputLines.Add(output); this.OutputLineReceived?.Invoke(this, output); } }; startInfo.StandardErrorEncoding = Encoding.UTF8; startInfo.RedirectStandardError = true; this.Process.ErrorDataReceived += (sender, args) => { string? error = args.Data; if (error != null) { this.errorLines.Add(error); this.ErrorLineReceived?.Invoke(this, error); } }; if (this.Input != null) { startInfo.StandardInputEncoding = Encoding.UTF8; startInfo.RedirectStandardInput = true; } foreach (var env in this.EnvironmentVariables) { switch (env.ValueType) { case ProcessExecutionEnvironmentVariableValueType.Override: startInfo.EnvironmentVariables[env.Name] = env.Value; break; case ProcessExecutionEnvironmentVariableValueType.Prepend: startInfo.EnvironmentVariables[env.Name] = MergeStringsWithSeparator(env.Value, startInfo.EnvironmentVariables[env.Name] ?? string.Empty, env.Separator); break; case ProcessExecutionEnvironmentVariableValueType.Append: startInfo.EnvironmentVariables[env.Name] = MergeStringsWithSeparator(startInfo.EnvironmentVariables[env.Name] ?? string.Empty, env.Value, env.Separator); break; } } this.Process.Start(); this.Process.BeginOutputReadLine(); this.Process.BeginErrorReadLine(); if (this.Input != null) { this.Process.StandardInput.Write(this.Input); this.Process.StandardInput.Close(); } return this; } /// /// Waits for the process to exit. /// /// The minimum amount of time to wait for the process to exit, in milliseconds. /// True if the process exited; false if not. /// Thrown if Start has not been called. public bool WaitForExit(int milliseconds = Timeout.Infinite) { if (this.Process == null) { throw new InvalidOperationException("Process has not been started."); } if (this.Process.WaitForExit(milliseconds)) { // According to documentation, this extra call will ensure that the redirected streams have finished reading all of the data. this.Process.WaitForExit(); this.ExitCode = this.Process.ExitCode; return true; } else { return false; } } /// /// Gets all of the output lines as a single string. /// /// The output lines as a string. public string GetAllOutputLines() { return GetAllLines(this.outputLines); } /// /// Gets all of the error lines as a single string. /// /// The error lines as a string. public string GetAllErrorLines() { return GetAllLines(this.errorLines); } private static string GetAllLines(List lines) { StringBuilder stringBuilder = new StringBuilder(); foreach (string line in lines) { stringBuilder.AppendLine(line); } return stringBuilder.ToString(); } private static string MergeStringsWithSeparator(string first, string second, string separator) { if (string.IsNullOrEmpty(separator)) { return first + second; } else { if (first.EndsWith(separator) && second.StartsWith(separator)) { return first + second.Substring(separator.Length); } else if (first.EndsWith(separator) || second.StartsWith(separator)) { return first + second; } else { return first + separator + second; } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecutionEnvironmentVariable.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { /// /// Contains custom environment variable info for ProcessExecution. /// internal class ProcessExecutionEnvironmentVariable { /// /// Gets the name of the environment variable. /// required public string Name { get; init; } /// /// Gets the value of the environment variable. /// required public string Value { get; init; } /// /// Gets the value type of the environment variable. /// public ProcessExecutionEnvironmentVariableValueType ValueType { get; init; } = ProcessExecutionEnvironmentVariableValueType.Override; /// /// Gets the separator of the environment variable if value type is prepend or append. /// public string Separator { get; init; } = ";"; } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecutionEnvironmentVariableValueType.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { /// /// The environment variable value type. /// internal enum ProcessExecutionEnvironmentVariableValueType { /// /// Prepend. /// Prepend, /// /// Append. /// Append, /// /// Override. /// Override, } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessorRunSettings.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Microsoft.Management.Configuration.Processor.Helpers; /// /// Contains settings for the DSC v3 processor components to share. /// internal class ProcessorRunSettings { /// /// Gets the paths for finding DSC resources and executables. /// public string ResourceSearchPaths { get; private set; } = string.Empty; /// /// Gets a value indicating whether the resource search paths are exclusive. /// public bool ResourceSearchPathsExclusive { get; private set; } = false; /// /// Creates ProcessorRunSettings from FindUnitProcessorsOptions. /// /// The find unit processors options. /// A ProcessorRunSettings. public static ProcessorRunSettings CreateFromFindUnitProcessorsOptions(FindUnitProcessorsOptions findOptions) { return new ProcessorRunSettings { ResourceSearchPaths = findOptions.SearchPaths, ResourceSearchPathsExclusive = findOptions.SearchPathsExclusive, }; } /// /// Creates ProcessorRunSettings from a ResourceDetails. /// /// The resource details to be used. /// A ProcessorRunSettings. public static ProcessorRunSettings CreateFromResourceDetails(ResourceDetails? resourceDetails) { return new ProcessorRunSettings { ResourceSearchPaths = Path.GetDirectoryName(resourceDetails?.Path) ?? string.Empty, ResourceSearchPathsExclusive = false, }; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessorSettings.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { using System; using System.Collections.Generic; using System.IO; using System.Text; using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Microsoft.Management.Configuration.Processor.Helpers; /// /// Contains settings for the DSC v3 processor components to share. /// internal class ProcessorSettings { private readonly object dscV3Lock = new (); private readonly object defaultPathLock = new (); private FindDscPackageStateMachine dscPackageStateMachine = new (); private IDSCv3? dscV3 = null; private string? defaultPath = null; private Dictionary resourceDetailsDictionary = new (); /// /// Gets or sets the path to the DSC v3 executable. /// public string? DscExecutablePath { get; set; } /// /// Gets the path to the DSC v3 executable. /// public string EffectiveDscExecutablePath { get { if (this.DscExecutablePath != null) { return this.DscExecutablePath; } lock (this.defaultPathLock) { if (this.defaultPath != null) { return this.defaultPath; } } string? localDefaultPath = this.GetFoundDscExecutablePath(); if (localDefaultPath == null) { throw new FileNotFoundException("Could not find DSC v3 executable path."); } lock (this.defaultPathLock) { if (this.defaultPath == null) { this.defaultPath = localDefaultPath; } return this.defaultPath; } } } /// /// Gets an object for interacting with the DSC executable at EffectiveDscExecutablePath. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation should match accessors", Justification = "Set is only provided for tests.")] public IDSCv3 DSCv3 { get { lock (this.dscV3Lock) { if (this.dscV3 == null) { this.dscV3 = IDSCv3.Create(this); } return this.dscV3; } } #if !AICLI_DISABLE_TEST_HOOKS set { lock (this.dscV3Lock) { this.dscV3 = value; } } #endif } /// /// Gets or sets the diagnostics sink to use. /// public IDiagnosticsSink? DiagnosticsSink { get; set; } = null; /// /// Gets or sets a value indicating whether the processor should produce more verbose output. /// public bool DiagnosticTraceEnabled { get; set; } = false; /// /// Find the DSC v3 executable. /// /// The full path to the dsc.exe executable, or null if not found. public string? GetFoundDscExecutablePath() { return this.dscPackageStateMachine.DscExecutablePath; } /// /// Invokes a step in the DSC search state machine. /// /// The transition to take in the state machine. public FindDscPackageStateMachine.Transition PumpFindDscStateMachine() { return this.dscPackageStateMachine.DetermineNextTransition(); } /// /// Create a deep copy of this settings object. /// /// A deep copy of this object. public ProcessorSettings Clone() { ProcessorSettings result = new ProcessorSettings(); result.resourceDetailsDictionary = this.resourceDetailsDictionary; result.DiagnosticsSink = this.DiagnosticsSink; result.DscExecutablePath = this.DscExecutablePath; result.DiagnosticTraceEnabled = this.DiagnosticTraceEnabled; #if !AICLI_DISABLE_TEST_HOOKS result.dscV3 = this.DSCv3; #endif return result; } /// /// Gets a string representation of this object. /// /// A string representation of this object. public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("EffectiveDscExecutablePath: "); sb.AppendLine(this.EffectiveDscExecutablePath); sb.Append("DiagnosticTraceLevel: "); sb.Append(this.DiagnosticTraceEnabled); return sb.ToString(); } /// /// Gets the ResourceDetails for a configuration unit. /// /// The configuration unit to find details for. /// The level of detail to get. /// The ResourceDetails for the unit, or null if not found. public ResourceDetails? GetResourceDetails(ConfigurationUnitInternal configurationUnitInternal, ConfigurationUnitDetailFlags detailFlags) { ResourceDetails? result = null; bool inDictionary = false; lock (this.resourceDetailsDictionary) { inDictionary = this.resourceDetailsDictionary.TryGetValue(configurationUnitInternal.QualifiedName, out result); } if (result == null) { result = new ResourceDetails(configurationUnitInternal.QualifiedName); } result.EnsureDetails(this, detailFlags); if (result.Exists) { if (!inDictionary) { lock (this.resourceDetailsDictionary) { this.resourceDetailsDictionary.Add(configurationUnitInternal.QualifiedName, result); } } return result; } else { return null; } } /// /// Gets all ResourceDetails matching find options. /// /// The find options. /// A list of ResourceDetails. public List FindAllResourceDetails(FindUnitProcessorsOptions findOptions) { List result = new List(); var resourceItemList = this.DSCv3.GetAllResources(ProcessorRunSettings.CreateFromFindUnitProcessorsOptions(findOptions)); foreach (var item in resourceItemList) { ResourceDetails? details = null; bool inDictionary = false; lock (this.resourceDetailsDictionary) { inDictionary = this.resourceDetailsDictionary.TryGetValue(item.Type, out details); } if (details == null) { details = new ResourceDetails(item.Type); } if (!details.Exists) { details.SetResourceListItem(item); } details.EnsureDetails(this, findOptions.UnitDetailFlags); if (!inDictionary) { lock (this.resourceDetailsDictionary) { this.resourceDetailsDictionary.Add(item.Type, details); } } result.Add(details); } return result; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ResourceDetails.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers { using System; using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.Unit; /// /// Cached data about a resource. /// internal class ResourceDetails { private readonly string resourceTypeName; private object detailsUpdateLock = new object(); private IResourceListItem? resourceListItem = null; /// /// The current level of detail stored by this object. /// /// The method of discovery for each of the levels in ConfigurationUnitDetailFlags: /// None: No details, either because the resource was not found or EnsureDetails has not been called. /// Local: `resource list` is used to determine the details. An embedded schema may enable "Load" details level. /// Property information may not be available at this level. /// Catalog: Same as local; there is currently no catalog to query against. /// Download: Same as local; there is currently no catalog to find anything to download. /// Load: `resource schema` is used to get the full schema for the resource. /// This ensures that property information is available. /// private ConfigurationUnitDetailFlags currentDetailLevel = ConfigurationUnitDetailFlags.None; /// /// Initializes a new instance of the class. /// /// The resource type name. public ResourceDetails(string resourceTypeName) { this.resourceTypeName = resourceTypeName; } /// /// Gets a value indicating whether this resource exists. /// Will be false until EnsureDetails is called and the resource is found. /// public bool Exists { get { lock (this.detailsUpdateLock) { return this.currentDetailLevel != ConfigurationUnitDetailFlags.None; } } } /// /// Gets the path of the resource. /// public string? Path { get { lock (this.detailsUpdateLock) { return this.resourceListItem?.Path; } } } /// /// Sets the resource list item directly to avoid duplicate "dsc resource list" calls. /// /// The resource list item. public void SetResourceListItem(IResourceListItem item) { lock (this.detailsUpdateLock) { if (this.resourceListItem != null) { throw new InvalidOperationException("Resource list item is already set"); } this.resourceListItem = item; this.currentDetailLevel |= ConfigurationUnitDetailFlags.Local; } } /// /// Ensures that the given detail level is present. /// /// The processor settings to use when getting details. /// The detail level flags. public void EnsureDetails(ProcessorSettings processorSettings, ConfigurationUnitDetailFlags detailFlags) { if (this.DetailsNeededFor(detailFlags, ConfigurationUnitDetailFlags.Local)) { // If we can't get local details, then exit until we have more options. if (!this.GetLocalDetails(processorSettings)) { return; } } if (this.DetailsNeededFor(detailFlags, ConfigurationUnitDetailFlags.Load)) { this.GetLoadDetails(processorSettings); } } /// /// Gets a ConfigurationUnitProcessorDetails populated with all available data. /// /// A ConfigurationUnitProcessorDetails populated with all available data. public ConfigurationUnitProcessorDetails? GetConfigurationUnitProcessorDetails() { if (!this.Exists) { return null; } ConfigurationUnitProcessorDetails result = new ConfigurationUnitProcessorDetails() { UnitType = this.resourceTypeName }; lock (this.detailsUpdateLock) { if (this.resourceListItem != null) { result.UnitType = this.resourceListItem.Type; result.IsGroup = IsGroup(this.resourceListItem.Kind); result.Version = this.resourceListItem.Version; result.UnitDescription = this.resourceListItem.Description; result.Author = this.resourceListItem.Author; result.Path = this.resourceListItem.Path; result.IsLocal = true; } } return result; } private static bool IsGroup(ResourceKind kind) => kind switch { ResourceKind.Adapter => true, ResourceKind.Group => true, _ => false, }; private bool DetailsNeededFor(ConfigurationUnitDetailFlags detailFlags, ConfigurationUnitDetailFlags targetLevel) { if (!detailFlags.HasFlag(targetLevel)) { return false; } lock (this.detailsUpdateLock) { return !this.currentDetailLevel.HasFlag(targetLevel); } } private bool GetLocalDetails(ProcessorSettings processorSettings) { IResourceListItem? resourceListItem = processorSettings.DSCv3.GetResourceByType(this.resourceTypeName, null); if (resourceListItem != null) { // TODO: Attempt to extract embedded schema to avoid the need for Load. lock (this.detailsUpdateLock) { if (!this.currentDetailLevel.HasFlag(ConfigurationUnitDetailFlags.Local)) { this.resourceListItem = resourceListItem; this.currentDetailLevel |= ConfigurationUnitDetailFlags.Local; } } return true; } else { return false; } } private void GetLoadDetails(ProcessorSettings processorSettings) { throw new NotImplementedException(); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IDSCv3.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Model { using System.Collections.Generic; using Microsoft.Management.Configuration.Processor.DSCv3.Helpers; using Microsoft.Management.Configuration.Processor.Helpers; /// /// Interface for interacting with DSC v3. /// internal interface IDSCv3 { /// /// Creates the appropriate instance of the DSCv3 interface for the given executable. /// /// The processor settings. /// An object that properly interacts with the specific version of DSC v3. public static IDSCv3 Create(ProcessorSettings processorSettings) { // Expand as needed to detect the version of dsc.exe and/or its schemas in use. return new Schema_2024_04.DSCv3(processorSettings); } /// /// Gets a single resource by its type name. /// /// The type name of the resource. /// The processor run settings. /// A single resource item. public IResourceListItem? GetResourceByType(string resourceType, ProcessorRunSettings? runSettings); /// /// Gets all resource items. /// /// The processor run settings. /// A list of resource items. public List GetAllResources(ProcessorRunSettings? runSettings); /// /// Tests a configuration unit. /// /// The unit to test. /// The processor run settings. /// A test result. public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings); /// /// Gets a configuration unit settings. /// /// The unit to get. /// The processor run settings. /// A get result. public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings); /// /// Sets a configuration unit settings. /// /// The unit to set. /// The processor run settings. /// A set result. public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings); /// /// Exports configuration unit. /// /// The unit to export. /// The processor run settings. /// A list of export results. public IList ExportResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings); } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IResourceExportItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Model { using System.Collections.Generic; using Windows.Foundation.Collections; /// /// The interface to a `resource export` command result. /// internal interface IResourceExportItem { /// /// Gets the type of the resource. /// public string Type { get; } /// /// Gets the name of the resource instance. /// public string Name { get; } /// /// Gets the settings for this item. /// public ValueSet Settings { get; } /// /// Gets the metadata for this item. /// public ValueSet Metadata { get; } /// /// Gets the dependencies for this item. /// public IList Dependencies { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IResourceGetItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Model { using Windows.Foundation.Collections; /// /// The interface to a `resource get` command result. /// internal interface IResourceGetItem { /// /// Gets the settings for this item. /// public ValueSet Settings { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IResourceListItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Model { /// /// The interface to a single JSON line output by the `resource list` command. /// internal interface IResourceListItem { /// /// Gets the type of the resource. /// Should match the regex "^\\w+(\\.\\w+){0,2}\\/\\w+$". /// public string Type { get; } /// /// Gets the kind of the resource. /// public ResourceKind Kind { get; } /// /// Gets the version of the resource. /// This is a semver version. /// public string? Version { get; } /// /// Gets the description of the resource. /// public string? Description { get; } /// /// Gets the path to the directory containing the resource. /// public string? Directory { get; } /// /// Gets the author of the resource. /// public string? Author { get; } /// /// Gets the path of the resource. /// public string? Path { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IResourceSetItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Model { using System.Collections.Generic; /// /// The interface to a `resource set` command result. /// internal interface IResourceSetItem { /// /// Gets a value indicating whether a reboot is required. /// public bool RebootRequired { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IResourceTestItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Model { /// /// The interface to a `resource test` command result. /// internal interface IResourceTestItem { /// /// Gets a value indicating whether the resource is in the desired state. /// public bool InDesiredState { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Model/ResourceKind.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Model { /// /// https://learn.microsoft.com/en-us/powershell/dsc/reference/schemas/definitions/resourcekind?view=dsc-3.0 /// The kind of resource. /// internal enum ResourceKind { /// /// The kind is unknown. /// Unknown, /// /// A standard resource. /// Resource, /// /// An adapter resource. /// Adapter, /// /// A group resource. /// Group, /// /// An importer resource. /// Importer, } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/DSCv3.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 { using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Management.Configuration.Processor.DSCv3.Helpers; using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.Processor.Helpers; using Windows.Foundation.Collections; /// /// An instance of IDSCv3 for interacting with 1.0. /// internal class DSCv3 : IDSCv3 { private const string PlainTextTraces = "-t plaintext"; private const string DiagnosticTraceLevelArguments = "-l trace"; private const string ResourceCommand = "resource"; private const string ListCommand = "list"; private const string TestCommand = "test"; private const string GetCommand = "get"; private const string SetCommand = "set"; private const string ExportCommand = "export"; private const string ResourceParameter = "-r"; private const string FileParameter = "-f"; private const string StdInputIdentifier = "-"; private const string PathEnvironmentVariable = "PATH"; private const string DSCResourcePathEnvironmentVariable = "DSC_RESOURCE_PATH"; private readonly ProcessorSettings processorSettings; /// /// Initializes a new instance of the class. /// /// The processor settings. public DSCv3(ProcessorSettings processorSettings) { this.processorSettings = processorSettings; } private string DiagnosticTraceLevel { get { return this.processorSettings.DiagnosticTraceEnabled ? DiagnosticTraceLevelArguments : string.Empty; } } /// public IResourceListItem? GetResourceByType(string resourceType, ProcessorRunSettings? runSettings) { ResourceListItem? result = this.GetResourceByTypeInternal(resourceType, null, runSettings); if (result != null) { return result; } // Check for this resource within adapters List results = new List(); foreach (ResourceListItem resource in this.GetAllResources(runSettings)) { if (resource.Kind == Definitions.ResourceKind.Adapter) { result = this.GetResourceByTypeInternal(resourceType, resource.Type, runSettings); if (result != null) { results.Add(result); } } } return this.GetResourceByLatestVersion(results); } /// public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, TestCommand, ResourceParameter, unitInternal.QualifiedName, FileParameter, StdInputIdentifier }, Input = ConvertValueSetToJSON(unitInternal.GetExpandedSettings()), EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; if (this.RunSynchronously(processExecution)) { throw new Exceptions.InvokeDscResourceException(Exceptions.InvokeDscResourceException.Test, unitInternal.QualifiedName, null, processExecution.GetAllErrorLines()); } return TestFullItem.CreateFrom(GetRequiredSingleOutputLineAsJSON(processExecution, Exceptions.InvokeDscResourceException.Test, unitInternal.QualifiedName), GetDefaultJsonOptions()); } /// public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, GetCommand, ResourceParameter, unitInternal.QualifiedName, FileParameter, StdInputIdentifier }, Input = ConvertValueSetToJSON(unitInternal.GetExpandedSettings()), EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; if (this.RunSynchronously(processExecution)) { throw new Exceptions.InvokeDscResourceException(Exceptions.InvokeDscResourceException.Get, unitInternal.QualifiedName, null, processExecution.GetAllErrorLines()); } return GetFullItem.CreateFrom(GetRequiredSingleOutputLineAsJSON(processExecution, Exceptions.InvokeDscResourceException.Get, unitInternal.QualifiedName), GetDefaultJsonOptions()); } /// public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, SetCommand, ResourceParameter, unitInternal.QualifiedName, FileParameter, StdInputIdentifier }, Input = ConvertValueSetToJSON(unitInternal.GetExpandedSettings()), EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; if (this.RunSynchronously(processExecution)) { throw new Exceptions.InvokeDscResourceException(Exceptions.InvokeDscResourceException.Set, unitInternal.QualifiedName, null, processExecution.GetAllErrorLines()); } return SetFullItem.CreateFrom(GetRequiredSingleOutputLineAsJSON(processExecution, Exceptions.InvokeDscResourceException.Set, unitInternal.QualifiedName), GetDefaultJsonOptions()); } /// public IList ExportResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { // 3.0 can't handle input to export; 3.1 will fix that. ValueSet expandedSettings = unitInternal.GetExpandedSettings(); if (expandedSettings.Count != 0) { throw new NotImplementedException("Must use DSC v3.1.* to provide input to export."); } ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, ExportCommand, ResourceParameter, unitInternal.QualifiedName }, EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; if (this.RunSynchronously(processExecution)) { throw new Exceptions.InvokeDscResourceException(Exceptions.InvokeDscResourceException.Export, unitInternal.QualifiedName, null, processExecution.GetAllErrorLines()); } return ConfigurationDocument.CreateFrom(GetRequiredSingleOutputLineAsJSON(processExecution, Exceptions.InvokeDscResourceException.Set, unitInternal.QualifiedName), GetDefaultJsonOptions()).InterfaceResources; } /// public List GetAllResources(ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, ListCommand }, EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; this.RunSynchronously(processExecution); return GetOutputLinesAs(processExecution).ToList(); } private static void ThrowOnMultipleOutputLines(ProcessExecution processExecution, string method, string resourceName) { if (processExecution.Output.Count > 1) { throw new Exceptions.InvokeDscResourceException(method, resourceName, processExecution.GetAllOutputLines()); } } private static void ThrowOnZeroOutputLines(ProcessExecution processExecution, string method, string resourceName) { if (processExecution.Output.Count == 0) { throw new Exceptions.InvokeDscResourceException(method, resourceName); } } private static T? GetOptionalSingleOutputLineAs(ProcessExecution processExecution) { if (processExecution.Output.Count == 0) { return default; } return JsonSerializer.Deserialize(processExecution.Output.First(), GetDefaultJsonOptions()); } private static List GetOutputLinesAs(ProcessExecution processExecution) { List result = new List(); var options = GetDefaultJsonOptions(); foreach (string line in processExecution.Output) { T? lineObject = JsonSerializer.Deserialize(line, options); if (lineObject != null) { result.Add(lineObject); } } return result; } private static JsonDocument GetRequiredSingleOutputLineAsJSON(ProcessExecution processExecution, string method, string resourceName) { ThrowOnMultipleOutputLines(processExecution, method, resourceName); ThrowOnZeroOutputLines(processExecution, method, resourceName); return JsonDocument.Parse(processExecution.Output.First()); } private static JsonSerializerOptions GetDefaultJsonOptions() { return new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, Converters = { new JsonStringEnumConverter(), }, }; } private static string ConvertValueSetToJSON(ValueSet valueSet) { return JsonSerializer.Serialize(valueSet.ToHashtable()); } private static List CreateEnvironmentVariablesFromProcessorRunSettings(ProcessorRunSettings? runSettings) { List result = new List(); if (runSettings is not null && !string.IsNullOrEmpty(runSettings.ResourceSearchPaths)) { // For exclusive search paths, adding to PATH is still needed as anything referenced in the manifest is still searched from PATH. result.Add(new ProcessExecutionEnvironmentVariable { Name = PathEnvironmentVariable, Value = runSettings.ResourceSearchPaths, ValueType = ProcessExecutionEnvironmentVariableValueType.Prepend }); if (runSettings.ResourceSearchPathsExclusive) { result.Add(new ProcessExecutionEnvironmentVariable { Name = DSCResourcePathEnvironmentVariable, Value = runSettings.ResourceSearchPaths, ValueType = ProcessExecutionEnvironmentVariableValueType.Override }); } } return result; } /// /// Runs the process, waiting until it completes. /// /// The process to run. /// True if the exit code was not 0. private bool RunSynchronously(ProcessExecution processExecution) { this.processorSettings.DiagnosticsSink?.OnDiagnostics(DiagnosticLevel.Verbose, $"Starting process: {processExecution.CommandLine}{(processExecution.Input == null ? string.Empty : $"\n--- Input Stream ---\n{processExecution.Input}")}"); processExecution.Start().WaitForExit(); this.processorSettings.DiagnosticsSink?.OnDiagnostics(DiagnosticLevel.Verbose, $"Process exited with code: {processExecution.ExitCode}\n--- Output Stream ---\n{processExecution.GetAllOutputLines()}\n--- Error Stream ---\n{processExecution.GetAllErrorLines()}"); return processExecution.ExitCode != 0; } private ResourceListItem? GetResourceByTypeInternal(string resourceType, string? adapter, ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, ListCommand, adapter != null ? $"-a {adapter}" : string.Empty, resourceType }, EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; this.RunSynchronously(processExecution); List results = GetOutputLinesAs(processExecution); return this.GetResourceByLatestVersion(results); } private ResourceListItem? GetResourceByLatestVersion(List resources) { // There may be different versions of same resource on the system. We check if all // resource types match, we return the first one. // TODO: May want to pick the latest one from the list. But since we are not using // the version in our commands, picking any one is good for now. ResourceListItem? candidate = null; string candidateType = string.Empty; foreach (ResourceListItem resource in resources) { if (candidate == null) { candidate = resource; candidateType = resource.Type; } else if (!candidateType.Equals(resource.Type, StringComparison.OrdinalIgnoreCase)) { throw new Exceptions.GetDscResourceMultipleMatches(candidateType, null); } } return candidate; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Definitions/ResourceKind.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Definitions { /// /// https://learn.microsoft.com/en-us/powershell/dsc/reference/schemas/definitions/resourcekind?view=dsc-3.0 /// The kind of resource. /// internal enum ResourceKind { /// /// The kind is unknown. /// Unknown, /// /// A standard resource. /// Resource, /// /// An adapter resource. /// Adapter, /// /// A group resource. /// Group, /// /// An import(er) resource. /// The name listed in the DSC schema. /// Import, /// /// An importer resource. /// The name used by the code. /// Importer = Import, } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Metadata/ResourceInstanceResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Metadata { using System; using System.Text.Json.Serialization; /// /// Defines metadata DSC returns for a DSC configuration operation against a resource instance. /// internal class ResourceInstanceResult { /// /// Gets or sets the context metadata for this instance result. /// [JsonRequired] [JsonPropertyName("Microsoft.DSC")] public ContextMetadata? MicrosoftDSC { get; set; } /// /// Contains properties generated by the DSC v3 platform. /// public class ContextMetadata { /// /// Gets or sets the duration of a resource instance execution. /// [JsonRequired] public TimeSpan Duration { get; set; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/ConfigurationDocument.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Management.Configuration.Processor.DSCv3.Model; /// /// A configuration document. /// internal class ConfigurationDocument { /// /// Gets or sets the list of resources in the document. /// public List Resources { get; set; } = new List(); /// /// Gets the list of resources as the interface version. /// [JsonIgnore] public IList InterfaceResources { get { return new List(this.Resources.AsEnumerable()); } } /// /// Initializes a new instance of the ConfigurationDocument class. /// /// The document to construct from. /// The options to use. /// The item created. public static ConfigurationDocument CreateFrom(JsonDocument document, JsonSerializerOptions options) { ConfigurationDocument? result = JsonSerializer.Deserialize(document, options); if (result == null) { throw new InvalidDataException("Unable to deserialize ConfigurationDocument."); } return result; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/FullItemBase.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using System.IO; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Metadata; /// /// The base implementation of the full form output. /// When the retrieved instance is for group resource, adapter resource, or nested inside a group or adapter resource, DSC returns a full result, which also includes the resource type and instance name. /// /// The simple item type. /// The full item type. internal class FullItemBase where TFull : FullItemBase, new() { private const string NameProperty = "name"; /// /// Initializes a new instance of the class. /// public FullItemBase() { } /// /// Gets or sets the metadata for this test result. /// [JsonRequired] public ResourceInstanceResult? Metadata { get; set; } /// /// Gets or sets the name for this test result. /// [JsonRequired] public string? Name { get; set; } /// /// Gets or sets the type for the resource. /// [JsonRequired] public string? Type { get; set; } /// /// Gets or sets the result of the test. /// [JsonRequired] public JsonNode? Result { get; set; } /// /// Gets or sets the simple result. /// [JsonIgnore] public TSimple? SimpleResult { get; set; } /// /// Gets or sets the full results. /// [JsonIgnore] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Opening square brackets should be spaced correctly", Justification = "Pending SC 1.2 release")] protected TFull[]? FullResults { get; set; } /// /// Initializes a new instance of the TFull class. /// /// The document to construct from. /// The options to use. /// The item created. public static TFull CreateFrom(JsonDocument document, JsonSerializerOptions options) { if (!document.RootElement.TryGetProperty(NameProperty, out JsonElement jsonElement)) { return new () { SimpleResult = JsonSerializer.Deserialize(document, options) }; } else { TFull? result = JsonSerializer.Deserialize(document, options); if (result == null) { throw new InvalidDataException("Unable to deserialize full result."); } result.ProcessResult(options); return result; } } /// /// Converts the Result property into the appropriate simple or full results. /// /// The options to use. public void ProcessResult(JsonSerializerOptions options) { if (this.Result == null) { throw new System.InvalidOperationException("JSON result has not been initialized."); } if (this.Result is JsonObject jsonObject) { this.SimpleResult = JsonSerializer.Deserialize(this.Result, options); } else { this.FullResults = JsonSerializer.Deserialize(this.Result, options); if (this.FullResults == null) { throw new InvalidDataException("Unable to deserialize full results."); } foreach (TFull result in this.FullResults) { result.ProcessResult(options); } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/GetFullItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Microsoft.Management.Configuration.Processor.Extensions; using Windows.Foundation.Collections; /// /// The full form of the get output. /// When the retrieved instance is for group resource, adapter resource, or nested inside a group or adapter resource, DSC returns a full get result, which also includes the resource type and instance name. /// internal class GetFullItem : FullItemBase, IResourceGetItem { /// /// Initializes a new instance of the class. /// public GetFullItem() { } /// public ValueSet Settings { get { if (this.SimpleResult != null) { return this.SimpleResult.ActualState?.ToValueSet() ?? throw new System.InvalidOperationException("Get result has not been initialized."); } else if (this.FullResults != null) { throw new System.NotImplementedException("Requires constructing the entire group as the settings."); } else { throw new System.InvalidOperationException("Get result has not been initialized."); } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/GetSimpleItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using System.Text.Json.Nodes; using System.Text.Json.Serialization; /// /// The simple form of the get output. /// DSC returns a simple get response when the instance isn't a group resource, adapter resource, or nested inside a group or adapter resource. /// internal class GetSimpleItem { /// /// Gets or sets the state of the resource properties. /// [JsonRequired] public JsonObject? ActualState { get; set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/ResourceCapability.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { /// /// https://learn.microsoft.com/en-us/powershell/dsc/reference/schemas/outputs/resource/list?view=dsc-3.0#capabilities /// The capabilities that a resource can have. /// internal enum ResourceCapability { /// /// Can call get on the resource. /// Required. /// Get, /// /// Can call set on the resource. /// Set, /// /// The resource operates properly in the presence of the `_exist` property. /// If not present, DSC will use `delete` when `_exist == false`. /// SetHandlesExist, /// /// The resource can handle a "what if" query directly. /// Otherwise, DSC will handle it synthetically. /// WhatIf, /// /// The resource can handle a "test" query directly. /// Otherwise, DSC will handle it synthetically. /// Test, /// /// Can call delete on the resource. /// Delete, /// /// Can call export on the resource. /// Export, /// /// Can call resolve on the resource. /// This can produce new resources, such as importing another configuration document. /// Resolve, } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/ResourceItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using System.Collections.Generic; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Microsoft.Management.Configuration.Processor.Extensions; using Windows.Foundation.Collections; /// /// The object type from a single resource item. /// internal class ResourceItem : IResourceExportItem { /// /// Gets or sets the type of the resource. /// Should match the regex "^\\w+(\\.\\w+){0,2}\\/\\w+$". /// [JsonRequired] required public string Type { get; set; } /// /// Gets or sets the name of the resource instance. /// [JsonRequired] required public string Name { get; set; } /// /// Gets or sets the properties object. /// public JsonObject? Properties { get; set; } /// /// Gets or sets the metadata object. /// [JsonPropertyName("metadata")] public JsonObject? MetadataObject { get; set; } /// /// Gets or sets the dependencies. /// [JsonPropertyName("dependencies")] public List DependenciesList { get; set; } = new List(); /// [JsonIgnore] public ValueSet Settings { get { return this.Properties.ToValueSet(); } } /// [JsonIgnore] public ValueSet Metadata { get { return this.MetadataObject.ToValueSet(); } } /// [JsonIgnore] public IList Dependencies { get { return this.DependenciesList; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/ResourceListItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using System.Text.Json.Nodes; using System.Text.Json.Serialization; using Microsoft.Management.Configuration.Processor.DSCv3.Model; /// /// The object type from a single JSON line output by the `resource list` command. /// internal class ResourceListItem : IResourceListItem { /// /// Gets or sets the type of the resource. /// Should match the regex "^\\w+(\\.\\w+){0,2}\\/\\w+$". /// [JsonRequired] required public string Type { get; set; } /// /// Gets or sets the kind of the resource. /// public Definitions.ResourceKind Kind { get; set; } = Definitions.ResourceKind.Unknown; /// [JsonIgnore] Model.ResourceKind IResourceListItem.Kind => this.Kind switch { Definitions.ResourceKind.Unknown => Model.ResourceKind.Unknown, Definitions.ResourceKind.Resource => Model.ResourceKind.Resource, Definitions.ResourceKind.Adapter => Model.ResourceKind.Adapter, Definitions.ResourceKind.Group => Model.ResourceKind.Group, Definitions.ResourceKind.Import => Model.ResourceKind.Importer, _ => throw new System.IO.InvalidDataException($"Unknown ResourceKind: {this.Kind}") }; /// /// Gets or sets the version of the resource. /// This is a semver version. /// public string? Version { get; set; } /// /// Gets or sets the capabilities of the resource. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public ResourceCapability[] Capabilities { get; set; } = []; /// /// Gets or sets the description of the resource. /// public string? Description { get; set; } /// /// Gets or sets the path to the resource definition file. /// public string? Path { get; set; } /// /// Gets or sets the path to the directory containing the resource. /// public string? Directory { get; set; } /// /// Gets or sets a value that indicates implementation details of the resource. /// public JsonNode? ImplementedAs { get; set; } /// /// Gets or sets the author of the resource. /// public string? Author { get; set; } /// /// Gets or sets the names of the properties of the resource. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public string[] Properties { get; set; } = []; /// /// Gets or sets the adapter required by the resource. /// public string? RequireAdapter { get; set; } /// /// Gets or sets the resource definition manifest. /// public JsonObject? Manifest { get; set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/SetFullItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using Microsoft.Management.Configuration.Processor.DSCv3.Model; /// /// The full form of the set output. /// When the retrieved instance is for group resource, adapter resource, or nested inside a group or adapter resource, DSC returns a full set result, which also includes the resource type and instance name. /// internal class SetFullItem : FullItemBase, IResourceSetItem { /// /// Initializes a new instance of the class. /// public SetFullItem() { } /// public bool RebootRequired => false; } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/SetSimpleItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using System.Text.Json.Nodes; using System.Text.Json.Serialization; /// /// The simple form of the set output. /// DSC returns a simple set response when the instance isn't a group resource, adapter resource, or nested inside a group or adapter resource. /// internal class SetSimpleItem { /// /// Gets or sets the state of the resource properties before the attempt to set them. /// [JsonRequired] public JsonObject? BeforeState { get; set; } /// /// Gets or sets the state of the resource properties after the attempt to set them. /// [JsonRequired] public JsonObject? AfterState { get; set; } /// /// Gets or sets the list of properties that changed. /// [JsonRequired] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public string[] ChangedProperties { get; set; } = []; } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/TestFullItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using Microsoft.Management.Configuration.Processor.DSCv3.Model; /// /// The full form of the test output. /// When the retrieved instance is for group resource, adapter resource, or nested inside a group or adapter resource, DSC returns a full test result, which also includes the resource type and instance name. /// internal class TestFullItem : FullItemBase, IResourceTestItem { /// /// Initializes a new instance of the class. /// public TestFullItem() { } /// public bool InDesiredState { get { if (this.SimpleResult != null) { return this.SimpleResult.InDesiredState; } else if (this.FullResults != null) { bool result = true; foreach (var item in this.FullResults) { result = result && item.InDesiredState; } return result; } else { throw new System.InvalidOperationException("Test result has not been initialized."); } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/Outputs/TestSimpleItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04.Outputs { using System.Text.Json.Nodes; using System.Text.Json.Serialization; /// /// The simple form of the test output. /// DSC returns a simple test response when the instance isn't a group resource, adapter resource, or nested inside a group or adapter resource. /// internal class TestSimpleItem { /// /// Gets or sets the desired state of the resource properties. /// [JsonRequired] public JsonObject? DesiredState { get; set; } /// /// Gets or sets the actual state of the resource properties. /// [JsonRequired] public JsonObject? ActualState { get; set; } /// /// Gets or sets a value indicating whether the resource is in the desired state. /// [JsonRequired] public bool InDesiredState { get; set; } /// /// Gets or sets the list of properties that are not in the desired state. /// [JsonRequired] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] public string[] DifferingProperties { get; set; } = []; } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Set/DSCv3ConfigurationSetProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Set { using System.Collections.Generic; using Microsoft.Management.Configuration.Processor.DSCv3.Helpers; using Microsoft.Management.Configuration.Processor.DSCv3.Unit; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.Set; /// /// Configuration set processor. /// internal sealed partial class DSCv3ConfigurationSetProcessor : ConfigurationSetProcessorBase, IConfigurationSetProcessor, IFindUnitProcessorsSetProcessor { private readonly ProcessorSettings processorSettings; /// /// Initializes a new instance of the class. /// /// The processor settings to use. /// Configuration set. /// Whether the set processor should work in limitation mode. public DSCv3ConfigurationSetProcessor(ProcessorSettings processorSettings, ConfigurationSet? configurationSet, bool isLimitMode = false) : base(configurationSet, isLimitMode) { this.processorSettings = processorSettings; } /// protected override IConfigurationUnitProcessor CreateUnitProcessorInternal(ConfigurationUnit unit) { ConfigurationUnitInternal configurationUnitInternal = new ConfigurationUnitInternal(unit, this.ConfigurationSet?.Path); this.OnDiagnostics(DiagnosticLevel.Verbose, $"Creating unit processor for: {configurationUnitInternal.QualifiedName}..."); ResourceDetails? resourceDetails = this.processorSettings.GetResourceDetails(configurationUnitInternal, ConfigurationUnitDetailFlags.Local); if (resourceDetails == null) { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Resource not found: {configurationUnitInternal.QualifiedName}"); // Don't throw when the resource is not found until https://github.com/PowerShell/DSC/issues/786 is resolved // throw new Exceptions.FindDscResourceNotFoundException(configurationUnitInternal.QualifiedName, null); } return new DSCv3ConfigurationUnitProcessor(this.processorSettings, resourceDetails, configurationUnitInternal, this.IsLimitMode) { SetProcessorFactory = this.SetProcessorFactory }; } /// protected override IConfigurationUnitProcessorDetails? GetUnitProcessorDetailsInternal(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags) { ConfigurationUnitInternal configurationUnitInternal = new ConfigurationUnitInternal(unit, this.ConfigurationSet?.Path); this.OnDiagnostics(DiagnosticLevel.Verbose, $"Getting resource details [{detailFlags}] for: {configurationUnitInternal.QualifiedName}..."); ResourceDetails? resourceDetails = this.processorSettings.GetResourceDetails(configurationUnitInternal, detailFlags); if (resourceDetails == null) { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Resource not found: {configurationUnitInternal.QualifiedName}"); return null; } return resourceDetails.GetConfigurationUnitProcessorDetails(); } /// protected override IList FindUnitProcessorsInternal(FindUnitProcessorsOptions findOptions) { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Finding unit processors with following options. SearchPaths: {findOptions.SearchPaths}, SearchPathsExclusive: {findOptions.SearchPathsExclusive}, DetailFlags: [{findOptions.UnitDetailFlags}]"); List result = new List(); var resourceDetailsList = this.processorSettings.FindAllResourceDetails(findOptions); foreach (var resourceDetails in resourceDetailsList) { result.Add(resourceDetails.GetConfigurationUnitProcessorDetails() !); } return result; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/DSCv3/Unit/DSCv3ConfigurationUnitProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.DSCv3.Unit { using System; using System.Collections.Generic; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.DSCv3.Helpers; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.Unit; using Windows.Foundation.Collections; /// /// Provides access to a specific configuration unit within the runtime. /// internal sealed partial class DSCv3ConfigurationUnitProcessor : ConfigurationUnitProcessorBase, IConfigurationUnitProcessor, IGetAllSettingsConfigurationUnitProcessor, IGetAllUnitsConfigurationUnitProcessor, IDiagnosticsSink { private readonly ProcessorSettings processorSettings; private readonly ResourceDetails? resourceDetails; /// /// Initializes a new instance of the class. /// /// The processor settings to use. /// The resource to use. /// Internal unit. /// Whether it is under limit mode. internal DSCv3ConfigurationUnitProcessor(ProcessorSettings processorSettings, ResourceDetails? resourceDetails, ConfigurationUnitInternal unitInternal, bool isLimitMode = false) : base(unitInternal, isLimitMode) { this.processorSettings = processorSettings; this.resourceDetails = resourceDetails; } /// void IDiagnosticsSink.OnDiagnostics(DiagnosticLevel level, string message) { this.OnDiagnostics(level, message); } /// protected override ValueSet GetSettingsInternal() { return this.processorSettings.DSCv3.GetResourceSettings(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)).Settings; } /// protected override bool TestSettingsInternal() { return this.processorSettings.DSCv3.TestResource(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)).InDesiredState; } /// protected override bool ApplySettingsInternal() { return this.processorSettings.DSCv3.SetResourceSettings(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)).RebootRequired; } /// protected override IList? GetAllSettingsInternal() { var exportResult = this.processorSettings.DSCv3.ExportResource(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)); string expectedType = this.UnitInternal.QualifiedName.ToLowerInvariant(); List result = new List(); foreach (var exportItem in exportResult) { if (exportItem.Type.ToLowerInvariant() != expectedType) { throw new UnitPropertyUnsupportedException(typeof(IGetAllSettingsConfigurationUnitProcessor)); } result.Add(exportItem.Settings); } return result; } /// protected override IList? GetAllUnitsInternal() { var exportResult = this.processorSettings.DSCv3.ExportResource(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)); List result = new List(); foreach (var exportItem in exportResult) { ConfigurationUnit unit = new ConfigurationUnit(); unit.Type = exportItem.Type; unit.Identifier = exportItem.Name; unit.Settings = exportItem.Settings; unit.Metadata = exportItem.Metadata; unit.Dependencies = exportItem.Dependencies; result.Add(unit); } return result; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/ErrorCodes.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { /// /// This should match the ones in AppInstallerErrors.h. /// internal static class ErrorCodes { /// /// Corresponds to E_UNEXPECTED; this code path was reached without the developer realizing it was possible. /// internal const int Unexpected = unchecked((int)0x8000ffff); /// /// The module of the unit was installed, but the unit was not found. /// internal const int WinGetConfigUnitNotFound = unchecked((int)0x8A15C101); /// /// The unit couldn't be found in the repository. /// internal const int WinGetConfigUnitNotFoundRepository = unchecked((int)0x8A15C102); /// /// Multiple units found with the same criteria. /// internal const int WinGetConfigUnitMultipleMatches = unchecked((int)0x8A15C103); /// /// Unit error calling Invoke-DscResource Get. /// internal const int WinGetConfigUnitInvokeGet = unchecked((int)0x8A15C104); /// /// Unit error calling Invoke-DscResource Test. /// internal const int WinGetConfigUnitInvokeTest = unchecked((int)0x8A15C105); /// /// Unit error calling Invoke-DscResource Set. /// internal const int WinGetConfigUnitInvokeSet = unchecked((int)0x8A15C106); /// /// Internal error calling Get-DscResource. More than one module found with the same version. /// internal const int WinGetConfigUnitModuleConflict = unchecked((int)0x8A15C107); /// /// The module where the DSC resource is implemented cannot be imported. /// internal const int WinGetConfigUnitImportModule = unchecked((int)0x8A15C108); /// /// The unit returned an invalid result. /// internal const int WinGetConfigUnitInvokeInvalidResult = unchecked((int)0x8A15C109); /// /// The unit contains a setting that requires config root. /// internal const int WinGetConfigUnitSettingConfigRoot = unchecked((int)0x8A15C110); /// /// The module where the DSC resource is implemented requires admin. /// internal const int WinGetConfigUnitImportModuleAdmin = unchecked((int)0x8A15C111); /// /// The property type of a unit is not supported. /// internal const int WinGetConfigUnitUnsupportedType = unchecked((int)0x8A15C112); } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/FindDscResourceNotFoundException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; using Microsoft.PowerShell.Commands; /// /// Resource not found by Find-DscResource. /// internal class FindDscResourceNotFoundException : Exception { /// /// Initializes a new instance of the class. /// /// Resource name. /// Optional module. public FindDscResourceNotFoundException(string resourceName, ModuleSpecification? module) : base($"Could not find resource: {resourceName} [{module?.ToString() ?? ""}]") { this.HResult = ErrorCodes.WinGetConfigUnitNotFoundRepository; this.ResourceName = resourceName; this.Module = module; } /// /// Gets the resource name. /// public string ResourceName { get; } /// /// Gets the module, if any. /// public ModuleSpecification? Module { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/GetDscResourceModuleConflict.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; using System.Management.Automation; using Microsoft.PowerShell.Commands; /// /// A call to Get-DscResource failed because at least two modules with the same version where found in the module path. /// If you are getting this verify the module path. /// internal class GetDscResourceModuleConflict : Exception { /// /// Initializes a new instance of the class. /// /// Resource name. /// Optional module. /// The original runtime exception thrown. public GetDscResourceModuleConflict(string? resourceName, ModuleSpecification? module, RuntimeException inner) : base($"Multiple modules with same version in module path: {resourceName?.ToString() ?? ""} [{module?.ToString() ?? ""}]", inner) { this.HResult = ErrorCodes.WinGetConfigUnitModuleConflict; this.ResourceName = resourceName; this.Module = module; } /// /// Gets the resource name. /// public string? ResourceName { get; } /// /// Gets the module, if any. /// public ModuleSpecification? Module { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/GetDscResourceMultipleMatches.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; using Microsoft.PowerShell.Commands; /// /// A call to Get-DscResource return multiple results for a specific resource. /// internal class GetDscResourceMultipleMatches : Exception { /// /// Initializes a new instance of the class. /// /// Resource name. /// Optional module. public GetDscResourceMultipleMatches(string resourceName, ModuleSpecification? module) : base($"Multiple matches found for resource: {resourceName} [{module?.ToString() ?? ""}]") { this.HResult = ErrorCodes.WinGetConfigUnitMultipleMatches; this.ResourceName = resourceName; this.Module = module; } /// /// Gets the resource name. /// public string ResourceName { get; } /// /// Gets the module, if any. /// public ModuleSpecification? Module { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/IConfigurationUnitResultException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; using Microsoft.PowerShell.Commands; /// /// An interface that enables an exception to expose information appropriate for a unit result. /// internal interface IConfigurationUnitResultException { /// /// Gets a value indicating the source of the result. /// public ConfigurationUnitResultSource ResultSource { get; } /// /// Gets the description of the result. /// public string Description { get; } /// /// Gets the details for the result. /// public string Details { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/ImportModuleException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; using System.Management.Automation; /// /// Import-Module threw an exception. /// internal class ImportModuleException : Exception { /// /// Initializes a new instance of the class. /// /// Module name. /// Inner exception. public ImportModuleException(string? moduleName, Exception pwshEx) : base($"Could not import module: {moduleName?.ToString() ?? ""}", pwshEx) { this.HResult = this.GetHResult(pwshEx); this.ModuleName = moduleName; } /// /// Gets the module name. /// public string? ModuleName { get; } private int GetHResult(Exception pwshEx) { if (pwshEx.InnerException is not null) { var scriptEx = pwshEx.InnerException as ScriptRequiresException; if (scriptEx is not null) { if (scriptEx.ErrorRecord.CategoryInfo.Category == ErrorCategory.PermissionDenied) { return ErrorCodes.WinGetConfigUnitImportModuleAdmin; } } } return ErrorCodes.WinGetConfigUnitImportModule; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/InstallDscResourceException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; using Microsoft.PowerShell.Commands; /// /// Installing a DSC resource failed unexpectedly. /// internal class InstallDscResourceException : Exception { /// /// Initializes a new instance of the class. /// /// Resource name. /// Module. public InstallDscResourceException(string resourceName, ModuleSpecification? module) : base($"Unable to find resource after install: {resourceName} [{module?.ToString() ?? ""}]") { this.HResult = ErrorCodes.WinGetConfigUnitNotFound; this.ResourceName = resourceName; this.Module = module; } /// /// Gets the resource name. /// public string ResourceName { get; } /// /// Gets the module, if any. /// public ModuleSpecification? Module { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/InvokeDscResourceException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; using System.Management.Automation; using Microsoft.Management.Configuration; using Microsoft.PowerShell.Commands; /// /// A call to Invoke-DscResource failed unexpectedly. /// internal class InvokeDscResourceException : Exception, IConfigurationUnitResultException { /// /// The string for the Get method. /// public const string Get = "Get"; /// /// The string for the Set method. /// public const string Set = "Set"; /// /// The string for the Test method. /// public const string Test = "Test"; /// /// The string for the Export method. /// public const string Export = "Export"; /// /// Initializes a new instance of the class. /// Use this constructor when no error is generated by the invoke and the result is not a valid value. /// /// Method. /// Resource name. /// Optional module. public InvokeDscResourceException(string method, string resourceName, ModuleSpecification? module = null) : base(CreateMessage(method, resourceName, module, null)) { // No message means that the invoke returned an invalid result. this.HResult = ErrorCodes.WinGetConfigUnitInvokeInvalidResult; this.Method = method; this.ResourceName = resourceName; this.Module = module; } /// /// Initializes a new instance of the class. /// Use this constructor when there is a message and the result is not valid. /// /// Method. /// Resource name. /// Message. public InvokeDscResourceException(string method, string resourceName, string message) : base(CreateMessage(method, resourceName, null, message)) { this.HResult = ErrorCodes.WinGetConfigUnitInvokeInvalidResult; this.Method = method; this.ResourceName = resourceName; } /// /// Initializes a new instance of the class. /// Use this constructor when the invoke fails with an error message. /// /// Method. /// Resource name. /// Optional module. /// Message. /// If true, the source of this error is set to be the configuration. public InvokeDscResourceException(string method, string resourceName, ModuleSpecification? module, string message, bool configurationSetSource = false) : base(CreateMessage(method, resourceName, module, message)) { this.HResult = GetHRForMethod(method); this.Method = method; this.ResourceName = resourceName; this.Module = module; this.Description = message; if (configurationSetSource) { this.ResultSource = ConfigurationUnitResultSource.ConfigurationSet; } } /// /// Initializes a new instance of the class. /// Use this constructor when the invoke fails with an exception. /// /// Method. /// Resource name. /// Optional module. /// The invoke exception. public InvokeDscResourceException(string method, string resourceName, ModuleSpecification? module, Exception inner) : base(CreateMessage(method, resourceName, module, inner.Message), inner) { this.HResult = GetHRForMethod(method); this.Method = method; this.ResourceName = resourceName; this.Module = module; this.Description = (inner as RuntimeException)?.ErrorRecord.ToString() ?? inner.Message; } /// /// Gets the invoke method. /// public string Method { get; } /// /// Gets the resource name. /// public string ResourceName { get; } /// /// Gets the module, if any. /// public ModuleSpecification? Module { get; } /// /// Gets a value indicating the source of the result. /// public ConfigurationUnitResultSource ResultSource { get; } = ConfigurationUnitResultSource.UnitProcessing; /// /// Gets the description of the result. /// public string Description { get; } = string.Empty; /// /// Gets the details for the result. /// public string Details { get { RuntimeException? re = this.InnerException as RuntimeException; if (re != null) { return re.ErrorRecord.ScriptStackTrace; } return this.ToString(); } } /// /// Gets the HRESULT value for the given method. /// /// The method. /// The HRESULT for the method. private static int GetHRForMethod(string method) { switch (method) { case Get: return ErrorCodes.WinGetConfigUnitInvokeGet; case Set: return ErrorCodes.WinGetConfigUnitInvokeSet; case Test: return ErrorCodes.WinGetConfigUnitInvokeTest; case Export: return ErrorCodes.WinGetConfigUnitInvokeGet; } return ErrorCodes.Unexpected; } private static string CreateMessage(string method, string resourceName, ModuleSpecification? module, string? message) { string result = $"Failed when calling `{method}` for resource: {resourceName} [{module?.ToString() ?? ""}]"; if (message != null) { result += $" Message: '{message}'"; } return result; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/UnitPropertyUnsupportedException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; /// /// The property type of a unit is not supported. /// internal class UnitPropertyUnsupportedException : Exception { /// /// Initializes a new instance of the class. /// /// Name. /// Type. /// Inner exception. public UnitPropertyUnsupportedException(string name, Type type, Exception inner) : base($"Property {name} of type {type.FullName} is not supported.", inner) { this.HResult = ErrorCodes.WinGetConfigUnitUnsupportedType; this.Name = name; this.Type = type; } /// /// Initializes a new instance of the class. /// /// Type. public UnitPropertyUnsupportedException(Type type) : base($"Type {type.FullName} is not supported.") { this.HResult = ErrorCodes.WinGetConfigUnitUnsupportedType; this.Type = type; } /// /// Gets the name. /// public string? Name { get; } /// /// Gets the type. /// public Type Type { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Exceptions/UnitSettingConfigRootException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Exceptions { using System; /// /// A setting uses the config root variable and the Path was not set in the ConfigurationSet. /// internal class UnitSettingConfigRootException : Exception { /// /// Initializes a new instance of the class. /// /// Unit name. /// Setting. public UnitSettingConfigRootException(string unitName, string setting) : base($"Unit: {unitName} Setting {setting} requires the ConfigurationSet Path") { this.HResult = ErrorCodes.WinGetConfigUnitSettingConfigRoot; this.UnitName = unitName; this.Setting = setting; } /// /// Gets the resource name. /// public string UnitName { get; } /// /// Gets the setting that reference the config root variable. /// public string Setting { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Extensions/DictionaryExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Extensions { using System; using System.Collections; using System.Collections.Generic; using Windows.Foundation.Collections; /// /// Extensions for dictionaries. /// internal static class DictionaryExtensions { /// /// Performs a deep compare of the dictionaries. /// /// First dictionary. /// Second dictionary. /// Whether the two dictionaries equal. internal static bool ContentEquals(this IDictionary first, IDictionary second) { if (first.Count != second.Count) { return false; } foreach (var keyValuePair in first) { string key = keyValuePair.Key; if (!second.ContainsKey(key)) { return false; } var firstValue = keyValuePair.Value; var secondValue = second[key]; // Empty value check. if (firstValue == null && secondValue == null) { continue; } else if (firstValue == null || secondValue == null) { return false; } if (firstValue != secondValue) { return false; } } return true; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Extensions/ExceptionExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Extensions { using System; /// /// Extension method for Exception. /// internal static class ExceptionExtensions { /// /// Gets the most inner exception. /// /// Exception. /// Most inner exception. public static Exception GetMostInnerException(this Exception e) { Exception ex = e; while (ex.InnerException != null) { ex = ex.InnerException; } return ex; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Extensions/HashtableExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Extensions { using System.Collections; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Helpers; using Windows.Foundation.Collections; /// /// Extensions for Hashtable. /// internal static class HashtableExtensions { /// /// Convert a hashtable to a value set. /// /// hashtable. /// Value set. public static ValueSet ToValueSet(this Hashtable hashtable) { var valueSet = new ValueSet(); foreach (DictionaryEntry entry in hashtable) { if (entry.Key is string key) { if (entry.Value is null) { valueSet.Add(key, null); } else { var value = TypeHelpers.GetCompatibleValueSetValueOfProperty(entry.Value.GetType(), entry.Value); if (value != null) { valueSet.Add(key, value); } } } else { throw new UnitPropertyUnsupportedException(entry.Key.GetType()); } } return valueSet; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Extensions/JsonObjectExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Extensions { using System.Text.Json; using System.Text.Json.Nodes; using Windows.Foundation.Collections; /// /// Extensions for JsonObject. /// internal static class JsonObjectExtensions { /// /// Converts the JSON object to a ValueSet. /// /// The object to convert. /// The ValueSet. public static ValueSet ToValueSet(this JsonObject? jsonObject) { ValueSet result = new ValueSet(); if (jsonObject != null) { foreach (var item in jsonObject) { result.Add(item.Key, ToValue(item.Value)); } } return result; } private static object? ToValue(JsonNode? node) => node switch { JsonObject obj => obj.ToValueSet(), JsonArray array => ToValueSet(array), JsonValue value => ToValue(value), _ => null, }; private static ValueSet ToValueSet(JsonArray array) { ValueSet result = new ValueSet(); result.Add(ValueSetExtensions.TreatAsArray, true); int index = 0; foreach (var item in array) { result.Add(index.ToString(), ToValue(item)); ++index; } return result; } private static object? ToValue(JsonValue value) => value.GetValueKind() switch { JsonValueKind.Null => null, JsonValueKind.Undefined => null, JsonValueKind.String => value.GetValue(), JsonValueKind.Number => value.GetValue(), JsonValueKind.True => true, JsonValueKind.False => false, _ => throw new System.NotImplementedException("Unexpected default case") }; } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Extensions/ValueSetExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Extensions { using System; using System.Collections; using System.Collections.Generic; using Windows.Foundation.Collections; /// /// Extensions for ValueSet. /// internal static class ValueSetExtensions { /// /// The value in a ValueSet that indicates that it is an array of items. /// internal const string TreatAsArray = "treatAsArray"; /// /// Extension method to transform a ValueSet to a Hashtable. /// /// Value set. /// A hashtable. public static Hashtable ToHashtable(this ValueSet valueSet) { var hashtable = new Hashtable(); foreach (var keyValuePair in valueSet) { if (keyValuePair.Value is ValueSet innerValueSet) { if (innerValueSet.ContainsKey(TreatAsArray)) { hashtable.Add(keyValuePair.Key, innerValueSet.ToArray()); } else { hashtable.Add(keyValuePair.Key, innerValueSet.ToHashtable()); } } else { hashtable.Add(keyValuePair.Key, keyValuePair.Value); } } return hashtable; } /// /// Gets ordered list from a ValueSet that is threated as an array. /// /// ValueSet. /// Ordered list. public static IList ToArray(this ValueSet valueSet) { if (!valueSet.ContainsKey(TreatAsArray)) { throw new InvalidOperationException(); } var sortedList = new SortedList(); foreach (var keyValuePair in valueSet) { if (keyValuePair.Key == TreatAsArray) { continue; } if (int.TryParse(keyValuePair.Key, out int key)) { if (keyValuePair.Value is ValueSet innerValueSet) { sortedList.Add(key, innerValueSet.ToHashtable()); } else { sortedList.Add(key, keyValuePair.Value); } } else { throw new InvalidOperationException($"Invalid key for ValueSet to array {keyValuePair.Key}"); } } return sortedList.Values; } /// /// Performs a deep compare of the ValueSets. /// /// First ValueSet. /// Second ValueSet. /// Whether the two ValueSets equal. public static bool ContentEquals(this ValueSet first, ValueSet second) { if (first.Count != second.Count) { return false; } foreach (var keyValuePair in first) { string key = keyValuePair.Key; if (!second.ContainsKey(key)) { return false; } var firstValue = keyValuePair.Value; var secondValue = second[key]; // Empty value check. if (firstValue == null && secondValue == null) { continue; } else if (firstValue == null || secondValue == null) { return false; } // Try as ValueSet. var firstValueSet = firstValue as ValueSet; var secondValueSet = secondValue as ValueSet; if (firstValueSet != null && secondValueSet != null) { if (!firstValueSet.ContentEquals(secondValueSet)) { return false; } else { continue; } } else if (firstValueSet != null || secondValueSet != null) { return false; } // Try as scalar. if (firstValue is string firstString && secondValue is string secondString) { if (firstString != secondString) { return false; } } else if (firstValue is long firstLong && secondValue is long secondLong) { if (firstLong != secondLong) { return false; } } else if (firstValue is bool firstBool && secondValue is bool secondBool) { if (firstBool != secondBool) { return false; } } else { // Note: DateTime and float are not supported in parser yet. return false; } } return true; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Factory/ConfigurationSetProcessorFactoryBase.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Factory { using System; using System.Runtime.CompilerServices; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.DSCv3.Helpers; using Microsoft.Management.Configuration.Processor.Set; /// /// IConfigurationSetProcessorFactory base implementation. /// internal abstract partial class ConfigurationSetProcessorFactoryBase : IDiagnosticsSink { private bool isCreateProcessorInvoked = false; // Backing variables for properties that are restricted in limit mode. private ConfigurationSet? limitationSet; /// /// Initializes a new instance of the class. /// public ConfigurationSetProcessorFactoryBase() { } /// /// Diagnostics event; useful for logging and/or verbose output. /// public event EventHandler? Diagnostics; /// /// Gets or sets the minimum diagnostic level to send. /// public DiagnosticLevel MinimumLevel { get; set; } = DiagnosticLevel.Informational; /// /// Gets or sets the limitation set. Limitation set can only be set once. /// public ConfigurationSet? LimitationSet { get { return this.limitationSet; } set { if (this.IsLimitMode()) { throw new InvalidOperationException("Setting LimitationSet in limit mode is invalid."); } this.limitationSet = value; } } /// /// Gets the configuration unit processor details for the given unit. /// /// Configuration Set. /// Configuration set processor. public IConfigurationSetProcessor CreateSetProcessor(ConfigurationSet? incomingSet) { try { bool isLimitMode = this.IsLimitMode(); this.OnDiagnostics(DiagnosticLevel.Informational, $"The set processor factory is running in limit mode: {isLimitMode}."); this.CheckLimitMode(); ConfigurationSet? set = isLimitMode ? this.limitationSet : incomingSet; this.OnDiagnostics(DiagnosticLevel.Verbose, $"Creating set processor for `{set?.Name ?? ""}`..."); if (set != null && (set.Parameters.Count > 0 || set.Variables.Count > 0)) { this.OnDiagnostics(DiagnosticLevel.Error, $" Parameters/variables are not yet supported."); throw new NotImplementedException(); } IConfigurationSetProcessor result = this.CreateSetProcessorInternal(set, isLimitMode); this.OnDiagnostics(DiagnosticLevel.Verbose, "... done creating set processor."); return result; } catch (Exception ex) { this.OnDiagnostics(DiagnosticLevel.Error, ex.ToString()); throw; } } /// void IDiagnosticsSink.OnDiagnostics(DiagnosticLevel level, string message) { this.OnDiagnostics(level, message); } /// /// Sends diagnostics if appropriate. /// /// The level of this diagnostic message. /// The diagnostic message. internal void OnDiagnostics(DiagnosticLevel level, string message) { EventHandler? diagnostics = this.Diagnostics; if (diagnostics != null && level >= this.MinimumLevel) { this.InvokeDiagnostics(diagnostics, level, message); } } /// /// Determines if diagnostics are enabled. /// This allows optimizing out string construction for some cases. /// /// True if diagnostics are enabled; false if not. protected bool AreDiagnosticsEnabled() { return this.Diagnostics != null; } /// /// Gets the configuration unit processor details for the given unit. /// /// Configuration Set. /// Whether the processor should be in limit mode. /// Configuration set processor. protected abstract IConfigurationSetProcessor CreateSetProcessorInternal(ConfigurationSet? set, bool isLimitMode); /// /// Gets a value indicating whether the factory is operation in limit mode. /// /// True if the factory is in limit mode, false otherwise. protected bool IsLimitMode() { return this.limitationSet != null; } /// /// Sends diagnostics to the given handler. /// /// The handler to invoke. /// The level of this diagnostic message. /// The diagnostic message. private void InvokeDiagnostics(EventHandler diagnostics, DiagnosticLevel level, string message) { Helpers.DiagnosticInformation information = new () { Level = level, Message = message, }; diagnostics.Invoke(this, information); } [MethodImpl(MethodImplOptions.Synchronized)] private void CheckLimitMode() { if (!this.IsLimitMode()) { return; } if (this.isCreateProcessorInvoked) { this.OnDiagnostics(DiagnosticLevel.Error, "CreateSetProcessor is already invoked in limit mode."); throw new InvalidOperationException("CreateSetProcessor is already invoked in limit mode."); } else { this.isCreateProcessorInvoked = true; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Helpers/ConfigurationUnitInternal.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Helpers { using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using Microsoft.Management.Configuration.Processor.Constants; using Microsoft.Management.Configuration.Processor.Exceptions; using Windows.Foundation.Collections; /// /// Wrapper around Configuration units and its directives. /// Creates a normalized directives map for consumption. /// internal class ConfigurationUnitInternal { private const string ConfigRootVar = "${WinGetConfigRoot}"; private readonly string? configurationFileRootPath = null; private readonly Dictionary normalizedDirectives = new (); /// /// Initializes a new instance of the class. /// /// Configuration unit. /// The configuration file path. public ConfigurationUnitInternal( ConfigurationUnit unit, string? configurationFilePath) { this.Unit = unit; this.InitializeDirectives(); this.InitializeNames(); if (!string.IsNullOrEmpty(configurationFilePath)) { if (!File.Exists(configurationFilePath)) { throw new FileNotFoundException(configurationFilePath); } this.configurationFileRootPath = Path.GetDirectoryName(configurationFilePath); } } /// /// Gets the configuration unit. /// public ConfigurationUnit Unit { get; } /// /// Gets a value indicating whether the unit type should be treated as the resource name. /// public bool UnitTypeIsResourceName { get; init; } = false; /// /// Gets the resource name *only*. For example, "Resource". /// public string ResourceName { get; private set; } /// /// Gets the qualified name, which includes the module. For example, "Module/Resource". /// public string QualifiedName { get; private set; } /// /// Gets the directive value from the unit taking into account the directives overlay. /// /// Directive name. /// Value of directive, null if not found. /// Directive type value. public TType? GetDirective(string directiveName) where TType : class { var normalizedDirectiveName = StringHelpers.Normalize(directiveName); if (this.normalizedDirectives.TryGetValue(normalizedDirectiveName, out object? value)) { return value as TType; } return null; } /// /// Gets the bool value of a directive from the unit taking into account the directives overlay. /// /// Directive name. /// Value of directive, false if not found. public bool? GetDirective(string directiveName) { var normalizedDirectiveName = StringHelpers.Normalize(directiveName); if (this.normalizedDirectives.TryGetValue(normalizedDirectiveName, out object? value)) { return value as bool?; } return null; } /// /// Gets the semantic version, if any. /// /// SemanticVersion, null if not specified. public SemanticVersion? GetSemanticVersion() { string? semanticVersion = this.GetDirective(DirectiveConstants.Version); if (!string.IsNullOrWhiteSpace(semanticVersion)) { return new SemanticVersion(semanticVersion); } return null; } /// /// Gets the semantic min version, if any. /// /// SemanticVersion, null if not specified. public SemanticVersion? GetSemanticMinVersion() { string? semanticVersion = this.GetDirective(DirectiveConstants.MinVersion); if (!string.IsNullOrWhiteSpace(semanticVersion)) { return new SemanticVersion(semanticVersion); } return null; } /// /// Gets the semantic max version, if any. /// /// SemanticVersion, null if not specified. public SemanticVersion? GetSemanticMaxVersion() { string? semanticVersion = this.GetDirective(DirectiveConstants.MaxVersion); if (!string.IsNullOrWhiteSpace(semanticVersion)) { return new SemanticVersion(semanticVersion); } return null; } /// /// TODO: Implement for more variables. /// I am so sad because rs.SessionStateProxy.InvokeCommand.ExpandString doesn't work as I wanted. /// PowerShell assumes all code passed to ExpandString is trusted and we cannot assume that. /// /// ValueSet with settings. public ValueSet GetExpandedSettings() { var valueSet = new ValueSet(); foreach (var value in this.Unit.Settings) { if (value.Value is string) { // For now, we just expand config root. valueSet.Add(value.Key, this.ExpandConfigRoot(value.Value as string, value.Key)); } else { valueSet.Add(value); } } return valueSet; } private string? ExpandConfigRoot(string? value, string settingName) { if (!string.IsNullOrEmpty(value)) { // TODO: since we only support one variable, this only finds and replace // ${WingetConfigRoot} if found in the string when the work of expanding // string is done it should take into account other operators like the subexpression operator $() if (value.Contains(ConfigRootVar, StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(this.configurationFileRootPath)) { throw new UnitSettingConfigRootException(this.QualifiedName, settingName); } return value.Replace(ConfigRootVar, this.configurationFileRootPath, StringComparison.OrdinalIgnoreCase); } } return value; } private void InitializeDirectives() { foreach (var directive in this.Unit.Metadata) { var normalizedKey = StringHelpers.Normalize(directive.Key); this.normalizedDirectives.Add(normalizedKey, directive.Value); } } private string ConstructQualifiedName(string? moduleName) { return $"{(moduleName == null ? string.Empty : $"{moduleName}/")}{this.ResourceName}"; } [MemberNotNull(nameof(ResourceName), nameof(QualifiedName))] private void InitializeNames() { // Determine ResourceName, QualifiedName, and the module directive string unitType = this.Unit.Type; string? moduleDirective = this.GetDirective(DirectiveConstants.Module); if (this.UnitTypeIsResourceName) { this.ResourceName = unitType; this.QualifiedName = this.ConstructQualifiedName(moduleDirective); return; } int unitTypeDividerPosition = unitType.IndexOf('/'); if (unitTypeDividerPosition == unitType.Length - 1) { throw new ArgumentException($"Invalid unit Type: {unitType}"); } string? moduleName; if (unitTypeDividerPosition == -1) { moduleName = moduleDirective; this.ResourceName = unitType; this.QualifiedName = this.ConstructQualifiedName(moduleDirective); } else { moduleName = unitType.Substring(0, unitTypeDividerPosition); this.ResourceName = unitType.Substring(unitTypeDividerPosition + 1); this.QualifiedName = unitType; } if (moduleName != null) { if (moduleDirective != null) { if (moduleName != moduleDirective) { throw new ArgumentException($"Mismatched module specifiers: {moduleName} != {moduleDirective}"); } } else { this.normalizedDirectives.Add(DirectiveConstants.Module, moduleName); } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Helpers/DiagnosticInformation.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Helpers { using System; using Microsoft.Management.Configuration; /// /// Implements IDiagnosticInformation. /// internal sealed partial class DiagnosticInformation : IDiagnosticInformation { /// public DiagnosticLevel Level { get; internal set; } /// public string? Message { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Helpers/SemanticVersion.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Helpers { using System; /// /// A semantic version. /// internal class SemanticVersion { private const string MaxRange = "999999999"; private readonly string semanticVersion; /// /// Initializes a new instance of the class. /// /// Version. public SemanticVersion(string version) { this.semanticVersion = GetMaximumVersion(version); // Prerelease versions append the prerelease tag after a - // PowerShell doesn't handle semantic versions. if (this.semanticVersion.Contains("-")) { var indexOf = this.semanticVersion.IndexOf("-"); this.Version = new Version(this.semanticVersion[..indexOf]); this.PrereleaseTag = this.semanticVersion[(indexOf + 1) ..]; } else { this.Version = new Version(this.semanticVersion); } } /// /// Gets the version without a prerelease tag. /// public Version Version { get; } /// /// Gets prerelease tag if any. /// public string? PrereleaseTag { get; } = null; /// /// Gets a value indicating whether if the semantic version is prerelease. /// public bool IsPrerelease => !string.IsNullOrEmpty(this.PrereleaseTag); /// public override string ToString() { return this.semanticVersion; } /// /// Max out a version by replacing * if needed. /// /// Version. /// Maxed version. private static string GetMaximumVersion(string version) { return version.Replace("*", MaxRange); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Helpers/StringHelpers.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Helpers { /// /// String helpers. /// internal static class StringHelpers { /// /// Normalize string. /// /// Value. /// Normalized string. public static string Normalize(string value) { return value.ToLowerInvariant(); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Helpers/TypeHelpers.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Helpers { using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.SetProcessorFactory; using Windows.Foundation.Collections; /// /// Type helpers. /// internal static class TypeHelpers { /// /// Verifies a property exists. /// /// Dynamic object. /// Name of property. /// True if property exists. public static bool PropertyExists(dynamic obj, string name) { return obj.GetType().GetProperty(name) is not null; } /// /// Verifies a property exists with the specified type. /// /// Expected type. /// Dynamic object. /// Name of property. /// True if property and is of the specified type. public static bool PropertyWithTypeExists(dynamic obj, string name) { return PropertyExists(obj, name) && obj.GetType().GetProperty(name).PropertyType == typeof(TType); } /// /// Verifies the property exist and is an enum. /// /// Dynamic object. /// Name of property. /// True if property exists and is an enum. public static bool PropertyExistsAndIsEnum(dynamic obj, string name) { return PropertyExists(obj, name) && obj.GetType().GetProperty(name).PropertyType.IsEnum; } /// /// Verifies the property exists and is a list. Use this when you don't know the type of the List. /// /// Dynamic object. /// Name of property. /// True if property exists and is a list. public static bool PropertyExistsAndIsList(dynamic obj, string name) { return PropertyExists(obj, name) && obj.GetType().GetProperty(name).PropertyType.IsGenericType && obj.GetType().GetProperty(name).PropertyType.GetGenericTypeDefinition() == typeof(List<>); } /// /// Gets all the properties and values from an object. /// /// Object. /// ValueSet with properties names and values. public static ValueSet GetAllPropertiesValues(object obj) { var result = new ValueSet(); foreach (PropertyInfo property in obj.GetType().GetProperties()) { var key = property.Name; var value = GetCompatibleValueSetValueOfProperty(property.PropertyType, property.GetValue(obj)); result.Add(key, value); } return result; } /// /// Gets a compatible type for a ValueSet value. /// /// Type. /// Value. /// Value converted to a compatible type. public static object? GetCompatibleValueSetValueOfProperty(Type type, object? value) { if (value == null) { return null; } // Specialize here. if (type.IsEnum) { return value.ToString(); } else if (type == typeof(Hashtable)) { Hashtable hashtable = (Hashtable)value; return hashtable.ToValueSet(); } else if (type.IsArray) { var valueSetArray = new ValueSet(); int index = 0; foreach (object arrayObj in (Array)value) { var arrayValue = GetCompatibleValueSetValueOfProperty(arrayObj.GetType(), arrayObj); if (arrayValue != null) { valueSetArray.Add(index.ToString(), arrayValue); index++; } } if (valueSetArray.Count > 0) { valueSetArray.Add("treatAsArray", true); } return valueSetArray; } else if (type == typeof(string)) { // Ignore empty strings. string propertyString = (string)value; if (!string.IsNullOrEmpty(propertyString)) { return propertyString; } else { return null; } } else if (type.IsValueType) { return value; } // This might be too restrictive but anything else is going to be some object that we don't support anyway. throw new UnitPropertyUnsupportedException(value.GetType()); } /// /// Converts PowerShellConfigurationProcessorPolicy string value to PwshConfigurationProcessorPolicy. /// /// PowerShellConfigurationProcessorPolicy value. /// PwshConfigurationProcessorPolicy. public static PwshConfigurationProcessorPolicy ToPwshConfigurationProcessorPolicy(PowerShellConfigurationProcessorPolicy value) { return value switch { PowerShellConfigurationProcessorPolicy.Unrestricted => PwshConfigurationProcessorPolicy.Unrestricted, PowerShellConfigurationProcessorPolicy.RemoteSigned => PwshConfigurationProcessorPolicy.RemoteSigned, PowerShellConfigurationProcessorPolicy.AllSigned => PwshConfigurationProcessorPolicy.AllSigned, PowerShellConfigurationProcessorPolicy.Restricted => PwshConfigurationProcessorPolicy.Restricted, PowerShellConfigurationProcessorPolicy.Bypass => PwshConfigurationProcessorPolicy.Bypass, PowerShellConfigurationProcessorPolicy.Undefined => PwshConfigurationProcessorPolicy.Undefined, _ => throw new InvalidOperationException(), }; } /// /// Converts PwshConfigurationProcessorPolicy string value to PowerShellConfigurationProcessorPolicy. /// /// PwshConfigurationProcessorPolicy value. /// PowerShellConfigurationProcessorPolicy. public static PowerShellConfigurationProcessorPolicy ToPowerShellConfigurationProcessorPolicy(PwshConfigurationProcessorPolicy value) { return value switch { PwshConfigurationProcessorPolicy.Unrestricted => PowerShellConfigurationProcessorPolicy.Unrestricted, PwshConfigurationProcessorPolicy.RemoteSigned => PowerShellConfigurationProcessorPolicy.RemoteSigned, PwshConfigurationProcessorPolicy.AllSigned => PowerShellConfigurationProcessorPolicy.AllSigned, PwshConfigurationProcessorPolicy.Restricted => PowerShellConfigurationProcessorPolicy.Restricted, PwshConfigurationProcessorPolicy.Bypass => PowerShellConfigurationProcessorPolicy.Bypass, PwshConfigurationProcessorPolicy.Undefined => PowerShellConfigurationProcessorPolicy.Undefined, _ => throw new InvalidOperationException(), }; } /// /// Converts PowerShellConfigurationProcessorLocation string value to PwshConfigurationProcessorLocation. /// /// PowerShellConfigurationProcessorLocation value. /// PwshConfigurationProcessorLocation. public static PwshConfigurationProcessorLocation ToPwshConfigurationProcessorLocation(PowerShellConfigurationProcessorLocation value) { return value switch { PowerShellConfigurationProcessorLocation.CurrentUser => PwshConfigurationProcessorLocation.CurrentUser, PowerShellConfigurationProcessorLocation.AllUsers => PwshConfigurationProcessorLocation.AllUsers, PowerShellConfigurationProcessorLocation.WinGetModulePath => PwshConfigurationProcessorLocation.WinGetModulePath, PowerShellConfigurationProcessorLocation.Custom => PwshConfigurationProcessorLocation.Custom, _ => throw new InvalidOperationException(), }; } /// /// Converts PwshConfigurationProcessorLocation string value to PowerShellConfigurationProcessorLocation. /// /// PwshConfigurationProcessorLocation value. /// PowerShellConfigurationProcessorLocation. public static PowerShellConfigurationProcessorLocation ToPowerShellConfigurationProcessorLocation(PwshConfigurationProcessorLocation value) { return value switch { PwshConfigurationProcessorLocation.CurrentUser => PowerShellConfigurationProcessorLocation.CurrentUser, PwshConfigurationProcessorLocation.AllUsers => PowerShellConfigurationProcessorLocation.AllUsers, PwshConfigurationProcessorLocation.WinGetModulePath => PowerShellConfigurationProcessorLocation.WinGetModulePath, PwshConfigurationProcessorLocation.Custom => PowerShellConfigurationProcessorLocation.Custom, _ => throw new InvalidOperationException(), }; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Microsoft.Management.Configuration.Processor.csproj ================================================ net8.0 $(DotNetVersion)-windows10.0.26100.0 enable 10.0.17763.0 false AnyCpu win $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ true None 1591,8785 Debug;Release;ReleaseStatic true true true true true 10.0.26100.0 contentFiles all Content PreserveNewest True Content Always $(DefineConstants);WinGetCsWinRTEmbedded false Microsoft.Management.Configuration; Windows.ApplicationModel.AppDisplayInf; Windows.ApplicationModel.IAppDisplayInf; Windows.ApplicationModel.AppExecutionContex; Windows.ApplicationModel.IAppExecutionContex; Windows.ApplicationModel.AppInf; Windows.ApplicationModel.IAppInf; Windows.ApplicationModel.AppInstallerInf; Windows.ApplicationModel.IAppInstallerInf; Windows.ApplicationModel.AppInstallerPolicySourc; Windows.ApplicationModel.AddResourcePackageOption; Windows.ApplicationModel.IAddResourcePackageOption; Windows.ApplicationModel.FindRelatedPackagesOption; Windows.ApplicationModel.IFindRelatedPackagesOption; Windows.ApplicationModel.Packag; Windows.ApplicationModel.IPackag; Windows.ApplicationModel.Core.AppListEntr; Windows.ApplicationModel.Core.IAppListEntr; Windows.Data.Text.TextSegmen; Windows.Management.Deployment; Windows.Devices.Geolocation; Windows.Foundation; Windows.Globalization.DayOfWee; Windows.Networking.Connectivity; Windows.Networking.DomainNameTyp; Windows.Networking.EndpointPai; Windows.Networking.IEndpointPai; Windows.Networking.HostNam; Windows.Networking.IHostNam; Windows.Security.Cryptography.Certificates; Windows.Storage; Windows.Storage.Provider.FileUpdateStatu; Windows.System.ProcessorArchitectur; Windows.System.Use; Windows.System.IUse; Windows.Foundation.PropertyType; Windows.Storage.Provider; 10.0.17763.0 $(OutputPath)..\Microsoft.Management.Configuration\Microsoft.Management.Configuration.winmd $(SolutionDir)x64\$(Configuration)\Microsoft.Management.Configuration\Microsoft.Management.Configuration.winmd $(SolutionDir)x86\$(Configuration)\Microsoft.Management.Configuration\Microsoft.Management.Configuration.winmd $(SolutionDir)arm64\$(Configuration)\Microsoft.Management.Configuration\Microsoft.Management.Configuration.winmd ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Constants/PowerShellConstants.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Constants { /// /// Constants related to PowerShell. /// internal static class PowerShellConstants { #pragma warning disable SA1600 // ElementsMustBeDocumented public const string Core = "Core"; internal static class Variables { public const string PSEdition = "PSEdition"; public const string Error = "Error"; public const string PSModulePath = "env:PSModulePath"; } internal static class Modules { public const string PSDesiredStateConfiguration = "PSDesiredStateConfiguration"; public const string PSDesiredStateConfigurationMinVersion = "2.0.7"; public const string PowerShellGet = "PowerShellGet"; public const string PowerShellGetMinVersion = "2.2.5"; public const string PSDesiredStateConfigurationMaxVersion = "2.*"; } internal static class Commands { public const string FindDscResource = "Find-DscResource"; public const string GetAuthenticodeSignature = "Get-AuthenticodeSignature"; public const string GetChildItem = "Get-ChildItem"; public const string GetCommand = "Get-Command"; public const string GetDscResource = "Get-DscResource"; public const string GetInstalledModule = "Get-InstalledModule"; public const string GetModule = "Get-Module"; public const string ImportModule = "Import-Module"; public const string InstallModule = "Install-Module"; public const string InvokeDscResource = "Invoke-DscResource"; public const string SaveModule = "Save-Module"; public const string FindModule = "Find-Module"; public const string ImportCliXml = "Import-CliXml"; } internal static class Parameters { public const string AllowPrerelease = "AllowPrerelease"; public const string Force = "Force"; public const string FullyQualifiedName = "FullyQualifiedName"; public const string Guid = "GUID"; public const string InputObject = "InputObject"; public const string ListAvailable = "ListAvailable"; public const string MaximumVersion = "MaximumVersion"; public const string Method = "Method"; public const string MinimumVersion = "MinimumVersion"; public const string Module = "Module"; public const string ModuleName = "ModuleName"; public const string ModuleVersion = "ModuleVersion"; public const string Name = "Name"; public const string Path = "Path"; public const string Property = "Property"; public const string Recurse = "Recurse"; public const string Repository = "Repository"; public const string RequiredVersion = "RequiredVersion"; public const string Scope = "Scope"; } internal static class DscMethods { public const string Get = "Get"; public const string Set = "Set"; public const string Test = "Test"; } #pragma warning restore SA1600 // ElementsMustBeDocumented } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/DscModules/DscModuleV2.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.DscModules { using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.PowerShell.Extensions; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.PowerShell.Commands; using Windows.Foundation.Collections; using static Microsoft.Management.Configuration.Processor.PowerShell.Constants.PowerShellConstants; /// /// PSDesiredStateConfiguration v2. /// internal class DscModuleV2 : IDscModule { private const string InDesiredState = "InDesiredState"; private const string RebootRequired = "RebootRequired"; private static readonly IEnumerable ExclusionResourcesParentPath = new string[] { @"C:\WINDOWS\system32\WindowsPowershell\v1.0\Modules\PsDesiredStateConfiguration\DscResources", @"C:\Program Files\WindowsPowerShell\Modules\PackageManagement\1.0.0.1", }; /// /// Initializes a new instance of the class. /// public DscModuleV2() { this.ModuleSpecification = PowerShellHelpers.CreateModuleSpecification( Modules.PSDesiredStateConfiguration, minVersion: Modules.PSDesiredStateConfigurationMinVersion, maxVersion: Modules.PSDesiredStateConfigurationMaxVersion); } /// public ModuleSpecification ModuleSpecification { get; private init; } /// public string GetDscResourceCmd { get; } = Commands.GetDscResource; /// public string InvokeDscResourceCmd { get; } = Commands.InvokeDscResource; /// public IReadOnlyList GetAllDscResources(PowerShell pwsh) { return this.GetDscResources(pwsh, null, null); } /// public IReadOnlyList GetDscResourcesInModule( PowerShell pwsh, ModuleSpecification moduleSpecification) { return this.GetDscResources(pwsh, null, moduleSpecification); } /// public DscResourceInfoInternal? GetDscResource( PowerShell pwsh, string name, ModuleSpecification? moduleSpecification) { var dscResourceInfos = this.GetDscResources(pwsh, name, moduleSpecification); if (dscResourceInfos.Count == 0) { return null; } else if (dscResourceInfos.Count > 1) { throw new GetDscResourceMultipleMatches(name, moduleSpecification); } return dscResourceInfos[0]; } /// public ValueSet InvokeGetResource( PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification) { PSObject? getResult = null; try { getResult = pwsh.AddCommand(this.InvokeDscResourceCmd) .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification)) .AddParameter(Parameters.Method, DscMethods.Get) .InvokeAndStopOnError() .FirstOrDefault(); } catch (Exception ex) { throw new InvokeDscResourceException(InvokeDscResourceException.Get, name, moduleSpecification, ex); } string? errorMessage = pwsh.GetErrorMessage(); if (errorMessage is not null) { throw new InvokeDscResourceException(InvokeDscResourceException.Get, name, moduleSpecification, errorMessage); } if (getResult is null) { throw new InvokeDscResourceException(InvokeDscResourceException.Get, name, moduleSpecification); } // Script based resource. if (getResult.BaseObject is Hashtable) { Hashtable hashTable = (Hashtable)getResult.BaseObject; var resultSettings = new ValueSet(); foreach (DictionaryEntry entry in hashTable) { resultSettings.Add(entry.Key as string, entry.Value); } return resultSettings; } // Class-based resource. return TypeHelpers.GetAllPropertiesValues(getResult.BaseObject); } /// public bool InvokeTestResource( PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification) { // Returned type is InvokeDscResourceTestResult which is a PowerShell classed defined // in PSDesiredStateConfiguration.psm1. dynamic? testResult = null; try { testResult = pwsh.AddCommand(this.InvokeDscResourceCmd) .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification)) .AddParameter(Parameters.Method, DscMethods.Test) .InvokeAndStopOnError() .FirstOrDefault(); } catch (Exception ex) { throw new InvokeDscResourceException(InvokeDscResourceException.Test, name, moduleSpecification, ex); } string? errorMessage = pwsh.GetErrorMessage(); if (errorMessage is not null) { throw new InvokeDscResourceException(InvokeDscResourceException.Test, name, moduleSpecification, errorMessage); } if (testResult is null || !TypeHelpers.PropertyWithTypeExists(testResult, InDesiredState)) { throw new InvokeDscResourceException(InvokeDscResourceException.Test, name, moduleSpecification); } return testResult?.InDesiredState; } /// public bool InvokeSetResource( PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification) { // Returned type is InvokeDscResourceSetResult which is a PowerShell classed defined // in PSDesiredStateConfiguration.psm1. dynamic? setResult = null; try { setResult = pwsh.AddCommand(this.InvokeDscResourceCmd) .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification)) .AddParameter(Parameters.Method, DscMethods.Set) .InvokeAndStopOnError() .FirstOrDefault(); } catch (Exception ex) { throw new InvokeDscResourceException(InvokeDscResourceException.Set, name, moduleSpecification, ex); } string? errorMessage = pwsh.GetErrorMessage(); if (errorMessage is not null) { throw new InvokeDscResourceException(InvokeDscResourceException.Set, name, moduleSpecification, errorMessage, pwsh.ContainsPropertyError()); } if (setResult is null || !TypeHelpers.PropertyWithTypeExists(setResult, RebootRequired)) { throw new InvokeDscResourceException(InvokeDscResourceException.Set, name, moduleSpecification); } return setResult?.RebootRequired; } private static Dictionary PrepareInvokeParameters( string name, ValueSet settings, ModuleSpecification? moduleSpecification) { Hashtable properties = settings.ToHashtable(); var parameters = new Dictionary() { { Parameters.Name, name }, { Parameters.Property, properties }, }; if (moduleSpecification is not null) { parameters.Add(Parameters.ModuleName, moduleSpecification); } return parameters; } private IReadOnlyList GetDscResources( PowerShell pwsh, string? name, ModuleSpecification? moduleSpecification) { pwsh.AddCommand(this.GetDscResourceCmd); if (name is not null) { pwsh.AddParameter(Parameters.Name, name); } if (moduleSpecification is not null) { pwsh.AddParameter(Parameters.Module, moduleSpecification); } try { var resources = pwsh.Invoke(); return this.ConvertToDscResourceInfoInternal(resources); } catch (RuntimeException e) { // Detect easily this. if (e.ErrorRecord.FullyQualifiedErrorId == "ExceptionWhenSetting,GetResourceFromKeyword") { throw new GetDscResourceModuleConflict(name, moduleSpecification, e); } throw; } } private List ConvertToDscResourceInfoInternal(Collection psObjects) { var result = new List(); foreach (dynamic psObject in psObjects) { var dscResourceInfo = new DscResourceInfoInternal(psObject); // Explicitly don't support old DSC resources from v1 PSDesiredStateConfiguration. // Even if the Windows System32 Windows PowerShell module path is removed they // will show up. if (ExclusionResourcesParentPath.Any(e => dscResourceInfo.ParentPath!.StartsWith(e))) { continue; } result.Add(dscResourceInfo); } return result; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/DscModules/IDscModule.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.DscModules { using System.Collections.Generic; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.PowerShell.Commands; using Windows.Foundation.Collections; /// /// Interface for defining a PSDesiredStateConfiguration module. /// internal interface IDscModule { /// /// Gets the module specification. /// ModuleSpecification ModuleSpecification { get; } /// /// Gets the name of the Get-DscResource Cmdlet. /// string GetDscResourceCmd { get; } /// /// Gets the name of the Invoke-DscResource Cmdlet. /// string InvokeDscResourceCmd { get; } /// /// Gets all DSC resource. /// /// PowerShell. /// A list with the DSC resource. IReadOnlyList GetAllDscResources(PowerShell pwsh); /// /// Gets all resources in a module. /// /// PowerShell. /// Module specification. /// List of resources of that module and version. IReadOnlyList GetDscResourcesInModule(PowerShell pwsh, ModuleSpecification moduleSpecification); /// /// Gets a DSC Resource. /// /// PowerShell. /// Name. /// Module specification. /// DSC Resource from that module and version. DscResourceInfoInternal? GetDscResource(PowerShell pwsh, string name, ModuleSpecification? moduleSpecification); /// /// Calls Invoke-DscResource -Method Get from this module. /// /// PowerShell. /// Settings. /// Name. /// Module specification. /// Properties of resource. ValueSet InvokeGetResource(PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification); /// /// Calls Invoke-DscResource -Method Test from this module. /// /// PowerShell. /// Settings. /// Name. /// Module specification. /// Is in desired state. bool InvokeTestResource(PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification); /// /// Calls Invoke-DscResource -Method Set from this module. /// /// PowerShell. /// Settings. /// Name. /// Module specification. /// If a reboot is required. bool InvokeSetResource(PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification); } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/DscResourcesInfo/DscResourceInfoInternal.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo { using System; using System.Collections.Generic; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.Helpers; /// /// Contains a DSC resource information. /// This object should be initialized with a Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo. /// That object is the result of Get-DscResource and is not the same as System.Management.Automation.DscResourceInfo. /// The type is defined in a psd1 in the PSDesiredStateConfiguration. This file is based on that. /// If Invoke-DscResource gets support for passing the DscResourceInfo, we could keep the original object as member /// to then pass it in code. /// internal class DscResourceInfoInternal { private const string DscResourceInfoFullName = "Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo"; /// /// Initializes a new instance of the class. /// /// Dynamic object. Expected DscResourceInfo. public DscResourceInfoInternal(dynamic info) { if (info.GetType().FullName != DscResourceInfoFullName) { throw new ArgumentException(); } this.ResourceType = info.ResourceType; this.Name = info.Name; this.FriendlyName = info.FriendlyName; this.Module = info.Module; this.Path = info.Path; this.ParentPath = info.ParentPath; this.ImplementedAs = Enum.Parse(info.ImplementedAs.ToString()); this.CompanyName = info.CompanyName; if (this.Module is not null) { this.ModuleName = this.Module.Name; this.Version = this.Module.Version; } foreach (object property in info.Properties) { this.Properties.Add(new DscResourcePropertyInfoInternal(property)); } } /// /// Initializes a new instance of the class. /// Used for unit tests. /// /// Resource name. /// Module name. /// Version. internal DscResourceInfoInternal(string name, string? moduleName, Version? version) { this.Name = name; this.ModuleName = moduleName; this.Version = version; } /// /// Gets or sets resource type name. /// public string? ResourceType { get; set; } /// /// Gets or sets Name of the resource. This name is used to access the resource. /// public string Name { get; set; } /// /// Gets the normalized resource name. /// public string NormalizedName { get { return StringHelpers.Normalize(this.Name); } } /// /// Gets or sets friendly name defined for the resource. /// public string? FriendlyName { get; set; } /// /// Gets or sets module which implements the resource. This could point to parent module, if the DSC resource is implemented. /// by one of nested modules. /// public PSModuleInfo? Module { get; set; } /// /// Gets or sets name of the module which implements the resource. /// public string? ModuleName { get; set; } /// /// Gets the normalized module name. /// public string? NormalizedModuleName { get { return this.ModuleName is not null ? StringHelpers.Normalize(this.ModuleName) : null; } } /// /// Gets or sets version of the module which implements the resource. /// public Version? Version { get; set; } /// /// Gets or sets of the file which implements the resource. For the resources which are defined using /// MOF file, this will be path to a module which resides in the same folder where schema.mof file is present. /// For composite resources, this will be the module which implements the resource. /// public string? Path { get; set; } /// /// Gets or sets parent folder, where the resource is defined /// It is the folder containing either the implementing module(=Path) or folder containing ".schema.mof". /// For native providers, Path will be null and only ParentPath will be present. /// public string? ParentPath { get; set; } /// /// Gets or sets a value which indicate how DSC resource is implemented. /// public ImplementedAsTypeInternal? ImplementedAs { get; set; } /// /// Gets or sets company which owns this resource. /// public string? CompanyName { get; set; } /// /// Gets properties of the resource. /// public List Properties { get; private set; } = new List(); /// /// Updates properties of the resource. /// /// Updated properties. public void UpdateProperties(List properties) { this.Properties = properties; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/DscResourcesInfo/DscResourcePropertyInfoInternal.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo { using System; using System.Collections.Generic; /// /// Contains a DSC resource property information. /// internal sealed class DscResourcePropertyInfoInternal { private const string DscResourcePropertyInfoFullName = "Microsoft.PowerShell.DesiredStateConfiguration.DscResourcePropertyInfo"; /// /// Initializes a new instance of the class. /// /// Dynamic DSC property info. public DscResourcePropertyInfoInternal(dynamic dscPropertyInfo) { if (dscPropertyInfo.GetType().FullName != DscResourcePropertyInfoFullName) { throw new ArgumentException(); } this.Name = dscPropertyInfo.Name; this.PropertyType = dscPropertyInfo.PropertyType; this.IsMandatory = dscPropertyInfo.IsMandatory; this.Values = dscPropertyInfo.Values; } /// /// Gets or sets name of the property. /// public string Name { get; set; } /// /// Gets or sets type of the property. /// public string PropertyType { get; set; } /// /// Gets or sets a value indicating whether the property is mandatory or not. /// public bool IsMandatory { get; set; } /// /// Gets Values for a resource property. /// public List Values { get; private set; } = new List(); } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/DscResourcesInfo/ImplementedAsTypeInternal.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo { /// /// Enumerated values for DSC resource implementation type. /// internal enum ImplementedAsTypeInternal { /// /// DSC resource implementation type not known. /// None = 0, /// /// DSC resource is implemented using PowerShell module. /// PowerShell = 1, /// /// DSC resource is implemented using a CIM provider. /// Binary = 2, /// /// DSC resource is a composite and implemented using configuration keyword. /// Composite = 3, } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Extensions/PowerShellExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Extensions { using System.Collections.ObjectModel; using System.Management.Automation; using System.Text; /// /// Extensions methods for class. /// internal static class PowerShellExtensions { /// /// Calls Invoke with to stop execution of commands. /// /// PowerShell. /// Collection of PSObjects representing output. public static Collection InvokeAndStopOnError(this PowerShell pwsh) { var settings = new PSInvocationSettings() { ErrorActionPreference = ActionPreference.Stop, }; return pwsh.Invoke(null, settings); } /// /// Calls Invoke with to stop execution of commands. /// /// Type of output object(s) expected from the command invocation. /// PowerShell. /// Collection of the type output representing output. public static Collection InvokeAndStopOnError(this PowerShell pwsh) { var settings = new PSInvocationSettings() { ErrorActionPreference = ActionPreference.Stop, }; return pwsh.Invoke(null, settings); } /// /// Gets the error stream message if any. /// /// PowerShell. /// Error message. Null if none. public static string? GetErrorMessage(this PowerShell pwsh) { if (pwsh.HadErrors) { var psStreamBuilder = new StringBuilder(); foreach (var line in pwsh.Streams.Error) { psStreamBuilder.AppendLine(line.ToString()); } return psStreamBuilder.ToString(); } return null; } /// /// Determines if the given shell contains a property error, meaning that the source of this error is the /// configuration values and not the configuration unit itself. /// /// The shell to inspect. /// True if it only contains property errors; false otherwise. public static bool ContainsPropertyError(this PowerShell pwsh) { if (!pwsh.HadErrors) { return false; } bool result = true; foreach (ErrorRecord? error in pwsh.Streams.Error) { if (error?.FullyQualifiedErrorId == "PropertyAssignmentException") { result = result && true; } } return result; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Helpers/ConfigurationUnitAndModule.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Helpers { using System; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.Constants; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.PowerShell.Commands; /// /// Contains information about the unit and the DSC resource that applies to it. /// internal class ConfigurationUnitAndModule : ConfigurationUnitInternal { /// /// Initializes a new instance of the class. /// /// Configuration unit. /// The configuration file path. public ConfigurationUnitAndModule(ConfigurationUnit unit, string? configurationFilePath) : base(unit, configurationFilePath) { string? moduleName = this.GetDirective(DirectiveConstants.Module); if (string.IsNullOrEmpty(moduleName)) { this.Module = null; } else { this.Module = PowerShellHelpers.CreateModuleSpecification( moduleName, this.GetDirective(DirectiveConstants.Version), this.GetDirective(DirectiveConstants.MinVersion), this.GetDirective(DirectiveConstants.MaxVersion), this.GetDirective(DirectiveConstants.ModuleGuid)); } } /// /// Gets the module specification. /// public ModuleSpecification? Module { get; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Helpers/ConfigurationUnitAndResource.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Helpers { using System; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.PowerShell.Commands; using Windows.Foundation.Collections; /// /// Contains information about the unit and the DSC resource that applies to it. /// internal class ConfigurationUnitAndResource { private readonly DscResourceInfoInternal dscResourceInfoInternal; /// /// Initializes a new instance of the class. /// /// Configuration unit internal. /// DscResourceInfoInternal. public ConfigurationUnitAndResource( ConfigurationUnitAndModule configurationUnitInternal, DscResourceInfoInternal dscResourceInfoInternal) { if (!configurationUnitInternal.ResourceName.Equals(dscResourceInfoInternal.Name, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(); } this.UnitInternal = configurationUnitInternal; this.dscResourceInfoInternal = dscResourceInfoInternal; } /// /// Gets or initializes the internal unit. /// public ConfigurationUnitAndModule UnitInternal { get; private init; } /// /// Gets the configuration unit. /// public ConfigurationUnit Unit { get { return this.UnitInternal.Unit; } } /// /// Gets the DSC resource name. /// /// DSC resource name. public string ResourceName { get { return this.dscResourceInfoInternal.Name; } } /// /// Gets the module specification. /// public ModuleSpecification? Module { get { return this.UnitInternal.Module; } } /// /// Gets a directive if exits. /// /// Name of directive. /// The value of the directive. Null if doesn't exist. /// Directive type value. public TType? GetDirective(string directiveName) where TType : class { return this.UnitInternal.GetDirective(directiveName); } /// /// Gets a directive bool value. /// /// Name of directive. /// The value of the directive. False if not set. public bool? GetDirective(string directiveName) { return this.UnitInternal.GetDirective(directiveName); } /// /// Gets the settings of the unit. /// /// ValueSet with settings. public ValueSet GetSettings() { return this.UnitInternal.GetExpandedSettings(); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Helpers/DscResourcesMap.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Helpers { using System; using System.Collections.Generic; using System.Linq; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; /// /// DscResources in the system. Get-DscResource looks the entire PSModulePath to find resources every time, so /// calling it multiples times is expensive. This class will store them in memory per configuration set. /// The resources are stored in a dictionary in the following hierarchy. /// - Module name /// - Resource name /// - Module Version /// - DscResourceInfo obj. /// There are some resources that don't have a module name. These are modules that live under /// C:\WINDOWS\system32\WindowsPowershell\v1.0\Modules\PsDesiredStateConfiguration\DscResources. /// This is not currently used. For now, we let PowerShell handle all the versioning. In order to avoid /// calls to Get-DscResource every time a unit is set, this class needs to be updated to handle min and max /// version. As it is, it just does RequiredVersion. /// internal sealed class DscResourcesMap { private readonly Dictionary resources = new Dictionary(); /// /// Initializes a new instance of the class. /// /// DSC Resources. public DscResourcesMap(IReadOnlyList resources) { foreach (var resource in resources) { if (string.IsNullOrEmpty(resource.Name)) { throw new ArgumentException(nameof(resource.Name)); } if (resource.Version is null) { throw new ArgumentException(nameof(resource.Version)); } this.Insert(resource); } } /// /// Looks for a DSC resource in the specified module. /// /// DSC resource name. /// Optional module name. /// Optional module version. /// DscResourceInfoInternal. Null if not found. public DscResourceInfoInternal? GetResource(string dscResourceName, string? moduleName, Version? version) { string normalizedResourceName = StringHelpers.Normalize(dscResourceName); string? normalizedModuleName = null; if (moduleName is not null) { normalizedModuleName = StringHelpers.Normalize(moduleName); } if (normalizedModuleName is null) { foreach (var module in this.resources.Values) { var resourceInfo = module.GetDscResourceInfo(normalizedResourceName, version); if (resourceInfo is not null) { return resourceInfo; } } } else { if (this.resources.TryGetValue(normalizedModuleName, out ModuleDscResources? module)) { return module.GetDscResourceInfo(normalizedResourceName, version); } } return null; } /// /// Does a DSC resource exists. /// /// DSC resource name. /// Optional module name. /// Optional module version. /// True if resource exists. public bool Exists(string dscResourceName, string? moduleName, Version? version) { return this.GetResource(dscResourceName, moduleName, version) != null; } private void Insert(DscResourceInfoInternal dscResourceInfo) { string? normalizedModuleName = dscResourceInfo.NormalizedModuleName; // There are some resources that doesn't have module, use empty as key. string moduleKey = string.Empty; if (normalizedModuleName is not null) { moduleKey = normalizedModuleName; } if (!this.resources.ContainsKey(moduleKey)) { this.resources.Add(moduleKey, new ModuleDscResources(moduleKey, dscResourceInfo)); } else { this.resources[moduleKey].AddResource(moduleKey, dscResourceInfo); } } /// /// Represents a map versions for a DscResource. /// private class DscResourceVersions : Dictionary { private readonly string resourceName; public DscResourceVersions(DscResourceInfoInternal dscResourceInfo) { this.resourceName = dscResourceInfo.NormalizedName; this.AddVersion(dscResourceInfo); } /// /// Adds a resource version. /// /// DscResourceInfo. public void AddVersion(DscResourceInfoInternal dscResourceInfo) { if (this.resourceName != dscResourceInfo.NormalizedName) { throw new ArgumentException(dscResourceInfo.NormalizedName); } // There are some system resources without version or module name. if (dscResourceInfo.Version is null) { this.Add(new Version(), dscResourceInfo); } else { // Get-DscResource will fail if the same module with same // resources and same version exists in the module path // so this shouldn't happen. Either way, we should throw there or here. this.Add(dscResourceInfo.Version, dscResourceInfo); } } public DscResourceInfoInternal? GetDscResourceInfo(Version? version) { if (version is null) { // Sort keys, get the latest. var newestVersion = this.Keys.OrderByDescending(v => v).First(); return this[newestVersion]; } if (this.TryGetValue(version, out DscResourceInfoInternal? resourceInfo)) { return resourceInfo; } return null; } } /// /// Represent a map of resources for a module. /// private class ModuleDscResources : Dictionary { private readonly string normalizedModuleName; public ModuleDscResources(string normalizedModuleName, DscResourceInfoInternal dscResourceInfo) { this.normalizedModuleName = normalizedModuleName; this.AddResource(this.normalizedModuleName, dscResourceInfo); } public void AddResource(string normalizedModuleName, DscResourceInfoInternal dscResourceInfo) { if (this.normalizedModuleName != normalizedModuleName) { throw new ArgumentException(nameof(normalizedModuleName)); } var normalizedResourceName = dscResourceInfo.NormalizedName; if (!this.ContainsKey(normalizedResourceName)) { this.Add(normalizedResourceName, new DscResourceVersions(dscResourceInfo)); } else { this[normalizedResourceName].AddVersion(dscResourceInfo); } } public DscResourceInfoInternal? GetDscResourceInfo(string dscResourceName, Version? version) { if (this.TryGetValue(dscResourceName, out DscResourceVersions? versions)) { return versions.GetDscResourceInfo(version); } return null; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Helpers/Factory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Helpers { using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.Unit; using Windows.Security.Cryptography.Certificates; /// /// Enables creation of PowerShell specific instances of configuration interfaces. /// internal static class Factory { private static readonly IEnumerable PublicRepositories = new string[] { "https://www.powershellgallery.com/api/v2", }; /// /// Initializes a new instance of the class. /// /// Unit name. /// DSC Resource Info. /// PSModuleInfo. /// GetModuleInfo. /// List of certificates. /// An initialized instance of . public static ConfigurationUnitProcessorDetails CreateUnitProcessorDetails( string unitName, DscResourceInfoInternal? dscResourceInfo, PSModuleInfo? psModuleInfo, PSObject? getModuleInfo, List? certs) { if (dscResourceInfo is null && psModuleInfo is null && getModuleInfo is null) { throw new ArgumentException(); } ConfigurationUnitProcessorDetails result = new ConfigurationUnitProcessorDetails { UnitType = unitName, }; if (dscResourceInfo is not null) { result.IsLocal = true; var settings = new List(); foreach (var properties in dscResourceInfo.Properties) { settings.Add(CreateUnitSettingDetails(properties)); } result.Settings = settings; if (psModuleInfo is null) { result.ModuleName = dscResourceInfo.ModuleName; result.Version = dscResourceInfo.Version is not null ? dscResourceInfo.Version.ToString() : null; } } if (psModuleInfo is not null) { result.ModuleDocumentationUri = psModuleInfo.HelpInfoUri is not null ? new Uri(psModuleInfo.HelpInfoUri) : null; result.UnitIconUri = psModuleInfo.IconUri; result.ModuleName = psModuleInfo.Name; result.ModuleType = psModuleInfo.ModuleType.ToString(); result.ModuleDescription = psModuleInfo.Description; result.PublishedModuleUri = psModuleInfo.ProjectUri; result.Version = psModuleInfo.Version.ToString(); result.Author = psModuleInfo.Author; result.Publisher = psModuleInfo.CompanyName; } if (getModuleInfo is not null) { result.ModuleSource = TryGetPropertyAsString(getModuleInfo, "Repository"); result.PublishedDate = TryGetPropertyFromDateTimeToDateTimeOffset(getModuleInfo, "PublishedDate"); var repoSourceLocation = getModuleInfo.Properties["RepositorySourceLocation"]; if (repoSourceLocation is not null) { string? repoSourceLocationValue = repoSourceLocation.Value as string; if (repoSourceLocationValue is not null) { result.IsPublic = PublicRepositories.Any(r => r == repoSourceLocationValue); } } if (psModuleInfo is null) { // Type is not the same as this PSModuleType. result.UnitIconUri = TryGetPropertyAsUri(getModuleInfo, "IconUri"); result.ModuleName = TryGetPropertyAsString(getModuleInfo, "Name"); result.ModuleDescription = TryGetPropertyAsString(getModuleInfo, "Description"); result.Version = TryGetPropertyAsString(getModuleInfo, "Version"); result.Author = TryGetPropertyAsString(getModuleInfo, "Author"); result.Publisher = TryGetPropertyAsString(getModuleInfo, "CompanyName"); } } result.SigningInformation = certs; return result; } /// /// Initializes a new instance of the class. /// /// DSC Resource info. /// An initialized instance of . public static ConfigurationUnitSettingDetails CreateUnitSettingDetails(DscResourcePropertyInfoInternal dscResourceInfo) { return new ConfigurationUnitSettingDetails { Identifier = dscResourceInfo.Name, IsRequired = dscResourceInfo.IsMandatory, Type = GetPropertyType(dscResourceInfo.PropertyType), }; } private static string? TryGetPropertyAsString(PSObject getModuleInfo, string getModuleInfoProperty) { var moduleProperty = getModuleInfo.Properties[getModuleInfoProperty]; if (moduleProperty is not null) { return moduleProperty.Value as string; } return null; } private static Uri? TryGetPropertyAsUri(PSObject getModuleInfo, string getModuleInfoProperty) { var moduleProperty = getModuleInfo.Properties[getModuleInfoProperty]; if (moduleProperty is not null) { string? modulePropertyString = moduleProperty.Value as string; if (modulePropertyString is not null) { return new Uri(modulePropertyString); } } return null; } private static DateTimeOffset TryGetPropertyFromDateTimeToDateTimeOffset(PSObject getModuleInfo, string getModuleInfoProperty) { var moduleProperty = getModuleInfo.Properties[getModuleInfoProperty]; if (moduleProperty is not null) { DateTime propertyAsDateTime; try { propertyAsDateTime = (DateTime)moduleProperty.Value; return new DateTimeOffset(propertyAsDateTime); } catch { } } return default; } private static Windows.Foundation.PropertyType GetPropertyType(string propertyType) { switch (propertyType.ToLowerInvariant()) { case "[byte]": return Windows.Foundation.PropertyType.UInt8; case "[int16]": return Windows.Foundation.PropertyType.Int16; case "[uint16]": return Windows.Foundation.PropertyType.UInt16; case "[int32]": return Windows.Foundation.PropertyType.Int32; case "[uint32]": return Windows.Foundation.PropertyType.UInt32; case "[int64]": return Windows.Foundation.PropertyType.Int64; case "[uint64]": return Windows.Foundation.PropertyType.UInt64; case "[single]": return Windows.Foundation.PropertyType.Single; case "[double]": return Windows.Foundation.PropertyType.Double; case "[char]": return Windows.Foundation.PropertyType.Char16; case "[bool]": return Windows.Foundation.PropertyType.Boolean; case "[string]": return Windows.Foundation.PropertyType.String; case "[datetime]": return Windows.Foundation.PropertyType.DateTime; case "[datetimeoffset]": return Windows.Foundation.PropertyType.DateTime; case "[timespan]": return Windows.Foundation.PropertyType.TimeSpan; case "[guid]": return Windows.Foundation.PropertyType.Guid; case "[byte[]]": return Windows.Foundation.PropertyType.UInt8Array; case "[int16[]]": return Windows.Foundation.PropertyType.Int16Array; case "[uint16[]]": return Windows.Foundation.PropertyType.UInt16Array; case "[int32[]]": return Windows.Foundation.PropertyType.Int32Array; case "[uint32[]]": return Windows.Foundation.PropertyType.UInt32Array; case "[int64[]]": return Windows.Foundation.PropertyType.Int64Array; case "[uint64[]]": return Windows.Foundation.PropertyType.UInt64Array; case "[single[]]": return Windows.Foundation.PropertyType.SingleArray; case "[double[]]": return Windows.Foundation.PropertyType.DoubleArray; case "[char[]]": return Windows.Foundation.PropertyType.Char16Array; case "[bool[]]": return Windows.Foundation.PropertyType.BooleanArray; case "[string[]]": return Windows.Foundation.PropertyType.StringArray; case "[object[]]": return Windows.Foundation.PropertyType.InspectableArray; case "[datetime[]]": return Windows.Foundation.PropertyType.DateTimeArray; case "[datetimeoffset[]]": return Windows.Foundation.PropertyType.DateTimeArray; case "[timespan[]]": return Windows.Foundation.PropertyType.TimeSpanArray; case "[guid[]]": return Windows.Foundation.PropertyType.GuidArray; // Everything else will just be an object... default: return Windows.Foundation.PropertyType.Inspectable; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Helpers/IPowerShellGet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Helpers { using System.Management.Automation; using Microsoft.PowerShell.Commands; using SemanticVersion = Microsoft.Management.Configuration.Processor.Helpers.SemanticVersion; /// /// Interface for PowerShellGet cmdlets. /// internal interface IPowerShellGet { /// /// Calls Find-Module. /// /// PowerShell instance. /// Module name. /// Optional version. /// Optional min version. /// Optional max version. /// Optional repository. /// Optional allow prerelease module. /// Module info, null if not found. PSObject? FindModule( PowerShell pwsh, string moduleName, SemanticVersion? semanticVersion, SemanticVersion? semanticMinVersion, SemanticVersion? semanticMaxVersion, string? repository, bool? allowPrerelease); /// /// Calls Find-DscResource. /// /// PowerShell instance. /// resource name. /// Optional module name. /// Optional version. /// Optional min version. /// Optional max version. /// Optional repository. /// Optional allow prerelease module. /// Dsc Resource info, null if not found. PSObject? FindDscResource( PowerShell pwsh, string resourceName, string? moduleName, SemanticVersion? semanticVersion, SemanticVersion? semanticMinVersion, SemanticVersion? semanticMaxVersion, string? repository, bool? allowPrerelease); /// /// Calls Save-Module with module specification. /// /// PowerShell instance. /// Module specification. /// Location to save module. void SaveModule(PowerShell pwsh, ModuleSpecification moduleSpecification, string location); /// /// Calls Save-Module -InputObject object -Path location. /// Input object must be the result of Find cmdlets of PowerShellGet. /// /// PowerShell instance. /// Input object. /// Location to save module. void SaveModule(PowerShell pwsh, PSObject inputObject, string location); /// /// Calls Install-Module -InputObject object. /// Input object must be the result of Find cmdlets of PowerShellGet. /// /// PowerShell instance. /// Input object. /// If to install to all users. void InstallModule(PowerShell pwsh, PSObject inputObject, bool allUsers); /// /// Calls Install-Module with a module specification. /// /// PowerShell instance. /// Module specification. /// If to install to all users. void InstallModule(PowerShell pwsh, ModuleSpecification moduleSpecification, bool allUsers); } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Helpers/PowerShellGetV2.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Helpers { using System.Collections.Generic; using System.Linq; using System.Management.Automation; using Microsoft.PowerShell.Commands; using static Microsoft.Management.Configuration.Processor.PowerShell.Constants.PowerShellConstants; using SemanticVersion = Microsoft.Management.Configuration.Processor.Helpers.SemanticVersion; /// /// PowerShellGet implementation for 2.2.5 . /// internal class PowerShellGetV2 : IPowerShellGet { private const string AllUsers = "AllUsers"; /// public PSObject? FindModule( PowerShell pwsh, string moduleName, SemanticVersion? semanticVersion, SemanticVersion? semanticMinVersion, SemanticVersion? semanticMaxVersion, string? repository, bool? allowPrerelease) { bool implicitAllowPrerelease = false; var parameters = new Dictionary() { { Parameters.Name, moduleName }, }; if (semanticVersion != null) { implicitAllowPrerelease |= semanticVersion.IsPrerelease; parameters.Add(Parameters.RequiredVersion, semanticVersion.ToString()); } if (semanticMinVersion != null) { implicitAllowPrerelease |= semanticMinVersion.IsPrerelease; parameters.Add(Parameters.MinimumVersion, semanticMinVersion.ToString()); } if (semanticMaxVersion != null) { implicitAllowPrerelease |= semanticMaxVersion.IsPrerelease; parameters.Add(Parameters.MaximumVersion, semanticMaxVersion.ToString()); } if (!string.IsNullOrEmpty(repository)) { parameters.Add(Parameters.Repository, repository); } if (allowPrerelease.HasValue || implicitAllowPrerelease) { // If explicit allowPrerelease = false don't use implicit. bool allow = allowPrerelease ?? implicitAllowPrerelease; parameters.Add(Parameters.AllowPrerelease, allow); } pwsh.AddCommand(Commands.FindModule) .AddParameters(parameters); return pwsh.Invoke().FirstOrDefault(); } /// public PSObject? FindDscResource( PowerShell pwsh, string resourceName, string? moduleName, SemanticVersion? semanticVersion, SemanticVersion? semanticMinVersion, SemanticVersion? semanticMaxVersion, string? repository, bool? allowPrerelease) { var parameters = new Dictionary() { { Parameters.Name, resourceName }, }; bool implicitAllowPrerelease = false; if (!string.IsNullOrEmpty(moduleName)) { parameters.Add(Parameters.ModuleName, moduleName); } if (semanticVersion != null) { implicitAllowPrerelease |= semanticVersion.IsPrerelease; parameters.Add(Parameters.RequiredVersion, semanticVersion.ToString()); } if (semanticMinVersion != null) { implicitAllowPrerelease |= semanticMinVersion.IsPrerelease; parameters.Add(Parameters.MinimumVersion, semanticMinVersion.ToString()); } if (semanticMaxVersion != null) { implicitAllowPrerelease |= semanticMaxVersion.IsPrerelease; parameters.Add(Parameters.MaximumVersion, semanticMaxVersion.ToString()); } if (!string.IsNullOrEmpty(repository)) { parameters.Add(Parameters.Repository, repository); } if (allowPrerelease.HasValue || implicitAllowPrerelease) { // If explicit allowPrerelease = false don't use implicit. bool allow = allowPrerelease ?? implicitAllowPrerelease; parameters.Add(Parameters.AllowPrerelease, allow); } pwsh.AddCommand(Commands.FindDscResource) .AddParameters(parameters); // The result is just a PSCustomObject with a type name of Microsoft.PowerShell.Commands.PSGetDscResourceInfo. // When no module is passed and a resource is not found, this will return an empty list. If a module // is specified and no resource is found then it will fail earlier because of a Write-Error. return pwsh.Invoke().FirstOrDefault(); } /// public void SaveModule( PowerShell pwsh, ModuleSpecification moduleSpecification, string location) { var parameters = new Dictionary() { { Parameters.Name, moduleSpecification.Name }, { Parameters.Path, location }, }; if (moduleSpecification.Version is not null) { parameters.Add(Parameters.MinimumVersion, moduleSpecification.Version); } if (moduleSpecification.MaximumVersion is not null) { parameters.Add(Parameters.MaximumVersion, moduleSpecification.MaximumVersion); } if (moduleSpecification.RequiredVersion is not null) { parameters.Add(Parameters.RequiredVersion, moduleSpecification.RequiredVersion); } _ = pwsh.AddCommand(Commands.SaveModule) .AddParameters(parameters) .AddParameter(Parameters.Force) .Invoke(); } /// public void SaveModule( PowerShell pwsh, PSObject inputObject, string location) { _ = pwsh.AddCommand(Commands.SaveModule) .AddParameter(Parameters.Path, location) .AddParameter(Parameters.InputObject, inputObject) .AddParameter(Parameters.Force) .Invoke(); } /// public void InstallModule( PowerShell pwsh, PSObject inputObject, bool allUsers) { var parameters = new Dictionary() { { Parameters.InputObject, inputObject }, }; if (allUsers) { parameters.Add(Parameters.Scope, AllUsers); } // If the repository is untrusted, it will fail with: // Microsoft.PowerShell.Commands.WriteErrorException : Exception calling "ShouldContinue" with "5" // argument(s): "A command that prompts the user failed because the host program or the command type // does not support user interaction. // If its trusted, PowerShellGets adds the Force parameter to the call to PackageManager\Install-Package. // TODO: Once we have policies, we should remove Force. For hosted environments and depending // on the policy we will trust PSGallery when we create the Runspace or add Force here. _ = pwsh.AddCommand(Commands.InstallModule) .AddParameters(parameters) .AddParameter(Parameters.Force) .Invoke(); } /// public void InstallModule( PowerShell pwsh, ModuleSpecification moduleSpecification, bool allUsers) { var parameters = new Dictionary() { { Parameters.Name, moduleSpecification.Name }, }; if (moduleSpecification.Version is not null) { parameters.Add(Parameters.MinimumVersion, moduleSpecification.Version); } if (moduleSpecification.MaximumVersion is not null) { parameters.Add(Parameters.MaximumVersion, moduleSpecification.MaximumVersion); } if (moduleSpecification.RequiredVersion is not null) { parameters.Add(Parameters.RequiredVersion, moduleSpecification.RequiredVersion); } if (allUsers) { parameters.Add(Parameters.Scope, AllUsers); } _ = pwsh.AddCommand(Commands.InstallModule) .AddParameters(parameters) .AddParameter(Parameters.Force) .Invoke(); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Helpers/PowerShellHelpers.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Helpers { using System.Collections; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.PowerShell.Commands; using static Microsoft.Management.Configuration.Processor.PowerShell.Constants.PowerShellConstants; /// /// General PowerShell helpers. /// internal static class PowerShellHelpers { /// /// Creates a module specification object. /// /// Module name. /// Optional version. /// Optional min version. /// Optional max version. /// Optional guid. /// ModuleSpecification. public static ModuleSpecification CreateModuleSpecification( string moduleName, string? version = null, string? minVersion = null, string? maxVersion = null, string? guid = null) { if (version is null && minVersion is null && maxVersion is null) { // Otherwise will fail with MissingMemberException... return new ModuleSpecification(moduleName); } var moduleInfo = new Hashtable { { Parameters.ModuleName, moduleName }, }; if (!string.IsNullOrEmpty(version)) { var semanticVersion = new SemanticVersion(version); moduleInfo.Add(Parameters.RequiredVersion, semanticVersion.Version); } if (!string.IsNullOrEmpty(minVersion)) { var semanticVersion = new SemanticVersion(minVersion); moduleInfo.Add(Parameters.ModuleVersion, semanticVersion.Version); } if (!string.IsNullOrEmpty(maxVersion)) { var semanticVersion = new SemanticVersion(maxVersion); // For some reason, the constructor of ModuleSpecification that takes // a hashtable calls ModuleCmdletBase.GetMaximumVersion. This method will // validate the max version and replace * for 999999999 only if its the last // char in the string. But then the returned value is not assigned to the // ModuleSpecification's MaximumVersion property. If we want to set a // MaximumVersion with a wildcard and pass this to Install-Module it will // fail with "Cannot convert value 'x.*' to type 'System.Version'." moduleInfo.Add(Parameters.MaximumVersion, semanticVersion.Version.ToString()); } if (!string.IsNullOrEmpty(guid)) { moduleInfo.Add(Parameters.Guid, guid); } // Using the Hashtable constructor will verify that RequiredVersion is used properly. return new ModuleSpecification(moduleInfo); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/ProcessorEnvironments/HostedEnvironment.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Runspaces { using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Runtime.InteropServices.WindowsRuntime; using Microsoft.Management.Configuration.Processor.Constants; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.DscModules; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments; using Microsoft.PowerShell.Commands; using Windows.Foundation.Collections; using Windows.Security.Cryptography.Certificates; using Windows.Storage.Streams; using static Microsoft.Management.Configuration.Processor.PowerShell.Constants.PowerShellConstants; /// /// Process environment. Provides interaction with PowerShell for a hosted environment. /// internal class HostedEnvironment : IProcessorEnvironment { private readonly PowerShellConfigurationProcessorType type; private readonly IPowerShellGet powerShellGet; private PowerShellConfigurationProcessorLocation location = PowerShellConfigurationProcessorLocation.CurrentUser; private string? customLocation; /// /// Initializes a new instance of the class. /// /// PowerShell Runspace. /// Configuration processor type. /// IDscModule. public HostedEnvironment( Runspace runspace, PowerShellConfigurationProcessorType type, IDscModule dscModule) { this.Runspace = runspace; this.type = type; this.DscModule = dscModule; // TODO: once v3 is release implement v3 version. this.powerShellGet = new PowerShellGetV2(); } /// public Runspace Runspace { get; } /// /// Gets the DscModule. /// internal IDscModule DscModule { get; } /// /// Gets or initializes the set processor factory. /// internal PowerShellConfigurationSetProcessorFactory? SetProcessorFactory { get; init; } /// public void ValidateRunspace() { // Only support PowerShell Core. if (this.GetVariable(Variables.PSEdition) != Core) { throw new NotSupportedException("Only PowerShell Core is supported."); } // If opening a runspace has failures, like one of the modules in ImportPSModule is not found, it won't throw but // write to the error output. This is not a fatal error, since we install PSDesiredStateConfiguration // module if not found, so unless there's a real reason keep it in verbose. var errors = this.GetVariable(Variables.Error); if (errors.Count > 0) { this.OnDiagnostics( DiagnosticLevel.Verbose, $"Error creating runspace '{string.Join("\n", errors.Cast().ToArray())}'"); } var powerShellGet = PowerShellHelpers.CreateModuleSpecification( Modules.PowerShellGet, minVersion: Modules.PowerShellGetMinVersion); if (!this.ValidateModule(powerShellGet)) { var previousVersion = this.GetAvailableModule( PowerShellHelpers.CreateModuleSpecification( Modules.PowerShellGet)); string message = $"Required '{powerShellGet}'"; if (previousVersion is not null) { message += $" Found '{previousVersion.Name} {previousVersion.Version}'"; } throw new NotSupportedException(message); } // Make sure PSDesiredConfiguration is present. this.InstallModule(this.DscModule.ModuleSpecification); } /// public IReadOnlyList GetAllDscResources() { using PowerShell pwsh = PowerShell.Create(this.Runspace); var results = this.DscModule.GetAllDscResources(pwsh); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return results; } /// public IReadOnlyList GetDscResourcesInModule(ModuleSpecification moduleSpecification) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var results = this.DscModule.GetDscResourcesInModule(pwsh, moduleSpecification); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return results; } /// public DscResourceInfoInternal? GetDscResource(ConfigurationUnitAndModule unitInternal) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var result = this.DscModule.GetDscResource(pwsh, unitInternal.ResourceName, unitInternal.Module); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return result; } /// public ValueSet InvokeGetResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var result = this.DscModule.InvokeGetResource(pwsh, settings, name, moduleSpecification); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return result; } /// public bool InvokeTestResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var result = this.DscModule.InvokeTestResource(pwsh, settings, name, moduleSpecification); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return result; } /// public bool InvokeSetResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var result = this.DscModule.InvokeSetResource(pwsh, settings, name, moduleSpecification); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return result; } /// public PSModuleInfo? GetImportedModule(ModuleSpecification moduleSpecification) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var moduleInfo = pwsh.AddCommand(Commands.GetModule) .AddParameter(Parameters.FullyQualifiedName, moduleSpecification) .Invoke() .FirstOrDefault(); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return moduleInfo; } /// public PSModuleInfo? GetAvailableModule(ModuleSpecification moduleSpecification) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var moduleInfo = pwsh.AddCommand(Commands.GetModule) .AddParameter(Parameters.FullyQualifiedName, moduleSpecification) .AddParameter(Parameters.ListAvailable) .Invoke() .FirstOrDefault(); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return moduleInfo; } /// public PSModuleInfo? GetAvailableModule(string path) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var moduleInfo = pwsh.AddCommand(Commands.GetModule) .AddParameter(Parameters.Name, path) .AddParameter(Parameters.ListAvailable) .Invoke() .FirstOrDefault(); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return moduleInfo; } /// public void ImportModule(ModuleSpecification moduleSpecification) { using PowerShell pwsh = PowerShell.Create(this.Runspace); _ = pwsh.AddCommand(Commands.ImportModule) .AddParameter(Parameters.FullyQualifiedName, moduleSpecification) .Invoke(); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } /// public void ImportModule(string path) { if (!File.Exists(path)) { throw new FileNotFoundException(path); } using PowerShell pwsh = PowerShell.Create(this.Runspace); _ = pwsh.AddCommand(Commands.ImportModule) .AddParameter(Parameters.Name, path) .Invoke(); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } /// public PSObject? GetInstalledModule(ModuleSpecification moduleSpecification) { // Instead of Get-InstalledModule, we look for PSGetModuleInfo.xml and serialize it // if found. This allow us to get the information from Install-Module and Save-Module. var module = this.GetAvailableModule(moduleSpecification); if (module is null) { return null; } var getModuleInfoFile = Path.Combine(module.ModuleBase, "PSGetModuleInfo.xml"); if (!File.Exists(getModuleInfoFile)) { // Keep Get-InstalledModule behaviour. return null; } using PowerShell pwsh = PowerShell.Create(this.Runspace); var installedModule = pwsh.AddCommand(Commands.ImportCliXml) .AddParameter(Parameters.Path, getModuleInfoFile) .Invoke() .FirstOrDefault(); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return installedModule; } /// public PSObject? FindModule(ConfigurationUnitInternal unitInternal) { // Don't use ModuleSpecification here. Each parameter is independent and // we need version even if a module was not specified. string? moduleName = unitInternal.GetDirective(DirectiveConstants.Module); if (string.IsNullOrEmpty(moduleName)) { return null; } using PowerShell pwsh = PowerShell.Create(this.Runspace); var result = this.powerShellGet.FindModule( pwsh, moduleName, unitInternal.GetSemanticVersion(), unitInternal.GetSemanticMinVersion(), unitInternal.GetSemanticMaxVersion(), unitInternal.GetDirective(DirectiveConstants.Repository), unitInternal.GetDirective(DirectiveConstants.AllowPrerelease)); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return result; } /// public PSObject? FindDscResource(ConfigurationUnitInternal unitInternal) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var result = this.powerShellGet.FindDscResource( pwsh, unitInternal.ResourceName, unitInternal.GetDirective(DirectiveConstants.Module), unitInternal.GetSemanticVersion(), unitInternal.GetSemanticMinVersion(), unitInternal.GetSemanticMaxVersion(), unitInternal.GetDirective(DirectiveConstants.Repository), unitInternal.GetDirective(DirectiveConstants.AllowPrerelease)); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return result; } /// public void SaveModule(PSObject inputObject, string location) { using PowerShell pwsh = PowerShell.Create(this.Runspace); this.powerShellGet.SaveModule(pwsh, inputObject, location); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } /// public void SaveModule(ModuleSpecification moduleSpecification, string location) { using PowerShell pwsh = PowerShell.Create(this.Runspace); this.powerShellGet.SaveModule(pwsh, moduleSpecification, location); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } /// public void InstallModule(PSObject inputObject) { if (this.location == PowerShellConfigurationProcessorLocation.Custom) { if (string.IsNullOrEmpty(this.customLocation)) { throw new ArgumentNullException(nameof(this.customLocation)); } this.SaveModule(inputObject, this.customLocation); } else { using PowerShell pwsh = PowerShell.Create(this.Runspace); this.powerShellGet.InstallModule(pwsh, inputObject, this.location == PowerShellConfigurationProcessorLocation.AllUsers); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } } /// public void InstallModule(ModuleSpecification moduleSpecification) { // Maybe is already there. if (!this.ValidateModule(moduleSpecification)) { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Installing module: {moduleSpecification.Name} ..."); // Ok, we have to get it. if (this.location == PowerShellConfigurationProcessorLocation.Custom) { if (string.IsNullOrEmpty(this.customLocation)) { throw new ArgumentNullException(nameof(this.customLocation)); } this.OnDiagnostics(DiagnosticLevel.Verbose, $"... calling save module ..."); this.SaveModule(moduleSpecification, this.customLocation); } else { this.OnDiagnostics(DiagnosticLevel.Verbose, $"... calling install module ..."); using PowerShell pwsh = PowerShell.Create(this.Runspace); this.powerShellGet.InstallModule(pwsh, moduleSpecification, this.location == PowerShellConfigurationProcessorLocation.AllUsers); this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } this.OnDiagnostics(DiagnosticLevel.Verbose, $" ... module installed."); } } /// public List GetCertsOfValidSignedFiles(string[] paths) { using PowerShell pwsh = PowerShell.Create(this.Runspace); var signatures = pwsh.AddCommand(Commands.GetChildItem) .AddParameter(Parameters.Path, paths) .AddCommand(Commands.GetAuthenticodeSignature) .Invoke(); var thumbprint = new HashSet(); var certificates = new List(); foreach (var signature in signatures) { if (signature.Status == SignatureStatus.Valid) { if (thumbprint.Add(signature.SignerCertificate.Thumbprint)) { IBuffer buffer = signature.SignerCertificate.GetRawCertData().AsBuffer(); certificates.Add(new Certificate(buffer)); } } } this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return certificates; } /// public TType GetVariable(string name) { return (TType)this.Runspace.SessionStateProxy.PSVariable.GetValue(name); } /// public void SetVariable(string name, object value) { this.Runspace.SessionStateProxy.PSVariable.Set(name, value); } /// public void SetPSModulePath(string path) { this.SetVariable(Variables.PSModulePath, path); } /// public void SetPSModulePaths(IReadOnlyList paths) { this.SetVariable(Variables.PSModulePath, string.Join(";", paths)); } /// public void PrependPSModulePath(string path) { var oldModulePath = this.GetModulePaths(); if (!oldModulePath.Contains(path)) { this.SetPSModulePath($"{path};{string.Join(";", oldModulePath)}"); } } /// public void PrependPSModulePaths(IReadOnlyList paths) { var newPaths = paths.ToList(); var oldModulePath = this.GetModulePaths(); foreach (var newPath in paths) { if (oldModulePath.Contains(newPath)) { newPaths.Remove(newPath); } } if (newPaths.Any()) { this.SetPSModulePath($"{string.Join(";", newPaths)};{string.Join(";", oldModulePath)}"); } } /// public void AppendPSModulePath(string path) { var oldModulePath = this.GetModulePaths(); if (!oldModulePath.Contains(path)) { this.SetPSModulePath($"{string.Join(";", oldModulePath)};{path}"); } } /// public void AppendPSModulePaths(IReadOnlyList paths) { var newPaths = paths.ToList(); var oldModulePath = this.GetModulePaths(); foreach (var newPath in paths) { if (oldModulePath.Contains(newPath)) { newPaths.Remove(newPath); } } if (newPaths.Any()) { this.SetPSModulePath($"{string.Join(";", oldModulePath)};{string.Join(";", newPaths)}"); } } /// public void CleanupPSModulePath(string path) { string newModulePath = this.GetVariable(Variables.PSModulePath) .Replace($"{path};", null) .Replace($";{path}", null); this.SetPSModulePath(newModulePath); } /// public void SetLocation(PowerShellConfigurationProcessorLocation location, string? customLocation) { this.location = location; if (this.location == PowerShellConfigurationProcessorLocation.Custom) { if (string.IsNullOrEmpty(customLocation)) { throw new ArgumentNullException(nameof(customLocation)); } this.customLocation = customLocation; } } private bool ValidateModule(ModuleSpecification moduleSpecification) { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Validating module: {moduleSpecification.Name} ..."); var loadedModule = this.GetImportedModule(moduleSpecification); if (loadedModule is not null) { this.OnDiagnostics(DiagnosticLevel.Verbose, $" ... module is already imported."); return true; } var availableModule = this.GetAvailableModule(moduleSpecification); if (availableModule is not null) { this.OnDiagnostics(DiagnosticLevel.Verbose, $" ... module is available, importing ..."); this.ImportModule(moduleSpecification); this.OnDiagnostics(DiagnosticLevel.Verbose, $" ... module imported."); return true; } this.OnDiagnostics(DiagnosticLevel.Verbose, $" ... module not found."); return false; } private void OnDiagnostics(DiagnosticLevel level, PowerShell pwsh) { this.SetProcessorFactory?.OnDiagnostics(level, pwsh); } private void OnDiagnostics(DiagnosticLevel level, string message) { this.SetProcessorFactory?.OnDiagnostics(level, message); } private HashSet GetModulePaths() { return this.GetVariable(Variables.PSModulePath).Split(";").ToHashSet(); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/ProcessorEnvironments/IProcessorEnvironment.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments { using System.Collections.Generic; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.PowerShell.Commands; using Windows.Foundation.Collections; using Windows.Security.Cryptography.Certificates; /// /// IProcessorEnvironment. Provides interaction with PowerShell. /// internal interface IProcessorEnvironment { /// /// Gets the runspace. /// System.Management.Automation.Runspaces.Runspace Runspace { get; } /// /// Validates the runspace. /// void ValidateRunspace(); /// /// Gets all DSC resource. /// /// A list with the DSC resource. IReadOnlyList GetAllDscResources(); /// /// Gets all resources in a module. /// /// Module specification. /// List of resources. IReadOnlyList GetDscResourcesInModule(ModuleSpecification moduleSpecification); /// /// Gets a DSC Resource. /// /// Configuration unit internal. /// DSC Resource. DscResourceInfoInternal? GetDscResource(ConfigurationUnitAndModule unitInternal); /// /// Calls Invoke-DscResource -Method Get from this module. /// /// Settings. /// Name. /// Module specification. /// Properties of resource. ValueSet InvokeGetResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification); /// /// Calls Invoke-DscResource -Method Test from this module. /// /// Settings. /// Name. /// Module specification. /// Is in desired state. bool InvokeTestResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification); /// /// Calls Invoke-DscResource -Method Set from this module. /// /// Settings. /// Name. /// Module specification. /// If a reboot is required. bool InvokeSetResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification); /// /// Calls Get-Module with fully qualified name. /// /// Module name. /// PSModuleInfo, null if not imported. PSModuleInfo? GetImportedModule(ModuleSpecification moduleSpecification); /// /// Calls Get-Module with the fully qualified name and using ListAvailable. /// /// Module specification. /// PSModuleInfo, null if not found. PSModuleInfo? GetAvailableModule(ModuleSpecification moduleSpecification); /// /// Calls Get-Module from a path using ListAvailable. /// /// Path. /// The first module returned, null if none. PSModuleInfo? GetAvailableModule(string path); /// /// Calls Import-Module with the fully qualified name. /// /// Module specification. void ImportModule(ModuleSpecification moduleSpecification); /// /// Calls Import-Module with a file path. /// /// Module file path. void ImportModule(string path); /// /// Calls Get-InstalledModule. /// /// Module specification. /// Module info, null if not installed. PSObject? GetInstalledModule(ModuleSpecification moduleSpecification); /// /// Calls Find-Module. /// /// Configuration unit internal. /// Module info, null if not found. PSObject? FindModule(ConfigurationUnitInternal unitInternal); /// /// Calls Find-DscResource. /// /// Configuration unit internal. /// Dsc Resource info, null if not found. PSObject? FindDscResource(ConfigurationUnitInternal unitInternal); /// /// Calls Save-Module -InputObject object -Path location. /// Input object must be the result of Find cmdlets of PowerShellGet. /// /// Input object. /// Location to save module. void SaveModule(PSObject inputObject, string location); /// /// Calls Save-Module. /// /// Module specification. /// Location to save module. void SaveModule(ModuleSpecification moduleSpecification, string location); /// /// Calls Install-Module -InputObject object. /// Input object must be the result of Find cmdlets of PowerShellGet. /// /// Input object. void InstallModule(PSObject inputObject); /// /// Calls Install-Module with a module specification. /// /// Module specification. void InstallModule(ModuleSpecification moduleSpecification); /// /// Get unique certificates of valid signed files from the specified paths. /// /// Path. /// List with valid signatures. List GetCertsOfValidSignedFiles(string[] paths); /// /// Gets the value of a variable. /// /// Type of the variable. /// Name of variable. /// The value of a variable, null if doesn't exist. TType GetVariable(string name); /// /// Sets a variable with its value. /// /// Name of variable. /// Value of variable. void SetVariable(string name, object value); /// /// Overwrites PSModulePath with the specified path. /// /// Path. void SetPSModulePath(string path); /// /// Overwrites PSModulePath with the specified paths. /// /// Paths. void SetPSModulePaths(IReadOnlyList paths); /// /// Prepends path to the PSModulePath. /// /// Path. void PrependPSModulePath(string path); /// /// Prepends paths to the PSModulePath. /// /// Paths. void PrependPSModulePaths(IReadOnlyList paths); /// /// Append path to the PSModulePath. /// /// Path. void AppendPSModulePath(string path); /// /// Append paths to the PSModulePath. /// /// Path. void AppendPSModulePaths(IReadOnlyList paths); /// /// Removes a path from the module path. /// /// Path. void CleanupPSModulePath(string path); /// /// Sets the location for installing modules. /// /// Location. /// Path for custom location. void SetLocation(PowerShellConfigurationProcessorLocation location, string? customLocation); } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/ProcessorEnvironments/ProcessorEnvironmentFactory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments { using System; using System.Collections.Generic; using System.Management.Automation.Runspaces; using Microsoft.Management.Configuration.Processor.PowerShell.DscModules; using Microsoft.Management.Configuration.Processor.PowerShell.Runspaces; using Microsoft.PowerShell; using Microsoft.PowerShell.Commands; /// /// Factory class to create a processor environment. /// internal class ProcessorEnvironmentFactory { private readonly PowerShellConfigurationProcessorType type; /// /// Initializes a new instance of the class. /// /// Configuration processor type. public ProcessorEnvironmentFactory(PowerShellConfigurationProcessorType type) { this.type = type; } /// /// Create process environment. /// /// Optional processor factory. /// Configuration processor policy. /// IProcessorEnvironment. public IProcessorEnvironment CreateEnvironment( PowerShellConfigurationSetProcessorFactory? setProcessorFactory, PowerShellConfigurationProcessorPolicy policy) { IDscModule dscModule = new DscModuleV2(); ExecutionPolicy executionPolicy = this.GetExecutionPolicy(policy); // The for ConfigurationProcessorType.Default the idea was that since is already running in PowerShell we will // have access to the variables in the current runspace, but we can't use that runspace and AFAIK // there's not a simple way to simply clone a runspace. If we want to do it, we will need to get the // variables from the current runspace and add them here, but maybe some of them are objects that can't // handle being used in different runspace. It will also be time consuming and we can't block for creating // the create set processor. Even if we could clone it, at this point we are running in a different thread, // so there's no default runspace to clone here (aka. PowerShell.Create(RunspaceMode.CurrentRunspace) throws) // // If we want to somehow support, it might be easier to explicitly ask for the variables that need to be // ported. We can add a new property to IConfigurationProcessorFactoryProperties with the variable names // and set them here, but if they change they won't get reflected in our runspace (which might be a good thing). // The problem with that is that they will need to be defined when the configuration set is opened and it really // just makes sense before the ConfigurationSetProcessor gets created. We could add a new IConfigurationSetProcessorProperties // Then in PowerShell it can be something like // Get-WinGetConfiguration | Add-WinGetConfigurationVariable -Name foo | Start-WinGetConfiguration if (this.type == PowerShellConfigurationProcessorType.Hosted || this.type == PowerShellConfigurationProcessorType.Default) { var initialSessionState = this.CreateInitialSessionState( executionPolicy, new List { dscModule.ModuleSpecification, }); var runspace = RunspaceFactory.CreateRunspace(initialSessionState); runspace.Open(); return new HostedEnvironment(runspace, this.type, dscModule) { SetProcessorFactory = setProcessorFactory, }; } throw new ArgumentException(this.type.ToString()); } private InitialSessionState CreateInitialSessionState(ExecutionPolicy policy, IReadOnlyList modules) { InitialSessionState initialSessionState = InitialSessionState.CreateDefault(); // If this call fails importing the module, it won't throw but write to the error output. DSCModule is // in charge of verifying that it got loaded correctly and if not, to install it. initialSessionState.ImportPSModule(modules); initialSessionState.ExecutionPolicy = policy; return initialSessionState; } private ExecutionPolicy GetExecutionPolicy(PowerShellConfigurationProcessorPolicy policy) { return policy switch { PowerShellConfigurationProcessorPolicy.Unrestricted => ExecutionPolicy.Unrestricted, PowerShellConfigurationProcessorPolicy.RemoteSigned => ExecutionPolicy.RemoteSigned, PowerShellConfigurationProcessorPolicy.AllSigned => ExecutionPolicy.AllSigned, PowerShellConfigurationProcessorPolicy.Restricted => ExecutionPolicy.Restricted, PowerShellConfigurationProcessorPolicy.Bypass => ExecutionPolicy.Bypass, _ => throw new InvalidOperationException(), }; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Set/PowerShellConfigurationSetProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Set { using System; using System.Collections.Generic; using System.IO; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments; using Microsoft.Management.Configuration.Processor.PowerShell.Unit; using Microsoft.Management.Configuration.Processor.Set; using Microsoft.Management.Configuration.Processor.Unit; using Windows.Security.Cryptography.Certificates; /// /// IConfigurationSetProcessor implementation using PowerShell DSC v2. /// internal sealed partial class PowerShellConfigurationSetProcessor : ConfigurationSetProcessorBase, IConfigurationSetProcessor { /// /// Initializes a new instance of the class. /// /// The processor environment. /// Configuration set. /// Whether the set processor should work in limitation mode. public PowerShellConfigurationSetProcessor(IProcessorEnvironment processorEnvironment, ConfigurationSet? configurationSet, bool isLimitMode = false) : base(configurationSet, isLimitMode) { this.ProcessorEnvironment = processorEnvironment; } /// /// Gets the processor environment. /// internal IProcessorEnvironment ProcessorEnvironment { get; } /// protected override IConfigurationUnitProcessor CreateUnitProcessorInternal(ConfigurationUnit unit) { var configurationUnitInternal = new ConfigurationUnitAndModule(unit, this.ConfigurationSet?.Path) { UnitTypeIsResourceName = IsUnitTypeResourceName(this.ConfigurationSet?.SchemaVersion) }; this.OnDiagnostics(DiagnosticLevel.Verbose, $"Creating unit processor for: {configurationUnitInternal.QualifiedName}..."); var dscResourceInfo = this.PrepareUnitForProcessing(configurationUnitInternal); this.OnDiagnostics(DiagnosticLevel.Verbose, $"Using unit from location: {dscResourceInfo.Path}"); return new PowerShellConfigurationUnitProcessor( this.ProcessorEnvironment, new ConfigurationUnitAndResource(configurationUnitInternal, dscResourceInfo), this.IsLimitMode) { SetProcessorFactory = this.SetProcessorFactory }; } /// protected override IConfigurationUnitProcessorDetails? GetUnitProcessorDetailsInternal(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags) { var unitInternal = new ConfigurationUnitAndModule(unit, this.ConfigurationSet?.Path); this.OnDiagnostics(DiagnosticLevel.Verbose, $"Getting unit details [{detailFlags}] for: {unitInternal.QualifiedName}"); // (Local | Download | Load) will all work off of local files, so if any one is an option just use the local module info if found. DscResourceInfoInternal? dscResourceInfo = null; if (detailFlags.HasFlag(ConfigurationUnitDetailFlags.Local) || detailFlags.HasFlag(ConfigurationUnitDetailFlags.Download) || detailFlags.HasFlag(ConfigurationUnitDetailFlags.Load)) { dscResourceInfo = this.ProcessorEnvironment.GetDscResource(unitInternal); } if (dscResourceInfo is not null) { return this.GetUnitProcessorDetailsLocal( dscResourceInfo.Name, dscResourceInfo, detailFlags.HasFlag(ConfigurationUnitDetailFlags.Load)); } if (!(detailFlags.HasFlag(ConfigurationUnitDetailFlags.Catalog) || detailFlags.HasFlag(ConfigurationUnitDetailFlags.Download) || detailFlags.HasFlag(ConfigurationUnitDetailFlags.Load))) { // Not found locally. return null; } var unitModuleInfo = this.FindUnitModule(unitInternal); if (unitModuleInfo is null) { // Not found in catalog. return null; } PSObject foundModule = unitModuleInfo.Value.Module; string resourceName = unitModuleInfo.Value.ResourceName; dynamic foundModuleInfo = foundModule; if (detailFlags.HasFlag(ConfigurationUnitDetailFlags.Catalog)) { return Factory.CreateUnitProcessorDetails( resourceName, null, null, foundModule, null); } if (detailFlags.HasFlag(ConfigurationUnitDetailFlags.Download)) { var tempSavePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(tempSavePath); this.ProcessorEnvironment.SaveModule(foundModule, tempSavePath); var moduleInfo = this.ProcessorEnvironment.GetAvailableModule( Path.Combine(tempSavePath, foundModuleInfo.Name)); return Factory.CreateUnitProcessorDetails( resourceName, null, moduleInfo, foundModule, this.GetCertificates(moduleInfo)); } if (detailFlags.HasFlag(ConfigurationUnitDetailFlags.Load)) { this.ProcessorEnvironment.InstallModule(foundModule); dscResourceInfo = this.ProcessorEnvironment.GetDscResource(unitInternal); if (dscResourceInfo is null) { // Well, this is awkward. throw new InstallDscResourceException( unitInternal.ResourceName, PowerShellHelpers.CreateModuleSpecification(foundModuleInfo.Name, foundModuleInfo.Version)); } return this.GetUnitProcessorDetailsLocal(dscResourceInfo.Name, dscResourceInfo, true); } return null; } private static bool IsUnitTypeResourceName(string? schemaVersion) { return schemaVersion != null && schemaVersion == "0.1"; } /// /// Finds the module and preferred resource name for processing the configuration unit. /// /// The internal configuration unit. /// A tuple containing the module info and preferred resource name, or null if not found. private (PSObject Module, string ResourceName)? FindUnitModule(ConfigurationUnitAndModule unitInternal) { PSObject? foundModule = null; string resourceName = string.Empty; // If module has been specified, find it and assume that the resource will be within it. // Do this first as we do not currently gain much from FindDscResource; if that changes then it can be the primary. if (unitInternal.Module != null) { foundModule = this.ProcessorEnvironment.FindModule(unitInternal); if (foundModule != null) { resourceName = unitInternal.ResourceName; } } else { dynamic? foundResource = this.ProcessorEnvironment.FindDscResource(unitInternal); if (foundResource != null) { foundModule = foundResource.PSGetModuleInfo; // Hopefully they will never change the properties name. If someone can explain to me // why assign it Name to $_ in Find-DscResource turns into a string in PowerShell but // into a PSObject here that would be nice... resourceName = foundResource.Name.ToString(); } } if (foundModule != null) { return (foundModule, resourceName); } return null; } private DscResourceInfoInternal PrepareUnitForProcessing(ConfigurationUnitAndModule unitInternal) { // Invoke-DscResource makes a call to Get-DscResource which looks at the entire PSModulePath // to see if a resource exists. DscResourcesMap is an attempt to try to optimize Get-DscResource // by making just one call and get all of them, but it doesn't support minVersion and maxVersion. // For now, lets make PowerShell fully figure out which module to use and try to optimize it later. // This class will have a private member Lazy which will be initialized by calling // this.ProcessorEnvironment.GetAllDscResources() // To improve the performance even more, we will still need Invoke-DscResource to be update to // get a DSC resource info object instead of calling Get-DscResource every time. var dscResourceInfo = this.ProcessorEnvironment.GetDscResource(unitInternal); if (dscResourceInfo is null) { var findUnitModuleResult = this.FindUnitModule(unitInternal); if (findUnitModuleResult is null) { throw new FindDscResourceNotFoundException(unitInternal.ResourceName, unitInternal.Module); } this.ProcessorEnvironment.InstallModule(findUnitModuleResult.Value.Module); // Now we should find it. dscResourceInfo = this.ProcessorEnvironment.GetDscResource(unitInternal); if (dscResourceInfo is null) { throw new InstallDscResourceException(unitInternal.ResourceName, unitInternal.Module); } } // PowerShell will prompt the user when a module that is downloaded from the internet is imported. // For a hosted environment, this will throw an exception because it doesn't support user interaction. // In the case we don't import the module here, eventually Invoke-DscResource will fail for class // resources because they will call a method on a null obj. It is easier to just fail here. // The exception being thrown will have the correct details (user needs to call Unblock-File) // instead of the cryptic Invoke with 0 arguments. if (!string.IsNullOrEmpty(dscResourceInfo.Path)) { try { this.ProcessorEnvironment.ImportModule(dscResourceInfo.Path); } catch (Exception e) { throw new ImportModuleException(dscResourceInfo.ModuleName, e); } } return dscResourceInfo; } private ConfigurationUnitProcessorDetails GetUnitProcessorDetailsLocal( string unitName, DscResourceInfoInternal dscResourceInfo, bool importModule) { // I'm looking at you resources under C:\WINDOWS\system32\WindowsPowershell if (dscResourceInfo.ModuleName is null || dscResourceInfo.Version is null) { return Factory.CreateUnitProcessorDetails( dscResourceInfo.Name, dscResourceInfo, null, null, null); } var module = PowerShellHelpers.CreateModuleSpecification( dscResourceInfo.ModuleName, dscResourceInfo.Version.ToString()); // Get-InstalledModule only works for modules installed via PowerShell-Get. // There are some properties that can only be obtain by that it so is better to take both. var moduleInfo = this.ProcessorEnvironment.GetAvailableModule(module); var installedModule = this.ProcessorEnvironment.GetInstalledModule(module); if (importModule) { this.ProcessorEnvironment.ImportModule(module); } return Factory.CreateUnitProcessorDetails( dscResourceInfo.Name, dscResourceInfo, moduleInfo, installedModule, this.GetCertificates(moduleInfo)); } private List? GetCertificates(PSModuleInfo? moduleInfo) { if (moduleInfo is null) { return null; } // TODO: we still need to investigate more here, but lets start with something. var paths = new List(); var psdPath = Path.Combine(moduleInfo.ModuleBase, $"{moduleInfo.Name}.psd1"); if (File.Exists(psdPath)) { paths.Add(psdPath); } var psmPath = Path.Combine(moduleInfo.ModuleBase, $"{moduleInfo.Name}.psm1"); if (File.Exists(psmPath)) { paths.Add(psmPath); } return this.ProcessorEnvironment.GetCertsOfValidSignedFiles(paths.ToArray()); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/PowerShell/Unit/PowerShellConfigurationUnitProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.PowerShell.Unit { using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments; using Microsoft.Management.Configuration.Processor.Unit; using Windows.Foundation.Collections; /// /// Provides access to a specific configuration unit within the runtime. /// internal sealed partial class PowerShellConfigurationUnitProcessor : ConfigurationUnitProcessorBase, IConfigurationUnitProcessor { private readonly IProcessorEnvironment processorEnvironment; private readonly ConfigurationUnitAndResource unitResource; /// /// Initializes a new instance of the class. /// /// Processor environment. /// UnitResource. /// Whether it is under limit mode. internal PowerShellConfigurationUnitProcessor(IProcessorEnvironment processorEnvironment, ConfigurationUnitAndResource unitResource, bool isLimitMode = false) : base(unitResource.UnitInternal, isLimitMode) { this.processorEnvironment = processorEnvironment; this.unitResource = unitResource; } /// protected override ValueSet GetSettingsInternal() { return this.processorEnvironment.InvokeGetResource( this.unitResource.GetSettings(), this.unitResource.ResourceName, this.unitResource.Module); } /// protected override bool TestSettingsInternal() { return this.processorEnvironment.InvokeTestResource( this.unitResource.GetSettings(), this.unitResource.ResourceName, this.unitResource.Module); } /// protected override bool ApplySettingsInternal() { return this.processorEnvironment.InvokeSetResource( this.unitResource.GetSettings(), this.unitResource.ResourceName, this.unitResource.Module); } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Properties/AssemblyInfo.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- using System.Runtime.CompilerServices; using System.Runtime.Versioning; // InternalsVisibleTo specifies that types that are ordinarily visible only within the current // assembly are visible to a specified assembly. This is only for types and members with internal // or private protected scope, NOT private. Add any test dll that requires access to internal members. [assembly: InternalsVisibleTo("Microsoft.Management.Configuration.UnitTests")] // Needed to allow us mock internal interfaces. [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] #if WinGetCsWinRTEmbedded // Allow our consuming assemblies access when built embedded. [assembly: InternalsVisibleTo("Microsoft.WinGet.Configuration.Engine")] [assembly: InternalsVisibleTo("ConfigurationRemotingServer")] #endif // Forcibly set the target and supported platforms due to the internal build setup. // Keep in sync with project versions. [assembly: TargetPlatform("Windows10.0.26100.0")] [assembly: SupportedOSPlatform("Windows10.0.17763.0")] ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Public/DSCv3ConfigurationSetProcessorFactory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor { using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.DSCv3.Helpers; using Microsoft.Management.Configuration.Processor.DSCv3.Set; using Microsoft.Management.Configuration.Processor.Factory; /// /// IConfigurationSetProcessorFactory implementation using DSC v3. /// internal sealed partial class DSCv3ConfigurationSetProcessorFactory : ConfigurationSetProcessorFactoryBase, IConfigurationSetProcessorFactory, IDictionary { private const string DscExecutablePathPropertyName = "DscExecutablePath"; private const string FoundDscExecutablePathPropertyName = "FoundDscExecutablePath"; private const string DiagnosticTraceEnabledPropertyName = "DiagnosticTraceEnabled"; private const string FindDscStateMachinePropertyName = "FindDscStateMachine"; private ProcessorSettings processorSettings = new (); /// /// Initializes a new instance of the class. /// public DSCv3ConfigurationSetProcessorFactory() { this.processorSettings.DiagnosticsSink = this; } /// /// Gets or sets the path to the DSC v3 executable. /// public string? DscExecutablePath { get { return this.processorSettings.DscExecutablePath; } set { if (this.IsLimitMode()) { throw new InvalidOperationException("Setting DscExecutablePath in limit mode is invalid."); } this.processorSettings.DscExecutablePath = value; } } #if !AICLI_DISABLE_TEST_HOOKS /// /// Gets the processor settings; for tests only. /// public ProcessorSettings Settings { get { return this.processorSettings; } } #endif /// public ICollection Keys => throw new NotImplementedException(); /// public ICollection Values => throw new NotImplementedException(); /// public int Count => throw new NotImplementedException(); /// public bool IsReadOnly => this.IsLimitMode(); /// public string this[string key] { get => this.GetValue(key); set => this.SetValue(key, value); } /// public void Add(string key, string value) { this.SetValue(key, value); } /// public void Add(KeyValuePair item) { this.SetValue(item.Key, item.Value); } /// public void Clear() { throw new NotImplementedException(); } /// public bool Contains(KeyValuePair item) { throw new NotImplementedException(); } /// public bool ContainsKey(string key) { switch (key) { case DscExecutablePathPropertyName: return this.DscExecutablePath != null; } return false; } /// public void CopyTo(KeyValuePair[] array, int arrayIndex) { throw new NotImplementedException(); } /// public IEnumerator> GetEnumerator() { throw new NotImplementedException(); } /// public bool Remove(string key) { throw new NotImplementedException(); } /// public bool Remove(KeyValuePair item) { throw new NotImplementedException(); } /// public bool TryGetValue(string key, [MaybeNullWhen(false)] out string value) { value = null; switch (key) { case DscExecutablePathPropertyName: value = this.DscExecutablePath!; return true; case FoundDscExecutablePathPropertyName: value = this.processorSettings.GetFoundDscExecutablePath() !; return true; case DiagnosticTraceEnabledPropertyName: value = this.processorSettings.DiagnosticTraceEnabled.ToString(); return true; case FindDscStateMachinePropertyName: value = this.processorSettings.PumpFindDscStateMachine().ToString(); return true; } return false; } /// IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } /// protected override IConfigurationSetProcessor CreateSetProcessorInternal(ConfigurationSet? set, bool isLimitMode) { ProcessorSettings processorSettingsCopy = this.processorSettings.Clone(); this.OnDiagnostics(DiagnosticLevel.Verbose, "Creating set processor with settings:\n" + processorSettingsCopy.ToString()); return new DSCv3ConfigurationSetProcessor(processorSettingsCopy, set, isLimitMode) { SetProcessorFactory = this }; } private string GetValue(string name) { if (this.TryGetValue(name, out string? result)) { return result; } throw new ArgumentOutOfRangeException($"Invalid property name: {name}"); } private void SetValue(string name, string value) { switch (name) { case DscExecutablePathPropertyName: this.DscExecutablePath = value; break; case DiagnosticTraceEnabledPropertyName: this.processorSettings.DiagnosticTraceEnabled = bool.Parse(value); break; default: throw new ArgumentOutOfRangeException($"Invalid property name: {name}"); } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Public/IPowerShellConfigurationProcessorFactoryProperties.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor { using System.Collections.Generic; /// /// Properties for the configuration processor factory. /// public interface IPowerShellConfigurationProcessorFactoryProperties { /// /// Gets or sets the processor type. /// PowerShellConfigurationProcessorType ProcessorType { get; set; } /// /// Gets or sets the additional module paths. /// IReadOnlyList? AdditionalModulePaths { get; set; } /// /// Gets or sets the configuration policy. /// PowerShellConfigurationProcessorPolicy Policy { get; set; } /// /// Gets or sets the module location. /// PowerShellConfigurationProcessorLocation Location { get; set; } /// /// Gets or sets the install module path. Only used for Scope.Custom. /// string? CustomLocation { get; set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Public/PathEnvironmentVariableHandler.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Helpers { using System; using System.Collections.Generic; /// /// Class for handling PATH environment variable. /// public static class PathEnvironmentVariableHandler { private const string PathEnvironmentVariable = "PATH"; private static readonly object EnvironmentVariableLock = new object(); /// /// Gets the lock to read or write PATH environment variable. /// public static object Lock { get { return EnvironmentVariableLock; } } /// /// Updates the process's PATH environment variable if new paths added. /// Only adds new paths since we add to PATH in other code which may not be in the registry. /// public static void UpdatePath() { HashSet paths = new HashSet(Environment.GetEnvironmentVariable(PathEnvironmentVariable)?.Split(';') ?? Array.Empty()); var originalPathsSize = paths.Count; AddPathsIfNotExist(paths, Environment.GetEnvironmentVariable(PathEnvironmentVariable, EnvironmentVariableTarget.Machine)?.Split(';')); AddPathsIfNotExist(paths, Environment.GetEnvironmentVariable(PathEnvironmentVariable, EnvironmentVariableTarget.User)?.Split(';')); if (paths.Count > originalPathsSize) { lock (Lock) { Environment.SetEnvironmentVariable(PathEnvironmentVariable, string.Join(';', paths)); } } } // TODO: Currently it always adds new paths to the end. The "proper" thing to do would probably be to calculate // the full new list of paths (what one would expect to get from a new process launch) and use a line merge algorithm // with a strategy that puts the ephemeral entries before the new permanent ones. #pragma warning disable SA1011 // Closing square brackets should be spaced correctly private static void AddPathsIfNotExist(HashSet currentPaths, string[]? paths) #pragma warning restore SA1011 // Closing square brackets should be spaced correctly { if (paths is not null) { foreach (var path in paths) { if (!currentPaths.Contains(path)) { currentPaths.Add(path); } } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Public/PowerShellConfigurationProcessorLocation.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor { /// /// The location where modules are going to be installed. /// public enum PowerShellConfigurationProcessorLocation { /// /// Current user path. /// CurrentUser = 0, /// /// AllUsers path. Requires admin. /// AllUsers = 1, /// /// The winget location %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules. /// WinGetModulePath = 2, /// /// Custom path. /// Custom = 3, /// /// Default. /// Default = WinGetModulePath, } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Public/PowerShellConfigurationProcessorPolicy.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor { /// /// Processor policy. /// For Processor type Default and Hosted they mean the same as PowerShell ExecutionPolicy. /// https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies. /// public enum PowerShellConfigurationProcessorPolicy { /// /// Unrestricted. /// Unrestricted = 0, /// /// RemoteSigned. /// RemoteSigned = 1, /// /// AllSigned. /// AllSigned = 2, /// /// Restricted. /// Restricted = 3, /// /// Bypass. /// Bypass = 4, /// /// Undefined. /// Undefined = 5, /// /// Default. /// Default = RemoteSigned, } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Public/PowerShellConfigurationProcessorType.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor { /// /// Configuration processor runspace type. /// public enum PowerShellConfigurationProcessorType { /// /// Uses default runspace. Requires to be running in PowerShell. Uses current runspace. /// Default, /// /// Creates a new runspace in a hosted environment. /// Hosted, } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Public/PowerShellConfigurationSetProcessorFactory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor { using System; using System.Collections.Generic; using System.IO; using System.Text; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.Factory; using Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments; using Microsoft.Management.Configuration.Processor.PowerShell.Set; using Microsoft.Management.Configuration.SetProcessorFactory; using static Microsoft.Management.Configuration.Processor.PowerShell.Constants.PowerShellConstants; /// /// IConfigurationSetProcessorFactory implementation using PowerShell DSC v2. /// #if WinGetCsWinRTEmbedded internal #else public #endif sealed partial class PowerShellConfigurationSetProcessorFactory : ConfigurationSetProcessorFactoryBase, IConfigurationSetProcessorFactory, IPowerShellConfigurationProcessorFactoryProperties, IPwshConfigurationSetProcessorFactoryProperties { // Backing variables for properties that are restricted in limit mode. private PowerShellConfigurationProcessorType processorType = PowerShellConfigurationProcessorType.Default; private IReadOnlyList? additionalModulePaths; private IReadOnlyList? implicitModulePaths; private PowerShellConfigurationProcessorPolicy policy = PowerShellConfigurationProcessorPolicy.Default; private PowerShellConfigurationProcessorLocation location = PowerShellConfigurationProcessorLocation.Default; private string? customLocation; /// /// Initializes a new instance of the class. /// public PowerShellConfigurationSetProcessorFactory() { } /// /// Gets or sets the processor type. /// public PowerShellConfigurationProcessorType ProcessorType { get { return this.processorType; } set { if (this.IsLimitMode()) { throw new InvalidOperationException("Setting ProcessorType in limit mode is invalid."); } this.processorType = value; } } /// /// Gets or sets the additional module paths. /// public IReadOnlyList? AdditionalModulePaths { get { return this.additionalModulePaths; } set { if (this.IsLimitMode()) { throw new InvalidOperationException("Setting AdditionalModulePaths in limit mode is invalid."); } // Create a copy of incoming value List newModulePaths = new List(); if (value != null) { foreach (string path in value) { newModulePaths.Add(path); } } // Add implicit module paths if applicable if (this.implicitModulePaths != null) { foreach (string path in this.implicitModulePaths) { if (!newModulePaths.Contains(path)) { newModulePaths.Add(path); } } } this.additionalModulePaths = newModulePaths; } } /// /// Gets or sets the implicit module paths. These paths are always included in AdditionalModulePaths. /// public IReadOnlyList? ImplicitModulePaths { get { return this.implicitModulePaths; } set { if (this.IsLimitMode()) { throw new InvalidOperationException("Setting ImplicitModulePaths in limit mode is invalid."); } this.implicitModulePaths = value; // Apply to additional module paths if applicable. if (this.implicitModulePaths != null) { List newModulePaths = new List(); if (this.additionalModulePaths != null) { foreach (string path in this.additionalModulePaths) { newModulePaths.Add(path); } } foreach (string path in this.implicitModulePaths) { if (!newModulePaths.Contains(path)) { newModulePaths.Add(path); } } this.additionalModulePaths = newModulePaths; } } } /// /// Gets or sets the configuration policy. /// public PowerShellConfigurationProcessorPolicy Policy { get { return this.policy; } set { if (this.IsLimitMode()) { throw new InvalidOperationException("Setting Policy in limit mode is invalid."); } this.policy = value; } } /// /// Gets or sets the configuration policy. /// PwshConfigurationProcessorPolicy IPwshConfigurationSetProcessorFactoryProperties.Policy { get { return Helpers.TypeHelpers.ToPwshConfigurationProcessorPolicy(this.Policy); } set { this.Policy = Helpers.TypeHelpers.ToPowerShellConfigurationProcessorPolicy(value); } } /// /// Gets or sets the module location. /// public PowerShellConfigurationProcessorLocation Location { get { return this.location; } set { if (this.IsLimitMode()) { throw new InvalidOperationException("Setting Location in limit mode is invalid."); } this.location = value; } } /// /// Gets or sets the module location. /// PwshConfigurationProcessorLocation IPwshConfigurationSetProcessorFactoryProperties.Location { get { return Helpers.TypeHelpers.ToPwshConfigurationProcessorLocation(this.Location); } set { this.Location = Helpers.TypeHelpers.ToPowerShellConfigurationProcessorLocation(value); } } /// /// Gets or sets the install module path. Only used for Scope = Custom. /// public string? CustomLocation { get { return this.customLocation; } set { if (this.IsLimitMode()) { throw new InvalidOperationException("Setting CustomLocation in limit mode is invalid."); } this.customLocation = value; } } /// /// Gets the winget module path. /// /// The winget module path. internal static string GetWinGetModulePath() { return Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\WinGet\Configuration\Modules"); } /// /// Sends diagnostic if appropriate for PowerShell streams. /// /// The level of this diagnostic message. /// The PowerShell object. internal void OnDiagnostics(DiagnosticLevel level, System.Management.Automation.PowerShell pwsh) { if (this.AreDiagnosticsEnabled() && level >= this.MinimumLevel && pwsh.HadErrors) { var builder = new StringBuilder(); // There are the last commands ran by that PowerShell obj, not all in our session. builder.Append("PowerShellCommands: "); foreach (var c in pwsh.Commands.Commands) { builder.Append($"['{c.CommandText}'"); if (c.Parameters.Count > 0) { builder.Append(" Parameters: "); foreach (var p in c.Parameters) { builder.Append($"{p.Name} = '{p.Value}' "); } builder.Append("]"); } builder.AppendLine(); } foreach (var error in pwsh.Streams.Error) { builder.AppendLine($"[WriteError] {error}"); } this.OnDiagnostics(level, builder.ToString()); } } /// protected override IConfigurationSetProcessor CreateSetProcessorInternal(ConfigurationSet? set, bool isLimitMode) { var envFactory = new ProcessorEnvironmentFactory(this.ProcessorType); var processorEnvironment = envFactory.CreateEnvironment( this, this.Policy); if (this.AdditionalModulePaths is not null) { processorEnvironment.PrependPSModulePaths(this.AdditionalModulePaths); } // Always add the winget path. var wingetModulePath = GetWinGetModulePath(); processorEnvironment.PrependPSModulePath(wingetModulePath); if (this.Location == PowerShellConfigurationProcessorLocation.WinGetModulePath) { this.OnDiagnostics(DiagnosticLevel.Verbose, "Using winget module path"); processorEnvironment.SetLocation(PowerShellConfigurationProcessorLocation.Custom, wingetModulePath); } else if (this.Location == PowerShellConfigurationProcessorLocation.Custom) { if (string.IsNullOrEmpty(this.CustomLocation)) { throw new ArgumentNullException(nameof(this.CustomLocation)); } processorEnvironment.SetLocation(this.Location, this.CustomLocation); processorEnvironment.PrependPSModulePath(this.CustomLocation); } else { processorEnvironment.SetLocation(this.Location, null); } this.OnDiagnostics(DiagnosticLevel.Verbose, $" Effective module path:\n{processorEnvironment.GetVariable(Variables.PSModulePath)}"); processorEnvironment.ValidateRunspace(); return new PowerShellConfigurationSetProcessor(processorEnvironment, set, isLimitMode) { SetProcessorFactory = this }; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessorBase.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Set { using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.Processor.Factory; using Microsoft.Management.Configuration.Processor.Unit; /// /// IConfigurationSetProcessor base implementation. /// internal abstract partial class ConfigurationSetProcessorBase { private readonly ConfigurationSet? configurationSet; private List limitUnitList = new List(); /// /// Initializes a new instance of the class. /// /// Configuration set. /// Whether the set processor should work in limitation mode. public ConfigurationSetProcessorBase(ConfigurationSet? configurationSet, bool isLimitMode) { this.configurationSet = configurationSet; this.IsLimitMode = isLimitMode; // In limit mode, configurationSet is the limitation set to be used. It cannot be null. if (this.IsLimitMode) { if (this.configurationSet == null) { throw new ArgumentNullException(nameof(configurationSet), "configurationSet is required in limit mode."); } foreach (var unit in this.configurationSet.Units) { this.limitUnitList.Add(unit); } } } /// /// Gets or initializes the set processor factory. /// internal ConfigurationSetProcessorFactoryBase? SetProcessorFactory { get; init; } /// /// Gets a value indicating whether the set processor is running in limit mode. /// internal bool IsLimitMode { get; private set; } /// /// Gets the configuration set for this processor. /// protected ConfigurationSet? ConfigurationSet { get { return this.configurationSet; } } /// /// Creates a configuration unit processor for the given unit. /// /// Configuration unit. /// A configuration unit processor. public IConfigurationUnitProcessor CreateUnitProcessor(ConfigurationUnit incomingUnit) { try { this.OnDiagnostics(DiagnosticLevel.Informational, $"GetUnitProcessorDetails is running in limit mode: {this.IsLimitMode}."); // CreateUnitProcessor can only be called once on each configuration unit in limit mode. var unit = this.GetConfigurationUnit(incomingUnit, true); IConfigurationUnitProcessor result = this.CreateUnitProcessorInternal(unit); this.OnDiagnostics(DiagnosticLevel.Verbose, "... done creating unit processor."); return result; } catch (Exception ex) { this.OnDiagnostics(DiagnosticLevel.Error, ex.ToString()); throw; } } /// /// Gets the configuration unit processor details for the given unit. /// /// Configuration unit. /// Detail flags. /// Configuration unit processor details. public IConfigurationUnitProcessorDetails? GetUnitProcessorDetails(ConfigurationUnit incomingUnit, ConfigurationUnitDetailFlags detailFlags) { try { this.OnDiagnostics(DiagnosticLevel.Informational, $"GetUnitProcessorDetails is running in limit mode: {this.IsLimitMode}."); // GetUnitProcessorDetails can be invoked multiple times on each configuration unit in limit mode. var unit = this.GetConfigurationUnit(incomingUnit); return this.GetUnitProcessorDetailsInternal(unit, detailFlags); } catch (Exception ex) { this.OnDiagnostics(DiagnosticLevel.Error, ex.ToString()); throw; } } /// /// Gets all configuration units for the given unit type. /// Returned units may be of types other than the one passed in. /// /// Find unit processors options. /// A list of unit processor details. public IList FindUnitProcessors(FindUnitProcessorsOptions findOptions) { try { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `FindUnitProcessors` ..."); return this.FindUnitProcessorsInternal(findOptions); } catch (Exception ex) { this.OnDiagnostics(DiagnosticLevel.Error, ex.ToString()); throw; } } /// /// Creates a configuration unit processor for the given unit. /// /// Configuration unit. /// A configuration unit processor. protected abstract IConfigurationUnitProcessor CreateUnitProcessorInternal(ConfigurationUnit unit); /// /// Gets the configuration unit processor details for the given unit. /// /// Configuration unit. /// Detail flags. /// Configuration unit processor details. protected abstract IConfigurationUnitProcessorDetails? GetUnitProcessorDetailsInternal(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags); /// /// Finds unit processors based on the input FindUnitProcessorsOptions. /// Derive from IFindUnitProcessorsSetProcessor and implement an override to support this. /// /// Find unit processors options. /// A list of unit processor details. protected virtual IList FindUnitProcessorsInternal(FindUnitProcessorsOptions findOptions) { throw new NotImplementedException("Configuration set processor did not implement FindUnitProcessorsInternal."); } /// /// Sends diagnostics to factory. /// /// The level of this diagnostic message. /// The diagnostic message. protected void OnDiagnostics(DiagnosticLevel level, string message) { this.SetProcessorFactory?.OnDiagnostics(level, message); } private static bool ConfigurationUnitEquals(ConfigurationUnit first, ConfigurationUnit second) { var firstIdentifier = first.Identifier; var firstIntent = first.Intent; var firstType = first.Type; var secondIdentifier = second.Identifier; var secondType = second.Type; var secondIntent = second.Intent; if (firstIdentifier != secondIdentifier || firstType != secondType || firstIntent != secondIntent) { return false; } var firstEnvironment = first.Environment; var secondEnvironment = second.Environment; if (firstEnvironment.Context != secondEnvironment.Context || firstEnvironment.ProcessorIdentifier != secondEnvironment.ProcessorIdentifier || !firstEnvironment.ProcessorProperties.ContentEquals(secondEnvironment.ProcessorProperties)) { return false; } if (!first.Settings.ContentEquals(second.Settings)) { return false; } if (!first.Metadata.ContentEquals(second.Metadata)) { return false; } // Note: Consider group units logic when group units are supported. return true; } [MethodImpl(MethodImplOptions.Synchronized)] private ConfigurationUnit GetConfigurationUnit(ConfigurationUnit incomingUnit, bool useLimitList = false) { if (this.IsLimitMode) { if (this.configurationSet == null) { throw new InvalidOperationException("Configuration set should not be null in limit mode."); } var unitList = useLimitList ? this.limitUnitList : this.configurationSet.Units; for (int i = 0; i < unitList.Count; i++) { var unit = unitList[i]; if (ConfigurationUnitEquals(incomingUnit, unit)) { if (useLimitList) { this.limitUnitList.RemoveAt(i); } return unit; } // Note: Consider group units logic when group units are supported. } this.OnDiagnostics(DiagnosticLevel.Error, "Configuration unit not found in limit mode."); throw new InvalidOperationException("Configuration unit not found in limit mode."); } else { return incomingUnit; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/ApplySettingsResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using Microsoft.Management.Configuration; /// /// Implements IApplySettingsResult. /// internal sealed partial class ApplySettingsResult : IApplySettingsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public ApplySettingsResult(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public ConfigurationUnitResultInformation InternalResult { get; } = new ConfigurationUnitResultInformation(); /// public bool RebootRequired { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessorBase.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.Processor.Factory; using Microsoft.Management.Configuration.Processor.Helpers; using Windows.Foundation.Collections; /// /// IConfigurationUnitProcessor base implementation. /// internal abstract partial class ConfigurationUnitProcessorBase { private readonly ConfigurationUnitInternal unitInternal; private readonly bool isLimitMode; private bool isTestInvoked = false; private bool isApplyInvoked = false; /// /// Initializes a new instance of the class. /// /// UnitInternal. /// Whether it is under limit mode. internal ConfigurationUnitProcessorBase(ConfigurationUnitInternal unitInternal, bool isLimitMode) { this.unitInternal = unitInternal; this.isLimitMode = isLimitMode; } /// /// Gets the configuration unit that the processor was created for. /// public ConfigurationUnit Unit => this.unitInternal.Unit; /// /// Gets or initializes the set processor factory. /// internal ConfigurationSetProcessorFactoryBase? SetProcessorFactory { get; init; } /// /// Gets the internal configuration unit. /// protected ConfigurationUnitInternal UnitInternal => this.unitInternal; /// /// Gets the current system state for the configuration unit. /// Calls Get on the DSC resource. /// /// A . public IGetSettingsResult GetSettings() { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `Get` for resource: {this.unitInternal.QualifiedName}..."); this.CheckLimitMode(ConfigurationUnitIntent.Inform); var result = new GetSettingsResult(this.Unit); try { result.Settings = this.GetSettingsInternal(); } catch (Exception e) { this.ExtractExceptionInformation(e, result.InternalResult); } this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `Get`."); return result; } /// /// Determines if the system is already in the state described by the configuration unit. /// Calls Test on the DSC resource. /// /// A . public ITestSettingsResult TestSettings() { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `Test` for resource: {this.unitInternal.QualifiedName}..."); if (this.Unit.Intent == ConfigurationUnitIntent.Inform) { this.OnDiagnostics(DiagnosticLevel.Error, "`Test` should not be called on a unit with intent of `Inform`"); throw new NotSupportedException(); } this.CheckLimitMode(ConfigurationUnitIntent.Assert); var result = new TestSettingsResult(this.Unit); result.TestResult = ConfigurationTestResult.Failed; try { bool testResult = this.TestSettingsInternal(); result.TestResult = testResult ? ConfigurationTestResult.Positive : ConfigurationTestResult.Negative; } catch (Exception e) { this.ExtractExceptionInformation(e, result.InternalResult); } this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `Test`."); return result; } /// /// Applies the state described in the configuration unit. /// Calls Set in the DSC resource. /// /// A . public IApplySettingsResult ApplySettings() { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `Apply` for resource: {this.unitInternal.QualifiedName}..."); if (this.Unit.Intent == ConfigurationUnitIntent.Inform || this.Unit.Intent == ConfigurationUnitIntent.Assert) { this.OnDiagnostics(DiagnosticLevel.Error, $"`Apply` should not be called on a unit with intent of `{this.Unit.Intent}`"); throw new NotSupportedException(); } this.CheckLimitMode(ConfigurationUnitIntent.Apply); var result = new ApplySettingsResult(this.Unit); try { result.RebootRequired = this.ApplySettingsInternal(); } catch (Exception e) { this.ExtractExceptionInformation(e, result.InternalResult); } this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `Apply`."); return result; } /// /// Gets the current settings for all the instances of a configuration unit. /// /// A . public IGetAllSettingsResult GetAllSettings() { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `GetAllSettings` for resource: {this.unitInternal.QualifiedName}..."); this.CheckLimitMode(ConfigurationUnitIntent.Inform); var result = new GetAllSettingsResult(this.Unit); try { result.Settings = this.GetAllSettingsInternal(); } catch (Exception e) { this.ExtractExceptionInformation(e, result.InternalResult); } this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `GetAllSettings`."); return result; } /// /// Gets all configuration units for the given unit type. /// Returned units may be of types other than the one passed in. /// /// A . public IGetAllUnitsResult GetAllUnits() { this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `GetAllUnits` for resource: {this.unitInternal.QualifiedName}..."); this.CheckLimitMode(ConfigurationUnitIntent.Inform); var result = new GetAllUnitsResult(this.Unit); try { result.Units = this.GetAllUnitsInternal(); } catch (Exception e) { this.ExtractExceptionInformation(e, result.InternalResult); } this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `GetAllUnits`."); return result; } /// /// Gets the current settings. /// /// The current settings. protected abstract ValueSet GetSettingsInternal(); /// /// Tests the current settings. /// /// A boolean indicating whether the settings are in the desired state. protected abstract bool TestSettingsInternal(); /// /// Applies the desired settings. /// /// A boolean indicating whether a reboot is required. protected abstract bool ApplySettingsInternal(); /// /// Gets the current settings for all the instances of a configuration unit. /// Derive from IGetAllSettingsConfigurationUnitProcessor and implement an override to support this. /// /// The settings as ValueSets. protected virtual IList? GetAllSettingsInternal() { throw new NotImplementedException("Configuration unit processor did not implement GetAllSettingsInternal."); } /// /// Gets all configuration units for the given unit type. /// Returned units may be of types other than the one passed in. /// Derive from IGetAllUnitsConfigurationUnitProcessor and implement an override to support this. /// /// The configuration units. protected virtual IList? GetAllUnitsInternal() { throw new NotImplementedException("Configuration unit processor did not implement GetAllUnitsInternal."); } /// /// Sends diagnostics if appropriate. /// /// The level of this diagnostic message. /// The diagnostic message. protected void OnDiagnostics(DiagnosticLevel level, string message) { this.SetProcessorFactory?.OnDiagnostics(level, message); } private void ExtractExceptionInformation(Exception e, ConfigurationUnitResultInformation resultInformation) { this.OnDiagnostics(DiagnosticLevel.Verbose, e.ToString()); IConfigurationUnitResultException? configurationUnitResultException = e as IConfigurationUnitResultException; if (configurationUnitResultException != null) { resultInformation.ResultCode = e; resultInformation.Description = configurationUnitResultException.Description; resultInformation.Details = configurationUnitResultException.Details; resultInformation.ResultSource = configurationUnitResultException.ResultSource; } else { var inner = e.GetMostInnerException(); resultInformation.ResultCode = inner; resultInformation.Description = e.Message; resultInformation.Details = e.ToString(); resultInformation.ResultSource = ConfigurationUnitResultSource.Internal; } } [MethodImpl(MethodImplOptions.Synchronized)] private void CheckLimitMode(ConfigurationUnitIntent intent) { if (!this.isLimitMode) { return; } if (intent == ConfigurationUnitIntent.Unknown) { throw new InvalidEnumArgumentException(nameof(ConfigurationUnitIntent.Unknown)); } if (intent == ConfigurationUnitIntent.Assert) { if (this.isTestInvoked) { this.OnDiagnostics(DiagnosticLevel.Error, "TestSettings is already invoked in limit mode."); throw new InvalidOperationException("TestSettings is already invoked in limit mode."); } else { this.isTestInvoked = true; } } if (intent == ConfigurationUnitIntent.Apply) { if (this.isApplyInvoked) { this.OnDiagnostics(DiagnosticLevel.Error, "ApplySettings is already invoked in limit mode."); throw new InvalidOperationException("ApplySettings is already invoked in limit mode."); } else { this.isApplyInvoked = true; } } // Get is always allowed now. } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitProcessorDetails.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using System; using System.Collections.Generic; using Microsoft.Management.Configuration; /// /// Provides information for a specific configuration unit within the runtime. /// internal sealed partial class ConfigurationUnitProcessorDetails : IConfigurationUnitProcessorDetails, IConfigurationUnitProcessorDetails2, IConfigurationUnitProcessorDetails3 { /// /// Initializes a new instance of the class. /// public ConfigurationUnitProcessorDetails() { } /// /// Gets or sets the name of the unit of configuration. /// required public string UnitType { get; internal set; } /// /// Gets or sets the description of the unit of configuration. /// public string? UnitDescription { get; internal set; } /// /// Gets or sets the URI of the documentation for the unit of configuration. /// public Uri? UnitDocumentationUri { get; internal set; } /// /// Gets or sets the URI of the icon for the unit of configuration. /// public Uri? UnitIconUri { get; internal set; } /// /// Gets or sets the name of the module containing the unit of configuration. /// public string? ModuleName { get; internal set; } /// /// Gets or sets the type of the module containing the unit of configuration. /// public string? ModuleType { get; internal set; } /// /// Gets or sets the source of the module containing the unit of configuration. /// public string? ModuleSource { get; internal set; } /// /// Gets or sets the description of the module containing the unit of configuration. /// public string? ModuleDescription { get; internal set; } /// /// Gets or sets the URI of the documentation for the module containing the unit of configuration. /// public Uri? ModuleDocumentationUri { get; internal set; } /// /// Gets or sets the URI for the published module containing the unit of configuration. /// public Uri? PublishedModuleUri { get; internal set; } /// /// Gets or sets the version of the module containing the unit of configuration. /// public string? Version { get; internal set; } /// /// Gets or sets the publishing date of the module containing the unit of configuration. /// public DateTimeOffset PublishedDate { get; internal set; } /// /// Gets or sets a value indicating whether the module is already present on the system. /// public bool IsLocal { get; internal set; } /// /// Gets or sets the author of the module containing the unit of configuration. /// public string? Author { get; internal set; } /// /// Gets or sets the publisher of the module containing the unit of configuration. /// public string? Publisher { get; internal set; } /// /// Gets or sets the signing certificate of the module files containing the unit of configuration. /// public IReadOnlyList? SigningInformation { get; internal set; } /// /// Gets or sets the settings information for the unit of configuration. /// public IReadOnlyList? Settings { get; internal set; } /// /// Gets or sets a value indicating whether the module comes from a public repository. /// public bool IsPublic { get; internal set; } /// /// Gets or sets a value indicating whether this resource is a group. /// public bool IsGroup { get; internal set; } /// /// Gets or sets the path of the resource. /// public string? Path { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitResultInformation.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using System; using Microsoft.Management.Configuration; /// /// Implements IConfigurationUnitResultInformation. /// internal sealed partial class ConfigurationUnitResultInformation : IConfigurationUnitResultInformation { /// public string? Description { get; internal set; } /// public string? Details { get; internal set; } /// public Exception? ResultCode { get; internal set; } /// public ConfigurationUnitResultSource ResultSource { get; internal set; } = ConfigurationUnitResultSource.None; } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/ConfigurationUnitSettingDetails.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using Microsoft.Management.Configuration; /// /// Provides information for a specific configuration unit setting. /// internal sealed partial class ConfigurationUnitSettingDetails : IConfigurationUnitSettingDetails { /// /// Initializes a new instance of the class. /// public ConfigurationUnitSettingDetails() { } /// /// Gets the name of the setting. /// required public string Identifier { get; init; } /// /// Gets the title of the setting. /// public string Title { get; init; } = string.Empty; /// /// Gets the description of the setting. /// public string Description { get; init; } = string.Empty; /// /// Gets a value indicating whether the setting is a key. This is used to determine if different settings are in conflict. /// public bool IsKey { get; init; } = false; /// /// Gets a value indicating whether a non-empty value for the setting is required. /// public bool IsRequired { get; init; } = false; /// /// Gets a value indicating whether the setting should be serialized in order to be applied on another system. /// public bool IsInformational { get; init; } = false; /// /// Gets the data type for the value of this setting. /// public Windows.Foundation.PropertyType Type { get; init; } = Windows.Foundation.PropertyType.Inspectable; /// /// Gets the semantics to be used for this setting. The goal is to enable richer conflict detection and authoring /// scenarios by having a deeper understanding of this value than "String". /// public string Schema { get; init; } = string.Empty; } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/GetAllSettingsResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using System.Collections.Generic; using Microsoft.Management.Configuration; using Windows.Foundation.Collections; /// /// Implements IGetAllSettingsResult. /// internal partial class GetAllSettingsResult : IGetAllSettingsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public GetAllSettingsResult(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public ConfigurationUnitResultInformation InternalResult { get; } = new ConfigurationUnitResultInformation(); /// public IList? Settings { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/GetAllUnitsResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using System.Collections.Generic; using Microsoft.Management.Configuration; using Windows.Foundation.Collections; /// /// Implements IGetAllUnitsResult. /// internal partial class GetAllUnitsResult : IGetAllUnitsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public GetAllUnitsResult(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public ConfigurationUnitResultInformation InternalResult { get; } = new ConfigurationUnitResultInformation(); /// public IList? Units { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/GetSettingsResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using Microsoft.Management.Configuration; using Windows.Foundation.Collections; /// /// Implements IGetSettingsResult. /// internal sealed partial class GetSettingsResult : IGetSettingsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public GetSettingsResult(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public ConfigurationUnitResultInformation InternalResult { get; } = new ConfigurationUnitResultInformation(); /// public ValueSet? Settings { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Processor/Unit/TestSettingsResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.Processor.Unit { using Microsoft.Management.Configuration; /// /// Implements ITestSettingsResult. /// internal sealed partial class TestSettingsResult : ITestSettingsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public TestSettingsResult(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public ConfigurationUnitResultInformation InternalResult { get; } = new ConfigurationUnitResultInformation(); /// public ConfigurationTestResult TestResult { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.Projection/Microsoft.Management.Configuration.Projection.csproj ================================================ net8.0-windows10.0.26100.0 AnyCpu enable enable $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ Debug;Release;ReleaseStatic true Microsoft.Management.Configuration $(OutDir) 10.0.26100.0 10.0.17763.0 True None Content PreserveNewest True ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestCollection.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Fixtures { using Xunit; /// /// Unit test collection. /// [CollectionDefinition("UnitTestCollection")] public class UnitTestCollection : ICollectionFixture { // This class has no code, and is never created. Its purpose is simply // to be the place to apply [CollectionDefinition] and all the // ICollectionFixture<> interfaces. } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestFixture.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Fixtures { using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using Microsoft.Management.Configuration.Processor; using Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments; using WinRT; using Xunit.Abstractions; /// /// Unit test fixture. /// public class UnitTestFixture { /// /// Initializes a new instance of the class. /// /// The message sink for the fixture. public UnitTestFixture(IMessageSink messageSink) { this.MessageSink = messageSink; string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new ArgumentException(); this.TestModulesPath = Path.Combine(assemblyPath, "TestCollateral", "PowerShellModules"); if (!Directory.Exists(this.TestModulesPath)) { throw new DirectoryNotFoundException(this.TestModulesPath); } // Use the environment variable if present, which is how ADO pipelines will find it. string? gitSearchPath = Environment.GetEnvironmentVariable("BUILD_SOURCESDIRECTORY"); if (string.IsNullOrWhiteSpace(gitSearchPath)) { gitSearchPath = Path.GetDirectoryName(assemblyPath); while (!string.IsNullOrEmpty(gitSearchPath)) { if (Directory.Exists(Path.Combine(gitSearchPath, ".git"))) { break; } gitSearchPath = Path.GetDirectoryName(gitSearchPath); } } this.GitRootPath = gitSearchPath ?? throw new DirectoryNotFoundException("git root path"); this.ExternalModulesPath = Path.Combine(this.GitRootPath, "src", "PowerShell", "ExternalModules"); if (!Directory.Exists(this.ExternalModulesPath)) { throw new DirectoryNotFoundException(this.ExternalModulesPath); } this.RecreateStatics(); } /// /// Gets the message sink for the fixture. /// public IMessageSink MessageSink { get; private init; } /// /// Gets the test module path. /// public string TestModulesPath { get; } /// /// Gets the git root path. /// public string GitRootPath { get; } /// /// Gets the external module path. /// public string ExternalModulesPath { get; } /// /// Gets the configuration statics object to use. /// internal IConfigurationStatics2 ConfigurationStatics { get; private set; } /// /// Creates a new statics object for use by the tests. /// [MemberNotNull("ConfigurationStatics")] public void RecreateStatics() { this.ConfigurationStatics = new ConfigurationStaticFunctions().As(); } /// /// Creates a runspace adding the test module path. /// /// Validate runspace. /// PowerShellRunspace. internal IProcessorEnvironment PrepareTestProcessorEnvironment(bool validate = false) { var processorEnv = new ProcessorEnvironmentFactory(PowerShellConfigurationProcessorType.Hosted).CreateEnvironment(null, PowerShellConfigurationProcessorPolicy.Unrestricted); processorEnv.PrependPSModulePath(this.ExternalModulesPath); processorEnv.PrependPSModulePath(this.TestModulesPath); if (validate) { processorEnv.ValidateRunspace(); } return processorEnv; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupMemberSettingsResultInstance.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { /// /// Implements IApplyGroupMemberSettingsResult. /// internal sealed partial class ApplyGroupMemberSettingsResultInstance : IApplyGroupMemberSettingsResult { /// /// Initializes a new instance of the class. /// /// The unit for this result. internal ApplyGroupMemberSettingsResultInstance(ConfigurationUnit unit) { this.Unit = unit; } /// public bool PreviouslyInDesiredState { get; internal set; } /// public bool RebootRequired { get; internal set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public TestConfigurationUnitResultInformation InternalResult { get; } = new TestConfigurationUnitResultInformation(); /// public ConfigurationUnitState State { get; internal set; } /// public ConfigurationUnit Unit { get; private init; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplyGroupSettingsResultInstance.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; /// /// Implements IApplyGroupSettingsResult. /// internal sealed partial class ApplyGroupSettingsResultInstance : IApplyGroupSettingsResult { /// /// Initializes a new instance of the class. /// /// The group for this result. internal ApplyGroupSettingsResultInstance(object? group) { this.Group = group; } /// public object? Group { get; private init; } /// public bool RebootRequired { get; internal set; } /// public IConfigurationUnitResultInformation? ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public TestConfigurationUnitResultInformation InternalResult { get; } = new TestConfigurationUnitResultInformation(); /// public IList? UnitResults { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/ApplySettingsResultInstance.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using Microsoft.Management.Configuration; /// /// Implements IApplySettingsResult. /// internal sealed partial class ApplySettingsResultInstance : IApplySettingsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public ApplySettingsResultInstance(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public TestConfigurationUnitResultInformation InternalResult { get; } = new TestConfigurationUnitResultInformation(); /// public bool RebootRequired { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationEnvironmentData.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Collections.Generic; /// /// Contains the data defining a configuration environment. /// internal class ConfigurationEnvironmentData { /// /// Initializes a new instance of the class. /// internal ConfigurationEnvironmentData() { } /// /// Gets or sets the security context. /// internal SecurityContext Context { get; set; } = SecurityContext.Current; /// /// Gets or sets the processor identifier. /// internal string ProcessorIdentifier { get; set; } = string.Empty; /// /// Gets or sets the processor properties. /// internal Dictionary ProcessorProperties { get; set; } = new (); /// /// Applies this environment to the given unit. /// /// The unit to apply to. /// The given unit. internal ConfigurationUnit ApplyToUnit(ConfigurationUnit unit) { var environment = unit.Environment; environment.Context = this.Context; environment.ProcessorIdentifier = this.ProcessorIdentifier; environment.ProcessorProperties.Clear(); foreach (var property in this.ProcessorProperties) { environment.ProcessorProperties.Add(property.Key, property.Value); } return unit; } /// /// Tests whether the given properties match this object's properties. /// /// The properties to test. /// True if the properties match; false if not. internal bool PropertiesEqual(IDictionary properties) { if (properties.Count != this.ProcessorProperties.Count) { return false; } foreach (var property in properties) { string? value = null; if (!this.ProcessorProperties.TryGetValue(property.Key, out value)) { return false; } if (property.Value != value) { return false; } } return true; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Linq; using System.Reflection; /// /// Contains extension methods for configuration objects. /// internal static class ConfigurationExtensions { /// /// Assigns the given properties to the configuration unit. /// /// The unit to assign the properties of. /// The properties to assign. /// The given ConfigurationUnit. internal static ConfigurationUnit Assign(this ConfigurationUnit unit, object properties) { PropertyInfo[] unitProperties = typeof(ConfigurationUnit).GetProperties(); foreach (PropertyInfo property in properties.GetType().GetProperties()) { PropertyInfo matchingProperty = unitProperties.First(pi => pi.Name == property.Name); matchingProperty.SetValue(unit, property.GetValue(properties)); } return unit; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationProcessorTestBase.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Windows.Storage.Streams; using Xunit; using Xunit.Abstractions; /// /// Test base that provides helpers for dealing with . /// public class ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. protected ConfigurationProcessorTestBase(UnitTestFixture fixture, ITestOutputHelper log) { this.Fixture = fixture; this.Log = log; this.EventSink = new DiagnosticsEventSink(fixture, log); } /// /// Gets the event sink for this test base. /// protected DiagnosticsEventSink EventSink { get; private set; } /// /// Gets the test fixture. /// protected UnitTestFixture Fixture { get; private init; } /// /// Gets the output helper. /// protected ITestOutputHelper Log { get; private init; } /// /// Create a new with the diagnostics event hooked up. /// /// The factory to use. /// The new object. internal ConfigurationProcessor CreateConfigurationProcessorWithDiagnostics(IConfigurationSetProcessorFactory? factory = null) { ConfigurationProcessor result = this.Fixture.ConfigurationStatics.CreateConfigurationProcessor(factory); result.Diagnostics += this.EventSink.DiagnosticsHandler; result.MinimumLevel = DiagnosticLevel.Verbose; return result; } /// /// Creates an input stream from the given string. /// /// The contents that the stream should contain. /// The created stream. internal IInputStream CreateStream(string contents) { InMemoryRandomAccessStream result = new InMemoryRandomAccessStream(); using (DataWriter writer = new DataWriter(result)) { writer.UnicodeEncoding = UnicodeEncoding.Utf8; writer.WriteString(contents); writer.StoreAsync().AsTask().Wait(); writer.DetachStream(); } result.Seek(0); return result; } /// /// Creates an string from the given output stream. /// /// The output stream. /// The created string. internal string ReadStream(InMemoryRandomAccessStream stream) { string result = string.Empty; using (DataReader reader = new DataReader(stream.GetInputStreamAt(0))) { reader.UnicodeEncoding = UnicodeEncoding.Utf8; reader.LoadAsync((uint)stream.Size).AsTask().Wait(); uint bytesToRead = reader.UnconsumedBufferLength; if (bytesToRead > 0) { result = reader.ReadString(bytesToRead); } } return result; } /// /// Creates a configuration unit via the configuration statics object. /// /// A new configuration unit. internal ConfigurationUnit ConfigurationUnit() { return this.Fixture.ConfigurationStatics.CreateConfigurationUnit(); } /// /// Creates a configuration parameter via the configuration statics object. /// /// A new configuration parameter. internal ConfigurationParameter ConfigurationParameter() { return this.Fixture.ConfigurationStatics.CreateConfigurationParameter(); } /// /// Creates a configuration set via the configuration statics object. /// /// A new configuration set. internal ConfigurationSet ConfigurationSet() { return this.Fixture.ConfigurationStatics.CreateConfigurationSet(); } /// /// Verifies the summary event generated by a processing run. /// /// The configuration set. /// The set result. /// The result source. internal void VerifySummaryEvent(ConfigurationSet configurationSet, ApplyConfigurationSetResult setResult, ConfigurationUnitResultSource resultSource) { TelemetryEvent summary = this.VerifySummaryEventShared(configurationSet, ConfigurationUnitIntent.Apply, resultSource == ConfigurationUnitResultSource.None ? 0 : setResult.ResultCode.HResult, resultSource); int[] counts = new int[3]; int[] runs = new int[3]; int[] failures = new int[3]; var unitResults = setResult.UnitResults; for (int i = 0; i < unitResults.Count; ++i) { ApplyConfigurationUnitResult unitResult = unitResults[i]; SummaryCountByIntent(counts, runs, failures, unitResult.Unit.Intent, unitResult.ResultInformation); } VerifySummaryCounts(summary, counts, runs, failures); } /// /// Verifies the summary event generated by a processing run. /// /// The configuration set. /// The set result. /// The result code. /// The result source. internal void VerifySummaryEvent(ConfigurationSet configurationSet, TestConfigurationSetResult setResult, int resultCode, ConfigurationUnitResultSource resultSource) { TelemetryEvent summary = this.VerifySummaryEventShared(configurationSet, ConfigurationUnitIntent.Assert, resultCode, resultSource); int[] counts = new int[3]; int[] runs = new int[3]; int[] failures = new int[3]; foreach (TestConfigurationUnitResult unitResult in setResult.UnitResults) { SummaryCountByIntent(counts, runs, failures, unitResult.Unit.Intent, unitResult.ResultInformation); } VerifySummaryCounts(summary, counts, runs, failures); } private static void SummaryCountByIntent(int[] counts, int[] runs, int[] failures, ConfigurationUnitIntent intent, IConfigurationUnitResultInformation resultInformation) { if (intent == ConfigurationUnitIntent.Unknown) { intent = ConfigurationUnitIntent.Apply; } int index = (int)intent; counts[index]++; if (resultInformation.ResultSource != ConfigurationUnitResultSource.ConfigurationSet && resultInformation.ResultSource != ConfigurationUnitResultSource.Precondition) { runs[index]++; } if (resultInformation.ResultCode != null) { failures[index]++; } } private static void VerifySummaryCounts(TelemetryEvent summary, int[] counts, int[] runs, int[] failures) { Assert.Equal(counts[(int)ConfigurationUnitIntent.Assert].ToString(), summary.Properties[TelemetryEvent.AssertCount]); Assert.Equal(runs[(int)ConfigurationUnitIntent.Assert].ToString(), summary.Properties[TelemetryEvent.AssertsRun]); Assert.Equal(failures[(int)ConfigurationUnitIntent.Assert].ToString(), summary.Properties[TelemetryEvent.AssertsFailed]); Assert.Equal(counts[(int)ConfigurationUnitIntent.Inform].ToString(), summary.Properties[TelemetryEvent.InformCount]); Assert.Equal(runs[(int)ConfigurationUnitIntent.Inform].ToString(), summary.Properties[TelemetryEvent.InformsRun]); Assert.Equal(failures[(int)ConfigurationUnitIntent.Inform].ToString(), summary.Properties[TelemetryEvent.InformsFailed]); Assert.Equal(counts[(int)ConfigurationUnitIntent.Apply].ToString(), summary.Properties[TelemetryEvent.ApplyCount]); Assert.Equal(runs[(int)ConfigurationUnitIntent.Apply].ToString(), summary.Properties[TelemetryEvent.AppliesRun]); Assert.Equal(failures[(int)ConfigurationUnitIntent.Apply].ToString(), summary.Properties[TelemetryEvent.AppliesFailed]); } /// /// Verifies the summary event generated by a processing run. /// /// The configuration set. /// The run intent. /// The result code. /// The result source. private TelemetryEvent VerifySummaryEventShared(ConfigurationSet configurationSet, ConfigurationUnitIntent runIntent, int resultCode, ConfigurationUnitResultSource resultSource) { Assert.Single(this.EventSink.Events); TelemetryEvent summary = this.EventSink.Events[0]; Assert.Equal(TelemetryEvent.ConfigProcessingSummaryName, summary.Name); Assert.NotEqual(string.Empty, summary.CodeVersion); Assert.NotEqual(Guid.Empty, summary.ActivityID); Assert.Equal(string.Empty, summary.Caller); Assert.Equal(configurationSet.InstanceIdentifier, Guid.Parse(summary.Properties[TelemetryEvent.SetID])); Assert.False(int.Parse(summary.Properties[TelemetryEvent.FromHistory]) != 0); Assert.Equal(((int)runIntent).ToString(), summary.Properties[TelemetryEvent.RunIntent]); Assert.Equal(resultCode.ToString(), summary.Properties[TelemetryEvent.Result]); Assert.Equal(((int)resultSource).ToString(), summary.Properties[TelemetryEvent.FailurePoint]); return summary; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/Constants.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { /// /// Constants used by the tests. /// public class Constants { /// /// The assembly name value used by xUnit traits. /// public const string AssemblyNameForTraits = "Microsoft.Management.Configuration.UnitTests"; /// /// The namespace where xUnit traits will be defined. /// public const string NamespaceNameForTraits = "Microsoft.Management.Configuration.UnitTests.Helpers"; /// /// The dynamic runtime factory handler identifier. /// public const string DynamicRuntimeHandlerIdentifier = "{73fea39f-6f4a-41c9-ba94-6fd14d633e40}"; /// /// Test guid for enabling test mode for the dynamic runtime factory. Forces factory to exclude 'runas' verb and sets current IL to medium. /// public const string EnableDynamicFactoryTestMode = "1e62d683-2999-44e7-81f7-6f8f35e8d731"; /// /// Test guid for allowing the restricted integrity level to be supported. /// public const string EnableRestrictedIntegrityLevelTestGuid = "5cae3226-185f-4289-815c-3c089d238dc6"; /// /// Test guid for forcing units to have a high integrity level during the final routing of unit processor creation. /// public const string ForceHighIntegrityLevelUnitsTestGuid = "f698d20f-3584-4f28-bc75-28037e08e651"; } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/DiagnosticsEventSink.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Xunit.Abstractions; using Xunit.Sdk; /// /// This class aids in getting diagnostics data from the out to the xUnit infrastructure. /// public class DiagnosticsEventSink { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public DiagnosticsEventSink(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Gets the telemetry events that have been seen. /// public List Events { get; private set; } = new List(); /// /// Handles diagnostic information from a . /// /// The object sending the information. /// The diagnostic information. internal void DiagnosticsHandler(object? sender, IDiagnosticInformation e) { if (e.Message.Contains(TelemetryEvent.Preamble)) { this.Events.Add(new TelemetryEvent(e.Message)); } if (e.Level == DiagnosticLevel.Verbose) { this.fixture.MessageSink.OnMessage(new DiagnosticMessage(e.Message)); } else { this.log.WriteLine(e.Message); } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/Errors.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { /// /// Contains the error codes used by Microsoft.Management.Configuration. /// internal static class Errors { #pragma warning disable SA1310 // Field names should not contain underscore #pragma warning disable SA1600 // Elements should be documented #pragma warning disable SA1025 // Code should not contain multiple whitespace in a row public static readonly int WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE = unchecked((int)0x8A15C001); public static readonly int WINGET_CONFIG_ERROR_INVALID_YAML = unchecked((int)0x8A15C002); public static readonly int WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE = unchecked((int)0x8A15C003); public static readonly int WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION = unchecked((int)0x8A15C004); public static readonly int WINGET_CONFIG_ERROR_SET_APPLY_FAILED = unchecked((int)0x8A15C005); public static readonly int WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER = unchecked((int)0x8A15C006); public static readonly int WINGET_CONFIG_ERROR_MISSING_DEPENDENCY = unchecked((int)0x8A15C007); public static readonly int WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED = unchecked((int)0x8A15C008); public static readonly int WINGET_CONFIG_ERROR_ASSERTION_FAILED = unchecked((int)0x8A15C009); public static readonly int WINGET_CONFIG_ERROR_MANUALLY_SKIPPED = unchecked((int)0x8A15C00A); public static readonly int WINGET_CONFIG_ERROR_WARNING_NOT_ACCEPTED = unchecked((int)0x8A15C00B); public static readonly int WINGET_CONFIG_ERROR_SET_DEPENDENCY_CYCLE = unchecked((int)0x8A15C00C); public static readonly int WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE = unchecked((int)0x8A15C00D); public static readonly int WINGET_CONFIG_ERROR_MISSING_FIELD = unchecked((int)0x8A15C00E); public static readonly int WINGET_CONFIG_ERROR_TEST_FAILED = unchecked((int)0x8A15C00F); public static readonly int WINGET_CONFIG_ERROR_TEST_NOT_RUN = unchecked((int)0x8A15C010); public static readonly int WINGET_CONFIG_ERROR_GET_FAILED = unchecked((int)0x8A15C011); // Configuration Processor Errors public static readonly int WINGET_CONFIG_ERROR_UNIT_NOT_INSTALLED = unchecked((int)0x8A15C101); public static readonly int WINGET_CONFIG_ERROR_UNIT_NOT_FOUND_REPOSITORY = unchecked((int)0x8A15C102); public static readonly int WINGET_CONFIG_ERROR_UNIT_MULTIPLE_MATCHES = unchecked((int)0x8A15C103); public static readonly int WINGET_CONFIG_ERROR_UNIT_INVOKE_GET = unchecked((int)0x8A15C104); public static readonly int WINGET_CONFIG_ERROR_UNIT_INVOKE_TEST = unchecked((int)0x8A15C105); public static readonly int WINGET_CONFIG_ERROR_UNIT_INVOKE_SET = unchecked((int)0x8A15C106); public static readonly int WINGET_CONFIG_ERROR_UNIT_MODULE_CONFLICT = unchecked((int)0x8A15C107); public static readonly int WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE = unchecked((int)0x8A15C108); public static readonly int WINGET_CONFIG_ERROR_UNIT_INVOKE_INVALID_RESULT = unchecked((int)0x8A15C109); public static readonly int WINGET_CONFIG_ERROR_UNIT_SETTING_CONFIG_ROOT = unchecked((int)0x8A15C110); public static readonly int WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE_ADMIN = unchecked((int)0x8A15C111); public static readonly int WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR = unchecked((int)0x8A15C112); public static readonly int WINGET_CONFIG_ERROR_PARAMETER_INTEGRITY_BOUNDARY = unchecked((int)0x8A15C013); // Limitation Set Errors public static readonly int CORE_INVALID_OPERATION = unchecked((int)0x80131509); #pragma warning restore SA1025 // Code should not contain multiple whitespace in a row #pragma warning restore SA1600 // Elements should be documented #pragma warning restore SA1310 // Field names should not contain underscore } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/FactSkipIfCI.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using Xunit; /// /// Skip fact tests if running in CI builds. /// public class FactSkipIfCI : FactAttribute { /// /// Initializes a new instance of the class. /// public FactSkipIfCI() { if (Environment.GetEnvironmentVariable("BUILD_BUILDNUMBER") is not null) { this.Skip = "Skip test for CI builds"; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/GetAllSettingsResultInstance.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; using Microsoft.Management.Configuration; using Windows.Foundation.Collections; /// /// Implements IGetAllSettingsResult. /// internal sealed partial class GetAllSettingsResultInstance : IGetAllSettingsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public GetAllSettingsResultInstance(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public TestConfigurationUnitResultInformation InternalResult { get; } = new TestConfigurationUnitResultInformation(); /// public IList? Settings { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/GetSettingsResultInstance.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using Microsoft.Management.Configuration; using Windows.Foundation.Collections; /// /// Implements IGetSettingsResult. /// internal sealed partial class GetSettingsResultInstance : IGetSettingsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public GetSettingsResultInstance(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public TestConfigurationUnitResultInformation InternalResult { get; } = new TestConfigurationUnitResultInformation(); /// public ValueSet? Settings { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/InProcAttribute.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using Xunit.Sdk; /// /// Trait used to mark a test as only for the in proc scenario. /// [TraitDiscoverer(InProcDiscoverer.TypeName, Constants.AssemblyNameForTraits)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class InProcAttribute : Attribute, ITraitAttribute { /// /// Initializes a new instance of the class. /// public InProcAttribute() { } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/InProcDiscoverer.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; using Xunit.Abstractions; using Xunit.Sdk; /// /// Enables integration with xUnit trait system. /// public class InProcDiscoverer : ITraitDiscoverer { /// /// The type name for this discoverer. /// public const string TypeName = Constants.NamespaceNameForTraits + ".InProcDiscoverer"; /// /// Initializes a new instance of the class. /// public InProcDiscoverer() { } /// /// Gets the trait information for the InProcAttribute. /// /// The trait information. /// Trait name/value pairs. public IEnumerable> GetTraits(IAttributeInfo traitAttribute) { yield return new KeyValuePair("Category", "InProc"); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/OutOfProcAttribute.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using Xunit.Sdk; /// /// Trait used to mark a test as being able to run against the out of proc server. /// [TraitDiscoverer(OutOfProcDiscoverer.TypeName, Constants.AssemblyNameForTraits)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class OutOfProcAttribute : Attribute, ITraitAttribute { /// /// Initializes a new instance of the class. /// public OutOfProcAttribute() { // To run the tests OOP, you need to replace Microsoft.Management.Configuration.dll with Microsoft.Management.Configuration.OutOfProc.dll (renamed to remove the OutOfProc). // You will also need to copy over Microsoft.Management.Configuration.winmd as it is needed by COM. // // You can use the script to do this: // \src\Microsoft.Management.Configuration.OutOfProc\Prepare-ConfigurationOOPTests.ps1 -BuildOutputPath \src\x64\Debug // // It can be easier to run the tests on the command line because any changes needing a recompile will overwrite the DLL update above. // The test runner is located somewhere like this: // C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\Extensions\TestPlatform // and the command line from there is: // .\vstest.console.exe "\src\x64\Debug\Microsoft.Management.Configuration.UnitTests\net8.0-windows10.0.26100.0\Microsoft.Management.Configuration.UnitTests.dll" --TestCaseFilter:Category=OutOfProc } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/OutOfProcDiscoverer.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; using Xunit.Abstractions; using Xunit.Sdk; /// /// Enables integration with xUnit trait system. /// public class OutOfProcDiscoverer : ITraitDiscoverer { /// /// The type name for this discoverer. /// public const string TypeName = Constants.NamespaceNameForTraits + ".OutOfProcDiscoverer"; /// /// Initializes a new instance of the class. /// public OutOfProcDiscoverer() { } /// /// Gets the trait information for the OutOfProcAttribute. /// /// The trait information. /// Trait name/value pairs. public IEnumerable> GetTraits(IAttributeInfo traitAttribute) { yield return new KeyValuePair("Category", "OutOfProc"); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/PowerShellTestsConstants.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { /// /// PowerShell related constants. /// internal static class PowerShellTestsConstants { #pragma warning disable SA1600 // ElementsMustBeDocumented public static class TestModule { public const string SimpleTestResourceModuleName = "xSimpleTestResource"; public const string SimpleFileResourceName = "SimpleFileResource"; public const string SimpleTestResourceName = "SimpleTestResource"; public const string SimpleTestResourceThrowsName = "SimpleTestResourceThrows"; public const string SimpleTestResourceErrorName = "SimpleTestResourceError"; public const string SimpleTestResourceManifestFileName = "xSimpleTestResource.psd1"; public const string SimpleTestResourceVersion = "0.0.0.1"; } #pragma warning restore SA1600 // ElementsMustBeDocumented } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TelemetryEvent.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Collections.Generic; /// /// This class holds the data about a telemetry event detected via the diagnostics side channel. /// public class TelemetryEvent { /// /// The initial indicator that the diagnostics message contains the contents of a telemetry event. /// public const string Preamble = "#DebugEventStream"; /// /// The name of the ConfigUnitRun event. /// public const string ConfigUnitRunName = "ConfigUnitRun"; /// /// The name of the ConfigProcessingSummary event. /// public const string ConfigProcessingSummaryName = "ConfigProcessingSummary"; #pragma warning disable SA1600 // Elements should be documented // Shared fields public const string SetID = "SetID"; public const string RunIntent = "RunIntent"; public const string Result = "Result"; public const string FailurePoint = "FailurePoint"; // ConfigUnitRun fields public const string UnitID = "UnitID"; public const string UnitName = "UnitName"; public const string ModuleName = "ModuleName"; public const string UnitIntent = "UnitIntent"; public const string Action = "Action"; public const string SettingsProvided = "SettingsProvided"; // ConfigProcessingSummary fields public const string FromHistory = "FromHistory"; public const string AssertCount = "AssertCount"; public const string AssertsRun = "AssertsRun"; public const string AssertsFailed = "AssertsFailed"; public const string InformCount = "InformCount"; public const string InformsRun = "InformsRun"; public const string InformsFailed = "InformsFailed"; public const string ApplyCount = "ApplyCount"; public const string AppliesRun = "AppliesRun"; public const string AppliesFailed = "AppliesFailed"; #pragma warning restore SA1600 // Elements should be documented /// /// Initializes a new instance of the class. /// /// The message containing the event data. public TelemetryEvent(string eventMessage) { bool preambleSeen = false; foreach (string line in eventMessage.Split('\n')) { if (line == Preamble) { preambleSeen = true; continue; } if (!preambleSeen) { // Skip all lines until the preamble is seen continue; } int splitIndex = line.IndexOf(": "); if (splitIndex != -1) { this.Properties.Add(line.Substring(0, splitIndex), line.Substring(splitIndex + 2)); } } } /// /// Gets the properties for this event. /// public Dictionary Properties { get; private set; } = new Dictionary(); /// /// Gets the name of the event. /// public string Name { get { return this.Properties["Event"]; } } /// /// Gets the activity id. /// public Guid ActivityID { get { return Guid.Parse(this.Properties["ActivityID"]); } } /// /// Gets the version of the code. /// public string CodeVersion { get { return this.Properties["CodeVersion"]; } } /// /// Gets the caller. /// public string Caller { get { return this.Properties["Caller"]; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TempDirectory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.IO; /// /// Creates a temporary directory in the user's temporary directory. /// internal class TempDirectory : IDisposable { private bool disposed = false; private bool cleanup; /// /// Initializes a new instance of the class. /// /// Optional directory name. If null, creates a random directory name. /// Delete directory if already exists. Default true. /// Deletes directory at disposing time. Default true. public TempDirectory( string? directoryName = null, bool deleteIfExists = true, bool cleanup = true) { var path = Path.GetTempPath(); if (directoryName is null) { this.DirectoryName = Path.GetRandomFileName(); } else { this.DirectoryName = directoryName; } this.FullDirectoryPath = Path.Combine(Path.GetTempPath(), this.DirectoryName); if (deleteIfExists && Directory.Exists(this.FullDirectoryPath)) { Directory.Delete(this.FullDirectoryPath, true); } Directory.CreateDirectory(this.FullDirectoryPath); this.cleanup = cleanup; } /// /// Gets the directory name. /// public string DirectoryName { get; } /// /// Gets the full directory name. /// public string FullDirectoryPath { get; } /// /// IDisposable.Dispose . /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } /// /// Copies all contents of a directory into this directory. /// /// Source directory. public void CopyDirectory(string sourceDir) { this.CopyDirectory(sourceDir, this.FullDirectoryPath); } /// /// Protected disposed. /// /// Disposing. protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (this.cleanup && Directory.Exists(this.FullDirectoryPath)) { Directory.Delete(this.FullDirectoryPath, true); } this.disposed = true; } } private void CopyDirectory(string sourceDir, string destinationDir) { var dir = new DirectoryInfo(sourceDir); if (!dir.Exists) { throw new DirectoryNotFoundException(dir.FullName); } Directory.CreateDirectory(destinationDir); foreach (FileInfo file in dir.GetFiles()) { file.CopyTo(Path.Combine(destinationDir, file.Name)); } foreach (DirectoryInfo subDir in dir.GetDirectories()) { this.CopyDirectory(subDir.FullName, Path.Combine(destinationDir, subDir.Name)); } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TempFile.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.IO; /// /// Creates a temporary file in the user's temporary directory. /// internal class TempFile : IDisposable { private bool disposed = false; private bool cleanup; /// /// Initializes a new instance of the class. /// /// Optional file name. If null, creates a random file name. /// Delete file if already exists. Default true. /// Optional content. If not null or empty, creates file and writes to it. /// Deletes file at disposing time. Default true. public TempFile( string? fileName = null, bool deleteIfExists = true, string? content = null, bool cleanup = true) { if (fileName is null) { this.FileName = Path.GetRandomFileName(); } else { this.FileName = fileName; } this.FullFileName = Path.Combine(Path.GetTempPath(), this.FileName); if (deleteIfExists && File.Exists(this.FullFileName)) { File.Delete(this.FullFileName); } if (!string.IsNullOrWhiteSpace(content)) { this.CreateFile(content); } this.cleanup = cleanup; } /// /// Gets the file name. /// public string FileName { get; } /// /// Gets the full file name. /// public string FullFileName { get; } /// /// IDisposable.Dispose . /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } /// /// Creates the file. /// /// Content. public void CreateFile(string? content = null) { if (content is null) { using var fs = File.Create(this.FullFileName); } else { File.WriteAllText(this.FullFileName, content); } } /// /// Protected disposed. /// /// Disposing. protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (this.cleanup && File.Exists(this.FullFileName)) { File.Delete(this.FullFileName); } this.disposed = true; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Collections.Generic; /// /// A test implementation of IConfigurationSetProcessorFactory. /// internal partial class TestConfigurationProcessorFactory : IConfigurationSetProcessorFactory { /// /// Delegate type for CreateSetProcessor. /// /// The TestConfigurationProcessorFactory that is calling this function. /// The set. /// A new TestConfigurationSetProcessor for the set. internal delegate IConfigurationSetProcessor CreateSetProcessorDelegateType(TestConfigurationProcessorFactory factory, ConfigurationSet configurationSet); /// /// Diagnostics event; useful for logging and/or verbose output. /// #pragma warning disable CS0067 // The event is never used public event EventHandler? Diagnostics; #pragma warning restore CS0067 // The event is never used /// /// Gets or sets the minimum diagnostic level to send. /// public DiagnosticLevel MinimumLevel { get; set; } = DiagnosticLevel.Informational; /// /// Gets or sets the processor used when the incoming configuration set is null. /// internal TestConfigurationSetProcessor? NullProcessor { get; set; } /// /// Gets or sets the processors to be used by this factory. /// internal Dictionary Processors { get; set; } = new Dictionary(); /// /// Gets or sets the exception used when the incoming configuration set is null. /// internal Exception? NullException { get; set; } /// /// Gets or sets the exceptions to be used by this factory. /// internal Dictionary Exceptions { get; set; } = new Dictionary(); /// /// Gets or sets the delegate use to replace the default CreateSetProcessor functionality. /// internal CreateSetProcessorDelegateType? CreateSetProcessorDelegate { get; set; } /// /// Creates a new TestConfigurationSetProcessor for the set. /// /// The set. /// A new TestConfigurationSetProcessor for the set. public IConfigurationSetProcessor CreateSetProcessor(ConfigurationSet configurationSet) { if (this.CreateSetProcessorDelegate != null) { return this.CreateSetProcessorDelegate(this, configurationSet); } return this.DefaultCreateSetProcessor(configurationSet); } /// /// The default test implementation that creates a new TestConfigurationSetProcessor for the set. /// /// The set. /// A new TestConfigurationSetProcessor for the set. internal IConfigurationSetProcessor DefaultCreateSetProcessor(ConfigurationSet configurationSet) { if (configurationSet == null) { if (this.NullException != null) { throw this.NullException; } if (this.NullProcessor == null) { this.NullProcessor = new TestConfigurationSetProcessor(null); } return this.NullProcessor; } if (this.Exceptions.ContainsKey(configurationSet)) { throw this.Exceptions[configurationSet]; } if (!this.Processors.ContainsKey(configurationSet)) { this.Processors.Add(configurationSet, new TestConfigurationSetProcessor(configurationSet)); } return this.Processors[configurationSet]; } /// /// A convenience function to create a new processor for the given set and store it in the dictionary for use in the test. /// /// The set. /// A new TestConfigurationSetProcessor for the set. internal TestConfigurationSetProcessor CreateTestProcessor(ConfigurationSet configurationSet) { this.Processors[configurationSet] = new TestConfigurationSetProcessor(configurationSet); return this.Processors[configurationSet]; } /// /// A convenience function to create a new group processor for the given set and store it in the dictionary for use in the test. /// /// The set. /// A new TestConfigurationSetGroupProcessor for the set. internal TestConfigurationSetGroupProcessor CreateTestGroupProcessor(ConfigurationSet configurationSet) { TestConfigurationSetGroupProcessor result = new (configurationSet); this.Processors[configurationSet] = result; return result; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetGroupProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading; using System.Threading.Tasks; using Windows.Foundation; using Windows.Foundation.Collections; /// /// A test implementation of IConfigurationGroupProcessor. /// internal partial class TestConfigurationSetGroupProcessor : TestConfigurationSetProcessor, IConfigurationGroupProcessor { /// /// The event that is waited on before actually processing the async operations. /// private AutoResetEvent asyncWaitEvent = new AutoResetEvent(false); /// /// Initializes a new instance of the class. /// /// The set that this processor is for. internal TestConfigurationSetGroupProcessor(ConfigurationSet? set) : base(set) { } /// /// Gets the group that this processor targets. /// public object? Group { get { return this.Set; } } /// /// Gets or sets a value indicating whether the async methods should wait on an event before processing. /// internal bool ShouldWaitOnAsyncEvent { get; set; } = false; /// /// Apply settings for the group. /// /// The progress handler. /// The operation to apply settings. public IAsyncOperation ApplyGroupSettingsAsync(EventHandler progressHandler) { return AsyncInfo.Run((CancellationToken cancellationToken) => Task.Run(() => { this.WaitOnAsyncEvent(cancellationToken); ApplyGroupSettingsResultInstance result = new (this.Group); result.UnitResults = new List(); if (this.Set != null) { TestConfigurationUnitGroupProcessor.ApplyGroupSettings(this.Set.Units, progressHandler, result); } return result; })); } /// /// Test settings for the group. /// /// The progress handler. /// The operation to test settings. public IAsyncOperation TestGroupSettingsAsync(EventHandler progressHandler) { return AsyncInfo.Run((CancellationToken cancellationToken) => Task.Run(() => { this.WaitOnAsyncEvent(cancellationToken); TestGroupSettingsResultInstance result = new (this.Group); result.UnitResults = new List(); if (this.Set != null) { result.TestResult = TestConfigurationUnitGroupProcessor.GetTestResult(this.Set.Metadata); TestConfigurationUnitGroupProcessor.TestGroupSettings(this.Set.Units, progressHandler, result); } return result; })); } /// /// Signals the async event. /// internal void SignalAsyncEvent() { this.asyncWaitEvent.Set(); } /// /// Waits on the async event. /// private void WaitOnAsyncEvent(CancellationToken cancellationToken) { if (this.ShouldWaitOnAsyncEvent) { cancellationToken.Register(() => this.asyncWaitEvent.Set()); if (!this.asyncWaitEvent.WaitOne(10000)) { throw new TimeoutException(); } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Collections.Generic; /// /// A test implementation of IConfigurationSetProcessor. /// internal partial class TestConfigurationSetProcessor : IConfigurationSetProcessor { /// /// Initializes a new instance of the class. /// /// The set that this processor is for. internal TestConfigurationSetProcessor(ConfigurationSet? set) { this.Set = set; } /// /// Gets or sets the processors to be used by this factory. /// internal Dictionary Processors { get; set; } = new Dictionary(); /// /// Gets or sets the details to be used by this factory. /// internal Dictionary Details { get; set; } = new Dictionary(); /// /// Gets or sets the exceptions to be used by this factory. /// internal Dictionary Exceptions { get; set; } = new Dictionary(); /// /// Gets or sets a value indicating whether the default unit processors for groups will enable group processing. /// internal bool EnableDefaultGroupProcessorCreation { get; set; } = false; /// /// Gets the ConfigurationSet that this processor targets. /// protected ConfigurationSet? Set { get; private set; } /// /// Creates a new unit processor for the given unit. /// /// The unit. /// The configuration unit processor. public IConfigurationUnitProcessor CreateUnitProcessor(ConfigurationUnit unit) { if (this.Exceptions.ContainsKey(unit)) { throw this.Exceptions[unit]; } if (!this.Processors.ContainsKey(unit)) { if (this.EnableDefaultGroupProcessorCreation && unit.IsGroup) { this.Processors.Add(unit, new TestConfigurationUnitGroupProcessor(unit)); } else { this.Processors.Add(unit, new TestConfigurationUnitProcessor(unit)); } } return this.Processors[unit]; } /// /// Gets the unit processor details for the given unit. /// /// The unit. /// The detail flags. /// The details requested. public IConfigurationUnitProcessorDetails GetUnitProcessorDetails(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags) { if (this.Exceptions.ContainsKey(unit)) { throw this.Exceptions[unit]; } if (!this.Details.ContainsKey(unit)) { this.Details.Add(unit, new TestConfigurationUnitProcessorDetails(unit, detailFlags)); } return this.Details[unit]; } /// /// Creates a new test processor for the given unit. /// /// The unit. /// The configuration unit processor. internal TestConfigurationUnitProcessor CreateTestProcessor(ConfigurationUnit unit) { this.Processors[unit] = new TestConfigurationUnitProcessor(unit); return this.Processors[unit]; } /// /// Creates a new test group processor for the given unit. /// /// The unit. /// A new TestConfigurationUnitGroupProcessor for the unit. internal TestConfigurationUnitGroupProcessor CreateTestGroupProcessor(ConfigurationUnit unit) { TestConfigurationUnitGroupProcessor result = new (unit); this.Processors[unit] = result; return result; } /// /// Creates a new test processor that supports GetAllSettings for the given unit. /// /// The unit. /// The configuration unit processor. internal TestGetAllSettingsConfigurationUnitProcessor CreateGetAllSettingsTestProcessor(ConfigurationUnit unit) { var processor = new TestGetAllSettingsConfigurationUnitProcessor(unit); this.Processors[unit] = processor; return processor; } /// /// Creates a new unit processor details for the given unit. /// /// The unit. /// The detail flags. /// The details requested. internal TestConfigurationUnitProcessorDetails CreateUnitDetails(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags) { this.Details[unit] = new TestConfigurationUnitProcessorDetails(unit, detailFlags); return this.Details[unit]; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitGroupProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading; using System.Threading.Tasks; using Windows.Foundation; using Windows.Foundation.Collections; /// /// A test implementation of IConfigurationGroupProcessor. /// internal partial class TestConfigurationUnitGroupProcessor : TestConfigurationUnitProcessor, IConfigurationGroupProcessor { /// /// The Setting key that will be used to set the TestResult of the unit. /// internal const string TestResultSetting = "TestResult"; /// /// The event that is waited on before actually processing the async operations. /// private AutoResetEvent asyncWaitEvent = new AutoResetEvent(false); /// /// Initializes a new instance of the class. /// /// The unit that this processor is for. internal TestConfigurationUnitGroupProcessor(ConfigurationUnit unit) : base(unit) { } /// /// Gets the group that this processor targets. /// public object Group { get { return this.Unit; } } /// /// Gets or sets a value indicating whether the async methods should wait on an event before processing. /// internal bool ShouldWaitOnAsyncEvent { get; set; } = false; /// /// Apply settings for the group. /// /// The progress handler. /// The operation to apply settings. public IAsyncOperation ApplyGroupSettingsAsync(EventHandler progressHandler) { return AsyncInfo.Run((CancellationToken cancellationToken) => Task.Run(() => { this.WaitOnAsyncEvent(cancellationToken); ApplyGroupSettingsResultInstance result = new (this.Group); result.UnitResults = new List(); ApplyGroupSettings(this.Unit.Units, progressHandler, result); return result; })); } /// /// Test settings for the group. /// /// The progress handler. /// The operation to test settings. public IAsyncOperation TestGroupSettingsAsync(EventHandler progressHandler) { return AsyncInfo.Run((CancellationToken cancellationToken) => Task.Run(() => { this.WaitOnAsyncEvent(cancellationToken); TestGroupSettingsResultInstance result = new (this.Group); result.UnitResults = new List(); result.TestResult = GetTestResult(this.Unit.Metadata); TestGroupSettings(this.Unit.Units, progressHandler, result); return result; })); } /// /// Gets the tests result for the given unit. /// /// The unit. /// The test result for the unit. internal static ConfigurationTestResult GetTestResult(ConfigurationUnit unit) { return GetTestResult(unit.Settings); } /// /// Gets the tests result for the given values. /// /// The values. /// The test result for the values. internal static ConfigurationTestResult GetTestResult(ValueSet values) { if (values.ContainsKey(TestResultSetting)) { string? valueString = values[TestResultSetting]?.ToString(); if (valueString != null) { return Enum.Parse(valueString); } } return ConfigurationTestResult.Positive; } /// /// Applies group settings for the given group members. /// /// The group members. /// The progress reporting object. /// The result object. internal static void ApplyGroupSettings(IList? groupMembers, EventHandler progress, ApplyGroupSettingsResultInstance result) { if (groupMembers != null) { foreach (ConfigurationUnit unit in groupMembers) { ApplyGroupMemberSettingsResultInstance unitResult = new (unit); result.UnitResults!.Add(unitResult); unitResult.State = ConfigurationUnitState.InProgress; progress.Invoke(null, unitResult); unitResult.PreviouslyInDesiredState = GetTestResult(unit) == ConfigurationTestResult.Positive; if (unit.IsGroup) { ApplyGroupSettings(unit.Units, progress, result); } unitResult.State = ConfigurationUnitState.Completed; progress.Invoke(null, unitResult); } } } /// /// Tests group settings for the given group members. /// /// The group members. /// The progress reporting object. /// The result object. internal static void TestGroupSettings(IList? groupMembers, EventHandler progress, TestGroupSettingsResultInstance result) { if (groupMembers != null) { foreach (ConfigurationUnit unit in groupMembers) { TestSettingsResultInstance unitResult = new (unit); if (unit.IsGroup) { TestGroupSettings(unit.Units, progress, result); } unitResult.TestResult = GetTestResult(unit); result.UnitResults!.Add(unitResult); progress.Invoke(null, unitResult); } } } /// /// Signals the async event. /// internal void SignalAsyncEvent() { this.asyncWaitEvent.Set(); } /// /// Waits on the async event. /// private void WaitOnAsyncEvent(CancellationToken cancellationToken) { if (this.ShouldWaitOnAsyncEvent) { cancellationToken.Register(() => this.asyncWaitEvent.Set()); if (!this.asyncWaitEvent.WaitOne(10000)) { throw new TimeoutException(); } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; /// /// A test implementation of IConfigurationProcessorFactory. /// internal partial class TestConfigurationUnitProcessor : IConfigurationUnitProcessor { /// /// Initializes a new instance of the class. /// /// The unit. internal TestConfigurationUnitProcessor(ConfigurationUnit unit) { this.Unit = unit; } /// /// The delegate for ApplySettings. /// /// The result. internal delegate IApplySettingsResult ApplySettingsDelegateType(); /// /// The delegate for GetSettings. /// /// The result. internal delegate IGetSettingsResult GetSettingsDelegateType(); /// /// The delegate for TestSettings. /// /// The result. internal delegate ITestSettingsResult TestSettingsDelegateType(); /// /// The delegate for TestSettings that passes the unit in. /// /// The unit. /// The result. internal delegate ITestSettingsResult TestSettingsDelegateWithUnitType(ConfigurationUnit unit); /// /// Gets or sets the directives overlay. /// public IReadOnlyDictionary? DirectivesOverlay { get; set; } /// /// Gets the configuration unit. /// public ConfigurationUnit Unit { get; private set; } /// /// Gets or sets the delegate object for ApplySettings. /// internal ApplySettingsDelegateType? ApplySettingsDelegate { get; set; } /// /// Gets the number of times ApplySettings is called. /// internal int ApplySettingsCalls { get; private set; } = 0; /// /// Gets or sets the delegate object for GetSettings. /// internal GetSettingsDelegateType? GetSettingsDelegate { get; set; } /// /// Gets the number of times GetSettings is called. /// internal int GetSettingsCalls { get; private set; } = 0; /// /// Gets or sets the delegate object for TestSettings. /// internal TestSettingsDelegateType? TestSettingsDelegate { get; set; } /// /// Gets or sets the delegate object for TestSettings that takes in the unit. /// internal TestSettingsDelegateWithUnitType? TestSettingsDelegateWithUnit { get; set; } /// /// Gets the number of times TestSettings is called. /// internal int TestSettingsCalls { get; private set; } = 0; /// /// Calls the ApplySettingsDelegate if one is provided; returns success if not. /// /// The result. public IApplySettingsResult ApplySettings() { ++this.ApplySettingsCalls; if (this.ApplySettingsDelegate != null) { return this.ApplySettingsDelegate(); } else { return new ApplySettingsResultInstance(this.Unit); } } /// /// Calls the GetSettingsDelegate if one is provided; returns success if not (with no settings values). /// /// The result. public IGetSettingsResult GetSettings() { ++this.GetSettingsCalls; if (this.GetSettingsDelegate != null) { return this.GetSettingsDelegate(); } else { return new GetSettingsResultInstance(this.Unit); } } /// /// Calls the TestSettingsDelegate if one is provided; returns success if not (with a positive test result). /// /// The result. public ITestSettingsResult TestSettings() { ++this.TestSettingsCalls; if (this.TestSettingsDelegateWithUnit != null) { return this.TestSettingsDelegateWithUnit(this.Unit); } else if (this.TestSettingsDelegate != null) { return this.TestSettingsDelegate(); } else { return new TestSettingsResultInstance(this.Unit) { TestResult = ConfigurationTestResult.Positive }; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessorDetails.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Collections.Generic; using Windows.Security.Cryptography.Certificates; /// /// A test implementation of IConfigurationProcessorFactory. /// internal partial class TestConfigurationUnitProcessorDetails : IConfigurationUnitProcessorDetails { private ConfigurationUnit unit; private ConfigurationUnitDetailFlags detailFlags; /// /// Initializes a new instance of the class. /// /// The unit. /// The flags of the details. internal TestConfigurationUnitProcessorDetails(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags) { this.unit = unit; this.detailFlags = detailFlags; } #pragma warning disable SA1600 // Elements should be documented public string? Author { get; internal set; } public bool IsLocal { get; internal set; } public string? ModuleDescription { get; internal set; } public Uri? ModuleDocumentationUri { get; internal set; } public string? ModuleName { get; internal set; } public string? ModuleSource { get; internal set; } public string? ModuleType { get; internal set; } public DateTimeOffset PublishedDate { get; internal set; } public Uri? PublishedModuleUri { get; internal set; } public string? Publisher { get; internal set; } public IReadOnlyList? Settings { get; internal set; } public IReadOnlyList? SigningInformation { get; internal set; } public string? UnitDescription { get; internal set; } public Uri? UnitDocumentationUri { get; internal set; } public Uri? UnitIconUri { get; internal set; } public string? UnitType { get; internal set; } public string? Version { get; internal set; } public bool IsPublic { get; internal set; } #pragma warning restore SA1600 // Elements should be documented } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitResultInformation.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Collections.Generic; /// /// A test implementation of IConfigurationSetProcessorFactory. /// internal partial class TestConfigurationUnitResultInformation : IConfigurationUnitResultInformation { /// /// Gets or sets the description. /// public string Description { get; set; } = string.Empty; /// /// Gets or sets the details. /// public string Details { get; set; } = string.Empty; /// /// Gets or sets the result code. /// public Exception? ResultCode { get; set; } /// /// Gets or sets the result source. /// public ConfigurationUnitResultSource ResultSource { get; set; } = ConfigurationUnitResultSource.None; } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestDSCv3.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; using Microsoft.Management.Configuration.Processor.DSCv3.Helpers; using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Microsoft.Management.Configuration.Processor.Helpers; /// /// Implements IDSCv3 for tests. /// internal class TestDSCv3 : IDSCv3 { /// /// The delegate type for GetResourceByType. /// /// The type name of the resource. /// A single resource item. internal delegate IResourceListItem? GetResourceByTypeDelegateType(string resourceType); /// /// The delegate type for GetResourceSettings. /// /// The unit to get. /// A get result. internal delegate IResourceGetItem GetResourceSettingsDelegateType(ConfigurationUnitInternal unitInternal); /// /// The delegate type for SetResourceSettings. /// /// The unit to set. /// A set result. internal delegate IResourceSetItem SetResourceSettingsDelegateType(ConfigurationUnitInternal unitInternal); /// /// The delegate type for TestResource. /// /// The unit to test. /// A test result. internal delegate IResourceTestItem TestResourceDelegateType(ConfigurationUnitInternal unitInternal); /// /// The delegate type for TestResource. /// /// The unit to test. /// A test result. internal delegate IList ExportResourceDelegateType(ConfigurationUnitInternal unitInternal); /// /// Gets or sets the GetResourceByType result. /// public IResourceListItem? GetResourceByTypeResult { get; set; } /// /// Gets or sets the GetResourceByType delegate. /// public GetResourceByTypeDelegateType? GetResourceByTypeDelegate { get; set; } /// /// Gets or sets the GetResourceSettings result. /// public IResourceGetItem? GetResourceSettingsResult { get; set; } /// /// Gets or sets the GetResourceSettings delegate. /// public GetResourceSettingsDelegateType? GetResourceSettingsDelegate { get; set; } /// /// Gets or sets the SetResourceSettings result. /// public IResourceSetItem? SetResourceSettingsResult { get; set; } /// /// Gets or sets the SetResourceSettings delegate. /// public SetResourceSettingsDelegateType? SetResourceSettingsDelegate { get; set; } /// /// Gets or sets the TestResource result. /// public IResourceTestItem? TestResourceResult { get; set; } /// /// Gets or sets the TestResource delegate. /// public TestResourceDelegateType? TestResourceDelegate { get; set; } /// /// Gets or sets the ExportResource result. /// public IList? ExportResourceResult { get; set; } /// /// Gets or sets the ExportResource delegate. /// public ExportResourceDelegateType? ExportResourceDelegate { get; set; } /// /// Gets or sets the GetAllResources result. /// public List? GetAllResourcesResult { get; set; } /// public IResourceListItem? GetResourceByType(string resourceType, ProcessorRunSettings? runSettings) { return this.GetResourceByTypeResult ?? this.GetResourceByTypeDelegate?.Invoke(resourceType); } /// public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { return this.GetResourceSettingsResult ?? this.GetResourceSettingsDelegate?.Invoke(unitInternal) ?? throw new System.NotImplementedException(); } /// public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { return this.SetResourceSettingsResult ?? this.SetResourceSettingsDelegate?.Invoke(unitInternal) ?? throw new System.NotImplementedException(); } /// public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { return this.TestResourceResult ?? this.TestResourceDelegate?.Invoke(unitInternal) ?? throw new System.NotImplementedException(); } /// public IList ExportResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { return this.ExportResourceResult ?? this.ExportResourceDelegate?.Invoke(unitInternal) ?? throw new System.NotImplementedException(); } /// public List GetAllResources(ProcessorRunSettings? runSettings) { return this.GetAllResourcesResult ?? throw new System.NotImplementedException(); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGetAllSettingsConfigurationUnitProcessor.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { /// /// A test implementation of IConfigurationProcessorFactory. /// internal partial class TestGetAllSettingsConfigurationUnitProcessor : TestConfigurationUnitProcessor, IGetAllSettingsConfigurationUnitProcessor { /// /// Initializes a new instance of the class. /// /// The unit. internal TestGetAllSettingsConfigurationUnitProcessor(ConfigurationUnit unit) : base(unit) { } /// /// The delegate for GetAllSettings. /// /// The result. internal delegate IGetAllSettingsResult GetAllSettingsDelegateType(); /// /// Gets or sets the delegate object for GetAllSettings. /// internal GetAllSettingsDelegateType? GetAllSettingsDelegate { get; set; } /// /// Gets the number of times GetAllSettings is called. /// internal int GetAllSettingsCalls { get; private set; } = 0; /// /// Calls the GetAllSettingsDelegate if one is provided; returns success if not (with no settings values). /// /// The result. public IGetAllSettingsResult GetAllSettings() { ++this.GetAllSettingsCalls; if (this.GetAllSettingsDelegate != null) { return this.GetAllSettingsDelegate(); } else { return new GetAllSettingsResultInstance(this.Unit); } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestGroupSettingsResultInstance.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; /// /// Implements ITestGroupSettingsResult. /// internal partial class TestGroupSettingsResultInstance : ITestGroupSettingsResult { /// /// Initializes a new instance of the class. /// /// The group for this result. internal TestGroupSettingsResultInstance(object? group) { this.Group = group; } /// public object? Group { get; private init; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public TestConfigurationUnitResultInformation InternalResult { get; } = new TestConfigurationUnitResultInformation(); /// public ConfigurationTestResult TestResult { get; internal set; } /// public IList? UnitResults { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestResourceExportItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System.Collections.Generic; using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Windows.Foundation.Collections; /// /// Implements IResourceExportItem for tests. /// internal class TestResourceExportItem : IResourceExportItem { /// /// Gets or sets the type. /// required public string Type { get; set; } /// /// Gets or sets the name. /// required public string Name { get; set; } /// /// Gets or sets the settings. /// public ValueSet Settings { get; set; } = new ValueSet(); /// /// Gets or sets the metadata. /// public ValueSet Metadata { get; set; } = new ValueSet(); /// /// Gets or sets the dependencies. /// public IList Dependencies { get; set; } = new List(); } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestResourceGetItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Windows.Foundation.Collections; /// /// Implements IResourceGetItem for tests. /// internal class TestResourceGetItem : IResourceGetItem { /// /// Gets or sets the settings. /// public ValueSet Settings { get; set; } = new ValueSet(); } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestResourceListItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using Microsoft.Management.Configuration.Processor.DSCv3.Model; /// /// Implements IResourceListItem for tests. /// internal class TestResourceListItem : IResourceListItem { /// /// Gets or sets the type. /// required public string Type { get; set; } /// /// Gets or sets the kind. /// public ResourceKind Kind { get; set; } /// /// Gets or sets the version. /// public string? Version { get; set; } /// /// Gets or sets the description. /// public string? Description { get; set; } /// /// Gets or sets the directory. /// public string? Directory { get; set; } /// /// Gets or sets the author. /// public string? Author { get; set; } /// /// Gets or sets the path. /// public string? Path { get; set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestResourceSetItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using Microsoft.Management.Configuration.Processor.DSCv3.Model; /// /// Implements IResourceSetItem for tests. /// internal class TestResourceSetItem : IResourceSetItem { /// /// Gets or sets a value indicating whether a reboot is required. /// public bool RebootRequired { get; set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestResourceTestItem.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using Microsoft.Management.Configuration.Processor.DSCv3.Model; /// /// Implements IResourceTestItem for tests. /// internal class TestResourceTestItem : IResourceTestItem { /// /// Gets or sets a value indicating whether the system is in the desired state. /// public bool InDesiredState { get; set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TestSettingsResultInstance.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using Microsoft.Management.Configuration; /// /// Implements ITestSettingsResult. /// internal sealed partial class TestSettingsResultInstance : ITestSettingsResult { /// /// Initializes a new instance of the class. /// /// The configuration unit that the result is for. public TestSettingsResultInstance(ConfigurationUnit unit) { this.Unit = unit; } /// /// Gets the configuration unit that the result is for. /// public ConfigurationUnit Unit { get; private set; } /// public IConfigurationUnitResultInformation ResultInformation { get { return this.InternalResult; } } /// /// Gets the implementation object for ResultInformation. /// public TestConfigurationUnitResultInformation InternalResult { get; } = new TestConfigurationUnitResultInformation(); /// public ConfigurationTestResult TestResult { get; internal set; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/TheorySkipIfCI.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using Xunit; /// /// Skip theory test if running in CI builds. /// public sealed class TheorySkipIfCI : TheoryAttribute { /// /// Initializes a new instance of the class. /// public TheorySkipIfCI() { if (Environment.GetEnvironmentVariable("BUILD_BUILDNUMBER") is not null) { this.Skip = "Skip test for CI builds"; } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Helpers/ValueSetExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Helpers { using System; using System.Text; using Windows.Foundation.Collections; /// /// Extensions for ValueSet. /// internal static class ValueSetExtensions { /// /// Converts the value set to YAML like output. /// /// The set to output. /// The string. public static string ToYaml(this ValueSet set) { StringBuilder sb = new StringBuilder(); ToYaml(set, sb); return sb.ToString(); } private static void ToYaml(ValueSet set, StringBuilder sb, int indentation = 0) { foreach (var keyValuePair in set) { bool addLine = true; sb.Append(' ', indentation); sb.Append(keyValuePair.Key); sb.Append(": "); if (keyValuePair.Value == null) { sb.Append("null"); } else { switch (keyValuePair.Value) { case int i: sb.Append(i); break; case long l: sb.Append(l); break; case string s: sb.Append(s); break; case bool b: sb.Append(b); break; case ValueSet v: sb.AppendLine(); ToYaml(v, sb, indentation + 2); addLine = false; break; default: throw new NotImplementedException($"Add ToYaml type `{keyValuePair.Value.GetType().Name}`"); } } if (addLine) { sb.AppendLine(); } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Microsoft.Management.Configuration.UnitTests.csproj ================================================ net8.0-windows10.0.26100.0 enable 10.0.17763.0 x64;x86;arm64 $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ win-x64;win-x86;win-arm64 runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive all all runtime; build; native; contentfiles; analyzers; buildtransitive True True PreserveNewest True PreserveNewest PreserveNewest PreserveNewest PreserveNewest ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/TestCollateral/PowerShellModules/xAdminTestResource/xAdminTestResource.psd1 ================================================ # # Module manifest for module 'xAdminTestResource' # # Generated by: Luffytaro # # Generated on: 10/11/2023 # @{ RootModule = 'xAdminTestResource.psm1' ModuleVersion = '0.0.0.1' GUID = 'a0be43e8-ac22-4244-8efc-7263dfa58b8c' CompatiblePSEditions = 'Core' Author = 'Luffytaro' CompanyName = 'Microsoft Corporation' Copyright = '(c) Microsoft Corporation. All rights reserved.' Description = 'PowerShell module with DSC resources for unit tests that requires admin' PowerShellVersion = '7.2' FunctionsToExport = @() CmdletsToExport = @() DscResourcesToExport = @( 'AdminResource' ) HelpInfoURI = 'https://www.contoso.com/help' # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ ProjectUri = 'https://github.com/microsoft/winget-cli' IconUri = 'https://www.contoso.com/icons/icon.png' } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/TestCollateral/PowerShellModules/xAdminTestResource/xAdminTestResource.psm1 ================================================ # Simple module that requires admin. #Requires -RunAsAdministrator enum Ensure { Absent Present } [DscResource()] class AdminResource { [DscProperty(Key)] [string] $key [AdminResource] Get() { return $this } [bool] Test() { return $false } [void] Set() { } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/TestCollateral/PowerShellModules/xSimpleTestResource/xSimpleTestResource.psd1 ================================================ # # Module manifest for module 'xSimpleTestResource' # # Generated by: Luffytaro # # Generated on: 1/24/2023 # @{ RootModule = 'xSimpleTestResource.psm1' ModuleVersion = '0.0.0.1' GUID = 'a0be43e8-ac22-4244-8efc-7263dfa50b8c' CompatiblePSEditions = 'Core' Author = 'Luffytaro' CompanyName = 'Microsoft Corporation' Copyright = '(c) Microsoft Corporation. All rights reserved.' Description = 'PowerShell module with DSC resources for unit tests' PowerShellVersion = '7.2' FunctionsToExport = @() CmdletsToExport = @() DscResourcesToExport = @( 'SimpleFileResource' 'SimpleTestResource' 'SimpleTestResourceThrows' 'SimpleTestResourceError' 'SimpleTestResourceTypes' ) HelpInfoURI = 'https://www.contoso.com/help' # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ ProjectUri = 'https://github.com/microsoft/winget-cli' IconUri = 'https://www.contoso.com/icons/icon.png' } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/TestCollateral/PowerShellModules/xSimpleTestResource/xSimpleTestResource.psm1 ================================================ # Simple module with resources. enum Ensure { Absent Present } # This resource just checks if a file is there or not with and if its with the specified content. [DscResource()] class SimpleFileResource { [DscProperty(Key)] [string] $Path [DscProperty()] [Ensure] $Ensure = [Ensure]::Present [DscProperty()] [string] $Content = $null [SimpleFileResource] Get() { if ([string]::IsNullOrEmpty($this.Path)) { throw } $fileContent = $null if (Test-Path -Path $this.Path -PathType Leaf) { $fileContent = Get-Content $this.Path -Raw } $result = @{ Path = $this.Path Content = $fileContent } return $result } [bool] Test() { $get = $this.Get() if (Test-Path -Path $this.Path -PathType Leaf) { if ($this.Ensure -eq [Ensure]::Present) { return $this.Content -eq $get.Content } } elseif ($this.Ensure -eq [Ensure]::Absent) { return $true } return $false } [void] Set() { if (-not $this.Test()) { if (Test-Path -Path $this.Path -PathType Leaf) { if ($this.Ensure -eq [Ensure]::Present) { Set-Content $this.Path $this.Content -NoNewline } else { Remove-Item $this.Path } } else { if ($this.Ensure -eq [Ensure]::Present) { Set-Content $this.Path $this.Content -NoNewline } } } } } [DscResource()] class SimpleTestResource { [DscProperty(Key)] [string] $key [DscProperty(Mandatory)] [string] $secretCode [SimpleTestResource] Get() { $result = @{ key = "SimpleTestResourceKey" } return $result } [bool] Test() { return $this.secretCode -eq "4815162342" } [void] Set() { if (-not $this.Test()) { $global:DSCMachineStatus = 1 } } } [DscResource()] class SimpleTestResourceThrows { [DscProperty(Key)] [string] $key [SimpleTestResourceThrows] Get() { $result = @{ key = "SimpleTestResourceThrowsKey" } throw "throws in Get" return $result } [bool] Test() { throw "throws in Test" return $false } [void] Set() { throw "throws in Set" } } [DscResource()] class SimpleTestResourceError { [DscProperty(Key)] [string] $key [SimpleTestResourceError] Get() { $result = @{ key = "SimpleTestResourceErrorKey" } Write-Error "Error in Get" return $result } [bool] Test() { Write-Error "Error in Test" return $true } [void] Set() { Write-Error "Error in Set" } } [DscResource()] class SimpleTestResourceTypes { [DscProperty(Key)] [string] $key [DscProperty()] [boolean] $boolProperty [DscProperty()] [int] $intProperty; [DscProperty()] [double] $doubleProperty; [DscProperty()] [char] $charProperty; [DscProperty()] [Hashtable] $hashtableProperty; [SimpleTestResourceTypes] Get() { $result = @{ key = "SimpleTestResourceTypesKey" boolProperty = $false intProperty = 0 doubleProperty = 0.0 charProperty = 'z' hashtableProperty = @{} } return $result } [bool] Test() { # Because we can't get the error stream from a class based resource, I throw so is easier to know if # there's something wrong. if ($this.boolProperty -ne $true) { throw "Failed boolProperty" } if ($this.intProperty -ne 3) { throw "Failed intProperty. Got $($this.intProperty)" } if ($this.doubleProperty -ne -9.876) { throw "Failed doubleProperty Got $($this.doubleProperty)" } if ($this.charProperty -ne 'f') { throw "Failed charProperty Got $($this.charProperty)" } if ($this.hashtableProperty.ContainsKey("secretStringKey")) { if ($this.hashtableProperty["secretStringKey"] -ne "secretCode") { throw "Failed comparing value of `$hashtableProperty.secretStringKey Got $($this.hashtableProperty["secretStringKey"])" } } else { throw "Failed finding secretStringKey in hashtableProperty" } if ($this.hashtableProperty.ContainsKey("secretIntKey")) { if ($this.hashtableProperty["secretIntKey"] -ne 123456) { throw "Failed comparing value of `$hashtableProperty.secretIntKey Got $($this.hashtableProperty["secretIntKey"])" } } else { throw "Failed finding secretIntKey in hashtableProperty" } return $true } [void] Set() { # no-op } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationDetailsTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.Processor.Unit; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Windows.Security.Cryptography.Certificates; using Xunit; using Xunit.Abstractions; /// /// Tests for ConfigurationUnitProcessorDetails and ConfigurationUnitSettingDetails. /// [Collection("UnitTestCollection")] [InProc] public class ConfigurationDetailsTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Fixture. /// log. public ConfigurationDetailsTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Tests creating ConfigurationUnitProcessorDetails and ConfigurationUnitSettingDetails with different inputs. /// /// Has dsc info. /// Has ps module info. /// Has get module info. /// Has certs. [Theory] [InlineData(false, false, false, false)] [InlineData(false, false, false, true)] [InlineData(false, false, true, false)] [InlineData(false, false, true, true)] [InlineData(false, true, false, false)] [InlineData(false, true, false, true)] [InlineData(false, true, true, false)] [InlineData(false, true, true, true)] [InlineData(true, false, false, false)] [InlineData(true, false, false, true)] [InlineData(true, false, true, false)] [InlineData(true, false, true, true)] [InlineData(true, true, false, false)] [InlineData(true, true, false, true)] [InlineData(true, true, true, false)] [InlineData(true, true, true, true)] public void ConfigurationUnitProcessorDetails_CreationTest(bool hasDscInfo, bool hasPSModuleInfo, bool hasGetModuleInfo, bool hasCerts) { List? certsInput = null; if (hasCerts) { certsInput = new List(); } if (!hasDscInfo && !hasPSModuleInfo && !hasGetModuleInfo) { Assert.Throws( () => Factory.CreateUnitProcessorDetails("unitName", null, null, null, certsInput)); } else { var unit = this.CreateConfigurationUnit(); var (dscResourceInfo, psModuleInfo) = this.GetResourceAndModuleInfo(unit); DscResourceInfoInternal? dscResourceInfoInput = null; if (hasDscInfo) { dscResourceInfoInput = dscResourceInfo; } PSModuleInfo? psModuleInfoInput = null; if (hasPSModuleInfo) { psModuleInfoInput = psModuleInfo; } PSObject? getModuleInfo = null; if (hasGetModuleInfo) { getModuleInfo = this.CreateGetModuleInfo(); } var details = Factory.CreateUnitProcessorDetails(unit.Type, dscResourceInfoInput, psModuleInfoInput, getModuleInfo, certsInput); Assert.Equal(unit.Type, details.UnitType); if (hasDscInfo) { Assert.True(details.IsLocal); Assert.Equal("xSimpleTestResource", details.ModuleName); Assert.Equal("0.0.0.1", details.Version); Assert.NotNull(details.Settings); Assert.True(details.Settings.Count == 5); var pathSetting = details.Settings.Where(s => s.Identifier == "Path").FirstOrDefault(); Assert.NotNull(pathSetting); Assert.Equal(Windows.Foundation.PropertyType.String, pathSetting.Type); Assert.True(pathSetting.IsRequired); Assert.Equal(string.Empty, pathSetting.Schema); var contentSetting = details.Settings.Where(s => s.Identifier == "Content").FirstOrDefault(); Assert.NotNull(contentSetting); Assert.Equal(Windows.Foundation.PropertyType.String, contentSetting.Type); Assert.False(contentSetting.IsRequired); Assert.Equal(string.Empty, contentSetting.Schema); var dependsOnSetting = details.Settings.Where(s => s.Identifier == "DependsOn").FirstOrDefault(); Assert.NotNull(dependsOnSetting); Assert.Equal(Windows.Foundation.PropertyType.StringArray, dependsOnSetting.Type); Assert.False(dependsOnSetting.IsRequired); Assert.Equal(string.Empty, dependsOnSetting.Schema); var ensureSetting = details.Settings.Where(s => s.Identifier == "Ensure").FirstOrDefault(); Assert.NotNull(ensureSetting); Assert.Equal(Windows.Foundation.PropertyType.String, ensureSetting.Type); Assert.False(ensureSetting.IsRequired); Assert.Equal(string.Empty, ensureSetting.Schema); var psDscRunAsCredentialSetting = details.Settings.Where(s => s.Identifier == "PsDscRunAsCredential").FirstOrDefault(); Assert.NotNull(psDscRunAsCredentialSetting); Assert.Equal(Windows.Foundation.PropertyType.Inspectable, psDscRunAsCredentialSetting.Type); Assert.False(psDscRunAsCredentialSetting.IsRequired); Assert.Equal(string.Empty, psDscRunAsCredentialSetting.Schema); } if (hasPSModuleInfo) { Assert.Equal(new Uri("https://www.contoso.com/help"), details.ModuleDocumentationUri); Assert.Equal(new Uri("https://www.contoso.com/icons/icon.png"), details.UnitIconUri); Assert.Equal("xSimpleTestResource", details.ModuleName); Assert.Equal(ModuleType.Script.ToString(), details.ModuleType); Assert.Equal("PowerShell module with DSC resources for unit tests", details.ModuleDescription); Assert.Equal(new Uri("https://github.com/microsoft/winget-cli"), details.PublishedModuleUri); Assert.Equal("0.0.0.1", details.Version); Assert.Equal("Luffytaro", details.Author); Assert.Equal("Microsoft Corporation", details.Publisher); } if (hasGetModuleInfo) { Assert.Equal("PSGallery", details.ModuleSource); Assert.Equal(new DateTimeOffset(new DateTime(2017, 12, 10)), details.PublishedDate); Assert.Equal(new Uri("https://www.contoso.com/icons/icon.png"), details.UnitIconUri); Assert.Equal("xSimpleTestResource", details.ModuleName); Assert.Equal("PowerShell module with DSC resources for unit tests", details.ModuleDescription); Assert.Equal("0.0.0.1", details.Version); Assert.Equal("Luffytaro", details.Author); Assert.Equal("Microsoft Corporation", details.Publisher); Assert.True(details.IsPublic); } if (hasCerts) { Assert.NotNull(details.SigningInformation); } } } private ConfigurationUnit CreateConfigurationUnit() { var unit = new ConfigurationUnit(); unit.Type = "SimpleFileResource"; unit.Metadata.Add("module", "xSimpleTestResource"); unit.Metadata.Add("version", "0.0.0.1"); return unit; } private (DscResourceInfoInternal dscResourceInfo, PSModuleInfo psModuleInfo) GetResourceAndModuleInfo(ConfigurationUnit unit) { // This is easier than trying to mock sealed class from external code... var testEnv = this.fixture.PrepareTestProcessorEnvironment(true); var dscResourceInfo = testEnv.GetDscResource(new ConfigurationUnitAndModule(unit, string.Empty)); var psModuleInfo = testEnv.GetAvailableModule(PowerShellHelpers.CreateModuleSpecification("xSimpleTestResource", "0.0.0.1")); if (dscResourceInfo is null || psModuleInfo is null) { throw new ArgumentNullException("Test processor environment not set correctly"); } return (dscResourceInfo, psModuleInfo); } private PSObject CreateGetModuleInfo() { return new PSObject(new { Repository = "PSGallery", PublishedDate = new DateTime(2017, 12, 10), IconUri = "https://www.contoso.com/icons/icon.png", Name = "xSimpleTestResource", Description = "PowerShell module with DSC resources for unit tests", RepositorySourceLocation = "https://www.powershellgallery.com/api/v2", Version = "0.0.0.1", Author = "Luffytaro", CompanyName = "Microsoft Corporation", }); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationHistoryTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Xml.Linq; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.VisualBasic; using Xunit; using Xunit.Abstractions; /// /// Unit tests for configuration history. /// [Collection("UnitTestCollection")] [InProc] public class ConfigurationHistoryTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationHistoryTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// Checks that the history matches the applied set. /// [Fact] [OutOfProc] public void ApplySet_HistoryMatches_0_1() { this.RunApplyHistoryMatchTest( @" properties: configurationVersion: 0.1 assertions: - resource: Assert id: AssertIdentifier1 directives: module: Module settings: Setting1: '1' Setting2: 2 - resource: Assert id: AssertIdentifier2 dependsOn: - AssertIdentifier1 directives: module: Module settings: Setting1: Setting2: 2 parameters: - resource: Inform id: InformIdentifier1 directives: module: Module2 settings: Setting1: Setting2: Setting3: 3 resources: - resource: Apply ", new string[] { "AssertIdentifier2" }); } /// /// Checks that the history matches the applied set. /// [Fact] [OutOfProc] public void ApplySet_HistoryMatches_0_2() { this.RunApplyHistoryMatchTest( @" properties: configurationVersion: 0.2 assertions: - resource: Module/Assert id: AssertIdentifier1 settings: Setting1: '1' Setting2: 2 - resource: Module/Assert id: AssertIdentifier2 dependsOn: - AssertIdentifier1 directives: description: Describe! settings: Setting1: Setting2: 2 parameters: - resource: Module2/Inform id: InformIdentifier1 settings: Setting1: Setting2: Setting3: 3 resources: - resource: Apply ", new string[] { "AssertIdentifier2" }); } /// /// Checks that the history matches the applied set. /// [Fact] public void ApplySet_HistoryMatches_0_3() { this.RunApplyHistoryMatchTest( @" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: a: 1 b: '2' variables: v1: var1 v2: 42 resources: - name: Name type: Module/Resource metadata: e: '5' f: 6 properties: c: 3 d: '4' - name: Name2 type: Module/Resource2 dependsOn: - Name properties: l: '10' metadata: i: '7' j: 8 q: 42 - name: Group type: Module2/Resource metadata: isGroup: true properties: resources: - name: Child1 type: Module3/Resource metadata: e: '5' f: 6 properties: c: 3 d: '4' - name: Child2 type: Module4/Resource2 properties: l: '10' metadata: i: '7' j: 8 q: 42 "); } /// /// Applies a set, reads the history, changes the read set and reapplies it. /// [Fact] [OutOfProc] public void ApplySet_ChangeHistory() { string disabledIdentifier = "AssertIdentifier2"; ConfigurationSet returnedSet = this.RunApplyHistoryMatchTest( @" properties: configurationVersion: 0.2 assertions: - resource: Module/Assert id: AssertIdentifier1 settings: Setting1: '1' Setting2: 2 - resource: Module/Assert id: AssertIdentifier2 dependsOn: - AssertIdentifier1 directives: description: Describe! settings: Setting1: Setting2: 2 parameters: - resource: Module2/Inform id: InformIdentifier1 settings: Setting1: Setting2: Setting3: 3 resources: - resource: Apply ", new string[] { disabledIdentifier }); foreach (ConfigurationUnit unit in returnedSet.Units) { if (unit.Identifier == disabledIdentifier) { unit.IsActive = true; } } TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(returnedSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.Null(result.ResultCode); ConfigurationSet? historySet = null; foreach (ConfigurationSet set in processor.GetConfigurationHistory()) { if (set.InstanceIdentifier == returnedSet.InstanceIdentifier) { historySet = set; } } this.AssertSetsEqual(returnedSet, historySet); } /// /// Applies a set, reads the history and removes it. /// [Fact] [OutOfProc] public void ApplySet_RemoveHistory() { ConfigurationSet returnedSet = this.RunApplyHistoryMatchTest( @" properties: configurationVersion: 0.2 assertions: - resource: Module/Assert id: AssertIdentifier1 settings: Setting1: '1' Setting2: 2 - resource: Module/Assert id: AssertIdentifier2 dependsOn: - AssertIdentifier1 directives: description: Describe! settings: Setting1: Setting2: 2 parameters: - resource: Module2/Inform id: InformIdentifier1 settings: Setting1: Setting2: Setting3: 3 resources: - resource: Apply "); returnedSet.Remove(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ConfigurationSet? historySet = null; foreach (ConfigurationSet set in processor.GetConfigurationHistory()) { if (set.InstanceIdentifier == returnedSet.InstanceIdentifier) { historySet = set; } } Assert.Null(historySet); } [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2927")] private ConfigurationSet RunApplyHistoryMatchTest(string contents, string[]? inactiveIdentifiers = null) { TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); OpenConfigurationSetResult configurationSetResult = processor.OpenConfigurationSet(this.CreateStream(contents)); ConfigurationSet configurationSet = configurationSetResult.Set; Assert.NotNull(configurationSet); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); setProcessor.EnableDefaultGroupProcessorCreation = true; configurationSet.Name = "Test Name"; configurationSet.Origin = "Test Origin"; configurationSet.Path = "Test Path"; if (inactiveIdentifiers != null) { foreach (string identifier in inactiveIdentifiers) { foreach (ConfigurationUnit unit in configurationSet.Units) { if (unit.Identifier == identifier) { unit.IsActive = false; } } } } ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.Null(result.ResultCode); ConfigurationSet? historySet = null; foreach (ConfigurationSet set in processor.GetConfigurationHistory()) { if (set.InstanceIdentifier == configurationSet.InstanceIdentifier) { historySet = set; } } this.AssertSetsEqual(configurationSet, historySet); this.AssertResultsEqual(result, historySet); return historySet; } private void AssertSetsEqual(ConfigurationSet expectedSet, [NotNull] ConfigurationSet? actualSet) { Assert.NotNull(actualSet); Assert.Equal(expectedSet.Name, actualSet.Name); Assert.Equal(expectedSet.Origin, actualSet.Origin); Assert.Equal(expectedSet.Path, actualSet.Path); Assert.Equal(ConfigurationSetState.Completed, actualSet.State); this.AssertTimeNotZero(actualSet.FirstApply); this.AssertTimeNotZero(actualSet.ApplyBegun); this.AssertTimeNotZero(actualSet.ApplyEnded); Assert.True(actualSet.FirstApply <= actualSet.ApplyBegun); Assert.True(actualSet.ApplyBegun <= actualSet.ApplyEnded); Assert.Equal(expectedSet.SchemaVersion, actualSet.SchemaVersion); Assert.Equal(expectedSet.SchemaUri, actualSet.SchemaUri); Assert.True(expectedSet.Metadata.ContentEquals(actualSet.Metadata)); this.AssertUnitsListEqual(expectedSet.Units, actualSet.Units); } private void AssertTimeNotZero(DateTimeOffset actualTime) { Assert.NotEqual(DateTimeOffset.UnixEpoch, actualTime); Assert.NotEqual(DateTimeOffset.MinValue, actualTime); } private void AssertUnitsListEqual(IList expectedUnits, IList actualUnits) { Assert.Equal(expectedUnits.Count, actualUnits.Count); foreach (ConfigurationUnit expectedUnit in expectedUnits) { ConfigurationUnit? actualUnit = null; foreach (ConfigurationUnit historyUnit in actualUnits) { if (historyUnit.InstanceIdentifier == expectedUnit.InstanceIdentifier) { actualUnit = historyUnit; } } this.AssertUnitsEqual(expectedUnit, actualUnit); } } private void AssertUnitsEqual(ConfigurationUnit expectedUnit, ConfigurationUnit? actualUnit) { Assert.NotNull(actualUnit); Assert.Equal(expectedUnit.Type, actualUnit.Type); Assert.Equal(expectedUnit.Identifier, actualUnit.Identifier); Assert.Equal(expectedUnit.Intent, actualUnit.Intent); Assert.Equal(expectedUnit.Dependencies, actualUnit.Dependencies); Assert.True(expectedUnit.Metadata.ContentEquals(actualUnit.Metadata), $"Metadata not equal: {expectedUnit.Identifier}\n---expected---:\n{expectedUnit.Metadata.ToYaml()}\n---actual---:\n{actualUnit.Metadata.ToYaml()}"); Assert.True(expectedUnit.Settings.ContentEquals(actualUnit.Settings)); Assert.Equal(expectedUnit.IsActive, actualUnit.IsActive); Assert.Equal(expectedUnit.IsGroup, actualUnit.IsGroup); if (expectedUnit.IsGroup) { this.AssertUnitsListEqual(expectedUnit.Units, actualUnit.Units); } } private void AssertResultsEqual(ApplyConfigurationSetResult expected, ConfigurationSet actualSet) { List actualUnitList = new List(); foreach (ConfigurationUnit unit in actualSet.Units) { this.AccumulateUnits(actualUnitList, unit); } foreach (ApplyConfigurationUnitResult expectedUnitResult in expected.UnitResults) { ConfigurationUnit? actualUnit = null; foreach (ConfigurationUnit historyUnit in actualUnitList) { if (historyUnit.InstanceIdentifier == expectedUnitResult.Unit.InstanceIdentifier) { actualUnit = historyUnit; } } this.AssertUnitResultsEqual(expectedUnitResult, actualUnit); } } private void AccumulateUnits(List unitList, ConfigurationUnit unit) { unitList.Add(unit); if (unit.IsGroup) { foreach (ConfigurationUnit child in unit.Units) { this.AccumulateUnits(unitList, child); } } } private void AssertUnitResultsEqual(ApplyConfigurationUnitResult expectedResult, ConfigurationUnit? actualUnit) { Assert.NotNull(actualUnit); Assert.Equal(expectedResult.State, actualUnit.State); var expectedResultInformation = expectedResult.ResultInformation; if (expectedResultInformation != null) { var actualResultInformation = actualUnit.ResultInformation; Assert.NotNull(actualResultInformation); Assert.Equal(expectedResultInformation.ResultCode == null, actualResultInformation.ResultCode == null); if (expectedResultInformation.ResultCode != null) { Assert.Equal(expectedResultInformation.ResultCode.HResult, actualResultInformation.ResultCode!.HResult); } Assert.Equal(expectedResultInformation.Description, actualResultInformation.Description); Assert.Equal(expectedResultInformation.Details, actualResultInformation.Details); Assert.Equal(expectedResultInformation.ResultSource, actualResultInformation.ResultSource); } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationMixedElevationTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.IO; using System.Threading.Tasks; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Xunit; using Xunit.Abstractions; /// /// Unit tests for verifying the processor behavior for handling mixed elevation scenarios. /// [Collection("UnitTestCollection")] [OutOfProc] public class ConfigurationMixedElevationTests : ConfigurationProcessorTestBase { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationMixedElevationTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { this.fixture = fixture; this.log = log; } /// /// Verifies that applying units of mixed elevation is successful. Also verifies that the elevated processor has a different process id. /// /// A representing the asynchronous unit test. [Fact] public async Task ApplyMixedElevationUnits() { string resourceName = "E2ETestResourcePID"; string moduleName = "xE2ETestResource"; Version version = new Version("0.0.0.1"); string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(tempDirectory); ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.SchemaVersion = "0.2"; configurationSet.Metadata.Add(Helpers.Constants.EnableDynamicFactoryTestMode, true); ConfigurationUnit unit = this.ConfigurationUnit(); unit.Metadata.Add("version", version.ToString()); unit.Metadata.Add("module", moduleName); unit.Settings.Add("directoryPath", tempDirectory); unit.Type = resourceName; unit.Intent = ConfigurationUnitIntent.Apply; ConfigurationUnit elevatedUnit = this.ConfigurationUnit(); elevatedUnit.Metadata.Add("version", version.ToString()); elevatedUnit.Metadata.Add("module", moduleName); elevatedUnit.Environment.Context = SecurityContext.Elevated; elevatedUnit.Settings.Add("directoryPath", tempDirectory); elevatedUnit.Type = resourceName; elevatedUnit.Intent = ConfigurationUnitIntent.Apply; configurationSet.Units = new ConfigurationUnit[] { unit, elevatedUnit }; IConfigurationSetProcessorFactory dynamicFactory = await this.fixture.ConfigurationStatics.CreateConfigurationSetProcessorFactoryAsync(Helpers.Constants.DynamicRuntimeHandlerIdentifier); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(dynamicFactory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); // Get the number of unique PIDs from temp directory. int pidCount = Directory.GetFiles(tempDirectory).Length; // Clean up temp directory folder. Directory.Delete(tempDirectory, true); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.Equal(2, result.UnitResults.Count); foreach (var unitResult in result.UnitResults) { Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } // There should be exactly 2 unique PIDs, one for each integrity level. Assert.Equal(2, pidCount); } /// /// Verifies that applying units of mixed elevation is successful. Also verifies that the elevated processor has a different process id. /// /// A representing the asynchronous unit test. [Fact] public async Task ApplyMixedElevationUnits_Schema_0_3() { string resourceName = "xE2ETestResource/E2ETestResourcePID"; Version version = new Version("0.0.0.1"); string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(tempDirectory); ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.SchemaVersion = "0.3"; configurationSet.Metadata.Add(Helpers.Constants.EnableDynamicFactoryTestMode, true); ConfigurationUnit unit = this.ConfigurationUnit(); unit.Metadata.Add("version", version.ToString()); unit.Settings.Add("directoryPath", tempDirectory); unit.Type = resourceName; unit.Identifier = "current"; ConfigurationUnit elevatedUnit = this.ConfigurationUnit(); elevatedUnit.Intent = ConfigurationUnitIntent.Unknown; elevatedUnit.Metadata.Add("version", version.ToString()); elevatedUnit.Environment.Context = SecurityContext.Elevated; elevatedUnit.Settings.Add("directoryPath", tempDirectory); elevatedUnit.Type = resourceName; elevatedUnit.Identifier = "elevated"; configurationSet.Units = new ConfigurationUnit[] { unit, elevatedUnit }; IConfigurationSetProcessorFactory dynamicFactory = await this.fixture.ConfigurationStatics.CreateConfigurationSetProcessorFactoryAsync(Helpers.Constants.DynamicRuntimeHandlerIdentifier); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(dynamicFactory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); // Get the number of unique PIDs from temp directory. int pidCount = Directory.GetFiles(tempDirectory).Length; // Clean up temp directory folder. Directory.Delete(tempDirectory, true); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.Equal(2, result.UnitResults.Count); foreach (var unitResult in result.UnitResults) { Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } // There should be exactly 2 unique PIDs, one for each integrity level. Assert.Equal(2, pidCount); } /// /// Verifies that creating a high integrity unit processor for a non elevated unit should return an invalid operation result. /// /// A representing the asynchronous unit test. [Fact] public async Task ApplyUnitNotInLimitationSet() { string resourceName = "E2ETestResource"; string moduleName = "xE2ETestResource"; Version version = new Version("0.0.0.1"); ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.SchemaVersion = "0.2"; configurationSet.Metadata.Add(Helpers.Constants.EnableDynamicFactoryTestMode, true); configurationSet.Metadata.Add(Helpers.Constants.ForceHighIntegrityLevelUnitsTestGuid, true); configurationSet.Metadata.Add(Helpers.Constants.EnableRestrictedIntegrityLevelTestGuid, true); ConfigurationUnit unit = this.ConfigurationUnit(); unit.Metadata.Add("version", version.ToString()); unit.Metadata.Add("module", moduleName); unit.Metadata.Add("unique", "value"); unit.Type = resourceName; unit.Intent = ConfigurationUnitIntent.Apply; ConfigurationUnit elevatedUnit = this.ConfigurationUnit(); elevatedUnit.Metadata.Add("version", version.ToString()); elevatedUnit.Metadata.Add("module", moduleName); elevatedUnit.Environment.Context = SecurityContext.Elevated; elevatedUnit.Type = resourceName; elevatedUnit.Intent = ConfigurationUnitIntent.Apply; configurationSet.Units = new ConfigurationUnit[] { unit, elevatedUnit }; IConfigurationSetProcessorFactory dynamicFactory = await this.fixture.ConfigurationStatics.CreateConfigurationSetProcessorFactoryAsync(Helpers.Constants.DynamicRuntimeHandlerIdentifier); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(dynamicFactory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_SET_APPLY_FAILED, result.ResultCode.HResult); Assert.Equal(2, result.UnitResults.Count); ApplyConfigurationUnitResult unitResult = result.UnitResults[0]; Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.NotNull(unitResult.ResultInformation.ResultCode); Assert.Equal(Errors.CORE_INVALID_OPERATION, unitResult.ResultInformation.ResultCode.HResult); Assert.Equal(ConfigurationUnitResultSource.Internal, unitResult.ResultInformation.ResultSource); // Elevated unit should still succeed when applied. ApplyConfigurationUnitResult elevatedUnitResult = result.UnitResults[1]; Assert.NotNull(elevatedUnitResult); Assert.False(elevatedUnitResult.PreviouslyInDesiredState); Assert.False(elevatedUnitResult.RebootRequired); Assert.NotNull(elevatedUnitResult.ResultInformation); Assert.Null(elevatedUnitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, elevatedUnitResult.ResultInformation.ResultSource); } /// /// Verifies that attempting to pass a secure parameter across the integrity boundary fails. /// /// A representing the asynchronous unit test. [Fact] public async Task SecureParameterAcrossIntegrityBoundaryFails() { string resourceName = "E2ETestResourcePID"; string moduleName = "xE2ETestResource"; Version version = new Version("0.0.0.1"); string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(tempDirectory); ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.Metadata.Add(Helpers.Constants.EnableDynamicFactoryTestMode, true); ConfigurationUnit elevatedUnit = this.ConfigurationUnit(); elevatedUnit.Metadata.Add("version", version.ToString()); elevatedUnit.Metadata.Add("module", moduleName); elevatedUnit.Environment.Context = SecurityContext.Elevated; elevatedUnit.Settings.Add("directoryPath", tempDirectory); elevatedUnit.Type = resourceName; elevatedUnit.Intent = ConfigurationUnitIntent.Apply; configurationSet.Units = new ConfigurationUnit[] { elevatedUnit }; ConfigurationParameter parameter = this.ConfigurationParameter(); parameter.Name = "param"; parameter.Type = Windows.Foundation.PropertyType.String; parameter.IsSecure = true; parameter.ProvidedValue = "secrets"; configurationSet.Parameters = new ConfigurationParameter[] { parameter }; IConfigurationSetProcessorFactory dynamicFactory = await this.fixture.ConfigurationStatics.CreateConfigurationSetProcessorFactoryAsync(Helpers.Constants.DynamicRuntimeHandlerIdentifier); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(dynamicFactory); // While parameters are not supported, we expect to get a not implemented exception. // Once they are implemented, swap to the appropriate error mechanism for the parameter integrity boundary. Assert.Throws(() => processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None)); } /// /// Verifies that DynamicFactoryProcessors do not break on empty set when working with only units. /// /// A representing the asynchronous unit test. [Fact] public async Task CreateDynamicFactoryProcessorsWithEmptyConfigurationSet() { string resourceName = "E2ETestResourcePID"; string moduleName = "xE2ETestResource"; Version version = new Version("0.0.0.1"); string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(tempDirectory); ConfigurationUnit unit = this.ConfigurationUnit(); unit.Metadata.Add("version", version.ToString()); unit.Metadata.Add("module", moduleName); unit.Settings.Add("directoryPath", tempDirectory); unit.Type = resourceName; unit.Intent = ConfigurationUnitIntent.Inform; IConfigurationSetProcessorFactory dynamicFactory = await this.fixture.ConfigurationStatics.CreateConfigurationSetProcessorFactoryAsync(Helpers.Constants.DynamicRuntimeHandlerIdentifier); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(dynamicFactory); var result = await processor.GetUnitSettingsAsync(unit); Assert.NotNull(result); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorApplyTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.VisualBasic; using Xunit; using Xunit.Abstractions; /// /// Unit tests for running apply on the processor. /// [Collection("UnitTestCollection")] [InProc] [OutOfProc] public class ConfigurationProcessorApplyTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationProcessorApplyTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// An error creating the set processor results in an error for the function. /// [Fact] public void ApplySet_SetProcessorError() { ConfigurationSet configurationSet = this.ConfigurationSet(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.Exceptions.Add(configurationSet, new FileNotFoundException()); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); Assert.Throws(() => processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None)); Assert.Empty(this.EventSink.Events); } /// /// Multiple configuration units with the same identifier. /// [Fact] public void ApplySet_DuplicateIdentifiers() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnit1 = this.ConfigurationUnit(); ConfigurationUnit configurationUnit2 = this.ConfigurationUnit(); ConfigurationUnit configurationUnitDifferentIdentifier = this.ConfigurationUnit(); string sharedIdentifier = "SameIdentifier"; configurationUnit1.Identifier = sharedIdentifier; configurationUnit2.Identifier = sharedIdentifier; configurationSet.Units = new ConfigurationUnit[] { configurationUnit1, configurationUnit2, configurationUnitDifferentIdentifier }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, result.ResultCode.HResult); Assert.Equal(3, result.UnitResults.Count); foreach (var configurationUnit in new ConfigurationUnit[] { configurationUnit1, configurationUnit2 }) { ApplyConfigurationUnitResult? unitResult = result.UnitResults.First(x => x.Unit == configurationUnit); Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.NotNull(unitResult.ResultInformation.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, unitResult.ResultInformation.ResultCode.HResult); Assert.Equal(ConfigurationUnitResultSource.ConfigurationSet, unitResult.ResultInformation.ResultSource); } ApplyConfigurationUnitResult unitResultDifferentIdentifier = result.UnitResults.First(x => x.Unit == configurationUnitDifferentIdentifier); Assert.NotNull(unitResultDifferentIdentifier); Assert.False(unitResultDifferentIdentifier.PreviouslyInDesiredState); Assert.False(unitResultDifferentIdentifier.RebootRequired); Assert.NotNull(unitResultDifferentIdentifier.ResultInformation); Assert.Null(unitResultDifferentIdentifier.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResultDifferentIdentifier.ResultInformation.ResultSource); this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.ConfigurationSet); } /// /// A configuration unit has a dependency that is not in the set. /// [Fact] public void ApplySet_MissingDependency() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnit = this.ConfigurationUnit(); ConfigurationUnit configurationUnitMissingDependency = this.ConfigurationUnit(); configurationUnit.Identifier = "Identifier"; configurationUnitMissingDependency.Dependencies = new string[] { "Dependency" }; configurationSet.Units = new ConfigurationUnit[] { configurationUnit, configurationUnitMissingDependency }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_MISSING_DEPENDENCY, result.ResultCode.HResult); Assert.Equal(2, result.UnitResults.Count); ApplyConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == configurationUnit); Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); unitResult = result.UnitResults.First(x => x.Unit == configurationUnitMissingDependency); Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.NotNull(unitResult.ResultInformation.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_MISSING_DEPENDENCY, unitResult.ResultInformation.ResultCode.HResult); Assert.Equal(ConfigurationUnitResultSource.ConfigurationSet, unitResult.ResultInformation.ResultSource); this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.ConfigurationSet); } /// /// The configuration set has a dependency cycle. /// [Fact] public void ApplySet_DependencyCycle() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnit1 = this.ConfigurationUnit(); ConfigurationUnit configurationUnit2 = this.ConfigurationUnit(); ConfigurationUnit configurationUnit3 = this.ConfigurationUnit(); configurationUnit1.Identifier = "Identifier1"; configurationUnit2.Identifier = "Identifier2"; configurationUnit3.Identifier = "Identifier3"; configurationUnit1.Dependencies = new string[] { "Identifier3" }; configurationUnit2.Dependencies = new string[] { "Identifier1" }; configurationUnit3.Dependencies = new string[] { "Identifier2" }; configurationSet.Units = new ConfigurationUnit[] { configurationUnit1, configurationUnit2, configurationUnit3 }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_SET_DEPENDENCY_CYCLE, result.ResultCode.HResult); Assert.Equal(3, result.UnitResults.Count); foreach (var unitResult in result.UnitResults) { Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.NotNull(unitResult.ResultInformation.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, unitResult.ResultInformation.ResultCode.HResult); Assert.Equal(ConfigurationUnitResultSource.Precondition, unitResult.ResultInformation.ResultSource); } this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.Precondition); } /// /// Checks that the intent for configuration units is handled properly. /// [Fact] public void ApplySet_IntentRespected() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitAssert = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Assert }); ConfigurationUnit configurationUnitInform = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Inform }); ConfigurationUnit configurationUnitApply = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply }); configurationSet.Units = new ConfigurationUnit[] { configurationUnitInform, configurationUnitApply, configurationUnitAssert }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessorAssert = setProcessor.CreateTestProcessor(configurationUnitAssert); TestConfigurationUnitProcessor unitProcessorInform = setProcessor.CreateTestProcessor(configurationUnitInform); TestConfigurationUnitProcessor unitProcessorApply = setProcessor.CreateTestProcessor(configurationUnitApply); unitProcessorApply.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnitApply) { TestResult = ConfigurationTestResult.Negative }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.Equal(3, result.UnitResults.Count); foreach (var unitResult in result.UnitResults) { Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } Assert.Equal(1, unitProcessorAssert.TestSettingsCalls); Assert.Equal(0, unitProcessorAssert.GetSettingsCalls); Assert.Equal(0, unitProcessorAssert.ApplySettingsCalls); Assert.Equal(0, unitProcessorInform.TestSettingsCalls); Assert.Equal(1, unitProcessorInform.GetSettingsCalls); Assert.Equal(0, unitProcessorInform.ApplySettingsCalls); Assert.Equal(1, unitProcessorApply.TestSettingsCalls); Assert.Equal(0, unitProcessorApply.GetSettingsCalls); Assert.Equal(1, unitProcessorApply.ApplySettingsCalls); this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.None); } /// /// An assertion fails to run. /// [Fact] public void ApplySet_AssertionFailure() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitAssert = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Assert }); ConfigurationUnit configurationUnitApply = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply }); configurationSet.Units = new ConfigurationUnit[] { configurationUnitApply, configurationUnitAssert }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessorAssert = setProcessor.CreateTestProcessor(configurationUnitAssert); unitProcessorAssert.TestSettingsDelegate = () => throw new NullReferenceException(); TestConfigurationUnitProcessor unitProcessorApply = setProcessor.CreateTestProcessor(configurationUnitApply); unitProcessorApply.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnitApply) { TestResult = ConfigurationTestResult.Negative }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_ASSERTION_FAILED, result.ResultCode.HResult); Assert.Equal(2, result.UnitResults.Count); ApplyConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == configurationUnitAssert); Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.NotNull(unitResult.ResultInformation.ResultCode); Assert.IsType(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.Internal, unitResult.ResultInformation.ResultSource); unitResult = result.UnitResults.First(x => x.Unit == configurationUnitApply); Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.NotNull(unitResult.ResultInformation.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_ASSERTION_FAILED, unitResult.ResultInformation.ResultCode.HResult); Assert.Equal(ConfigurationUnitResultSource.Precondition, unitResult.ResultInformation.ResultSource); this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.Internal); } /// /// An assertion is found to be false. /// [Fact] public void ApplySet_AssertionNegative() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitAssert = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Assert }); ConfigurationUnit configurationUnitApply = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply }); configurationSet.Units = new ConfigurationUnit[] { configurationUnitApply, configurationUnitAssert }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessorAssert = setProcessor.CreateTestProcessor(configurationUnitAssert); unitProcessorAssert.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnitAssert) { TestResult = ConfigurationTestResult.Negative }; TestConfigurationUnitProcessor unitProcessorApply = setProcessor.CreateTestProcessor(configurationUnitApply); unitProcessorApply.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnitApply) { TestResult = ConfigurationTestResult.Negative }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_ASSERTION_FAILED, result.ResultCode.HResult); Assert.Equal(2, result.UnitResults.Count); foreach (var unitResult in result.UnitResults) { Assert.NotNull(unitResult); Assert.False(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.NotNull(unitResult.ResultInformation.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_ASSERTION_FAILED, unitResult.ResultInformation.ResultCode.HResult); Assert.Equal(ConfigurationUnitResultSource.Precondition, unitResult.ResultInformation.ResultSource); } this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.Precondition); } /// /// A unit in the correct state is not applied again. /// [Fact] public void ApplySet_UnitAlreadyInCorrectState() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnit = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply }); configurationSet.Units = new ConfigurationUnit[] { configurationUnit }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnit); unitProcessor.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnit) { TestResult = ConfigurationTestResult.Positive }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.Equal(1, result.UnitResults.Count); ApplyConfigurationUnitResult unitResult = result.UnitResults.First(); Assert.NotNull(unitResult); Assert.True(unitResult.PreviouslyInDesiredState); Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.None); } /// /// Checks the progress reporting. /// [Fact] public void ApplySet_Progress() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit assert1 = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Assert, Identifier = "Assert1" }); ConfigurationUnit assert2 = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Assert, Identifier = "Assert2", Dependencies = new string[] { assert1.Identifier } }); ConfigurationUnit inform1 = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Inform, Identifier = "Inform1" }); ConfigurationUnit apply1 = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply1" }); ConfigurationUnit apply2 = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply2" }); ConfigurationUnit apply3 = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply3", Dependencies = new string[] { apply1.Identifier, apply2.Identifier } }); ConfigurationUnit apply4 = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply4", IsActive = false }); ConfigurationUnit apply5 = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply5", Dependencies = new string[] { apply4.Identifier } }); configurationSet.Units = new ConfigurationUnit[] { assert2, assert1, inform1, apply1, apply3, apply4, apply2, apply5 }; ManualResetEvent startProcessing = new ManualResetEvent(false); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.CreateSetProcessorDelegate = (f, c) => { startProcessing.WaitOne(); return f.DefaultCreateSetProcessor(c); }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); List progressEvents = new List(); var operation = processor.ApplySetAsync(configurationSet, ApplyConfigurationSetFlags.None); operation.Progress = (asyncInfo, progress) => progressEvents.Add(progress); startProcessing.Set(); operation.AsTask().Wait(); ApplyConfigurationSetResult result = operation.GetResults(); Assert.NotNull(result); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, result.ResultCode.HResult); Assert.NotNull(result.UnitResults); Assert.Equal(configurationSet.Units.Count, result.UnitResults.Count); // Verify that progress events match the expected ExpectedConfigurationChangeData[] expectedProgress = new ExpectedConfigurationChangeData[] { new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.SetStateChanged, SetState = ConfigurationSetState.InProgress }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = assert1 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = assert1 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = assert2 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = assert2 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = inform1 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = inform1 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = apply1 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = apply1 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Skipped, Unit = apply4, HResult = Errors.WINGET_CONFIG_ERROR_MANUALLY_SKIPPED }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = apply2 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = apply2 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = apply3 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = apply3 }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Skipped, Unit = apply5, HResult = Errors.WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED }, new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.SetStateChanged, SetState = ConfigurationSetState.Completed }, }; // Drop the pending event if it happens to be present if (progressEvents.Count > 0 && progressEvents[0].Change == ConfigurationSetChangeEventType.SetStateChanged && progressEvents[0].SetState == ConfigurationSetState.Pending) { progressEvents.RemoveAt(0); } Assert.Equal(expectedProgress.Count(), progressEvents.Count); for (int i = 0; i < progressEvents.Count; ++i) { this.Log.WriteLine($"Comparing event {i}"); Assert.Equal(expectedProgress[i].Change, progressEvents[i].Change); switch (expectedProgress[i].Change) { case ConfigurationSetChangeEventType.SetStateChanged: Assert.Equal(expectedProgress[i].SetState, progressEvents[i].SetState); break; case ConfigurationSetChangeEventType.UnitStateChanged: Assert.Equal(expectedProgress[i].UnitState, progressEvents[i].UnitState); Assert.Same(expectedProgress[i].Unit, progressEvents[i].Unit); Assert.NotNull(progressEvents[i].ResultInformation); if (expectedProgress[i].HResult == 0) { Assert.Null(progressEvents[i].ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, progressEvents[i].ResultInformation.ResultSource); } else { Assert.NotNull(progressEvents[i].ResultInformation.ResultCode); Assert.Equal(expectedProgress[i].HResult, progressEvents[i].ResultInformation.ResultCode.HResult); Assert.Equal(ConfigurationUnitResultSource.Precondition, progressEvents[i].ResultInformation.ResultSource); } break; default: Assert.Fail("Unexpected ConfigurationSetChangeEventType value"); break; } } this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.Precondition); } /// /// Ensures that multiple apply operations are sequenced. /// [Fact] public void ApplySet_Sequenced() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitApply = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply }); configurationSet.Units = new ConfigurationUnit[] { configurationUnitApply }; ManualResetEvent startProcessing = new ManualResetEvent(true); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.CreateSetProcessorDelegate = (f, c) => { WaitOn(startProcessing); return f.DefaultCreateSetProcessor(c); }; TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessorApply = setProcessor.CreateTestProcessor(configurationUnitApply); unitProcessorApply.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnitApply) { TestResult = ConfigurationTestResult.Negative }; ManualResetEvent applyEventWaiting = new ManualResetEvent(false); ManualResetEvent completeApplyEvent = new ManualResetEvent(false); unitProcessorApply.ApplySettingsDelegate = () => { applyEventWaiting.Set(); WaitOn(completeApplyEvent); return new ApplySettingsResultInstance(configurationUnitApply); }; ConfigurationSet configurationSetThatWaits = this.ConfigurationSet(); ConfigurationUnit configurationUnitThatWaits = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply }); configurationSetThatWaits.Units = new ConfigurationUnit[] { configurationUnitThatWaits }; TestConfigurationSetProcessor setThatWaitsProcessor = factory.CreateTestProcessor(configurationSetThatWaits); TestConfigurationUnitProcessor unitThatWaitsProcessor = setProcessor.CreateTestProcessor(configurationUnitThatWaits); unitThatWaitsProcessor.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnitApply) { TestResult = ConfigurationTestResult.Negative }; ManualResetEvent waitingUnitApply = new ManualResetEvent(false); unitThatWaitsProcessor.ApplySettingsDelegate = () => { WaitOn(waitingUnitApply); return new ApplySettingsResultInstance(configurationUnitThatWaits); }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); var applySetOperation = processor.ApplySetAsync(configurationSet, ApplyConfigurationSetFlags.None); WaitOn(applyEventWaiting); startProcessing.Reset(); var waitingSetOperation = processor.ApplySetAsync(configurationSetThatWaits, ApplyConfigurationSetFlags.None); AutoResetEvent waitingProgress = new AutoResetEvent(false); ConfigurationSetState progressState = ConfigurationSetState.Unknown; waitingSetOperation.Progress += (result, changeData) => { if (changeData.Change == ConfigurationSetChangeEventType.SetStateChanged) { progressState = changeData.SetState; waitingProgress.Set(); } }; startProcessing.Set(); WaitOn(waitingProgress); Assert.Equal(ConfigurationSetState.Pending, progressState); completeApplyEvent.Set(); WaitOn(waitingProgress); Assert.Equal(ConfigurationSetState.InProgress, progressState); waitingUnitApply.Set(); WaitOn(waitingProgress); Assert.Equal(ConfigurationSetState.Completed, progressState); waitingSetOperation.AsTask().Wait(); } /// /// Ensures that a consistency check apply is not blocked. /// [Fact] public void ApplySet_ConsistencyCheckNotSequenced() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitApply = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply }); configurationSet.Units = new ConfigurationUnit[] { configurationUnitApply }; ManualResetEvent startProcessing = new ManualResetEvent(true); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.CreateSetProcessorDelegate = (f, c) => { WaitOn(startProcessing); return f.DefaultCreateSetProcessor(c); }; TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessorApply = setProcessor.CreateTestProcessor(configurationUnitApply); unitProcessorApply.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnitApply) { TestResult = ConfigurationTestResult.Negative }; ManualResetEvent applyEventWaiting = new ManualResetEvent(false); ManualResetEvent completeApplyEvent = new ManualResetEvent(false); unitProcessorApply.ApplySettingsDelegate = () => { applyEventWaiting.Set(); WaitOn(completeApplyEvent); return new ApplySettingsResultInstance(configurationUnitApply); }; ConfigurationSet configurationSetThatWaits = this.ConfigurationSet(); ConfigurationUnit configurationUnitThatWaits = this.ConfigurationUnit().Assign(new { Intent = ConfigurationUnitIntent.Apply }); configurationSetThatWaits.Units = new ConfigurationUnit[] { configurationUnitThatWaits }; TestConfigurationSetProcessor setThatWaitsProcessor = factory.CreateTestProcessor(configurationSetThatWaits); TestConfigurationUnitProcessor unitThatWaitsProcessor = setProcessor.CreateTestProcessor(configurationUnitThatWaits); unitThatWaitsProcessor.TestSettingsDelegate = () => new TestSettingsResultInstance(configurationUnitApply) { TestResult = ConfigurationTestResult.Negative }; ManualResetEvent waitingUnitApply = new ManualResetEvent(false); unitThatWaitsProcessor.ApplySettingsDelegate = () => { WaitOn(waitingUnitApply); return new ApplySettingsResultInstance(configurationUnitThatWaits); }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); var applySetOperation = processor.ApplySetAsync(configurationSet, ApplyConfigurationSetFlags.None); WaitOn(applyEventWaiting); startProcessing.Reset(); var waitingSetOperation = processor.ApplySetAsync(configurationSetThatWaits, ApplyConfigurationSetFlags.PerformConsistencyCheckOnly); Assert.True(waitingSetOperation.AsTask().Wait(10000)); completeApplyEvent.Set(); } private static void WaitOn(WaitHandle waitable) { if (!waitable.WaitOne(10000)) { throw new TimeoutException(); } } private struct ExpectedConfigurationChangeData { public ConfigurationSetChangeEventType Change; public ConfigurationSetState SetState; public ConfigurationUnitState UnitState; public int HResult; public ConfigurationUnit Unit; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorFactoryTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.Collections.Generic; using Microsoft.Management.Configuration.Processor; using Microsoft.Management.Configuration.Processor.PowerShell.Set; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using WinRT; using Xunit; using Xunit.Abstractions; using static Microsoft.Management.Configuration.Processor.PowerShell.Constants.PowerShellConstants; /// /// Tests ConfigurationProcessorFactory. /// [Collection("UnitTestCollection")] [InProc] public class ConfigurationProcessorFactoryTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationProcessorFactoryTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// CreateSetProcessor test. /// [Fact] public void CreateSetProcessor_Test() { var configurationProcessorFactory = new PowerShellConfigurationSetProcessorFactory(); var properties = configurationProcessorFactory.As(); properties.ProcessorType = PowerShellConfigurationProcessorType.Hosted; var configurationSet = new ConfigurationSet(); var configurationProcessorSet = configurationProcessorFactory.CreateSetProcessor(configurationSet); Assert.NotNull(configurationProcessorSet); Assert.IsType(configurationProcessorSet); var processorSet = configurationProcessorSet as PowerShellConfigurationSetProcessor; Assert.NotNull(processorSet); } /// /// AdditionalModulePaths test. /// [Fact] public void CreateSetProcessor_Properties_PsModulePath() { var configurationProcessorFactory = new PowerShellConfigurationSetProcessorFactory(); var properties = configurationProcessorFactory.As(); properties.ProcessorType = PowerShellConfigurationProcessorType.Hosted; properties.AdditionalModulePaths = new List { "ThisIsOnePath", "ThisIsAnotherPath", }; var configurationSet = new ConfigurationSet(); var configurationProcessorSet = configurationProcessorFactory.CreateSetProcessor(configurationSet); Assert.IsType(configurationProcessorSet); var processorSet = configurationProcessorSet as PowerShellConfigurationSetProcessor; Assert.NotNull(processorSet); var modulePath = processorSet.ProcessorEnvironment.GetVariable(Variables.PSModulePath); Assert.Contains("ThisIsOnePath;ThisIsAnotherPath", modulePath); } /// /// Make sure the winget path is always added to PSModulePath. /// /// Location. [Theory] [InlineData(PowerShellConfigurationProcessorLocation.CurrentUser)] [InlineData(PowerShellConfigurationProcessorLocation.AllUsers)] [InlineData(PowerShellConfigurationProcessorLocation.WinGetModulePath)] [InlineData(PowerShellConfigurationProcessorLocation.Custom)] public void CreateSetProcessor_WinGetPath(PowerShellConfigurationProcessorLocation location) { var configurationProcessorFactory = new PowerShellConfigurationSetProcessorFactory(); var properties = configurationProcessorFactory.As(); properties.Location = location; if (properties.Location == PowerShellConfigurationProcessorLocation.Custom) { properties.CustomLocation = @"c:\this\is\a\module\path"; } var configurationSet = new ConfigurationSet(); var setProcessor = configurationProcessorFactory.CreateSetProcessor(configurationSet) as PowerShellConfigurationSetProcessor; Assert.NotNull(setProcessor); var modulePath = setProcessor.ProcessorEnvironment.GetVariable(Variables.PSModulePath); Assert.Contains($"{PowerShellConfigurationSetProcessorFactory.GetWinGetModulePath()};", modulePath); } /// /// Tests the custom location is added successfully. /// [Fact] public void CreateSetProcessor_CustomLocation() { var configurationProcessorFactory = new PowerShellConfigurationSetProcessorFactory(); var properties = configurationProcessorFactory.As(); properties.Location = PowerShellConfigurationProcessorLocation.Custom; properties.CustomLocation = @"c:\this\is\a\module\path"; var configurationSet = new ConfigurationSet(); var setProcessor = configurationProcessorFactory.CreateSetProcessor(configurationSet) as PowerShellConfigurationSetProcessor; Assert.NotNull(setProcessor); var modulePath = setProcessor.ProcessorEnvironment.GetVariable(Variables.PSModulePath); Assert.Contains($"{properties.CustomLocation};", modulePath); } /// /// Tests the configuration set processor in limitation mode. /// [Fact] public void CreateSetProcessor_LimitMode() { var configurationProcessorFactory = new PowerShellConfigurationSetProcessorFactory(); var configurationSet = new ConfigurationSet(); configurationProcessorFactory.LimitationSet = configurationSet; Assert.Throws(() => configurationProcessorFactory.LimitationSet = configurationSet); Assert.Throws(() => configurationProcessorFactory.ProcessorType = PowerShellConfigurationProcessorType.Default); Assert.Throws(() => configurationProcessorFactory.AdditionalModulePaths = new List()); Assert.Throws(() => configurationProcessorFactory.ImplicitModulePaths = new List()); Assert.Throws(() => configurationProcessorFactory.Policy = PowerShellConfigurationProcessorPolicy.Unrestricted); Assert.Throws(() => configurationProcessorFactory.Location = PowerShellConfigurationProcessorLocation.Custom); Assert.Throws(() => configurationProcessorFactory.CustomLocation = @"c:\this\is\a\module\path"); var setProcessor = configurationProcessorFactory.CreateSetProcessor(configurationSet) as PowerShellConfigurationSetProcessor; Assert.NotNull(setProcessor); Assert.True(setProcessor.IsLimitMode); // Create processor again in limit mode should fail Assert.Throws(() => configurationProcessorFactory.CreateSetProcessor(configurationSet)); } /// /// Tests the configuration set processor factory with ImplicitModulePaths. /// [Fact] public void ImplicitModulePaths() { var configurationProcessorFactory = new PowerShellConfigurationSetProcessorFactory(); // When ImplicitModulePaths module paths are not set configurationProcessorFactory.AdditionalModulePaths = new List { @"c:\this\is\additional" }; Assert.Equal(configurationProcessorFactory.AdditionalModulePaths, new List { @"c:\this\is\additional" }); // Implicit ModulePaths are set, it automatically populates AdditionalModulePaths configurationProcessorFactory.ImplicitModulePaths = new List { @"c:\this\is\implicit" }; Assert.Equal(configurationProcessorFactory.AdditionalModulePaths, new List { @"c:\this\is\additional", @"c:\this\is\implicit" }); // Set AdditionalModulePaths when ImplicitModulePaths module paths are set configurationProcessorFactory.AdditionalModulePaths = new List { @"c:\this\is\additional\2" }; Assert.Equal(configurationProcessorFactory.AdditionalModulePaths, new List { @"c:\this\is\additional\2", @"c:\this\is\implicit" }); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorGetAllTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.Collections.Generic; using System.IO; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.VisualBasic; using Windows.Foundation.Collections; using Xunit; using Xunit.Abstractions; /// /// Unit tests for getting details on processors. /// [Collection("UnitTestCollection")] [InProc] [OutOfProc] public class ConfigurationProcessorGetAllTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationProcessorGetAllTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// The unit settings processor returns an error HRESULT. /// [Fact] public void GetAllSettings_ProcessorSettingsError() { ConfigurationUnit configurationUnit = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.NullProcessor = new TestConfigurationSetProcessor(null); var unitProcessor = factory.NullProcessor.CreateGetAllSettingsTestProcessor(configurationUnit); unitProcessor.GetAllSettingsDelegate = () => throw new FileNotFoundException(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetAllConfigurationUnitSettingsResult result = processor.GetAllUnitSettings(configurationUnit); Assert.NotNull(result); Assert.Null(result.Settings); Assert.NotNull(result.ResultInformation); Assert.NotNull(result.ResultInformation.ResultCode); Assert.IsType(result.ResultInformation.ResultCode); } /// /// The unit settings processor returns an error in its result. /// [Fact] public void GetAllSettings_ProcessorSettingsFailedResult() { ConfigurationUnit configurationUnit = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.NullProcessor = new TestConfigurationSetProcessor(null); var unitProcessor = factory.NullProcessor.CreateGetAllSettingsTestProcessor(configurationUnit); GetAllSettingsResultInstance getAllSettingsResult = new GetAllSettingsResultInstance(configurationUnit); getAllSettingsResult.InternalResult.ResultCode = new InvalidDataException(); getAllSettingsResult.InternalResult.Description = "We fail because we must"; unitProcessor.GetAllSettingsDelegate = () => getAllSettingsResult; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetAllConfigurationUnitSettingsResult result = processor.GetAllUnitSettings(configurationUnit); Assert.NotNull(result); Assert.Null(result.Settings); Assert.NotNull(result.ResultInformation); Assert.Equal(getAllSettingsResult.ResultInformation.ResultCode.HResult, result.ResultInformation.ResultCode.HResult); Assert.Equal(getAllSettingsResult.ResultInformation.Description, result.ResultInformation.Description); } /// /// The unit settings processor returns good settings. /// [Fact] public void GetAllSettings_ProcessorSettingsSuccess() { ConfigurationUnit configurationUnit = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.NullProcessor = new TestConfigurationSetProcessor(null); var unitProcessor = factory.NullProcessor.CreateGetAllSettingsTestProcessor(configurationUnit); GetAllSettingsResultInstance getAllSettingsResult = new GetAllSettingsResultInstance(configurationUnit); getAllSettingsResult.Settings = new List() { new ValueSet { { "key", "value" }, }, }; unitProcessor.GetAllSettingsDelegate = () => getAllSettingsResult; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetAllConfigurationUnitSettingsResult result = processor.GetAllUnitSettings(configurationUnit); Assert.NotNull(result); Assert.NotNull(result.ResultInformation); Assert.Null(result.ResultInformation.ResultCode); Assert.Empty(result.ResultInformation.Description); Assert.NotNull(result.Settings); Assert.Equal(1, result.Settings.Count); Assert.Contains("key", result.Settings[0]); Assert.IsType(result.Settings[0]["key"]); Assert.Equal("value", (string)result.Settings[0]["key"]); } /// /// The unit settings processor does not support GetAllSettings. /// [Fact] public void GetAllSettings_UnsupportedByProcessor() { ConfigurationUnit configurationUnit = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.NullProcessor = new TestConfigurationSetProcessor(null); var unitProcessor = factory.NullProcessor.CreateTestProcessor(configurationUnit); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetAllConfigurationUnitSettingsResult result = processor.GetAllUnitSettings(configurationUnit); Assert.NotNull(result); Assert.NotNull(result.ResultInformation); Assert.NotNull(result.ResultInformation.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR, result.ResultInformation.ResultCode.HResult); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorGetTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.IO; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.VisualBasic; using Xunit; using Xunit.Abstractions; /// /// Unit tests for getting details on processors. /// [Collection("UnitTestCollection")] [InProc] [OutOfProc] public class ConfigurationProcessorGetTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationProcessorGetTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// Getting unit details throws an error. /// [Fact] public void GetUnitDetailsError() { ConfigurationUnit configurationUnitThrows = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.NullProcessor = new TestConfigurationSetProcessor(null); var thrownException = new FileNotFoundException(); factory.NullProcessor.Exceptions.Add(configurationUnitThrows, thrownException); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetConfigurationUnitDetailsResult result = processor.GetUnitDetails(configurationUnitThrows, ConfigurationUnitDetailFlags.Local); Assert.Null(result.Details); Assert.Equal(configurationUnitThrows, result.Unit); Assert.Equal(thrownException.HResult, result.ResultInformation.ResultCode.HResult); Assert.Equal(ConfigurationUnitResultSource.Internal, result.ResultInformation.ResultSource); } /// /// Getting unit details retrieves a value. /// [Fact] public void GetUnitDetailsSuccess() { ConfigurationUnit configurationUnit = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); Assert.Null(configurationUnit.Details); processor.GetUnitDetails(configurationUnit, ConfigurationUnitDetailFlags.Local); Assert.NotNull(configurationUnit.Details); } /// /// Getting set details throws an error. /// [Fact] public void GetSetDetailsError() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitWorks = this.ConfigurationUnit(); ConfigurationUnit configurationUnitThrows = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnitWorks, configurationUnitThrows }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); var thrownException = new InvalidDataException(); setProcessor.Exceptions.Add(configurationUnitThrows, thrownException); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetConfigurationSetDetailsResult result = processor.GetSetDetails(configurationSet, ConfigurationUnitDetailFlags.Local); var unitResults = result.UnitResults; Assert.Equal(2, unitResults.Count); Assert.Equal(configurationUnitWorks, unitResults[0].Unit); Assert.Null(unitResults[0].ResultInformation.ResultCode); Assert.NotNull(configurationUnitWorks.Details); Assert.Equal(configurationUnitThrows, unitResults[1].Unit); Assert.NotNull(unitResults[1].ResultInformation.ResultCode); Assert.Equal(thrownException.HResult, unitResults[1].ResultInformation.ResultCode.HResult); Assert.Null(configurationUnitThrows.Details); } /// /// Getting set details retrieves all values. /// [Fact] public void GetSetDetailsSuccess() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnit1 = this.ConfigurationUnit(); ConfigurationUnit configurationUnit2 = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnit1, configurationUnit2 }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); processor.GetSetDetails(configurationSet, ConfigurationUnitDetailFlags.Local); Assert.NotNull(configurationUnit1.Details); Assert.NotNull(configurationUnit2.Details); } /// /// The unit settings processor returns an error HRESULT. /// [Fact] public void GetSettings_ProcessorSettingsError() { ConfigurationUnit configurationUnit = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.NullProcessor = new TestConfigurationSetProcessor(null); TestConfigurationUnitProcessor unitProcessor = factory.NullProcessor.CreateTestProcessor(configurationUnit); unitProcessor.GetSettingsDelegate = () => throw new FileNotFoundException(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetConfigurationUnitSettingsResult result = processor.GetUnitSettings(configurationUnit); Assert.NotNull(result); Assert.NotNull(result.Settings); Assert.NotNull(result.ResultInformation); Assert.NotNull(result.ResultInformation.ResultCode); Assert.IsType(result.ResultInformation.ResultCode); } /// /// The unit settings processor returns an error in it's result. /// [Fact] public void GetSettings_ProcessorSettingsFailedResult() { ConfigurationUnit configurationUnit = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.NullProcessor = new TestConfigurationSetProcessor(null); TestConfigurationUnitProcessor unitProcessor = factory.NullProcessor.CreateTestProcessor(configurationUnit); GetSettingsResultInstance getSettingsResult = new GetSettingsResultInstance(configurationUnit); getSettingsResult.InternalResult.ResultCode = new InvalidDataException(); getSettingsResult.InternalResult.Description = "We fail because we must"; unitProcessor.GetSettingsDelegate = () => getSettingsResult; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetConfigurationUnitSettingsResult result = processor.GetUnitSettings(configurationUnit); Assert.NotNull(result); Assert.Null(result.Settings); Assert.NotNull(result.ResultInformation); Assert.Equal(getSettingsResult.ResultInformation.ResultCode.HResult, result.ResultInformation.ResultCode.HResult); Assert.Equal(getSettingsResult.ResultInformation.Description, result.ResultInformation.Description); } /// /// The unit settings processor returns good settings. /// [Fact] public void GetSettings_ProcessorSettingsSuccess() { ConfigurationUnit configurationUnit = this.ConfigurationUnit(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.NullProcessor = new TestConfigurationSetProcessor(null); TestConfigurationUnitProcessor unitProcessor = factory.NullProcessor.CreateTestProcessor(configurationUnit); GetSettingsResultInstance getSettingsResult = new GetSettingsResultInstance(configurationUnit); getSettingsResult.Settings = new Windows.Foundation.Collections.ValueSet(); getSettingsResult.Settings.Add("key", "value"); unitProcessor.GetSettingsDelegate = () => getSettingsResult; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); GetConfigurationUnitSettingsResult result = processor.GetUnitSettings(configurationUnit); Assert.NotNull(result); Assert.NotNull(result.ResultInformation); Assert.Null(result.ResultInformation.ResultCode); Assert.Empty(result.ResultInformation.Description); Assert.NotNull(result.Settings); Assert.NotEmpty(result.Settings); Assert.Contains("key", result.Settings); Assert.IsType(result.Settings["key"]); Assert.Equal("value", (string)result.Settings["key"]); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorGroupTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Xunit; using Xunit.Abstractions; /// /// Unit tests for running group processing. /// [Collection("UnitTestCollection")] [InProc] public class ConfigurationProcessorGroupTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationProcessorGroupTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// Test a set that was parsed with schema 0.3. /// [Fact] public void TestSet_Parsed_0_3() { TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json resources: - name: Name type: Module/Resource properties: c: 3 d: '4' - name: Name2 type: Module/Resource2 properties: l: '10' ")); Assert.Null(openResult.ResultCode); Assert.NotNull(openResult.Set); Assert.Equal(string.Empty, openResult.Field); Assert.Equal(string.Empty, openResult.Value); Assert.Equal(0U, openResult.Line); Assert.Equal(0U, openResult.Column); ConfigurationSet configurationSet = openResult.Set; int unitCount = configurationSet.Units.Count; TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor[] unitProcessors = new TestConfigurationUnitProcessor[unitCount]; for (int i = 0; i < unitCount; ++i) { unitProcessors[i] = setProcessor.CreateTestProcessor(configurationSet.Units[i]); if (i == 0) { unitProcessors[i].TestSettingsDelegateWithUnit = (ConfigurationUnit unit) => new TestSettingsResultInstance(unit) { TestResult = ConfigurationTestResult.Negative }; } else { unitProcessors[i].TestSettingsDelegateWithUnit = (ConfigurationUnit unit) => new TestSettingsResultInstance(unit) { TestResult = ConfigurationTestResult.Positive }; } } TestConfigurationSetResult result = processor.TestSet(configurationSet); Assert.NotNull(result); Assert.Equal(ConfigurationTestResult.Negative, result.TestResult); Assert.Equal(unitCount, result.UnitResults.Count); for (int i = 0; i < unitCount; ++i) { var unitResult = result.UnitResults[i]; Assert.NotNull(unitResult); if (i == 0) { Assert.Equal(ConfigurationTestResult.Negative, unitResult.TestResult); } else { Assert.Equal(ConfigurationTestResult.Positive, unitResult.TestResult); } Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } this.VerifySummaryEvent(configurationSet, result, 0, ConfigurationUnitResultSource.None); } /// /// Test when the set processor is a group processor. /// [Fact] public void TestSet_SetGroupProcessor() { ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.Metadata[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnitNegative = this.ConfigurationUnit(); configurationUnitNegative.Settings[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnitPositive = this.ConfigurationUnit(); configurationUnitPositive.Settings[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Positive.ToString(); configurationSet.Units = new ConfigurationUnit[] { configurationUnitNegative, configurationUnitPositive }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetGroupProcessor setProcessor = factory.CreateTestGroupProcessor(configurationSet); setProcessor.ShouldWaitOnAsyncEvent = true; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); List progressValues = new List(); var operation = processor.TestSetAsync(configurationSet); operation.Progress = (Windows.Foundation.IAsyncOperationWithProgress op, TestConfigurationUnitResult unitResult) => { progressValues.Add(unitResult); }; setProcessor.SignalAsyncEvent(); operation.AsTask().Wait(); TestConfigurationSetResult result = operation.GetResults(); Assert.NotNull(result); Assert.Equal(ConfigurationTestResult.Negative, result.TestResult); Assert.NotNull(result.UnitResults); Assert.Equal(2, result.UnitResults.Count); Assert.Equal(2, progressValues.Count); TestConfigurationUnitResult negativeResult = result.UnitResults.First(x => x.Unit == configurationUnitNegative); TestConfigurationUnitResult negativeProgress = progressValues.First(x => x.Unit == configurationUnitNegative); foreach (TestConfigurationUnitResult unitResult in new TestConfigurationUnitResult[] { negativeResult, negativeProgress }) { Assert.NotNull(unitResult); Assert.Equal(ConfigurationTestResult.Negative, unitResult.TestResult); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } TestConfigurationUnitResult positiveResult = result.UnitResults.First(x => x.Unit == configurationUnitPositive); TestConfigurationUnitResult positiveProgress = progressValues.First(x => x.Unit == configurationUnitPositive); foreach (TestConfigurationUnitResult unitResult in new TestConfigurationUnitResult[] { positiveResult, positiveProgress }) { Assert.NotNull(unitResult); Assert.Equal(ConfigurationTestResult.Positive, unitResult.TestResult); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } this.VerifySummaryEvent(configurationSet, result, 0, ConfigurationUnitResultSource.None); } /// /// Test when the set processor is a group processor and contains a unit that is also a group. /// [Fact] public void TestSet_SetGroupProcessor_WithGroupUnit() { ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.Metadata[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnit = this.ConfigurationUnit(); ConfigurationUnit configurationUnitGroup = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnit, configurationUnitGroup }; ConfigurationUnit configurationUnitGroupMember = this.ConfigurationUnit(); configurationUnitGroup.IsGroup = true; configurationUnitGroup.Units = new ConfigurationUnit[] { configurationUnitGroupMember }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetGroupProcessor setProcessor = factory.CreateTestGroupProcessor(configurationSet); setProcessor.ShouldWaitOnAsyncEvent = true; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); List progressValues = new List(); var operation = processor.TestSetAsync(configurationSet); operation.Progress = (Windows.Foundation.IAsyncOperationWithProgress op, TestConfigurationUnitResult unitResult) => { progressValues.Add(unitResult); }; setProcessor.SignalAsyncEvent(); operation.AsTask().Wait(); TestConfigurationSetResult result = operation.GetResults(); Assert.NotNull(result); Assert.Equal(ConfigurationTestResult.Positive, result.TestResult); Assert.NotNull(result.UnitResults); Assert.Equal(3, result.UnitResults.Count); Assert.Equal(3, progressValues.Count); foreach (ConfigurationUnit unit in new ConfigurationUnit[] { configurationUnit, configurationUnitGroup, configurationUnitGroupMember }) { foreach (IReadOnlyList unitResults in new IReadOnlyList[] { result.UnitResults, progressValues }) { TestConfigurationUnitResult unitResult = unitResults.First(x => x.Unit == unit); Assert.NotNull(unitResult); Assert.Equal(ConfigurationTestResult.Positive, unitResult.TestResult); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } } this.VerifySummaryEvent(configurationSet, result, 0, ConfigurationUnitResultSource.None); } /// /// Test when the standard set processor is used and there is a group unit. /// [Fact] public void TestSet_UnitGroupProcessor_WithGroupUnit() { ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.Metadata[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnit = this.ConfigurationUnit(); ConfigurationUnit configurationUnitGroup = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnit, configurationUnitGroup }; ConfigurationUnit configurationUnitGroupMember = this.ConfigurationUnit(); configurationUnitGroup.IsGroup = true; configurationUnitGroup.Units = new ConfigurationUnit[] { configurationUnitGroupMember }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); setProcessor.CreateTestProcessor(configurationUnit); setProcessor.CreateTestGroupProcessor(configurationUnitGroup); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); TestConfigurationSetResult result = processor.TestSet(configurationSet); Assert.NotNull(result); Assert.Equal(ConfigurationTestResult.Positive, result.TestResult); Assert.NotNull(result.UnitResults); Assert.Equal(3, result.UnitResults.Count); foreach (ConfigurationUnit unit in new ConfigurationUnit[] { configurationUnit, configurationUnitGroup, configurationUnitGroupMember }) { TestConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == unit); Assert.NotNull(unitResult); Assert.Equal(ConfigurationTestResult.Positive, unitResult.TestResult); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } this.VerifySummaryEvent(configurationSet, result, 0, ConfigurationUnitResultSource.None); } /// /// Test when the standard set processor is used and there is a non-group unit that still exposes a group processor. /// [Fact] public void TestSet_UnitGroupProcessor_WithNonGroupUnit() { ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.Metadata[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnit = this.ConfigurationUnit(); ConfigurationUnit configurationUnitGroup = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnit, configurationUnitGroup }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); setProcessor.CreateTestProcessor(configurationUnit); setProcessor.CreateTestGroupProcessor(configurationUnitGroup); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); TestConfigurationSetResult result = processor.TestSet(configurationSet); Assert.NotNull(result); Assert.Equal(ConfigurationTestResult.Positive, result.TestResult); Assert.NotNull(result.UnitResults); Assert.Equal(2, result.UnitResults.Count); foreach (ConfigurationUnit unit in new ConfigurationUnit[] { configurationUnit, configurationUnitGroup }) { TestConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == unit); Assert.NotNull(unitResult); Assert.Equal(ConfigurationTestResult.Positive, unitResult.TestResult); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } this.VerifySummaryEvent(configurationSet, result, 0, ConfigurationUnitResultSource.None); } /// /// Apply a set that was parsed with schema 0.3. /// [Fact] public void ApplySet_Parsed_0_3() { TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json resources: - name: Name type: Module/Resource properties: c: 3 d: '4' - name: Name2 type: Module/Resource2 properties: l: '10' ")); Assert.Null(openResult.ResultCode); Assert.NotNull(openResult.Set); Assert.Equal(string.Empty, openResult.Field); Assert.Equal(string.Empty, openResult.Value); Assert.Equal(0U, openResult.Line); Assert.Equal(0U, openResult.Column); ConfigurationSet configurationSet = openResult.Set; int unitCount = configurationSet.Units.Count; TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor[] unitProcessors = new TestConfigurationUnitProcessor[unitCount]; for (int i = 0; i < unitCount; ++i) { unitProcessors[i] = setProcessor.CreateTestProcessor(configurationSet.Units[i]); if (i == 0) { unitProcessors[i].TestSettingsDelegateWithUnit = (ConfigurationUnit unit) => new TestSettingsResultInstance(unit) { TestResult = ConfigurationTestResult.Negative }; } else { unitProcessors[i].TestSettingsDelegateWithUnit = (ConfigurationUnit unit) => new TestSettingsResultInstance(unit) { TestResult = ConfigurationTestResult.Positive }; } } ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.Equal(unitCount, result.UnitResults.Count); for (int i = 0; i < unitCount; ++i) { var unitResult = result.UnitResults[i]; Assert.NotNull(unitResult); if (i == 0) { Assert.False(unitResult.PreviouslyInDesiredState); } else { Assert.True(unitResult.PreviouslyInDesiredState); } Assert.False(unitResult.RebootRequired); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); } for (int i = 0; i < unitCount; ++i) { Assert.Equal(1, unitProcessors[i].TestSettingsCalls); Assert.Equal(0, unitProcessors[i].GetSettingsCalls); if (i == 0) { Assert.Equal(1, unitProcessors[i].ApplySettingsCalls); } else { Assert.Equal(0, unitProcessors[i].ApplySettingsCalls); } } this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.None); } /// /// Test when the set processor is a group processor. /// [Fact] public void ApplySet_SetGroupProcessor() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitNegative = this.ConfigurationUnit(); configurationUnitNegative.Settings[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnitPositive = this.ConfigurationUnit(); configurationUnitPositive.Settings[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Positive.ToString(); configurationSet.Units = new ConfigurationUnit[] { configurationUnitNegative, configurationUnitPositive }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ManualResetEvent startProcessing = new ManualResetEvent(false); factory.CreateSetProcessorDelegate = (f, c) => { startProcessing.WaitOne(); return f.CreateTestGroupProcessor(c); }; var operation = processor.ApplySetAsync(configurationSet, ApplyConfigurationSetFlags.None); List progressValues = new List(); operation.Progress = (Windows.Foundation.IAsyncOperationWithProgress op, ConfigurationSetChangeData unitResult) => { progressValues.Add(unitResult); }; startProcessing.Set(); operation.AsTask().Wait(); ApplyConfigurationSetResult result = operation.GetResults(); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.NotNull(result.UnitResults); Assert.Equal(configurationSet.Units.Count, result.UnitResults.Count); Assert.Equal((configurationSet.Units.Count * 2) + 2, progressValues.Count); ApplyConfigurationUnitResult negativeResult = result.UnitResults.First(x => x.Unit == configurationUnitNegative); Assert.NotNull(negativeResult); Assert.NotNull(negativeResult.ResultInformation); Assert.Null(negativeResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, negativeResult.ResultInformation.ResultSource); Assert.Equal(ConfigurationUnitState.Completed, negativeResult.State); Assert.False(negativeResult.PreviouslyInDesiredState); IEnumerable negativeProgress = progressValues.Where(x => x.Unit == configurationUnitNegative); Assert.Equal(2, negativeProgress.Count()); foreach (ConfigurationSetChangeData change in negativeProgress) { Assert.Equal(ConfigurationSetChangeEventType.UnitStateChanged, change.Change); Assert.Equal(ConfigurationSetState.InProgress, change.SetState); Assert.NotNull(change.ResultInformation); Assert.Null(change.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, change.ResultInformation.ResultSource); } Assert.Single(negativeProgress.Where(x => x.UnitState == ConfigurationUnitState.InProgress)); Assert.Single(negativeProgress.Where(x => x.UnitState == ConfigurationUnitState.Completed)); ApplyConfigurationUnitResult positiveResult = result.UnitResults.First(x => x.Unit == configurationUnitPositive); Assert.NotNull(positiveResult); Assert.NotNull(positiveResult.ResultInformation); Assert.Null(positiveResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, positiveResult.ResultInformation.ResultSource); Assert.Equal(ConfigurationUnitState.Completed, positiveResult.State); Assert.True(positiveResult.PreviouslyInDesiredState); IEnumerable positiveProgress = progressValues.Where(x => x.Unit == configurationUnitPositive); Assert.Equal(2, positiveProgress.Count()); foreach (ConfigurationSetChangeData change in positiveProgress) { Assert.Equal(ConfigurationSetChangeEventType.UnitStateChanged, change.Change); Assert.Equal(ConfigurationSetState.InProgress, change.SetState); Assert.NotNull(change.ResultInformation); Assert.Null(change.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, change.ResultInformation.ResultSource); } Assert.Single(positiveProgress.Where(x => x.UnitState == ConfigurationUnitState.InProgress)); Assert.Single(positiveProgress.Where(x => x.UnitState == ConfigurationUnitState.Completed)); this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.None); } /// /// Test when the set processor is a group processor and contains a unit that is also a group. /// [Fact] public void ApplySet_SetGroupProcessor_WithGroupUnit() { ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.Metadata[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnit = this.ConfigurationUnit(); ConfigurationUnit configurationUnitGroup = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnit, configurationUnitGroup }; ConfigurationUnit configurationUnitGroupMember = this.ConfigurationUnit(); configurationUnitGroup.IsGroup = true; configurationUnitGroup.Units = new ConfigurationUnit[] { configurationUnitGroupMember }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ManualResetEvent startProcessing = new ManualResetEvent(false); factory.CreateSetProcessorDelegate = (f, c) => { startProcessing.WaitOne(); return f.CreateTestGroupProcessor(c); }; var operation = processor.ApplySetAsync(configurationSet, ApplyConfigurationSetFlags.None); List progressValues = new List(); operation.Progress = (Windows.Foundation.IAsyncOperationWithProgress op, ConfigurationSetChangeData unitResult) => { progressValues.Add(unitResult); }; startProcessing.Set(); operation.AsTask().Wait(); ApplyConfigurationSetResult result = operation.GetResults(); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.NotNull(result.UnitResults); Assert.Equal(3, result.UnitResults.Count); Assert.Equal(2 + (3 * 2), progressValues.Count); foreach (ConfigurationUnit unit in new ConfigurationUnit[] { configurationUnit, configurationUnitGroup, configurationUnitGroupMember }) { ApplyConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == unit); Assert.NotNull(unitResult); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); Assert.Equal(ConfigurationUnitState.Completed, unitResult.State); Assert.True(unitResult.PreviouslyInDesiredState); IEnumerable unitProgress = progressValues.Where(x => x.Unit == unit); Assert.Equal(2, unitProgress.Count()); foreach (ConfigurationSetChangeData change in unitProgress) { Assert.Equal(ConfigurationSetChangeEventType.UnitStateChanged, change.Change); Assert.Equal(ConfigurationSetState.InProgress, change.SetState); Assert.NotNull(change.ResultInformation); Assert.Null(change.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, change.ResultInformation.ResultSource); } Assert.True(unitProgress.Where(x => x.UnitState == ConfigurationUnitState.InProgress).Count() <= 1); Assert.True(unitProgress.Where(x => x.UnitState == ConfigurationUnitState.Completed).Count() <= 1); } this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.None); } /// /// Test when the standard set processor is used and there is a group unit. /// [Fact] public void ApplySet_UnitGroupProcessor_WithGroupUnit() { ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.Metadata[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnit = this.ConfigurationUnit(); ConfigurationUnit configurationUnitGroup = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnit, configurationUnitGroup }; ConfigurationUnit configurationUnitGroupMember = this.ConfigurationUnit(); configurationUnitGroup.IsGroup = true; configurationUnitGroup.Units = new ConfigurationUnit[] { configurationUnitGroupMember }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); setProcessor.CreateTestProcessor(configurationUnit); setProcessor.CreateTestGroupProcessor(configurationUnitGroup); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.NotNull(result.UnitResults); Assert.Equal(3, result.UnitResults.Count); foreach (ConfigurationUnit unit in new ConfigurationUnit[] { configurationUnit, configurationUnitGroup, configurationUnitGroupMember }) { ApplyConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == unit); Assert.NotNull(unitResult); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); Assert.Equal(ConfigurationUnitState.Completed, unitResult.State); Assert.True(unitResult.PreviouslyInDesiredState); } this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.None); } /// /// Test when the standard set processor is used and there is a non-group unit that still exposes a group processor. /// [Fact] public void ApplySet_UnitGroupProcessor_WithNonGroupUnit() { ConfigurationSet configurationSet = this.ConfigurationSet(); configurationSet.Metadata[TestConfigurationUnitGroupProcessor.TestResultSetting] = ConfigurationTestResult.Negative.ToString(); ConfigurationUnit configurationUnit = this.ConfigurationUnit(); ConfigurationUnit configurationUnitGroup = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnit, configurationUnitGroup }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); setProcessor.CreateTestProcessor(configurationUnit); setProcessor.CreateTestGroupProcessor(configurationUnitGroup); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); Assert.NotNull(result); Assert.Null(result.ResultCode); Assert.NotNull(result.UnitResults); Assert.Equal(2, result.UnitResults.Count); foreach (ConfigurationUnit unit in new ConfigurationUnit[] { configurationUnit, configurationUnitGroup }) { ApplyConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == unit); Assert.NotNull(unitResult); Assert.NotNull(unitResult.ResultInformation); Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); Assert.Equal(ConfigurationUnitState.Completed, unitResult.State); Assert.True(unitResult.PreviouslyInDesiredState); } this.VerifySummaryEvent(configurationSet, result, ConfigurationUnitResultSource.None); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorTelemetryTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.VisualBasic; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Xunit; using Xunit.Abstractions; using static System.Collections.Specialized.BitVector32; /// /// Unit tests for running test on the processor. /// [Collection("UnitTestCollection")] [InProc] public class ConfigurationProcessorTelemetryTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationProcessorTelemetryTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// No event is generated if the unit succeeds. /// [Fact] public void Telemetry_NoUnitEventOnSuccess() { TelemetryTestObjects testObjects = new TelemetryTestObjects(getFails: false); testObjects.Processor = this.CreateConfigurationProcessorWithDiagnostics(testObjects.Factory); testObjects.CreateDetails(); GetConfigurationUnitSettingsResult result = testObjects.Processor.GetUnitSettings(testObjects.Unit); Assert.Single(this.EventSink.Events); Assert.Equal(TelemetryEvent.ConfigUnitRunName, this.EventSink.Events[0].Name); } /// /// No event is generated if the details have not been retrieved. /// [Fact] public void Telemetry_NoUnitEventIfNoDetails() { TelemetryTestObjects testObjects = new TelemetryTestObjects(); testObjects.Processor = this.CreateConfigurationProcessorWithDiagnostics(testObjects.Factory); GetConfigurationUnitSettingsResult result = testObjects.Processor.GetUnitSettings(testObjects.Unit); Assert.Empty(this.EventSink.Events); } /// /// No event is generated if the module is not public. /// [Fact] public void Telemetry_NoUnitEventIfNotPublic() { TelemetryTestObjects testObjects = new TelemetryTestObjects(); testObjects.Processor = this.CreateConfigurationProcessorWithDiagnostics(testObjects.Factory); testObjects.CreateDetails(isPublic: false); GetConfigurationUnitSettingsResult result = testObjects.Processor.GetUnitSettings(testObjects.Unit); Assert.Empty(this.EventSink.Events); } /// /// The activity set by the caller is the value in the event. /// [Fact] public void Telemetry_ActivityID() { TelemetryTestObjects testObjects = new TelemetryTestObjects(); testObjects.Processor = this.CreateConfigurationProcessorWithDiagnostics(testObjects.Factory); testObjects.CreateDetails(); Guid activity = Guid.NewGuid(); testObjects.Processor.ActivityIdentifier = activity; GetConfigurationUnitSettingsResult result = testObjects.Processor.GetUnitSettings(testObjects.Unit); Assert.Single(this.EventSink.Events); Assert.Equal(TelemetryEvent.ConfigUnitRunName, this.EventSink.Events[0].Name); Assert.Equal(activity, this.EventSink.Events[0].ActivityID); } /// /// Disabling telemetry causes no event to be produced. /// /// The state of telemetry. [Theory] [InlineData(true)] [InlineData(false)] public void Telemetry_EnableState(bool state) { TelemetryTestObjects testObjects = new TelemetryTestObjects(); testObjects.Processor = this.CreateConfigurationProcessorWithDiagnostics(testObjects.Factory); testObjects.CreateDetails(); testObjects.Processor.GenerateTelemetryEvents = state; GetConfigurationUnitSettingsResult result = testObjects.Processor.GetUnitSettings(testObjects.Unit); if (state) { Assert.Single(this.EventSink.Events); Assert.Equal(TelemetryEvent.ConfigUnitRunName, this.EventSink.Events[0].Name); } else { Assert.Empty(this.EventSink.Events); } } /// /// The caller set by the caller is the value in the event. /// [Fact] public void Telemetry_Caller() { TelemetryTestObjects testObjects = new TelemetryTestObjects(); testObjects.Processor = this.CreateConfigurationProcessorWithDiagnostics(testObjects.Factory); testObjects.CreateDetails(); string caller = "TheTests"; testObjects.Processor.Caller = caller; GetConfigurationUnitSettingsResult result = testObjects.Processor.GetUnitSettings(testObjects.Unit); Assert.Single(this.EventSink.Events); Assert.Equal(TelemetryEvent.ConfigUnitRunName, this.EventSink.Events[0].Name); Assert.Equal(caller, this.EventSink.Events[0].Caller); } /// /// Verifies all of the telemetry fields that come from executing a specific unit. /// [Fact] public void Telemetry_UnitFields() { #pragma warning disable CS8602 // Dereference of a possibly null reference. TelemetryTestObjects testObjects = new TelemetryTestObjects(); testObjects.Processor = this.CreateConfigurationProcessorWithDiagnostics(testObjects.Factory); testObjects.CreateDetails(); testObjects.Unit.Type = "TestUnitName"; testObjects.UnitDetails.ModuleName = "TestModuleName"; string setting1 = "setting1"; string setting2 = "setting2"; testObjects.Unit.Settings.Add(setting1, 0); testObjects.Unit.Settings.Add(setting2, 0); GetConfigurationUnitSettingsResult result = testObjects.Processor.GetUnitSettings(testObjects.Unit); Assert.Single(this.EventSink.Events); TelemetryEvent runEvent = this.EventSink.Events[0]; Assert.Equal(TelemetryEvent.ConfigUnitRunName, runEvent.Name); Assert.NotEqual(string.Empty, runEvent.CodeVersion); Assert.NotEqual(Guid.Empty, runEvent.ActivityID); Assert.Equal(string.Empty, runEvent.Caller); Assert.Equal(Guid.Empty, Guid.Parse(runEvent.Properties[TelemetryEvent.SetID])); Assert.NotEqual(Guid.Empty, Guid.Parse(runEvent.Properties[TelemetryEvent.UnitID])); Assert.Equal(testObjects.Unit.Type, runEvent.Properties[TelemetryEvent.UnitName]); Assert.Equal(testObjects.UnitDetails.ModuleName, runEvent.Properties[TelemetryEvent.ModuleName]); Assert.Equal(((int)testObjects.Unit.Intent).ToString(), runEvent.Properties[TelemetryEvent.UnitIntent]); Assert.Equal(((int)ConfigurationUnitIntent.Inform).ToString(), runEvent.Properties[TelemetryEvent.RunIntent]); Assert.NotEqual(string.Empty, runEvent.Properties[TelemetryEvent.Action]); Assert.Equal(testObjects.GetResult.ResultInformation.ResultCode.HResult.ToString(), runEvent.Properties[TelemetryEvent.Result]); Assert.Equal(((int)testObjects.GetResult.ResultInformation.ResultSource).ToString(), runEvent.Properties[TelemetryEvent.FailurePoint]); Assert.Equal(setting1 + "|" + setting2, runEvent.Properties[TelemetryEvent.SettingsProvided]); #pragma warning restore CS8602 // Dereference of a possibly null reference. } private class TelemetryTestObjects { public TelemetryTestObjects(bool getFails = true) { this.Unit = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Apply }; this.Factory = new TestConfigurationProcessorFactory(); this.Factory.NullProcessor = new TestConfigurationSetProcessor(null); this.UnitProcessor = this.Factory.NullProcessor.CreateTestProcessor(this.Unit); if (getFails) { var getResult = new GetSettingsResultInstance(this.Unit); getResult.InternalResult.ResultCode = new NullReferenceException(); getResult.InternalResult.ResultSource = ConfigurationUnitResultSource.UnitProcessing; this.GetResult = getResult; this.UnitProcessor.GetSettingsDelegate = () => this.GetResult; } } public ConfigurationUnit Unit { get; set; } public TestConfigurationProcessorFactory Factory { get; set; } public TestConfigurationUnitProcessor UnitProcessor { get; set; } public IGetSettingsResult? GetResult { get; set; } public TestConfigurationUnitProcessorDetails? UnitDetails { get; set; } public ConfigurationProcessor? Processor { get; set; } public void CreateDetails(bool isPublic = true) { #pragma warning disable CS8602 // Dereference of a possibly null reference. this.UnitDetails = this.Factory.NullProcessor.CreateUnitDetails(this.Unit, ConfigurationUnitDetailFlags.ReadOnly); #pragma warning restore CS8602 // Dereference of a possibly null reference. this.UnitDetails.IsPublic = isPublic; this.Processor?.GetUnitDetails(this.Unit, ConfigurationUnitDetailFlags.ReadOnly); } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorTestTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.IO; using System.Linq; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.VisualBasic; using Xunit; using Xunit.Abstractions; /// /// Unit tests for running test on the processor. /// [Collection("UnitTestCollection")] [InProc] [OutOfProc] public class ConfigurationProcessorTestTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationProcessorTestTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// An error creating the set processor results in an error for the function. /// [Fact] public void TestSet_SetProcessorError() { ConfigurationSet configurationSet = this.ConfigurationSet(); TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); factory.Exceptions.Add(configurationSet, new FileNotFoundException()); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); Assert.Throws(() => processor.TestSet(configurationSet)); Assert.Empty(this.EventSink.Events); } /// /// An error creating a unit processor results in an error for that unit. /// [Fact] public void TestSet_UnitProcessorCreationError() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitThrows = this.ConfigurationUnit(); ConfigurationUnit configurationUnitWorks = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnitThrows, configurationUnitWorks }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); setProcessor.Exceptions.Add(configurationUnitThrows, new NullReferenceException()); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); TestConfigurationSetResult result = processor.TestSet(configurationSet); Assert.NotNull(result); Assert.Equal(ConfigurationTestResult.Failed, result.TestResult); Assert.NotNull(result.UnitResults); Assert.Equal(2, result.UnitResults.Count); TestConfigurationUnitResult throwsResult = result.UnitResults.First(x => x.Unit == configurationUnitThrows); Assert.NotNull(throwsResult); Assert.Equal(ConfigurationTestResult.Failed, throwsResult.TestResult); Assert.NotNull(throwsResult.ResultInformation); Assert.NotNull(throwsResult.ResultInformation.ResultCode); Assert.IsType(throwsResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.Internal, throwsResult.ResultInformation.ResultSource); TestConfigurationUnitResult worksResult = result.UnitResults.First(x => x.Unit == configurationUnitWorks); Assert.NotNull(worksResult); Assert.Equal(ConfigurationTestResult.Positive, worksResult.TestResult); Assert.NotNull(worksResult.ResultInformation); Assert.Null(worksResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, worksResult.ResultInformation.ResultSource); this.VerifySummaryEvent(configurationSet, result, throwsResult.ResultInformation.ResultCode.HResult, ConfigurationUnitResultSource.Internal); } /// /// An error running a unit processor results in an error for that unit. /// [Fact] public void TestSet_UnitProcessorExecutionError() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitThrows = this.ConfigurationUnit(); ConfigurationUnit configurationUnitWorks = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnitWorks, configurationUnitThrows }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnitThrows); unitProcessor.TestSettingsDelegate = () => throw new NullReferenceException(); ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); TestConfigurationSetResult result = processor.TestSet(configurationSet); Assert.NotNull(result); Assert.Equal(ConfigurationTestResult.Failed, result.TestResult); Assert.NotNull(result.UnitResults); Assert.Equal(2, result.UnitResults.Count); TestConfigurationUnitResult throwsResult = result.UnitResults.First(x => x.Unit == configurationUnitThrows); Assert.NotNull(throwsResult); Assert.Equal(ConfigurationTestResult.Failed, throwsResult.TestResult); Assert.NotNull(throwsResult.ResultInformation); Assert.NotNull(throwsResult.ResultInformation.ResultCode); Assert.IsType(throwsResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.Internal, throwsResult.ResultInformation.ResultSource); TestConfigurationUnitResult worksResult = result.UnitResults.First(x => x.Unit == configurationUnitWorks); Assert.NotNull(worksResult); Assert.Equal(ConfigurationTestResult.Positive, worksResult.TestResult); Assert.NotNull(worksResult.ResultInformation); Assert.Null(worksResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, worksResult.ResultInformation.ResultSource); this.VerifySummaryEvent(configurationSet, result, throwsResult.ResultInformation.ResultCode.HResult, ConfigurationUnitResultSource.Internal); } /// /// An error running a unit processor results in an error for that unit. /// [Fact] public void TestSet_UnitProcessorResultError() { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitThrows = this.ConfigurationUnit(); ConfigurationUnit configurationUnitWorks = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnitWorks, configurationUnitThrows }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnitThrows); TestSettingsResultInstance testResult = new TestSettingsResultInstance(configurationUnitThrows); testResult.TestResult = ConfigurationTestResult.Failed; testResult.InternalResult.ResultCode = new NullReferenceException(); testResult.InternalResult.Description = "Failed again"; testResult.InternalResult.ResultSource = ConfigurationUnitResultSource.UnitProcessing; unitProcessor.TestSettingsDelegate = () => testResult; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); TestConfigurationSetResult result = processor.TestSet(configurationSet); Assert.NotNull(result); Assert.Equal(ConfigurationTestResult.Failed, result.TestResult); Assert.NotNull(result.UnitResults); Assert.Equal(2, result.UnitResults.Count); TestConfigurationUnitResult throwsResult = result.UnitResults.First(x => x.Unit == configurationUnitThrows); Assert.NotNull(throwsResult); Assert.Equal(ConfigurationTestResult.Failed, throwsResult.TestResult); Assert.NotNull(throwsResult.ResultInformation); Assert.NotNull(throwsResult.ResultInformation.ResultCode); Assert.IsType(throwsResult.ResultInformation.ResultCode); Assert.Equal(testResult.ResultInformation.Description, throwsResult.ResultInformation.Description); Assert.Equal(testResult.ResultInformation.ResultSource, throwsResult.ResultInformation.ResultSource); TestConfigurationUnitResult worksResult = result.UnitResults.First(x => x.Unit == configurationUnitWorks); Assert.NotNull(worksResult); Assert.Equal(ConfigurationTestResult.Positive, worksResult.TestResult); Assert.NotNull(worksResult.ResultInformation); Assert.Null(worksResult.ResultInformation.ResultCode); Assert.Equal(ConfigurationUnitResultSource.None, worksResult.ResultInformation.ResultSource); this.VerifySummaryEvent(configurationSet, result, testResult.ResultInformation.ResultCode.HResult, testResult.ResultInformation.ResultSource); } /// /// Ensures that the expected TestResult comes back for all types. /// [Fact] public void TestSet_ResultTypes() { this.RunTestSetTestForResultTypes(new ConfigurationTestResult[] { ConfigurationTestResult.Positive, ConfigurationTestResult.Negative, ConfigurationTestResult.Failed, ConfigurationTestResult.NotRun }, ConfigurationTestResult.Failed); } /// /// Ensures that a single negative makes the overall result negative. /// [Fact] public void TestSet_Negative() { this.RunTestSetTestForResultTypes(new ConfigurationTestResult[] { ConfigurationTestResult.Positive, ConfigurationTestResult.Negative, ConfigurationTestResult.Positive, ConfigurationTestResult.NotRun }, ConfigurationTestResult.Negative); } /// /// Ensures that a single not run test does not impact the positive result. /// [Fact] public void TestSet_Positive() { this.RunTestSetTestForResultTypes(new ConfigurationTestResult[] { ConfigurationTestResult.Positive, ConfigurationTestResult.Positive, ConfigurationTestResult.Positive, ConfigurationTestResult.NotRun }, ConfigurationTestResult.Positive); } private TestSettingsResultInstance PositiveResult(ConfigurationUnit unit) { TestSettingsResultInstance positiveResult = new TestSettingsResultInstance(unit); positiveResult.TestResult = ConfigurationTestResult.Positive; return positiveResult; } private TestSettingsResultInstance NegativeResult(ConfigurationUnit unit) { TestSettingsResultInstance negativeResult = new TestSettingsResultInstance(unit); negativeResult.TestResult = ConfigurationTestResult.Negative; return negativeResult; } private TestSettingsResultInstance FailedResult(ConfigurationUnit unit, string description, ConfigurationUnitResultSource resultSource) { TestSettingsResultInstance failedResult = new TestSettingsResultInstance(unit); failedResult.TestResult = ConfigurationTestResult.Failed; failedResult.InternalResult.ResultCode = new NullReferenceException(); failedResult.InternalResult.Description = description; failedResult.InternalResult.ResultSource = resultSource; return failedResult; } /// /// Creates a test scenario where the units produce the given test results. /// /// The result types for each unit. /// The expected overall test result. private void RunTestSetTestForResultTypes(ConfigurationTestResult[] resultTypes, ConfigurationTestResult overallResult) { ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit[] configurationUnits = new ConfigurationUnit[resultTypes.Length]; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); string failedDescription = "Failed again"; ConfigurationUnitResultSource failedResultSource = ConfigurationUnitResultSource.UnitProcessing; for (int i = 0; i < resultTypes.Length; ++i) { configurationUnits[i] = this.ConfigurationUnit(); configurationUnits[i].Type = $"Unit {i}"; TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnits[i]); switch (resultTypes[i]) { case ConfigurationTestResult.Positive: unitProcessor.TestSettingsDelegateWithUnit = (ConfigurationUnit unit) => this.PositiveResult(unit); break; case ConfigurationTestResult.Negative: unitProcessor.TestSettingsDelegateWithUnit = (ConfigurationUnit unit) => this.NegativeResult(unit); break; case ConfigurationTestResult.NotRun: configurationUnits[i].Intent = ConfigurationUnitIntent.Inform; break; case ConfigurationTestResult.Failed: unitProcessor.TestSettingsDelegateWithUnit = (ConfigurationUnit unit) => this.FailedResult(unit, failedDescription, failedResultSource); break; } } configurationSet.Units = configurationUnits; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); TestConfigurationSetResult result = processor.TestSet(configurationSet); Assert.NotNull(result); Assert.Equal(overallResult, result.TestResult); Assert.NotNull(result.UnitResults); Assert.Equal(resultTypes.Length, result.UnitResults.Count); int summaryEventResult = 0; ConfigurationUnitResultSource resultSource = ConfigurationUnitResultSource.None; for (int i = 0; i < resultTypes.Length; ++i) { TestConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == configurationUnits[i]); Assert.NotNull(unitResult); Assert.Equal(resultTypes[i], unitResult.TestResult); Assert.NotNull(unitResult.ResultInformation); switch (resultTypes[i]) { case ConfigurationTestResult.Positive: case ConfigurationTestResult.Negative: case ConfigurationTestResult.NotRun: Assert.Null(unitResult.ResultInformation.ResultCode); Assert.Empty(unitResult.ResultInformation.Description); Assert.Equal(ConfigurationUnitResultSource.None, unitResult.ResultInformation.ResultSource); break; case ConfigurationTestResult.Failed: Assert.NotNull(unitResult.ResultInformation.ResultCode); Assert.IsType(unitResult.ResultInformation.ResultCode); Assert.Equal(failedDescription, unitResult.ResultInformation.Description); Assert.Equal(failedResultSource, unitResult.ResultInformation.ResultSource); summaryEventResult = unitResult.ResultInformation.ResultCode.HResult; resultSource = unitResult.ResultInformation.ResultSource; break; } } this.VerifySummaryEvent(configurationSet, result, summaryEventResult, resultSource); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetAuthoringTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.VisualBasic; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; using Windows.Foundation.Collections; using Windows.Storage.Streams; using Xunit; using Xunit.Abstractions; /// /// Unit tests for configuration set authoring (creating objects). /// [Collection("UnitTestCollection")] [InProc] [OutOfProc] public class ConfigurationSetAuthoringTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationSetAuthoringTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// Creates a configuration set and sets all available properties. /// [Fact] public void ConfigurationSetAndProperties() { string testName = "Test Name"; string testOrigin = "Test Origin"; string testPath = "TestPath.ext"; ConfigurationSet testSet = this.ConfigurationSet(); testSet.Name = testName; Assert.Equal(testName, testSet.Name); testSet.Origin = testOrigin; Assert.Equal(testOrigin, testSet.Origin); testSet.Path = testPath; Assert.Equal(testPath, testSet.Path); Assert.NotEqual(Guid.Empty, testSet.InstanceIdentifier); Assert.Equal(ConfigurationSetState.Unknown, testSet.State); Assert.Empty(testSet.Units); testSet.Units = new ConfigurationUnit[] { this.ConfigurationUnit() }; Assert.Equal(1, testSet.Units.Count); Assert.NotEqual(string.Empty, testSet.SchemaVersion); } /// /// Creates a configuration unit and sets all available properties. /// [Fact] public void ConfigurationUnitAndProperties() { string testName = "Test Name"; string testIdentifier = "Test Identifier"; ConfigurationUnitIntent testIntent = ConfigurationUnitIntent.Assert; ConfigurationUnit testUnit = this.ConfigurationUnit(); testUnit.Type = testName; Assert.Equal(testName, testUnit.Type); testUnit.Identifier = testIdentifier; Assert.Equal(testIdentifier, testUnit.Identifier); Assert.NotEqual(Guid.Empty, testUnit.InstanceIdentifier); Assert.Equal(ConfigurationUnitIntent.Apply, testUnit.Intent); testUnit.Intent = testIntent; Assert.Equal(testIntent, testUnit.Intent); Assert.Empty(testUnit.Dependencies); testUnit.Dependencies = new string[] { "dependency1", "dependency2" }; Assert.Equal(2, testUnit.Dependencies.Count); Assert.Empty(testUnit.Metadata); Assert.Empty(testUnit.Settings); Assert.Null(testUnit.Details); Assert.Equal(ConfigurationUnitState.Unknown, testUnit.State); Assert.Null(testUnit.ResultInformation); Assert.True(testUnit.IsActive); testUnit.IsActive = false; Assert.False(testUnit.IsActive); } /// /// Basic sanity check to verify that nested value sets can be serialized successfully. /// [Fact] public void ConfigurationSetSerializeNestedValueSets() { ConfigurationSet testSet = this.ConfigurationSet(); testSet.SchemaVersion = "0.2"; ConfigurationUnit testUnit = this.ConfigurationUnit(); string testName = "Test Name"; string testIdentifier = "Test Identifier"; testUnit.Type = testName; testUnit.Identifier = testIdentifier; ValueSet innerValueSet = new ValueSet(); innerValueSet.Add("innerKey", "innerValue"); ValueSet outerValueSet = new ValueSet(); outerValueSet.Add("outerKey", innerValueSet); testUnit.Metadata = outerValueSet; testSet.Units.Add(testUnit); InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); testSet.Serialize(stream); string yamlOutput = this.ReadStream(stream); Assert.NotNull(yamlOutput); } /// /// Test for unique unit environment calculation. /// [Fact] public void ConfigurationSet_UnitEnvironments() { ConfigurationSet testSet = this.ConfigurationSet(); Dictionary firstProperty = new Dictionary(); firstProperty.Add("property", "value1"); Dictionary secondProperty = new Dictionary(); secondProperty.Add("property", "value2"); Helpers.ConfigurationEnvironmentData[] environments = new Helpers.ConfigurationEnvironmentData[] { new () { ProcessorIdentifier = "dscv3" }, new () { ProcessorIdentifier = "pwsh" }, new () { ProcessorIdentifier = "dscv3", Context = SecurityContext.Elevated }, new () { ProcessorIdentifier = "pwsh", Context = SecurityContext.Restricted }, new () { ProcessorIdentifier = "dscv3", ProcessorProperties = firstProperty }, new () { ProcessorIdentifier = "pwsh", ProcessorProperties = firstProperty }, new () { ProcessorIdentifier = "pwsh", ProcessorProperties = secondProperty }, new () { ProcessorIdentifier = "dscv3", Context = SecurityContext.Restricted, ProcessorProperties = firstProperty }, new () { ProcessorIdentifier = "pwsh", Context = SecurityContext.Elevated, ProcessorProperties = firstProperty }, }; foreach (int index in new int[] { 0, 1, 1, 2, 3, 5, 4, 6, 7, 8, 2, 7, 7, 7 }) { Assert.True(index < environments.Length); testSet.Units.Add(environments[index].ApplyToUnit(this.ConfigurationUnit())); } var uniqueEnvironments = testSet.GetUnitEnvironments(); this.EnsureEnvironmentEquivalence(environments, uniqueEnvironments); } /// /// Test for unique unit environment calculation with group units. /// [Fact] public void ConfigurationSet_GroupUnitEnvironments() { ConfigurationSet testSet = this.ConfigurationSet(); Dictionary firstProperty = new Dictionary(); firstProperty.Add("property", "value1"); Dictionary secondProperty = new Dictionary(); secondProperty.Add("property", "value2"); Helpers.ConfigurationEnvironmentData[] environments = new Helpers.ConfigurationEnvironmentData[] { new () { ProcessorIdentifier = "dscv3" }, new () { ProcessorIdentifier = "pwsh" }, new () { ProcessorIdentifier = "dscv3", Context = SecurityContext.Elevated }, new () { ProcessorIdentifier = "pwsh", Context = SecurityContext.Restricted }, new () { ProcessorIdentifier = "dscv3", ProcessorProperties = firstProperty }, new () { ProcessorIdentifier = "pwsh", ProcessorProperties = firstProperty }, new () { ProcessorIdentifier = "pwsh", ProcessorProperties = secondProperty }, new () { ProcessorIdentifier = "dscv3", Context = SecurityContext.Restricted, ProcessorProperties = firstProperty }, new () { ProcessorIdentifier = "pwsh", Context = SecurityContext.Elevated, ProcessorProperties = firstProperty }, new (), // The default environment for the group unit }; foreach (int index in new int[] { 0, 1, 1, 3, 5, 4, 6, 8 }) { Assert.True(index < environments.Length); testSet.Units.Add(environments[index].ApplyToUnit(this.ConfigurationUnit())); } var groupUnit = this.ConfigurationUnit(); groupUnit.IsGroup = true; foreach (int index in new int[] { 7, 5, 2 }) { Assert.True(index < environments.Length); groupUnit.Units.Add(environments[index].ApplyToUnit(this.ConfigurationUnit())); } testSet.Units.Add(groupUnit); var uniqueEnvironments = testSet.GetUnitEnvironments(); this.EnsureEnvironmentEquivalence(environments, uniqueEnvironments); } private void EnsureEnvironmentEquivalence(Helpers.ConfigurationEnvironmentData[] expectedEnvironments, IList? actualEnvironments) { Assert.NotNull(actualEnvironments); Assert.Equal(expectedEnvironments.Length, actualEnvironments.Count); bool[] foundEnvironments = new bool[expectedEnvironments.Length]; foreach (var actual in actualEnvironments) { for (int i = 0; i < expectedEnvironments.Length; i++) { var expected = expectedEnvironments[i]; if (actual.Context == expected.Context && actual.ProcessorIdentifier == expected.ProcessorIdentifier && expected.PropertiesEqual(actual.ProcessorProperties)) { foundEnvironments[i] = true; break; } } } for (int i = 0; i < foundEnvironments.Length; i++) { Assert.True(foundEnvironments[i], $"Found expected environment: {i}"); } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetProcessorTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using System.Management.Automation; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments; using Microsoft.Management.Configuration.Processor.PowerShell.Set; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.PowerShell.Commands; using Moq; using Windows.Foundation.Collections; using Windows.Security.Cryptography.Certificates; using Xunit; using Xunit.Abstractions; /// /// Unit tests for configuration processor tests. /// [Collection("UnitTestCollection")] [InProc] public class ConfigurationSetProcessorTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationSetProcessorTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Test CreateUnitProcessor. Happy path. /// [Fact] public void CreateUnitProcessor_ResourceExists() { string resourceName = "xResourceName"; string moduleName = "xModuleName"; Version version = new Version("1.0"); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(new DscResourceInfoInternal(resourceName, moduleName, version)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; unit.Metadata.Add("module", moduleName); unit.Metadata.Add("version", version.ToString()); var unitProcessor = configurationSetProcessor.CreateUnitProcessor(unit); Assert.NotNull(unitProcessor); Assert.Equal(unit.Type, unitProcessor.Unit.Type); processorEnvMock.Verify(); } /// /// Test CreateUnitProcessor case-insensitive. /// [Fact] public void CreateUnitProcessor_CaseInsensitive() { string resourceName = "name"; string moduleName = "xModuleName"; Version version = new Version("1.0"); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type.Equals("Name", StringComparison.OrdinalIgnoreCase)))) .Returns(new DscResourceInfoInternal("Name", moduleName, version)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; unit.Metadata.Add("module", moduleName); unit.Metadata.Add("version", version.ToString()); var unitProcessor = configurationSetProcessor.CreateUnitProcessor(unit); Assert.NotNull(unitProcessor); Assert.Equal(unit.Type, unitProcessor.Unit.Type); processorEnvMock.Verify(); } /// /// Test CreateUnitProcessor case-insensitive. /// [Fact] public void CreateUnitProcessor_ResourceNameMismatch() { string resourceName = "name"; string moduleName = "xModuleName"; Version version = new Version("1.0"); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(new DscResourceInfoInternal("OtherName", moduleName, version)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; unit.Metadata.Add("module", moduleName); unit.Metadata.Add("version", version.ToString()); Assert.Throws(() => configurationSetProcessor.CreateUnitProcessor(unit)); processorEnvMock.Verify(); } /// /// Test CreateUnitProcessor with no version directive. /// [Fact] public void CreateUnitProcessor_ResourceExists_NoVersionDirective() { string resourceName = "xResourceName"; string moduleName = "xModuleName"; Version version = new Version("1.0.0.0"); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(new DscResourceInfoInternal(resourceName, moduleName, version)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; unit.Metadata.Add("module", moduleName); var unitProcessor = configurationSetProcessor.CreateUnitProcessor(unit); Assert.NotNull(unitProcessor); Assert.Equal(unit.Type, unitProcessor.Unit.Type); processorEnvMock.Verify(); } /// /// Test CreateUnitProcessor with no module directive. /// [Fact] public void CreateUnitProcessor_ResourceExists_NoModuleDirective() { string resourceName = "xResourceName"; string moduleName = "xModuleName"; Version version = new Version("1.0"); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(new DscResourceInfoInternal(resourceName, moduleName, version)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; var unitProcessor = configurationSetProcessor.CreateUnitProcessor(unit); Assert.NotNull(unitProcessor); Assert.Equal(unit.Type, unitProcessor.Unit.Type); processorEnvMock.Verify(); } /// /// Tests Creating a unit processor by downloading the resource. /// [Fact] public void CreateUnitProcessor_InstallResource() { string resourceName = "xResourceName"; string moduleName = "xModuleName"; Version version = new Version("1.0"); DscResourceInfoInternal? nullResource = null; DscResourceInfoInternal dscResourceInfo = new DscResourceInfoInternal(resourceName, moduleName, version); var processorEnvMock = new Mock(); processorEnvMock.SetupSequence( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(nullResource) .Returns(dscResourceInfo); PSObject findDscResourceResult = new PSObject(processorEnvMock); processorEnvMock.Setup( m => m.FindModule(It.Is(c => c.Unit.Type == resourceName))) .Returns(findDscResourceResult) .Verifiable(); processorEnvMock.Setup( m => m.InstallModule(findDscResourceResult)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; unit.Metadata.Add("module", moduleName); unit.Metadata.Add("version", version.ToString()); var unitProcessor = configurationSetProcessor.CreateUnitProcessor(unit); Assert.NotNull(unitProcessor); Assert.Equal(unit.Type, unitProcessor.Unit.Type); processorEnvMock.Verify(); } /// /// Tests Creating a unit processor by downloading the resource. /// [Fact] public void CreateUnitProcessor_InstallResource_WithoutModule() { string resourceName = "SimpleFileResource"; Version version = new Version("0.0.0.1"); DscResourceInfoInternal? nullResource = null; DscResourceInfoInternal dscResourceInfo = new DscResourceInfoInternal(resourceName, null, version); var processorEnvMock = new Mock(); processorEnvMock.SetupSequence( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(nullResource) .Returns(dscResourceInfo); PSObject findDscResourceResult = this.CreateFindResourceInfo(); processorEnvMock.Setup( m => m.FindDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(findDscResourceResult) .Verifiable(); PSObject moduleInfo = ((dynamic)findDscResourceResult).PSGetModuleInfo; processorEnvMock.Setup( m => m.InstallModule(moduleInfo)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; unit.Metadata.Add("version", version.ToString()); var unitProcessor = configurationSetProcessor.CreateUnitProcessor(unit); Assert.NotNull(unitProcessor); Assert.Equal(unit.Type, unitProcessor.Unit.Type); processorEnvMock.Verify(); } /// /// Tests Creating a unit processor by downloading the resource. /// [Fact] public void CreateUnitProcessor_InstallResource_NotFoundAfterInstall() { string resourceName = "xResourceName"; string moduleName = "xModuleName"; Version version = new Version("1.0"); DscResourceInfoInternal? nullResource = null; DscResourceInfoInternal dscResourceInfo = new DscResourceInfoInternal(resourceName, moduleName, version); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(nullResource); PSObject findDscResourceResult = new PSObject(processorEnvMock); processorEnvMock.Setup( m => m.FindModule(It.Is(c => c.Unit.Type == resourceName))) .Returns(findDscResourceResult) .Verifiable(); processorEnvMock.Setup( m => m.InstallModule(findDscResourceResult)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; unit.Metadata.Add("module", moduleName); unit.Metadata.Add("version", version.ToString()); Assert.Throws( () => configurationSetProcessor.CreateUnitProcessor(unit)); processorEnvMock.Verify(); } /// /// Tests Creating a unit processor by downloading the resource. /// [Fact] public void CreateUnitProcessor_InstallResource_NotFound() { string resourceName = "xResourceName"; string moduleName = "xModuleName"; Version version = new Version("1.0"); DscResourceInfoInternal? nullResource = null; DscResourceInfoInternal dscResourceInfo = new DscResourceInfoInternal(resourceName, moduleName, version); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(nullResource); PSObject? findDscResourceResult = null; processorEnvMock.Setup( m => m.FindModule(It.Is(c => c.Unit.Type == resourceName))) .Returns(findDscResourceResult) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = resourceName, }; unit.Metadata.Add("module", moduleName); unit.Metadata.Add("version", version.ToString()); Assert.Throws( () => configurationSetProcessor.CreateUnitProcessor(unit)); processorEnvMock.Verify(); } /// /// Test GetUnitProcessorDetails Local Resource not found. /// [Fact] public void GetUnitProcessorDetails_Local_NoFound() { string resourceName = "xResource"; DscResourceInfoInternal? nullDscInfoInternal = null; var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(u => u.Unit.Type == resourceName))) .Returns(nullDscInfoInternal) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var unit = new ConfigurationUnit() { Type = resourceName, }; var configurationUnitProcessorDetails = configurationSetProcessor.GetUnitProcessorDetails( unit, ConfigurationUnitDetailFlags.Local); Assert.Null(configurationUnitProcessorDetails); processorEnvMock.Verify(); } /// /// Test GetUnitProcessorDetails Local Found. Module not installed by PowerShellGet. /// [Fact] public void GetUnitProcessorDetails_Local_NotInstalledByPowerShellGet() { var unit = this.CreateConfigurationUnit(); var (dscResourceInfo, psModuleInfo) = this.GetResourceAndModuleInfo(unit); PSObject? nullPsModuleInfo = null; var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(u => u.Unit.Type == dscResourceInfo.Name))) .Returns(dscResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetAvailableModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Returns(psModuleInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetInstalledModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Returns(nullPsModuleInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetCertsOfValidSignedFiles(It.IsAny())) .Returns(new List()) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var configurationUnitProcessorDetails = configurationSetProcessor.GetUnitProcessorDetails( unit, ConfigurationUnitDetailFlags.Local); Assert.NotNull(configurationUnitProcessorDetails); Assert.Equal(dscResourceInfo.Name, configurationUnitProcessorDetails.UnitType); processorEnvMock.Verify(); } /// /// Test GetUnitProcessorDetails locally found. Do not include Load. /// /// Detail flags. [Theory] [InlineData(ConfigurationUnitDetailFlags.Local)] [InlineData(ConfigurationUnitDetailFlags.ReadOnly)] [InlineData(ConfigurationUnitDetailFlags.Download)] public void GetUnitProcessorDetails_Local(object detailFlags) { var unit = this.CreateConfigurationUnit(); var (dscResourceInfo, psModuleInfo) = this.GetResourceAndModuleInfo(unit); var getModuleInfo = this.CreateGetModuleInfo(); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(u => u.Unit.Type == dscResourceInfo.Name))) .Returns(dscResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetAvailableModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Returns(psModuleInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetInstalledModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Returns(getModuleInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetCertsOfValidSignedFiles(It.IsAny())) .Returns(new List()) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var configurationUnitProcessorDetails = configurationSetProcessor.GetUnitProcessorDetails( unit, Assert.IsType(detailFlags)); Assert.NotNull(configurationUnitProcessorDetails); Assert.Equal(dscResourceInfo.Name, configurationUnitProcessorDetails.UnitType); processorEnvMock.Verify(); processorEnvMock.Verify(m => m.FindDscResource(It.IsAny()), Times.Never()); processorEnvMock.Verify(m => m.ImportModule(It.IsAny()), Times.Never()); } /// /// Test GetUnitProcessorDetails locally found and load. /// [Fact] public void GetUnitProcessorDetails_Local_Load() { var unit = this.CreateConfigurationUnit(); var (dscResourceInfo, psModuleInfo) = this.GetResourceAndModuleInfo(unit); var getModuleInfo = this.CreateGetModuleInfo(); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(u => u.Unit.Type == dscResourceInfo.Name))) .Returns(dscResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetAvailableModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Returns(psModuleInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetInstalledModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Returns(getModuleInfo) .Verifiable(); processorEnvMock.Setup( m => m.ImportModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Verifiable(); processorEnvMock.Setup( m => m.GetCertsOfValidSignedFiles(It.IsAny())) .Returns(new List()) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var configurationUnitProcessorDetails = configurationSetProcessor.GetUnitProcessorDetails( unit, ConfigurationUnitDetailFlags.Load); Assert.NotNull(configurationUnitProcessorDetails); Assert.Equal(dscResourceInfo.Name, configurationUnitProcessorDetails.UnitType); processorEnvMock.Verify(); processorEnvMock.Verify(m => m.FindDscResource(It.IsAny()), Times.Never()); } /// /// Test GetUnitProcessorDetails Catalog Not Found. /// [Fact] public void GetUnitProcessorDetails_Catalog_NotFound() { var unit = this.CreateConfigurationUnit(); DscResourceInfoInternal? nullDscResourceInfo = null; PSObject? nullPsModuleInfo = null; var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(u => u.Unit.Type == unit.Type))) .Returns(nullDscResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.FindModule(It.Is(c => unit.Type == unit.Type))) .Returns(nullPsModuleInfo) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var configurationUnitProcessorDetails = configurationSetProcessor.GetUnitProcessorDetails( unit, ConfigurationUnitDetailFlags.ReadOnly); Assert.Null(configurationUnitProcessorDetails); processorEnvMock.Verify(); } /// /// Test GetUnitProcessorDetails Catalog Found. /// [Fact] public void GetUnitProcessorDetails_Catalog() { var unit = this.CreateConfigurationUnit(); DscResourceInfoInternal? nullDscResourceInfo = null; var getFindResourceInfo = this.CreateFindResourceInfo(); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(u => u.Unit.Type == unit.Type))) .Returns(nullDscResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.FindModule(It.Is(c => unit.Type == unit.Type))) .Returns(getFindResourceInfo) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var configurationUnitProcessorDetails = configurationSetProcessor.GetUnitProcessorDetails( unit, ConfigurationUnitDetailFlags.ReadOnly); Assert.NotNull(configurationUnitProcessorDetails); Assert.Equal("SimpleFileResource", configurationUnitProcessorDetails.UnitType); processorEnvMock.Verify(); } /// /// Test GetUnitProcessorDetails downloading module. /// [Fact] public void GetUnitProcessorDetails_Download() { var unit = this.CreateConfigurationUnit(); DscResourceInfoInternal? nullDscResourceInfo = null; var (_, psModuleInfo) = this.GetResourceAndModuleInfo(unit); var getFindModuleInfo = this.CreateGetModuleInfo(); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(u => u.Unit.Type == unit.Type))) .Returns(nullDscResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.FindModule(It.Is(c => unit.Type == unit.Type))) .Returns(getFindModuleInfo) .Verifiable(); processorEnvMock.Setup( m => m.SaveModule(getFindModuleInfo, It.IsAny())) .Verifiable(); processorEnvMock.Setup( m => m.GetAvailableModule(It.Is(s => s.EndsWith("xSimpleTestResource")))) .Returns(psModuleInfo) .Verifiable(); processorEnvMock.Setup( m => m.GetCertsOfValidSignedFiles(It.IsAny())) .Returns(new List()) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var configurationUnitProcessorDetails = configurationSetProcessor.GetUnitProcessorDetails( unit, ConfigurationUnitDetailFlags.Download); Assert.NotNull(configurationUnitProcessorDetails); Assert.Equal("SimpleFileResource", configurationUnitProcessorDetails.UnitType); processorEnvMock.Verify(); processorEnvMock.Verify(m => m.InstallModule(It.IsAny()), Times.Never()); } /// /// Tests GetUnitProcessorDetails install module, but resource not found anyway. /// [Fact] public void GetUnitProcessorDetails_Load_NotFoundAfterInstall() { var unit = this.CreateConfigurationUnit(); DscResourceInfoInternal? nullDscResourceInfo = null; var (_, psModuleInfo) = this.GetResourceAndModuleInfo(unit); var getFindResourceInfo = this.CreateFindResourceInfo(); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(u => u.Unit.Type == unit.Type))) .Returns(nullDscResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.FindModule(It.Is(c => unit.Type == unit.Type))) .Returns(getFindResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.InstallModule(getFindResourceInfo)) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); Assert.Throws(() => configurationSetProcessor.GetUnitProcessorDetails( unit, ConfigurationUnitDetailFlags.Load)); processorEnvMock.Verify(); processorEnvMock.Verify( m => m.GetDscResource(It.Is(u => u.Unit.Type == unit.Type)), Times.Exactly(2)); } /// /// Tests GetUnitProcessorDetails install module. /// [Fact] public void GetUnitProcessorDetails_Load() { var unit = this.CreateConfigurationUnit(); DscResourceInfoInternal? nullDscResourceInfo = null; var (dscResourceInfo, psModuleInfo) = this.GetResourceAndModuleInfo(unit); var getFindResourceInfo = this.CreateFindResourceInfo(); var processorEnvMock = new Mock(); processorEnvMock.SetupSequence( m => m.GetDscResource(It.Is(u => u.Unit.Type == unit.Type))) .Returns(nullDscResourceInfo) .Returns(dscResourceInfo); processorEnvMock.Setup( m => m.FindModule(It.Is(c => unit.Type == unit.Type))) .Returns(getFindResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.InstallModule(getFindResourceInfo)) .Verifiable(); processorEnvMock.Setup( m => m.GetInstalledModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Returns(getFindResourceInfo) .Verifiable(); processorEnvMock.Setup( m => m.ImportModule(It.Is(s => s.Name == dscResourceInfo.ModuleName))) .Verifiable(); var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, new ConfigurationSet()); var configurationUnitProcessorDetails = configurationSetProcessor.GetUnitProcessorDetails( unit, ConfigurationUnitDetailFlags.Load); Assert.NotNull(configurationUnitProcessorDetails); Assert.Equal(dscResourceInfo.Name, configurationUnitProcessorDetails.UnitType); processorEnvMock.Verify(); processorEnvMock.Verify( m => m.GetDscResource(It.Is(u => u.Unit.Type == unit.Type)), Times.Exactly(2)); } /// /// This tests uses SimpleTestResourceTypes Test to validate the resource got the correct types /// from the processor. /// [Fact] public void CreateUnitProcessor_TestTypes() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); var setProcessor = new PowerShellConfigurationSetProcessor(processorEnv, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = "SimpleTestResourceTypes", Intent = ConfigurationUnitIntent.Assert, }; unit.Metadata.Add("module", "xSimpleTestResource"); unit.Metadata.Add("version", "0.0.0.1"); var hashtableProperty = new ValueSet { { "secretStringKey", "secretCode" }, { "secretIntKey", "123456" }, }; unit.Settings.Add("boolProperty", true); unit.Settings.Add("intProperty", 3); unit.Settings.Add("doubleProperty", -9.876); unit.Settings.Add("charProperty", 'f'); unit.Settings.Add("hashtableProperty", hashtableProperty); var unitProcessor = setProcessor.CreateUnitProcessor(unit); unitProcessor.TestSettings(); } /// /// Tests a module that requires admin is loaded from non admin. /// [FactSkipIfCI] public void CreateUnitProcessor_ModuleRequiresAdmin() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); var setProcessor = new PowerShellConfigurationSetProcessor(processorEnv, new ConfigurationSet()); var unit = new ConfigurationUnit { Type = "AdminResource", Intent = ConfigurationUnitIntent.Assert, }; unit.Metadata.Add("module", "xAdminTestResource"); unit.Metadata.Add("version", "0.0.0.1"); unit.Settings.Add("key", "key"); var importModuleException = Assert.Throws(() => setProcessor.CreateUnitProcessor(unit)); Assert.Equal(ErrorCodes.WinGetConfigUnitImportModuleAdmin, importModuleException.HResult); } /// /// Test CreateUnitProcessor. Limit mode. /// [Fact] public void CreateUnitProcessor_LimitMode() { string resourceName = "xResourceName"; string moduleName = "xModuleName"; Version version = new Version("1.0"); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(new DscResourceInfoInternal(resourceName, moduleName, version)) .Verifiable(); var limitSet = new ConfigurationSet(); var limitUnit = new ConfigurationUnit { Type = resourceName, Intent = ConfigurationUnitIntent.Apply, }; limitUnit.Metadata.Add("module", moduleName); limitUnit.Metadata.Add("version", version.ToString()); limitSet.Units = new List { limitUnit }; var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, limitSet, true); // Calling with unit different from limit set should throw. var unitDifferentContent = new ConfigurationUnit { Type = "differentResourceName", Intent = ConfigurationUnitIntent.Apply, }; Assert.Throws(() => configurationSetProcessor.CreateUnitProcessor(unitDifferentContent)); Assert.Throws(() => configurationSetProcessor.GetUnitProcessorDetails(unitDifferentContent, ConfigurationUnitDetailFlags.Load)); // Calling with unit matching limit set. var unitProcessor = configurationSetProcessor.CreateUnitProcessor(limitUnit); Assert.NotNull(unitProcessor); Assert.Equal(limitUnit.Type, unitProcessor.Unit.Type); var processorDetails = configurationSetProcessor.GetUnitProcessorDetails(limitUnit, ConfigurationUnitDetailFlags.Load); Assert.NotNull(processorDetails); Assert.Equal(moduleName, processorDetails.ModuleName); // Calling CreateProcessor again should thow. Calling GetProcessorDetails multiple times is ok. Assert.Throws(() => configurationSetProcessor.CreateUnitProcessor(limitUnit)); var processorDetails2 = configurationSetProcessor.GetUnitProcessorDetails(limitUnit, ConfigurationUnitDetailFlags.Load); Assert.NotNull(processorDetails2); Assert.Equal(moduleName, processorDetails2.ModuleName); } /// /// Test CreateUnitProcessor. Limit mode. Duplicate units in limit set. /// [Fact] public void CreateUnitProcessor_LimitMode_DuplicateUnits() { string resourceName = "xResourceName"; string moduleName = "xModuleName"; Version version = new Version("1.0"); var processorEnvMock = new Mock(); processorEnvMock.Setup( m => m.GetDscResource(It.Is(c => c.Unit.Type == resourceName))) .Returns(new DscResourceInfoInternal(resourceName, moduleName, version)) .Verifiable(); var limitSet = new ConfigurationSet(); var limitUnit = new ConfigurationUnit { Type = resourceName, Intent = ConfigurationUnitIntent.Apply, }; limitUnit.Metadata.Add("module", moduleName); limitUnit.Metadata.Add("version", version.ToString()); limitSet.Units = new List { limitUnit, limitUnit }; var configurationSetProcessor = new PowerShellConfigurationSetProcessor( processorEnvMock.Object, limitSet, true); // Calling with unit different from limit set should throw. var unitDifferentContent = new ConfigurationUnit { Type = "differentResourceName", Intent = ConfigurationUnitIntent.Apply, }; Assert.Throws(() => configurationSetProcessor.CreateUnitProcessor(unitDifferentContent)); // Calling with unit matching limit set. var unitProcessor = configurationSetProcessor.CreateUnitProcessor(limitUnit); Assert.NotNull(unitProcessor); Assert.Equal(limitUnit.Type, unitProcessor.Unit.Type); // Calling again should also not thow. var unitProcessor2 = configurationSetProcessor.CreateUnitProcessor(limitUnit); Assert.NotNull(unitProcessor2); Assert.Equal(limitUnit.Type, unitProcessor2.Unit.Type); // Calling third time should throw. Assert.Throws(() => configurationSetProcessor.CreateUnitProcessor(limitUnit)); } private ConfigurationUnit CreateConfigurationUnit() { var unit = new ConfigurationUnit(); unit.Type = "SimpleFileResource"; unit.Metadata.Add("module", "xSimpleTestResource"); unit.Metadata.Add("version", "0.0.0.1"); return unit; } private (DscResourceInfoInternal dscResourceInfo, PSModuleInfo psModuleInfo) GetResourceAndModuleInfo(ConfigurationUnit unit) { // This is easier than trying to mock sealed class from external code... var testEnv = this.fixture.PrepareTestProcessorEnvironment(true); var dscResourceInfo = testEnv.GetDscResource(new ConfigurationUnitAndModule(unit, string.Empty)); var psModuleInfo = testEnv.GetAvailableModule(PowerShellHelpers.CreateModuleSpecification("xSimpleTestResource", "0.0.0.1")); if (dscResourceInfo is null || psModuleInfo is null) { throw new ArgumentNullException("Test processor environment not set correctly"); } return (dscResourceInfo, psModuleInfo); } private PSObject CreateGetModuleInfo() { return new PSObject(new { Repository = "PSGallery", PublishedDate = new DateTime(2017, 12, 10), IconUri = "https://github.com/microsoft/winget-cli", Name = "xSimpleTestResource", Description = "PowerShell module with DSC resources for unit tests", RepositorySourceLocation = "https://github.com/microsoft/winget-cli", Version = "0.0.0.1", Author = "Luffytaro", CompanyName = "Microsoft Corporation", }); } private PSObject CreateFindResourceInfo() { var getModuleInfo = this.CreateGetModuleInfo(); return new PSObject(new { Name = "SimpleFileResource", ModuleName = "xSimpleTestResource", Version = "0.0.0.1", PSGetModuleInfo = getModuleInfo, }); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationUnitInternalTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using System.IO; using System.Management.Automation; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Xunit; using Xunit.Abstractions; /// /// Tests ConfigurationUnitExtensionsTests. /// [Collection("UnitTestCollection")] [InProc] public class ConfigurationUnitInternalTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationUnitInternalTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Test GetDirectives. /// [Fact] public void GetDirectivesTest() { string moduleDirective = "module"; string unitModule = "xModule"; string versionDirective = "version"; string unitVersion = "1.0.0.0"; string descriptionDirective = "description"; string unitDescription = "beep beep boop i am a text"; string boolDirective = "boolDirective"; bool boolDirectiveValue = true; string boolDirective2 = "boolDirective2"; bool boolDirective2Value = false; var unit = new ConfigurationUnit().Assign(new { Type = $"{unitModule}/unitResource" }); unit.Metadata.Add(moduleDirective, unitModule); unit.Metadata.Add(versionDirective, unitVersion); unit.Metadata.Add(descriptionDirective, unitDescription); unit.Metadata.Add(boolDirective, boolDirectiveValue); unit.Metadata.Add(boolDirective2, boolDirective2Value); var unitInternal = new ConfigurationUnitAndModule(unit, string.Empty); var description = unitInternal.GetDirective(descriptionDirective); Assert.Equal(description, unitDescription); var fake = unitInternal.GetDirective("fake"); Assert.Null(fake); var description2 = unitInternal.GetDirective("DESCRIPTION"); Assert.Equal(description2, unitDescription); Assert.Equal(unitModule, unitInternal.Module!.Name); Assert.Equal(Version.Parse(unitVersion), unitInternal.Module!.RequiredVersion); Assert.Equal(boolDirectiveValue, unitInternal.GetDirective(boolDirective)); Assert.Equal(boolDirective2Value, unitInternal.GetDirective(boolDirective2)); Assert.Null(unitInternal.GetDirective("fakeBool")); } /// /// Tests GetVersion with a bad version. /// [Fact] public void GetVersion_BadVersion() { var unit = new ConfigurationUnit(); unit.Metadata.Add("module", "module"); unit.Metadata.Add("version", "not a version"); Assert.Throws( () => new ConfigurationUnitInternal(unit, string.Empty)); } /// /// Verifies expansion of ConfigRoot. /// [Fact] public void GetExpandedSettings_ConfigRoot() { using var tmpFile = new TempFile("fakeConfigFile.yml", content: "content"); var unit = new ConfigurationUnit().Assign(new { Type = "unitModule/unitResource" }); unit.Settings.Add("var1", @"$WinGetConfigRoot\this\is\a\path.txt"); unit.Settings.Add("var2", @"${WinGetConfigRoot}\this\is\a\path.txt"); unit.Settings.Add("var3", @"this\is\a\$WINGETCONFIGROOT\path.txt"); unit.Settings.Add("var4", @"this\is\a\${WINGETCONFIGROOT}\path.txt"); unit.Settings.Add("var5", @"this\is\a\path\$wingetconfigroot"); unit.Settings.Add("var6", @"this\is\a\path\${wingetconfigroot}"); string configPath = tmpFile.FullFileName; string? expectedPath = Path.GetDirectoryName(configPath); var unitInternal = new ConfigurationUnitInternal(unit, configPath); var expandedSettings = unitInternal.GetExpandedSettings(); var var1 = expandedSettings["var1"]; Assert.Equal(@"$WinGetConfigRoot\this\is\a\path.txt", var1 as string); var var2 = expandedSettings["var2"]; Assert.Equal($@"{expectedPath}\this\is\a\path.txt", var2 as string); var var3 = expandedSettings["var3"]; Assert.Equal(@"this\is\a\$WINGETCONFIGROOT\path.txt", var3 as string); var var4 = expandedSettings["var4"]; Assert.Equal($@"this\is\a\{expectedPath}\path.txt", var4 as string); var var5 = expandedSettings["var5"]; Assert.Equal(@"this\is\a\path\$wingetconfigroot", var5 as string); var var6 = expandedSettings["var6"]; Assert.Equal($@"this\is\a\path\{expectedPath}", var6 as string); } /// /// Verifies throws when config root is not set. /// [Fact] public void GetExpandedSetting_ConfigRoot_Throw() { var unit = new ConfigurationUnit().Assign(new { Type = "unitModule/unitResource" }); unit.Settings.Add("var2", @"${WinGetConfigRoot}\this\is\a\path.txt"); var unitInternal = new ConfigurationUnitInternal(unit, null!); Assert.Throws(() => unitInternal.GetExpandedSettings()); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationUnitProcessorTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Management.Automation; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.Processor.PowerShell.ProcessorEnvironments; using Microsoft.Management.Configuration.Processor.PowerShell.Unit; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.PowerShell.Commands; using Moq; using Windows.Foundation.Collections; using Xunit; using Xunit.Abstractions; /// /// Configuration unit processor tests. /// [Collection("UnitTestCollection")] [InProc] public class ConfigurationUnitProcessorTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ConfigurationUnitProcessorTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Call GetSettings with all intents should succeed. /// /// Intent. [Theory] [InlineData(ConfigurationUnitIntent.Inform)] [InlineData(ConfigurationUnitIntent.Assert)] [InlineData(ConfigurationUnitIntent.Apply)] public void GetSettings_Test(object intent) { string theKey = "key"; string theValue = "value"; var valueGetResult = new ValueSet { { theKey, theValue }, }; var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeGetResource( It.IsAny(), It.IsAny(), It.IsAny())) .Returns(valueGetResult) .Verifiable(); var unitResource = this.CreateUnitResource(Assert.IsType(intent)); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var result = unitProcessor.GetSettings(); processorEnvMock.Verify(); Assert.True(result.Settings.Count == 1); Assert.True(result.Settings.ContainsKey(theKey)); Assert.True(result.Settings.TryGetValue(theKey, out object keyValue)); Assert.Equal(theValue, keyValue as string); } /// /// Tests GetSettings when a System.Management.Automation.RuntimeException is thrown. /// [Fact] public void GetSettings_Throws_Pwsh_RuntimeException() { var thrownException = new RuntimeException("a message"); var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeGetResource( It.IsAny(), It.IsAny(), It.IsAny())) .Throws(() => thrownException) .Verifiable(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Inform); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var result = unitProcessor.GetSettings(); processorEnvMock.Verify(); // Do not check for the type. Assert.Equal(thrownException.HResult, result.ResultInformation.ResultCode.HResult); Assert.True(!string.IsNullOrWhiteSpace(result.ResultInformation.Description)); Assert.Equal(ConfigurationUnitResultSource.Internal, result.ResultInformation.ResultSource); } /// /// Tests GetSettings when a Microsoft.PowerShell.Commands.WriteErrorException is thrown. /// [Fact] public void GetSettings_Throws_Pwsh_WriteErrorException() { var thrownException = new WriteErrorException("a message"); var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeGetResource( It.IsAny(), It.IsAny(), It.IsAny())) .Throws(() => thrownException) .Verifiable(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Inform); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var result = unitProcessor.GetSettings(); processorEnvMock.Verify(); // Do not check for the type. Assert.Equal(thrownException.HResult, result.ResultInformation.ResultCode.HResult); Assert.True(!string.IsNullOrWhiteSpace(result.ResultInformation.Description)); Assert.Equal(ConfigurationUnitResultSource.Internal, result.ResultInformation.ResultSource); } /// /// Call TestSettings with Inform intent is not allowed. /// [Fact] public void TestSettings_InformIntent() { var processorEnvMock = new Mock(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Inform); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); Assert.Throws(() => unitProcessor.TestSettings()); } /// /// Call TestSettings with Assert and Apply should work. /// /// Intent. /// Invoke test result. [Theory] [InlineData(ConfigurationUnitIntent.Assert, false)] [InlineData(ConfigurationUnitIntent.Apply, false)] [InlineData(ConfigurationUnitIntent.Assert, true)] [InlineData(ConfigurationUnitIntent.Apply, true)] public void TestSettings_TestSucceeded(object intent, bool invokeTestResult) { var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeTestResource( It.IsAny(), It.IsAny(), It.IsAny())) .Returns(invokeTestResult) .Verifiable(); var unitResource = this.CreateUnitResource(Assert.IsType(intent)); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var testResult = unitProcessor.TestSettings(); processorEnvMock.Verify(); var expectedConfigTestResult = ConfigurationTestResult.Negative; if (invokeTestResult) { expectedConfigTestResult = ConfigurationTestResult.Positive; } Assert.Equal(expectedConfigTestResult, testResult.TestResult); } /// /// Tests TestSettings when a System.Management.Automation.RuntimeException is thrown. /// [Fact] public void TestSettings_Throws_Pwsh_RuntimeException() { var thrownException = new RuntimeException("a message"); var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeTestResource( It.IsAny(), It.IsAny(), It.IsAny())) .Throws(() => thrownException) .Verifiable(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Assert); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var result = unitProcessor.TestSettings(); processorEnvMock.Verify(); Assert.Equal(ConfigurationTestResult.Failed, result.TestResult); // Do not check for the type. Assert.Equal(thrownException.HResult, result.ResultInformation.ResultCode.HResult); Assert.True(!string.IsNullOrWhiteSpace(result.ResultInformation.Description)); Assert.Equal(ConfigurationUnitResultSource.Internal, result.ResultInformation.ResultSource); } /// /// Tests TestSettings when a Microsoft.PowerShell.Commands.WriteErrorException is thrown. /// [Fact] public void TestSettings_Throws_Pwsh_WriteErrorException() { var thrownException = new WriteErrorException("a message"); var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeTestResource( It.IsAny(), It.IsAny(), It.IsAny())) .Throws(() => thrownException) .Verifiable(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Assert); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var result = unitProcessor.TestSettings(); processorEnvMock.Verify(); Assert.Equal(ConfigurationTestResult.Failed, result.TestResult); // Do not check for the type. Assert.Equal(thrownException.HResult, result.ResultInformation.ResultCode.HResult); Assert.True(!string.IsNullOrWhiteSpace(result.ResultInformation.Description)); Assert.Equal(ConfigurationUnitResultSource.Internal, result.ResultInformation.ResultSource); } /// /// Call ApplySettings with invalid intents. /// /// Intent. [Theory] [InlineData(ConfigurationUnitIntent.Inform)] [InlineData(ConfigurationUnitIntent.Assert)] public void ApplySettings_InvalidIntent(object intent) { var processorEnvMock = new Mock(); var unitResource = this.CreateUnitResource(Assert.IsType(intent)); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); Assert.Throws(() => unitProcessor.ApplySettings()); } /// /// Call ApplySettings. /// /// Reboot required. [Theory] [InlineData(true)] [InlineData(false)] public void ApplySettings_Test(bool rebootRequired) { var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeSetResource( It.IsAny(), It.IsAny(), It.IsAny())) .Returns(rebootRequired) .Verifiable(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Apply); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var result = unitProcessor.ApplySettings(); Assert.Equal(rebootRequired, result.RebootRequired); } /// /// Tests ApplySettings when a System.Management.Automation.RuntimeException is thrown. /// [Fact] public void ApplySettings_Throws_Pwsh_RuntimeException() { var thrownException = new RuntimeException("a message"); var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeSetResource( It.IsAny(), It.IsAny(), It.IsAny())) .Throws(() => thrownException) .Verifiable(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Apply); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var result = unitProcessor.ApplySettings(); processorEnvMock.Verify(); // Do not check for the type. Assert.Equal(thrownException.HResult, result.ResultInformation.ResultCode.HResult); Assert.True(!string.IsNullOrWhiteSpace(result.ResultInformation.Description)); Assert.Equal(ConfigurationUnitResultSource.Internal, result.ResultInformation.ResultSource); } /// /// Tests ApplySettings when a Microsoft.PowerShell.Commands.WriteErrorException is thrown. /// [Fact] public void ApplySettings_Throws_Pwsh_WriteErrorException() { var thrownException = new RuntimeException("a message"); var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeSetResource( It.IsAny(), It.IsAny(), It.IsAny())) .Throws(() => thrownException) .Verifiable(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Apply); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource); var result = unitProcessor.ApplySettings(); processorEnvMock.Verify(); // Do not check for the type. Assert.Equal(thrownException.HResult, result.ResultInformation.ResultCode.HResult); Assert.True(!string.IsNullOrWhiteSpace(result.ResultInformation.Description)); Assert.Equal(ConfigurationUnitResultSource.Internal, result.ResultInformation.ResultSource); } /// /// Tests ApplySettings in limit mode. /// [Fact] public void ApplySettings_Test_LimitMode() { string theKey = "key"; string theValue = "value"; var valueGetResult = new ValueSet { { theKey, theValue }, }; var processorEnvMock = new Mock(); processorEnvMock.Setup(m => m.InvokeGetResource( It.IsAny(), It.IsAny(), It.IsAny())) .Returns(valueGetResult) .Verifiable(); var unitResource = this.CreateUnitResource(ConfigurationUnitIntent.Apply); var unitProcessor = new PowerShellConfigurationUnitProcessor(processorEnvMock.Object, unitResource, true); // GetSettings can be called multiple times. var getResult = unitProcessor.GetSettings(); getResult = unitProcessor.GetSettings(); // TestSettings can be called only once. var testResult = unitProcessor.TestSettings(); Assert.Throws(() => unitProcessor.TestSettings()); // ApplySettings can be called only once. var applyResult = unitProcessor.ApplySettings(); Assert.Throws(() => unitProcessor.ApplySettings()); } private ConfigurationUnitAndResource CreateUnitResource(ConfigurationUnitIntent intent) { string resourceName = "xResourceName"; return new ConfigurationUnitAndResource( new ConfigurationUnitAndModule( new ConfigurationUnit { Type = resourceName, Intent = intent, }, string.Empty), new DscResourceInfoInternal(resourceName, null, null)); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/DSCv3ProcessorTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.Collections.Generic; using System.Linq; using Microsoft.Management.Configuration.Processor; using Microsoft.Management.Configuration.Processor.DSCv3.Model; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Windows.Foundation.Collections; using Xunit; using Xunit.Abstractions; /// /// Tests for the DSCv3 processor. /// [Collection("UnitTestCollection")] [InProc] public class DSCv3ProcessorTests : ConfigurationProcessorTestBase { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public DSCv3ProcessorTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { this.fixture = fixture; this.log = log; } /// /// Tests for the unit details caching. /// [Fact] public void Set_UnitPropertyDetailsCached() { var (factory, dsc) = CreateTestFactory(); var set = this.ConfigurationSet(); string type1 = "Type1"; string type2 = "Type2"; var unit1 = this.ConfigurationUnit().Assign(new { Type = type1 }); var unit2 = this.ConfigurationUnit().Assign(new { Type = type2 }); var setProcessor = factory.CreateSetProcessor(set); // Initially, no details var details = setProcessor.GetUnitProcessorDetails(unit1, ConfigurationUnitDetailFlags.Local); Assert.Null(details); // Null result not cached dsc.GetResourceByTypeResult = new TestResourceListItem() { Type = type1 }; details = setProcessor.GetUnitProcessorDetails(unit1, ConfigurationUnitDetailFlags.Local); Assert.NotNull(details); Assert.Equal(type1, details.UnitType); // Not-null result cached dsc.GetResourceByTypeResult = null; dsc.GetResourceByTypeDelegate = s => throw new System.Exception("Shouldn't be called"); details = setProcessor.GetUnitProcessorDetails(unit1, ConfigurationUnitDetailFlags.Local); Assert.NotNull(details); Assert.Equal(type1, details.UnitType); // Different type, no details dsc.GetResourceByTypeDelegate = null; details = setProcessor.GetUnitProcessorDetails(unit2, ConfigurationUnitDetailFlags.Local); Assert.Null(details); // Null result not cached dsc.GetResourceByTypeResult = new TestResourceListItem() { Type = type2 }; details = setProcessor.GetUnitProcessorDetails(unit2, ConfigurationUnitDetailFlags.Local); Assert.NotNull(details); Assert.Equal(type2, details.UnitType); // First type is still first type dsc.GetResourceByTypeResult = null; dsc.GetResourceByTypeDelegate = s => throw new System.Exception("Shouldn't be called"); details = setProcessor.GetUnitProcessorDetails(unit1, ConfigurationUnitDetailFlags.Local); Assert.NotNull(details); Assert.Equal(type1, details.UnitType); } /// /// Test for unit processor creation requiring resource to be found. /// [Fact(Skip = "Disable this test while we have the bypass in place")] public void Set_ResourceNotFoundIsError() { var (factory, dsc) = CreateTestFactory(); var set = this.ConfigurationSet(); string type1 = "Type1"; var unit1 = this.ConfigurationUnit().Assign(new { Type = type1 }); var setProcessor = factory.CreateSetProcessor(set); // Not found is error Assert.Throws(() => setProcessor.CreateUnitProcessor(unit1)); // Found is not error dsc.GetResourceByTypeResult = new TestResourceListItem() { Type = type1 }; var unitProcessor = setProcessor.CreateUnitProcessor(unit1); Assert.NotNull(unitProcessor); Assert.Equal(type1, unitProcessor.Unit.Type); } /// /// Test for settings export. /// [Fact] public void GetAllSettings_Expected() { var (factory, dsc) = CreateTestFactory(); var processor = this.CreateConfigurationProcessorWithDiagnostics(factory); string type1 = "Type1"; var unit1 = this.ConfigurationUnit().Assign(new { Type = type1 }); dsc.GetResourceByTypeDelegate = (type) => { Assert.Equal(type1, type); return new TestResourceListItem() { Type = type1 }; }; ValueSet set1 = new ValueSet(); set1.Add("key1", "val1"); ValueSet set2 = new ValueSet(); set2.Add("key2", "val2"); dsc.ExportResourceResult = new List() { new TestResourceExportItem() { Type = type1, Name = "1", Settings = set1 }, new TestResourceExportItem() { Type = type1, Name = "2", Settings = set2 }, }; var result = processor.GetAllUnitSettings(unit1); Assert.NotNull(result); Assert.NotNull(result.ResultInformation); Assert.Null(result.ResultInformation.ResultCode); Assert.Equal(2, result.Settings.Count); Assert.NotNull(result.Settings.Single(set => set.Contains(set1.First()))); Assert.NotNull(result.Settings.Single(set => set.Contains(set2.First()))); } /// /// Test for settings export with differing types. /// [Fact] public void GetAllSettings_DifferentType() { var (factory, dsc) = CreateTestFactory(); var processor = this.CreateConfigurationProcessorWithDiagnostics(factory); string type1 = "Type1"; string type2 = "Type2"; var unit1 = this.ConfigurationUnit().Assign(new { Type = type1 }); dsc.GetResourceByTypeDelegate = (type) => { Assert.Equal(type1, type); return new TestResourceListItem() { Type = type1 }; }; ValueSet set1 = new ValueSet(); set1.Add("key1", "val1"); ValueSet set2 = new ValueSet(); set2.Add("key2", "val2"); dsc.ExportResourceResult = new List() { new TestResourceExportItem() { Type = type1, Name = "1", Settings = set1 }, new TestResourceExportItem() { Type = type2, Name = "2", Settings = set2 }, }; var result = processor.GetAllUnitSettings(unit1); Assert.NotNull(result); Assert.NotNull(result.ResultInformation); Assert.NotNull(result.ResultInformation.ResultCode); Assert.Equal(ErrorCodes.WinGetConfigUnitUnsupportedType, result.ResultInformation.ResultCode.HResult); } /// /// Test for unit export. /// [Fact] public void GetAllUnits_Simple() { var (factory, dsc) = CreateTestFactory(); var processor = this.CreateConfigurationProcessorWithDiagnostics(factory); string type1 = "Type1"; var unit1 = this.ConfigurationUnit().Assign(new { Type = type1 }); dsc.GetResourceByTypeDelegate = (type) => { Assert.Equal(type1, type); return new TestResourceListItem() { Type = type1 }; }; ValueSet set1 = new ValueSet(); set1.Add("key1", "val1"); ValueSet set2 = new ValueSet(); set2.Add("key2", "val2"); dsc.ExportResourceResult = new List() { new TestResourceExportItem() { Type = type1, Name = "1", Settings = set1 }, new TestResourceExportItem() { Type = type1, Name = "2", Settings = set2 }, }; var result = processor.GetAllUnits(unit1); Assert.NotNull(result); Assert.NotNull(result.ResultInformation); Assert.Null(result.ResultInformation.ResultCode); Assert.Equal(2, result.Units.Count); foreach (var unit in result.Units) { Assert.Equal(type1, unit.Type); Assert.NotEmpty(unit.Identifier); } Assert.NotEqual(result.Units[0].Identifier, result.Units[1].Identifier); Assert.NotNull(result.Units.Single(unit => unit.Settings.Contains(set1.First()))); Assert.NotNull(result.Units.Single(unit => unit.Settings.Contains(set2.First()))); } /// /// Test for unit export with complex data. /// [Fact] public void GetAllUnits_Complex() { var (factory, dsc) = CreateTestFactory(); var processor = this.CreateConfigurationProcessorWithDiagnostics(factory); string type1 = "Type1"; string type2 = "Type2"; string name1 = "1"; string name2 = "2"; var unit1 = this.ConfigurationUnit().Assign(new { Type = type1 }); dsc.GetResourceByTypeDelegate = (type) => { Assert.Equal(type1, type); return new TestResourceListItem() { Type = type1 }; }; ValueSet set1 = new ValueSet(); set1.Add("key1", "val1"); ValueSet metadata1 = new ValueSet(); metadata1.Add("met1", "val11"); ValueSet set2 = new ValueSet(); set2.Add("key2", "val2"); List dependencies2 = new List(); dependencies2.Add(name1); dsc.ExportResourceResult = new List() { new TestResourceExportItem() { Type = type1, Name = name1, Settings = set1, Metadata = metadata1 }, new TestResourceExportItem() { Type = type2, Name = name2, Settings = set2, Dependencies = dependencies2 }, }; var result = processor.GetAllUnits(unit1); Assert.NotNull(result); Assert.NotNull(result.ResultInformation); Assert.Null(result.ResultInformation.ResultCode); Assert.Equal(2, result.Units.Count); var result1 = result.Units.Single(unit => unit.Identifier == name1); var result2 = result.Units.Single(unit => unit.Identifier == name2); Assert.Equal(type1, result1.Type); Assert.Equal(type2, result2.Type); Assert.Contains(set1.First(), result1.Settings); Assert.Contains(set2.First(), result2.Settings); Assert.Contains(metadata1.First(), result1.Metadata); Assert.Empty(result2.Metadata); Assert.Empty(result1.Dependencies); Assert.Contains(dependencies2.First(), result2.Dependencies); } private static (DSCv3ConfigurationSetProcessorFactory, TestDSCv3) CreateTestFactory() { DSCv3ConfigurationSetProcessorFactory factory = new DSCv3ConfigurationSetProcessorFactory(); TestDSCv3 dsc = new TestDSCv3(); factory.Settings.DSCv3 = dsc; factory.Settings.DscExecutablePath = "Test-Path-Not-Used.txt"; return (factory, dsc); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/DscModuleV2SimpleFileResourceTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.IO; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.PowerShell.DscModules; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Windows.Foundation.Collections; using Xunit; using Xunit.Abstractions; using static Microsoft.Management.Configuration.UnitTests.Helpers.PowerShellTestsConstants; /// /// Class that tests a little not that complex resource. /// [Collection("UnitTestCollection")] [InProc] public class DscModuleV2SimpleFileResourceTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Fixture. /// log. public DscModuleV2SimpleFileResourceTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Test SimpleFileResource Ensure Present and Absent when file doesn't exist. /// /// Ensure value. /// Expected result. [Theory] [InlineData("Absent", true)] [InlineData("Present", false)] public void SimpleFileResource_FileAbsent(string ensureValue, bool expectedResult) { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); // Doesn't create a file. using var tmpFile = new TempFile(); var settings = new ValueSet { { "Path", tmpFile.FullFileName }, { "Ensure", ensureValue }, }; var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); Assert.Equal( expectedResult, dscModule.InvokeTestResource( pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); } /// /// Test SimpleFileResource when the file exists. /// [Fact] public void SimpleFileResource_FilePresent() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); using var tmpFile = new TempFile(); tmpFile.CreateFile(); var settings = new ValueSet { { "Path", tmpFile.FullFileName }, { "Ensure", "Present" }, }; var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); Assert.True(dscModule.InvokeTestResource( pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); } /// /// Test SimpleFileResource with different content. /// [Fact] public void SimpleFileResource_DifferentContent() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); using var tmpFile = new TempFile(content: "All work and no play makes Ruben a dull boy"); var settings = new ValueSet { { "Path", tmpFile.FullFileName }, { "Ensure", "Present" }, { "Content", "Is that a from somewhere?" }, }; var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); Assert.False(dscModule.InvokeTestResource( pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); } /// /// Test SimpleFileResource with same context. /// [Fact] public void SimpleFileResource_SameContent() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); string content = "This is literally the same, dont fail"; using var tmpFile = new TempFile(content: content); var settings = new ValueSet { { "Path", tmpFile.FullFileName }, { "Ensure", "Present" }, { "Content", content }, }; var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); Assert.True(dscModule.InvokeTestResource( pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); } /// /// Test SimpleFileResource Set creates the file. /// /// Ensure value. /// If file exists before calling set. /// If file exists after calling set. [Theory] [InlineData("Absent", false, false)] [InlineData("Present", false, true)] [InlineData("Absent", true, false)] [InlineData("Present", true, true)] public void SimpleFileResource_Set_Ensure(string ensureValue, bool preCondition, bool postCondition) { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); // Doesn't create a file. using var tmpFile = new TempFile(); var settings = new ValueSet { { "Path", tmpFile.FullFileName }, { "Ensure", ensureValue }, }; if (preCondition) { tmpFile.CreateFile(); } Assert.Equal( preCondition, File.Exists(tmpFile.FullFileName)); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); dscModule.InvokeSetResource( pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName)); Assert.Equal( postCondition, File.Exists(tmpFile.FullFileName)); } /// /// Tests SimpleFileResource Set writes to the file. /// /// The text in the file before calling Set. /// The test in the file after calling Set. [Theory] [InlineData(null, "after content")] [InlineData("i am a content", "and im another")] [InlineData("copy paste", "copy paste")] public void SimpleFileResource_Set_Content(string? preSetContent, string postSetContent) { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); // Doesn't create a file. using var tmpFile = new TempFile(); if (preSetContent is not null) { tmpFile.CreateFile(preSetContent); } var settings = new ValueSet { { "Path", tmpFile.FullFileName }, { "Ensure", "Present" }, { "Content", postSetContent }, }; var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); dscModule.InvokeSetResource( pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName)); Assert.Equal( postSetContent, File.ReadAllText(tmpFile.FullFileName)); } /// /// Test SimpleFileResource Get. /// [Fact] public void SimpleFileResource_Get() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); string content = "I'm out of ideas"; using var tmpFile = new TempFile(content: content); var settings = new ValueSet { { "Path", tmpFile.FullFileName }, }; var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); var properties = dscModule.InvokeGetResource( pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName)); Assert.True(properties.ContainsKey("Path")); Assert.True(properties.TryGetValue("Path", out object pathResult)); Assert.Equal(tmpFile.FullFileName, pathResult as string); // Present is just the default value. Assert.True(properties.ContainsKey("Ensure")); Assert.True(properties.TryGetValue("Ensure", out object ensureResult)); Assert.Equal("Present", ensureResult as string); Assert.True(properties.ContainsKey("Content")); Assert.True(properties.TryGetValue("Content", out object contentResult)); Assert.Equal(content, contentResult); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/DscModuleV2Tests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.IO; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.PowerShell.DscModules; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.PowerShell.Commands; using Windows.Foundation.Collections; using Xunit; using Xunit.Abstractions; using static Microsoft.Management.Configuration.UnitTests.Helpers.PowerShellTestsConstants; /// /// Tests DscModuleV2 with really simple resources. /// [Collection("UnitTestCollection")] [InProc] public class DscModuleV2Tests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Fixture. /// log. public DscModuleV2Tests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Tests GetAllDscResources. /// [Fact] public void GetAllDscResources_Test() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var resources = dscModule.GetAllDscResources(pwsh); Assert.True(resources.Count > 0); Assert.Contains(resources, r => r.Name == TestModule.SimpleFileResourceName); } /// /// Tests GetDscResourcesInModule. /// /// Module. /// Expected DSC resources. [Theory] [InlineData(TestModule.SimpleTestResourceModuleName, 5)] [InlineData("MyReallyFakeModule", 0)] public void GetDscResourcesInModule_Test(string module, int expectedResources) { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var resources = dscModule.GetDscResourcesInModule( pwsh, PowerShellHelpers.CreateModuleSpecification(module)); Assert.Equal(expectedResources, resources.Count); } /// /// Tests GetDscResourcesInModule with versions. /// [Fact] public void GetDscResourcesInModule_VersionTest() { string newVersion = "1.0.0.0"; var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); // Get duplicated resources by creating a new directory and copy our modules. // Change version and add them to the PSModulePath. using var tmpDir = new TempDirectory(); tmpDir.CopyDirectory(this.fixture.TestModulesPath); var manifestFile = Path.Combine( tmpDir.FullDirectoryPath, TestModule.SimpleTestResourceModuleName, TestModule.SimpleTestResourceManifestFileName); File.WriteAllText( manifestFile, File.ReadAllText(manifestFile).Replace("0.0.0.1", newVersion)); testEnvironment.AppendPSModulePath(tmpDir.FullDirectoryPath); var dscModule = new DscModuleV2(); // This doesn't work on v2 ////var allResources = dscModule.GetDscResourcesInModule( //// testEnvironment.Runspace, //// PowerShellHelpers.CreateModuleSpecification(TestModule.SimpleTestResourceModuleName)); ////Assert.Equal(8, allResources.Count); { using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var ogResources = dscModule.GetDscResourcesInModule( pwsh, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName, version: TestModule.SimpleTestResourceVersion)); Assert.Equal(5, ogResources.Count); } { using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var newVersionResources = dscModule.GetDscResourcesInModule( pwsh, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName, version: newVersion)); Assert.Equal(5, newVersionResources.Count); } { using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var badVersionResources = dscModule.GetDscResourcesInModule( pwsh, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName, version: "1.2.3.4")); Assert.Equal(0, badVersionResources.Count); } } /// /// Tests GetDscResource. Should return a resource. /// [Fact] public void GetDscResource_ResourceExists() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var resource = dscModule.GetDscResource( pwsh, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification(TestModule.SimpleTestResourceModuleName)); Assert.NotNull(resource); } /// /// Test GetDscResource for a resource that doesn't exist. /// [Fact] public void GetDscResource_ResourceDoesntExist() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var resource = dscModule.GetDscResource( pwsh, "FakeResourceName", PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName)); Assert.Null(resource); } /// /// Test GetDscResource when the same module is in different paths. /// [Fact] public void GetDscResource_Conflict() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); // Get duplicated resources by creating a new directory and copy our modules. // Then add it to the PSModulePath. using var tmpDir = new TempDirectory(); tmpDir.CopyDirectory(this.fixture.TestModulesPath); testEnvironment.AppendPSModulePath(tmpDir.FullDirectoryPath); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); Assert.Throws( () => dscModule.GetDscResource( pwsh, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); } /// /// Tests GetDscResource when there are multiple versions of a resource. /// [Fact] public void GetDscResource_DiffVersions() { string newVersion = "2.0.0.0"; var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); // Get duplicated resources by creating a new directory and copy our modules. // Change version and add them to the PSModulePath. using var tmpDir = new TempDirectory(); tmpDir.CopyDirectory(this.fixture.TestModulesPath); var manifestFile = Path.Combine( tmpDir.FullDirectoryPath, TestModule.SimpleTestResourceModuleName, TestModule.SimpleTestResourceManifestFileName); File.WriteAllText( manifestFile, File.ReadAllText(manifestFile).Replace("0.0.0.1", newVersion)); testEnvironment.AppendPSModulePath(tmpDir.FullDirectoryPath); var dscModule = new DscModuleV2(); // specific version. { using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var resource = dscModule.GetDscResource( pwsh, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName, TestModule.SimpleTestResourceVersion)); Assert.NotNull(resource); } { using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var dsc = dscModule.GetDscResource( pwsh, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName, version: newVersion)); Assert.NotNull(dsc); Assert.NotNull(dsc.Version); Assert.Equal(newVersion, dsc.Version.ToString()); } } /// /// Calls Invoke-DscResource Get. /// [Fact] public void InvokeGetResource_Test() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var getResult = dscModule.InvokeGetResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName)); Assert.True(getResult.ContainsKey("key")); Assert.True(getResult.TryGetValue("key", out object keyValue)); Assert.Equal("SimpleTestResourceKey", keyValue as string); } /// /// Calls Invoke-DscResource Get. Resource Get throws. /// [Fact] public void InvokeGetResource_ResourceThrows() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws(() => dscModule.InvokeGetResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceThrowsName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); Assert.IsType(exception.InnerException); } /// /// Calls Invoke-DscResource Get. Resource writes error. /// [Fact(Skip = "Not supported in PSDesiredStateConfiguration 2.0.7")] public void InvokeGetResource_ResourceError() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); Assert.Throws(() => dscModule.InvokeGetResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceErrorName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); } /// /// Calls Invoke-DscResource Get. Resource does not exist. /// [Fact] public void InvokeGetResource_ResourceDoesntExist() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws( () => dscModule.InvokeGetResource( pwsh, new ValueSet(), "FakeResourceName", PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); Assert.IsType(exception.InnerException); } /// /// Calls Invoke-DscResource Test. /// /// Setting value. /// Expected result. [Theory] [InlineData("4815162342", true)] [InlineData("notalostreference", false)] public void InvokeTestResource_Test(string value, bool expectedResult) { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); var settings = new ValueSet() { { "secretCode", value }, }; using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var testResult = dscModule.InvokeTestResource( pwsh, settings, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName)); Assert.Equal(expectedResult, testResult); } /// /// Calls Invoke-DscResource Test. Resource throws. /// [Fact] public void InvokeTestResource_Throws() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws(() => dscModule.InvokeTestResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceThrowsName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); Assert.IsType(exception.InnerException); } /// /// Calls Invoke-DscResource Test. Resource writes error. /// [Fact(Skip = "Not supported in PSDesiredStateConfiguration 2.0.7")] public void InvokeTestResource_ResourceError() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); Assert.Throws(() => dscModule.InvokeTestResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceErrorName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); } /// /// Calls Invoke-DscResource Test. Resource does not exist. /// [Fact] public void InvokeTestResource_ResourceDoesntExist() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws(() => _ = dscModule.InvokeTestResource( pwsh, new ValueSet(), "FakeResourceName", PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); Assert.IsType(exception.InnerException); } /// /// Calls Invoke-DscResource Set. /// /// Setting value. /// Expected reboot required. [Fact] public void InvokeSetResource_Test() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); var settings = new ValueSet() { { "secretCode", "4815162342" }, }; using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var testResult = dscModule.InvokeSetResource( pwsh, settings, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName)); // TODO: Verify reboot required when is supported for class resources. ////Assert.Equal(rebootRequired, testResult); } /// /// Calls Invoke-DscResource Set. Resource throws. /// [Fact] public void InvokeSetResource_Throws() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws(() => dscModule.InvokeSetResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceThrowsName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); Assert.IsType(exception.InnerException); } /// /// Calls Invoke-DscResource Set. Resource writes error. /// [Fact(Skip = "Not supported in PSDesiredStateConfiguration 2.0.7")] public void InvokeSetResource_ResourceError() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); Assert.Throws(() => dscModule.InvokeSetResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceErrorName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); } /// /// Calls Invoke-DscResource Set. Resource does not exist. /// [Fact] public void InvokeSetResource_ResourceDoesntExist() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws(() => dscModule.InvokeSetResource( pwsh, new ValueSet(), "FakeResourceName", PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); Assert.IsType(exception.InnerException); } /// /// Test calling Invoke-DscResource when a resource has multiple versions. /// [Fact] public void InvokeResource_MultipleVersions() { string newVersion = "0.0.2.0"; var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); // Get duplicated resources by creating a new directory and copy our modules. // Change version and add them to the PSModulePath. using var tmpDir = new TempDirectory(); tmpDir.CopyDirectory(this.fixture.TestModulesPath); var manifestFile = Path.Combine( tmpDir.FullDirectoryPath, TestModule.SimpleTestResourceModuleName, TestModule.SimpleTestResourceManifestFileName); File.WriteAllText( manifestFile, File.ReadAllText(manifestFile).Replace("0.0.0.1", newVersion)); testEnvironment.AppendPSModulePath(tmpDir.FullDirectoryPath); var dscModule = new DscModuleV2(); { using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); dscModule.InvokeSetResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName, TestModule.SimpleTestResourceVersion)); } { using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); dscModule.InvokeSetResource( pwsh, new ValueSet(), TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName, version: newVersion)); } } /// /// Calls Invoke-DscResource invalid arguments. /// [Fact] public void InvokeSetResource_InvalidArguments() { var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); var settings = new ValueSet() { { "Fake", "please dont add it" }, }; using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var e = Assert.Throws(() => dscModule.InvokeSetResource( pwsh, settings, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); Assert.Contains("The property 'Fake' cannot be found on this object.", e.Message); Assert.Equal(ConfigurationUnitResultSource.ConfigurationSet, e.ResultSource); } /// /// Tests GetDscResourcesInModule with versions. /// [Fact] public void InvokeSetResource_ModulePathSpaces() { // Copy test module to a directory with spaces. using var tmpDir = new TempDirectory(directoryName: Path.Combine(Guid.NewGuid().ToString(), "Path With Spaces")); tmpDir.CopyDirectory(this.fixture.TestModulesPath); var manifestFile = Path.Combine( tmpDir.FullDirectoryPath, TestModule.SimpleTestResourceModuleName, TestModule.SimpleTestResourceManifestFileName); var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); testEnvironment.CleanupPSModulePath(this.fixture.TestModulesPath); testEnvironment.AppendPSModulePath(tmpDir.FullDirectoryPath); var dscModule = new DscModuleV2(); var settings = new ValueSet() { { "secretCode", "4815162342" }, }; using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var testResult = dscModule.InvokeSetResource( pwsh, settings, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName)); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/DscResourceMapTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Xunit; using Xunit.Abstractions; /// /// DscResourceMap tests. /// [Collection("UnitTestCollection")] [InProc] public class DscResourceMapTests { private const string ResourceZoro = "xResourceZoro"; private const string ModuleMugiwara = "xMugiwaraModule"; private const string ModuleMugiwaraResourceLuffy = "xResourceLuffy"; private const string ModuleOni = "xModuleOni"; private const string ModuleOniResourceKaido = "xResourceKaido"; private const string ModuleOniResourceYamato = "xResourceYamato"; private static readonly Version VersionZoro = new Version("1.0.0.0"); private static readonly Version VersionLuffyGear4 = new Version("4.0.0.1"); private static readonly Version VersionLuffyGear5 = new Version("5.0.0.0"); private static readonly Version VersionKaido = new Version("0.0.0.4"); private static readonly Version VersionYamato = new Version("0.0.0.11"); private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log. public DscResourceMapTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Test GetResource for resources in the map. /// [Fact] public void DscResourcesMap_GetResource() { var dscResourceMap = new DscResourcesMap(this.CreateDscResourceInfo()); // Get just by name. var zoroResult = dscResourceMap.GetResource(ResourceZoro, null, null); Assert.NotNull(zoroResult); Assert.Equal(ResourceZoro, zoroResult.Name); Assert.Equal(VersionZoro, zoroResult.Version); // Name not normalized. var zoroResultNormalize = dscResourceMap.GetResource("XRESOURCEZORO", null, null); Assert.NotNull(zoroResultNormalize); Assert.Equal(ResourceZoro, zoroResultNormalize.Name); Assert.Equal(VersionZoro, zoroResultNormalize.Version); var yamatoResult = dscResourceMap.GetResource(ModuleOniResourceYamato, null, null); Assert.NotNull(yamatoResult); Assert.Equal(ModuleOniResourceYamato, yamatoResult.Name); Assert.Equal(ModuleOni, yamatoResult.ModuleName); Assert.Equal(VersionYamato, yamatoResult.Version); // Just by name and module get latest. var luffyResult5 = dscResourceMap.GetResource(ModuleMugiwaraResourceLuffy, ModuleMugiwara, null); Assert.NotNull(luffyResult5); Assert.Equal(ModuleMugiwaraResourceLuffy, luffyResult5.Name); Assert.Equal(ModuleMugiwara, luffyResult5.ModuleName); Assert.Equal(VersionLuffyGear5, luffyResult5.Version); // Specific version. var luffyResult4 = dscResourceMap.GetResource(ModuleMugiwaraResourceLuffy, ModuleMugiwara, VersionLuffyGear4); Assert.NotNull(luffyResult4); Assert.Equal(ModuleMugiwaraResourceLuffy, luffyResult4.Name); Assert.Equal(ModuleMugiwara, luffyResult4.ModuleName); Assert.Equal(VersionLuffyGear4, luffyResult4.Version); // Module name not normalized var luffyResult4Normalized = dscResourceMap.GetResource(ModuleMugiwaraResourceLuffy, "XMUGIWARAMODULE", VersionLuffyGear4); Assert.NotNull(luffyResult4Normalized); Assert.Equal(ModuleMugiwaraResourceLuffy, luffyResult4Normalized.Name); Assert.Equal(ModuleMugiwara, luffyResult4Normalized.ModuleName); Assert.Equal(VersionLuffyGear4, luffyResult4Normalized.Version); } /// /// Tests GetResource when resources are not in the maps. /// [Fact] public void DscResourcesMap_GetResource_NoResource() { var dscResourceMap = new DscResourcesMap(this.CreateDscResourceInfo()); // Zoro is lost. var zoroResult = dscResourceMap.GetResource(ResourceZoro, ModuleMugiwara, null); Assert.Null(zoroResult); // Yamato didn't join (spoilers) var yamatoResult = dscResourceMap.GetResource(ModuleOniResourceYamato, ModuleMugiwara, VersionYamato); Assert.Null(yamatoResult); // Gear 6 is not a thing (?) var luffyResult = dscResourceMap.GetResource(ModuleMugiwaraResourceLuffy, ModuleMugiwara, Version.Parse("6.0.0.0")); Assert.Null(luffyResult); // Wrong universe. var gokuResult = dscResourceMap.GetResource("xResourceGoku", null, null); Assert.Null(gokuResult); } /// /// Test Exists for resources in the map. /// [Fact] public void DscResourcesMap_Exists() { var dscResourceMap = new DscResourcesMap(this.CreateDscResourceInfo()); Assert.True(dscResourceMap.Exists(ResourceZoro, null, null)); Assert.True(dscResourceMap.Exists(ModuleOniResourceYamato, null, null)); Assert.True(dscResourceMap.Exists(ModuleMugiwaraResourceLuffy, ModuleMugiwara, null)); Assert.True(dscResourceMap.Exists(ModuleMugiwaraResourceLuffy, ModuleMugiwara, VersionLuffyGear4)); } /// /// Tests Exists when resources are not in the maps. /// [Fact] public void DscResourcesMap_Exists_NoResource() { var dscResourceMap = new DscResourcesMap(this.CreateDscResourceInfo()); Assert.False(dscResourceMap.Exists(ResourceZoro, ModuleMugiwara, null)); Assert.False(dscResourceMap.Exists(ModuleOniResourceYamato, ModuleMugiwara, VersionYamato)); Assert.False(dscResourceMap.Exists(ModuleMugiwaraResourceLuffy, ModuleMugiwara, Version.Parse("6.0.0.0"))); Assert.False(dscResourceMap.Exists("xResourceGoku", null, null)); } /// /// Creates 5 fake resources. /// 1 resource without module name. /// 2 resources module A same resource v1 v2. /// 2 resources module B different resources. /// /// Fake resources. private IReadOnlyList CreateDscResourceInfo() { return new List() { new DscResourceInfoInternal(ResourceZoro, null, VersionZoro), new DscResourceInfoInternal(ModuleMugiwaraResourceLuffy, ModuleMugiwara, VersionLuffyGear4), new DscResourceInfoInternal(ModuleMugiwaraResourceLuffy, ModuleMugiwara, VersionLuffyGear5), new DscResourceInfoInternal(ModuleOniResourceKaido, ModuleOni, VersionKaido), new DscResourceInfoInternal(ModuleOniResourceYamato, ModuleOni, VersionYamato), }; } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ExceptionExtensionsTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.PowerShell.Commands; using Xunit; using Xunit.Abstractions; /// /// Exception extension tests. /// [Collection("UnitTestCollection")] [InProc] public class ExceptionExtensionsTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ExceptionExtensionsTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Tests GetMostInnerException. /// [Fact] public void GetMostInnerException_Test() { var exception = new Exception( "message1", new WriteErrorException( "WriteException", new ArgumentNullException())); var mostInner = exception.GetMostInnerException(); Assert.IsType(mostInner); var exception2 = new Exception( "message2", new WriteErrorException( "WriteException2")); mostInner = exception2.GetMostInnerException(); Assert.IsType(mostInner); var exception3 = new ArgumentOutOfRangeException("message2"); mostInner = exception3.GetMostInnerException(); Assert.IsType(mostInner); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/HashtableExtensionsTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.Collections; using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Windows.Foundation.Collections; using Xunit; using Xunit.Abstractions; /// /// Hashtable extension tests. /// [Collection("UnitTestCollection")] [InProc] public class HashtableExtensionsTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public HashtableExtensionsTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Tests ToValueSet with simple types. /// [Fact] public void ToValueSet_Test() { var ht = new Hashtable() { { "key1", "value1" }, { "key2", 2 }, { "key3", true }, }; var valueSet = ht.ToValueSet(); Assert.True(valueSet.ContainsKey("key1")); Assert.Equal("value1", (string)valueSet["key1"]); Assert.True(valueSet.ContainsKey("key2")); Assert.Equal(2, (int)valueSet["key2"]); Assert.True(valueSet.ContainsKey("key3")); Assert.True((bool)valueSet["key3"]); } /// /// Test for inner hashtables. /// [Fact] public void ToValueSet_InnerHashtable() { var ht = new Hashtable() { { "hashtableKey", new Hashtable() { { "key1", "value1" }, { "key2", 2 }, { "key3", true }, } }, }; var valueSet = ht.ToValueSet(); Assert.True(valueSet.ContainsKey("hashtableKey")); var resultValueSet = (ValueSet)valueSet["hashtableKey"]; Assert.True(resultValueSet.ContainsKey("key1")); Assert.Equal("value1", (string)resultValueSet["key1"]); Assert.True(resultValueSet.ContainsKey("key2")); Assert.Equal(2, (int)resultValueSet["key2"]); Assert.True(resultValueSet.ContainsKey("key3")); Assert.True((bool)resultValueSet["key3"]); } /// /// Test for inner arrays. /// [Fact] public void ToValueSet_InnerArray() { var ht = new Hashtable() { { "arrayKey", new string[] { "s1", "s2", "s3", } }, }; var valueSet = ht.ToValueSet(); Assert.True(valueSet.ContainsKey("arrayKey")); var resultValueSet = (ValueSet)valueSet["arrayKey"]; Assert.True(resultValueSet.ContainsKey("treatAsArray")); Assert.Equal(4, resultValueSet.Count); } /// /// Test when a key is not a string. /// [Fact] public void ToValueSet_KeyNotString() { var ht = new Hashtable() { { 1, "value" }, }; Assert.Throws(() => ht.ToValueSet()); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; using System.DirectoryServices; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Microsoft.VisualBasic; using Windows.Foundation.Collections; using Windows.Storage.Streams; using WinRT; using Xunit; using Xunit.Abstractions; /// /// Unit tests for parsing configuration sets from streams. /// [Collection("UnitTestCollection")] [InProc] [OutOfProc] public class OpenConfigurationSetTests : ConfigurationProcessorTestBase { /// /// The directives key for the module property. /// internal const string ModuleDirective = "module"; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public OpenConfigurationSetTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// Passes a null stream as input. /// [Fact] public void NullStream() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(null); Assert.Null(result.Set); Assert.IsType(result.ResultCode); Assert.Equal(string.Empty, result.Field); } /// /// Passes an empty stream as input. /// [Fact] public void EmptyStream() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(string.Empty)); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_YAML, result.ResultCode.HResult); Assert.Equal(string.Empty, result.Field); Assert.Equal(0U, result.Line); Assert.Equal(0U, result.Column); } /// /// Passes a stream with a single null byte in it. /// [Fact] public void NullByteStream() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream("\0")); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_YAML, result.ResultCode.HResult); Assert.NotEqual(string.Empty, result.Field); Assert.Equal(0U, result.Line); Assert.Equal(0U, result.Column); } /// /// Passes YAML, but it isn't anything like a configuration file. /// [Fact] public void NotConfigYAML() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream("yaml: yep")); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_MISSING_FIELD, result.ResultCode.HResult); Assert.Equal("$schema", result.Field); Assert.Equal(0U, result.Line); Assert.Equal(0U, result.Column); } /// /// Passes YAML without a schema version. /// [Fact] public void NoConfigVersion() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: thing: 1 ")); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_MISSING_FIELD, result.ResultCode.HResult); Assert.Equal("configurationVersion", result.Field); Assert.Equal(0U, result.Line); Assert.Equal(0U, result.Column); } /// /// Passes YAML that appears to be from the distant future. /// [Fact] public void UnknownConfigVersion() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 99999999 ")); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, result.ResultCode.HResult); Assert.Equal("configurationVersion", result.Field); Assert.Equal("99999999", result.Value); Assert.Equal(0U, result.Line); Assert.Equal(0U, result.Column); } /// /// Has one of each type of intent to ensure that it is set properly. /// [Fact] public void EnsureIntent() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.1 assertions: - resource: Assert parameters: - resource: Inform resources: - resource: Apply ")); Assert.NotNull(result.Set); Assert.Null(result.ResultCode); Assert.Equal(string.Empty, result.Field); var units = result.Set.Units; Assert.Equal(3, units.Count); bool sawAssert = false; bool sawInform = false; bool sawApply = false; foreach (var unit in units) { Assert.Equal(unit.Type, unit.Intent.ToString()); switch (unit.Intent) { case ConfigurationUnitIntent.Assert: sawAssert = true; break; case ConfigurationUnitIntent.Inform: sawInform = true; break; case ConfigurationUnitIntent.Apply: sawApply = true; break; default: Assert.Fail("Unknown intent"); break; } } Assert.True(sawAssert); Assert.True(sawInform); Assert.True(sawApply); } /// /// Passes YAML with resources being something other than a sequence. /// [Fact] public void NonSequenceUnits() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.1 resources: 1 ")); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, result.ResultCode.HResult); Assert.Equal("resources", result.Field); Assert.Equal(4U, result.Line); Assert.NotEqual(0U, result.Column); } /// /// Passes YAML with a resource being something other than a map. /// [Fact] public void NonMapSequenceUnits() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.1 resources: - string ")); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, result.ResultCode.HResult); Assert.Equal("resources[0]", result.Field); Assert.Equal(5U, result.Line); Assert.NotEqual(0U, result.Column); } /// /// Passes YAML with all values present. /// [Fact] public void CheckAllUnitProperties() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.1 resources: - resource: Resource id: Identifier dependsOn: - Dependency1 - Dependency2 directives: Directive1: A Directive2: B settings: Setting1: '1' Setting2: 2 ")); Assert.NotNull(result.Set); Assert.Null(result.ResultCode); Assert.Equal(string.Empty, result.Field); Assert.NotEqual(Guid.Empty, result.Set.InstanceIdentifier); var units = result.Set.Units; Assert.NotNull(units); Assert.Equal(1, units.Count); ConfigurationUnit unit = units[0]; Assert.NotNull(unit); Assert.Equal("Resource", unit.Type); Assert.NotEqual(Guid.Empty, unit.InstanceIdentifier); Assert.Equal("Identifier", unit.Identifier); Assert.Equal(ConfigurationUnitIntent.Apply, unit.Intent); var dependencies = unit.Dependencies; Assert.NotNull(dependencies); Assert.Equal(2, dependencies.Count); Assert.Contains("Dependency1", dependencies); Assert.Contains("Dependency2", dependencies); var directives = unit.Metadata; Assert.NotNull(directives); Assert.Equal(2, directives.Count); Assert.Contains("Directive1", directives); Assert.Equal("A", directives["Directive1"]); Assert.Contains("Directive2", directives); Assert.Equal("B", directives["Directive2"]); var settings = unit.Settings; Assert.NotNull(settings); Assert.Equal(2, settings.Count); Assert.Contains("Setting1", settings); Assert.Equal("1", settings["Setting1"]); Assert.Contains("Setting2", settings); Assert.Equal(2L, settings["Setting2"]); Assert.Null(unit.Details); Assert.Equal(ConfigurationUnitState.Unknown, unit.State); Assert.Null(unit.ResultInformation); Assert.True(unit.IsActive); } /// /// Test type of scalar nodes. /// [Fact] public void CheckUnitScalarTypes() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.1 resources: - resource: Resource id: Identifier settings: SettingInt: 1 SettingString: '1' SettingBool: false SettingStringBool: 'false' ")); Assert.NotNull(result.Set); Assert.Null(result.ResultCode); var units = result.Set.Units; Assert.NotNull(units); Assert.Equal(1, units.Count); ConfigurationUnit unit = units[0]; Assert.NotNull(unit); var settings = unit.Settings; Assert.NotNull(settings); Assert.Equal(4, settings.Count); Assert.Contains("SettingInt", settings); Assert.Equal(1L, settings["SettingInt"]); Assert.Contains("SettingString", settings); Assert.Equal("1", settings["SettingString"]); Assert.Contains("SettingBool", settings); Assert.Equal(false, settings["SettingBool"]); Assert.Contains("SettingStringBool", settings); Assert.Equal("false", settings["SettingStringBool"]); } /// /// Test that module gets left in resource name in 0.1. /// [Fact] public void ModuleInResourceName_NotFor0_1() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.1 resources: - resource: Module/Resource id: Identifier settings: SettingInt: 1 ")); Assert.NotNull(result.Set); Assert.Null(result.ResultCode); Assert.Equal("0.1", result.Set.SchemaVersion); Assert.Single(result.Set.Units); var unit = result.Set.Units[0]; Assert.NotNull(unit); Assert.Equal("Module/Resource", unit.Type); Assert.Empty(unit.Metadata); } /// /// Test that module gets parsed out of resource name in 0.2. /// [Fact] public void ModuleInResourceName() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.2 resources: - resource: Module/Resource id: Identifier directives: module: Module settings: SettingInt: 1 ")); Assert.NotNull(result.Set); Assert.Null(result.ResultCode); Assert.Equal("0.2", result.Set.SchemaVersion); Assert.Single(result.Set.Units); var unit = result.Set.Units[0]; Assert.NotNull(unit); Assert.Equal("Resource", unit.Type); Assert.Single(unit.Metadata); Assert.True(unit.Metadata.ContainsKey(ModuleDirective)); Assert.Equal("Module", unit.Metadata[ModuleDirective]); } /// /// Test that module is in the resource name and the directives and are different. /// [Fact] public void ModuleInResourceName_DirectiveDifferent() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.2 resources: - resource: Module/Resource id: Identifier directives: module: DifferentModule settings: SettingInt: 1 ")); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, result.ResultCode.HResult); Assert.Equal(ModuleDirective, result.Field); Assert.Equal("DifferentModule", result.Value); Assert.Equal(5U, result.Line); Assert.NotEqual(0U, result.Column); } /// /// Test that providing only the module in the qualified name is an error. /// [Fact] public void EmptyResourceWithModule() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.2 resources: - resource: Module/ id: Identifier settings: SettingInt: 1 ")); Assert.Null(result.Set); Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, result.ResultCode.HResult); Assert.Equal("resource", result.Field); Assert.Equal("Module/", result.Value); Assert.Equal(5U, result.Line); Assert.NotEqual(0U, result.Column); } /// /// Verifies that the configuration set (0.2) can be serialized and reopened correctly. /// [Fact] public void TestSet_Serialize_0_2() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.2 assertions: - resource: FakeModule/FakeResource id: TestId directives: description: FakeDescription allowPrerelease: true securityContext: elevated settings: TestString: Hello TestBool: false TestInt: 1234 resources: - resource: FakeModule2/FakeResource2 id: TestId2 dependsOn: - TestId - dependency2 - dependency3 directives: description: FakeDescription2 securityContext: elevated settings: TestString: Bye TestBool: true TestInt: 4321 Mapping: Key: TestValue ")); // Serialize set. ConfigurationSet configurationSet = openResult.Set; InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); configurationSet.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); Assert.Null(serializedSetResult.ResultCode); ConfigurationSet set = serializedSetResult.Set; Assert.NotNull(set); Assert.Equal("0.2", set.SchemaVersion); Assert.Equal(2, set.Units.Count); Assert.Equal("FakeResource", set.Units[0].Type); Assert.Equal(ConfigurationUnitIntent.Assert, set.Units[0].Intent); Assert.Equal("TestId", set.Units[0].Identifier); this.VerifyValueSet(set.Units[0].Metadata, new ("description", "FakeDescription"), new ("allowPrerelease", true), new ("module", "FakeModule")); this.VerifyValueSet(set.Units[0].Settings, new ("TestString", "Hello"), new ("TestBool", false), new ("TestInt", 1234)); Assert.Equal("FakeResource2", set.Units[1].Type); Assert.Equal(ConfigurationUnitIntent.Apply, set.Units[1].Intent); Assert.Equal("TestId2", set.Units[1].Identifier); this.VerifyStringArray(set.Units[1].Dependencies, "TestId", "dependency2", "dependency3"); this.VerifyValueSet(set.Units[1].Metadata, new ("description", "FakeDescription2"), new ("module", "FakeModule2")); ValueSet mapping = new ValueSet(); mapping.Add("Key", "TestValue"); this.VerifyValueSet(set.Units[1].Settings, new ("TestString", "Bye"), new ("TestBool", true), new ("TestInt", 4321), new ("Mapping", mapping)); } /// /// Verifies that the configuration set (0.3) can be serialized and reopened correctly. /// [Fact] public void TestSet_Serialize_0_3() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: description: FakeSetDescription variables: var1: Test1 var2: 42 parameters: param1: type: securestring param2: type: int defaultValue: 89 resources: - type: FakeModule/FakeResource name: TestId metadata: description: FakeDescription allowPrerelease: true myVal: mine properties: TestString: Hello TestBool: false TestInt: 1234 - type: FakeModule2/FakeResource2 name: TestId2 dependsOn: - TestId - dependency2 - dependency3 metadata: description: FakeDescription2 myVal: yours properties: TestString: Bye TestBool: true TestInt: 4321 Mapping: Key: TestValue - type: FakeModule/FakeResource3 name: TestId3 metadata: isGroup: true properties: other: value resources: - type: Grouped/Resource name: Child properties: b: c ")); // Serialize set. ConfigurationSet configurationSet = openResult.Set; InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); configurationSet.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); Assert.Null(serializedSetResult.ResultCode); ConfigurationSet set = serializedSetResult.Set; Assert.NotNull(set); Assert.Equal("0.3", set.SchemaVersion); Assert.Equal(3, set.Units.Count); this.VerifyValueSet(set.Metadata, new KeyValuePair("description", "FakeSetDescription")); this.VerifyValueSet(set.Variables, new ("var1", "Test1"), new ("var2", 42)); Assert.Equal(2, set.Parameters.Count); this.VerifyParameter(set.Parameters[0], "param1", Windows.Foundation.PropertyType.String, true); this.VerifyParameter(set.Parameters[1], "param2", Windows.Foundation.PropertyType.Int64, false, 89); Assert.Equal("FakeModule/FakeResource", set.Units[0].Type); Assert.Equal("TestId", set.Units[0].Identifier); this.VerifyValueSet(set.Units[0].Metadata, new ("description", "FakeDescription"), new ("allowPrerelease", true), new ("myVal", "mine")); this.VerifyValueSet(set.Units[0].Settings, new ("TestString", "Hello"), new ("TestBool", false), new ("TestInt", 1234)); Assert.Equal("FakeModule2/FakeResource2", set.Units[1].Type); Assert.Equal("TestId2", set.Units[1].Identifier); this.VerifyStringArray(set.Units[1].Dependencies, "TestId", "dependency2", "dependency3"); this.VerifyValueSet(set.Units[1].Metadata, new ("description", "FakeDescription2"), new ("myVal", "yours")); ValueSet mapping = new ValueSet(); mapping.Add("Key", "TestValue"); this.VerifyValueSet(set.Units[1].Settings, new ("TestString", "Bye"), new ("TestBool", true), new ("TestInt", 4321), new ("Mapping", mapping)); Assert.Equal("FakeModule/FakeResource3", set.Units[2].Type); Assert.Equal("TestId3", set.Units[2].Identifier); Assert.True(set.Units[2].IsGroup); ValueSet childResource = new ValueSet(); childResource.Add("type", "Grouped/Resource"); childResource.Add("name", "Child"); ValueSet childResourceProperties = new ValueSet(); childResourceProperties.Add("b", "c"); childResource.Add("properties", childResourceProperties); ValueSet resourcesArray = new ValueSet(); resourcesArray.Add("treatAsArray", true); resourcesArray.Add("0", childResource); this.VerifyValueSet(set.Units[2].Settings, new ("other", "value"), new ("resources", resourcesArray)); var groupChildren = set.Units[2].Units; Assert.Single(groupChildren); Assert.Equal("Grouped/Resource", groupChildren[0].Type); Assert.Equal("Child", groupChildren[0].Identifier); this.VerifyValueSet(groupChildren[0].Settings, new KeyValuePair("b", "c")); } /// /// Test for using version 0.3 schema. /// [Fact] public void BasicVersion_0_3() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: a: 1 b: '2' variables: v1: var1 v2: 42 resources: - name: Name type: Module/Resource metadata: e: '5' f: 6 properties: c: 3 d: '4' dependsOn: - g - h - name: Name2 type: Module/Resource2 dependsOn: - m properties: l: '10' metadata: i: '7' j: 8 q: 42 ")); Assert.Null(result.ResultCode); Assert.NotNull(result.Set); Assert.Equal(string.Empty, result.Field); Assert.Equal(string.Empty, result.Value); Assert.Equal(0U, result.Line); Assert.Equal(0U, result.Column); ConfigurationSet set = result.Set; Assert.Equal("0.3", set.SchemaVersion); Assert.NotNull(set.SchemaUri); Assert.Equal("https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json", set.SchemaUri.ToString()); this.VerifyValueSet(set.Metadata, new ("a", 1), new ("b", "2")); this.VerifyValueSet(set.Variables, new ("v1", "var1"), new ("v2", 42)); Assert.Empty(set.Parameters); Assert.Equal(2, set.Units.Count); this.VerifyUnitProperties(set.Units[0], "Name", "Module/Resource"); this.VerifyValueSet(set.Units[0].Metadata, new ("e", "5"), new ("f", 6)); this.VerifyValueSet(set.Units[0].Settings, new ("c", 3), new ("d", "4")); this.VerifyStringArray(set.Units[0].Dependencies, "g", "h"); this.VerifyUnitProperties(set.Units[1], "Name2", "Module/Resource2"); this.VerifyValueSet(set.Units[1].Metadata, new ("i", "7"), new ("j", 8), new ("q", 42)); this.VerifyValueSet(set.Units[1].Settings, new KeyValuePair("l", "10")); this.VerifyStringArray(set.Units[1].Dependencies, "m"); } /// /// Test for the successful parsing of default value of a parameter. /// /// The type. /// The default value. /// The expected value. /// The expected type. /// The secure state. [Theory] [InlineData("string", "abc", "abc", Windows.Foundation.PropertyType.String)] [InlineData("string", "'42'", "42", Windows.Foundation.PropertyType.String)] [InlineData("securestring", "abcdef", "abcdef", Windows.Foundation.PropertyType.String, true)] [InlineData("int", "42", 42, Windows.Foundation.PropertyType.Int64)] [InlineData("bool", "true", true, Windows.Foundation.PropertyType.Boolean)] [InlineData("object", "string", "string", Windows.Foundation.PropertyType.Inspectable)] [InlineData("object", "42", 42, Windows.Foundation.PropertyType.Inspectable)] [InlineData("secureobject", "string", "string", Windows.Foundation.PropertyType.Inspectable, true)] [InlineData("secureobject", "42", 42, Windows.Foundation.PropertyType.Inspectable, true)] public void Parameters_DefaultValue_Success(string type, string defaultValue, object expectedValue, object expectedType, bool secure = false) { this.TestParameterDefaultValue(type, defaultValue, expectedValue, Assert.IsType(expectedType), secure); } /// /// Test for the failed parsing of default value of a parameter. /// /// The type. /// The default value. /// The expected value. [Theory] [InlineData("string", "42")] [InlineData("int", "abc")] [InlineData("int", "'42'", "42")] [InlineData("bool", "'true'", "true")] public void Parameters_DefaultValue_Failure(string type, string defaultValue, object? expectedValue = null) { this.TestParameterDefaultValue(type, defaultValue, expectedValue); } /// /// Test to ensure that schema version and uri is working as expected. /// /// The version. /// The uri. [Theory] [InlineData("0.1", null)] [InlineData("0.2", null)] [InlineData("0.3", "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json")] public void Schema_Version_Uri(string version, string? uri) { ConfigurationSet set = this.ConfigurationSet(); set.SchemaVersion = version; if (uri != null) { Assert.Equal(uri, set.SchemaUri.AbsoluteUri); } else { Assert.Null(set.SchemaUri); } if (!string.IsNullOrEmpty(uri)) { set.SchemaUri = new Uri(uri); Assert.Equal(version, set.SchemaVersion); } } /// /// Verifies that the configuration set (0.2) with environments parses and serializes. /// [Fact] public void Environment_0_2() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" properties: configurationVersion: 0.2 resources: - resource: FakeModule/FakeResource id: elevated directives: description: FakeDescription allowPrerelease: true securityContext: elevated settings: TestString: Hello - resource: FakeModule2/FakeResource2 id: restricted directives: description: FakeDescription2 securityContext: restricted settings: TestString: Bye - resource: FakeModule2/FakeResource2 id: current directives: securityContext: current settings: TestString: Bye - resource: FakeModule2/FakeResource2 id: default settings: TestString: Bye ")); Dictionary expectedEnvironments = new Dictionary(); expectedEnvironments.Add("elevated", SecurityContext.Elevated); expectedEnvironments.Add("restricted", SecurityContext.Restricted); expectedEnvironments.Add("current", SecurityContext.Current); expectedEnvironments.Add("default", SecurityContext.Current); this.ValidateSecurityContexts(openResult, expectedEnvironments); // Shuffle security contexts, serialize, parse and validate again expectedEnvironments["elevated"] = SecurityContext.Restricted; expectedEnvironments["restricted"] = SecurityContext.Current; expectedEnvironments["current"] = SecurityContext.Restricted; expectedEnvironments["default"] = SecurityContext.Elevated; var units = openResult.Set.Units; foreach (var unit in units) { SecurityContext newContext = SecurityContext.Current; Assert.True(expectedEnvironments.TryGetValue(unit.Identifier, out newContext)); unit.Environment.Context = newContext; } // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); openResult.Set.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); this.ValidateSecurityContexts(serializedSetResult, expectedEnvironments); } /// /// Verifies that the configuration set (0.3) inherits set environment. /// [Fact] public void SetMetadataEnvironmentInheritance_0_3() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: winget: securityContext: elevated processor: identifier: pwsh properties: a: b resources: - name: first type: Module/Resource properties: c: 3 - name: second type: Module/Resource2 properties: l: '10' ")); Dictionary environmentProperties = new Dictionary(); environmentProperties.Add("a", "b"); ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }; Dictionary expectedEnvironments = new Dictionary(); expectedEnvironments.Add("first", new ConfigurationEnvironmentData()); expectedEnvironments.Add("second", new ConfigurationEnvironmentData()); this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); openResult.Set.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// /// Verifies that the configuration set (0.3) inherits set environment. /// [Fact] public void SetMetadataEnvironmentInheritance_ProcessorOverridden_0_3() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: winget: securityContext: elevated processor: identifier: pwsh properties: a: b resources: - name: first type: Module/Resource properties: c: 3 - name: second type: Module/Resource2 properties: l: '10' metadata: winget: processor: not-pwsh ")); Dictionary environmentProperties = new Dictionary(); environmentProperties.Add("a", "b"); ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }; Dictionary expectedEnvironments = new Dictionary(); expectedEnvironments.Add("first", new ConfigurationEnvironmentData()); expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { ProcessorIdentifier = "not-pwsh" }); this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); openResult.Set.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// /// Verifies that the configuration set (0.3) inherits set environment. /// [Fact] public void SetMetadataEnvironmentInheritance_ContextOverridden_0_3() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: winget: securityContext: elevated processor: identifier: pwsh properties: a: b resources: - name: first type: Module/Resource properties: c: 3 - name: second type: Module/Resource2 properties: l: '10' metadata: winget: securityContext: restricted ")); Dictionary environmentProperties = new Dictionary(); environmentProperties.Add("a", "b"); ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }; Dictionary expectedEnvironments = new Dictionary(); expectedEnvironments.Add("first", new ConfigurationEnvironmentData()); expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted }); this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); openResult.Set.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// /// Verifies that the configuration set (0.3) serializes common environment to the set metadata. /// [Fact] public void CommonEnvironmentElevatedToSetMetadata_0_3() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json resources: - name: first type: Module/Resource metadata: winget: securityContext: elevated processor: pwsh properties: c: 3 - name: second type: Module/Resource2 properties: l: '10' metadata: winget: securityContext: elevated processor: identifier: pwsh ")); ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData(); Dictionary expectedEnvironments = new Dictionary(); expectedEnvironments.Add("first", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh" }); expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh" }); this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); openResult.Set.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// /// Verifies that the configuration set (0.3) serializes common environment to the set metadata. /// [Fact] public void CommonProcessorElevatedToSetMetadata_0_3() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json resources: - name: first type: Module/Resource metadata: winget: securityContext: elevated processor: pwsh properties: c: 3 - name: second type: Module/Resource2 properties: l: '10' metadata: winget: securityContext: restricted processor: identifier: pwsh ")); ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData(); Dictionary expectedEnvironments = new Dictionary(); expectedEnvironments.Add("first", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh" }); expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted, ProcessorIdentifier = "pwsh" }); this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); openResult.Set.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// /// Verifies that the configuration set (0.3) environments work with group units. /// [Fact] public void EnvironmentsWithGroups_0_3() { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: winget: securityContext: elevated processor: identifier: pwsh properties: a: b resources: - name: non-group type: Module/Resource2 properties: l: '10' - name: group type: Module/Resource metadata: isGroup: true winget: securityContext: restricted properties: resources: - name: inherit type: Module/Resource properties: a: b - name: override type: Module/Resource properties: c: d metadata: winget: processor: not-pwsh ")); Dictionary environmentProperties = new Dictionary(); environmentProperties.Add("a", "b"); ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }; Dictionary expectedEnvironments = new Dictionary(); expectedEnvironments.Add("non-group", new ConfigurationEnvironmentData()); expectedEnvironments.Add("group", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted }); Dictionary groupExpectedEnvironments = new Dictionary(); groupExpectedEnvironments.Add("inherit", new ConfigurationEnvironmentData()); groupExpectedEnvironments.Add("override", new ConfigurationEnvironmentData() { ProcessorIdentifier = "not-pwsh" }); this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments, "group", groupExpectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); openResult.Set.Serialize(stream); string yamlOutput = this.ReadStream(stream); // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments, "group", groupExpectedEnvironments); } private void ValidateEnvironments(OpenConfigurationSetResult openResult, ConfigurationEnvironmentData setEnvironment, Dictionary expectedEnvironments, string? groupToCheck = null, Dictionary? groupExpectedEnvironments = null) { Assert.Null(openResult.ResultCode); Assert.NotNull(openResult.Set); ConfigurationSet configurationSet = openResult.Set; this.ValidateEnvironment(setEnvironment, configurationSet.Environment); var units = configurationSet.Units; this.ValidateEnvironments(units, expectedEnvironments, groupToCheck, groupExpectedEnvironments); } private void ValidateEnvironments(IList units, Dictionary expectedEnvironments, string? groupToCheck = null, Dictionary? groupExpectedEnvironments = null) { Assert.Equal(expectedEnvironments.Count, units.Count); foreach (var unit in units) { ConfigurationEnvironmentData? expectedEnvironment = null; Assert.True(expectedEnvironments.TryGetValue(unit.Identifier, out expectedEnvironment)); this.ValidateEnvironment(expectedEnvironment, unit.Environment); if (unit.Identifier == groupToCheck) { Assert.True(unit.IsGroup); Assert.NotNull(groupExpectedEnvironments); var groupUnits = unit.Units; this.ValidateEnvironments(groupUnits, groupExpectedEnvironments); } } } private void ValidateEnvironment(ConfigurationEnvironmentData? expectedEnvironment, ConfigurationEnvironment? actualEnvironment) { Assert.NotNull(expectedEnvironment); Assert.NotNull(actualEnvironment); Assert.Equal(expectedEnvironment.Context, actualEnvironment.Context); Assert.Equal(expectedEnvironment.ProcessorIdentifier, actualEnvironment.ProcessorIdentifier); Assert.True(expectedEnvironment.PropertiesEqual(actualEnvironment.ProcessorProperties)); } private void ValidateSecurityContexts(OpenConfigurationSetResult openResult, Dictionary expectedContexts) { Assert.Null(openResult.ResultCode); Assert.NotNull(openResult.Set); ConfigurationSet configurationSet = openResult.Set; var units = configurationSet.Units; Assert.Equal(expectedContexts.Count, units.Count); foreach (var unit in units) { SecurityContext expectedContext = SecurityContext.Current; Assert.True(expectedContexts.TryGetValue(unit.Identifier, out expectedContext)); Assert.Equal(expectedContext, unit.Environment.Context); } } private void TestParameterDefaultValue(string type, string defaultValue, object? expectedValue = null, Windows.Foundation.PropertyType? expectedType = null, bool secure = false) { ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(string.Format( @" $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json parameters: {0}: type: {0} defaultValue: {1} ", type, defaultValue))); if (expectedType != null) { Assert.Null(result.ResultCode); Assert.NotNull(result.Set); Assert.Equal(string.Empty, result.Field); Assert.Equal(string.Empty, result.Value); Assert.Equal(0U, result.Line); Assert.Equal(0U, result.Column); var parameters = result.Set.Parameters; Assert.NotNull(parameters); Assert.Single(parameters); Assert.Equal(type, parameters[0].Name); Assert.Equal(expectedType, parameters[0].Type); Assert.Equal(secure, parameters[0].IsSecure); Assert.NotNull(expectedValue); this.VerifyObject(type, expectedValue, parameters[0].DefaultValue); } else { Assert.NotNull(result.ResultCode); Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, result.ResultCode.HResult); Assert.Null(result.Set); Assert.Equal("defaultValue", result.Field); Assert.Equal(expectedValue?.ToString() ?? defaultValue, result.Value); Assert.NotEqual(0U, result.Line); Assert.NotEqual(0U, result.Column); } } private void VerifyUnitProperties(ConfigurationUnit unit, string identifier, string type) { Assert.NotNull(unit); Assert.Equal(identifier, unit.Identifier); Assert.Equal(type, unit.Type); } private void VerifyValueSet(ValueSet values, params KeyValuePair[] expected) { Assert.NotNull(values); Assert.Equal(expected.Length, values.Count); foreach (var expectation in expected) { Assert.True(values.ContainsKey(expectation.Key), $"Not Found {expectation.Key}"); object value = values[expectation.Key]; this.VerifyObject(expectation.Key, expectation.Value, value); } } private void VerifyStringArray(IList strings, params string[] expected) { Assert.NotNull(strings); Assert.Equal(expected.Length, strings.Count); foreach (var expectation in expected) { bool found = false; foreach (var value in strings) { if (!found) { found = expectation == value; } } Assert.True(found, $"Did not find {expectation} in string array"); } } private void VerifyParameter(ConfigurationParameter parameter, string name, Windows.Foundation.PropertyType type, bool secure, object? defaultValue = null) { Assert.Equal(name, parameter.Name); Assert.Equal(type, parameter.Type); Assert.Equal(secure, parameter.IsSecure); this.VerifyObject(name, defaultValue, parameter.DefaultValue); } private void VerifyObject(string name, object? expectedValue, object? actualValue) { if (expectedValue != null) { Assert.NotNull(actualValue); switch (expectedValue) { case int i: Assert.True(i == (int)(long)actualValue, $"{name}: expected[{i}], actual[{(int)(long)actualValue}]"); break; case string s: Assert.True(s == (string)actualValue, $"{name}: expected[{s}], actual[{(string)actualValue}]"); break; case bool b: Assert.True(b == (bool)actualValue, $"{name}: expected[{b}], actual[{(bool)actualValue}]"); break; case ValueSet v: var actualValueSet = actualValue.As(); Assert.True(v.ContentEquals(actualValueSet), $"ValueSets not equal: {name}\n---expected---:\n{v.ToYaml()}\n---actual---:\n{actualValueSet.ToYaml()}"); break; default: Assert.Fail($"Add expected type `{expectedValue.GetType().Name}` to switch statement for {name}."); break; } } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/PowerShellHelperTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using Microsoft.Management.Configuration.Processor.PowerShell.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Xunit; using Xunit.Abstractions; /// /// PowerShell helper tests. /// [Collection("UnitTestCollection")] [InProc] public class PowerShellHelperTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public PowerShellHelperTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Tests CreateModuleSpecification. /// [Fact] public void CreateModuleSpecification_Module_Test() { string moduleName = "MyModule"; var moduleSpecification = PowerShellHelpers.CreateModuleSpecification(moduleName); Assert.Equal(moduleName, moduleSpecification.Name); Assert.Null(moduleSpecification.RequiredVersion); Assert.Null(moduleSpecification.Version); Assert.Null(moduleSpecification.MaximumVersion); Assert.Null(moduleSpecification.Guid); } /// /// Tests CreateModuleSpecification with arguments. use version. /// [Fact] public void CreateModuleSpecification_Required_Test() { string moduleName = "MyModule"; string version = "0.1.0"; string guid = Guid.NewGuid().ToString(); var moduleSpecification = PowerShellHelpers.CreateModuleSpecification( moduleName, version, null, null, guid); Assert.Equal(moduleName, moduleSpecification.Name); Assert.NotNull(moduleSpecification.RequiredVersion); Assert.Equal(version, moduleSpecification.RequiredVersion.ToString()); Assert.Null(moduleSpecification.Version); Assert.Null(moduleSpecification.MaximumVersion); Assert.NotNull(moduleSpecification.Guid); Assert.Equal(guid, moduleSpecification.Guid.ToString()); } /// /// Tests CreateModuleSpecification with arguments. Use min version. /// [Fact] public void CreateModuleSpecification_MinMax_Test() { string moduleName = "MyModule"; string minVersion = "0.0.1"; string maxVersion = "1.0.0"; string guid = Guid.NewGuid().ToString(); var moduleSpecification = PowerShellHelpers.CreateModuleSpecification( moduleName, null, minVersion, maxVersion, guid); Assert.Equal(moduleName, moduleSpecification.Name); Assert.Null(moduleSpecification.RequiredVersion); Assert.NotNull(moduleSpecification.Version); Assert.Equal(minVersion, moduleSpecification.Version.ToString()); Assert.NotNull(moduleSpecification.MaximumVersion); Assert.Equal(maxVersion, moduleSpecification.MaximumVersion.ToString()); Assert.NotNull(moduleSpecification.Guid); Assert.Equal(guid, moduleSpecification.Guid.ToString()); } /// /// Tests CreateModuleSpecification with prerelease versions. /// [Fact] public void CreateModuleSpecification_PrereleaseVersions_Required_Test() { string moduleName = "MyModule"; string preVersion = "0.1.0-pre"; string version = "0.1.0"; var moduleSpecification = PowerShellHelpers.CreateModuleSpecification( moduleName, preVersion); Assert.Equal(moduleName, moduleSpecification.Name); Assert.NotNull(moduleSpecification.RequiredVersion); Assert.Equal(version, moduleSpecification.RequiredVersion.ToString()); Assert.Null(moduleSpecification.Version); Assert.Null(moduleSpecification.MaximumVersion); Assert.Null(moduleSpecification.Guid); } /// /// Tests CreateModuleSpecification with prerelease versions. /// [Fact] public void CreateModuleSpecification_PrereleaseVersions_Test() { string moduleName = "MyModule"; string preMinVersion = "0.0.1-pre"; string minVersion = "0.0.1"; string preMaxVersion = "1.0.0-pre"; string maxVersion = "1.0.0"; var moduleSpecification = PowerShellHelpers.CreateModuleSpecification( moduleName, null, preMinVersion, preMaxVersion); Assert.Equal(moduleName, moduleSpecification.Name); Assert.Null(moduleSpecification.RequiredVersion); Assert.NotNull(moduleSpecification.Version); Assert.Equal(minVersion, moduleSpecification.Version.ToString()); Assert.NotNull(moduleSpecification.MaximumVersion); Assert.Equal(maxVersion, moduleSpecification.MaximumVersion.ToString()); Assert.Null(moduleSpecification.Guid); } /// /// Tests CreateModuleSpecification with arguments. Don't use minVersion and version. /// [Fact] public void CreateModuleSpecification_Args_Throws_Test() { string moduleName = "MyModule"; string version = "0.1.0"; string minVersion = "0.0.1"; string maxVersion = "1.0.0"; Assert.Throws(() => PowerShellHelpers.CreateModuleSpecification( moduleName, version, minVersion, null, null)); Assert.Throws(() => PowerShellHelpers.CreateModuleSpecification( moduleName, version, null, maxVersion, null)); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ProcessorEnvironmentTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.Collections.Generic; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Moq; using Xunit; using Xunit.Abstractions; using static Microsoft.Management.Configuration.Processor.PowerShell.Constants.PowerShellConstants; /// /// HostedEnvironment tests, that is more ProcessorEnvironmentBase tests for non forwarding functions. /// [Collection("UnitTestCollection")] [InProc] public class ProcessorEnvironmentTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log. public ProcessorEnvironmentTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Test SetVariable and SetVariable methods. /// [Fact] public void HostedEnvironment_Variables() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); // As string. string var1Name = "var1"; string var1 = "This is a string"; processorEnv.SetVariable(var1Name, var1); string var1Result = processorEnv.GetVariable(var1Name); Assert.Equal(var1, var1Result); // As int. string var2Name = "var2"; int var2 = 42; processorEnv.SetVariable(var2Name, var2); int var2Result = processorEnv.GetVariable(var2Name); // Wrong type. Assert.Throws(() => processorEnv.GetVariable(var1Name)); } /// /// Tests SetPsModulePath. /// [Fact] public void HostedEnvironment_SetPSModulePath() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); string psModulePathInput = "SetPSModulePathModulePath"; string psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.NotEqual(psModulePathInput, psModulePath); processorEnv.SetPSModulePath(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.Equal(psModulePathInput, psModulePath); } /// /// Tests SetPsModulePaths. /// [Fact] public void HostedEnvironment_SetPSModulePaths() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); var psModulePathInput = new List() { "Path1", "Path2", "Path3", "Path4", }; string psModulePathExpected = "Path1;Path2;Path3;Path4"; string psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.NotEqual(psModulePathExpected, psModulePath); processorEnv.SetPSModulePaths(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.Equal(psModulePathExpected, psModulePath); } /// /// Tests AppendPSModulePath. /// [Fact] public void HostedEnvironment_AppendPSModulePath() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); string psModulePathInput = "AppendPSModulePathModulePath"; string psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.False(psModulePath.EndsWith($";{psModulePathInput}")); processorEnv.AppendPSModulePath(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.EndsWith($";{psModulePathInput}", psModulePath); // No duplicates processorEnv.AppendPSModulePath(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.False(psModulePath.EndsWith($";{psModulePathInput};{psModulePathInput}")); Assert.EndsWith($";{psModulePathInput}", psModulePath); } /// /// Tests AppendPSModulePaths. /// [Fact] public void HostedEnvironment_AppendPSModulePaths() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); var psModulePathInput = new List() { "AppendPSModulePathsPath1", "AppendPSModulePathsPath2", "AppendPSModulePathsPath3", "AppendPSModulePathsPath4", }; string psModulePathExpected = "AppendPSModulePathsPath1;AppendPSModulePathsPath2;AppendPSModulePathsPath3;AppendPSModulePathsPath4"; string psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.False(psModulePath.EndsWith($";{psModulePathExpected}")); processorEnv.AppendPSModulePaths(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.EndsWith($";{psModulePathExpected}", psModulePath); // No duplicates psModulePathInput = new List() { "AppendPSModulePathsPath1", "AppendPSModulePathsPath5", "AppendPSModulePathsPath2", }; processorEnv.AppendPSModulePaths(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.EndsWith($";{psModulePathExpected};AppendPSModulePathsPath5", psModulePath); } /// /// Tests PrependPSModulePath. /// [Fact] public void HostedEnvironment_PrependPSModulePath() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); string psModulePathInput = "PrependPSModulePathModulePath"; string psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.False(psModulePath.StartsWith($";{psModulePathInput}")); processorEnv.PrependPSModulePath(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.StartsWith($"{psModulePathInput};", psModulePath); // No duplicates processorEnv.PrependPSModulePath(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.False(psModulePath.StartsWith($"{psModulePathInput};{psModulePathInput};")); Assert.StartsWith($"{psModulePathInput};", psModulePath); } /// /// Tests PrependPSModulePaths. /// [Fact] public void HostedEnvironment_PrependPSModulePaths() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); var psModulePathInput = new List() { "PrependPSModulePathsPath1", "PrependPSModulePathsPath2", "PrependPSModulePathsPath3", "PrependPSModulePathsPath4", }; string psModulePathExpected = "PrependPSModulePathsPath1;PrependPSModulePathsPath2;PrependPSModulePathsPath3;PrependPSModulePathsPath4"; string psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.False(psModulePath.StartsWith($";{psModulePathExpected}")); processorEnv.PrependPSModulePaths(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.StartsWith($"{psModulePathExpected};", psModulePath); // No duplicates psModulePathInput = new List() { "PrependPSModulePathsPath1", "PrependPSModulePathsPath5", "PrependPSModulePathsPath2", }; processorEnv.PrependPSModulePaths(psModulePathInput); psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.StartsWith($"PrependPSModulePathsPath5;{psModulePathExpected};", psModulePath); } /// /// Test CleanupPSModulePath. /// [Fact] public void HostedEnvironment_CleanupPSModulePath() { var processorEnv = this.fixture.PrepareTestProcessorEnvironment(); var psModulePathInput = new List() { "CleanupPSModulePathPath1", "CleanupPSModulePathPath2", "CleanupPSModulePathPath3", "CleanupPSModulePathPath1", "CleanupPSModulePathPath1", }; string psModulePathExpected = "CleanupPSModulePathPath1;CleanupPSModulePathPath2;CleanupPSModulePathPath3;CleanupPSModulePathPath1;CleanupPSModulePathPath1"; processorEnv.SetPSModulePaths(psModulePathInput); string psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.Equal(psModulePathExpected, psModulePath); processorEnv.CleanupPSModulePath("CleanupPSModulePathPath1"); psModulePathExpected = "CleanupPSModulePathPath2;CleanupPSModulePathPath3"; psModulePath = processorEnv.GetVariable(Variables.PSModulePath); Assert.Equal(psModulePathExpected, psModulePath); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/SemanticVersionTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Xunit; using Xunit.Abstractions; /// /// Semantic version tests. /// [Collection("UnitTestCollection")] [InProc] public class SemanticVersionTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public SemanticVersionTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Test version. /// [Fact] public void SemanticVersion_Test() { var semanticVersion = new SemanticVersion("1.0.1"); Assert.Equal("1.0.1", semanticVersion.ToString()); Assert.Equal(new Version("1.0.1"), semanticVersion.Version); Assert.False(semanticVersion.IsPrerelease); Assert.Null(semanticVersion.PrereleaseTag); } /// /// Tests prerelease version. /// [Fact] public void PrereleaseVersion_Test() { var semanticVersion = new SemanticVersion("1.0.1-pre"); Assert.Equal("1.0.1-pre", semanticVersion.ToString()); Assert.Equal(new Version("1.0.1"), semanticVersion.Version); Assert.True(semanticVersion.IsPrerelease); Assert.Equal("pre", semanticVersion.PrereleaseTag); } /// /// Tests GetMaximumVersion. /// /// Input. /// Expected. [Theory] [InlineData("1.0.1", "1.0.1")] [InlineData("1.0.*", "1.0.999999999")] [InlineData("*.*.1", "999999999.999999999.1")] public void MaximumVersion_Test(string input, string expected) { var semanticVersion = new SemanticVersion(input); Assert.Equal(expected, semanticVersion.ToString()); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ShutdownTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using WinGetTestCommon; using Xunit; using Xunit.Abstractions; /// /// Unit tests for running test on the processor. /// [Collection("UnitTestCollection")] [OutOfProc] public class ShutdownTests : ConfigurationProcessorTestBase { /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ShutdownTests(UnitTestFixture fixture, ITestOutputHelper log) : base(fixture, log) { } /// /// Initiates a shutdown on the process when it is running a synchronous operation. /// [Fact] public void ShutdownSynchronization_SyncCall() { this.Fixture.RecreateStatics(); ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitWaits = this.ConfigurationUnit(); ConfigurationUnit configurationUnitWorks = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnitWaits, configurationUnitWorks }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnitWaits); ManualResetEvent isWaiting = new ManualResetEvent(false); ManualResetEvent waitingOn = new ManualResetEvent(false); unitProcessor.TestSettingsDelegate = () => { isWaiting.Set(); waitingOn.WaitOne(); return new TestSettingsResultInstance(configurationUnitWaits) { TestResult = ConfigurationTestResult.Positive }; }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); TestConfigurationSetResult? result = null; Exception? exception = null; ManualResetEvent syncCallDone = new ManualResetEvent(false); Thread thread = new Thread(() => { try { result = processor.TestSet(configurationSet); } catch (Exception ex) { exception = ex; } syncCallDone.Set(); }); thread.Start(); Assert.True(isWaiting.WaitOne(5000)); var servers = WinGetServerInstance.GetInstances(); Assert.Single(servers); var server = servers[0]; Assert.True(server.HasWindow); // This is the call pattern from Windows this.SendMessageAndLog(server, WindowMessage.QueryEndSession); Thread thread2 = new Thread(() => { // Release the wait after initiating the shutdown, but before waiting on it waitingOn.Set(); }); thread2.Start(); this.SendMessageAndLog(server, WindowMessage.EndSession); this.SendMessageAndLog(server, WindowMessage.Close); Assert.True(syncCallDone.WaitOne(5000)); Assert.NotNull(exception); Assert.True(server.Process.WaitForExit(5000)); } /// /// Initiates a shutdown on the process when it is running an asynchronous operation. /// [Fact] public void ShutdownSynchronization_AsyncCall() { this.Fixture.RecreateStatics(); ConfigurationSet configurationSet = this.ConfigurationSet(); ConfigurationUnit configurationUnitWaits = this.ConfigurationUnit(); ConfigurationUnit configurationUnitWorks = this.ConfigurationUnit(); configurationSet.Units = new ConfigurationUnit[] { configurationUnitWaits, configurationUnitWorks }; TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnitWaits); ManualResetEvent isWaiting = new ManualResetEvent(false); ManualResetEvent waitingOn = new ManualResetEvent(false); unitProcessor.TestSettingsDelegate = () => { isWaiting.Set(); waitingOn.WaitOne(); return new TestSettingsResultInstance(configurationUnitWaits) { TestResult = ConfigurationTestResult.Positive }; }; ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); var operation = processor.TestSetAsync(configurationSet); Assert.True(isWaiting.WaitOne(5000)); var servers = WinGetServerInstance.GetInstances(); Assert.Single(servers); var server = servers[0]; Assert.True(server.HasWindow); // This is the call pattern from Windows this.SendMessageAndLog(server, WindowMessage.QueryEndSession); Thread thread2 = new Thread(() => { // Release the wait after initiating the shutdown, but before waiting on it waitingOn.Set(); }); thread2.Start(); this.SendMessageAndLog(server, WindowMessage.EndSession); this.SendMessageAndLog(server, WindowMessage.Close); Assert.ThrowsAny(() => operation.GetAwaiter().GetResult()); Assert.True(server.Process.WaitForExit(5000)); } private void SendMessageAndLog(WinGetServerInstance server, WindowMessage message) { this.Log.WriteLine($"Sending message {message} to process {server.Process.Id}..."); try { if (server.SendMessage(message)) { this.Log.WriteLine("... succeeded."); } else { this.Log.WriteLine("... failed."); } } catch (Exception e) { this.Log.WriteLine($"... had exception: {e.Message}"); } } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/TypeHelpersTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.Collections; using System.Collections.Generic; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Windows.Foundation.Collections; using Xunit; using Xunit.Abstractions; /// /// TypeHelpers tests. /// [Collection("UnitTestCollection")] [InProc] public class TypeHelpersTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public TypeHelpersTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } private enum TestEnum { Value, } /// /// Tests PropertyExists. /// [Fact] public void PropertyExistsTests() { dynamic obj = new { Property1 = "value", }; Assert.True(TypeHelpers.PropertyExists(obj, "Property1")); Assert.False(TypeHelpers.PropertyExists(obj, "Property2")); } /// /// Tests PropertyWithTypeExists. /// [Fact] public void PropertyWithTypeExistsTests() { dynamic obj = new { Property1 = "value", Property2 = 42, }; Assert.True(TypeHelpers.PropertyWithTypeExists(obj, "Property1")); Assert.True(TypeHelpers.PropertyWithTypeExists(obj, "Property2")); Assert.False(TypeHelpers.PropertyWithTypeExists(obj, "Property1")); Assert.False(TypeHelpers.PropertyWithTypeExists(obj, "Property2")); } /// /// Test PropertyExistsAndIsEnum. /// [Fact] public void PropertyExistsAndIsEnumTests() { dynamic obj = new { Property1 = TestEnum.Value, Property2 = "string", }; Assert.True(TypeHelpers.PropertyExistsAndIsEnum(obj, "Property1")); Assert.False(TypeHelpers.PropertyExistsAndIsEnum(obj, "Property2")); } /// /// Tests PropertyExistsAndIsList. /// [Fact] public void PropertyExistsAndIsListTests() { dynamic obj = new { Property1 = new List() { "value" }, Property2 = "string", }; Assert.True(TypeHelpers.PropertyExistsAndIsList(obj, "Property1")); Assert.False(TypeHelpers.PropertyExistsAndIsList(obj, "Property2")); } /// /// Tests GetAllPropertiesValues. /// [Fact] public void GetAllPropertiesValuesTests() { string s = "value"; int i = 42; TestEnum e = TestEnum.Value; dynamic obj = new { Property1 = s, Property2 = i, Property3 = e, }; ValueSet set = TypeHelpers.GetAllPropertiesValues(obj); Assert.Equal(3, set.Count); Assert.True(set.ContainsKey("Property1")); Assert.True(set.TryGetValue("Property1", out object v1)); Assert.Equal(s, v1 as string); Assert.True(set.ContainsKey("Property2")); Assert.True(set.TryGetValue("Property2", out object v2)); Assert.Equal(i, (int)v2); Assert.True(set.ContainsKey("Property3")); Assert.True(set.TryGetValue("Property3", out object v3)); Assert.Equal(e.ToString(), v3); } /// /// Verifies when a property is a Hashtable. It must be converted to a ValueSet. /// [Fact] public void GetAllPropertiesValuesTest_Hashtable() { string k1 = "key1"; string k2 = "key2"; int v1 = 7; string v2 = "value2"; dynamic obj = new { Property1 = new Hashtable { { k1, v1 }, { k2, v2 }, }, }; ValueSet set = TypeHelpers.GetAllPropertiesValues(obj); Assert.Single(set); Assert.True(set.ContainsKey("Property1")); Assert.True(set.TryGetValue("Property1", out object valueSetResultObj)); ValueSet? valueSetResult = valueSetResultObj as ValueSet; Assert.NotNull(valueSetResult); Assert.Equal(2, valueSetResult.Count); Assert.True(valueSetResult.ContainsKey(k1)); Assert.Equal(v1, (int)valueSetResult[k1]); Assert.True(valueSetResult.ContainsKey(k2)); Assert.Equal(v2, (string)valueSetResult[k2]); } /// /// Verifies when a property is an array. It must generate a ValueSet /// where the keys are the index and a key treatAsArray means the value /// must be treated an array. /// [Fact] public void GetAllPropertiesValuesTest_Array() { dynamic obj = new { Property1 = new int[] { 1, 2, 3, 4, }, }; ValueSet set = TypeHelpers.GetAllPropertiesValues(obj); Assert.Single(set); Assert.True(set.ContainsKey("Property1")); Assert.True(set.TryGetValue("Property1", out object valueSetResultObj)); ValueSet? valueSetResult = valueSetResultObj as ValueSet; Assert.NotNull(valueSetResult); Assert.Equal(5, valueSetResult.Count); Assert.True(valueSetResult.ContainsKey("treatAsArray")); Assert.True(valueSetResult.ContainsKey("0")); Assert.True(valueSetResult.ContainsKey("1")); Assert.True(valueSetResult.ContainsKey("2")); Assert.True(valueSetResult.ContainsKey("3")); } } } ================================================ FILE: src/Microsoft.Management.Configuration.UnitTests/Tests/ValueSetExtensionsTests.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections; using System.Collections.Generic; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; using Windows.Foundation.Collections; using Xunit; using Xunit.Abstractions; /// /// ValueSet extension tests. /// [Collection("UnitTestCollection")] [InProc] public class ValueSetExtensionsTests { private readonly UnitTestFixture fixture; private readonly ITestOutputHelper log; /// /// Initializes a new instance of the class. /// /// Unit test fixture. /// Log helper. public ValueSetExtensionsTests(UnitTestFixture fixture, ITestOutputHelper log) { this.fixture = fixture; this.log = log; } /// /// Tests PropertyExists. /// [Fact] public void ValueSet_SimpleTypes() { string stringProperty = "stringProperty"; string stringPropertyValue = "string"; string intProperty = "intProperty"; long intPropertyValue = 64; string boolProperty = "boolProperty"; bool boolPropertyValue = true; var valueSet = new ValueSet { { stringProperty, stringPropertyValue }, { intProperty, intPropertyValue }, { boolProperty, boolPropertyValue }, }; var resultHashtable = valueSet.ToHashtable(); Assert.NotNull(resultHashtable); Assert.True(resultHashtable.ContainsKey(stringProperty)); Assert.Equal(stringPropertyValue, resultHashtable[stringProperty]); Assert.True(resultHashtable.ContainsKey(intProperty)); Assert.Equal(intPropertyValue, resultHashtable[intProperty]); Assert.True(resultHashtable.ContainsKey(boolProperty)); Assert.Equal(boolPropertyValue, resultHashtable[boolProperty]); } /// /// Tests when a ValueSet has inner value sets. /// [Fact] public void ValueSet_NestedValueSets() { string boolPropertyInner = "boolPropertyInner"; bool boolPropertyValueInner = true; var valueSetInner = new ValueSet() { { boolPropertyInner, boolPropertyValueInner }, }; string stringPropertyInnerInner = "stringPropertyInnerInner"; string stringPropertyValueInnerInner = "stringInnerInner"; var valueSetInnerInner = new ValueSet() { { stringPropertyInnerInner, stringPropertyValueInnerInner }, }; string inner2Key = "InnerKey2"; var valueSetInner2 = new ValueSet() { { inner2Key, valueSetInnerInner }, }; string key1 = "key1"; string key2 = "key2"; var valueSet = new ValueSet() { { key1, valueSetInner }, { key2, valueSetInner2 }, }; var hashtable = valueSet.ToHashtable(); Assert.NotNull(hashtable); Assert.Equal(2, hashtable.Count); Assert.True(hashtable.ContainsKey(key1)); Assert.True(hashtable.ContainsKey(key2)); var key1Result = hashtable[key1] as Hashtable; Assert.NotNull(key1Result); Assert.Single(key1Result); Assert.True(key1Result.ContainsKey(boolPropertyInner)); Assert.Equal(boolPropertyValueInner, key1Result[boolPropertyInner]); var key2Result = hashtable[key2] as Hashtable; Assert.NotNull(key2Result); Assert.Single(key2Result); Assert.True(key2Result.ContainsKey(inner2Key)); var key2ResultInner = key2Result[inner2Key] as Hashtable; Assert.NotNull(key2ResultInner); Assert.True(key2ResultInner.ContainsKey(stringPropertyInnerInner)); Assert.Equal(stringPropertyValueInnerInner, key2ResultInner[stringPropertyInnerInner]); } /// /// Test when ValueSet contains a ValueSet that is threated as an. /// [Fact] public void ValueSet_ArraySimpleTypes() { var valueSetArray = new ValueSet() { { "treatAsArray", true }, { "0", "value1" }, { "1", "value1" }, { "2", "value1" }, { "3", "value1" }, }; string arrayKey = "arrayKey"; var valueSet = new ValueSet() { { arrayKey, valueSetArray }, }; var hashtable = valueSet.ToHashtable(); Assert.NotNull(hashtable); Assert.True(hashtable.ContainsKey(arrayKey)); var expectedList = hashtable[arrayKey] as IList; Assert.NotNull(expectedList); Assert.Equal(4, expectedList.Count); foreach (var element in expectedList) { Assert.IsType(element); } } /// /// Test ValueSet with an array of value sets. /// [Fact] public void ValueSet_ArrayHashtable() { var arrayValue1 = new ValueSet() { { "key11", "value11" }, { "key12", "value12" }, }; var arrayValue2 = new ValueSet() { { "key21", "value21" }, }; var valueSetArray = new ValueSet() { { "treatAsArray", true }, { "0", arrayValue1 }, { "1", arrayValue2 }, }; string arrayKey = "arrayKey"; var valueSet = new ValueSet() { { arrayKey, valueSetArray }, }; var hashtable = valueSet.ToHashtable(); Assert.NotNull(hashtable); Assert.True(hashtable.ContainsKey(arrayKey)); var expectedList = hashtable[arrayKey] as IList; Assert.NotNull(expectedList); Assert.Equal(2, expectedList.Count); var resultValue1 = expectedList[0] as Hashtable; Assert.NotNull(resultValue1); Assert.Equal(2, resultValue1.Count); var resultValue2 = expectedList[1] as Hashtable; Assert.NotNull(resultValue2); Assert.Single(resultValue2); } /// /// Tests ConvertValueSetToArray. /// [Fact] public void ValueSet_ArrayOrder() { string arrayValue0 = "arrayValue0"; string arrayValue1 = "arrayValue1"; string arrayValue2 = "arrayValue2"; string arrayValue3 = "arrayValue3"; string arrayValue4 = "arrayValue4"; string arrayValue5 = "arrayValue5"; string arrayValue6 = "arrayValue6"; string arrayValue7 = "arrayValue7"; string arrayValue8 = "arrayValue8"; string arrayValue9 = "arrayValue9"; string arrayValue10 = "arrayValue10"; string arrayValue11 = "arrayValue11"; string arrayValue12 = "arrayValue12"; var valueSetArray = new ValueSet() { { "10", arrayValue10 }, { "7", arrayValue7 }, { "2", arrayValue2 }, { "12", arrayValue12 }, { "6", arrayValue6 }, { "treatAsArray", true }, { "3", arrayValue3 }, { "1", arrayValue1 }, { "9", arrayValue9 }, { "0", arrayValue0 }, { "4", arrayValue4 }, { "11", arrayValue11 }, { "8", arrayValue8 }, { "5", arrayValue5 }, }; var result = valueSetArray.ToArray(); Assert.NotNull(result); Assert.Equal(valueSetArray.Count - 1, result.Count); Assert.Equal(arrayValue0, result[0]); Assert.Equal(arrayValue1, result[1]); Assert.Equal(arrayValue2, result[2]); Assert.Equal(arrayValue3, result[3]); Assert.Equal(arrayValue4, result[4]); Assert.Equal(arrayValue5, result[5]); Assert.Equal(arrayValue6, result[6]); Assert.Equal(arrayValue7, result[7]); Assert.Equal(arrayValue8, result[8]); Assert.Equal(arrayValue9, result[9]); Assert.Equal(arrayValue10, result[10]); Assert.Equal(arrayValue11, result[11]); Assert.Equal(arrayValue12, result[12]); } /// /// Tests ConvertValueSetToArray. /// [Fact] public void ValueSet_InvalidArray() { string arrayValue0 = "arrayValue0"; string arrayValue1 = "arrayValue1"; string arrayValue2 = "arrayValue2"; string arrayValue3 = "arrayValue3"; string arrayValue4 = "arrayValue4"; string arrayValue5 = "arrayValue5"; string arrayValue6 = "arrayValue6"; string arrayValue7 = "arrayValue7"; string arrayValue8 = "arrayValue8"; string arrayValue9 = "arrayValue9"; string arrayValue10 = "arrayValue10"; string arrayValue11 = "arrayValue11"; string arrayValue12 = "arrayValue12"; var valueSetArray = new ValueSet() { { "10", arrayValue10 }, { "7", arrayValue7 }, { "2", arrayValue2 }, { "12", arrayValue12 }, { "6", arrayValue6 }, { "3", arrayValue3 }, { "1", arrayValue1 }, { "9", arrayValue9 }, { "0", arrayValue0 }, { "4", arrayValue4 }, { "11", arrayValue11 }, { "8", arrayValue8 }, { "5", arrayValue5 }, }; Assert.Throws(() => valueSetArray.ToArray()); } /// /// Tests ConvertValueSetToArray. /// [Fact] public void ValueSet_InvalidArrayKey() { string arrayValue0 = "arrayValue0"; string arrayValue1 = "arrayValue1"; string arrayValue2 = "arrayValue2"; string arrayValue3 = "arrayValue3"; string arrayValue4 = "arrayValue4"; string arrayValue5 = "arrayValue5"; string arrayValue6 = "arrayValue6"; string arrayValue7 = "arrayValue7"; string arrayValue8 = "arrayValue8"; string arrayValue9 = "arrayValue9"; string arrayValue10 = "arrayValue10"; string arrayValue11 = "arrayValue11"; string arrayValue12 = "arrayValue12"; var valueSetArray = new ValueSet() { { "10", arrayValue10 }, { "7", arrayValue7 }, { "2", arrayValue2 }, { "a", arrayValue12 }, { "6", arrayValue6 }, { "3", arrayValue3 }, { "1", arrayValue1 }, { "9", arrayValue9 }, { "0", arrayValue0 }, { "4", arrayValue4 }, { "11", arrayValue11 }, { "8", arrayValue8 }, { "5", arrayValue5 }, }; Assert.Throws(() => valueSetArray.ToArray()); } /// /// Tests ValueSet simple types content equals. /// [Fact] public void ValueSet_SimpleTypes_ContentEquals() { string stringProperty = "stringProperty"; string stringPropertyValue = "string"; string intProperty = "intProperty"; long intPropertyValue = 64; string boolProperty = "boolProperty"; bool boolPropertyValue = true; var valueSet = new ValueSet { { stringProperty, stringPropertyValue }, { intProperty, intPropertyValue }, { boolProperty, boolPropertyValue }, }; // Same content different order var valueSetDifferentOrder = new ValueSet { { boolProperty, boolPropertyValue }, { stringProperty, stringPropertyValue }, { intProperty, intPropertyValue }, }; Assert.True(valueSet.ContentEquals(valueSetDifferentOrder)); // Entry missing var valueSetEntryMissing = new ValueSet { { stringProperty, stringPropertyValue }, { intProperty, intPropertyValue }, }; Assert.False(valueSet.ContentEquals(valueSetEntryMissing)); // Different entry var valueSetEntryDifferent = new ValueSet { { stringProperty, stringPropertyValue }, { intProperty, intPropertyValue }, { "Another", "AnotherValue" }, }; Assert.False(valueSet.ContentEquals(valueSetEntryDifferent)); // Different value var valueSetDifferentValue = new ValueSet { { boolProperty, boolPropertyValue }, { stringProperty, stringPropertyValue }, { intProperty, 0 }, }; Assert.False(valueSet.ContentEquals(valueSetDifferentValue)); } /// /// Tests when a ValueSet has inner value sets. /// [Fact] public void ValueSet_NestedValueSets_ContentEquals() { string boolPropertyInner = "boolPropertyInner"; bool boolPropertyValueInner = true; var valueSetInner = new ValueSet() { { boolPropertyInner, boolPropertyValueInner }, }; string stringPropertyInnerInner = "stringPropertyInnerInner"; string stringPropertyValueInnerInner = "stringInnerInner"; var valueSetInnerInner = new ValueSet() { { stringPropertyInnerInner, stringPropertyValueInnerInner }, }; string inner2Key = "InnerKey2"; var valueSetInner2 = new ValueSet() { { inner2Key, valueSetInnerInner }, }; string key1 = "key1"; string key2 = "key2"; var valueSet = new ValueSet() { { key1, valueSetInner }, { key2, valueSetInner2 }, }; // Same content different order var valueSetDifferentOrder = new ValueSet() { { key2, valueSetInner2 }, { key1, valueSetInner }, }; Assert.True(valueSet.ContentEquals(valueSetDifferentOrder)); // Different nested content var valueSetDifferentContent = new ValueSet() { { key2, valueSetInner }, { key1, valueSetInner2 }, }; Assert.False(valueSet.ContentEquals(valueSetDifferentContent)); } } } ================================================ FILE: src/Microsoft.Management.Deployment/AddPackageCatalogOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "AddPackageCatalogOptions.h" #pragma warning( pop ) #include "AddPackageCatalogOptions.g.cpp" #include "Converters.h" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { hstring AddPackageCatalogOptions::Name() { return hstring(m_name); } void AddPackageCatalogOptions::Name(hstring const& value) { m_name = value; } hstring AddPackageCatalogOptions::SourceUri() { return hstring(m_sourceUri); } void AddPackageCatalogOptions::SourceUri(hstring const& value) { m_sourceUri = value; } hstring AddPackageCatalogOptions::Type() { return hstring(m_type); } void AddPackageCatalogOptions::Type(hstring const& value) { m_type = value; } winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel AddPackageCatalogOptions::TrustLevel() { return m_trustLevel; } void AddPackageCatalogOptions::TrustLevel(winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel const& value) { m_trustLevel = value; } hstring AddPackageCatalogOptions::CustomHeader() { return hstring(m_customHeader); } void AddPackageCatalogOptions::CustomHeader(hstring const& value) { m_customHeader = value; } bool AddPackageCatalogOptions::Explicit() { return m_explicit; } void AddPackageCatalogOptions::Explicit(bool value) { m_explicit = value; } int32_t AddPackageCatalogOptions::Priority() { return m_priority; } void AddPackageCatalogOptions::Priority(int32_t value) { m_priority = value; } CoCreatableMicrosoftManagementDeploymentClass(AddPackageCatalogOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/AddPackageCatalogOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AddPackageCatalogOptions.g.h" #include "public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_AddPackageCatalogOptions)] struct AddPackageCatalogOptions : AddPackageCatalogOptionsT { AddPackageCatalogOptions() = default; hstring Name(); void Name(hstring const& value); hstring SourceUri(); void SourceUri(hstring const& value); hstring Type(); void Type(hstring const& value); winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel TrustLevel(); void TrustLevel(winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel const& value); hstring CustomHeader(); void CustomHeader(hstring const& value); bool Explicit(); void Explicit(bool value); int32_t Priority(); void Priority(int32_t value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: hstring m_name = L""; hstring m_sourceUri = L""; hstring m_type = L""; winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel m_trustLevel = winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel::None; hstring m_customHeader = L""; bool m_explicit = false; int32_t m_priority = 0; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct AddPackageCatalogOptions : AddPackageCatalogOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/AddPackageCatalogResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AddPackageCatalogResult.h" #include "AddPackageCatalogResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void AddPackageCatalogResult::Initialize( winrt::Microsoft::Management::Deployment::AddPackageCatalogStatus status, winrt::hresult extendedErrorCode) { m_status = status; m_extendedErrorCode = extendedErrorCode; } winrt::Microsoft::Management::Deployment::AddPackageCatalogStatus AddPackageCatalogResult::Status() { return m_status; } winrt::hresult AddPackageCatalogResult::ExtendedErrorCode() { return m_extendedErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/AddPackageCatalogResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AddPackageCatalogResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct AddPackageCatalogResult : AddPackageCatalogResultT { AddPackageCatalogResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::AddPackageCatalogStatus status, winrt::hresult extendedErrorCode); #endif winrt::Microsoft::Management::Deployment::AddPackageCatalogStatus Status(); winrt::hresult ExtendedErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::AddPackageCatalogStatus m_status = winrt::Microsoft::Management::Deployment::AddPackageCatalogStatus::Ok; winrt::hresult m_extendedErrorCode = S_OK; #endif }; } #pragma once ================================================ FILE: src/Microsoft.Management.Deployment/AuthenticationArguments.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "AuthenticationArguments.h" #pragma warning( pop ) #include "AuthenticationArguments.g.cpp" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { winrt::Microsoft::Management::Deployment::AuthenticationMode AuthenticationArguments::AuthenticationMode() { return m_authenticationMode; } void AuthenticationArguments::AuthenticationMode(winrt::Microsoft::Management::Deployment::AuthenticationMode const& value) { m_authenticationMode = value; } hstring AuthenticationArguments::AuthenticationAccount() { return winrt::hstring(m_authenticationAccount); } void AuthenticationArguments::AuthenticationAccount(hstring const& value) { m_authenticationAccount = value; } CoCreatableMicrosoftManagementDeploymentClass(AuthenticationArguments); } ================================================ FILE: src/Microsoft.Management.Deployment/AuthenticationArguments.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AuthenticationArguments.g.h" #include "Public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_AuthenticationArguments)] struct AuthenticationArguments : AuthenticationArgumentsT { AuthenticationArguments() = default; winrt::Microsoft::Management::Deployment::AuthenticationMode AuthenticationMode(); void AuthenticationMode(winrt::Microsoft::Management::Deployment::AuthenticationMode const& value); hstring AuthenticationAccount(); void AuthenticationAccount(hstring const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::AuthenticationMode m_authenticationMode = winrt::Microsoft::Management::Deployment::AuthenticationMode::Silent; std::wstring m_authenticationAccount = L""; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct AuthenticationArguments : AuthenticationArgumentsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/AuthenticationInfo.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "AuthenticationInfo.h" #include "AuthenticationInfo.g.cpp" #include "MicrosoftEntraIdAuthenticationInfo.h" #include "Converters.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { void AuthenticationInfo::Initialize(::AppInstaller::Authentication::AuthenticationInfo authenticationInfo) { m_authenticationType = GetDeploymentAuthenticationType(authenticationInfo.Type); if (authenticationInfo.MicrosoftEntraIdInfo.has_value()) { auto microsoftEntraIdAuthenticationInfo = winrt::make_self>(); microsoftEntraIdAuthenticationInfo->Initialize(authenticationInfo.MicrosoftEntraIdInfo.value()); m_microsoftEntraIdAuthenticationInfo = *microsoftEntraIdAuthenticationInfo; } } winrt::Microsoft::Management::Deployment::AuthenticationType AuthenticationInfo::AuthenticationType() { return m_authenticationType; } winrt::Microsoft::Management::Deployment::MicrosoftEntraIdAuthenticationInfo AuthenticationInfo::MicrosoftEntraIdAuthenticationInfo() { return m_microsoftEntraIdAuthenticationInfo; } } ================================================ FILE: src/Microsoft.Management.Deployment/AuthenticationInfo.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AuthenticationInfo.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct AuthenticationInfo : AuthenticationInfoT { AuthenticationInfo() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Authentication::AuthenticationInfo authenticationInfo); #endif winrt::Microsoft::Management::Deployment::AuthenticationType AuthenticationType(); winrt::Microsoft::Management::Deployment::MicrosoftEntraIdAuthenticationInfo MicrosoftEntraIdAuthenticationInfo(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::AuthenticationType m_authenticationType = winrt::Microsoft::Management::Deployment::AuthenticationType::None; winrt::Microsoft::Management::Deployment::MicrosoftEntraIdAuthenticationInfo m_microsoftEntraIdAuthenticationInfo{ nullptr }; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/CanUnload.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" namespace winrt::Microsoft::Management::Deployment::implementation { static std::atomic_bool s_canUnload = true; void SetCanUnload(bool value) { s_canUnload = value; } bool GetCanUnload() { return s_canUnload; } } ================================================ FILE: src/Microsoft.Management.Deployment/CatalogPackage.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include "CatalogPackage.h" #include "CatalogPackage.g.cpp" #include "PackageCatalog.h" #include "PackageVersionInfo.h" #include "PackageVersionId.h" #include "PackageInstallerInstalledStatus.h" #include "CheckInstalledStatusResult.h" #include #include #include #include namespace winrt::Microsoft::Management::Deployment::implementation { void CatalogPackage::Initialize( ::AppInstaller::Repository::Source source, std::shared_ptr<::AppInstaller::Repository::ICompositePackage> package) { m_source = std::move(source); m_package = std::move(package); } hstring CatalogPackage::Id() { return winrt::to_hstring(m_package->GetProperty(::AppInstaller::Repository::PackageProperty::Id).get()); } hstring CatalogPackage::Name() { return winrt::to_hstring(m_package->GetProperty(::AppInstaller::Repository::PackageProperty::Name)); } Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::InstalledVersion() { std::call_once(m_installedVersionOnceFlag, [&]() { std::shared_ptr<::AppInstaller::Repository::IPackageVersion> installedVersion = GetInstalledVersion(m_package); if (installedVersion) { auto installedVersionImpl = winrt::make_self>(); installedVersionImpl->Initialize(std::move(installedVersion)); m_installedVersion = *installedVersionImpl; } }); return m_installedVersion; } Windows::Foundation::Collections::IVectorView CatalogPackage::AvailableVersions() { std::call_once(m_availableVersionsOnceFlag, [&]() { // Vector hasn't been populated yet. for (auto const& versionKey : ::AppInstaller::Repository::GetAllAvailableVersions(m_package)->GetVersionKeys()) { auto packageVersionId = winrt::make_self>(); packageVersionId->Initialize(versionKey); m_availableVersions.Append(*packageVersionId); } }); return m_availableVersions.GetView(); } void CatalogPackage::InitializeLatestApplicableVersion() { std::call_once(m_latestApplicableVersionOnceFlag, [&]() { auto data = AppInstaller::Repository::GetLatestApplicableVersion(m_package); m_updateAvailable = data.UpdateAvailable; if (data.LatestApplicableVersion) { // DefaultInstallVersion hasn't been created yet, create and populate it. // DefaultInstallVersion is the latest applicable version of the internal package object. auto latestVersionImpl = winrt::make_self>(); latestVersionImpl->Initialize(std::move(data.LatestApplicableVersion)); m_latestApplicableVersion = *latestVersionImpl; } }); } Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::DefaultInstallVersion() { InitializeLatestApplicableVersion(); if (m_latestApplicableVersion) { return m_latestApplicableVersion; } else if (AvailableVersions().Size() > 0) { return GetPackageVersionInfo(m_availableVersions.GetAt(0)); } else { return nullptr; } } Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::GetPackageVersionInfo(Microsoft::Management::Deployment::PackageVersionId const& versionKey) { winrt::Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo{ nullptr }; ::AppInstaller::Repository::PackageVersionKey internalVersionKey(winrt::to_string(versionKey.PackageCatalogId()), winrt::to_string(versionKey.Version()), winrt::to_string(versionKey.Channel())); std::shared_ptr<::AppInstaller::Repository::IPackageVersion> availableVersion = ::AppInstaller::Repository::GetAllAvailableVersions(m_package)->GetVersion(internalVersionKey); if (availableVersion) { auto packageVersionInfoImpl = winrt::make_self>(); packageVersionInfoImpl->Initialize(std::move(availableVersion)); packageVersionInfo =*packageVersionInfoImpl; } return packageVersionInfo; } bool CatalogPackage::IsUpdateAvailable() { InitializeLatestApplicableVersion(); return m_updateAvailable; } Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync( Microsoft::Management::Deployment::InstalledStatusType checkTypes) { co_return CheckInstalledStatus(checkTypes); } Microsoft::Management::Deployment::CheckInstalledStatusResult CatalogPackage::CheckInstalledStatus( Microsoft::Management::Deployment::InstalledStatusType checkTypes) { Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::Ok; Windows::Foundation::Collections::IVector installedStatus{ winrt::single_threaded_vector() }; try { auto checkResult = ::AppInstaller::Repository::CheckPackageInstalledStatus(m_package, static_cast<::AppInstaller::Repository::InstalledStatusType>(checkTypes)); // Build the result object from the checkResult for (auto const& entry : checkResult) { auto checkInstallerResult = winrt::make_self>(); checkInstallerResult->Initialize(entry); installedStatus.Append(*checkInstallerResult); } } catch (...) { status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::InternalError; } auto checkInstalledStatusResult = winrt::make_self>(); checkInstalledStatusResult->Initialize(status, installedStatus); return *checkInstalledStatusResult; } winrt::Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync() { co_return CheckInstalledStatus(InstalledStatusType::AllChecks); } winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CatalogPackage::CheckInstalledStatus() { return CheckInstalledStatus(InstalledStatusType::AllChecks); } std::shared_ptr<::AppInstaller::Repository::ICompositePackage> CatalogPackage::GetRepositoryPackage() { return m_package; } Windows::Foundation::IReference CatalogPackage::CatalogPriority() { return AppInstaller::Repository::GetSourcePriority(m_package); } } ================================================ FILE: src/Microsoft.Management.Deployment/CatalogPackage.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "CatalogPackage.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct CatalogPackage : CatalogPackageT { CatalogPackage() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( ::AppInstaller::Repository::Source source, std::shared_ptr<::AppInstaller::Repository::ICompositePackage> package); std::shared_ptr<::AppInstaller::Repository::ICompositePackage> GetRepositoryPackage(); #endif hstring Id(); hstring Name(); winrt::Microsoft::Management::Deployment::PackageVersionInfo InstalledVersion(); winrt::Windows::Foundation::Collections::IVectorView AvailableVersions(); winrt::Microsoft::Management::Deployment::PackageVersionInfo DefaultInstallVersion(); winrt::Microsoft::Management::Deployment::PackageVersionInfo GetPackageVersionInfo(winrt::Microsoft::Management::Deployment::PackageVersionId const& versionKey); bool IsUpdateAvailable(); // Contract 5.0 winrt::Windows::Foundation::IAsyncOperation CheckInstalledStatusAsync( winrt::Microsoft::Management::Deployment::InstalledStatusType checkTypes); winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CheckInstalledStatus( winrt::Microsoft::Management::Deployment::InstalledStatusType checkTypes); winrt::Windows::Foundation::IAsyncOperation CheckInstalledStatusAsync(); winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CheckInstalledStatus(); Windows::Foundation::IReference CatalogPriority(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Repository::Source m_source; std::shared_ptr<::AppInstaller::Repository::ICompositePackage> m_package; bool m_updateAvailable = false; Windows::Foundation::Collections::IVector m_availableVersions{ winrt::single_threaded_vector() }; winrt::Microsoft::Management::Deployment::PackageVersionInfo m_installedVersion{ nullptr }; winrt::Microsoft::Management::Deployment::PackageVersionInfo m_latestApplicableVersion{ nullptr }; std::once_flag m_installedVersionOnceFlag; std::once_flag m_availableVersionsOnceFlag; std::once_flag m_latestApplicableVersionOnceFlag; void InitializeLatestApplicableVersion(); #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/CatalogPackageMetadata.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "CatalogPackageMetadata.h" #include "CatalogPackageMetadata.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { using Localization = ::AppInstaller::Manifest::Localization; void CatalogPackageMetadata::Initialize(::AppInstaller::Manifest::ManifestLocalization manifestLocalization) { m_manifestLocalization = std::move(manifestLocalization); } hstring CatalogPackageMetadata::Locale() { return winrt::to_hstring(m_manifestLocalization.Locale); } hstring CatalogPackageMetadata::Publisher() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::PublisherUrl() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::PublisherSupportUrl() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::PrivacyUrl() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::Author() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::PackageName() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::PackageUrl() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::License() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::LicenseUrl() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::Copyright() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::CopyrightUrl() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::ShortDescription() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::Description() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::ReleaseNotes() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::ReleaseNotesUrl() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::PurchaseUrl() { return winrt::to_hstring(m_manifestLocalization.Get()); } hstring CatalogPackageMetadata::InstallationNotes() { return winrt::to_hstring(m_manifestLocalization.Get()); } winrt::Windows::Foundation::Collections::IVectorView CatalogPackageMetadata::Agreements() { if (!m_packageAgreements) { auto agreements = winrt::single_threaded_vector(); for (auto const& agreement : m_manifestLocalization.Get()) { auto packageAgreement = winrt::make_self>(); packageAgreement->Initialize(agreement); agreements.Append(*packageAgreement); } m_packageAgreements = agreements; } return m_packageAgreements.GetView(); } winrt::Windows::Foundation::Collections::IVectorView CatalogPackageMetadata::Tags() { if (!m_tags) { // Vector hasn't been created yet, create and populate it. auto tags = winrt::single_threaded_vector(); for (auto&& tag : m_manifestLocalization.Get()) { tags.Append(winrt::to_hstring(tag)); } m_tags = tags; } return m_tags.GetView(); } winrt::Windows::Foundation::Collections::IVectorView CatalogPackageMetadata::Documentations() { if (!m_documentations) { auto documentations = winrt::single_threaded_vector(); for (auto const& documentation : m_manifestLocalization.Get()) { auto documentationImpl = winrt::make_self>(); documentationImpl->Initialize(documentation); documentations.Append(*documentationImpl); } m_documentations = documentations; } return m_documentations.GetView(); } winrt::Windows::Foundation::Collections::IVectorView CatalogPackageMetadata::Icons() { if (!m_icons) { auto icons = winrt::single_threaded_vector(); for (auto const& icon : m_manifestLocalization.Get()) { auto iconImpl = winrt::make_self>(); iconImpl->Initialize(icon); icons.Append(*iconImpl); } m_icons = icons; } return m_icons.GetView(); } } ================================================ FILE: src/Microsoft.Management.Deployment/CatalogPackageMetadata.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "CatalogPackageMetadata.g.h" #include "PackageAgreement.h" #include "Documentation.h" #include "Icon.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct CatalogPackageMetadata : CatalogPackageMetadataT { CatalogPackageMetadata() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Manifest::ManifestLocalization manifestLocalization); #endif hstring Locale(); hstring Publisher(); hstring PublisherUrl(); hstring PublisherSupportUrl(); hstring PrivacyUrl(); hstring Author(); hstring PackageName(); hstring PackageUrl(); hstring License(); hstring LicenseUrl(); hstring Copyright(); hstring CopyrightUrl(); hstring ShortDescription(); hstring Description(); winrt::Windows::Foundation::Collections::IVectorView Tags(); winrt::Windows::Foundation::Collections::IVectorView Agreements(); winrt::Windows::Foundation::Collections::IVectorView Documentations(); winrt::Windows::Foundation::Collections::IVectorView Icons(); hstring ReleaseNotes(); hstring ReleaseNotesUrl(); hstring PurchaseUrl(); hstring InstallationNotes(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Manifest::ManifestLocalization m_manifestLocalization; Windows::Foundation::Collections::IVector m_packageAgreements{ nullptr }; Windows::Foundation::Collections::IVector m_documentations{ nullptr }; Windows::Foundation::Collections::IVector m_icons{ nullptr }; Windows::Foundation::Collections::IVector m_tags{ nullptr }; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "CheckInstalledStatusResult.h" #include "CheckInstalledStatusResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void CheckInstalledStatusResult::Initialize( winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status, Windows::Foundation::Collections::IVector installedStatus) { m_status = status; m_installedStatus = installedStatus; } winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus CheckInstalledStatusResult::Status() { return m_status; } winrt::Windows::Foundation::Collections::IVectorView CheckInstalledStatusResult::PackageInstalledStatus() { return m_installedStatus.GetView(); } } ================================================ FILE: src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "CheckInstalledStatusResult.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct CheckInstalledStatusResult : CheckInstalledStatusResultT { CheckInstalledStatusResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status, Windows::Foundation::Collections::IVector installedStatus); #endif winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus Status(); winrt::Windows::Foundation::Collections::IVectorView PackageInstalledStatus(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus m_status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::Ok; Windows::Foundation::Collections::IVector m_installedStatus{ winrt::single_threaded_vector() }; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/ComClsids.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/ComClsids.h" #pragma warning( push ) #pragma warning ( disable : 4467 ) // 4467 Allow use of uuid attribute for com object creation. #include "PackageManager.h" #include "FindPackagesOptions.h" #include "CreateCompositePackageCatalogOptions.h" #include "InstallOptions.h" #include "UninstallOptions.h" #include "PackageMatchFilter.h" #include "PackageManagerSettings.h" #include "DownloadOptions.h" #include "AuthenticationArguments.h" #include "RepairOptions.h" #include "AddPackageCatalogOptions.h" #include "RemovePackageCatalogOptions.h" #include "EditPackageCatalogOptions.h" #pragma warning( pop ) namespace winrt::Microsoft::Management::Deployment { CLSID GetRedirectedClsidFromInProcClsid(REFCLSID clsid) { if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_PackageManager)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::PackageManager); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_FindPackagesOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::FindPackagesOptions); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_CreateCompositePackageCatalogOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::CreateCompositePackageCatalogOptions); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_InstallOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::InstallOptions); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_UninstallOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::UninstallOptions); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_DownloadOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::DownloadOptions); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_PackageMatchFilter)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::PackageMatchFilter); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_AuthenticationArguments)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::AuthenticationArguments); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_PackageManagerSettings)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::PackageManagerSettings); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_RepairOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::RepairOptions); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_AddPackageCatalogOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::AddPackageCatalogOptions); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_RemovePackageCatalogOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::RemovePackageCatalogOptions); } else if (IsEqualCLSID(clsid, WINGET_INPROC_COM_CLSID_EditPackageCatalogOptions)) { return __uuidof(winrt::Microsoft::Management::Deployment::implementation::EditPackageCatalogOptions); } else { return CLSID_NULL; } } } ================================================ FILE: src/Microsoft.Management.Deployment/ConnectResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "ConnectResult.h" #include "ConnectResult.g.cpp" namespace winrt::Microsoft::Management::Deployment::implementation { void ConnectResult::Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus status, winrt::Microsoft::Management::Deployment::PackageCatalog packageCatalog, winrt::hresult extendedErrorCode) { m_status = status; m_packageCatalog = packageCatalog; m_extendedErrorCode = extendedErrorCode; } winrt::Microsoft::Management::Deployment::ConnectResultStatus ConnectResult::Status() { return m_status; } winrt::Microsoft::Management::Deployment::PackageCatalog ConnectResult::PackageCatalog() { return m_packageCatalog; } winrt::hresult ConnectResult::ExtendedErrorCode() { return m_extendedErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/ConnectResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "ConnectResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct ConnectResult : ConnectResultT { ConnectResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus status, winrt::Microsoft::Management::Deployment::PackageCatalog packageCatalog, winrt::hresult extendedErrorCode); #endif winrt::Microsoft::Management::Deployment::ConnectResultStatus Status(); winrt::Microsoft::Management::Deployment::PackageCatalog PackageCatalog(); winrt::hresult ExtendedErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::ConnectResultStatus m_status = winrt::Microsoft::Management::Deployment::ConnectResultStatus::Ok; winrt::Microsoft::Management::Deployment::PackageCatalog m_packageCatalog{ nullptr }; winrt::hresult m_extendedErrorCode = S_OK; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/Converters.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include "Microsoft/PredefinedInstalledSourceFactory.h" #include "Workflows/WorkflowBase.h" #include "Converters.h" namespace winrt::Microsoft::Management::Deployment::implementation { Microsoft::Management::Deployment::PackageMatchField GetDeploymentMatchField(::AppInstaller::Repository::PackageMatchField field) { Microsoft::Management::Deployment::PackageMatchField matchField = Microsoft::Management::Deployment::PackageMatchField::Id; switch (field) { case ::AppInstaller::Repository::PackageMatchField::Command: matchField = Microsoft::Management::Deployment::PackageMatchField::Command; break; case ::AppInstaller::Repository::PackageMatchField::Id: matchField = Microsoft::Management::Deployment::PackageMatchField::Id; break; case ::AppInstaller::Repository::PackageMatchField::Moniker: matchField = Microsoft::Management::Deployment::PackageMatchField::Moniker; break; case ::AppInstaller::Repository::PackageMatchField::Name: matchField = Microsoft::Management::Deployment::PackageMatchField::Name; break; case ::AppInstaller::Repository::PackageMatchField::Tag: matchField = Microsoft::Management::Deployment::PackageMatchField::Tag; break; case ::AppInstaller::Repository::PackageMatchField::ProductCode: matchField = Microsoft::Management::Deployment::PackageMatchField::ProductCode; break; case ::AppInstaller::Repository::PackageMatchField::PackageFamilyName: matchField = Microsoft::Management::Deployment::PackageMatchField::PackageFamilyName; break; default: matchField = Microsoft::Management::Deployment::PackageMatchField::Id; break; } return matchField; } ::AppInstaller::Repository::PackageMatchField GetRepositoryMatchField(Microsoft::Management::Deployment::PackageMatchField field) { ::AppInstaller::Repository::PackageMatchField matchField = ::AppInstaller::Repository::PackageMatchField::Id; switch (field) { case Microsoft::Management::Deployment::PackageMatchField::Command: matchField = ::AppInstaller::Repository::PackageMatchField::Command; break; case Microsoft::Management::Deployment::PackageMatchField::Id: matchField = ::AppInstaller::Repository::PackageMatchField::Id; break; case Microsoft::Management::Deployment::PackageMatchField::Moniker: matchField = ::AppInstaller::Repository::PackageMatchField::Moniker; break; case Microsoft::Management::Deployment::PackageMatchField::Name: matchField = ::AppInstaller::Repository::PackageMatchField::Name; break; case Microsoft::Management::Deployment::PackageMatchField::Tag: matchField = ::AppInstaller::Repository::PackageMatchField::Tag; break; case Microsoft::Management::Deployment::PackageMatchField::ProductCode: matchField = ::AppInstaller::Repository::PackageMatchField::ProductCode; break; case Microsoft::Management::Deployment::PackageMatchField::PackageFamilyName: matchField = ::AppInstaller::Repository::PackageMatchField::PackageFamilyName; break; default: matchField = ::AppInstaller::Repository::PackageMatchField::Id; break; } return matchField; } Microsoft::Management::Deployment::PackageFieldMatchOption GetDeploymentMatchOption(::AppInstaller::Repository::MatchType type) { Microsoft::Management::Deployment::PackageFieldMatchOption matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::Equals; switch (type) { case ::AppInstaller::Repository::MatchType::CaseInsensitive: matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::EqualsCaseInsensitive; break; case ::AppInstaller::Repository::MatchType::Exact: matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::Equals; break; case ::AppInstaller::Repository::MatchType::StartsWith: matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::StartsWithCaseInsensitive; break; case ::AppInstaller::Repository::MatchType::Substring: matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::ContainsCaseInsensitive; break; default: matchOption = Microsoft::Management::Deployment::PackageFieldMatchOption::Equals; break; } return matchOption; } ::AppInstaller::Repository::MatchType GetRepositoryMatchType(Microsoft::Management::Deployment::PackageFieldMatchOption option) { ::AppInstaller::Repository::MatchType packageFieldMatchOption = ::AppInstaller::Repository::MatchType::Exact; switch (option) { case Microsoft::Management::Deployment::PackageFieldMatchOption::EqualsCaseInsensitive: packageFieldMatchOption = ::AppInstaller::Repository::MatchType::CaseInsensitive; break; case Microsoft::Management::Deployment::PackageFieldMatchOption::Equals: packageFieldMatchOption = ::AppInstaller::Repository::MatchType::Exact; break; case Microsoft::Management::Deployment::PackageFieldMatchOption::StartsWithCaseInsensitive: packageFieldMatchOption = ::AppInstaller::Repository::MatchType::StartsWith; break; case Microsoft::Management::Deployment::PackageFieldMatchOption::ContainsCaseInsensitive: packageFieldMatchOption = ::AppInstaller::Repository::MatchType::Substring; break; default: packageFieldMatchOption = ::AppInstaller::Repository::MatchType::Exact; break; } return packageFieldMatchOption; } ::AppInstaller::Repository::CompositeSearchBehavior GetRepositoryCompositeSearchBehavior(Microsoft::Management::Deployment::CompositeSearchBehavior searchBehavior) { ::AppInstaller::Repository::CompositeSearchBehavior repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::AllPackages; switch (searchBehavior) { case Microsoft::Management::Deployment::CompositeSearchBehavior::LocalCatalogs: repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::Installed; break; case Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs: repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::AvailablePackages; break; case Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromAllCatalogs: repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::AvailablePackages; break; case Microsoft::Management::Deployment::CompositeSearchBehavior::AllCatalogs: default: repositorySearchBehavior = ::AppInstaller::Repository::CompositeSearchBehavior::AllPackages; break; } return repositorySearchBehavior; } ::AppInstaller::Repository::PackageVersionMetadata GetRepositoryPackageVersionMetadata(Microsoft::Management::Deployment::PackageVersionMetadataField packageVersionMetadataField) { ::AppInstaller::Repository::PackageVersionMetadata metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::InstalledLocation; switch (packageVersionMetadataField) { case Microsoft::Management::Deployment::PackageVersionMetadataField::InstalledLocation: metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::InstalledLocation; break; case Microsoft::Management::Deployment::PackageVersionMetadataField::InstalledScope: metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::InstalledScope; break; case Microsoft::Management::Deployment::PackageVersionMetadataField::InstallerType: metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::InstalledType; break; case Microsoft::Management::Deployment::PackageVersionMetadataField::PublisherDisplayName: metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::Publisher; break; case Microsoft::Management::Deployment::PackageVersionMetadataField::SilentUninstallCommand: metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::SilentUninstallCommand; break; case Microsoft::Management::Deployment::PackageVersionMetadataField::StandardUninstallCommand: metadataKey = ::AppInstaller::Repository::PackageVersionMetadata::StandardUninstallCommand; break; } return metadataKey; } winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResultStatus(winrt::hresult hresult) { winrt::Microsoft::Management::Deployment::FindPackagesResultStatus resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; switch (hresult) { case(S_OK): resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; break; case APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY: resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::BlockedByPolicy; break; case APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE: case APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA: case APPINSTALLER_CLI_ERROR_RESTAPI_ENDPOINT_NOT_FOUND: case APPINSTALLER_CLI_ERROR_RESTAPI_INTERNAL_ERROR: case APPINSTALLER_CLI_ERROR_RESTAPI_UNSUPPORTED_MIME_TYPE: case APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION: case APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE: resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::CatalogError; break; case E_INVALIDARG: case APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS: resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::InvalidOptions; break; case APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO: case APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED: case APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED: case APPINSTALLER_CLI_ERROR_AUTHENTICATION_INTERACTIVE_REQUIRED: case APPINSTALLER_CLI_ERROR_AUTHENTICATION_CANCELLED_BY_USER: case APPINSTALLER_CLI_ERROR_AUTHENTICATION_INCORRECT_ACCOUNT: resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::AuthenticationError; break; case HTTP_E_STATUS_DENIED: case HTTP_E_STATUS_FORBIDDEN: resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::AccessDenied; break; case APPINSTALLER_CLI_ERROR_COMMAND_FAILED: case APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX: case APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED: default: resultStatus = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::InternalError; break; } return resultStatus; } std::optional<::AppInstaller::Utility::Architecture> GetUtilityArchitecture(winrt::Windows::System::ProcessorArchitecture architecture) { return ::AppInstaller::Utility::ConvertToArchitectureEnum(architecture); } std::optional GetWindowsSystemProcessorArchitecture(::AppInstaller::Utility::Architecture architecture) { switch (architecture) { case ::AppInstaller::Utility::Architecture::X86: return winrt::Windows::System::ProcessorArchitecture::X86; case ::AppInstaller::Utility::Architecture::Arm: return winrt::Windows::System::ProcessorArchitecture::Arm; case ::AppInstaller::Utility::Architecture::X64: return winrt::Windows::System::ProcessorArchitecture::X64; case ::AppInstaller::Utility::Architecture::Neutral: return winrt::Windows::System::ProcessorArchitecture::Neutral; case ::AppInstaller::Utility::Architecture::Arm64: return winrt::Windows::System::ProcessorArchitecture::Arm64; } return {}; } std::pair<::AppInstaller::Manifest::ScopeEnum, bool> GetManifestScope(winrt::Microsoft::Management::Deployment::PackageInstallScope scope) { switch (scope) { case winrt::Microsoft::Management::Deployment::PackageInstallScope::Any: return std::make_pair(::AppInstaller::Manifest::ScopeEnum::Unknown, false); case winrt::Microsoft::Management::Deployment::PackageInstallScope::User: return std::make_pair(::AppInstaller::Manifest::ScopeEnum::User, false); case winrt::Microsoft::Management::Deployment::PackageInstallScope::System: return std::make_pair(::AppInstaller::Manifest::ScopeEnum::Machine, false); case winrt::Microsoft::Management::Deployment::PackageInstallScope::UserOrUnknown: return std::make_pair(::AppInstaller::Manifest::ScopeEnum::User, true); case winrt::Microsoft::Management::Deployment::PackageInstallScope::SystemOrUnknown: return std::make_pair(::AppInstaller::Manifest::ScopeEnum::Machine, true); } return std::make_pair(::AppInstaller::Manifest::ScopeEnum::Unknown, false); } winrt::Microsoft::Management::Deployment::PackageInstallerType GetDeploymentInstallerType(::AppInstaller::Manifest::InstallerTypeEnum installerType) { switch (installerType) { case ::AppInstaller::Manifest::InstallerTypeEnum::Burn: return Microsoft::Management::Deployment::PackageInstallerType::Burn; case ::AppInstaller::Manifest::InstallerTypeEnum::Exe: return Microsoft::Management::Deployment::PackageInstallerType::Exe; case ::AppInstaller::Manifest::InstallerTypeEnum::Inno: return Microsoft::Management::Deployment::PackageInstallerType::Inno; case ::AppInstaller::Manifest::InstallerTypeEnum::Msi: return Microsoft::Management::Deployment::PackageInstallerType::Msi; case ::AppInstaller::Manifest::InstallerTypeEnum::Msix: return Microsoft::Management::Deployment::PackageInstallerType::Msix; case ::AppInstaller::Manifest::InstallerTypeEnum::MSStore: return Microsoft::Management::Deployment::PackageInstallerType::MSStore; case ::AppInstaller::Manifest::InstallerTypeEnum::Nullsoft: return Microsoft::Management::Deployment::PackageInstallerType::Nullsoft; case ::AppInstaller::Manifest::InstallerTypeEnum::Portable: return Microsoft::Management::Deployment::PackageInstallerType::Portable; case ::AppInstaller::Manifest::InstallerTypeEnum::Wix: return Microsoft::Management::Deployment::PackageInstallerType::Wix; case ::AppInstaller::Manifest::InstallerTypeEnum::Zip: return Microsoft::Management::Deployment::PackageInstallerType::Zip; case ::AppInstaller::Manifest::InstallerTypeEnum::Unknown: return Microsoft::Management::Deployment::PackageInstallerType::Unknown; } return Microsoft::Management::Deployment::PackageInstallerType::Unknown; } ::AppInstaller::Manifest::InstallerTypeEnum GetManifestInstallerType(winrt::Microsoft::Management::Deployment::PackageInstallerType installerType) { switch (installerType) { case Microsoft::Management::Deployment::PackageInstallerType::Burn: return ::AppInstaller::Manifest::InstallerTypeEnum::Burn; case Microsoft::Management::Deployment::PackageInstallerType::Exe: return ::AppInstaller::Manifest::InstallerTypeEnum::Exe; case Microsoft::Management::Deployment::PackageInstallerType::Inno: return ::AppInstaller::Manifest::InstallerTypeEnum::Inno; case Microsoft::Management::Deployment::PackageInstallerType::Msi: return ::AppInstaller::Manifest::InstallerTypeEnum::Msi; case Microsoft::Management::Deployment::PackageInstallerType::Msix: return ::AppInstaller::Manifest::InstallerTypeEnum::Msix; case Microsoft::Management::Deployment::PackageInstallerType::MSStore: return ::AppInstaller::Manifest::InstallerTypeEnum::MSStore; case Microsoft::Management::Deployment::PackageInstallerType::Nullsoft: return ::AppInstaller::Manifest::InstallerTypeEnum::Nullsoft; case Microsoft::Management::Deployment::PackageInstallerType::Portable: return ::AppInstaller::Manifest::InstallerTypeEnum::Portable; case Microsoft::Management::Deployment::PackageInstallerType::Wix: return ::AppInstaller::Manifest::InstallerTypeEnum::Wix; case Microsoft::Management::Deployment::PackageInstallerType::Zip: return ::AppInstaller::Manifest::InstallerTypeEnum::Zip; case Microsoft::Management::Deployment::PackageInstallerType::Unknown: return ::AppInstaller::Manifest::InstallerTypeEnum::Unknown; } return ::AppInstaller::Manifest::InstallerTypeEnum::Unknown; } winrt::Microsoft::Management::Deployment::PackageInstallerScope GetDeploymentInstallerScope(::AppInstaller::Manifest::ScopeEnum installerScope) { switch (installerScope) { case ::AppInstaller::Manifest::ScopeEnum::User: return Microsoft::Management::Deployment::PackageInstallerScope::User; case ::AppInstaller::Manifest::ScopeEnum::Machine: return Microsoft::Management::Deployment::PackageInstallerScope::System; case ::AppInstaller::Manifest::ScopeEnum::Unknown: return Microsoft::Management::Deployment::PackageInstallerScope::Unknown; } return Microsoft::Management::Deployment::PackageInstallerScope::Unknown; } ::AppInstaller::Manifest::ScopeEnum GetManifestUninstallScope(winrt::Microsoft::Management::Deployment::PackageUninstallScope scope) { switch (scope) { case winrt::Microsoft::Management::Deployment::PackageUninstallScope::Any: return ::AppInstaller::Manifest::ScopeEnum::Unknown; case winrt::Microsoft::Management::Deployment::PackageUninstallScope::User: return ::AppInstaller::Manifest::ScopeEnum::User; case winrt::Microsoft::Management::Deployment::PackageUninstallScope::System: return ::AppInstaller::Manifest::ScopeEnum::Machine; } return ::AppInstaller::Manifest::ScopeEnum::Unknown; } ::AppInstaller::Manifest::ScopeEnum GetManifestRepairScope(winrt::Microsoft::Management::Deployment::PackageRepairScope scope) { switch (scope) { case winrt::Microsoft::Management::Deployment::PackageRepairScope::Any: return ::AppInstaller::Manifest::ScopeEnum::Unknown; case winrt::Microsoft::Management::Deployment::PackageRepairScope::User: return ::AppInstaller::Manifest::ScopeEnum::User; case winrt::Microsoft::Management::Deployment::PackageRepairScope::System: return ::AppInstaller::Manifest::ScopeEnum::Machine; } return ::AppInstaller::Manifest::ScopeEnum::Unknown; } winrt::Microsoft::Management::Deployment::ElevationRequirement GetDeploymentElevationRequirement(::AppInstaller::Manifest::ElevationRequirementEnum elevationRequirement) { switch (elevationRequirement) { case ::AppInstaller::Manifest::ElevationRequirementEnum::ElevationRequired: return Microsoft::Management::Deployment::ElevationRequirement::ElevationRequired; case ::AppInstaller::Manifest::ElevationRequirementEnum::ElevationProhibited: return Microsoft::Management::Deployment::ElevationRequirement::ElevationProhibited; case ::AppInstaller::Manifest::ElevationRequirementEnum::ElevatesSelf: return Microsoft::Management::Deployment::ElevationRequirement::ElevatesSelf; case ::AppInstaller::Manifest::ElevationRequirementEnum::Unknown: return Microsoft::Management::Deployment::ElevationRequirement::Unknown; } return Microsoft::Management::Deployment::ElevationRequirement::Unknown; } winrt::Microsoft::Management::Deployment::IconFileType GetDeploymentIconFileType(::AppInstaller::Manifest::IconFileTypeEnum iconFileType) { switch (iconFileType) { case ::AppInstaller::Manifest::IconFileTypeEnum::Ico: return Microsoft::Management::Deployment::IconFileType::Ico; case ::AppInstaller::Manifest::IconFileTypeEnum::Jpeg: return Microsoft::Management::Deployment::IconFileType::Jpeg; case ::AppInstaller::Manifest::IconFileTypeEnum::Png: return Microsoft::Management::Deployment::IconFileType::Png; } return Microsoft::Management::Deployment::IconFileType::Unknown; } winrt::Microsoft::Management::Deployment::IconResolution GetDeploymentIconResolution(::AppInstaller::Manifest::IconResolutionEnum iconResolution) { switch (iconResolution) { case ::AppInstaller::Manifest::IconResolutionEnum::Custom: return Microsoft::Management::Deployment::IconResolution::Custom; case ::AppInstaller::Manifest::IconResolutionEnum::Square16: return Microsoft::Management::Deployment::IconResolution::Square16; case ::AppInstaller::Manifest::IconResolutionEnum::Square20: return Microsoft::Management::Deployment::IconResolution::Square20; case ::AppInstaller::Manifest::IconResolutionEnum::Square24: return Microsoft::Management::Deployment::IconResolution::Square24; case ::AppInstaller::Manifest::IconResolutionEnum::Square30: return Microsoft::Management::Deployment::IconResolution::Square30; case ::AppInstaller::Manifest::IconResolutionEnum::Square32: return Microsoft::Management::Deployment::IconResolution::Square32; case ::AppInstaller::Manifest::IconResolutionEnum::Square36: return Microsoft::Management::Deployment::IconResolution::Square36; case ::AppInstaller::Manifest::IconResolutionEnum::Square40: return Microsoft::Management::Deployment::IconResolution::Square40; case ::AppInstaller::Manifest::IconResolutionEnum::Square48: return Microsoft::Management::Deployment::IconResolution::Square48; case ::AppInstaller::Manifest::IconResolutionEnum::Square60: return Microsoft::Management::Deployment::IconResolution::Square60; case ::AppInstaller::Manifest::IconResolutionEnum::Square64: return Microsoft::Management::Deployment::IconResolution::Square64; case ::AppInstaller::Manifest::IconResolutionEnum::Square72: return Microsoft::Management::Deployment::IconResolution::Square72; case ::AppInstaller::Manifest::IconResolutionEnum::Square80: return Microsoft::Management::Deployment::IconResolution::Square80; case ::AppInstaller::Manifest::IconResolutionEnum::Square96: return Microsoft::Management::Deployment::IconResolution::Square96; case ::AppInstaller::Manifest::IconResolutionEnum::Square256: return Microsoft::Management::Deployment::IconResolution::Square256; } return Microsoft::Management::Deployment::IconResolution::Custom; } winrt::Microsoft::Management::Deployment::IconTheme GetDeploymentIconTheme(::AppInstaller::Manifest::IconThemeEnum iconTheme) { switch (iconTheme) { case ::AppInstaller::Manifest::IconThemeEnum::Default: return Microsoft::Management::Deployment::IconTheme::Default; case ::AppInstaller::Manifest::IconThemeEnum::Light: return Microsoft::Management::Deployment::IconTheme::Light; case ::AppInstaller::Manifest::IconThemeEnum::Dark: return Microsoft::Management::Deployment::IconTheme::Dark; case ::AppInstaller::Manifest::IconThemeEnum::HighContrast: return Microsoft::Management::Deployment::IconTheme::HighContrast; } return Microsoft::Management::Deployment::IconTheme::Unknown; } winrt::Microsoft::Management::Deployment::AuthenticationType GetDeploymentAuthenticationType(::AppInstaller::Authentication::AuthenticationType authType) { switch (authType) { case ::AppInstaller::Authentication::AuthenticationType::None: return Microsoft::Management::Deployment::AuthenticationType::None; case ::AppInstaller::Authentication::AuthenticationType::MicrosoftEntraId: return Microsoft::Management::Deployment::AuthenticationType::MicrosoftEntraId; case ::AppInstaller::Authentication::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage: return Microsoft::Management::Deployment::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage; } return Microsoft::Management::Deployment::AuthenticationType::Unknown; } ::AppInstaller::Authentication::AuthenticationMode GetAuthenticationMode(winrt::Microsoft::Management::Deployment::AuthenticationMode authMode) { switch (authMode) { case winrt::Microsoft::Management::Deployment::AuthenticationMode::Interactive: return ::AppInstaller::Authentication::AuthenticationMode::Interactive; case winrt::Microsoft::Management::Deployment::AuthenticationMode::SilentPreferred: return ::AppInstaller::Authentication::AuthenticationMode::SilentPreferred; case winrt::Microsoft::Management::Deployment::AuthenticationMode::Silent: return ::AppInstaller::Authentication::AuthenticationMode::Silent; } return ::AppInstaller::Authentication::AuthenticationMode::Unknown; } ::AppInstaller::Authentication::AuthenticationArguments GetAuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments authArgs) { ::AppInstaller::Authentication::AuthenticationArguments result; result.Mode = ::AppInstaller::Authentication::AuthenticationMode::Silent; // Default to silent for com invocations. if (authArgs) { result.Mode = GetAuthenticationMode(authArgs.AuthenticationMode()); result.AuthenticationAccount = ::AppInstaller::Utility::ConvertToUTF8(authArgs.AuthenticationAccount()); } return result; } AddPackageCatalogStatus GetAddPackageCatalogOperationStatus(winrt::hresult hresult) { switch (hresult) { case APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED: return AddPackageCatalogStatus::AuthenticationError; case APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE: case APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE: case APPINSTALLER_CLI_ERROR_SOURCE_NOT_REMOTE: case APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS: case APPINSTALLER_CLI_ERROR_SOURCE_ARG_ALREADY_EXISTS: return AddPackageCatalogStatus::InvalidOptions; default: return HandleCommonCatalogOperationStatus(hresult); } } RemovePackageCatalogStatus GetRemovePackageCatalogOperationStatus(winrt::hresult hresult) { switch (hresult) { case APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST: return RemovePackageCatalogStatus::InvalidOptions; case APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE: return RemovePackageCatalogStatus::CatalogError; default: return HandleCommonCatalogOperationStatus(hresult); } } EditPackageCatalogStatus GetEditPackageCatalogOperationStatus(winrt::hresult hresult) { switch (hresult) { case APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST: return EditPackageCatalogStatus::InvalidOptions; case APPINSTALLER_CLI_ERROR_INVALID_SOURCE_TYPE: return EditPackageCatalogStatus::CatalogError; default: return HandleCommonCatalogOperationStatus(hresult); } } ::AppInstaller::Manifest::PlatformEnum GetPlatformEnum(WindowsPlatform value) { switch (value) { case WindowsPlatform::Unknown: return AppInstaller::Manifest::PlatformEnum::Unknown; case WindowsPlatform::Universal: return AppInstaller::Manifest::PlatformEnum::Universal; case WindowsPlatform::Desktop: return AppInstaller::Manifest::PlatformEnum::Desktop; case WindowsPlatform::IoT: return AppInstaller::Manifest::PlatformEnum::IoT; case WindowsPlatform::Team: return AppInstaller::Manifest::PlatformEnum::Team; case WindowsPlatform::Holographic: return AppInstaller::Manifest::PlatformEnum::Holographic; default: return AppInstaller::Manifest::PlatformEnum::Unknown; } } } ================================================ FILE: src/Microsoft.Management.Deployment/Converters.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageMatchFilter.g.h" #include #include #include #include namespace winrt::Microsoft::Management::Deployment::implementation { winrt::Microsoft::Management::Deployment::PackageMatchField GetDeploymentMatchField(::AppInstaller::Repository::PackageMatchField field); ::AppInstaller::Repository::PackageMatchField GetRepositoryMatchField(winrt::Microsoft::Management::Deployment::PackageMatchField field); winrt::Microsoft::Management::Deployment::PackageFieldMatchOption GetDeploymentMatchOption(::AppInstaller::Repository::MatchType type); ::AppInstaller::Repository::MatchType GetRepositoryMatchType(winrt::Microsoft::Management::Deployment::PackageFieldMatchOption option); ::AppInstaller::Repository::CompositeSearchBehavior GetRepositoryCompositeSearchBehavior(winrt::Microsoft::Management::Deployment::CompositeSearchBehavior searchBehavior); ::AppInstaller::Repository::PackageVersionMetadata GetRepositoryPackageVersionMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField packageVersionMetadataField); winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResultStatus(winrt::hresult hresult); std::optional<::AppInstaller::Utility::Architecture> GetUtilityArchitecture(winrt::Windows::System::ProcessorArchitecture architecture); std::optional GetWindowsSystemProcessorArchitecture(::AppInstaller::Utility::Architecture architecture); std::pair<::AppInstaller::Manifest::ScopeEnum, bool> GetManifestScope(winrt::Microsoft::Management::Deployment::PackageInstallScope scope); ::AppInstaller::Manifest::InstallerTypeEnum GetManifestInstallerType(winrt::Microsoft::Management::Deployment::PackageInstallerType installerType); winrt::Microsoft::Management::Deployment::PackageInstallerType GetDeploymentInstallerType(::AppInstaller::Manifest::InstallerTypeEnum installerType); winrt::Microsoft::Management::Deployment::PackageInstallerScope GetDeploymentInstallerScope(::AppInstaller::Manifest::ScopeEnum installerScope); ::AppInstaller::Manifest::ScopeEnum GetManifestUninstallScope(winrt::Microsoft::Management::Deployment::PackageUninstallScope scope); winrt::Microsoft::Management::Deployment::ElevationRequirement GetDeploymentElevationRequirement(::AppInstaller::Manifest::ElevationRequirementEnum elevationRequirement); winrt::Microsoft::Management::Deployment::IconFileType GetDeploymentIconFileType(::AppInstaller::Manifest::IconFileTypeEnum iconFileType); winrt::Microsoft::Management::Deployment::IconResolution GetDeploymentIconResolution(::AppInstaller::Manifest::IconResolutionEnum iconResolution); winrt::Microsoft::Management::Deployment::IconTheme GetDeploymentIconTheme(::AppInstaller::Manifest::IconThemeEnum iconTheme); winrt::Microsoft::Management::Deployment::AuthenticationType GetDeploymentAuthenticationType(::AppInstaller::Authentication::AuthenticationType authType); ::AppInstaller::Authentication::AuthenticationMode GetAuthenticationMode(winrt::Microsoft::Management::Deployment::AuthenticationMode authMode); ::AppInstaller::Authentication::AuthenticationArguments GetAuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments authArgs); ::AppInstaller::Manifest::ScopeEnum GetManifestRepairScope(winrt::Microsoft::Management::Deployment::PackageRepairScope scope); winrt::Microsoft::Management::Deployment::AddPackageCatalogStatus GetAddPackageCatalogOperationStatus(winrt::hresult hresult); winrt::Microsoft::Management::Deployment::RemovePackageCatalogStatus GetRemovePackageCatalogOperationStatus(winrt::hresult hresult); winrt::Microsoft::Management::Deployment::EditPackageCatalogStatus GetEditPackageCatalogOperationStatus(winrt::hresult hresult); ::AppInstaller::Manifest::PlatformEnum GetPlatformEnum(winrt::Microsoft::Management::Deployment::WindowsPlatform value); #define WINGET_GET_OPERATION_RESULT_STATUS(_installResultStatus_, _uninstallResultStatus_, _downloadResultStatus_, _repairResultStatus_) \ if constexpr (std::is_same_v) \ { \ resultStatus = TStatus::_installResultStatus_; \ } \ else if constexpr (std::is_same_v) \ { \ resultStatus = TStatus::_uninstallResultStatus_; \ } \ else if constexpr (std::is_same_v) \ { \ resultStatus = TStatus::_downloadResultStatus_; \ } \ else if constexpr (std::is_same_v) \ { \ resultStatus = TStatus::_repairResultStatus_; \ } \ template TStatus GetOperationResultStatus(::AppInstaller::CLI::Workflow::ExecutionStage executionStage, winrt::hresult hresult) { TStatus resultStatus = TStatus::Ok; // Map some known hresults to specific statuses, otherwise use the execution stage to determine the status. switch (hresult) { case S_OK: resultStatus = TStatus::Ok; break; case APPINSTALLER_CLI_ERROR_MSSTORE_BLOCKED_BY_POLICY: case APPINSTALLER_CLI_ERROR_MSSTORE_APP_BLOCKED_BY_POLICY: case APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED: case APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY: resultStatus = TStatus::BlockedByPolicy; break; case APPINSTALLER_CLI_ERROR_INVALID_MANIFEST: resultStatus = TStatus::ManifestError; break; case E_INVALIDARG: case APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS: resultStatus = TStatus::InvalidOptions; break; case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER: WINGET_GET_OPERATION_RESULT_STATUS(NoApplicableInstallers, InternalError, NoApplicableInstallers, NoApplicableRepairer); break; case APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE: case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN: case APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER: WINGET_GET_OPERATION_RESULT_STATUS(NoApplicableUpgrade, InternalError, InternalError, InternalError); break; case APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND: case APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED: WINGET_GET_OPERATION_RESULT_STATUS(InstallError, UninstallError, InternalError, InternalError); break; case APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND: case APPINSTALLER_CLI_ERROR_REPAIR_NOT_APPLICABLE: case APPINSTALLER_CLI_ERROR_EXEC_REPAIR_FAILED: case APPINSTALLER_CLI_ERROR_REPAIR_NOT_SUPPORTED: case APPINSTALLER_CLI_ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED: WINGET_GET_OPERATION_RESULT_STATUS(InternalError, InternalError, InternalError, RepairError); break; case APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED: WINGET_GET_OPERATION_RESULT_STATUS(PackageAgreementsNotAccepted, InternalError, PackageAgreementsNotAccepted, PackageAgreementsNotAccepted); break; case APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX: case APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED: case APPINSTALLER_CLI_ERROR_YAML_INIT_FAILED: case APPINSTALLER_CLI_ERROR_YAML_INVALID_MAPPING_KEY: case APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY: case APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION: case APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED: case APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE: case APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA: case APPINSTALLER_CLI_ERROR_LIBYAML_ERROR: case APPINSTALLER_CLI_ERROR_INTERNAL_ERROR: resultStatus = TStatus::InternalError; break; default: switch (executionStage) { case ::AppInstaller::CLI::Workflow::ExecutionStage::Initial: resultStatus = TStatus::InternalError; break; case ::AppInstaller::CLI::Workflow::ExecutionStage::ParseArgs: resultStatus = TStatus::InvalidOptions; break; case ::AppInstaller::CLI::Workflow::ExecutionStage::Discovery: resultStatus = TStatus::CatalogError; break; case ::AppInstaller::CLI::Workflow::ExecutionStage::Download: WINGET_GET_OPERATION_RESULT_STATUS(DownloadError, InternalError, DownloadError, DownloadError); break; case ::AppInstaller::CLI::Workflow::ExecutionStage::PreExecution: resultStatus = TStatus::InternalError; break; case ::AppInstaller::CLI::Workflow::ExecutionStage::Execution: WINGET_GET_OPERATION_RESULT_STATUS(InstallError, UninstallError, InternalError, RepairError); break; case ::AppInstaller::CLI::Workflow::ExecutionStage::PostExecution: resultStatus = TStatus::InternalError; break; default: resultStatus = TStatus::InternalError; break; } } return resultStatus; } template TStatus HandleCommonCatalogOperationStatus(winrt::hresult hresult) { // Common status handling for AddPackageCatalogStatus and RemovePackageCatalogStatus. if constexpr (std::is_same_v || std::is_same_v) { switch (hresult) { case APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN: case E_ACCESSDENIED: return TStatus::AccessDenied; case APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS: case E_INVALIDARG: return TStatus::InvalidOptions; default: break; } } // Common status handling for AddPackageCatalogStatus, RemovePackageCatalogStatus, and RefreshPackageCatalogStatus. switch (hresult) { case S_OK: return TStatus::Ok; case APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY: return TStatus::GroupPolicyError; case APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE: return TStatus::CatalogError; case APPINSTALLER_CLI_ERROR_INTERNAL_ERROR: default: return TStatus::InternalError; } } template TStatus GetPackageCatalogOperationStatus(winrt::hresult hresult) { if constexpr (std::is_same_v) { return GetAddPackageCatalogOperationStatus(hresult); } else if constexpr (std::is_same_v) { return GetRemovePackageCatalogOperationStatus(hresult); } else if constexpr (std::is_same_v) { return HandleCommonCatalogOperationStatus(hresult); } else if constexpr (std::is_same_v) { return GetEditPackageCatalogOperationStatus(hresult); } else { throw winrt::hresult_error(E_UNEXPECTED); } } } ================================================ FILE: src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "CreateCompositePackageCatalogOptions.h" #pragma warning( pop ) #include "CreateCompositePackageCatalogOptions.g.cpp" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { Windows::Foundation::Collections::IVector CreateCompositePackageCatalogOptions::Catalogs() { return m_catalogs; } winrt::Microsoft::Management::Deployment::CompositeSearchBehavior CreateCompositePackageCatalogOptions::CompositeSearchBehavior() { return m_compositeSearchBehavior; } void CreateCompositePackageCatalogOptions::CompositeSearchBehavior(winrt::Microsoft::Management::Deployment::CompositeSearchBehavior const& value) { m_compositeSearchBehavior = value; } winrt::Microsoft::Management::Deployment::PackageInstallScope CreateCompositePackageCatalogOptions::InstalledScope() { return m_installedScope; } void CreateCompositePackageCatalogOptions::InstalledScope(winrt::Microsoft::Management::Deployment::PackageInstallScope const& value) { m_installedScope = value; } CoCreatableMicrosoftManagementDeploymentClass(CreateCompositePackageCatalogOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/CreateCompositePackageCatalogOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "CreateCompositePackageCatalogOptions.g.h" #include "Public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_CreateCompositePackageCatalogOptions)] struct CreateCompositePackageCatalogOptions : CreateCompositePackageCatalogOptionsT { CreateCompositePackageCatalogOptions() = default; winrt::Windows::Foundation::Collections::IVector Catalogs(); winrt::Microsoft::Management::Deployment::CompositeSearchBehavior CompositeSearchBehavior(); void CompositeSearchBehavior(winrt::Microsoft::Management::Deployment::CompositeSearchBehavior const& value); winrt::Microsoft::Management::Deployment::PackageInstallScope InstalledScope(); void InstalledScope(winrt::Microsoft::Management::Deployment::PackageInstallScope const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Windows::Foundation::Collections::IVector m_catalogs{ winrt::single_threaded_vector() }; winrt::Microsoft::Management::Deployment::CompositeSearchBehavior m_compositeSearchBehavior = winrt::Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromAllCatalogs; winrt::Microsoft::Management::Deployment::PackageInstallScope m_installedScope = winrt::Microsoft::Management::Deployment::PackageInstallScope::Any; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct CreateCompositePackageCatalogOptions : CreateCompositePackageCatalogOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/Documentation.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Documentation.g.cpp" #include "Documentation.h" namespace winrt::Microsoft::Management::Deployment::implementation { void Documentation::Initialize(::AppInstaller::Manifest::Documentation documentation) { m_documentation = std::move(documentation); } hstring Documentation::DocumentLabel() { return winrt::to_hstring(m_documentation.DocumentLabel); } hstring Documentation::DocumentUrl() { return winrt::to_hstring(m_documentation.DocumentUrl); } } ================================================ FILE: src/Microsoft.Management.Deployment/Documentation.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Documentation.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct Documentation : DocumentationT { Documentation() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Manifest::Documentation documentation); #endif hstring DocumentLabel(); hstring DocumentUrl(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Manifest::Documentation m_documentation{}; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/DownloadOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "DownloadOptions.h" #pragma warning( pop ) #include "DownloadOptions.g.cpp" #include "Helpers.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { DownloadOptions::DownloadOptions() { } winrt::Microsoft::Management::Deployment::PackageVersionId DownloadOptions::PackageVersionId() { return m_packageVersionId; } void DownloadOptions::PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value) { m_packageVersionId = value; } winrt::Microsoft::Management::Deployment::PackageInstallScope DownloadOptions::Scope() { return m_scope; } void DownloadOptions::Scope(winrt::Microsoft::Management::Deployment::PackageInstallScope const& value) { m_scope = value; } winrt::Microsoft::Management::Deployment::PackageInstallerType DownloadOptions::InstallerType() { return m_installerType; } void DownloadOptions::InstallerType(winrt::Microsoft::Management::Deployment::PackageInstallerType const& value) { m_installerType = value; } winrt::Windows::System::ProcessorArchitecture DownloadOptions::Architecture() { return m_architecture; } void DownloadOptions::Architecture(winrt::Windows::System::ProcessorArchitecture const& value) { m_architecture = value; } hstring DownloadOptions::Locale() { return hstring(m_locale); } void DownloadOptions::Locale(hstring const& value) { m_locale = value; } hstring DownloadOptions::DownloadDirectory() { return hstring(m_downloadDirectory); } void DownloadOptions::DownloadDirectory(hstring const& value) { m_downloadDirectory = value; } bool DownloadOptions::AllowHashMismatch() { return m_allowHashMismatch; } void DownloadOptions::AllowHashMismatch(bool value) { m_allowHashMismatch = value; } bool DownloadOptions::SkipDependencies() { return m_skipDependencies; } void DownloadOptions::SkipDependencies(bool value) { m_skipDependencies = value; } bool DownloadOptions::AcceptPackageAgreements() { return m_acceptPackageAgreements; } void DownloadOptions::AcceptPackageAgreements(bool value) { m_acceptPackageAgreements = value; } hstring DownloadOptions::CorrelationData() { return hstring(m_correlationData); } void DownloadOptions::CorrelationData(hstring const& value) { m_correlationData = value; } winrt::Microsoft::Management::Deployment::AuthenticationArguments DownloadOptions::AuthenticationArguments() { return m_authenticationArguments; } void DownloadOptions::AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value) { m_authenticationArguments = value; } bool DownloadOptions::SkipMicrosoftStoreLicense() { return m_skipMicrosoftStoreLicense; } void DownloadOptions::SkipMicrosoftStoreLicense(bool value) { m_skipMicrosoftStoreLicense = value; } winrt::Microsoft::Management::Deployment::WindowsPlatform DownloadOptions::Platform() { return m_platform; } void DownloadOptions::Platform(winrt::Microsoft::Management::Deployment::WindowsPlatform value) { m_platform = value; } hstring DownloadOptions::TargetOSVersion() { return hstring(m_targetOSVersion); } void DownloadOptions::TargetOSVersion(hstring const& value) { m_targetOSVersion = value; } CoCreatableMicrosoftManagementDeploymentClass(DownloadOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/DownloadOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "DownloadOptions.g.h" #include "Public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_DownloadOptions)] struct DownloadOptions : DownloadOptionsT { DownloadOptions(); winrt::Microsoft::Management::Deployment::PackageVersionId PackageVersionId(); void PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value); winrt::Microsoft::Management::Deployment::PackageInstallScope Scope(); void Scope(winrt::Microsoft::Management::Deployment::PackageInstallScope const& value); winrt::Microsoft::Management::Deployment::PackageInstallerType InstallerType(); void InstallerType(winrt::Microsoft::Management::Deployment::PackageInstallerType const& value); winrt::Windows::System::ProcessorArchitecture Architecture(); void Architecture(winrt::Windows::System::ProcessorArchitecture const& value); hstring Locale(); void Locale(hstring const& value); hstring DownloadDirectory(); void DownloadDirectory(hstring const& value); bool AllowHashMismatch(); void AllowHashMismatch(bool value); bool SkipDependencies(); void SkipDependencies(bool value); bool AcceptPackageAgreements(); void AcceptPackageAgreements(bool value); hstring CorrelationData(); void CorrelationData(hstring const& value); winrt::Microsoft::Management::Deployment::AuthenticationArguments AuthenticationArguments(); void AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value); bool SkipMicrosoftStoreLicense(); void SkipMicrosoftStoreLicense(bool value); winrt::Microsoft::Management::Deployment::WindowsPlatform Platform(); void Platform(winrt::Microsoft::Management::Deployment::WindowsPlatform value); hstring TargetOSVersion(); void TargetOSVersion(hstring const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::PackageVersionId m_packageVersionId{ nullptr }; winrt::Microsoft::Management::Deployment::PackageInstallScope m_scope = winrt::Microsoft::Management::Deployment::PackageInstallScope::Any; winrt::Microsoft::Management::Deployment::PackageInstallerType m_installerType = winrt::Microsoft::Management::Deployment::PackageInstallerType::Unknown; winrt::Windows::System::ProcessorArchitecture m_architecture = winrt::Windows::System::ProcessorArchitecture::Unknown; std::wstring m_locale = L""; std::wstring m_downloadDirectory = L""; bool m_allowHashMismatch = false; bool m_skipDependencies = false; bool m_acceptPackageAgreements = true; std::wstring m_correlationData = L""; winrt::Microsoft::Management::Deployment::AuthenticationArguments m_authenticationArguments{ nullptr }; bool m_skipMicrosoftStoreLicense = false; winrt::Microsoft::Management::Deployment::WindowsPlatform m_platform = winrt::Microsoft::Management::Deployment::WindowsPlatform::Unknown; std::wstring m_targetOSVersion; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct DownloadOptions : DownloadOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/DownloadResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "DownloadResult.h" #include "DownloadResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void DownloadResult::Initialize( winrt::Microsoft::Management::Deployment::DownloadResultStatus status, winrt::hresult extendedErrorCode, hstring const& correlationData) { m_status = status; m_extendedErrorCode = extendedErrorCode; m_correlationData = correlationData; } hstring DownloadResult::CorrelationData() { return hstring(m_correlationData); } winrt::Microsoft::Management::Deployment::DownloadResultStatus DownloadResult::Status() { return m_status; } winrt::hresult DownloadResult::ExtendedErrorCode() { return m_extendedErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/DownloadResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "DownloadResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct DownloadResult : DownloadResultT { DownloadResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::DownloadResultStatus status, winrt::hresult extendedErrorCode, hstring const& correlationData); #endif hstring CorrelationData(); winrt::Microsoft::Management::Deployment::DownloadResultStatus Status(); winrt::hresult ExtendedErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: std::wstring m_correlationData = L""; winrt::Microsoft::Management::Deployment::DownloadResultStatus m_status = winrt::Microsoft::Management::Deployment::DownloadResultStatus::Ok; winrt::hresult m_extendedErrorCode = S_OK; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/EditPackageCatalogOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "EditPackageCatalogOptions.h" #pragma warning( pop ) #include "EditPackageCatalogOptions.g.cpp" #include "Converters.h" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { hstring EditPackageCatalogOptions::Name() { return hstring(m_name); } void EditPackageCatalogOptions::Name(hstring const& value) { m_name = value; } Windows::Foundation::IReference EditPackageCatalogOptions::Explicit() { return m_explicit; } void EditPackageCatalogOptions::Explicit(Windows::Foundation::IReference value) { m_explicit = value; } Windows::Foundation::IReference EditPackageCatalogOptions::Priority() { return m_priority; } void EditPackageCatalogOptions::Priority(Windows::Foundation::IReference value) { m_priority = value; } CoCreatableMicrosoftManagementDeploymentClass(EditPackageCatalogOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/EditPackageCatalogOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "EditPackageCatalogOptions.g.h" #include "public/ComClsids.h" #include #include #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_EditPackageCatalogOptions)] struct EditPackageCatalogOptions : EditPackageCatalogOptionsT { EditPackageCatalogOptions() = default; hstring Name(); void Name(hstring const& value); Windows::Foundation::IReference Explicit(); void Explicit(Windows::Foundation::IReference value); Windows::Foundation::IReference Priority(); void Priority(Windows::Foundation::IReference value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: hstring m_name = L""; std::optional m_explicit; std::optional m_priority; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct EditPackageCatalogOptions : EditPackageCatalogOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/EditPackageCatalogResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "EditPackageCatalogResult.h" #include "EditPackageCatalogResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void EditPackageCatalogResult::Initialize( winrt::Microsoft::Management::Deployment::EditPackageCatalogStatus status, winrt::hresult extendedErrorCode) { m_status = status; m_extendedErrorCode = extendedErrorCode; } winrt::Microsoft::Management::Deployment::EditPackageCatalogStatus EditPackageCatalogResult::Status() { return m_status; } winrt::hresult EditPackageCatalogResult::ExtendedErrorCode() { return m_extendedErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/EditPackageCatalogResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "EditPackageCatalogResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct EditPackageCatalogResult : EditPackageCatalogResultT { EditPackageCatalogResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::EditPackageCatalogStatus status, winrt::hresult extendedErrorCode); #endif winrt::Microsoft::Management::Deployment::EditPackageCatalogStatus Status(); winrt::hresult ExtendedErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::EditPackageCatalogStatus m_status = winrt::Microsoft::Management::Deployment::EditPackageCatalogStatus::Ok; winrt::hresult m_extendedErrorCode = S_OK; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/FindPackagesOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "FindPackagesOptions.h" #pragma warning( pop ) #include "FindPackagesOptions.g.cpp" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { winrt::Windows::Foundation::Collections::IVector FindPackagesOptions::Selectors() { return m_selectors; } winrt::Windows::Foundation::Collections::IVector FindPackagesOptions::Filters() { return m_filters; } uint32_t FindPackagesOptions::ResultLimit() { return m_resultLimit; } void FindPackagesOptions::ResultLimit(uint32_t value) { m_resultLimit = value; } CoCreatableMicrosoftManagementDeploymentClass(FindPackagesOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/FindPackagesOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "FindPackagesOptions.g.h" #include "Public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_FindPackagesOptions)] struct FindPackagesOptions : FindPackagesOptionsT { FindPackagesOptions() = default; winrt::Windows::Foundation::Collections::IVector Selectors(); winrt::Windows::Foundation::Collections::IVector Filters(); uint32_t ResultLimit(); void ResultLimit(uint32_t value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: uint32_t m_resultLimit = 0; Windows::Foundation::Collections::IVector m_selectors{ winrt::single_threaded_vector() }; Windows::Foundation::Collections::IVector m_filters{ winrt::single_threaded_vector() }; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct FindPackagesOptions : FindPackagesOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/FindPackagesResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "FindPackagesResult.h" #include "FindPackagesResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void FindPackagesResult::Initialize( winrt::Microsoft::Management::Deployment::FindPackagesResultStatus status, bool wasLimitExceeded, Windows::Foundation::Collections::IVector matches, winrt::hresult extendedErrorCode) { m_status = status; m_matches = matches; m_wasLimitExceeded = wasLimitExceeded; m_extendedErrorCode = extendedErrorCode; } winrt::Microsoft::Management::Deployment::FindPackagesResultStatus FindPackagesResult::Status() { return m_status; } winrt::Windows::Foundation::Collections::IVectorView FindPackagesResult::Matches() { return m_matches.GetView(); } bool FindPackagesResult::WasLimitExceeded() { return m_wasLimitExceeded; } winrt::hresult FindPackagesResult::ExtendedErrorCode() { return m_extendedErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/FindPackagesResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "FindPackagesResult.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct FindPackagesResult : FindPackagesResultT { FindPackagesResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::FindPackagesResultStatus status, bool wasLimitExceeded, Windows::Foundation::Collections::IVector matches, winrt::hresult extendedErrorCode); #endif winrt::Microsoft::Management::Deployment::FindPackagesResultStatus Status(); winrt::Windows::Foundation::Collections::IVectorView Matches(); bool WasLimitExceeded(); winrt::hresult ExtendedErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::FindPackagesResultStatus m_status = winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; Windows::Foundation::Collections::IVector m_matches{ winrt::single_threaded_vector() }; bool m_wasLimitExceeded = false; winrt::hresult m_extendedErrorCode = S_OK; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/Helpers.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include #include #include #include #include #include #include using namespace std::string_literals; using namespace std::string_view_literals; namespace winrt::Microsoft::Management::Deployment::implementation { namespace { static std::optional s_callerName; static wil::srwlock s_callerNameLock; } void SetComCallerName(std::string name) { auto lock = s_callerNameLock.lock_exclusive(); s_callerName.emplace(std::move(name)); } std::string GetComCallerName(std::string defaultNameIfNotSet) { auto lock = s_callerNameLock.lock_shared(); return s_callerName.has_value() ? s_callerName.value() : defaultNameIfNotSet; } std::pair GetCallerProcessId() { RPC_STATUS rpcStatus = RPC_S_OK; RPC_CALL_ATTRIBUTES callAttributes = {}; callAttributes.Version = RPC_CALL_ATTRIBUTES_VERSION; callAttributes.Flags = RPC_QUERY_CLIENT_PID; rpcStatus = RpcServerInqCallAttributes(nullptr, &callAttributes); if (rpcStatus == RPC_S_NO_CALL_ACTIVE || (rpcStatus == RPC_S_OK && HandleToULong(callAttributes.ClientPID) == GetCurrentProcessId())) { // in-proc is supported now. return { S_OK, GetCurrentProcessId() }; } else if (rpcStatus == RPC_S_OK) { // out-of-proc case. return { S_OK, HandleToULong(callAttributes.ClientPID) }; } else { return { E_ACCESSDENIED, 0 }; } } std::wstring_view GetStringForCapability(Capability capability) { switch (capability) { case Capability::PackageManagement: return L"packageManagement"sv; case Capability::PackageQuery: return L"packageQuery"sv; default: winrt::throw_hresult(E_UNEXPECTED); } } HRESULT EnsureProcessHasCapability(Capability requiredCapability, DWORD callerProcessId) { bool allowed = false; if (winrt::Windows::Foundation::Metadata::ApiInformation::IsTypePresent(winrt::name_of())) { // Get the caller process id and use it to check if the caller has permissions to access the feature. winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapabilityAccessStatus status = winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapabilityAccessStatus::DeniedBySystem; winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapability capability{ nullptr }; try { capability = winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapability::CreateWithProcessIdForUser(nullptr, GetStringForCapability(requiredCapability), callerProcessId); } catch (const winrt::hresult_invalid_argument&) { } if (capability) { status = capability.CheckAccess(); return ((status == winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapabilityAccessStatus::Allowed) ? S_OK : E_ACCESSDENIED); } } // If AppCapability is not present, require at least medium IL callers auto requiredIntegrityLevel = AppInstaller::Security::IntegrityLevel::Medium; if (callerProcessId != GetCurrentProcessId()) { allowed = AppInstaller::Security::IsCOMCallerIntegrityLevelAtLeast(requiredIntegrityLevel); } else { allowed = AppInstaller::Security::IsCurrentIntegrityLevelAtLeast(requiredIntegrityLevel); } return (allowed ? S_OK : E_ACCESSDENIED); } HRESULT EnsureComCallerHasCapability(Capability requiredCapability) { auto [hr, callerProcessId] = GetCallerProcessId(); RETURN_IF_FAILED(hr); hr = EnsureProcessHasCapability(requiredCapability, callerProcessId); // The Windows.Management.Deployment API has set the precedent that packageManagement is a superset of packageQuery // and packageQuery does not need to be declared separately. if (FAILED(hr) && requiredCapability == Capability::PackageQuery) { hr = EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId); } RETURN_HR(hr); } // Best effort at getting caller info. This should only be used for logging. std::wstring TryGetCallerProcessInfo(DWORD callerProcessId) { wil::unique_process_handle processHandle(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, callerProcessId)); if (processHandle) { WCHAR packageFamilyName[PACKAGE_FAMILY_NAME_MAX_LENGTH]{}; UINT32 length = ARRAYSIZE(packageFamilyName); if (::GetPackageFamilyName(processHandle.get(), &length, packageFamilyName) == ERROR_SUCCESS) { // If the package is calling into itself, fall through to the executable name if (AppInstaller::Runtime::GetPackageFamilyName() != packageFamilyName) { return { packageFamilyName }; } } // if the caller doesn't have an AppUserModelID then fall back to the executable name std::filesystem::path executablePath = AppInstaller::Filesystem::GetExecutablePathForProcess(processHandle.get()); if (executablePath.has_filename()) { return executablePath.filename(); } else if (!executablePath.empty()) { AICLI_LOG(Fail, Error, << "Unable to get valid executable for process ID [" << callerProcessId << "]: " << executablePath); } } return {}; } std::string GetCallerName() { // See if caller name is set by caller std::string callerName = GetComCallerName(""); // Get process string if (callerName.empty()) { try { auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); if (SUCCEEDED(hrGetCallerId)) { callerName = AppInstaller::Utility::ConvertToUTF8(TryGetCallerProcessInfo(callerProcessId)); } } CATCH_LOG(); } if (callerName.empty()) { callerName = "UnknownComCaller"; } return callerName; } bool IsBackgroundProcessForPolicy() { bool isBackgroundProcessForPolicy = false; try { auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); if (SUCCEEDED(hrGetCallerId) && callerProcessId != GetCurrentProcessId()) { // OutOfProc case, we check for explorer.exe auto callerNameWide = AppInstaller::Utility::ConvertToUTF16(GetCallerName()); auto processName = AppInstaller::Utility::ConvertToUTF8(std::filesystem::path{ callerNameWide }.filename().wstring()); if (::AppInstaller::Utility::CaseInsensitiveEquals("explorer.exe", processName) || ::AppInstaller::Utility::CaseInsensitiveEquals("taskhostw.exe", processName)) { isBackgroundProcessForPolicy = true; } } } CATCH_LOG(); return isBackgroundProcessForPolicy; } } ================================================ FILE: src/Microsoft.Management.Deployment/Helpers.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Public/CoCreatableMicrosoftManagementDeploymentClass.h" namespace winrt::Microsoft::Management::Deployment::implementation { void SetComCallerName(std::string name); std::string GetComCallerName(std::string defaultNameIfNotSet); enum class Capability { PackageManagement, PackageQuery }; HRESULT EnsureProcessHasCapability(Capability requiredCapability, DWORD callerProcessId); HRESULT EnsureComCallerHasCapability(Capability requiredCapability); std::pair GetCallerProcessId(); std::wstring TryGetCallerProcessInfo(DWORD callerProcessId); std::string GetCallerName(); bool IsBackgroundProcessForPolicy(); } ================================================ FILE: src/Microsoft.Management.Deployment/Icon.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Icon.g.cpp" #include "Icon.h" #include "Converters.h" namespace winrt::Microsoft::Management::Deployment::implementation { void Icon::Initialize(::AppInstaller::Manifest::Icon icon) { m_icon = std::move(icon); } hstring Icon::Url() { return winrt::to_hstring(m_icon.Url); } IconFileType Icon::FileType() { return GetDeploymentIconFileType(m_icon.FileType); } IconResolution Icon::Resolution() { return GetDeploymentIconResolution(m_icon.Resolution); } IconTheme Icon::Theme() { return GetDeploymentIconTheme(m_icon.Theme); } com_array Icon::Sha256() { return com_array(m_icon.Sha256); } } ================================================ FILE: src/Microsoft.Management.Deployment/Icon.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "Icon.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct Icon : IconT { Icon() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Manifest::Icon icon); #endif hstring Url(); IconFileType FileType(); IconResolution Resolution(); IconTheme Theme(); com_array Sha256(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Manifest::Icon m_icon{}; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/InstallOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "InstallOptions.h" #pragma warning( pop ) #include "InstallOptions.g.cpp" #include "Converters.h" #include "Helpers.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { InstallOptions::InstallOptions() { // Populate the allowed architectures with the default values for the machine for (AppInstaller::Utility::Architecture architecture : AppInstaller::Utility::GetApplicableArchitectures()) { auto convertedArchitecture = GetWindowsSystemProcessorArchitecture(architecture); if (convertedArchitecture) { m_allowedArchitectures.Append(convertedArchitecture.value()); } } } winrt::Microsoft::Management::Deployment::PackageVersionId InstallOptions::PackageVersionId() { return m_packageVersionId; } void InstallOptions::PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value) { m_packageVersionId = value; } hstring InstallOptions::PreferredInstallLocation() { return hstring(m_preferredInstallLocation); } void InstallOptions::PreferredInstallLocation(hstring const& value) { m_preferredInstallLocation = value; } winrt::Microsoft::Management::Deployment::PackageInstallScope InstallOptions::PackageInstallScope() { return m_packageInstallScope; } void InstallOptions::PackageInstallScope(winrt::Microsoft::Management::Deployment::PackageInstallScope const& value) { m_packageInstallScope = value; } winrt::Microsoft::Management::Deployment::PackageInstallMode InstallOptions::PackageInstallMode() { return m_packageInstallMode; } void InstallOptions::PackageInstallMode(winrt::Microsoft::Management::Deployment::PackageInstallMode const& value) { m_packageInstallMode = value; } winrt::Microsoft::Management::Deployment::PackageInstallerType InstallOptions::InstallerType() { return m_installerType; } void InstallOptions::InstallerType(winrt::Microsoft::Management::Deployment::PackageInstallerType const& value) { m_installerType = value; } hstring InstallOptions::LogOutputPath() { return hstring(m_logOutputPath); } void InstallOptions::LogOutputPath(hstring const& value) { m_logOutputPath = value; } bool InstallOptions::AllowHashMismatch() { return m_allowHashMismatch; } void InstallOptions::AllowHashMismatch(bool value) { m_allowHashMismatch = value; } bool InstallOptions::BypassIsStoreClientBlockedPolicyCheck() { return m_bypassIsStoreClientBlockedPolicyCheck; } void InstallOptions::BypassIsStoreClientBlockedPolicyCheck(bool value) { m_bypassIsStoreClientBlockedPolicyCheck = value; } hstring InstallOptions::ReplacementInstallerArguments() { return hstring(m_replacementInstallerArguments); } void InstallOptions::ReplacementInstallerArguments(hstring const& value) { m_replacementInstallerArguments = value; } hstring InstallOptions::AdditionalInstallerArguments() { return hstring(m_additionalInstallerArguments); } void InstallOptions::AdditionalInstallerArguments(hstring const& value) { m_additionalInstallerArguments = value; } hstring InstallOptions::CorrelationData() { return hstring(m_correlationData); } void InstallOptions::CorrelationData(hstring const& value) { m_correlationData = value; } hstring InstallOptions::AdditionalPackageCatalogArguments() { return hstring(m_additionalPackageCatalogArguments); } void InstallOptions::AdditionalPackageCatalogArguments(hstring const& value) { m_additionalPackageCatalogArguments = value; } winrt::Windows::Foundation::Collections::IVector InstallOptions::AllowedArchitectures() { return m_allowedArchitectures; } bool InstallOptions::AllowUpgradeToUnknownVersion() { return m_allowUpgradeToUnknownVersion; } void InstallOptions::AllowUpgradeToUnknownVersion(bool value) { m_allowUpgradeToUnknownVersion = value; } bool InstallOptions::Force() { return m_force; } void InstallOptions::Force(bool value) { m_force = value; } void InstallOptions::AcceptPackageAgreements(bool value) { m_acceptPackageAgreements = value; } bool InstallOptions::AcceptPackageAgreements() { return m_acceptPackageAgreements; } void InstallOptions::SkipDependencies(bool value) { m_skipDependencies = value; } bool InstallOptions::SkipDependencies() { return m_skipDependencies; } winrt::Microsoft::Management::Deployment::AuthenticationArguments InstallOptions::AuthenticationArguments() { return m_authenticationArguments; } void InstallOptions::AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value) { m_authenticationArguments = value; } CoCreatableMicrosoftManagementDeploymentClass(InstallOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/InstallOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "InstallOptions.g.h" #include "Public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_InstallOptions)] struct InstallOptions : InstallOptionsT { InstallOptions(); winrt::Microsoft::Management::Deployment::PackageVersionId PackageVersionId(); void PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value); hstring PreferredInstallLocation(); void PreferredInstallLocation(hstring const& value); winrt::Microsoft::Management::Deployment::PackageInstallScope PackageInstallScope(); void PackageInstallScope(winrt::Microsoft::Management::Deployment::PackageInstallScope const& value); winrt::Microsoft::Management::Deployment::PackageInstallMode PackageInstallMode(); void PackageInstallMode(winrt::Microsoft::Management::Deployment::PackageInstallMode const& value); winrt::Microsoft::Management::Deployment::PackageInstallerType InstallerType(); void InstallerType(winrt::Microsoft::Management::Deployment::PackageInstallerType const& value); hstring LogOutputPath(); void LogOutputPath(hstring const& value); bool AllowHashMismatch(); void AllowHashMismatch(bool value); bool BypassIsStoreClientBlockedPolicyCheck(); void BypassIsStoreClientBlockedPolicyCheck(bool value); hstring ReplacementInstallerArguments(); void ReplacementInstallerArguments(hstring const& value); hstring AdditionalInstallerArguments(); void AdditionalInstallerArguments(hstring const& value); hstring CorrelationData(); void CorrelationData(hstring const& value); hstring AdditionalPackageCatalogArguments(); void AdditionalPackageCatalogArguments(hstring const& value); winrt::Windows::Foundation::Collections::IVector AllowedArchitectures(); bool AllowUpgradeToUnknownVersion(); void AllowUpgradeToUnknownVersion(bool value); bool Force(); void Force(bool value); bool AcceptPackageAgreements(); void AcceptPackageAgreements(bool value); bool SkipDependencies(); void SkipDependencies(bool value); winrt::Microsoft::Management::Deployment::AuthenticationArguments AuthenticationArguments(); void AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::PackageVersionId m_packageVersionId{ nullptr }; std::wstring m_preferredInstallLocation = L""; winrt::Microsoft::Management::Deployment::PackageInstallScope m_packageInstallScope = winrt::Microsoft::Management::Deployment::PackageInstallScope::Any; winrt::Microsoft::Management::Deployment::PackageInstallMode m_packageInstallMode = winrt::Microsoft::Management::Deployment::PackageInstallMode::Default; winrt::Microsoft::Management::Deployment::PackageInstallerType m_installerType = winrt::Microsoft::Management::Deployment::PackageInstallerType::Unknown; std::wstring m_logOutputPath = L""; bool m_allowHashMismatch = false; bool m_bypassIsStoreClientBlockedPolicyCheck = false; std::wstring m_replacementInstallerArguments = L""; std::wstring m_additionalInstallerArguments = L""; std::wstring m_correlationData = L""; std::wstring m_additionalPackageCatalogArguments = L""; Windows::Foundation::Collections::IVector m_allowedArchitectures{ winrt::single_threaded_vector() }; bool m_allowUpgradeToUnknownVersion = false; bool m_force = false; bool m_acceptPackageAgreements = true; bool m_skipDependencies = false; winrt::Microsoft::Management::Deployment::AuthenticationArguments m_authenticationArguments{ nullptr }; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct InstallOptions : InstallOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/InstallResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "InstallResult.h" #include "InstallResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void InstallResult::Initialize( winrt::Microsoft::Management::Deployment::InstallResultStatus status, winrt::hresult extendedErrorCode, uint32_t installerErrorCode, hstring const& correlationData, bool rebootRequired) { m_status = status; m_extendedErrorCode = extendedErrorCode; m_installerErrorCode = installerErrorCode; m_correlationData = correlationData; m_rebootRequired = rebootRequired; } hstring InstallResult::CorrelationData() { return hstring(m_correlationData); } bool InstallResult::RebootRequired() { return m_rebootRequired; } winrt::Microsoft::Management::Deployment::InstallResultStatus InstallResult::Status() { return m_status; } winrt::hresult InstallResult::ExtendedErrorCode() { return m_extendedErrorCode; } uint32_t InstallResult::InstallerErrorCode() { return m_installerErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/InstallResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "InstallResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct InstallResult : InstallResultT { InstallResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::InstallResultStatus status, winrt::hresult extendedErrorCode, uint32_t installerErrorCode, hstring const& correlationData, bool rebootRequired); #endif hstring CorrelationData(); bool RebootRequired(); winrt::Microsoft::Management::Deployment::InstallResultStatus Status(); winrt::hresult ExtendedErrorCode(); uint32_t InstallerErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: std::wstring m_correlationData = L""; bool m_rebootRequired = false; winrt::Microsoft::Management::Deployment::InstallResultStatus m_status = winrt::Microsoft::Management::Deployment::InstallResultStatus::Ok; winrt::hresult m_extendedErrorCode = S_OK; uint32_t m_installerErrorCode = 0; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/InstalledStatus.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "InstalledStatus.h" #include "InstalledStatus.g.cpp" namespace winrt::Microsoft::Management::Deployment::implementation { void InstalledStatus::Initialize(const ::AppInstaller::Repository::InstalledStatus& installedStatus) { m_type = static_cast(installedStatus.Type); m_path = winrt::to_hstring(installedStatus.Path); m_status = installedStatus.Status; } winrt::Microsoft::Management::Deployment::InstalledStatusType InstalledStatus::Type() { return m_type; } hstring InstalledStatus::Path() { return m_path; } winrt::hresult InstalledStatus::Status() { return m_status; } } ================================================ FILE: src/Microsoft.Management.Deployment/InstalledStatus.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "InstalledStatus.g.h" #include #include namespace winrt::Microsoft::Management::Deployment::implementation { struct InstalledStatus : InstalledStatusT { InstalledStatus() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(const ::AppInstaller::Repository::InstalledStatus& installedStatus); #endif winrt::Microsoft::Management::Deployment::InstalledStatusType Type(); hstring Path(); winrt::hresult Status(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::InstalledStatusType m_type = winrt::Microsoft::Management::Deployment::InstalledStatusType::None; hstring m_path; winrt::hresult m_status = S_OK; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/MatchResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "MatchResult.h" #include "MatchResult.g.cpp" #include "CatalogPackage.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { void MatchResult::Initialize(Microsoft::Management::Deployment::CatalogPackage package, Microsoft::Management::Deployment::PackageMatchFilter matchCriteria) { m_catalogPackage = package; m_matchCriteria = matchCriteria; } winrt::Microsoft::Management::Deployment::CatalogPackage MatchResult::CatalogPackage() { return m_catalogPackage; } winrt::Microsoft::Management::Deployment::PackageMatchFilter MatchResult::MatchCriteria() { return m_matchCriteria; } } ================================================ FILE: src/Microsoft.Management.Deployment/MatchResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "MatchResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct MatchResult : MatchResultT { MatchResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(Microsoft::Management::Deployment::CatalogPackage package, Microsoft::Management::Deployment::PackageMatchFilter matchCriteria); #endif winrt::Microsoft::Management::Deployment::CatalogPackage CatalogPackage(); winrt::Microsoft::Management::Deployment::PackageMatchFilter MatchCriteria(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: Microsoft::Management::Deployment::CatalogPackage m_catalogPackage{ nullptr }; Microsoft::Management::Deployment::PackageMatchFilter m_matchCriteria{ nullptr }; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj ================================================ true true true true {1cc41a9a-ae66-459d-9210-1e572dd7be69} Microsoft.Management.Deployment Microsoft.Management.Deployment en-US 14.0 10.0 10.0.26100.0 10.0.17763.0 true -library Microsoft_Management_Deployment Debug ARM64 Debug Win32 Debug x64 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Release x64 StaticLibrary false true true false true false Spectre false true false Spectre $(VC_IncludePath);$(WindowsSDK_IncludePath); $(SolutionDir)$(PlatformTarget)\$(Configuration)\$(ProjectName)\ $(PlatformTarget)\$(Configuration)\ $(RootNamespace).Server true false ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch Level4 %(AdditionalOptions) /bigobj true true _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) $(ProjectDir)..\AppInstallerCLICore;$(ProjectDir);$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerRepositoryCore\Public;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories) Console false Microsoft_Management_Deployment.def $(OutDir)$(ProjectName).winmd AppInstallerCLICore.lib;AppInstallerCommonCore.lib;AppInstallerRepositoryCore.lib;JsonCppLib.lib;YamlCppLib.lib;wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;pure.lib%(AdditionalDependencies) _DEBUG;%(PreprocessorDefinitions) $(OutDir)$(TargetName)Debug.pdb false false false NDEBUG;%(PreprocessorDefinitions) false false false true true NDEBUG;%(PreprocessorDefinitions) MultiThreaded MultiThreaded MultiThreaded false false false true true Create This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj.filters ================================================  Public Public Public {9c3907ed-84d9-4485-9b15-04c50717f0ab} ================================================ FILE: src/Microsoft.Management.Deployment/MicrosoftEntraIdAuthenticationInfo.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "MicrosoftEntraIdAuthenticationInfo.h" #include "MicrosoftEntraIdAuthenticationInfo.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void MicrosoftEntraIdAuthenticationInfo::Initialize(::AppInstaller::Authentication::MicrosoftEntraIdAuthenticationInfo authInfo) { m_authInfo = std::move(authInfo); } hstring MicrosoftEntraIdAuthenticationInfo::Resource() { return winrt::to_hstring(m_authInfo.Resource); } hstring MicrosoftEntraIdAuthenticationInfo::Scope() { return winrt::to_hstring(m_authInfo.Scope); } } ================================================ FILE: src/Microsoft.Management.Deployment/MicrosoftEntraIdAuthenticationInfo.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "MicrosoftEntraIdAuthenticationInfo.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct MicrosoftEntraIdAuthenticationInfo : MicrosoftEntraIdAuthenticationInfoT { MicrosoftEntraIdAuthenticationInfo() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Authentication::MicrosoftEntraIdAuthenticationInfo authInfo); #endif hstring Resource(); hstring Scope(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Authentication::MicrosoftEntraIdAuthenticationInfo m_authInfo; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/Microsoft_Management_Deployment.def ================================================ EXPORTS DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE ================================================ FILE: src/Microsoft.Management.Deployment/PackageAgreement.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageAgreement.h" #include "PackageAgreement.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void PackageAgreement::Initialize(::AppInstaller::Manifest::Agreement packageAgreement) { m_packageAgreement = std::move(packageAgreement); } hstring PackageAgreement::Label() { return winrt::to_hstring(m_packageAgreement.Label); } hstring PackageAgreement::Text() { return winrt::to_hstring(m_packageAgreement.AgreementText); } hstring PackageAgreement::Url() { return winrt::to_hstring(m_packageAgreement.AgreementUrl); } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageAgreement.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageAgreement.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct PackageAgreement : PackageAgreementT { PackageAgreement() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Manifest::Agreement packageAgreement); #endif hstring Label(); hstring Text(); hstring Url(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Manifest::Agreement m_packageAgreement{}; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/PackageCatalog.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include "Workflows/WorkflowBase.h" #include "Converters.h" #include "PackageCatalog.h" #include "PackageCatalog.g.cpp" #include "PackageCatalogInfo.h" #include "FindPackagesResult.h" #include "MatchResult.h" #include "CatalogPackage.h" #include "Commands/RootCommand.h" #include "ExecutionContext.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "PackageMatchFilter.h" #pragma warning( pop ) #include "Microsoft/PredefinedInstalledSourceFactory.h" #include #include namespace winrt::Microsoft::Management::Deployment::implementation { void PackageCatalog::Initialize( winrt::Microsoft::Management::Deployment::PackageCatalogInfo info, ::AppInstaller::Repository::Source source, bool isComposite) { m_info = info; m_source = std::move(source); m_isComposite = isComposite; } bool PackageCatalog::IsComposite() { // Can't use m_source->IsComposite for this because all remote sources are turned into composite sources // behind the scenes when being opened in PackageCatalogReference.cpp so that CatalogPackage.IsInstalled works. return m_isComposite; } winrt::Microsoft::Management::Deployment::PackageCatalogInfo PackageCatalog::Info() { return m_info; } winrt::Windows::Foundation::IAsyncOperation PackageCatalog::FindPackagesAsync(winrt::Microsoft::Management::Deployment::FindPackagesOptions options) { auto strong_this = get_strong(); co_await resume_background(); co_return FindPackages(options); } HRESULT PopulateSearchRequestFromVector( ::AppInstaller::Repository::SearchRequest* searchRequest, Windows::Foundation::Collections::IVector vector, bool isSelector) { // Populates either the Filters vector of a searchRequest (if isSelector is false), // or the Inclusions and Query (if true) for (uint32_t i = 0; i < vector.Size(); ++i) { Microsoft::Management::Deployment::PackageMatchFilter filter = vector.GetAt(i); if (filter.Value().size() == 0) { // If the caller did not add a value it can't actually be used to filter or include anything so just ignore it. continue; } ::AppInstaller::Repository::MatchType packageFieldMatchOption = GetRepositoryMatchType(filter.Option()); ::AppInstaller::Repository::PackageMatchField matchField = GetRepositoryMatchField(filter.Field()); if (isSelector) { if (filter.Field() == Microsoft::Management::Deployment::PackageMatchField::CatalogDefault) { if (searchRequest->Query.has_value()) { // CatalogDefault match field can't be used twice. return E_INVALIDARG; } searchRequest->Query = ::AppInstaller::Repository::RequestMatch(packageFieldMatchOption, winrt::to_string(filter.Value())); } else { auto matchFilter = ::AppInstaller::Repository::PackageMatchFilter(matchField, packageFieldMatchOption, winrt::to_string(filter.Value())); searchRequest->Inclusions.emplace_back(matchFilter); } } else { if (filter.Field() == Microsoft::Management::Deployment::PackageMatchField::CatalogDefault) { // CatalogDefault match fields can't be used in the Filters. return E_INVALIDARG; } auto matchFilter = ::AppInstaller::Repository::PackageMatchFilter(matchField, packageFieldMatchOption, winrt::to_string(filter.Value())); searchRequest->Filters.emplace_back(matchFilter); } } return S_OK; } HRESULT PopulateSearchRequest( ::AppInstaller::Repository::SearchRequest* searchRequest, winrt::Microsoft::Management::Deployment::FindPackagesOptions const& options) { RETURN_IF_FAILED(PopulateSearchRequestFromVector(searchRequest, options.Filters(), false)); RETURN_IF_FAILED(PopulateSearchRequestFromVector(searchRequest, options.Selectors(), true)); return S_OK; } winrt::Microsoft::Management::Deployment::FindPackagesResult GetFindPackagesResult(HRESULT hr, bool isTruncated, Windows::Foundation::Collections::IVector matches) { auto findPackagesResult = winrt::make_self>(); // TODO: Add search timeout and error code. winrt::Microsoft::Management::Deployment::FindPackagesResultStatus status = FindPackagesResultStatus(hr); findPackagesResult->Initialize(status, isTruncated, matches, hr); return *findPackagesResult; } winrt::Microsoft::Management::Deployment::FindPackagesResult PackageCatalog::FindPackages(winrt::Microsoft::Management::Deployment::FindPackagesOptions const& options) { bool isTruncated = false; Windows::Foundation::Collections::IVector matches{ winrt::single_threaded_vector() }; ::AppInstaller::Repository::SearchRequest searchRequest; HRESULT hr = S_OK; try { // No need to check for caller capability again since packageQuery was required in order to get the PackageCatalog object through Connect if (FAILED(hr = PopulateSearchRequest(&searchRequest, options))) { return GetFindPackagesResult(hr, isTruncated, matches); } searchRequest.MaximumResults = options.ResultLimit(); auto searchResult = m_source.Search(searchRequest); // Handle failures by just rethrowing the first one for now. // TODO: Look into updating the COM interface to enable the single source // failures to flow out. if (!searchResult.Failures.empty()) { std::rethrow_exception(searchResult.Failures[0].Exception); } // Build the result object from the searchResult for (size_t i = 0; i < searchResult.Matches.size(); ++i) { auto match = searchResult.Matches[i]; auto catalogPackage = winrt::make_self>(); catalogPackage->Initialize(m_source, match.Package); auto packageMatchFilter = winrt::make_self>(); packageMatchFilter->Initialize(match.MatchCriteria); auto matchResult = winrt::make_self>(); matchResult->Initialize(*catalogPackage, *packageMatchFilter); matches.Append(*matchResult); } isTruncated = searchResult.Truncated; } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); return GetFindPackagesResult(hr, isTruncated, matches); } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageCatalog.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageCatalog.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct PackageCatalog : PackageCatalogT { PackageCatalog() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::PackageCatalogInfo info, ::AppInstaller::Repository::Source source, bool isComposite); #endif bool IsComposite(); winrt::Microsoft::Management::Deployment::PackageCatalogInfo Info(); winrt::Windows::Foundation::IAsyncOperation FindPackagesAsync(winrt::Microsoft::Management::Deployment::FindPackagesOptions options); winrt::Microsoft::Management::Deployment::FindPackagesResult FindPackages(winrt::Microsoft::Management::Deployment::FindPackagesOptions const& options); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::PackageCatalogInfo m_info{ nullptr }; ::AppInstaller::Repository::Source m_source; bool m_isComposite = false; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageCatalogInfo.h" #include "PackageCatalogInfo.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void PackageCatalogInfo::Initialize(const ::AppInstaller::Repository::SourceDetails& sourceDetails) { m_sourceDetails = sourceDetails; } ::AppInstaller::Repository::SourceDetails& PackageCatalogInfo::GetSourceDetails() { return m_sourceDetails; } hstring PackageCatalogInfo::Id() { return winrt::to_hstring(m_sourceDetails.Identifier); } hstring PackageCatalogInfo::Name() { return winrt::to_hstring(m_sourceDetails.Name); } hstring PackageCatalogInfo::Type() { return winrt::to_hstring(m_sourceDetails.Type); } hstring PackageCatalogInfo::Argument() { return winrt::to_hstring(m_sourceDetails.Arg); } winrt::Windows::Foundation::DateTime PackageCatalogInfo::LastUpdateTime() { return winrt::clock::from_time_t(std::chrono::system_clock::to_time_t(m_sourceDetails.LastUpdateTime)); } winrt::Microsoft::Management::Deployment::PackageCatalogOrigin PackageCatalogInfo::Origin() { switch (m_sourceDetails.Origin) { case ::AppInstaller::Repository::SourceOrigin::Default : case ::AppInstaller::Repository::SourceOrigin::Predefined: return PackageCatalogOrigin::Predefined; case ::AppInstaller::Repository::SourceOrigin::User: case ::AppInstaller::Repository::SourceOrigin::GroupPolicy: default: return PackageCatalogOrigin::User; } } winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel PackageCatalogInfo::TrustLevel() { if (WI_IsFlagSet(m_sourceDetails.TrustLevel, ::AppInstaller::Repository::SourceTrustLevel::Trusted)) { return PackageCatalogTrustLevel::Trusted; } return PackageCatalogTrustLevel::None; } bool PackageCatalogInfo::Explicit() { return m_sourceDetails.Explicit; } int32_t PackageCatalogInfo::Priority() { return m_sourceDetails.Priority; } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageCatalogInfo.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageCatalogInfo.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct PackageCatalogInfo : PackageCatalogInfoT { PackageCatalogInfo() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(const ::AppInstaller::Repository::SourceDetails& sourceDetails); ::AppInstaller::Repository::SourceDetails& GetSourceDetails(); #endif hstring Id(); hstring Name(); hstring Type(); hstring Argument(); winrt::Windows::Foundation::DateTime LastUpdateTime(); winrt::Microsoft::Management::Deployment::PackageCatalogOrigin Origin(); winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel TrustLevel(); bool Explicit(); int32_t Priority(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Repository::SourceDetails m_sourceDetails; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/PackageCatalogProgress.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageCatalogProgress.h" #include "AppInstallerStrings.h" #include "Microsoft/PredefinedInstalledSourceFactory.h" using namespace AppInstaller; using namespace AppInstaller::Repository; namespace winrt::Microsoft::Management::Deployment { namespace ProgressSinkFactory { std::shared_ptr CreatePackageCatalogProgressSink(std::string sourceType, std::function progressReporter, bool removeOperation) { if (sourceType.empty() || Utility::CaseInsensitiveEquals( Repository::Microsoft::PredefinedInstalledSourceFactory::Type(), sourceType)) { std::vector> progressWeights; // There is no download operation for remove operation, so use only percentage based progress to account for uninstall. if (removeOperation) { // it is percentage based progress. progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 1.0)); } else { // Add/Update operation has two progress types: // 1. Bytes for downloading index and // 2. Percent for index installation. progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Bytes, 0.7)); progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 0.3)); } return std::make_shared(progressWeights, progressReporter); } else { return std::make_shared(progressReporter); } } } CompletionOnlyProgressSink::CompletionOnlyProgressSink(std::function progressReporter) : m_progressReporter(progressReporter) { if (!m_progressReporter) { THROW_HR(E_INVALIDARG); } } void CompletionOnlyProgressSink::OnProgress(uint64_t /*current*/, uint64_t /*maximum*/, AppInstaller::ProgressType /*type*/) { } void CompletionOnlyProgressSink::SetProgressMessage(std::string_view /*message*/) { } void CompletionOnlyProgressSink::BeginProgress() { m_progressReporter(0); } void CompletionOnlyProgressSink::EndProgress(bool /*hideProgressWhenDone*/) { m_progressReporter(100); } PreIndexedPackageCatalogProgressSink::PreIndexedPackageCatalogProgressSink(std::vector> progressWeights, std::function progressReporter) : m_progressWeights(progressWeights), m_progressReporter(progressReporter) { if (!m_progressReporter) { THROW_HR(E_INVALIDARG); } // If no weights are provided, default to percent. if (m_progressWeights.empty()) { m_progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 1.0)); } // Calculate the total weight. double totalWeight = 0; for (const auto& weight : m_progressWeights) { if (weight.first != AppInstaller::ProgressType::None) { totalWeight += weight.second; } } // If the total weight is greater than 1, throw an exception. if (totalWeight != 1.0) { THROW_HR(E_INVALIDARG); } } void PreIndexedPackageCatalogProgressSink::OnProgress(uint64_t current, uint64_t maximum, AppInstaller::ProgressType type) { if (maximum == 0 || type == AppInstaller::ProgressType::None) { return; } double progress = static_cast(current) / maximum; m_progressValues[type] = progress; double totalProgress = 0.0; double totalWeight = 0.0; // Calculate the total progress. for (const auto& [progressType, weight] : m_progressWeights) { double progressValue = m_progressValues[progressType]; // [NOTE:] Sequential execution assumption & Handling incomplete progress reports : // This progress calculation assumes that each operation is executed sequentially, meaning the download must be complete before // the installation begins.If the download fails, the installation will not proceed.However, there may be cases where the previous // operation completes successfully, but its onprogress callback does not report 100% completion(e.g., the last progress report for // the download was at 90%, but the download is complete, and the installation has started).This can result in the total progress not // reaching 100% after the last operation completes due to the gap in the previous operation's progress report.To handle this, consider // the progress for the last operation as complete by assigning its full weight while computing progress for the following operation. // For example, while computing progress for the installation, consider the download operation complete even if it did not report progress // exactly at 100%. if (progressValue != 0) { totalProgress = totalWeight; } // Adjust the total progress value based on the weight. totalWeight += weight; totalProgress += progressValue * weight; } m_progressReporter(totalProgress * 100); } void PreIndexedPackageCatalogProgressSink::SetProgressMessage(std::string_view /*message*/) { } void PreIndexedPackageCatalogProgressSink::BeginProgress() { m_progressReporter(0); } void PreIndexedPackageCatalogProgressSink::EndProgress(bool /*hideProgressWhenDone*/) { m_progressReporter(100); } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageCatalogProgress.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "AppInstallerProgress.h" #include #include namespace winrt::Microsoft::Management::Deployment { namespace ProgressSinkFactory { /// /// Creates a progress sink for package catalog operations based on sourceType. /// /// sourceType. /// callback function that reports progress to caller. /// Default value is false. Identifies if the operation is a PackageCatalog removal and requests the ProgressSink. /// IProgressSink. std::shared_ptr CreatePackageCatalogProgressSink(std::string sourceType, std::function progressReporter, bool removeOperation = false); } /// /// Progress sink that only reports start and completion to caller. /// struct CompletionOnlyProgressSink : AppInstaller::IProgressSink { /// /// Constructor. /// /// callback that reports progress to caller. CompletionOnlyProgressSink(std::function progressReporter); void OnProgress(uint64_t current, uint64_t maximum, AppInstaller::ProgressType type) override; void SetProgressMessage(std::string_view message) override; void BeginProgress() override; void EndProgress(bool hideProgressWhenDone) override; private: std::function m_progressReporter; }; /// /// Progress sink for pre-indexed package catalog operations. /// capable of reporting progress for download and installation of index. /// Add/update operation has two progress types: Bytes for downloading index and Percent for index installation. /// Remove operation has only percentage based progress. /// struct PreIndexedPackageCatalogProgressSink : AppInstaller::IProgressSink { /// /// Constructor. /// /// ProgressType weight map. /// Callback function that reports progress to caller. PreIndexedPackageCatalogProgressSink(std::vector> progressWeights, std::function progressReporter); /// /// Reports combined progress to caller when configured for multiple progress types. /// /// The current progress value. /// The maximum progress value. /// ProgressType for which progress is applicable. void OnProgress(uint64_t current, uint64_t maximum, AppInstaller::ProgressType type) override; void SetProgressMessage(std::string_view message) override; void BeginProgress() override; void EndProgress(bool hideProgressWhenDone) override; private: std::vector> m_progressWeights; std::function m_progressReporter; std::unordered_map m_progressValues; }; } ================================================ FILE: src/Microsoft.Management.Deployment/PackageCatalogReference.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "PackageCatalogReference.h" #include "PackageCatalogReference.g.cpp" #include "PackageCatalogInfo.h" #include "PackageCatalog.h" #include "SourceAgreement.h" #include "ConnectResult.h" #include "AuthenticationInfo.h" #include "Workflows/WorkflowBase.h" #include "Converters.h" #include "Microsoft/PredefinedInstalledSourceFactory.h" #include #include #include #include #include #include #include #include #include namespace winrt::Microsoft::Management::Deployment::implementation { namespace { winrt::Microsoft::Management::Deployment::RefreshPackageCatalogResult GetRefreshPackageCatalogResult(winrt::hresult terminationStatus) { winrt::Microsoft::Management::Deployment::RefreshPackageCatalogStatus status = GetPackageCatalogOperationStatus(terminationStatus); auto updateResult = winrt::make_self>(); updateResult->Initialize(status, terminationStatus); return *updateResult; } } void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::PackageCatalogInfo packageCatalogInfo, ::AppInstaller::Repository::Source sourceReference) { m_info = packageCatalogInfo; m_sourceReference = std::move(sourceReference); m_packageCatalogBackgroundUpdateInterval = ::AppInstaller::Settings::User().Get<::AppInstaller::Settings::Setting::AutoUpdateTimeInMinutes>(); if (IsBackgroundProcessForPolicy()) { // Delay the default update interval for these background processes static constexpr winrt::Windows::Foundation::TimeSpan s_PackageCatalogUpdateIntervalDelay_Base = 168h; //1 week // Add a bit of randomness to the default interval time std::default_random_engine randomEngine(std::random_device{}()); std::uniform_int_distribution distribution(0, 604800); m_packageCatalogBackgroundUpdateInterval = s_PackageCatalogUpdateIntervalDelay_Base + std::chrono::seconds(distribution(randomEngine)); // Prevent any update / data processing by default for these background processes for now m_installedPackageInformationOnly = m_sourceReference.IsWellKnownSource(AppInstaller::Repository::WellKnownSource::WinGet) || m_sourceReference.IsWellKnownSource(AppInstaller::Repository::WellKnownSource::WinGetFont); } } void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions options) { m_compositePackageCatalogOptions = options; } bool PackageCatalogReference::IsComposite() { return (m_compositePackageCatalogOptions != nullptr); } winrt::Microsoft::Management::Deployment::PackageCatalogInfo PackageCatalogReference::Info() { return m_info; } winrt::Windows::Foundation::IAsyncOperation PackageCatalogReference::ConnectAsync() { co_return Connect(); } winrt::Microsoft::Management::Deployment::ConnectResult GetConnectCatalogErrorResult(hresult hr) { auto connectResult = winrt::make_self>(); connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::CatalogError, nullptr, hr); return *connectResult; } winrt::Microsoft::Management::Deployment::ConnectResult GetConnectSourceAgreementsNotAcceptedErrorResult() { auto connectResult = winrt::make_self>(); connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::SourceAgreementsNotAccepted, nullptr, APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED); return *connectResult; } winrt::Microsoft::Management::Deployment::ConnectResult PackageCatalogReference::Connect() try { HRESULT hr = EnsureComCallerHasCapability(Capability::PackageQuery); if (FAILED(hr)) { // TODO: When more error codes are added, this should go back as something other than CatalogError. return GetConnectCatalogErrorResult(hr); } std::string callerName = GetCallerName(); ::AppInstaller::ProgressCallback progress; ::AppInstaller::Repository::Source source; if (m_compositePackageCatalogOptions) { std::vector<::AppInstaller::Repository::Source> remoteSources; for (uint32_t i = 0; i < m_compositePackageCatalogOptions.Catalogs().Size(); ++i) { auto catalog = m_compositePackageCatalogOptions.Catalogs().GetAt(i); if (!catalog.AcceptSourceAgreements() && catalog.SourceAgreements().Size() != 0) { return GetConnectSourceAgreementsNotAcceptedErrorResult(); } winrt::Microsoft::Management::Deployment::implementation::PackageCatalogReference* catalogImpl = get_self(catalog); auto copy = catalogImpl->m_sourceReference; copy.SetCaller(callerName); copy.SetBackgroundUpdateInterval(catalog.PackageCatalogBackgroundUpdateInterval()); copy.InstalledPackageInformationOnly(catalog.InstalledPackageInformationOnly()); if (catalog.AuthenticationInfo().AuthenticationType() != winrt::Microsoft::Management::Deployment::AuthenticationType::None) { copy.SetAuthenticationArguments(GetAuthenticationArguments(catalog.AuthenticationArguments())); } copy.Open(progress); remoteSources.emplace_back(std::move(copy)); } // Create the aggregated source. source = ::AppInstaller::Repository::Source{ remoteSources }; // Create composite with installed source if needed. ::AppInstaller::Repository::CompositeSearchBehavior searchBehavior = GetRepositoryCompositeSearchBehavior(m_compositePackageCatalogOptions.CompositeSearchBehavior()); // Check if search behavior indicates that the caller does not want to do local correlation. if (m_compositePackageCatalogOptions.CompositeSearchBehavior() != Microsoft::Management::Deployment::CompositeSearchBehavior::RemotePackagesFromRemoteCatalogs) { ::AppInstaller::Repository::Source installedSource; auto manifestInstalledScope = GetManifestScope(m_compositePackageCatalogOptions.InstalledScope()).first; if (manifestInstalledScope == ::AppInstaller::Manifest::ScopeEnum::User) { installedSource = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::PredefinedSource::InstalledUser }; } else if (manifestInstalledScope == ::AppInstaller::Manifest::ScopeEnum::Machine) { installedSource = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::PredefinedSource::InstalledMachine }; } else { installedSource = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::PredefinedSource::Installed }; } installedSource.Open(progress); source = ::AppInstaller::Repository::Source{ installedSource, source, searchBehavior }; } } else { if (!AcceptSourceAgreements() && SourceAgreements().Size() != 0) { return GetConnectSourceAgreementsNotAcceptedErrorResult(); } source = m_sourceReference; source.SetCaller(callerName); source.SetBackgroundUpdateInterval(PackageCatalogBackgroundUpdateInterval()); source.InstalledPackageInformationOnly(m_installedPackageInformationOnly); if (AuthenticationInfo().AuthenticationType() != winrt::Microsoft::Management::Deployment::AuthenticationType::None) { source.SetAuthenticationArguments(GetAuthenticationArguments(m_authenticationArguments)); } source.Open(progress); } if (!source) { // We call `Open` on each individual source above, meaning that they should throw any error that occurs. // If the source is still not open at this point it is a bug. return GetConnectCatalogErrorResult(E_UNEXPECTED); } // Have to make another package catalog info because source->GetDetails has more fields than m_info does. // Specifically, Rest sources do not have the Ids filled in m_info since they only get the id from the rest server after being Opened. auto packageCatalogInfo = winrt::make_self>(); packageCatalogInfo->Initialize(source.GetDetails()); auto connectResult = winrt::make_self>(); auto packageCatalog = winrt::make_self>(); packageCatalog->Initialize(*packageCatalogInfo, source, (m_compositePackageCatalogOptions != nullptr)); connectResult->Initialize(winrt::Microsoft::Management::Deployment::ConnectResultStatus::Ok, *packageCatalog, S_OK); return *connectResult; } catch (...) { return GetConnectCatalogErrorResult(AppInstaller::CLI::Workflow::HandleException(nullptr, std::current_exception())); } winrt::Windows::Foundation::Collections::IVectorView PackageCatalogReference::SourceAgreements() { std::call_once(m_sourceAgreementsOnceFlag, [&]() { if (!IsComposite()) { for (auto const& agreement : m_sourceReference.GetInformation().SourceAgreements) { auto sourceAgreement = winrt::make_self>(); sourceAgreement->Initialize(agreement); m_sourceAgreements.Append(*sourceAgreement); } } }); return m_sourceAgreements.GetView(); } hstring PackageCatalogReference::AdditionalPackageCatalogArguments() { if (!IsComposite()) { if (m_additionalPackageCatalogArguments.has_value()) { return winrt::to_hstring(m_additionalPackageCatalogArguments.value()); } } return {}; } void PackageCatalogReference::AdditionalPackageCatalogArguments(hstring const& value) { if (IsComposite()) { // Can't set AdditionalPackageCatalogArguments on a composite. Callers should set it on each non-composite PackageCatalogReference in the composite. throw winrt::hresult_illegal_state_change(); } else { m_additionalPackageCatalogArguments = ::AppInstaller::Utility::ConvertToUTF8(value); m_sourceReference.SetCustomHeader(m_additionalPackageCatalogArguments); } } void PackageCatalogReference::AcceptSourceAgreements(bool value) { if (IsComposite()) { // Can't set AcceptSourceAgreements on a composite. Callers should set it on each non-composite PackageCatalogReference in the composite. throw winrt::hresult_illegal_state_change(); } m_acceptSourceAgreements = value; } bool PackageCatalogReference::AcceptSourceAgreements() { return m_acceptSourceAgreements; } void PackageCatalogReference::PackageCatalogBackgroundUpdateInterval(winrt::Windows::Foundation::TimeSpan const& value) { if (IsComposite()) { // Can't set PackageCatalogBackgroundUpdateInterval on a composite. Callers should set it on each non-composite PackageCatalogReference in the composite. throw winrt::hresult_illegal_state_change(); } m_packageCatalogBackgroundUpdateInterval = value; } winrt::Windows::Foundation::TimeSpan PackageCatalogReference::PackageCatalogBackgroundUpdateInterval() { return m_packageCatalogBackgroundUpdateInterval; } bool PackageCatalogReference::InstalledPackageInformationOnly() { return m_installedPackageInformationOnly; } void PackageCatalogReference::InstalledPackageInformationOnly(bool value) { if (IsComposite()) { throw winrt::hresult_illegal_state_change(); } m_installedPackageInformationOnly = value; } winrt::Microsoft::Management::Deployment::AuthenticationArguments PackageCatalogReference::AuthenticationArguments() { return m_authenticationArguments; } void PackageCatalogReference::AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value) { if (IsComposite()) { throw winrt::hresult_illegal_state_change(); } m_authenticationArguments = value; } winrt::Microsoft::Management::Deployment::AuthenticationInfo PackageCatalogReference::AuthenticationInfo() { std::call_once(m_authenticationInfoOnceFlag, [&]() { if (!IsComposite()) { auto authenticationInfo = winrt::make_self>(); authenticationInfo->Initialize(m_sourceReference.GetInformation().Authentication); m_authenticationInfo = *authenticationInfo; } }); return m_authenticationInfo; } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageCatalogReference::RefreshPackageCatalogAsync() { HRESULT terminationHR = S_OK; try { // Check for permissions and get caller info for telemetry THROW_IF_FAILED(EnsureComCallerHasCapability(Capability::PackageQuery)); auto strong_this = get_strong(); auto report_progress{ co_await winrt::get_progress_token() }; co_await winrt::resume_background(); auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink(this->m_sourceReference.GetDetails().Type, report_progress); packageCatalogProgressSink->BeginProgress(); ::AppInstaller::ProgressCallback progress(packageCatalogProgressSink.get()); this->m_sourceReference.Update(progress); packageCatalogProgressSink->EndProgress(false); } catch (...) { terminationHR = AppInstaller::CLI::Workflow::HandleException(nullptr, std::current_exception()); } co_return GetRefreshPackageCatalogResult(terminationHR); } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageCatalogReference.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageCatalogReference.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct PackageCatalogReference : PackageCatalogReferenceT { PackageCatalogReference() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(Deployment::PackageCatalogInfo packageCatalogInfo, ::AppInstaller::Repository::Source sourceReference); void Initialize(Deployment::CreateCompositePackageCatalogOptions options); #endif bool IsComposite(); winrt::Microsoft::Management::Deployment::PackageCatalogInfo Info(); winrt::Windows::Foundation::IAsyncOperation ConnectAsync(); winrt::Microsoft::Management::Deployment::ConnectResult Connect(); winrt::Windows::Foundation::Collections::IVectorView SourceAgreements(); hstring AdditionalPackageCatalogArguments(); void AdditionalPackageCatalogArguments(hstring const& value); // Contract 6 bool AcceptSourceAgreements(); void AcceptSourceAgreements(bool value); // Contract 8.0 winrt::Windows::Foundation::TimeSpan PackageCatalogBackgroundUpdateInterval(); void PackageCatalogBackgroundUpdateInterval(winrt::Windows::Foundation::TimeSpan const& value); bool InstalledPackageInformationOnly(); void InstalledPackageInformationOnly(bool value); winrt::Microsoft::Management::Deployment::AuthenticationArguments AuthenticationArguments(); void AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value); winrt::Microsoft::Management::Deployment::AuthenticationInfo AuthenticationInfo(); // Contract 12.0 winrt::Windows::Foundation::IAsyncOperationWithProgress RefreshPackageCatalogAsync(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions m_compositePackageCatalogOptions{ nullptr }; winrt::Microsoft::Management::Deployment::PackageCatalogInfo m_info{ nullptr }; std::once_flag m_sourceAgreementsOnceFlag; winrt::Windows::Foundation::Collections::IVector m_sourceAgreements{ winrt::single_threaded_vector() }; ::AppInstaller::Repository::Source m_sourceReference; std::optional m_additionalPackageCatalogArguments; bool m_acceptSourceAgreements = true; bool m_installedPackageInformationOnly = false; winrt::Windows::Foundation::TimeSpan m_packageCatalogBackgroundUpdateInterval = winrt::Windows::Foundation::TimeSpan::zero(); winrt::Microsoft::Management::Deployment::AuthenticationArguments m_authenticationArguments{ nullptr }; std::once_flag m_authenticationInfoOnceFlag; winrt::Microsoft::Management::Deployment::AuthenticationInfo m_authenticationInfo{ nullptr }; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageInstallerInfo.h" #include "PackageInstallerInfo.g.cpp" #include "AuthenticationInfo.h" #include "Converters.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { void PackageInstallerInfo::Initialize(const ::AppInstaller::Manifest::ManifestInstaller& manifestInstaller) { m_manifestInstaller = manifestInstaller; } winrt::Microsoft::Management::Deployment::PackageInstallerType PackageInstallerInfo::InstallerType() { return GetDeploymentInstallerType(m_manifestInstaller.BaseInstallerType); } winrt::Microsoft::Management::Deployment::PackageInstallerType PackageInstallerInfo::NestedInstallerType() { return GetDeploymentInstallerType(m_manifestInstaller.NestedInstallerType); } winrt::Windows::System::ProcessorArchitecture PackageInstallerInfo::Architecture() { auto convertedArchitecture = GetWindowsSystemProcessorArchitecture(m_manifestInstaller.Arch); return convertedArchitecture ? convertedArchitecture.value() : Windows::System::ProcessorArchitecture::Unknown; } winrt::Microsoft::Management::Deployment::PackageInstallerScope PackageInstallerInfo::Scope() { return GetDeploymentInstallerScope(m_manifestInstaller.Scope); } hstring PackageInstallerInfo::Locale() { return winrt::to_hstring(m_manifestInstaller.Locale); } winrt::Microsoft::Management::Deployment::ElevationRequirement PackageInstallerInfo::ElevationRequirement() { return GetDeploymentElevationRequirement(m_manifestInstaller.ElevationRequirement); } winrt::Microsoft::Management::Deployment::AuthenticationInfo PackageInstallerInfo::AuthenticationInfo() { std::call_once(m_authenticationInfoOnceFlag, [&]() { auto authenticationInfo = winrt::make_self>(); authenticationInfo->Initialize(m_manifestInstaller.AuthInfo); m_authenticationInfo = *authenticationInfo; }); return m_authenticationInfo; } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageInstallerInfo.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageInstallerInfo.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct PackageInstallerInfo : PackageInstallerInfoT { PackageInstallerInfo() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(const ::AppInstaller::Manifest::ManifestInstaller& manifestInstaller); #endif winrt::Microsoft::Management::Deployment::PackageInstallerType InstallerType(); winrt::Microsoft::Management::Deployment::PackageInstallerType NestedInstallerType(); winrt::Windows::System::ProcessorArchitecture Architecture(); winrt::Microsoft::Management::Deployment::PackageInstallerScope Scope(); hstring Locale(); // Contract 6.0 winrt::Microsoft::Management::Deployment::ElevationRequirement ElevationRequirement(); // Contract 12.0 winrt::Microsoft::Management::Deployment::AuthenticationInfo AuthenticationInfo(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Manifest::ManifestInstaller m_manifestInstaller; std::once_flag m_authenticationInfoOnceFlag; winrt::Microsoft::Management::Deployment::AuthenticationInfo m_authenticationInfo{ nullptr }; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "PackageInstallerInstalledStatus.h" #include "PackageInstallerInstalledStatus.g.cpp" #include "InstalledStatus.h" #include "PackageInstallerInfo.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { void PackageInstallerInstalledStatus::Initialize(const ::AppInstaller::Repository::InstallerInstalledStatus& installerInstalledStatus) { // Initialize m_installerInfo auto installerInfo = winrt::make_self>(); installerInfo->Initialize(installerInstalledStatus.Installer); m_installerInfo = *installerInfo; // Initialize m_installedStatus for (auto const& entry : installerInstalledStatus.Status) { auto status = winrt::make_self>(); status->Initialize(entry); m_installedStatus.Append(*status); } } winrt::Microsoft::Management::Deployment::PackageInstallerInfo PackageInstallerInstalledStatus::InstallerInfo() { return m_installerInfo; } winrt::Windows::Foundation::Collections::IVectorView PackageInstallerInstalledStatus::InstallerInstalledStatus() { return m_installedStatus.GetView(); } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageInstallerInstalledStatus.g.h" #include #include #include namespace winrt::Microsoft::Management::Deployment::implementation { struct PackageInstallerInstalledStatus : PackageInstallerInstalledStatusT { PackageInstallerInstalledStatus() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(const ::AppInstaller::Repository::InstallerInstalledStatus& installerInstalledStatus); #endif winrt::Microsoft::Management::Deployment::PackageInstallerInfo InstallerInfo(); winrt::Windows::Foundation::Collections::IVectorView InstallerInstalledStatus(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::PackageInstallerInfo m_installerInfo{ nullptr }; winrt::Windows::Foundation::Collections::IVector m_installedStatus{ winrt::single_threaded_vector() }; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/PackageManager.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerCLICore.h" #include "Microsoft/PredefinedInstalledSourceFactory.h" #include "Commands/RootCommand.h" #include "ComContext.h" #include "ExecutionContext.h" #include "Workflows/WorkflowBase.h" #include #include #include #include "Commands/COMCommand.h" #include #include #include #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "PackageManager.h" #pragma warning( pop ) #include "PackageManager.g.cpp" #include "CatalogPackage.h" #include "DownloadResult.h" #include "InstallResult.h" #include "UninstallResult.h" #include "RepairResult.h" #include "PackageCatalogInfo.h" #include "PackageCatalogReference.h" #include "PackageVersionInfo.h" #include "PackageVersionId.h" #include "AddPackageCatalogResult.h" #include "RemovePackageCatalogResult.h" #include "EditPackageCatalogResult.h" #include "Converters.h" #include "Helpers.h" #include "ContextOrchestrator.h" #include "AppInstallerRuntime.h" #include #include using namespace std::literals::chrono_literals; using namespace ::AppInstaller::CLI; using namespace ::AppInstaller::CLI::Execution; namespace winrt::Microsoft::Management::Deployment::implementation { namespace { void LogStartupIfApplicable() { static std::once_flag logStartupOnceFlag; std::call_once(logStartupOnceFlag, [&]() { ::AppInstaller::Logging::Telemetry().SetCaller(GetCallerName()); ::AppInstaller::Logging::Telemetry().LogStartup(true); }); } winrt::Microsoft::Management::Deployment::AddPackageCatalogResult GetAddPackageCatalogResult(winrt::hresult terminationStatus) { winrt::Microsoft::Management::Deployment::AddPackageCatalogStatus status = GetPackageCatalogOperationStatus(terminationStatus); auto addPackageCatalogResult = winrt::make_self>(); addPackageCatalogResult->Initialize(status, terminationStatus); return *addPackageCatalogResult; } void CheckForDuplicateSource(const std::string& name, const std::string& type, const std::string& sourceUri) { auto sourceList = ::AppInstaller::Repository::Source::GetCurrentSources(); std::string sourceType = type; // [NOTE:] If the source type is not specified, the default source type will be used for validation.In cases where the source type is empty, // it remains unassigned until the add operation, at which point it is assigned.Without this default assignment, an empty string could be // compared to the default type, potentially allowing different source names with the same URI to be seen as unique. // To avoid this, assign the default source type prior to comparison. if (sourceType.empty()) { // This method of obtaining the default source type is slightly expensive as it requires creating a SourceFactory object // and fetching the type name.Nonetheless, it future-proofs the code against any changes in the SourceFactory's default type. sourceType = ::AppInstaller::Repository::Source::GetDefaultSourceType(); } for (const auto& source : sourceList) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_ALREADY_EXISTS, ::AppInstaller::Utility::ICUCaseInsensitiveEquals(source.Name, name)); bool sourceUriAlreadyExists = !source.Arg.empty() && source.Arg == sourceUri && source.Type == sourceType; THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_ARG_ALREADY_EXISTS, sourceUriAlreadyExists); } } ::AppInstaller::Repository::Source CreateSourceFromOptions(const winrt::Microsoft::Management::Deployment::AddPackageCatalogOptions& options) { std::string name = winrt::to_string(options.Name()); std::string type = winrt::to_string(options.Type()); std::string sourceUri = winrt::to_string(options.SourceUri()); AppInstaller::Repository::SourceTrustLevel trustLevel = AppInstaller::Repository::SourceTrustLevel::None; if (options.TrustLevel() == winrt::Microsoft::Management::Deployment::PackageCatalogTrustLevel::Trusted) { trustLevel = AppInstaller::Repository::SourceTrustLevel::Trusted; } CheckForDuplicateSource(name, type, sourceUri); ::AppInstaller::Repository::SourceEdit additionalProperties; additionalProperties.Explicit = options.Explicit(); additionalProperties.Priority = options.Priority(); ::AppInstaller::Repository::Source source = ::AppInstaller::Repository::Source{ name, sourceUri, type, trustLevel, additionalProperties }; std::string customHeader = winrt::to_string(options.CustomHeader()); if (!customHeader.empty()) { source.SetCustomHeader(customHeader); } auto sourceInfo = source.GetInformation(); if (sourceInfo.Authentication.Type == ::AppInstaller::Authentication::AuthenticationType::Unknown) { THROW_HR(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED); } return source; } winrt::Microsoft::Management::Deployment::RemovePackageCatalogResult GetRemovePackageCatalogResult(winrt::hresult terminationStatus) { winrt::Microsoft::Management::Deployment::RemovePackageCatalogStatus status = GetPackageCatalogOperationStatus(terminationStatus); auto removeResult = winrt::make_self>(); removeResult->Initialize(status, terminationStatus); return *removeResult; } std::optional<::AppInstaller::Repository::SourceDetails> GetMatchingSource(const std::string& name) { auto sourceList = ::AppInstaller::Repository::Source::GetCurrentSources(); for (const auto& source : sourceList) { if (::AppInstaller::Utility::ICUCaseInsensitiveEquals(source.Name, name)) { return source; // Return the first matching source } } return std::nullopt; // Return std::nullopt if no matching source is found } } PackageManager::PackageManager() { Execution::ContextOrchestrator::RegisterForShutdownSynchronization(); } winrt::Windows::Foundation::Collections::IVectorView PackageManager::GetPackageCatalogs() { LogStartupIfApplicable(); Windows::Foundation::Collections::IVector catalogs{ winrt::single_threaded_vector() }; std::vector<::AppInstaller::Repository::SourceDetails> sources = ::AppInstaller::Repository::Source::GetCurrentSources(); for (uint32_t i = 0; i < sources.size(); i++) { auto packageCatalogInfo = winrt::make_self>(); ::AppInstaller::Repository::Source sourceReference{ sources.at(i).Name }; packageCatalogInfo->Initialize(sourceReference.GetDetails()); auto packageCatalogRef = winrt::make_self>(); packageCatalogRef->Initialize(*packageCatalogInfo, sourceReference); catalogs.Append(*packageCatalogRef); } return catalogs.GetView(); } winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::GetPredefinedPackageCatalog(winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog const& predefinedPackageCatalog) { LogStartupIfApplicable(); ::AppInstaller::Repository::Source source; switch (predefinedPackageCatalog) { case winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog::OpenWindowsCatalog: source = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::WellKnownSource::WinGet }; break; case winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog::MicrosoftStore: source = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::WellKnownSource::MicrosoftStore }; break; case winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog::DesktopFrameworks: source = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::WellKnownSource::DesktopFrameworks }; break; case winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog::OpenWindowsCatalogFont: source = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::WellKnownSource::WinGetFont }; break; default: throw hresult_invalid_argument(); } auto packageCatalogInfo = winrt::make_self>(); packageCatalogInfo->Initialize(source.GetDetails()); auto packageCatalogRef = winrt::make_self>(); packageCatalogRef->Initialize(*packageCatalogInfo, source); return *packageCatalogRef; } winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::GetLocalPackageCatalog(winrt::Microsoft::Management::Deployment::LocalPackageCatalog const& localPackageCatalog) { LogStartupIfApplicable(); ::AppInstaller::Repository::Source source; switch (localPackageCatalog) { case winrt::Microsoft::Management::Deployment::LocalPackageCatalog::InstalledPackages: source = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::PredefinedSource::Installed }; break; case winrt::Microsoft::Management::Deployment::LocalPackageCatalog::InstallingPackages: source = ::AppInstaller::Repository::Source{ ::AppInstaller::Repository::PredefinedSource::Installing }; break; default: throw hresult_invalid_argument(); } auto packageCatalogInfo = winrt::make_self>(); packageCatalogInfo->Initialize(source.GetDetails()); auto packageCatalogRef = winrt::make_self>(); packageCatalogRef->Initialize(*packageCatalogInfo, source); return *packageCatalogRef; } winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::GetPackageCatalogByName(hstring const& catalogName) { LogStartupIfApplicable(); std::string name = winrt::to_string(catalogName); if (name.empty()) { return nullptr; } ::AppInstaller::Repository::Source source{ name }; // Create the catalog object if the source is found, otherwise return null. Don't throw. if (source) { auto packageCatalogInfo = winrt::make_self>(); packageCatalogInfo->Initialize(source.GetDetails()); auto packageCatalogRef = winrt::make_self>(); packageCatalogRef->Initialize(*packageCatalogInfo, source); return *packageCatalogRef; } else { return nullptr; } } void AddPackageManifestToContext(winrt::Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo, ::AppInstaller::CLI::Execution::Context* context) { winrt::Microsoft::Management::Deployment::implementation::PackageVersionInfo* packageVersionInfoImpl = get_self(packageVersionInfo); std::shared_ptr<::AppInstaller::Repository::IPackageVersion> internalPackageVersion = packageVersionInfoImpl->GetRepositoryPackageVersion(); ::AppInstaller::Manifest::Manifest manifest = internalPackageVersion->GetManifest(); std::string targetLocale; if (context->Args.Contains(::AppInstaller::CLI::Execution::Args::Type::Locale)) { targetLocale = context->Args.GetArg(::AppInstaller::CLI::Execution::Args::Type::Locale); } manifest.ApplyLocale(targetLocale); context->GetThreadGlobals().GetTelemetryLogger().LogManifestFields(manifest.Id, manifest.DefaultLocalization.Get<::AppInstaller::Manifest::Localization::PackageName>(), manifest.Version); context->Add<::AppInstaller::CLI::Execution::Data::Manifest>(std::move(manifest)); context->Add<::AppInstaller::CLI::Execution::Data::PackageVersion>(std::move(internalPackageVersion)); } void AddInstalledVersionToContext(winrt::Microsoft::Management::Deployment::PackageVersionInfo installedVersionInfo, ::AppInstaller::CLI::Execution::Context* context) { winrt::Microsoft::Management::Deployment::implementation::PackageVersionInfo* installedVersionInfoImpl = get_self(installedVersionInfo); std::shared_ptr<::AppInstaller::Repository::IPackageVersion> internalInstalledVersion = installedVersionInfoImpl->GetRepositoryPackageVersion(); context->Add(internalInstalledVersion); } winrt::Microsoft::Management::Deployment::PackageCatalogReference PackageManager::CreateCompositePackageCatalog(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions const& options) { LogStartupIfApplicable(); if (!options) { // Can't make a composite source if the options aren't specified. throw hresult_invalid_argument(); } for (uint32_t i = 0; i < options.Catalogs().Size(); ++i) { auto catalog = options.Catalogs().GetAt(i); if (catalog.IsComposite()) { // Can't make a composite source out of a source that's already a composite. throw hresult_invalid_argument(); } } auto packageCatalogImpl = winrt::make_self>(); packageCatalogImpl->Initialize(options); return *packageCatalogImpl; } winrt::Microsoft::Management::Deployment::InstallResult GetInstallResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, uint32_t installerError, winrt::hstring correlationData, bool rebootRequired) { winrt::Microsoft::Management::Deployment::InstallResultStatus installResultStatus = GetOperationResultStatus(executionStage, terminationHR); auto installResult = winrt::make_self>(); installResult->Initialize(installResultStatus, terminationHR, installerError, correlationData, rebootRequired); return *installResult; } winrt::Microsoft::Management::Deployment::UninstallResult GetUninstallResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, uint32_t uninstallerError, winrt::hstring correlationData, bool rebootRequired) { winrt::Microsoft::Management::Deployment::UninstallResultStatus uninstallResultStatus = GetOperationResultStatus(executionStage, terminationHR); auto uninstallResult = winrt::make_self>(); uninstallResult->Initialize(uninstallResultStatus, terminationHR, uninstallerError, correlationData, rebootRequired); return *uninstallResult; } winrt::Microsoft::Management::Deployment::DownloadResult GetDownloadResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, winrt::hstring correlationData) { winrt::Microsoft::Management::Deployment::DownloadResultStatus downloadResultStatus = GetOperationResultStatus(executionStage, terminationHR); auto downloadResult = winrt::make_self>(); downloadResult->Initialize(downloadResultStatus, terminationHR, correlationData); return *downloadResult; } winrt::Microsoft::Management::Deployment::RepairResult GetRepairResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, uint32_t repairError, winrt::hstring correlationData, bool rebootRequired) { winrt::Microsoft::Management::Deployment::RepairResultStatus repairResultStatus = GetOperationResultStatus(executionStage, terminationHR); auto repairResult = winrt::make_self>(); repairResult->Initialize(repairResultStatus, terminationHR, repairError, correlationData, rebootRequired); return *repairResult; } template TResult GetOperationResult(::Workflow::ExecutionStage executionStage, winrt::hresult terminationHR, uint32_t operationError, winrt::hstring correlationData, bool rebootRequired) { if constexpr (std::is_same_v) { return GetInstallResult(executionStage, terminationHR, operationError, correlationData, rebootRequired); } else if constexpr (std::is_same_v) { return GetUninstallResult(executionStage, terminationHR, operationError, correlationData, rebootRequired); } else if constexpr (std::is_same_v) { return GetDownloadResult(executionStage, terminationHR, correlationData); } else if constexpr (std::is_same_v) { return GetRepairResult(executionStage, terminationHR, operationError, correlationData, rebootRequired); } } #define WINGET_GET_PROGRESS_STATE(_installState_, _uninstallState_, _repairState_) \ if constexpr (std::is_same_v) \ { \ progressState = TState::_installState_; \ } \ else if constexpr (std::is_same_v) \ { \ progressState = TState::_uninstallState_; \ } \ else if constexpr (std::is_same_v) \ { \ progressState = TState::_repairState_; \ } \ template std::optional GetProgress( ReportType reportType, uint64_t current, uint64_t maximum, ::AppInstaller::ProgressType progressType, ::Workflow::ExecutionStage executionPhase) { bool reportProgress = false; TState progressState = TState::Queued; double downloadProgress = 0; double operationProgress = 0; uint64_t downloadBytesDownloaded = 0; uint64_t downloadBytesRequired = 0; switch (executionPhase) { case ::Workflow::ExecutionStage::Initial: case ::Workflow::ExecutionStage::ParseArgs: case ::Workflow::ExecutionStage::Discovery: // We already reported queued progress up front. break; case ::Workflow::ExecutionStage::Download: if constexpr (std::is_same_v || std::is_same_v) { progressState = TState::Downloading; if (reportType == ReportType::BeginProgress) { reportProgress = true; } else if (progressType == ::AppInstaller::ProgressType::Bytes) { downloadBytesDownloaded = current; downloadBytesRequired = maximum; if (maximum > 0 && maximum >= current) { reportProgress = true; downloadProgress = static_cast(current) / static_cast(maximum); } } } break; case ::Workflow::ExecutionStage::PreExecution: // Wait until installer starts to report operation. break; case ::Workflow::ExecutionStage::Execution: WINGET_GET_PROGRESS_STATE(Installing, Uninstalling, Repairing); downloadProgress = 1; if (reportType == ReportType::ExecutionPhaseUpdate) { // Operation is starting. Send progress so callers know the AsyncOperation can't be cancelled. reportProgress = true; } else if (reportType == ReportType::EndProgress) { // Operation is "finished". May not have succeeded. reportProgress = true; operationProgress = 1; } else if (progressType == ::AppInstaller::ProgressType::Percent) { if (maximum > 0 && maximum >= current) { // Operation is progressing reportProgress = true; operationProgress = static_cast(current) / static_cast(maximum); } } break; case ::Workflow::ExecutionStage::PostExecution: if (reportType == ReportType::ExecutionPhaseUpdate) { // Send PostInstall progress when it switches to PostExecution phase. reportProgress = true; WINGET_GET_PROGRESS_STATE(PostInstall, PostUninstall, PostRepair); downloadProgress = 1; operationProgress = 1; } break; } if (reportProgress) { if constexpr (std::is_same_v) { TProgress progress{ progressState, downloadBytesDownloaded, downloadBytesRequired, downloadProgress, operationProgress }; return progress; } else if constexpr (std::is_same_v) { TProgress progress{ progressState, operationProgress }; return progress; } else if constexpr (std::is_same_v) { TProgress progress{ progressState, downloadBytesDownloaded, downloadBytesRequired, downloadProgress }; return progress; } else if constexpr (std::is_same_v) { TProgress progress{ progressState, operationProgress }; return progress; } } else { return {}; } } template Microsoft::Management::Deployment::PackageVersionInfo GetPackageVersionInfo(winrt::Microsoft::Management::Deployment::CatalogPackage package, TOptions options) { Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo{ nullptr }; winrt::Microsoft::Management::Deployment::PackageVersionId versionId = (options) ? options.PackageVersionId() : nullptr; // If the version of the package is specified use that, otherwise use the default. if (versionId) { packageVersionInfo = package.GetPackageVersionInfo(versionId); } else { if constexpr (std::is_same_v) { packageVersionInfo = package.DefaultInstallVersion(); } else if constexpr (std::is_same_v) { // For download, applicability check is not needed. Just use latest. if (package.AvailableVersions().Size() > 0) { packageVersionInfo = package.GetPackageVersionInfo(package.AvailableVersions().GetAt(0)); } } } // If the specified version wasn't found then return a failure. This is unusual, since all packages that came from a non-local catalog have a default version, // and the versionId is strongly typed and comes from the CatalogPackage.GetAvailableVersions. // If version is not specified, DefaultInstallVersion may be empty due to applicability check. THROW_HR_IF(versionId ? APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND : APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER, !packageVersionInfo); return packageVersionInfo; } void PopulateContextFromInstallOptions( ::AppInstaller::CLI::Execution::Context* context, winrt::Microsoft::Management::Deployment::InstallOptions options) { if (options) { if (!options.LogOutputPath().empty()) { context->Args.AddArg(Execution::Args::Type::Log, ::AppInstaller::Utility::ConvertToUTF8(options.LogOutputPath())); context->Args.AddArg(Execution::Args::Type::VerboseLogs); } if (options.AllowHashMismatch()) { context->Args.AddArg(Execution::Args::Type::HashOverride); } if (options.BypassIsStoreClientBlockedPolicyCheck()) { context->SetFlags(Execution::ContextFlag::BypassIsStoreClientBlockedPolicyCheck); } if (options.Force()) { context->Args.AddArg(Execution::Args::Type::Force); } // If the PackageInstallScope is anything other than ::Any then set it as a requirement. auto manifestScope = GetManifestScope(options.PackageInstallScope()); if (manifestScope.first != ::AppInstaller::Manifest::ScopeEnum::Unknown) { context->Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(manifestScope.first)); context->Add(manifestScope.second); } if (options.PackageInstallMode() == PackageInstallMode::Interactive) { context->Args.AddArg(Execution::Args::Type::Interactive); } else if (options.PackageInstallMode() == PackageInstallMode::Silent) { context->Args.AddArg(Execution::Args::Type::Silent); } auto installerType = GetManifestInstallerType(options.InstallerType()); if (installerType != AppInstaller::Manifest::InstallerTypeEnum::Unknown) { context->Args.AddArg(Execution::Args::Type::InstallerType, AppInstaller::Manifest::InstallerTypeToString(installerType)); } if (!options.PreferredInstallLocation().empty()) { context->Args.AddArg(Execution::Args::Type::InstallLocation, ::AppInstaller::Utility::ConvertToUTF8(options.PreferredInstallLocation())); } if (!options.ReplacementInstallerArguments().empty()) { context->Args.AddArg(Execution::Args::Type::Override, ::AppInstaller::Utility::ConvertToUTF8(options.ReplacementInstallerArguments())); } if (!options.AdditionalInstallerArguments().empty()) { context->Args.AddArg(Execution::Args::Type::CustomSwitches, ::AppInstaller::Utility::ConvertToUTF8(options.AdditionalInstallerArguments())); } if (options.AllowedArchitectures().Size() != 0) { std::vector allowedArchitectures; for (auto architecture : options.AllowedArchitectures()) { auto convertedArchitecture = GetUtilityArchitecture(architecture); if (convertedArchitecture) { allowedArchitectures.push_back(convertedArchitecture.value()); } } context->Add(std::move(allowedArchitectures)); } // Note: AdditionalPackageCatalogArguments is not needed during install since the manifest is already known so no additional calls to the source are needed. The property is deprecated. if (options.AcceptPackageAgreements()) { context->Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); } if (options.SkipDependencies()) { context->Args.AddArg(Execution::Args::Type::SkipDependencies); } if (options.AuthenticationArguments()) { context->Args.AddArg(Execution::Args::Type::AuthenticationMode, ::AppInstaller::Authentication::AuthenticationModeToString(GetAuthenticationMode(options.AuthenticationArguments().AuthenticationMode()))); context->Args.AddArg(Execution::Args::Type::AuthenticationAccount, ::AppInstaller::Utility::ConvertToUTF8(options.AuthenticationArguments().AuthenticationAccount())); } } else { // Note: If no install options are specified, we assume the caller is accepting the package agreements by default. context->Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); } } void PopulateContextFromUninstallOptions( ::AppInstaller::CLI::Execution::Context* context, winrt::Microsoft::Management::Deployment::UninstallOptions options) { if (options) { if (!options.LogOutputPath().empty()) { context->Args.AddArg(Execution::Args::Type::Log, ::AppInstaller::Utility::ConvertToUTF8(options.LogOutputPath())); context->Args.AddArg(Execution::Args::Type::VerboseLogs); } if (options.Force()) { context->Args.AddArg(Execution::Args::Type::Force); } if (options.PackageUninstallMode() == PackageUninstallMode::Interactive) { context->Args.AddArg(Execution::Args::Type::Interactive); } else if (options.PackageUninstallMode() == PackageUninstallMode::Silent) { context->Args.AddArg(Execution::Args::Type::Silent); } auto uninstallScope = GetManifestUninstallScope(options.PackageUninstallScope()); if (uninstallScope != ::AppInstaller::Manifest::ScopeEnum::Unknown) { context->Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(uninstallScope)); } } } void PopulateContextFromDownloadOptions( ::AppInstaller::CLI::Execution::Context* context, winrt::Microsoft::Management::Deployment::DownloadOptions options) { if (options) { if (!options.DownloadDirectory().empty()) { context->Args.AddArg(Execution::Args::Type::DownloadDirectory, ::AppInstaller::Utility::ConvertToUTF8(options.DownloadDirectory())); } if (!options.Locale().empty()) { context->Args.AddArg(Execution::Args::Type::Locale, ::AppInstaller::Utility::ConvertToUTF8(options.Locale())); } if (options.AllowHashMismatch()) { context->Args.AddArg(Execution::Args::Type::HashOverride); } if (options.SkipDependencies()) { context->Args.AddArg(Execution::Args::Type::SkipDependencies); } if (options.AcceptPackageAgreements()) { context->Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); } auto manifestScope = GetManifestScope(options.Scope()); if (manifestScope.first != ::AppInstaller::Manifest::ScopeEnum::Unknown) { context->Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(manifestScope.first)); } auto architecture = options.Architecture(); if (architecture != Windows::System::ProcessorArchitecture::Unknown) { auto convertedArchitecture = GetUtilityArchitecture(architecture); if (convertedArchitecture) { context->Args.AddArg(Execution::Args::Type::InstallerArchitecture, ToString(convertedArchitecture.value())); } } auto installerType = GetManifestInstallerType(options.InstallerType()); if (installerType != AppInstaller::Manifest::InstallerTypeEnum::Unknown) { context->Args.AddArg(Execution::Args::Type::InstallerType, AppInstaller::Manifest::InstallerTypeToString(installerType)); } if (options.AuthenticationArguments()) { context->Args.AddArg(Execution::Args::Type::AuthenticationMode, ::AppInstaller::Authentication::AuthenticationModeToString(GetAuthenticationMode(options.AuthenticationArguments().AuthenticationMode()))); context->Args.AddArg(Execution::Args::Type::AuthenticationAccount, ::AppInstaller::Utility::ConvertToUTF8(options.AuthenticationArguments().AuthenticationAccount())); } if (options.SkipMicrosoftStoreLicense()) { context->Args.AddArg(Execution::Args::Type::SkipMicrosoftStorePackageLicense); } WindowsPlatform platform = options.Platform(); if (platform != WindowsPlatform::Unknown) { context->Args.AddArg(Execution::Args::Type::Platform, AppInstaller::Manifest::PlatformToString(GetPlatformEnum(platform))); } hstring targetOSVersion = options.TargetOSVersion(); if (!targetOSVersion.empty()) { context->Args.AddArg(Execution::Args::Type::OSVersion, ::AppInstaller::Utility::ConvertToUTF8(targetOSVersion)); } } } void PopulateContextFromRepairOptions( ::AppInstaller::CLI::Execution::Context* context, winrt::Microsoft::Management::Deployment::RepairOptions options) { if (options) { if (!options.LogOutputPath().empty()) { context->Args.AddArg(Execution::Args::Type::Log, ::AppInstaller::Utility::ConvertToUTF8(options.LogOutputPath())); context->Args.AddArg(Execution::Args::Type::VerboseLogs); } if (options.PackageRepairMode() == PackageRepairMode::Interactive) { context->Args.AddArg(Execution::Args::Type::Interactive); } else if (options.PackageRepairMode() == PackageRepairMode::Silent) { context->Args.AddArg(Execution::Args::Type::Silent); } if (options.AcceptPackageAgreements()) { context->Args.AddArg(Execution::Args::Type::AcceptPackageAgreements); } if (options.AllowHashMismatch()) { context->Args.AddArg(Execution::Args::Type::HashOverride); } if (options.BypassIsStoreClientBlockedPolicyCheck()) { context->SetFlags(Execution::ContextFlag::BypassIsStoreClientBlockedPolicyCheck); } if (options.Force()) { context->Args.AddArg(Execution::Args::Type::Force); } auto repairScope = GetManifestRepairScope(options.PackageRepairScope()); if (repairScope != ::AppInstaller::Manifest::ScopeEnum::Unknown) { context->Args.AddArg(Execution::Args::Type::InstallScope, ScopeToString(repairScope)); } if (options.AuthenticationArguments()) { context->Args.AddArg(Execution::Args::Type::AuthenticationMode, ::AppInstaller::Authentication::AuthenticationModeToString(GetAuthenticationMode(options.AuthenticationArguments().AuthenticationMode()))); context->Args.AddArg(Execution::Args::Type::AuthenticationAccount, ::AppInstaller::Utility::ConvertToUTF8(options.AuthenticationArguments().AuthenticationAccount())); } } } template std::unique_ptr CreateContextFromOperationOptions( TOptions options, std::wstring callerProcessInfoString) { std::unique_ptr context = std::make_unique(); hstring correlationData = (options) ? options.CorrelationData() : L""; context->SetContextLoggers(correlationData, GetComCallerName(AppInstaller::Utility::ConvertToUTF8(callerProcessInfoString))); // Convert the options to arguments for the installer. if constexpr (std::is_same_v) { PopulateContextFromInstallOptions(context.get(), options); } else if constexpr (std::is_same_v) { PopulateContextFromUninstallOptions(context.get(), options); } else if constexpr (std::is_same_v) { PopulateContextFromDownloadOptions(context.get(), options); } else if constexpr (std::is_same_v) { PopulateContextFromRepairOptions(context.get(), options); } return context; } std::shared_ptr GetExistingQueueItemForPackage(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) { std::shared_ptr queueItem = nullptr; std::unique_ptr context = std::make_unique(); if (catalogInfo) { // If the caller has passed in the catalog they expect the package to have come from, then only look for an install from that catalog. // Fail if they've used a catalog that doesn't have an Id. This can currently happen for Info objects that come from PackageCatalogReference objects for REST catalogs. THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, catalogInfo.Id().empty()); auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring{ package.Id() }, std::wstring{ catalogInfo.Id() }, std::move(context)); queueItem = Execution::ContextOrchestrator::Instance().GetQueueItem(searchItem->GetId()); return queueItem; } // If the caller has not specified the catalog, then check InstalledVersion. When the package comes from the Installing catalog the PackageCatalog // of the InstalledVersion will be set to the original catalog that the install was from, so checking the InstalledVersion first is most likely to // find a result. Microsoft::Management::Deployment::PackageVersionInfo installedVersionInfo = package.InstalledVersion(); if (installedVersionInfo) { auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring{ package.Id() }, std::wstring{ installedVersionInfo.PackageCatalog().Info().Id() }, std::move(context)); queueItem = Execution::ContextOrchestrator::Instance().GetQueueItem(searchItem->GetId()); if (queueItem) { return queueItem; } } // If InstalledVersion was not found, check DefaultInstallVersion Microsoft::Management::Deployment::PackageVersionInfo defaultInstallVersionInfo = package.DefaultInstallVersion(); if (defaultInstallVersionInfo) { auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring{ package.Id() }, std::wstring{ defaultInstallVersionInfo.PackageCatalog().Info().Id() }, std::move(context)); queueItem = Execution::ContextOrchestrator::Instance().GetQueueItem(searchItem->GetId()); if (queueItem) { return queueItem; } } // Finally check all catalogs in AvailableVersions. for (Microsoft::Management::Deployment::PackageVersionId versionId : package.AvailableVersions()) { auto searchItem = Execution::OrchestratorQueueItemFactory::CreateItemForSearch(std::wstring{ package.Id() }, std::wstring{ package.GetPackageVersionInfo(versionId).PackageCatalog().Info().Id() }, std::move(context)); queueItem = Execution::ContextOrchestrator::Instance().GetQueueItem(searchItem->GetId()); if (queueItem) { return queueItem; } } return nullptr; } std::unique_ptr CreateQueueItemForInstall( std::unique_ptr<::AppInstaller::CLI::Execution::COMContext> comContext, winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options, bool isUpgrade) { // Add manifest and PackageVersion to context for install/upgrade. // If the version of the package is specified use that, otherwise use the default. Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo = GetPackageVersionInfo(package, options); AddPackageManifestToContext(packageVersionInfo, comContext.get()); if (isUpgrade) { AppInstaller::Utility::VersionAndChannel installedVersion{ winrt::to_string(package.InstalledVersion().Version()), winrt::to_string(package.InstalledVersion().Channel()) }; AppInstaller::Utility::VersionAndChannel upgradeVersion{ winrt::to_string(packageVersionInfo.Version()), winrt::to_string(packageVersionInfo.Channel()) }; // Perform upgrade version check if (upgradeVersion.GetVersion().IsUnknown()) { if (!(options.AllowUpgradeToUnknownVersion() && AppInstaller::Utility::ICUCaseInsensitiveEquals(installedVersion.GetChannel().ToString(), upgradeVersion.GetChannel().ToString()))) { THROW_HR(APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_UNKNOWN); } } else if (!installedVersion.IsUpdatedBy(upgradeVersion)) { THROW_HR(APPINSTALLER_CLI_ERROR_UPGRADE_VERSION_NOT_NEWER); } // Set upgrade flag comContext->SetFlags(AppInstaller::CLI::Execution::ContextFlag::InstallerExecutionUseUpdate); // Add installed version AddInstalledVersionToContext(package.InstalledVersion(), comContext.get()); } return Execution::OrchestratorQueueItemFactory::CreateItemForInstall(std::wstring{ package.Id() }, std::wstring{ packageVersionInfo.PackageCatalog().Info().Id() }, std::move(comContext), isUpgrade); } std::unique_ptr CreateQueueItemForUninstall( std::unique_ptr<::AppInstaller::CLI::Execution::COMContext> comContext, winrt::Microsoft::Management::Deployment::CatalogPackage package) { // Add installed version AddInstalledVersionToContext(package.InstalledVersion(), comContext.get()); // Add Package which is used by RecordUninstall later for removing from tracking catalog of correlated available sources as best effort winrt::Microsoft::Management::Deployment::implementation::CatalogPackage* catalogPackageImpl = get_self(package); std::shared_ptr<::AppInstaller::Repository::ICompositePackage> internalPackage = catalogPackageImpl->GetRepositoryPackage(); comContext->Add(internalPackage); return Execution::OrchestratorQueueItemFactory::CreateItemForUninstall(std::wstring{ package.Id() }, std::wstring{ package.InstalledVersion().PackageCatalog().Info().Id() }, std::move(comContext)); } std::unique_ptr CreateQueueItemForDownload( std::unique_ptr<::AppInstaller::CLI::Execution::COMContext> comContext, winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::DownloadOptions options) { // Add manifest and PackageVersion to context for download. // If the version of the package is specified use that, otherwise use the default. Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo = GetPackageVersionInfo(package, options); AddPackageManifestToContext(packageVersionInfo, comContext.get()); comContext->SetFlags(AppInstaller::CLI::Execution::ContextFlag::InstallerDownloadOnly); return Execution::OrchestratorQueueItemFactory::CreateItemForDownload(std::wstring{ package.Id() }, std::wstring{ packageVersionInfo.PackageCatalog().Info().Id() }, std::move(comContext)); } std::unique_ptr CreateQueueItemForRepair( std::unique_ptr<::AppInstaller::CLI::Execution::COMContext> comContext, winrt::Microsoft::Management::Deployment::CatalogPackage package) { // Add installed version AddInstalledVersionToContext(package.InstalledVersion(), comContext.get()); // Add Package which is used to co-relate installed package with available package for repair winrt::Microsoft::Management::Deployment::implementation::CatalogPackage* catalogPackageImpl = get_self(package); std::shared_ptr<::AppInstaller::Repository::ICompositePackage> internalPackage = catalogPackageImpl->GetRepositoryPackage(); comContext->Add(internalPackage); comContext->SetFlags(AppInstaller::CLI::Execution::ContextFlag::InstallerExecutionUseRepair); return Execution::OrchestratorQueueItemFactory::CreateItemForRepair(std::wstring{ package.Id() }, std::wstring{ package.InstalledVersion().PackageCatalog().Info().Id() }, std::move(comContext)); } template winrt::Windows::Foundation::IAsyncOperationWithProgress GetPackageOperation( bool canCancelQueueItem, std::shared_ptr queueItemParam, winrt::Microsoft::Management::Deployment::CatalogPackage package = nullptr, TOptions options = nullptr, std::wstring callerProcessInfoString = {}, bool isUpgrade = false) { winrt::hresult terminationHR = S_OK; uint32_t operationError = 0; hstring correlationData = (options) ? options.CorrelationData() : L""; ::Workflow::ExecutionStage executionStage = ::Workflow::ExecutionStage::Initial; try { // re-scope the parameter to inside the try block to avoid lifetime management issues. std::shared_ptr queueItem = std::move(queueItemParam); auto report_progress{ co_await winrt::get_progress_token() }; auto cancellationToken{ co_await winrt::get_cancellation_token() }; // co_await does not guarantee that it's on a background thread, so do so explicitly. co_await winrt::resume_background(); if (queueItem == nullptr) { std::unique_ptr comContext = CreateContextFromOperationOptions(options, callerProcessInfoString); if constexpr (std::is_same_v) { queueItem = CreateQueueItemForInstall(std::move(comContext), package, options, isUpgrade); } else if constexpr (std::is_same_v) { queueItem = CreateQueueItemForUninstall(std::move(comContext), package); } else if constexpr (std::is_same_v) { queueItem = CreateQueueItemForDownload(std::move(comContext), package, options); } else if constexpr (std::is_same_v) { queueItem = CreateQueueItemForRepair(std::move(comContext), package); } Execution::ContextOrchestrator::Instance().EnqueueAndRunItem(queueItem); if constexpr (std::is_same_v) { TProgress queuedProgress{ TProgressState::Queued, 0, 0, 0 }; report_progress(queuedProgress); } else if constexpr (std::is_same_v) { TProgress queuedProgress{ TProgressState::Queued, 0 }; report_progress(queuedProgress); } else if constexpr (std::is_same_v) { TProgress queuedProgress{ TProgressState::Queued, 0 }; report_progress(queuedProgress); } else if constexpr (std::is_same_v) { TProgress queuedProgress{ TProgressState::Queued, 0 }; report_progress(queuedProgress); } } { // correlation data is not passed in when retrieving an existing queue item, so get it from the existing context. correlationData = hstring(queueItem->GetContext().GetCorrelationJson()); } wil::unique_event progressEvent{ wil::EventOptions::None }; std::atomic operationProgress; queueItem->GetContext().AddProgressCallbackFunction([&operationProgress, &progressEvent]( ReportType reportType, uint64_t current, uint64_t maximum, ::AppInstaller::ProgressType progressType, ::Workflow::ExecutionStage executionPhase) { std::optional operationProgressOptional = GetProgress(reportType, current, maximum, progressType, executionPhase); if (operationProgressOptional.has_value()) { operationProgress = operationProgressOptional.value(); progressEvent.SetEvent(); } return; } ); std::weak_ptr weakQueueItem(queueItem); cancellationToken.callback([weakQueueItem, &canCancelQueueItem] { if (canCancelQueueItem) { auto strongQueueItem = weakQueueItem.lock(); if (strongQueueItem) { // The cancellation of the AsyncOperation on the client triggers Cancel which causes the Execute to end. Execution::ContextOrchestrator::Instance().CancelQueueItem(*strongQueueItem); } } }); // Wait for completion or progress events. // Waiting for both on the same thread ensures that progress is never reported after the async operation itself has completed. bool completionEventFired = false; HANDLE operationEvents[2]; operationEvents[0] = progressEvent.get(); operationEvents[1] = queueItem->GetCompletedEvent().get(); while (!completionEventFired) { DWORD dwEvent = WaitForMultipleObjects( _countof(operationEvents) /* number of events */, operationEvents /* event array */, FALSE /* bWaitAll, FALSE to wake on any event */, INFINITE /* wait until operation completion */); switch (dwEvent) { // operationEvents[0] was signaled, progress case WAIT_OBJECT_0 + 0: // The report_progress call will hang when making callbacks to suspended processes so it's important that this is now on a background thread. // Progress events are not queued - some will be missed if multiple progress events are fired from the ComContext to the callback // while the report_progress call is hung\in progress. // Duplicate progress events can be fired if another progress event comes from the ComContext to the callback after the listener // has been awaked, but before it has gotten the installProgress. report_progress(operationProgress); break; // operationEvents[1] was signaled, operation completed case WAIT_OBJECT_0 + 1: completionEventFired = true; break; // Return value is invalid. default: THROW_LAST_ERROR(); } } if (completionEventFired) { // The install command has finished, check for success/failure and how far it got. terminationHR = queueItem->GetContext().GetTerminationHR(); executionStage = queueItem->GetContext().GetExecutionStage(); if (queueItem->GetContext().Contains(Data::OperationReturnCode)) { operationError = static_cast(queueItem->GetContext().Get()); } } } WINGET_CATCH_STORE(terminationHR, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); // TODO - RebootRequired not yet populated, msi arguments not returned from Execute. co_return GetOperationResult(executionStage, terminationHR, operationError, correlationData, false); } template winrt::Windows::Foundation::IAsyncOperationWithProgress GetEmptyAsynchronousResultForOperation( HRESULT hr, hstring correlationData) { // If a function uses co_await or co_return (i.e. if it is a co_routine), it cannot use return directly. // This helper helps a function that is not a coroutine itself to return errors asynchronously. co_return GetOperationResult(::Workflow::ExecutionStage::Initial, hr, 0, correlationData, false); } #define WINGET_RETURN_INSTALL_RESULT_HR_IF(hr, boolVal) { if(boolVal) { return GetEmptyAsynchronousResultForOperation(hr, correlationData); }} #define WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr) { WINGET_RETURN_INSTALL_RESULT_HR_IF(hr, FAILED(hr)) } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::InstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options) { hstring correlationData = (options) ? options.CorrelationData() : L""; // options and catalog can both be null, package must be set. WINGET_RETURN_INSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); HRESULT hr = S_OK; std::wstring callerProcessInfoString; try { // Check for permissions and get caller info for telemetry. // This must be done before any co_awaits since it requires info from the rpc caller thread. auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hrGetCallerId); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); callerProcessInfoString = TryGetCallerProcessInfo(callerProcessId); } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr); return GetPackageOperation( true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::UpgradePackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options) { hstring correlationData = (options) ? options.CorrelationData() : L""; // options and catalog can both be null, package must be set. WINGET_RETURN_INSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); // the package should have an installed version to be upgraded. WINGET_RETURN_INSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package.InstalledVersion()); HRESULT hr = S_OK; std::wstring callerProcessInfoString; try { // Check for permissions and get caller info for telemetry. // This must be done before any co_awaits since it requires info from the rpc caller thread. auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hrGetCallerId); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); callerProcessInfoString = TryGetCallerProcessInfo(callerProcessId); } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr); return GetPackageOperation( true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString), true /* isUpgrade */); } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::GetInstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) { hstring correlationData; WINGET_RETURN_INSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); HRESULT hr = S_OK; std::shared_ptr queueItem = nullptr; bool canCancelQueueItem = false; try { // Check for permissions // This must be done before any co_awaits since it requires info from the rpc caller thread. auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hrGetCallerId); canCancelQueueItem = SUCCEEDED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); if (!canCancelQueueItem) { WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageQuery, callerProcessId)); } // Get the queueItem synchronously. queueItem = GetExistingQueueItemForPackage(package, catalogInfo); if (queueItem == nullptr || (queueItem->GetPackageOperationType() != PackageOperationType::Install && queueItem->GetPackageOperationType() != PackageOperationType::Upgrade)) { return nullptr; } } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_INSTALL_RESULT_HR_IF_FAILED(hr); return GetPackageOperation( canCancelQueueItem, std::move(queueItem)); } #define WINGET_RETURN_UNINSTALL_RESULT_HR_IF(hr, boolVal) { if(boolVal) { return GetEmptyAsynchronousResultForOperation(hr, correlationData); }} #define WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hr) { WINGET_RETURN_UNINSTALL_RESULT_HR_IF(hr, FAILED(hr)) } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::UninstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::UninstallOptions options) { hstring correlationData = (options) ? options.CorrelationData() : L""; // options and catalog can both be null, package must be set. WINGET_RETURN_UNINSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); // the package should have an installed version to be uninstalled. WINGET_RETURN_UNINSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package.InstalledVersion()); HRESULT hr = S_OK; std::wstring callerProcessInfoString; try { // Check for permissions and get caller info for telemetry. // This must be done before any co_awaits since it requires info from the rpc caller thread. auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hrGetCallerId); WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); callerProcessInfoString = TryGetCallerProcessInfo(callerProcessId); } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hr); return GetPackageOperation( true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::GetUninstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) { hstring correlationData; WINGET_RETURN_UNINSTALL_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); HRESULT hr = S_OK; std::shared_ptr queueItem = nullptr; bool canCancelQueueItem = false; try { // Check for permissions // This must be done before any co_awaits since it requires info from the rpc caller thread. auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hrGetCallerId); canCancelQueueItem = SUCCEEDED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); if (!canCancelQueueItem) { WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageQuery, callerProcessId)); } // Get the queueItem synchronously. queueItem = GetExistingQueueItemForPackage(package, catalogInfo); if (queueItem == nullptr || queueItem->GetPackageOperationType() != PackageOperationType::Uninstall) { return nullptr; } } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_UNINSTALL_RESULT_HR_IF_FAILED(hr); return GetPackageOperation( canCancelQueueItem, std::move(queueItem)); } #define WINGET_RETURN_DOWNLOAD_RESULT_HR_IF(hr, boolVal) { if(boolVal) { return GetEmptyAsynchronousResultForOperation(hr, correlationData); }} #define WINGET_RETURN_DOWNLOAD_RESULT_HR_IF_FAILED(hr) { WINGET_RETURN_DOWNLOAD_RESULT_HR_IF(hr, FAILED(hr)) } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::DownloadPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::DownloadOptions options) { hstring correlationData = (options) ? options.CorrelationData() : L""; // options and catalog can both be null, package must be set. WINGET_RETURN_DOWNLOAD_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); HRESULT hr = S_OK; std::wstring callerProcessInfoString; try { // Check for permissions and get caller info for telemetry. // This must be done before any co_awaits since it requires info from the rpc caller thread. auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); WINGET_RETURN_DOWNLOAD_RESULT_HR_IF_FAILED(hrGetCallerId); WINGET_RETURN_DOWNLOAD_RESULT_HR_IF_FAILED(EnsureComCallerHasCapability(Capability::PackageQuery)); callerProcessInfoString = TryGetCallerProcessInfo(callerProcessId); } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_DOWNLOAD_RESULT_HR_IF_FAILED(hr); return GetPackageOperation( true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::GetDownloadProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo) { hstring correlationData; WINGET_RETURN_DOWNLOAD_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); HRESULT hr = S_OK; std::shared_ptr queueItem = nullptr; try { WINGET_RETURN_DOWNLOAD_RESULT_HR_IF_FAILED(EnsureComCallerHasCapability(Capability::PackageQuery)); // Get the queueItem synchronously. queueItem = GetExistingQueueItemForPackage(package, catalogInfo); if (queueItem == nullptr || queueItem->GetPackageOperationType() != PackageOperationType::Download) { return nullptr; } } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_DOWNLOAD_RESULT_HR_IF_FAILED(hr); return GetPackageOperation(true, std::move(queueItem)); } #define WINGET_RETURN_REPAIR_RESULT_HR_IF(hr, boolVal) { if(boolVal) { return GetEmptyAsynchronousResultForOperation(hr, correlationData); }} #define WINGET_RETURN_REPAIR_RESULT_HR_IF_FAILED(hr) { WINGET_RETURN_REPAIR_RESULT_HR_IF(hr, FAILED(hr)) } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::RepairPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::RepairOptions options) { hstring correlationData = (options) ? options.CorrelationData() : L""; // options and catalog can both be null, package must be set. WINGET_RETURN_REPAIR_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package); // the package should have an installed version to be repaired. WINGET_RETURN_REPAIR_RESULT_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_CL_ARGUMENTS, !package.InstalledVersion()); HRESULT hr = S_OK; std::wstring callerProcessInfoString; try { // Check for permissions and get caller info for telemetry. // This must be done before any co_awaits since it requires info from the rpc caller thread. auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); WINGET_RETURN_REPAIR_RESULT_HR_IF_FAILED(hrGetCallerId); WINGET_RETURN_REPAIR_RESULT_HR_IF_FAILED(EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId)); callerProcessInfoString = TryGetCallerProcessInfo(callerProcessId); } WINGET_CATCH_STORE(hr, APPINSTALLER_CLI_ERROR_COMMAND_FAILED); WINGET_RETURN_REPAIR_RESULT_HR_IF_FAILED(hr); return GetPackageOperation( true /*canCancelQueueItem*/, nullptr /*queueItem*/, package, options, std::move(callerProcessInfoString)); } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::AddPackageCatalogAsync(winrt::Microsoft::Management::Deployment::AddPackageCatalogOptions options) { LogStartupIfApplicable(); // options must be set. THROW_HR_IF_NULL(E_POINTER, options); THROW_HR_IF(E_INVALIDARG, options.Name().empty()); THROW_HR_IF(E_INVALIDARG, options.SourceUri().empty()); HRESULT terminationHR = S_OK; try { // Check if running as admin/system. // [NOTE:] For OutOfProc calls, the Windows Package Manager Service executes in the context initiated by the caller process, // so the same admin/system validation check is applicable for both InProc and OutOfProc calls. THROW_HR_IF(APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN, !AppInstaller::Runtime::IsRunningAsAdminOrSystem()); ::AppInstaller::Repository::Source sourceToAdd = CreateSourceFromOptions(options); auto strong_this = get_strong(); auto report_progress{ co_await winrt::get_progress_token() }; co_await winrt::resume_background(); std::string type = winrt::to_string(options.Type()); auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink(type, report_progress ); packageCatalogProgressSink->BeginProgress(); ::AppInstaller::ProgressCallback progress(packageCatalogProgressSink.get()); sourceToAdd.Add(progress); packageCatalogProgressSink->EndProgress(false); } catch (...) { terminationHR = AppInstaller::CLI::Workflow::HandleException(nullptr, std::current_exception()); } co_return GetAddPackageCatalogResult(terminationHR); } winrt::Windows::Foundation::IAsyncOperationWithProgress PackageManager::RemovePackageCatalogAsync(winrt::Microsoft::Management::Deployment::RemovePackageCatalogOptions options) { LogStartupIfApplicable(); // options must be set. THROW_HR_IF_NULL(E_POINTER, options); THROW_HR_IF(E_INVALIDARG, options.Name().empty()); HRESULT terminationHR = S_OK; try { // Check if running as admin/system. // [NOTE:] For OutOfProc calls, the Windows Package Manager Service executes in the context initiated by the caller process, // so the same admin/system validation check is applicable for both InProc and OutOfProc calls. THROW_HR_IF(APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN, !AppInstaller::Runtime::IsRunningAsAdminOrSystem()); auto matchingSource = GetMatchingSource(winrt::to_string(options.Name())); THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST, !matchingSource.has_value()); auto strong_this = get_strong(); auto report_progress{ co_await winrt::get_progress_token() }; co_await winrt::resume_background(); auto packageCatalogProgressSink = winrt::Microsoft::Management::Deployment::ProgressSinkFactory::CreatePackageCatalogProgressSink(matchingSource.value().Type, report_progress, true); packageCatalogProgressSink->BeginProgress(); ::AppInstaller::Repository::Source sourceToRemove = ::AppInstaller::Repository::Source{ matchingSource.value().Name }; ::AppInstaller::ProgressCallback progress(packageCatalogProgressSink.get()); // If the PreserveData option is set, this is equivalent to the WinGet CLI Reset command on a single source; otherwise, it removes the source. if (options.PreserveData()) { THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST, !sourceToRemove.DropSource(matchingSource.value().Name)); } else { sourceToRemove.Remove(progress); } packageCatalogProgressSink->EndProgress(false); } catch (...) { terminationHR = AppInstaller::CLI::Workflow::HandleException(nullptr, std::current_exception()); } co_return GetRemovePackageCatalogResult(terminationHR); } winrt::hstring PackageManager::Version() const { return winrt::hstring{ AppInstaller::Utility::ConvertToUTF16(AppInstaller::Runtime::GetClientVersion()) }; } winrt::Microsoft::Management::Deployment::EditPackageCatalogResult GetEditPackageCatalogResult(winrt::hresult terminationStatus) { winrt::Microsoft::Management::Deployment::EditPackageCatalogStatus status = GetPackageCatalogOperationStatus(terminationStatus); auto editResult = winrt::make_self>(); editResult->Initialize(status, terminationStatus); return *editResult; } winrt::Microsoft::Management::Deployment::EditPackageCatalogResult PackageManager::EditPackageCatalog(winrt::Microsoft::Management::Deployment::EditPackageCatalogOptions options) { LogStartupIfApplicable(); // options must be set. THROW_HR_IF_NULL(E_POINTER, options); THROW_HR_IF(E_INVALIDARG, options.Name().empty()); HRESULT terminationHR = S_OK; try { // Check if running as admin/system. // [NOTE:] For OutOfProc calls, the Windows Package Manager Service executes in the context initiated by the caller process, // so the same admin/system validation check is applicable for both InProc and OutOfProc calls. THROW_HR_IF(APPINSTALLER_CLI_ERROR_COMMAND_REQUIRES_ADMIN, !AppInstaller::Runtime::IsRunningAsAdminOrSystem()); auto matchingSource = GetMatchingSource(winrt::to_string(options.Name())); THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST, !matchingSource.has_value()); ::AppInstaller::Repository::Source sourceToEdit = ::AppInstaller::Repository::Source{ matchingSource.value().Name }; ::AppInstaller::Repository::SourceEdit edits; edits.Explicit = options.Explicit(); edits.Priority = options.Priority(); if (sourceToEdit.RequiresChanges(edits)) { sourceToEdit.Edit(edits); } } catch (...) { terminationHR = AppInstaller::CLI::Workflow::HandleException(nullptr, std::current_exception()); } return GetEditPackageCatalogResult(terminationHR); } CoCreatableMicrosoftManagementDeploymentClass(PackageManager); } ================================================ FILE: src/Microsoft.Management.Deployment/PackageManager.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageManager.g.h" #include "Public/ComClsids.h" #include #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) // Forward declaration namespace AppInstaller::CLI::Execution { struct Context; } #endif namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_PackageManager)] struct PackageManager : PackageManagerT { PackageManager(); winrt::Windows::Foundation::Collections::IVectorView GetPackageCatalogs(); winrt::Microsoft::Management::Deployment::PackageCatalogReference GetPredefinedPackageCatalog(winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog const& predefinedPackageCatalog); winrt::Microsoft::Management::Deployment::PackageCatalogReference GetLocalPackageCatalog(winrt::Microsoft::Management::Deployment::LocalPackageCatalog const& localPackageCatalog); winrt::Microsoft::Management::Deployment::PackageCatalogReference GetPackageCatalogByName(hstring const& catalogName); winrt::Microsoft::Management::Deployment::PackageCatalogReference CreateCompositePackageCatalog(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions const& options); winrt::Windows::Foundation::IAsyncOperationWithProgress InstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options); // Contract 2.0 winrt::Windows::Foundation::IAsyncOperationWithProgress GetInstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo); // Contract 4.0 winrt::Windows::Foundation::IAsyncOperationWithProgress UpgradePackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::InstallOptions options); winrt::Windows::Foundation::IAsyncOperationWithProgress UninstallPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::UninstallOptions options); winrt::Windows::Foundation::IAsyncOperationWithProgress GetUninstallProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo); // Contract 7.0 winrt::Windows::Foundation::IAsyncOperationWithProgress DownloadPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::DownloadOptions options); winrt::Windows::Foundation::IAsyncOperationWithProgress GetDownloadProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo); // Contract 11.0 winrt::Windows::Foundation::IAsyncOperationWithProgress RepairPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::RepairOptions options); // Contract 12.0 winrt::Windows::Foundation::IAsyncOperationWithProgress AddPackageCatalogAsync(winrt::Microsoft::Management::Deployment::AddPackageCatalogOptions options); winrt::Windows::Foundation::IAsyncOperationWithProgress RemovePackageCatalogAsync(winrt::Microsoft::Management::Deployment::RemovePackageCatalogOptions options); // Contract 13.0 winrt::hstring Version() const; // Contract 28.0 winrt::Microsoft::Management::Deployment::EditPackageCatalogResult EditPackageCatalog(winrt::Microsoft::Management::Deployment::EditPackageCatalogOptions options); }; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void SetComCallerName(std::string name); void PopulateContextFromInstallOptions(AppInstaller::CLI::Execution::Context* context, winrt::Microsoft::Management::Deployment::InstallOptions options); #endif } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct PackageManager : PackageManagerT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/PackageManager.idl ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment { [contractversion(29)] // For version 1.29 apicontract WindowsPackageManagerContract{}; /// State of the install [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageInstallProgressState { /// The install is queued but not yet active. Cancellation of the IAsyncOperationWithProgress in this /// state will prevent the package from downloading or installing. Queued, /// The installer is downloading. Cancellation of the IAsyncOperationWithProgress in this state will /// end the download and prevent the package from installing. Downloading, /// The install is in progress. Cancellation of the IAsyncOperationWithProgress in this state will not /// stop the installation or the post install cleanup. Installing, /// The installer has completed and cleanup actions are in progress. Cancellation of the /// IAsyncOperationWithProgress in this state will not stop cleanup or roll back the install. PostInstall, /// The operation has completed. Finished, }; /// Progress object for the install /// DESIGN NOTE: percentage for the install as a whole is purposefully not included as there is no way to /// estimate progress when the installer is running. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] struct InstallProgress { /// State of the install PackageInstallProgressState State; /// DESIGN NOTE: BytesDownloaded may only be available for downloads done by Windows Package Manager itself. /// Number of bytes downloaded if known UInt64 BytesDownloaded; /// DESIGN NOTE: BytesRequired may only be available for downloads done by Windows Package Manager itself. /// Number of bytes required if known UInt64 BytesRequired; /// Download percentage completed Double DownloadProgress; /// Install percentage if known. Double InstallationProgress; }; /// Status of the Install call /// Implementation Note: Errors mapped from AppInstallerErrors.h [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum InstallResultStatus { Ok, BlockedByPolicy, CatalogError, InternalError, InvalidOptions, DownloadError, InstallError, ManifestError, NoApplicableInstallers, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] { NoApplicableUpgrade, }, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] { PackageAgreementsNotAccepted, } }; /// Result of the install [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass InstallResult { /// Used by a caller to correlate the install with a caller's data. String CorrelationData { get; }; /// Whether a restart is required to complete the install. Boolean RebootRequired { get; }; /// Batched error code, example APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED InstallResultStatus Status { get; }; /// The error code of the overall operation. HRESULT ExtendedErrorCode { get; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] { /// The error code from the install attempt. Only valid if the Status is InstallError. /// This value's meaning will require knowledge of the specific installer or install technology. UInt32 InstallerErrorCode { get; }; } } /// State of the uninstall [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] enum PackageUninstallProgressState { /// The uninstall is queued but not yet active. Cancellation of the IAsyncOperationWithProgress in this /// state will prevent the package from uninstalling. Queued, /// The uninstall is in progress. Cancellation of the IAsyncOperationWithProgress in this state will not /// stop the installation or the post uninstall steps. Uninstalling, /// The uninstaller has completed and cleanup actions are in progress. Cancellation of the /// IAsyncOperationWithProgress in this state will not stop cleanup or roll back the uninstall. PostUninstall, /// The operation has completed. Finished, }; /// Progress object for the uninstall [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] struct UninstallProgress { /// State of the uninstall PackageUninstallProgressState State; /// Uninstall percentage if known. Double UninstallationProgress; }; /// Status of the uninstall call /// Implementation Note: Errors mapped from AppInstallerErrors.h [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] enum UninstallResultStatus { Ok, BlockedByPolicy, CatalogError, InternalError, InvalidOptions, UninstallError, ManifestError, }; /// Result of the uninstall [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] runtimeclass UninstallResult { /// Used by a caller to correlate the install with a caller's data. String CorrelationData { get; }; /// Whether a restart is required to complete the install. Boolean RebootRequired { get; }; /// Batched error code, example APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED UninstallResultStatus Status { get; }; /// The error code of the overall operation. HRESULT ExtendedErrorCode { get; }; /// The error code from the uninstall attempt. Only valid if the Status is UninstallError. /// This value's meaning will require knowledge of the specific uninstaller or install technology. UInt32 UninstallerErrorCode { get; }; } /// State of the repair [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] enum PackageRepairProgressState { /// The repair is queued but not yet active. Cancellation of the IAsyncOperationWithProgress in this /// state will prevent the package from repairing. Queued, /// The repair is in progress. Cancellation of the IAsyncOperationWithProgress in this state will not /// stop the repair or the post repair steps. Repairing, /// The repair has completed and cleanup actions are in progress. Cancellation of the /// IAsyncOperationWithProgress in this state will not stop cleanup or roll back the repair. PostRepair, /// The operation has completed. Finished, }; /// Progress object for the repair [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] struct RepairProgress { /// State of the repair PackageRepairProgressState State; /// Repair percentage if known. Double RepairCompletionProgress; }; /// Status of the repair call /// Implementation Note: Errors mapped from AppInstallerErrors.h /// DESIGN NOTE: RepairResultStatus from AppInstallerErrors.h is not implemented in V1. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] enum RepairResultStatus { Ok, BlockedByPolicy, CatalogError, DownloadError, InternalError, InvalidOptions, RepairError, ManifestError, NoApplicableRepairer, PackageAgreementsNotAccepted, }; /// Result of the repair [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] runtimeclass RepairResult { /// Used by a caller to correlate the repair with a caller's data. String CorrelationData { get; }; /// Whether a restart is required to complete the repair. Boolean RebootRequired { get; }; /// Batched error code, example APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED RepairResultStatus Status { get; }; /// The error code of the overall operation. HRESULT ExtendedErrorCode { get; }; /// The error code from the repair attempt. Only valid if the Status is RepairError. /// This value's meaning will require knowledge of the specific repairer or repair technology. UInt32 RepairerErrorCode { get; }; } /// State of the download [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 7)] enum PackageDownloadProgressState { /// The download is queued but not yet active. Cancellation of the IAsyncOperationWithProgress in this /// state will prevent the package from downloading. Queued, /// The installer is downloading. Cancellation of the IAsyncOperationWithProgress in this state will /// end the download. Downloading, /// The operation has completed. Finished, }; /// Status of the download call /// Implementation Note: Errors mapped from AppInstallerErrors.h [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 7)] enum DownloadResultStatus { Ok, BlockedByPolicy, CatalogError, InternalError, InvalidOptions, DownloadError, ManifestError, NoApplicableInstallers, PackageAgreementsNotAccepted, }; /// Result of the download [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 7)] runtimeclass DownloadResult { /// Used by a caller to correlate the download with a caller's data. String CorrelationData { get; }; /// Batched error code. DownloadResultStatus Status { get; }; /// The error code of the overall operation. HRESULT ExtendedErrorCode { get; }; }; /// Progress object for the uninstall [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 7)] struct PackageDownloadProgress { /// State of the download PackageDownloadProgressState State; /// DESIGN NOTE: BytesDownloaded may only be available for downloads done by Windows Package Manager itself. /// Number of bytes downloaded if known UInt64 BytesDownloaded; /// DESIGN NOTE: BytesRequired may only be available for downloads done by Windows Package Manager itself. /// Number of bytes required if known UInt64 BytesRequired; /// Download percentage completed Double DownloadProgress; }; /// IMPLEMENTATION NOTE: SourceOrigin from winget/RepositorySource.h /// Defines the origin of the package catalog details. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageCatalogOrigin { /// Predefined means it came as part of the Windows Package Manager package and cannot be removed. Predefined, /// User means it was added by the user and could be removed. User, }; /// IMPLEMENTATION NOTE: SourceTrustLevel from winget/RepositorySource.h /// Defines the trust level of the package catalog. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageCatalogTrustLevel { None, Trusted, }; /// IMPLEMENTATION NOTE: SourceDetails from winget/RepositorySource.h /// Interface for retrieving information about an package catalog without acting on it. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageCatalogInfo { /// The package catalog's unique identifier. /// SAMPLE VALUES: For OpenWindowsCatalog "Microsoft.Winget.Source_8wekyb3d8bbwe" /// For contoso sample on msdn "contoso" String Id { get; }; /// The name of the package catalog. /// SAMPLE VALUES: For OpenWindowsCatalog "winget". /// For contoso sample on msdn "contoso" String Name { get; }; /// The type of the package catalog. /// ALLOWED VALUES: "Microsoft.Rest", "Microsoft.PreIndexed.Package" /// SAMPLE VALUES: For OpenWindowsCatalog "Microsoft.PreIndexed.Package". /// For contoso sample on msdn "Microsoft.PreIndexed.Package" String Type { get; }; /// The argument used when adding the package catalog. /// SAMPLE VALUES: For OpenWindowsCatalog "https://winget.azureedge.net/cache" /// For contoso sample on msdn "https://pkgmgr-int.azureedge.net/cache" String Argument { get; }; /// The last time that this package catalog was updated. Windows.Foundation.DateTime LastUpdateTime { get; }; /// The origin of the package catalog. PackageCatalogOrigin Origin { get; }; /// The trust level of the package catalog PackageCatalogTrustLevel TrustLevel { get; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] { /// Excludes a source from discovery unless specified. Boolean Explicit{ get; }; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 29)] { /// The priority of this catalog. Higher values are sorted first. Int32 Priority{ get; }; } } /// A metadata item of a package version. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageVersionMetadataField { /// The InstallerType of an installed package InstallerType, /// The Scope of an installed package InstalledScope, /// The system path where the package is installed InstalledLocation, /// The standard uninstall command; which may be interactive StandardUninstallCommand, /// An uninstall command that should be non-interactive SilentUninstallCommand, /// The publisher of the package PublisherDisplayName, }; /// The result of a comparison. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 2)] enum CompareResult { /// The comparison did not result in a succesful ordering. Unknown, /// The object value is lesser than the given value. Lesser, /// The object value is equal to the given value. Equal, /// The object value is greater than the given value. Greater, }; /// IMPLEMENTATION NOTE: IPackageVersion from winget/RepositorySearch.h /// A single package version. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageVersionInfo { /// IMPLEMENTATION NOTE: PackageVersionMetadata fields from winget/RepositorySearch.h /// Gets any metadata associated with this package version. /// Primarily stores data on installed packages. /// Metadata fields may have no value (e.g. packages that aren't installed will not have an InstalledLocation). String GetMetadata(PackageVersionMetadataField metadataField); /// IMPLEMENTATION NOTE: PackageVersionProperty fields from winget/RepositorySearch.h String Id { get; }; String DisplayName { get; }; String Version { get; }; String Channel { get; }; /// DESIGN NOTE: RelativePath from winget/RepositorySearch.h is excluded as not needed. /// String RelativePath; /// IMPLEMENTATION NOTE: PackageVersionMultiProperty fields from winget/RepositorySearch.h /// PackageFamilyName and ProductCode can have multiple values. Windows.Foundation.Collections.IVectorView PackageFamilyNames { get; }; Windows.Foundation.Collections.IVectorView ProductCodes { get; }; /// Gets the package catalog where this package version is from. PackageCatalog PackageCatalog { get; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 2)] { /// Compares the given value against the package version of this object, with the result being /// the enum value that represents where PackageVersionInfo::Version is ordered relative to the /// versionString. "if (this.CompareToVersion(that) == Greater)" can be thought of as reading /// the sentence "If this is compared to version that and is found to be greater". /// IE if PackageVersionInfo::Version returns "2", then CompareToVersion("1") will return Greater. /// Passing in an empty string will result in Unknown. CompareResult CompareToVersion(String versionString); } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] { /// Checks if this package version has at least one applicable installer. Boolean HasApplicableInstaller(InstallOptions options); /// Gets the publisher string for this package version, if one is available. String Publisher { get; }; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] { /// Gets the package catalog metadata of this package version with the default localization based on user settings. CatalogPackageMetadata GetCatalogPackageMetadata(); /// Gets the package catalog metadata of this package version with the preferred locale. CatalogPackageMetadata GetCatalogPackageMetadata(String preferredLocale); /// Gets the applicable installer for this package version. PackageInstallerInfo GetApplicableInstaller(InstallOptions options); } } /// IMPLEMENTATION NOTE: PackageVersionKey from winget/RepositorySearch.h /// A key to identify a package version within a package. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageVersionId { /// The package catalog id that this version came from. String PackageCatalogId { get; }; /// The version. String Version { get; }; /// The channel. String Channel { get; }; }; /// The package installer type. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] enum PackageInstallerType { /// Unknown type. Unknown, /// Inno type. Inno, /// Wix type. Wix, /// Msi type. Msi, /// Nullsoft type. Nullsoft, /// Zip type. Zip, /// Msix or Appx type. Msix, /// Exe type. Exe, /// Burn type. Burn, /// MSStore type. MSStore, /// Portable type. Portable, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 13)] { /// Font type. Font, }, }; /// The package installer scope. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] enum PackageInstallerScope { /// Scope not declared. Unknown, /// User scope. User, /// System scope. System, }; /// The package installer elevation requirement. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] enum ElevationRequirement { /// Elevation requirement not declared. Unknown, /// Package installer requires elevation. ElevationRequired, /// Package installer prohibits elevation. ElevationProhibited, /// Package installer elevates self. ElevatesSelf, }; /// Interface for retrieving information about a package installer. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] runtimeclass PackageInstallerInfo { /// The package installer type. PackageInstallerType InstallerType { get; }; /// The nested package installer type for archives. PackageInstallerType NestedInstallerType { get; }; /// The package installer architecture. Windows.System.ProcessorArchitecture Architecture { get; }; /// The package installer scope. PackageInstallerScope Scope { get; }; /// The package installer locale. String Locale { get; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] { /// The package installer elevation requirement. ElevationRequirement ElevationRequirement { get; }; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] { /// Authentication info from the package installer. AuthenticationInfo AuthenticationInfo { get; }; } }; /// The installed status type. The values need to match InstalledStatusType from winget/RepositorySearch.h. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] [flags] enum InstalledStatusType { /// None is checked. None = 0x0, /// Check Apps and Features entry. AppsAndFeaturesEntry = 0x0001, /// Check Apps and Features entry install location if applicable. AppsAndFeaturesEntryInstallLocation = 0x0002, /// Check Apps and Features entry install location with installed files if applicable. AppsAndFeaturesEntryInstallLocationFile = 0x0004, /// Check default install location if applicable. DefaultInstallLocation = 0x0008, /// Check default install location with installed files if applicable. DefaultInstallLocationFile = 0x0010, /// Below are helper values for calling CheckInstalledStatus as input. /// AppsAndFeaturesEntry related checks AllAppsAndFeaturesEntryChecks = AppsAndFeaturesEntry | AppsAndFeaturesEntryInstallLocation | AppsAndFeaturesEntryInstallLocationFile, /// DefaultInstallLocation related checks AllDefaultInstallLocationChecks = DefaultInstallLocation | DefaultInstallLocationFile, /// All checks AllChecks = AllAppsAndFeaturesEntryChecks | AllDefaultInstallLocationChecks, }; /// Interface representing an individual installed status. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] runtimeclass InstalledStatus { /// The installed status type. InstalledStatusType Type { get; }; /// The installed status path. String Path { get; }; /// The installed status result. HRESULT Status { get; }; }; /// Interface for retrieving information about a package installer installed status. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] runtimeclass PackageInstallerInstalledStatus { /// The package installer info. PackageInstallerInfo InstallerInfo { get; }; /// A list of various types of installed status of the package installer. Windows.Foundation.Collections.IVectorView InstallerInstalledStatus { get; }; }; /// Status of the check installed status call. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] enum CheckInstalledStatusResultStatus { Ok, InternalError, }; /// Interface for retrieving information about a package installer installed status. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] runtimeclass CheckInstalledStatusResult { /// Status of the check installed status call. CheckInstalledStatusResultStatus Status { get; }; /// A list of package installer installed status. Windows.Foundation.Collections.IVectorView PackageInstalledStatus { get; }; }; /// IMPLEMENTATION NOTE: IPackage from winget/RepositorySearch.h /// A package, potentially containing information about it's local state and the available versions. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass CatalogPackage { /// IMPLEMENTATION NOTE: PackageProperty fields from winget/RepositorySearch.h /// Gets a property of this package. String Id { get; }; String Name { get; }; /// Gets the installed package information if the package is installed. PackageVersionInfo InstalledVersion { get; }; /// Gets all available versions of this package. Ordering is not guaranteed. Windows.Foundation.Collections.IVectorView AvailableVersions { get; }; /// Gets the version of this package that will be installed if version is not set in InstallOptions. PackageVersionInfo DefaultInstallVersion { get; }; /// Gets a specific version of this package. PackageVersionInfo GetPackageVersionInfo(PackageVersionId versionKey); /// Gets a value indicating whether an available version is newer than the installed version. Boolean IsUpdateAvailable { get; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] { /// Check the installed status of the package. For more accurate and complete installed status, it's required to /// call this method from a composite package from a newly created package catalog with installed info. /// This may require downloading information from a server. Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(InstalledStatusType checkTypes); CheckInstalledStatusResult CheckInstalledStatus(InstalledStatusType checkTypes); Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(); CheckInstalledStatusResult CheckInstalledStatus(); } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 29)] { /// Determines the priority of the catalog for this package object. /// This should match the priority of the DefaultInstallVersion, but it is much more efficient than using that route. /// May be null if the package refers only to an installed item. Windows.Foundation.IReference CatalogPriority { get; }; } } /// IMPLEMENTATION NOTE: CompositeSearchBehavior from winget/RepositorySource.h /// Search behavior for composite catalogs. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum CompositeSearchBehavior { /// Search local catalogs only LocalCatalogs, /// Search remote catalogs only, don't check local catalogs for InstalledVersion RemotePackagesFromRemoteCatalogs, /// Search remote catalogs, and check local catalogs for InstalledVersion RemotePackagesFromAllCatalogs, /// Search both local and remote catalogs. AllCatalogs, }; /// IMPLEMENTATION NOTE: PackageFieldMatchOption from winget/RepositorySearch.h [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageFieldMatchOption { Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive, }; /// IMPLEMENTATION NOTE: PackageFieldMatchOption from winget/RepositorySearch.h /// The field to match on. /// The values must be declared in order of preference in search results. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageMatchField { CatalogDefault, Id, Name, Moniker, Command, Tag, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 3)] { PackageFamilyName, ProductCode, } /// DESIGN NOTE: The following PackageFieldMatchOption from winget/RepositorySearch.h are not implemented in V1. /// NormalizedNameAndPublisher, }; /// IMPLEMENTATION NOTE: PackageMatchFilter from winget/RepositorySearch.h [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageMatchFilter { PackageMatchFilter(); /// The type of string comparison for matching PackageFieldMatchOption Option; /// The field to search PackageMatchField Field; /// The value to match String Value; /// DESIGN NOTE: "Additional" from RequestMatch winget/RepositorySearch.h is not implemented here. } /// IMPLEMENTATION NOTE: MatchResult from winget/RepositorySearch.h /// A single result from the search. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass MatchResult { /// The package found by the search request. CatalogPackage CatalogPackage { get; }; /// The highest order field on which the package matched the search. PackageMatchFilter MatchCriteria { get; }; } /// Status of the FindPackages call [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum FindPackagesResultStatus { Ok, BlockedByPolicy, CatalogError, InternalError, InvalidOptions, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 10)] { AuthenticationError, AccessDenied, } }; /// IMPLEMENTATION NOTE: SearchResult from winget/RepositorySearch.h /// Search result data returned from FindPackages [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass FindPackagesResult { /// Error codes FindPackagesResultStatus Status{ get; }; /// The full set of results from the search. Windows.Foundation.Collections.IVectorView Matches { get; }; /// If true, the results were truncated by the given ResultLimit /// USAGE NOTE: Windows Package Manager does not support result pagination, there is no way to continue /// getting more results. Boolean WasLimitExceeded { get; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] { /// The error code of the operation. HRESULT ExtendedErrorCode{ get; }; } } /// Options for FindPackages [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass FindPackagesOptions { FindPackagesOptions(); /// DESIGN NOTE: /// This class maps to SearchRequest from winget/RepositorySearch.h /// That class is a container for data used to filter the available manifests in an package catalog. /// Its properties can be thought of as: /// (Query || Inclusions...) && Filters... /// If Query and Inclusions are both empty, the starting data set will be the entire database. /// Everything && Filters... /// That has been translated in this api so that /// Inclusions are Selectors below /// Filters are Filters below /// Query is PackageFieldMatchOption::PackageCatalogDefined and in the Selector list. /// USAGE NOTE: Only one selector with PackageFieldMatchOption::PackageCatalogDefined is allowed. /// Selectors = you have to match at least one selector (if there are no selectors, then nothing is selected) Windows.Foundation.Collections.IVector Selectors { get; }; /// Filters = you have to match all filters(if there are no filters, then there is no filtering of selected items) Windows.Foundation.Collections.IVector Filters{ get; }; /// Restricts the length of the returned results to the specified count. UInt32 ResultLimit; } /// IMPLEMENTATION NOTE: Source from winget/RepositorySource.h /// A catalog for searching for packages [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageCatalog { /// Gets a value indicating whether this package catalog is a composite of other package catalogs, /// and thus the packages may come from disparate package catalogs as well. Boolean IsComposite { get; }; /// The details of the package catalog if it is not a composite. PackageCatalogInfo Info { get; }; /// Searches for Packages in the catalog. Windows.Foundation.IAsyncOperation FindPackagesAsync(FindPackagesOptions options); FindPackagesResult FindPackages(FindPackagesOptions options); } /// Authentication mode [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 10)] enum AuthenticationMode { /// Always use interactive authentication flow on first authentication request, following requests may use cached result. Interactive, /// Try silent authentication flow first. If failed, use interactive authentication flow. SilentPreferred, /// Only use silent authentication flow. If failed, fail the authentication. Silent, }; /// Authentication related arguments [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 10)] runtimeclass AuthenticationArguments { AuthenticationArguments(); /// Choice of authentication flow behavior. AuthenticationMode AuthenticationMode; /// Optional. The authentication account to be used for authentication. String AuthenticationAccount; } /// Authentication method [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 10)] enum AuthenticationType { Unknown, None, MicrosoftEntraId, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] { MicrosoftEntraIdForAzureBlobStorage, } }; /// Microsoft Entra Id related authentication info. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 10)] runtimeclass MicrosoftEntraIdAuthenticationInfo { /// The resource identifier or resource uri. String Resource { get; }; /// Requested scope. May be empty. String Scope { get; }; } /// Authentication info. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 10)] runtimeclass AuthenticationInfo { /// The authentication type. AuthenticationType AuthenticationType { get; }; /// Microsoft Entra Id related authentication info. MicrosoftEntraIdAuthenticationInfo MicrosoftEntraIdAuthenticationInfo { get; }; } /// Status of the Connect call [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum ConnectResultStatus { Ok, CatalogError, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] { SourceAgreementsNotAccepted, } }; /// Result of the Connect call [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass ConnectResult { /// Error codes ConnectResultStatus Status { get; }; PackageCatalog PackageCatalog { get; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] { /// The error code of the operation. HRESULT ExtendedErrorCode{ get; }; } } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] enum RefreshPackageCatalogStatus { Ok, GroupPolicyError, CatalogError, InternalError, }; /// IMPLEMENTATION NOTE: RefreshPackageCatalogResult [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] runtimeclass RefreshPackageCatalogResult { RefreshPackageCatalogStatus Status { get; }; /// Error codes HRESULT ExtendedErrorCode { get; }; }; /// A reference to a catalog that callers can try to Connect. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageCatalogReference { /// Gets a value indicating whether this package catalog is a composite of other package catalogs, /// and thus the packages may come from disparate package catalogs as well. Boolean IsComposite { get; }; /// The details of the package catalog if it is not a composite. PackageCatalogInfo Info { get; }; /// Opens a catalog. Required before searching. For remote catalogs (i.e. not Installed and Installing) this /// may require downloading information from a server. Windows.Foundation.IAsyncOperation ConnectAsync(); ConnectResult Connect(); [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 2)] { /// A string that will be passed to the source server if using a REST source String AdditionalPackageCatalogArguments; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] { /// Gets the required agreements for connecting to the package catalog (source). Windows.Foundation.Collections.IVectorView SourceAgreements { get; }; Boolean AcceptSourceAgreements; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 8)] { /// Time interval for package catalog to check for an update. Setting to zero will disable the check for update. Windows.Foundation.TimeSpan PackageCatalogBackgroundUpdateInterval; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 9)] { /// When set to true, the opened catalog will only provide the information regarding packages installed from this catalog. /// In this mode, no external resources should be required. Boolean InstalledPackageInformationOnly; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 10)] { /// Authentication arguments used in authentication flow during package catalog operations if applicable. /// This is user or caller input. AuthenticationArguments AuthenticationArguments; /// Authentication info from the package catalog. /// This is defined by individual package catalog. AuthenticationInfo AuthenticationInfo { get; }; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] { /// Updates the package catalog. /// The progress value, represented as a double, indicates the percentage of update package catalog operation completion. /// The progress range is from 0 to 100. Windows.Foundation.IAsyncOperationWithProgress RefreshPackageCatalogAsync(); } } /// Catalogs with PackageCatalogOrigin Predefined [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PredefinedPackageCatalog { OpenWindowsCatalog, MicrosoftStore, DesktopFrameworks, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 13)] { OpenWindowsCatalogFont, }, }; /// Local Catalogs with PackageCatalogOrigin Predefined [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum LocalPackageCatalog { InstalledPackages, InstallingPackages }; /// Options for creating a composite catalog. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass CreateCompositePackageCatalogOptions { CreateCompositePackageCatalogOptions(); /// Create a composite catalog to allow searching a user defined or pre defined source /// and a local source (Installed packages) together IVector Catalogs { get; }; /// Sets the default search behavior if the catalog is a composite catalog. CompositeSearchBehavior CompositeSearchBehavior; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] { /// Create installed package catalog with required installed scope. PackageInstallScope InstalledScope; } } /// Required install scope for the package. If the package does not have an installer that /// supports the specified scope the Install call will fail with InstallResultStatus.NoApplicableInstallers [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageInstallScope { /// An installer with any install scope is valid. Any, /// Only User install scope installers are valid User, /// Only System installers will be valid System, [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] { /// Both User and Unknown install scope installers are valid UserOrUnknown, /// Both System and Unknown install scope installers are valid SystemOrUnknown, } }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] enum PackageInstallMode { /// The default experience for the installer. Installer may show some UI. Default, /// Runs the installer in silent mode. This suppresses the installer's UI to the extent /// possible (installer may still show some required UI). Silent, /// Runs the installer in interactive mode. Interactive, }; /// Options when installing a package. /// Intended to allow full compatibility with the "winget install" command line interface. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass InstallOptions { InstallOptions(); /// Optionally specifies the version from the package to install. If unspecified, the CatalogPackage.DefaultInstallVersion /// version is used. DefaultInstallVersion is the latest applicable version of the package. DefaultInstallVersion may be /// empty if there's no applicable version. In that case, install attempts without setting this PackageVersionId /// will return No Applicable Installer error code. PackageVersionId PackageVersionId; /// Specifies alternate location to install package (if supported). String PreferredInstallLocation; /// User or Machine. PackageInstallScope PackageInstallScope; /// Silent, Interactive, or Default PackageInstallMode PackageInstallMode; /// Directs the logging to a log file. If provided, the installer must have write access to the file String LogOutputPath; /// Continues the install even if the hash in the catalog does not match the linked installer. Boolean AllowHashMismatch; /// A string that will be passed to the installer. /// IMPLEMENTATION NOTE: maps to "--override" in the winget cmd line String ReplacementInstallerArguments; /// Used by a caller to correlate the install with a caller's data. /// The string must be JSON encoded. String CorrelationData; /// A string that will be passed to the source server if using a REST source String AdditionalPackageCatalogArguments; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 2)] { /// The set of allowed Architectures, in preference order, that will be considered for /// the install operation. Initially the vector contains the default allowed architectures /// in the default preference order for the current system. It is allowed to have repeated /// values in the list, to make prepending a preference override easier. Instances of an /// architecture after the first will simply be ignored. Windows.Foundation.Collections.IVector AllowedArchitectures { get; }; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] { /// Allow the upgrade to continue for upgrade packages with manifest versions Unknown. Boolean AllowUpgradeToUnknownVersion; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] { /// Force the operation to continue upon non security related failures. Boolean Force; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] { /// A string that will be passed to the installer /// IMPLEMENTATION NOTE: maps to "--custom" in the winget cmd line String AdditionalInstallerArguments; /// Accept the package agreements required for installation. Boolean AcceptPackageAgreements; /// Bypasses the Disabled Store Policy Boolean BypassIsStoreClientBlockedPolicyCheck; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 7)] { /// Skip installing the dependencies for the package. Boolean SkipDependencies; /// The package installer type. PackageInstallerType InstallerType; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] { /// Authentication arguments used when downloading the package installer if authentication is required. AuthenticationArguments AuthenticationArguments; } } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] enum PackageUninstallMode { /// The default experience for the installer. Installer may show some UI. Default, /// Runs the installer in silent mode. This suppresses the installer's UI to the extent /// possible (installer may still show some required UI). Silent, /// Runs the installer in interactive mode. Interactive, }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] enum PackageUninstallScope { /// Use default uninstall behavior. Any, /// Uninstall for current user. Currently only applicable to msix. User, /// Uninstall for all users. Currently only applicable to msix. System, }; /// Options when uninstalling a package. /// Intended to allow full compatibility with the "winget uninstall" command line interface. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] runtimeclass UninstallOptions { UninstallOptions(); /// This property is not currently used. The version of CatalogPackage.InstalledVersion is used for uninstall. PackageVersionId PackageVersionId; /// Silent, Interactive, or Default PackageUninstallMode PackageUninstallMode; /// Directs the logging to a log file. If provided, the installer must have write access to the file String LogOutputPath; /// Used by a caller to correlate the install with a caller's data. /// The string must be JSON encoded. String CorrelationData; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] { /// Force the operation to continue upon non security related failures. Boolean Force; // The scope the uninstall will perform. Currently only applicable to msix. PackageUninstallScope PackageUninstallScope; } } /// The Windows platform type. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 13)] enum WindowsPlatform { /// An unknown platform Unknown, /// Windows.Universal Universal, /// Windows.Desktop Desktop, /// Windows.IoT IoT, /// Windows.Team Team, /// Windows.Holographic Holographic, }; /// Options when downloading a package. /// Intended to allow full compatibility with the "winget download" command line interface. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 7)] runtimeclass DownloadOptions { DownloadOptions(); /// Optionally specifies the version from the package to download. If unspecified the version matching /// CatalogPackage.GetLatestVersion() is used. PackageVersionId PackageVersionId; /// The package installer type. PackageInstallerType InstallerType; /// The package installer scope. PackageInstallScope Scope; /// The package installer architecture. Windows.System.ProcessorArchitecture Architecture; /// The package installer locale. String Locale; /// The directory where the installers are downloaded to. String DownloadDirectory; /// Continues the download even if the hash in the catalog does not match the linked installer. Boolean AllowHashMismatch; /// Skip downloading the dependencies for the package. Boolean SkipDependencies; /// Accept the package agreements required for download. Boolean AcceptPackageAgreements; /// Used by a caller to correlate the download with a caller's data. /// The string must be JSON encoded. String CorrelationData; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] { /// Authentication arguments used when downloading the package installer if authentication is required. AuthenticationArguments AuthenticationArguments; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 13)] { /// If the package is licensed from the Microsoft Store, setting this value to true will not attempt to download the license file. Boolean SkipMicrosoftStoreLicense; /// The platform to download the package for. WindowsPlatform Platform; /// When applicable, uses the provided value as the target OS version for the download. String TargetOSVersion; } } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] enum PackageRepairMode { /// The default experience for the installer. Installer may show some UI. Default, /// Runs the installer in silent mode. This suppresses the installer's UI to the extent /// possible (installer may still show some required UI). Silent, /// Runs the installer in interactive mode. Interactive, }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] enum PackageRepairScope { /// Use default repair behavior. Any, /// Repair for current user. Currently only applicable to msix. User, /// Repair for all users. System, }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] runtimeclass RepairOptions { RepairOptions(); /// This property is not currently used. The version of CatalogPackage.InstalledVersion is used for repair. PackageVersionId PackageVersionId; /// The package Repair scope. PackageRepairScope PackageRepairScope; /// The package repair mode. PackageRepairMode PackageRepairMode; /// Optional parameter specifying Accept the package agreements required for download. Boolean AcceptPackageAgreements; /// Used by a caller to correlate the repair with a caller's data. /// The string must be JSON encoded. String CorrelationData; /// Continues the download even if the hash in the catalog does not match the linked installer used for repair. Boolean AllowHashMismatch; /// Directs the logging to a log file. If provided, the installer must have write access to the file String LogOutputPath; /// Force the operation to continue upon non security related failures. Boolean Force; /// Bypasses the Disabled Store Policy Boolean BypassIsStoreClientBlockedPolicyCheck; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] { /// Authentication arguments used when downloading the package installer if authentication is required. AuthenticationArguments AuthenticationArguments; } } /// IMPLEMENTATION NOTE: Documentation from AppInstaller::Manifest::Documentation [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] runtimeclass Documentation { String DocumentLabel { get; }; String DocumentUrl { get; }; } /// Icon resolution [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] enum IconResolution { Custom, Square16, Square20, Square24, Square30, Square32, Square36, Square40, Square48, Square60, Square64, Square72, Square80, Square96, Square256, }; /// Icon file type [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] enum IconFileType { Unknown, Jpeg, Png, Ico, }; /// Icon theme [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] enum IconTheme { Unknown, Default, Light, Dark, HighContrast, }; /// IMPLEMENTATION NOTE: Icon from AppInstaller::Manifest::Icon [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] runtimeclass Icon { String Url { get; }; IconFileType FileType{ get; }; IconResolution Resolution{ get; }; IconTheme Theme{ get; }; UInt8[] Sha256 { get; }; } /// IMPLEMENTATION NOTE: SourceAgreement from AppInstaller::Manifest::SourceAgreement [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] runtimeclass SourceAgreement { String Label { get; }; String Text { get; }; String Url { get; }; } /// IMPLEMENTATION NOTE: PackageAgreement from AppInstaller::Manifest::Agreement [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] runtimeclass PackageAgreement { String Label { get; }; String Text { get; }; String Url { get; }; } /// IMPLEMENTATION NOTE: CatalogPackageMetadata from AppInstaller::Manifest::Localization [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 6)] runtimeclass CatalogPackageMetadata { String Locale { get; }; String Publisher { get; }; String PublisherUrl { get; }; String PublisherSupportUrl { get; }; String PrivacyUrl { get; }; String Author { get; }; String PackageName { get; }; String PackageUrl { get; }; String License { get; }; String LicenseUrl { get; }; String Copyright { get; }; String CopyrightUrl { get; }; String ShortDescription { get; }; String Description { get; }; Windows.Foundation.Collections.IVectorView Tags { get; }; Windows.Foundation.Collections.IVectorView Agreements { get; }; Windows.Foundation.Collections.IVectorView Documentations { get; }; Windows.Foundation.Collections.IVectorView Icons { get; }; String ReleaseNotes { get; }; String ReleaseNotesUrl { get; }; String PurchaseUrl { get; }; String InstallationNotes { get; }; } /// IMPLEMENTATION NOTE: AddPackageCatalogOptions [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] runtimeclass AddPackageCatalogOptions { AddPackageCatalogOptions(); /// The name of the package catalog. /// SAMPLE VALUES: For OpenWindowsCatalog "winget". /// For contoso sample on msdn "contoso" String Name; /// The SourceUri used when adding the package catalog. /// SAMPLE VALUES: For OpenWindowsCatalog "https://winget.azureedge.net/cache" /// For contoso sample on msdn "https://pkgmgr-int.azureedge.net/cache" String SourceUri; /// ALLOWED VALUES: "Microsoft.Rest", "Microsoft.PreIndexed.Package" /// SAMPLE VALUES: For OpenWindowsCatalog "Microsoft.PreIndexed.Package". /// For contoso sample on msdn "Microsoft.PreIndexed.Package" String Type; /// The trust level of the catalog to add. PackageCatalogTrustLevel TrustLevel; /// Custom header to pass to the catalog. String CustomHeader; /// Excludes a source from discovery unless specified. Boolean Explicit; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 29)] { /// The priority of this catalog. Higher values are sorted first. Int32 Priority; } }; /// IMPLEMENTATION NOTE: AddPackageCatalogStatus [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] enum AddPackageCatalogStatus { Ok, GroupPolicyError, CatalogError, InternalError, InvalidOptions, AccessDenied, AuthenticationError, }; /// IMPLEMENTATION NOTE: AddPackageCatalogResult [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] runtimeclass AddPackageCatalogResult { AddPackageCatalogStatus Status { get; }; /// Error codes HRESULT ExtendedErrorCode { get; }; }; /// IMPLEMENTATION NOTE: RemovePackageCatalogOptions [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] runtimeclass RemovePackageCatalogOptions { RemovePackageCatalogOptions(); /// The name of the package catalog. /// SAMPLE VALUES: For OpenWindowsCatalog "winget". /// For contoso sample on msdn "contoso" String Name; /// By default, the value is 'false', resulting in the removal of the package catalog registration /// from the winget Package catalogs list and the deletion of all associated system artifacts. This /// mirrors the WinGet Source remove operation on a specific Package Catalog. /// If set to 'true', it removes the package catalog registration from the Windows Package Catalogs /// list without any cleanup, similar to the WinGet source reset operation on a specific Package /// Catalog. Boolean PreserveData; }; /// IMPLEMENTATION NOTE: RemovePackageCatalogStatus [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] enum RemovePackageCatalogStatus { Ok, GroupPolicyError, CatalogError, InternalError, AccessDenied, InvalidOptions, }; /// IMPLEMENTATION NOTE: RemovePackageCatalogResult /// Result of removing a package catalog. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] runtimeclass RemovePackageCatalogResult { RemovePackageCatalogStatus Status { get; }; /// Error codes HRESULT ExtendedErrorCode { get; }; }; /// IMPLEMENTATION NOTE: EditPackageCatalogOptions [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 28)] runtimeclass EditPackageCatalogOptions { EditPackageCatalogOptions(); /// The name of the package catalog. /// SAMPLE VALUES: For OpenWindowsCatalog "winget". /// For contoso sample on msdn "contoso" String Name; /// Editing the Explicit property has three states: true, false, and not specified (null). Windows.Foundation.IReference Explicit; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 29)] { /// The priority of this catalog. Higher values are sorted first. Windows.Foundation.IReference Priority; } }; /// IMPLEMENTATION NOTE: RemovePackageCatalogStatus [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 28)] enum EditPackageCatalogStatus { Ok, GroupPolicyError, CatalogError, InternalError, AccessDenied, InvalidOptions, }; /// IMPLEMENTATION NOTE: RemovePackageCatalogResult /// Result of editing a package catalog. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 28)] runtimeclass EditPackageCatalogResult { EditPackageCatalogStatus Status { get; }; /// Error codes HRESULT ExtendedErrorCode { get; }; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] runtimeclass PackageManager { PackageManager(); /// Get the available catalogs. Each source will have a separate catalog. /// This does not open the catalog. These catalogs can be used individually or merged with CreateCompositePackageCatalogAsync. /// IMPLEMENTATION NOTE: This is a list of sources returned by Windows Package Manager source list Windows.Foundation.Collections.IVectorView GetPackageCatalogs(); /// Get a built in catalog PackageCatalogReference GetPredefinedPackageCatalog(PredefinedPackageCatalog predefinedPackageCatalog); /// Get a built in catalog PackageCatalogReference GetLocalPackageCatalog(LocalPackageCatalog localPackageCatalog); /// Get a catalog by a known name PackageCatalogReference GetPackageCatalogByName(String catalogName); /// Get a composite catalog to allow searching a user defined or pre defined source and a local source /// (Installing, Installed) together at the same time. PackageCatalogReference CreateCompositePackageCatalog(CreateCompositePackageCatalogOptions options); [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 12)] { /// Add a catalog to the Windows Package Catalogs. /// The progress value, represented as a double, indicates the percentage of add package catalog operation completion. /// The progress range is from 0 to 100. Windows.Foundation.IAsyncOperationWithProgress AddPackageCatalogAsync(AddPackageCatalogOptions options); /// Unregisters a Package Catalog from the Windows Package Catalogs and eliminates the system artifacts based on the provided options. /// The progress value, represented as a double, indicates the percentage of remove package catalog operation completion. /// The progress range is from 0 to 100. Windows.Foundation.IAsyncOperationWithProgress RemovePackageCatalogAsync(RemovePackageCatalogOptions options); } /// Install the specified package Windows.Foundation.IAsyncOperationWithProgress InstallPackageAsync(CatalogPackage package, InstallOptions options); [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 2)] { /// Get install progress Windows.Foundation.IAsyncOperationWithProgress GetInstallProgress(CatalogPackage package, PackageCatalogInfo catalogInfo); } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] { /// Upgrade the specified package Windows.Foundation.IAsyncOperationWithProgress UpgradePackageAsync(CatalogPackage package, InstallOptions options); /// Uninstall the specified package Windows.Foundation.IAsyncOperationWithProgress UninstallPackageAsync(CatalogPackage package, UninstallOptions options); /// Get uninstall progress Windows.Foundation.IAsyncOperationWithProgress GetUninstallProgress(CatalogPackage package, PackageCatalogInfo catalogInfo); } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 7)] { // Download the specified package Windows.Foundation.IAsyncOperationWithProgress DownloadPackageAsync(CatalogPackage package, DownloadOptions options); // Get download progress Windows.Foundation.IAsyncOperationWithProgress GetDownloadProgress(CatalogPackage package, PackageCatalogInfo catalogInfo); } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 11)] { // Repair the specified package Windows.Foundation.IAsyncOperationWithProgress RepairPackageAsync(CatalogPackage package, RepairOptions options); } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 13)] { // The version of the Windows Package Manager that is running. String Version{ get; }; } [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 28)] { /// Edit an existing Windows Package Catalog. EditPackageCatalogResult EditPackageCatalog(EditPackageCatalogOptions options); } } /// Global settings for PackageManager operations. /// This settings should be invoked prior to invocation of PackageManager class. /// This settings is only exposed in in-proc Com invocation. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] runtimeclass PackageManagerSettings { PackageManagerSettings(); /// Sets caller name to be used in telemetry logging. Default value is the calling process name. /// Call this before any PackageManager operations. /// Returns true if successful, false if caller name is already set. /// This is a one time setup, multiple calls will not override existing caller name. Boolean SetCallerIdentifier(String callerIdentifier); /// Sets state name for state separation. If not set, state will be written in a default location and states may be affected by other callers. /// Call this before any PackageManager operations. /// Returns true if successful, false if state name is already set. /// This is a one time setup, multiple calls will not override existing state name. Boolean SetStateIdentifier(String stateIdentifier); /// Sets custom UserSettings. /// Returns true if successful, false if settingsContent cannot be parsed or UserSettings is already created. /// This is a one time setup, multiple calls will not override existing UserSettings. Boolean SetUserSettings(String settingsContent); [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 28)] { /// Gets or sets a value indicating whether the caller would prefer the module to stay loaded or not. /// This affects how the DllCanUnloadNow function called by COM behaves. If set to false it will act as if /// there are active objects at all times. If set to true it will allow the unload when there are no /// active objects. /// Defaults to true. Boolean CanUnloadPreference{ get; set; }; /// Gets or sets a value indicating whether the module should listen for termination signals (CTRL+C, window messages, package updates) /// and begin the process of cancelling active operations and preventing new ones. /// If set to false, the caller is responsible for handling these termination signals and cancelling active operations as necessary. /// Set this to the desired state before any PackageManager operations. Changing it after the first operation for the process may have undefined behavior. /// Defaults to true. Boolean TerminationSignalMonitoring{ get; set; }; } } /// Force midl3 to generate vector marshalling info. declare { interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageManagerSettings.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "PackageManager.h" #include "PackageManagerSettings.h" #pragma warning( pop ) #include "PackageManagerSettings.g.cpp" #include "Helpers.h" #include "Public/CanUnload.h" #include "Public/ShutdownMonitoring.h" #include #include namespace winrt::Microsoft::Management::Deployment::implementation { bool PackageManagerSettings::SetCallerIdentifier(hstring const& callerIdentifier) { bool success = false; static std::once_flag setCallerOnceFlag; std::call_once(setCallerOnceFlag, [&]() { SetComCallerName(AppInstaller::Utility::ConvertToUTF8(callerIdentifier)); success = true; }); return success; } bool PackageManagerSettings::SetStateIdentifier(hstring const& stateIdentifier) { bool success = false; static std::once_flag setStateOnceFlag; std::call_once(setStateOnceFlag, [&]() { AppInstaller::Runtime::SetRuntimePathStateName(AppInstaller::Utility::ConvertToUTF8(stateIdentifier)); success = true; }); return success; } bool PackageManagerSettings::SetUserSettings(hstring const& settingsContent) { bool success = false; static std::once_flag setSettingsOnceFlag; std::call_once(setSettingsOnceFlag, [&]() { success = AppInstaller::Settings::TryInitializeCustomUserSettings(AppInstaller::Utility::ConvertToUTF8(settingsContent)); if (success) { AppInstaller::Logging::Log().SetEnabledChannels(AppInstaller::Settings::User().Get()); AppInstaller::Logging::Log().SetLevel(AppInstaller::Settings::User().Get()); } }); return success; } bool PackageManagerSettings::CanUnloadPreference() const { return GetCanUnload(); } void PackageManagerSettings::CanUnloadPreference(bool value) { SetCanUnload(value); } bool PackageManagerSettings::TerminationSignalMonitoring() const { return AppInstaller::ShutdownMonitoring::TerminationSignalHandler::Enabled(); } void PackageManagerSettings::TerminationSignalMonitoring(bool value) { AppInstaller::ShutdownMonitoring::TerminationSignalHandler::Enabled(value); } CoCreatableMicrosoftManagementDeploymentClass(PackageManagerSettings); } ================================================ FILE: src/Microsoft.Management.Deployment/PackageManagerSettings.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageManagerSettings.g.h" #include "Public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_INPROC_ONLY_COM_CLSID_PackageManagerSettings)] struct PackageManagerSettings : PackageManagerSettingsT { PackageManagerSettings() = default; // Contract 4.0 bool SetCallerIdentifier(hstring const& callerIdentifier); bool SetStateIdentifier(hstring const& stateIdentifier); bool SetUserSettings(hstring const& settingsContent); // Contract 28 bool CanUnloadPreference() const; void CanUnloadPreference(bool value); bool TerminationSignalMonitoring() const; void TerminationSignalMonitoring(bool value); }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct PackageManagerSettings : PackageManagerSettingsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/PackageMatchFilter.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "Workflows/WorkflowBase.h" #include "Converters.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "PackageMatchFilter.h" #pragma warning( pop ) #include "PackageMatchFilter.g.cpp" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { void PackageMatchFilter::Initialize(::AppInstaller::Repository::PackageMatchFilter matchFilter) { m_value = winrt::to_hstring(matchFilter.Value); m_matchField = GetDeploymentMatchField(matchFilter.Field); m_packageFieldMatchOption = GetDeploymentMatchOption(matchFilter.Type); } winrt::Microsoft::Management::Deployment::PackageFieldMatchOption PackageMatchFilter::Option() { return m_packageFieldMatchOption; } void PackageMatchFilter::Option(winrt::Microsoft::Management::Deployment::PackageFieldMatchOption const& value) { m_packageFieldMatchOption = value; } winrt::Microsoft::Management::Deployment::PackageMatchField PackageMatchFilter::Field() { return m_matchField; } void PackageMatchFilter::Field(winrt::Microsoft::Management::Deployment::PackageMatchField const& value) { m_matchField = value; } hstring PackageMatchFilter::Value() { return hstring(m_value); } void PackageMatchFilter::Value(hstring const& value) { m_value = value; } CoCreatableMicrosoftManagementDeploymentClass(PackageMatchFilter); } ================================================ FILE: src/Microsoft.Management.Deployment/PackageMatchFilter.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageMatchFilter.g.h" #include "Public/ComClsids.h" #include #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace AppInstaller::Repository { struct PackageMatchFilter; } #endif namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_PackageMatchFilter)] struct PackageMatchFilter : PackageMatchFilterT { PackageMatchFilter() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Repository::PackageMatchFilter matchFilter); #endif winrt::Microsoft::Management::Deployment::PackageFieldMatchOption Option(); void Option(winrt::Microsoft::Management::Deployment::PackageFieldMatchOption const& value); winrt::Microsoft::Management::Deployment::PackageMatchField Field(); void Field(winrt::Microsoft::Management::Deployment::PackageMatchField const& value); hstring Value(); void Value(hstring const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: hstring m_value; winrt::Microsoft::Management::Deployment::PackageMatchField m_matchField = winrt::Microsoft::Management::Deployment::PackageMatchField::CatalogDefault; winrt::Microsoft::Management::Deployment::PackageFieldMatchOption m_packageFieldMatchOption = winrt::Microsoft::Management::Deployment::PackageFieldMatchOption::Equals; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct PackageMatchFilter : PackageMatchFilterT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/PackageVersionId.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "PackageVersionId.h" #include "PackageVersionId.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void PackageVersionId::Initialize(::AppInstaller::Repository::PackageVersionKey packageVersionKey) { m_packageVersionKey = packageVersionKey; } hstring PackageVersionId::PackageCatalogId() { return winrt::to_hstring(m_packageVersionKey.SourceId); } hstring PackageVersionId::Version() { return winrt::to_hstring(m_packageVersionKey.Version); } hstring PackageVersionId::Channel() { return winrt::to_hstring(m_packageVersionKey.Channel); } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageVersionId.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageVersionId.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct PackageVersionId : PackageVersionIdT { PackageVersionId() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Repository::PackageVersionKey packageVersionKey); #endif hstring PackageCatalogId(); hstring Version(); hstring Channel(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Repository::PackageVersionKey m_packageVersionKey{}; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/PackageVersionInfo.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include #include "PackageVersionInfo.h" #include "PackageVersionInfo.g.cpp" #include "PackageCatalogInfo.h" #include "PackageCatalog.h" #include "PackageInstallerInfo.h" #include "CatalogPackage.h" #include "CatalogPackageMetadata.h" #include "ComContext.h" #include "Workflows/WorkflowBase.h" #include #include "winget/RepositorySearch.h" #include "AppInstallerVersions.h" #include "Converters.h" #pragma warning( push ) #pragma warning ( disable : 4467 ) // 4467 Allow use of uuid attribute for com object creation. #include "PackageManager.h" #pragma warning( pop ) #include #include namespace winrt::Microsoft::Management::Deployment::implementation { namespace { // Do the same thing as PopulateContextFromInstallOptions but without all the string conversions. AppInstaller::Manifest::ManifestComparator::Options GetComparatorOptionsFromInstallOptions(InstallOptions options) { AppInstaller::Manifest::ManifestComparator::Options result; auto installerType = GetManifestInstallerType(options.InstallerType()); if (installerType != AppInstaller::Manifest::InstallerTypeEnum::Unknown) { result.RequestedInstallerType = installerType; } auto manifestScope = GetManifestScope(options.PackageInstallScope()); if (manifestScope.first != ::AppInstaller::Manifest::ScopeEnum::Unknown) { result.RequestedInstallerScope = manifestScope.first; result.AllowUnknownScope = manifestScope.second; } if (options.AllowedArchitectures().Size() != 0) { for (auto architecture : options.AllowedArchitectures()) { auto convertedArchitecture = GetUtilityArchitecture(architecture); if (convertedArchitecture) { result.AllowedArchitectures.push_back(convertedArchitecture.value()); } } } return result; } } void PackageVersionInfo::Initialize(std::shared_ptr<::AppInstaller::Repository::IPackageVersion> packageVersion) { m_packageVersion = std::move(packageVersion); } std::shared_ptr<::AppInstaller::Repository::IPackageVersion> PackageVersionInfo::GetRepositoryPackageVersion() { return m_packageVersion; } hstring PackageVersionInfo::GetMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField const& metadataField) { ::AppInstaller::Repository::PackageVersionMetadata metadataKey = GetRepositoryPackageVersionMetadata(metadataField); ::AppInstaller::Repository::IPackageVersion::Metadata metadata = m_packageVersion->GetMetadata(); auto result = metadata.find(metadataKey); if (result == metadata.end()) { return {}; } hstring resultString = winrt::to_hstring(result->second); // The api uses "System" rather than "Machine" for install scope. if (metadataField == PackageVersionMetadataField::InstalledScope && resultString == L"Machine") { return winrt::to_hstring(L"System"); } return resultString; } hstring PackageVersionInfo::Id() { return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Id).get()); } hstring PackageVersionInfo::DisplayName() { return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Name).get()); } hstring PackageVersionInfo::Publisher() { return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Publisher).get()); } hstring PackageVersionInfo::Version() { return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Version).get()); } hstring PackageVersionInfo::Channel() { return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Channel).get()); } winrt::Windows::Foundation::Collections::IVectorView PackageVersionInfo::PackageFamilyNames() { if (!m_packageFamilyNames) { // Vector hasn't been created yet, create and populate it. auto packageFamilyNames = winrt::single_threaded_vector(); for (auto&& string : m_packageVersion->GetMultiProperty(::AppInstaller::Repository::PackageVersionMultiProperty::PackageFamilyName)) { packageFamilyNames.Append(winrt::to_hstring(string)); } m_packageFamilyNames = packageFamilyNames; } return m_packageFamilyNames.GetView(); } winrt::Windows::Foundation::Collections::IVectorView PackageVersionInfo::ProductCodes() { if (!m_productCodes) { // Vector hasn't been created yet, create and populate it. auto productCodes = winrt::single_threaded_vector(); for (auto&& string : m_packageVersion->GetMultiProperty(::AppInstaller::Repository::PackageVersionMultiProperty::ProductCode)) { productCodes.Append(winrt::to_hstring(string)); } m_productCodes = productCodes; } return m_productCodes.GetView(); } winrt::Microsoft::Management::Deployment::PackageCatalog PackageVersionInfo::PackageCatalog() { if (!m_packageCatalog) { auto packageCatalogInfo = winrt::make_self>(); packageCatalogInfo->Initialize(m_packageVersion->GetSource().GetDetails()); auto packageCatalog = winrt::make_self>(); packageCatalog->Initialize(*packageCatalogInfo, m_packageVersion->GetSource(), false); m_packageCatalog = *packageCatalog; } return m_packageCatalog; } winrt::Microsoft::Management::Deployment::CompareResult PackageVersionInfo::CompareToVersion(const hstring& versionString) { if (versionString.empty()) { return CompareResult::Unknown; } AppInstaller::Utility::Version thisVersion{ m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Version).get() }; AppInstaller::Utility::Version otherVersion{ AppInstaller::Utility::ConvertToUTF8(versionString) }; if (thisVersion < otherVersion) { return CompareResult::Lesser; } else if (otherVersion < thisVersion) { return CompareResult::Greater; } else { return CompareResult::Equal; } } bool PackageVersionInfo::HasApplicableInstaller(InstallOptions options) { AppInstaller::Manifest::ManifestComparator manifestComparator{ GetComparatorOptionsFromInstallOptions(options) }; AppInstaller::Manifest::Manifest manifest = m_packageVersion->GetManifest(); auto result = manifestComparator.GetPreferredInstaller(manifest); return result.installer.has_value(); } winrt::Microsoft::Management::Deployment::PackageInstallerInfo PackageVersionInfo::GetApplicableInstaller(InstallOptions options) { AppInstaller::Manifest::ManifestComparator manifestComparator{ GetComparatorOptionsFromInstallOptions(options) }; AppInstaller::Manifest::Manifest manifest = m_packageVersion->GetManifest(); auto result = manifestComparator.GetPreferredInstaller(manifest); if (result.installer.has_value()) { auto packageInstallerInfo = winrt::make_self>(); packageInstallerInfo->Initialize(result.installer.value()); return *packageInstallerInfo; } else { return nullptr; } } Microsoft::Management::Deployment::CatalogPackageMetadata PackageVersionInfo::GetCatalogPackageMetadata() { auto catalogPackageMetadata = winrt::make_self>(); if (m_packageVersion) { auto manifest = m_packageVersion->GetManifest(); manifest.ApplyLocale(); catalogPackageMetadata->Initialize(manifest.CurrentLocalization); } return *catalogPackageMetadata; } Microsoft::Management::Deployment::CatalogPackageMetadata PackageVersionInfo::GetCatalogPackageMetadata(const hstring& preferredLocale) { std::string localeString = winrt::to_string(preferredLocale); if (!::AppInstaller::Locale::IsWellFormedBcp47Tag(localeString)) { throw hresult_invalid_argument(); } auto catalogPackageMetadata = winrt::make_self>(); if (m_packageVersion) { auto manifest = m_packageVersion->GetManifest(); manifest.ApplyLocale(localeString); catalogPackageMetadata->Initialize(manifest.CurrentLocalization); } return *catalogPackageMetadata; } } ================================================ FILE: src/Microsoft.Management.Deployment/PackageVersionInfo.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "PackageVersionInfo.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct PackageVersionInfo : PackageVersionInfoT { PackageVersionInfo() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(std::shared_ptr<::AppInstaller::Repository::IPackageVersion> packageVersion); std::shared_ptr<::AppInstaller::Repository::IPackageVersion> GetRepositoryPackageVersion(); #endif hstring GetMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField const& metadataField); hstring Id(); hstring DisplayName(); hstring Publisher(); hstring Version(); hstring Channel(); winrt::Windows::Foundation::Collections::IVectorView PackageFamilyNames(); winrt::Windows::Foundation::Collections::IVectorView ProductCodes(); winrt::Microsoft::Management::Deployment::PackageCatalog PackageCatalog(); winrt::Microsoft::Management::Deployment::CompareResult CompareToVersion(const hstring& versionString); // Contract 4.0 bool HasApplicableInstaller(InstallOptions options); // Contract 6.0 winrt::Microsoft::Management::Deployment::CatalogPackageMetadata GetCatalogPackageMetadata(); winrt::Microsoft::Management::Deployment::CatalogPackageMetadata GetCatalogPackageMetadata(const hstring& preferredLocale); winrt::Microsoft::Management::Deployment::PackageInstallerInfo GetApplicableInstaller(InstallOptions options); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::PackageCatalog m_packageCatalog{ nullptr }; std::shared_ptr<::AppInstaller::Repository::IPackageVersion> m_packageVersion; Windows::Foundation::Collections::IVector m_packageFamilyNames{ nullptr }; Windows::Foundation::Collections::IVector m_productCodes{ nullptr }; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/PropertySheet.props ================================================ ================================================ FILE: src/Microsoft.Management.Deployment/Public/CanUnload.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once namespace winrt::Microsoft::Management::Deployment::implementation { // Sets whether the module can unload or not. void SetCanUnload(bool value); // Gets whether the module can unload or not. bool GetCanUnload(); } ================================================ FILE: src/Microsoft.Management.Deployment/Public/CoCreatableMicrosoftManagementDeploymentClass.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace winrt::Microsoft::Management::Deployment::implementation { // Enable custom code to run before creating any object through the factory. // Currently that means requiring the overall WinGet policy to be enabled. template class wrl_factory_for_winrt_com_class : public ::wil::wrl_factory_for_winrt_com_class { public: IFACEMETHODIMP CreateInstance(_In_opt_::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void** object) noexcept try { *object = nullptr; RETURN_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::WinGet)); return ::wil::wrl_factory_for_winrt_com_class::CreateInstance(unknownOuter, riid, object); } CATCH_RETURN() }; #define CoCreatableMicrosoftManagementDeploymentClass(className) \ CoCreatableClassWithFactory(className, ::winrt::Microsoft::Management::Deployment::implementation::wrl_factory_for_winrt_com_class) \ void CoCreatableMicrosoftManagementDeploymentClass_WRL_ModuleCountCheckFor_ ## className() { \ static_assert(__is_base_of(::AppInstaller::WinRT::ModuleCountBase, ::winrt::Microsoft::Management::Deployment::factory_implementation:: ## className), "Object factories must derive from AppInstaller::WinRT::ModuleCountBase"); \ } } ================================================ FILE: src/Microsoft.Management.Deployment/Public/ComClsids.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include // Clsids for out-of-proc com invocation #if USE_PROD_CLSIDS #define WINGET_OUTOFPROC_COM_CLSID_PackageManager "C53A4F16-787E-42A4-B304-29EFFB4BF597" #define WINGET_OUTOFPROC_COM_CLSID_FindPackagesOptions "572DED96-9C60-4526-8F92-EE7D91D38C1A" #define WINGET_OUTOFPROC_COM_CLSID_CreateCompositePackageCatalogOptions "526534B8-7E46-47C8-8416-B1685C327D37" #define WINGET_OUTOFPROC_COM_CLSID_InstallOptions "1095F097-EB96-453B-B4E6-1613637F3B14" #define WINGET_OUTOFPROC_COM_CLSID_UninstallOptions "E1D9A11E-9F85-4D87-9C17-2B93143ADB8D" #define WINGET_OUTOFPROC_COM_CLSID_PackageMatchFilter "D02C9DAF-99DC-429C-B503-4E504E4AB000" #define WINGET_OUTOFPROC_COM_CLSID_ConfigurationStaticFunctions "73D763B7-2937-432F-A97A-D98A4A596126" #define WINGET_OUTOFPROC_COM_CLSID_DownloadOptions "4CBABE76-7322-4BE4-9CEA-2589A80682DC" #define WINGET_OUTOFPROC_COM_CLSID_AuthenticationArguments "BA580786-BDE3-4F6C-B8F3-44698AC8711A" #define WINGET_OUTOFPROC_COM_CLSID_RepairOptions "0498F441-3097-455F-9CAF-148F28293865" #define WINGET_OUTOFPROC_COM_CLSID_AddPackageCatalogOptions "DB9D012D-00D7-47EE-8FB1-606E10AC4F51" #define WINGET_OUTOFPROC_COM_CLSID_RemovePackageCatalogOptions "032B1C58-B975-469B-A013-E632B6ECE8D8" #define WINGET_OUTOFPROC_COM_CLSID_EditPackageCatalogOptions "A9F5E736-68CE-463C-BA6D-DE968F0CCE04" #else #define WINGET_OUTOFPROC_COM_CLSID_PackageManager "74CB3139-B7C5-4B9E-9388-E6616DEA288C" #define WINGET_OUTOFPROC_COM_CLSID_FindPackagesOptions "1BD8FF3A-EC50-4F69-AEEE-DF4C9D3BAA96" #define WINGET_OUTOFPROC_COM_CLSID_CreateCompositePackageCatalogOptions "EE160901-B317-4EA7-9CC6-5355C6D7D8A7" #define WINGET_OUTOFPROC_COM_CLSID_InstallOptions "44FE0580-62F7-44D4-9E91-AA9614AB3E86" #define WINGET_OUTOFPROC_COM_CLSID_UninstallOptions "AA2A5C04-1AD9-46C4-B74F-6B334AD7EB8C" #define WINGET_OUTOFPROC_COM_CLSID_PackageMatchFilter "3F85B9F4-487A-4C48-9035-2903F8A6D9E8" #define WINGET_OUTOFPROC_COM_CLSID_ConfigurationStaticFunctions "C9ED7917-66AB-4E31-A92A-F65F18EF7933" #define WINGET_OUTOFPROC_COM_CLSID_DownloadOptions "8EF324ED-367C-4880-83E5-BB2ABD0B72F6" #define WINGET_OUTOFPROC_COM_CLSID_AuthenticationArguments "6484A61D-50FA-41F0-B71E-F4370C6EB37C" #define WINGET_OUTOFPROC_COM_CLSID_RepairOptions "E62BB1E7-C7B2-4AEC-9E28-FB649B30FF03" #define WINGET_OUTOFPROC_COM_CLSID_AddPackageCatalogOptions "D58C7E4C-70E6-476C-A5D4-80341ED80252" #define WINGET_OUTOFPROC_COM_CLSID_RemovePackageCatalogOptions "87A96609-1A39-4955-BE72-7174E147B7DC" #define WINGET_OUTOFPROC_COM_CLSID_EditPackageCatalogOptions "29B19238-81AD-4A8E-A2FC-ADF17C38CAEB" #endif // Clsids only used in in-proc invocation #define WINGET_INPROC_ONLY_COM_CLSID_PackageManagerSettings "80CF9D63-5505-4342-B9B4-BB87895CA8BB" namespace winrt::Microsoft::Management::Deployment { // clsid constants for in-proc com invocation const CLSID WINGET_INPROC_COM_CLSID_PackageManager = { 0x2DDE4456, 0x64D9, 0x4673, 0x8F, 0x7E, 0xA4, 0xF1, 0x9A, 0x2E, 0x6C, 0xC3 }; // 2DDE4456-64D9-4673-8F7E-A4F19A2E6CC3 const CLSID WINGET_INPROC_COM_CLSID_FindPackagesOptions = { 0x96B9A53A, 0x9228, 0x4DA0, 0xB0, 0x13, 0xBB, 0x1B, 0x20, 0x31, 0xAB, 0x3D }; // 96B9A53A-9228-4DA0-B013-BB1B2031AB3D const CLSID WINGET_INPROC_COM_CLSID_CreateCompositePackageCatalogOptions = { 0x768318A6, 0x2EB5, 0x400D, 0x84, 0xD0, 0xDF, 0x35, 0x34, 0xC3, 0x0F, 0x5D }; // 768318A6-2EB5-400D-84D0-DF3534C30F5D const CLSID WINGET_INPROC_COM_CLSID_InstallOptions = { 0xE2AF3BA8, 0x8A88, 0x4766, 0x9D, 0xDA, 0xAE, 0x40, 0x13, 0xAD, 0xE2, 0x86 }; // E2AF3BA8-8A88-4766-9DDA-AE4013ADE286 const CLSID WINGET_INPROC_COM_CLSID_UninstallOptions = { 0x869CB959, 0xEB54, 0x425C, 0xA1, 0xE4, 0x1A, 0x1C, 0x29, 0x1C, 0x64, 0xE9 }; // 869CB959-EB54-425C-A1E4-1A1C291C64E9 const CLSID WINGET_INPROC_COM_CLSID_PackageMatchFilter = { 0x57DC8962, 0x7343, 0x42CD, 0xB9, 0x1C, 0x04, 0xF6, 0xA2, 0x5D, 0xB1, 0xD0 }; // 57DC8962-7343-42CD-B91C-04F6A25DB1D0 const CLSID WINGET_INPROC_COM_CLSID_PackageManagerSettings = { 0x80CF9D63, 0x5505, 0x4342, 0xB9, 0xB4, 0xBB, 0x87, 0x89, 0x5C, 0xA8, 0xBB }; // 80CF9D63-5505-4342-B9B4-BB87895CA8BB const CLSID WINGET_INPROC_COM_CLSID_DownloadOptions = { 0x4288DF96, 0xFDC9, 0x4B68, 0xB4, 0x03, 0x19, 0x3D, 0xBB, 0xF5, 0x6A, 0x24 }; // 4288DF96-FDC9-4B68-B403-193DBBF56A24 const CLSID WINGET_INPROC_COM_CLSID_AuthenticationArguments = { 0x8D593114, 0x1CF1, 0x43B9, 0x87, 0x22, 0x4D, 0xBB, 0x30, 0x10, 0x32, 0x96 }; // 8D593114-1CF1-43B9-8722-4DBB30103296 const CLSID WINGET_INPROC_COM_CLSID_RepairOptions = { 0x30c024c4, 0x852c, 0x4dd4, 0x98, 0x10, 0x13, 0x48, 0xc5, 0x1e, 0xf9, 0xbb }; // {30C024C4-852C-4DD4-9810-1348C51EF9BB} const CLSID WINGET_INPROC_COM_CLSID_AddPackageCatalogOptions = { 0x24e6f1fa, 0xe4c3, 0x4acd, 0x96, 0x5d, 0xdf, 0x21, 0x3f, 0xd5, 0x8f, 0x15 }; // {24E6F1FA-E4C3-4ACD-965D-DF213FD58F15} const CLSID WINGET_INPROC_COM_CLSID_RemovePackageCatalogOptions = { 0x1125d3a6, 0xe2ce, 0x479a, 0x91, 0xd5, 0x71, 0xa3, 0xf6, 0xf8, 0xb0, 0xb }; // {1125D3A6-E2CE-479A-91D5-71A3F6F8B00B} const CLSID WINGET_INPROC_COM_CLSID_EditPackageCatalogOptions = { 0xe8e12fe1, 0xab77, 0x40c4, 0xa5, 0x62, 0xe9, 0x1f, 0xb5, 0x1b, 0x4e, 0x82 }; // {E8E12FE1-AB77-40C4-A562-E91FB51B4E82} CLSID GetRedirectedClsidFromInProcClsid(REFCLSID clsid); } ================================================ FILE: src/Microsoft.Management.Deployment/RefreshPackageCatalogResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RefreshPackageCatalogResult.h" #include "RefreshPackageCatalogResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void RefreshPackageCatalogResult::Initialize( winrt::Microsoft::Management::Deployment::RefreshPackageCatalogStatus status, winrt::hresult extendedErrorCode) { m_status = status; m_extendedErrorCode = extendedErrorCode; } winrt::Microsoft::Management::Deployment::RefreshPackageCatalogStatus RefreshPackageCatalogResult::Status() { return m_status; } winrt::hresult RefreshPackageCatalogResult::ExtendedErrorCode() { return m_extendedErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/RefreshPackageCatalogResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "RefreshPackageCatalogResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct RefreshPackageCatalogResult : RefreshPackageCatalogResultT { RefreshPackageCatalogResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::RefreshPackageCatalogStatus status, winrt::hresult extendedErrorCode); #endif winrt::Microsoft::Management::Deployment::RefreshPackageCatalogStatus Status(); winrt::hresult ExtendedErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::RefreshPackageCatalogStatus m_status = winrt::Microsoft::Management::Deployment::RefreshPackageCatalogStatus::Ok; winrt::hresult m_extendedErrorCode = S_OK; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/RemovePackageCatalogOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "RemovePackageCatalogOptions.h" #pragma warning( pop ) #include "RemovePackageCatalogOptions.g.cpp" #include "Converters.h" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { hstring RemovePackageCatalogOptions::Name() { return hstring(m_name); } void RemovePackageCatalogOptions::Name(hstring const& value) { m_name = value; } bool RemovePackageCatalogOptions::PreserveData() { return m_preserveData; } void RemovePackageCatalogOptions::PreserveData(bool const& value) { m_preserveData = value; } CoCreatableMicrosoftManagementDeploymentClass(RemovePackageCatalogOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/RemovePackageCatalogOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "RemovePackageCatalogOptions.g.h" #include "public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_RemovePackageCatalogOptions)] struct RemovePackageCatalogOptions : RemovePackageCatalogOptionsT { RemovePackageCatalogOptions() = default; hstring Name(); void Name(hstring const& value); bool PreserveData(); void PreserveData(bool const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: hstring m_name = L""; bool m_preserveData = false; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct RemovePackageCatalogOptions : RemovePackageCatalogOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/RemovePackageCatalogResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RemovePackageCatalogResult.h" #include "RemovePackageCatalogResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void RemovePackageCatalogResult::Initialize( winrt::Microsoft::Management::Deployment::RemovePackageCatalogStatus status, winrt::hresult extendedErrorCode) { m_status = status; m_extendedErrorCode = extendedErrorCode; } winrt::Microsoft::Management::Deployment::RemovePackageCatalogStatus RemovePackageCatalogResult::Status() { return m_status; } winrt::hresult RemovePackageCatalogResult::ExtendedErrorCode() { return m_extendedErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/RemovePackageCatalogResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "RemovePackageCatalogResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct RemovePackageCatalogResult : RemovePackageCatalogResultT { RemovePackageCatalogResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::RemovePackageCatalogStatus status, winrt::hresult extendedErrorCode); #endif winrt::Microsoft::Management::Deployment::RemovePackageCatalogStatus Status(); winrt::hresult ExtendedErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::RemovePackageCatalogStatus m_status = winrt::Microsoft::Management::Deployment::RemovePackageCatalogStatus::Ok; winrt::hresult m_extendedErrorCode = S_OK; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/RepairOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "RepairOptions.h" #pragma warning( pop ) #include "RepairOptions.g.cpp" #include "Converters.h" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { RepairOptions::RepairOptions() { } winrt::Microsoft::Management::Deployment::PackageVersionId RepairOptions::PackageVersionId() { return m_packageVersionId; } void RepairOptions::PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value) { m_packageVersionId = value; } winrt::Microsoft::Management::Deployment::PackageRepairScope RepairOptions::PackageRepairScope() { return m_packageRepairScope; } void RepairOptions::PackageRepairScope(winrt::Microsoft::Management::Deployment::PackageRepairScope const& value) { m_packageRepairScope = value; } winrt::Microsoft::Management::Deployment::PackageRepairMode RepairOptions::PackageRepairMode() { return m_packageRepairMode; } void RepairOptions::PackageRepairMode(winrt::Microsoft::Management::Deployment::PackageRepairMode const& value) { m_packageRepairMode = value; } bool RepairOptions::AcceptPackageAgreements() { return m_acceptPackageAgreements; } void RepairOptions::AcceptPackageAgreements(bool value) { m_acceptPackageAgreements = value; } hstring RepairOptions::LogOutputPath() { return hstring(m_logOutputPath); } void RepairOptions::LogOutputPath(hstring const& value) { m_logOutputPath = value; } hstring RepairOptions::CorrelationData() { return hstring(m_correlationData); } void RepairOptions::CorrelationData(hstring const& value) { m_correlationData = value; } bool RepairOptions::AllowHashMismatch() { return m_allowHashMismatch; } void RepairOptions::AllowHashMismatch(bool value) { m_allowHashMismatch = value; } bool RepairOptions::BypassIsStoreClientBlockedPolicyCheck() { return m_bypassIsStoreClientBlockedPolicyCheck; } void RepairOptions::BypassIsStoreClientBlockedPolicyCheck(bool value) { m_bypassIsStoreClientBlockedPolicyCheck = value; } bool RepairOptions::Force() { return m_force; } void RepairOptions::Force(bool value) { m_force = value; } winrt::Microsoft::Management::Deployment::AuthenticationArguments RepairOptions::AuthenticationArguments() { return m_authenticationArguments; } void RepairOptions::AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value) { m_authenticationArguments = value; } CoCreatableMicrosoftManagementDeploymentClass(RepairOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/RepairOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "RepairOptions.g.h" #include "Public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_RepairOptions)] struct RepairOptions : RepairOptionsT { RepairOptions(); winrt::Microsoft::Management::Deployment::PackageVersionId PackageVersionId(); void PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value); winrt::Microsoft::Management::Deployment::PackageRepairScope PackageRepairScope(); void PackageRepairScope(winrt::Microsoft::Management::Deployment::PackageRepairScope const& value); winrt::Microsoft::Management::Deployment::PackageRepairMode PackageRepairMode(); void PackageRepairMode(winrt::Microsoft::Management::Deployment::PackageRepairMode const& value); bool AcceptPackageAgreements(); void AcceptPackageAgreements(bool value); hstring LogOutputPath(); void LogOutputPath(hstring const& value); hstring CorrelationData(); void CorrelationData(hstring const& value); bool AllowHashMismatch(); void AllowHashMismatch(bool value); bool BypassIsStoreClientBlockedPolicyCheck(); void BypassIsStoreClientBlockedPolicyCheck(bool value); bool Force(); void Force(bool value); winrt::Microsoft::Management::Deployment::AuthenticationArguments AuthenticationArguments(); void AuthenticationArguments(winrt::Microsoft::Management::Deployment::AuthenticationArguments const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::PackageVersionId m_packageVersionId{ nullptr }; bool m_acceptPackageAgreements = false; bool m_allowHashMismatch = false; bool m_bypassIsStoreClientBlockedPolicyCheck = false; bool m_force = false; std::wstring m_logOutputPath = L""; std::wstring m_correlationData = L""; winrt::Microsoft::Management::Deployment::PackageRepairScope m_packageRepairScope = winrt::Microsoft::Management::Deployment::PackageRepairScope::Any; winrt::Microsoft::Management::Deployment::PackageRepairMode m_packageRepairMode = winrt::Microsoft::Management::Deployment::PackageRepairMode::Default; winrt::Microsoft::Management::Deployment::AuthenticationArguments m_authenticationArguments{ nullptr }; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct RepairOptions : RepairOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/RepairResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "RepairResult.h" #include "RepairResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void RepairResult::Initialize( winrt::Microsoft::Management::Deployment::RepairResultStatus status, winrt::hresult extendedErrorCode, uint32_t repairerErrorCode, hstring const& correlationData, bool rebootRequired) { m_status = status; m_extendedErrorCode = extendedErrorCode; m_repairerErrorCode = repairerErrorCode; m_correlationData = correlationData; m_rebootRequired = rebootRequired; } hstring RepairResult::CorrelationData() { return hstring(m_correlationData); } bool RepairResult::RebootRequired() { return m_rebootRequired; } winrt::Microsoft::Management::Deployment::RepairResultStatus RepairResult::Status() { return m_status; } winrt::hresult RepairResult::ExtendedErrorCode() { return m_extendedErrorCode; } uint32_t RepairResult::RepairerErrorCode() { return m_repairerErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/RepairResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "RepairResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct RepairResult : RepairResultT { RepairResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::RepairResultStatus status, winrt::hresult extendedErrorCode, uint32_t repairerErrorCode, hstring const& correlationData, bool rebootRequired); #endif hstring CorrelationData(); bool RebootRequired(); winrt::Microsoft::Management::Deployment::RepairResultStatus Status(); winrt::hresult ExtendedErrorCode(); uint32_t RepairerErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: std::wstring m_correlationData = L""; bool m_rebootRequired = false; winrt::Microsoft::Management::Deployment::RepairResultStatus m_status = winrt::Microsoft::Management::Deployment::RepairResultStatus::Ok; winrt::hresult m_extendedErrorCode = S_OK; uint32_t m_repairerErrorCode = 0; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/SourceAgreement.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include "SourceAgreement.h" #include "SourceAgreement.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void SourceAgreement::Initialize(::AppInstaller::Repository::SourceAgreement sourceAgreement) { m_sourceAgreement = std::move(sourceAgreement); } hstring SourceAgreement::Label() { return winrt::to_hstring(m_sourceAgreement.Label); } hstring SourceAgreement::Text() { return winrt::to_hstring(m_sourceAgreement.Text); } hstring SourceAgreement::Url() { return winrt::to_hstring(m_sourceAgreement.Url); } } ================================================ FILE: src/Microsoft.Management.Deployment/SourceAgreement.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "SourceAgreement.g.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { struct SourceAgreement : SourceAgreementT { SourceAgreement() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(::AppInstaller::Repository::SourceAgreement sourceAgreement); #endif hstring Label(); hstring Text(); hstring Url(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Repository::SourceAgreement m_sourceAgreement{}; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/UninstallOptions.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #pragma warning( push ) #pragma warning ( disable : 4467 6388) // 6388 Allow CreateInstance. #include // 4467 Allow use of uuid attribute for com object creation. #include "UninstallOptions.h" #pragma warning( pop ) #include "UninstallOptions.g.cpp" #include "Converters.h" #include "Helpers.h" namespace winrt::Microsoft::Management::Deployment::implementation { UninstallOptions::UninstallOptions() { } winrt::Microsoft::Management::Deployment::PackageVersionId UninstallOptions::PackageVersionId() { return m_packageVersionId; } void UninstallOptions::PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value) { m_packageVersionId = value; } winrt::Microsoft::Management::Deployment::PackageUninstallMode UninstallOptions::PackageUninstallMode() { return m_packageUninstallMode; } void UninstallOptions::PackageUninstallMode(winrt::Microsoft::Management::Deployment::PackageUninstallMode const& value) { m_packageUninstallMode = value; } hstring UninstallOptions::LogOutputPath() { return hstring(m_logOutputPath); } void UninstallOptions::LogOutputPath(hstring const& value) { m_logOutputPath = value; } hstring UninstallOptions::CorrelationData() { return hstring(m_correlationData); } void UninstallOptions::CorrelationData(hstring const& value) { m_correlationData = value; } bool UninstallOptions::Force() { return m_force; } void UninstallOptions::Force(bool value) { m_force = value; } winrt::Microsoft::Management::Deployment::PackageUninstallScope UninstallOptions::PackageUninstallScope() { return m_packageUninstallScope; } void UninstallOptions::PackageUninstallScope(winrt::Microsoft::Management::Deployment::PackageUninstallScope const& value) { m_packageUninstallScope = value; } CoCreatableMicrosoftManagementDeploymentClass(UninstallOptions); } ================================================ FILE: src/Microsoft.Management.Deployment/UninstallOptions.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "UninstallOptions.g.h" #include "Public/ComClsids.h" #include namespace winrt::Microsoft::Management::Deployment::implementation { [uuid(WINGET_OUTOFPROC_COM_CLSID_UninstallOptions)] struct UninstallOptions : UninstallOptionsT { UninstallOptions(); winrt::Microsoft::Management::Deployment::PackageVersionId PackageVersionId(); void PackageVersionId(winrt::Microsoft::Management::Deployment::PackageVersionId const& value); winrt::Microsoft::Management::Deployment::PackageUninstallMode PackageUninstallMode(); void PackageUninstallMode(winrt::Microsoft::Management::Deployment::PackageUninstallMode const& value); hstring LogOutputPath(); void LogOutputPath(hstring const& value); hstring CorrelationData(); void CorrelationData(hstring const& value); bool Force(); void Force(bool value); winrt::Microsoft::Management::Deployment::PackageUninstallScope PackageUninstallScope(); void PackageUninstallScope(winrt::Microsoft::Management::Deployment::PackageUninstallScope const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: winrt::Microsoft::Management::Deployment::PackageVersionId m_packageVersionId{ nullptr }; winrt::Microsoft::Management::Deployment::PackageUninstallMode m_packageUninstallMode = winrt::Microsoft::Management::Deployment::PackageUninstallMode::Default; std::wstring m_logOutputPath = L""; std::wstring m_correlationData = L""; bool m_force = false; winrt::Microsoft::Management::Deployment::PackageUninstallScope m_packageUninstallScope = winrt::Microsoft::Management::Deployment::PackageUninstallScope::Any; #endif }; } #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) namespace winrt::Microsoft::Management::Deployment::factory_implementation { struct UninstallOptions : UninstallOptionsT, AppInstaller::WinRT::ModuleCountBase { }; } #endif ================================================ FILE: src/Microsoft.Management.Deployment/UninstallResult.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "UninstallResult.h" #include "UninstallResult.g.cpp" #include namespace winrt::Microsoft::Management::Deployment::implementation { void UninstallResult::Initialize( winrt::Microsoft::Management::Deployment::UninstallResultStatus status, winrt::hresult extendedErrorCode, uint32_t uninstallerErrorCode, hstring const& correlationData, bool rebootRequired) { m_status = status; m_extendedErrorCode = extendedErrorCode; m_uninstallerErrorCode = uninstallerErrorCode; m_correlationData = correlationData; m_rebootRequired = rebootRequired; } hstring UninstallResult::CorrelationData() { return hstring(m_correlationData); } bool UninstallResult::RebootRequired() { return m_rebootRequired; } winrt::Microsoft::Management::Deployment::UninstallResultStatus UninstallResult::Status() { return m_status; } winrt::hresult UninstallResult::ExtendedErrorCode() { return m_extendedErrorCode; } uint32_t UninstallResult::UninstallerErrorCode() { return m_uninstallerErrorCode; } } ================================================ FILE: src/Microsoft.Management.Deployment/UninstallResult.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include "UninstallResult.g.h" namespace winrt::Microsoft::Management::Deployment::implementation { struct UninstallResult : UninstallResultT { UninstallResult() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( winrt::Microsoft::Management::Deployment::UninstallResultStatus status, winrt::hresult extendedErrorCode, uint32_t uninstallerErrorCode, hstring const& correlationData, bool rebootRequired); #endif hstring CorrelationData(); bool RebootRequired(); winrt::Microsoft::Management::Deployment::UninstallResultStatus Status(); winrt::hresult ExtendedErrorCode(); uint32_t UninstallerErrorCode(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: std::wstring m_correlationData = L""; bool m_rebootRequired = false; winrt::Microsoft::Management::Deployment::UninstallResultStatus m_status = winrt::Microsoft::Management::Deployment::UninstallResultStatus::Ok; winrt::hresult m_extendedErrorCode = S_OK; uint32_t m_uninstallerErrorCode = 0; #endif }; } ================================================ FILE: src/Microsoft.Management.Deployment/packages.config ================================================  ================================================ FILE: src/Microsoft.Management.Deployment/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/Microsoft.Management.Deployment/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include #include #include #include #include #include ================================================ FILE: src/Microsoft.Management.Deployment.CsWinRTProjection/Microsoft.Management.Deployment.CsWinRTProjection.csproj ================================================ net8.0-windows10.0.26100.0 AnyCpu enable enable $(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\ Debug;Release;ReleaseStatic true true true Microsoft.Management.Deployment $(OutDir) 10.0.26100.0 10.0.17763.0 True None Content PreserveNewest True ================================================ FILE: src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.dll.manifest ================================================ ================================================ FILE: src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj ================================================ true true true 15.0 Win32Proj {9ac3c6a4-1875-4d3e-bf9c-c31e81eff6b4} MicrosoftManagementDeploymentInProc 10.0.26100.0 10.0.17763.0 true false Debug ARM64 Debug Win32 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Debug x64 Release x64 DynamicLibrary true true false true false false true false Spectre Spectre Spectre Spectre Spectre Spectre true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING Disabled _DEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) true true false false stdcpp17 stdcpp17 true true true true false false false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) WIN32;%(PreprocessorDefinitions) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) true false stdcpp17 true true false $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) true MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) true true true Guard Guard Guard stdcpp17 stdcpp17 stdcpp17 true true true false false false false false false true true false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full %(AdditionalOptions) /debug:full %(AdditionalOptions) /debug:full %(AdditionalOptions) true true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) $(ProjectDir)..\WindowsPackageManager;%(AdditionalIncludeDirectories) true true true Guard Guard Guard stdcpp17 stdcpp17 stdcpp17 true true true false false false MultiThreaded MultiThreaded MultiThreaded false false false true true false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) true true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) Create {2046b5af-666d-4ce8-8d3e-c32c57908a56} true This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Source Files Source Files Source Files ================================================ FILE: src/Microsoft.Management.Deployment.InProc/PropertySheet.props ================================================ ================================================ FILE: src/Microsoft.Management.Deployment.InProc/Source.def ================================================ EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllGetActivationFactory PRIVATE ================================================ FILE: src/Microsoft.Management.Deployment.InProc/dllmain.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include #include EXTERN_C BOOL WINAPI DllMain( HMODULE /* hModule */, DWORD reason, LPVOID /* lpReserved */) { switch (reason) { case DLL_PROCESS_ATTACH: { if (FAILED(WindowsPackageManagerInProcModuleInitialize())) { return FALSE; } } break; } return TRUE; } STDAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID* ppv) { RETURN_HR(WindowsPackageManagerInProcModuleGetClassObject(rclsid, riid, ppv)); } STDAPI DllCanUnloadNow() { return WindowsPackageManagerInProcModuleTerminate() ? S_OK : S_FALSE; } STDAPI DllGetActivationFactory(HSTRING classId, void** factory) { return WindowsPackageManagerInProcModuleGetActivationFactory(classId, factory); } ================================================ FILE: src/Microsoft.Management.Deployment.InProc/packages.config ================================================  ================================================ FILE: src/Microsoft.Management.Deployment.InProc/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/Microsoft.Management.Deployment.InProc/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #define WIN32_LEAN_AND_MEAN #include #include ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/Factory.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Factory.h" #include #include #include using namespace std::string_view_literals; namespace Microsoft::Management::Deployment::OutOfProc { namespace { #if USE_PROD_CLSIDS constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, { 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 } }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 constexpr CLSID CLSID_InstallOptions = { 0x1095f097, 0xEB96, 0x453B, { 0xB4, 0xE6, 0x16, 0x13, 0x63, 0x7F, 0x3B, 0x14 } }; //1095F097-EB96-453B-B4E6-1613637F3B14 constexpr CLSID CLSID_UninstallOptions = { 0xE1D9A11E, 0x9F85, 0x4D87, { 0x9C, 0x17, 0x2B, 0x93, 0x14, 0x3A, 0xDB, 0x8D } }; //E1D9A11E-9F85-4D87-9C17-2B93143ADB8D constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 constexpr CLSID CLSID_CreateCompositePackageCatalogOptions = { 0x526534B8, 0x7E46, 0x47C8, { 0x84, 0x16, 0xB1, 0x68, 0x5C, 0x32, 0x7D, 0x37 } }; //526534B8-7E46-47C8-8416-B1685C327D37 constexpr CLSID CLSID_DownloadOptions = { 0x4CBABE76, 0x7322, 0x4BE4, { 0x9C, 0xEA, 0x25, 0x89, 0xA8, 0x06, 0x82, 0xDC } }; //4CBABE76-7322-4BE4-9CEA-2589A80682DC constexpr CLSID CLSID_AuthenticationArguments = { 0xBA580786, 0xBDE3, 0x4F6C, { 0xB8, 0xF3, 0x44, 0x69, 0x8A, 0xC8, 0x71, 0x1A } }; //BA580786-BDE3-4F6C-B8F3-44698AC8711A constexpr CLSID CLSID_RepairOptions = { 0x0498F441, 0x3097, 0x455F, { 0x9C, 0xAF, 0x14, 0x8F, 0x28, 0x29, 0x38, 0x65 } }; //0498F441-3097-455F-9CAF-148F28293865 constexpr CLSID CLSID_AddPackageCatalogOptions = { 0xDB9D012D, 0x00D7, 0x47EE, { 0x8F, 0xB1, 0x60, 0x6E, 0x10, 0xAC, 0x4F, 0x51 } }; //DB9D012D-00D7-47EE-8FB1-606E10AC4F51 constexpr CLSID CLSID_RemovePackageCatalogOptions = { 0x032B1C58, 0xB975, 0x469B, { 0xA0, 0x13, 0xE6, 0x32, 0xB6, 0xEC, 0xE8, 0xD8 } }; //032B1C58-B975-469B-A013-E632B6ECE8D8 constexpr CLSID CLSID_EditPackageCatalogOptions = { 0xA9F5E736, 0x68CE, 0x463C, { 0xBA, 0x6D, 0xDE, 0x96, 0x8F, 0x0C, 0xCE, 0x04 } }; //A9F5E736-68CE-463C-BA6D-DE968F0CCE04 #else constexpr CLSID CLSID_PackageManager = { 0x74CB3139, 0xB7C5, 0x4B9E, { 0x93, 0x88, 0xE6, 0x61, 0x6D, 0xEA, 0x28, 0x8C } }; //74CB3139-B7C5-4B9E-9388-E6616DEA288C constexpr CLSID CLSID_InstallOptions = { 0x44FE0580, 0x62F7, 0x44D4, { 0x9E, 0x91, 0xAA, 0x96, 0x14, 0xAB, 0x3E, 0x86 } }; //44FE0580-62F7-44D4-9E91-AA9614AB3E86 constexpr CLSID CLSID_UninstallOptions = { 0xAA2A5C04, 0x1AD9, 0x46C4, { 0xB7, 0x4F, 0x6B, 0x33, 0x4A, 0xD7, 0xEB, 0x8C } }; //AA2A5C04-1AD9-46C4-B74F-6B334AD7EB8C constexpr CLSID CLSID_FindPackagesOptions = { 0x1BD8FF3A, 0xEC50, 0x4F69, { 0xAE, 0xEE, 0xDF, 0x4C, 0x9D, 0x3B, 0xAA, 0x96 } }; //1BD8FF3A-EC50-4F69-AEEE-DF4C9D3BAA96 constexpr CLSID CLSID_PackageMatchFilter = { 0x3F85B9F4, 0x487A, 0x4C48, { 0x90, 0x35, 0x29, 0x03, 0xF8, 0xA6, 0xD9, 0xE8 } }; //3F85B9F4-487A-4C48-9035-2903F8A6D9E8 constexpr CLSID CLSID_CreateCompositePackageCatalogOptions = { 0xEE160901, 0xB317, 0x4EA7, { 0x9C, 0xC6, 0x53, 0x55, 0xC6, 0xD7, 0xD8, 0xA7 } }; //EE160901-B317-4EA7-9CC6-5355C6D7D8A7 constexpr CLSID CLSID_DownloadOptions = { 0x8EF324ED, 0x367C, 0x4880, { 0x83, 0xE5, 0xBB, 0x2A, 0xBD, 0x0B, 0x72, 0xF6 } }; //8EF324ED-367C-4880-83E5-BB2ABD0B72F6 constexpr CLSID CLSID_AuthenticationArguments = { 0x6484A61D, 0x50FA, 0x41F0, { 0xB7, 0x1E, 0xF4, 0x37, 0x0C, 0x6E, 0xB3, 0x7C } }; //6484A61D-50FA-41F0-B71E-F4370C6EB37C constexpr CLSID CLSID_RepairOptions = { 0xE62BB1E7, 0xC7B2, 0x4AEC, { 0x9E, 0x28, 0xFB, 0x64, 0x9B, 0x30, 0xFF, 0x03 } }; //E62BB1E7-C7B2-4AEC-9E28-FB649B30FF03 constexpr CLSID CLSID_AddPackageCatalogOptions = { 0xD58C7E4C, 0x70E6, 0x476C, { 0xA5, 0xD4, 0x80, 0x34, 0x1E, 0xD8, 0x02, 0x52 } }; //D58C7E4C-70E6-476C-A5D4-80341ED80252 constexpr CLSID CLSID_RemovePackageCatalogOptions = { 0x87A96609, 0x1A39, 0x4955, { 0xBE, 0x72, 0x71, 0x74, 0xE1, 0x47, 0xB7, 0xDC } }; //87A96609-1A39-4955-BE72-7174E147B7DC constexpr CLSID CLSID_EditPackageCatalogOptions = { 0x29B19238, 0x81AD, 0x4A8E, { 0xA2, 0xFC, 0xAD, 0xF1, 0x7C, 0x38, 0xCA, 0xEB } }; //29B19238-81AD-4A8E-A2FC-ADF17C38CAEB #endif struct NameCLSIDPair { std::wstring_view Name; GUID CLSID; }; constexpr std::array s_nameCLSIDPairs { NameCLSIDPair{ L"Microsoft.Management.Deployment.PackageManager"sv, CLSID_PackageManager }, NameCLSIDPair{ L"Microsoft.Management.Deployment.InstallOptions"sv, CLSID_InstallOptions }, NameCLSIDPair{ L"Microsoft.Management.Deployment.UninstallOptions"sv, CLSID_UninstallOptions }, NameCLSIDPair{ L"Microsoft.Management.Deployment.FindPackagesOptions"sv, CLSID_FindPackagesOptions }, NameCLSIDPair{ L"Microsoft.Management.Deployment.PackageMatchFilter"sv, CLSID_PackageMatchFilter }, NameCLSIDPair{ L"Microsoft.Management.Deployment.CreateCompositePackageCatalogOptions"sv, CLSID_CreateCompositePackageCatalogOptions }, NameCLSIDPair{ L"Microsoft.Management.Deployment.DownloadOptions"sv, CLSID_DownloadOptions }, NameCLSIDPair{ L"Microsoft.Management.Deployment.AuthenticationArguments"sv, CLSID_AuthenticationArguments }, NameCLSIDPair{ L"Microsoft.Management.Deployment.RepairOptions"sv, CLSID_RepairOptions }, NameCLSIDPair{ L"Microsoft.Management.Deployment.AddPackageCatalogOptions"sv, CLSID_AddPackageCatalogOptions }, NameCLSIDPair{ L"Microsoft.Management.Deployment.RemovePackageCatalogOptions"sv, CLSID_RemovePackageCatalogOptions }, NameCLSIDPair{ L"Microsoft.Management.Deployment.EditPackageCatalogOptions"sv, CLSID_EditPackageCatalogOptions }, }; bool IsCLSIDPresent(const GUID& clsid) { for (const auto& pair : s_nameCLSIDPairs) { if (pair.CLSID == clsid) { return true; } } return false; } const GUID* GetCLSIDFor(HSTRING clsid) { UINT32 length = 0; PCWSTR buffer = WindowsGetStringRawBuffer(clsid, &length); std::wstring_view clsidView{ buffer, length }; for (const auto& pair : s_nameCLSIDPairs) { if (pair.Name == clsidView) { return &pair.CLSID; } } return nullptr; } winrt::Windows::Foundation::IInspectable CreateOOPObject(const GUID& clsid) { bool isAdmin = AppInstaller::Runtime::IsRunningAsAdmin(); try { return winrt::create_instance(clsid, CLSCTX_LOCAL_SERVER | CLSCTX_NO_CODE_DOWNLOAD); } catch (const winrt::hresult_error& hre) { // We only want to fall through to trying the manual activation if we are running as admin and couldn't find the registration. if (!(isAdmin && hre.code() == REGDB_E_CLASSNOTREG)) { throw; } } winrt::com_ptr<::IUnknown> result; THROW_IF_FAILED(WinGetServerManualActivation_CreateInstance(clsid, winrt::guid_of(), 0, result.put_void())); return result.as(); } } Factory::Factory(const GUID& clsid) : m_clsid(clsid) { IncrementRefCount(); } Factory::Factory(HSTRING clsid) : m_clsid(*GetCLSIDFor(clsid)) { IncrementRefCount(); } Factory::~Factory() { DecrementRefCount(); } bool Factory::HasReferences() { return s_referenceCount.load() != 0; } void Factory::Terminate() { WinGetServerManualActivation_Terminate(); } bool Factory::IsCLSID(const GUID& clsid) { return IsCLSIDPresent(clsid); } bool Factory::IsCLSID(HSTRING clsid) { return GetCLSIDFor(clsid) != nullptr; } winrt::Windows::Foundation::IInspectable Factory::ActivateInstance() { return CreateOOPObject(m_clsid); } HRESULT STDMETHODCALLTYPE Factory::CreateInstance(::IUnknown* pUnkOuter, REFIID riid, void** ppvObject) try { RETURN_HR_IF(E_POINTER, !ppvObject); *ppvObject = nullptr; RETURN_HR_IF(CLASS_E_NOAGGREGATION, pUnkOuter != nullptr); return CreateOOPObject(m_clsid).as(riid, ppvObject); } CATCH_RETURN(); HRESULT STDMETHODCALLTYPE Factory::LockServer(BOOL fLock) { if (fLock) { IncrementRefCount(); } else { DecrementRefCount(); } return S_OK; } void Factory::IncrementRefCount() { ++s_referenceCount; } void Factory::DecrementRefCount() { --s_referenceCount; } std::atomic Factory::s_referenceCount = ATOMIC_VAR_INIT(0); } ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/Factory.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #include #include #include #include namespace Microsoft::Management::Deployment::OutOfProc { struct Factory : winrt::implements { Factory(const GUID& clsid); Factory(HSTRING clsid); ~Factory(); // Returns true if the reference count is not 0; false if it is. static bool HasReferences(); // Forcibly destroys any static objects. static void Terminate(); // Determines if the given CLSID is the CLSID for the factory. static bool IsCLSID(const GUID& clsid); // Determines if the given CLSID is the CLSID for the factory. static bool IsCLSID(HSTRING clsid); // IActivationFactory winrt::Windows::Foundation::IInspectable ActivateInstance(); // IClassFactory HRESULT STDMETHODCALLTYPE CreateInstance(::IUnknown *pUnkOuter, REFIID riid, void **ppvObject); HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock); private: static void IncrementRefCount(); static void DecrementRefCount(); static std::atomic s_referenceCount; GUID m_clsid; }; } ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj ================================================ true true true 15.0 Win32Proj MicrosoftManagementDeploymentOutOfProc 10.0.26100.0 10.0.17763.0 true false {0BA531C8-CF0C-405B-8221-0FE51BA529D1} Debug ARM64 Debug Win32 ReleaseStatic ARM64 ReleaseStatic Win32 ReleaseStatic x64 Release ARM64 Release Win32 Debug x64 Release x64 DynamicLibrary true true false true false false true false Spectre Spectre Spectre Spectre Spectre Spectre true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset true $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true true ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset false $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ true false ..\CodeAnalysis.ruleset Use pch.h $(IntDir)pch.pch _CONSOLE;%(PreprocessorDefinitions) Level4 %(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING Disabled _DEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) true true false false stdcpp17 stdcpp17 true true true true 6001 6001 false false false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) WIN32;%(PreprocessorDefinitions) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) true false stdcpp17 true true 6001 false $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) true MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) true true true Guard Guard Guard stdcpp17 stdcpp17 stdcpp17 true true true false false false 6001 6001 6001 false false false true true false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) true true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) MaxSpeed true true NDEBUG;%(PreprocessorDefinitions) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) $(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\WinGetServer;%(AdditionalIncludeDirectories) true true true Guard Guard Guard stdcpp17 stdcpp17 stdcpp17 true true true false false false MultiThreaded MultiThreaded MultiThreaded 6001 6001 6001 false false false true true false Windows Windows Windows Source.def Source.def Source.def wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;Advapi32.lib;winhttp.lib;onecoreuap.lib;msi.lib;%(AdditionalDependencies) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) /debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions) true true $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) $(ProjectDir)..\manifest\shared.manifest %(AdditionalManifestFiles) NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing NotUsing Create {f3f6e699-bc5d-4950-8a05-e49dd9eb0d51} {1cc41a9a-ae66-459d-9210-1e572dd7be69} This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {6017fd94-3eb1-40bc-964f-5dd571077d3c} Header Files Header Files Source Files Source Files Source Files WinGetServerManualActivation WinGetServerManualActivation WinGetServerManualActivation Source Files ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/PropertySheet.props ================================================ ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/Source.def ================================================ EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllGetActivationFactory PRIVATE ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/dllmain.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" #include "Factory.h" #include using namespace Microsoft::Management::Deployment::OutOfProc; EXTERN_C BOOL WINAPI DllMain( HMODULE /* hModule */, DWORD reason, LPVOID /* lpReserved */) { switch (reason) { case DLL_PROCESS_DETACH: Factory::Terminate(); break; } return TRUE; } _Check_return_ STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) try { RETURN_HR_IF(E_POINTER, !ppv); *ppv = nullptr; winrt::Windows::Foundation::IUnknown result; if (Factory::IsCLSID(rclsid)) { result = winrt::make(rclsid).as(); } if (result) { return result.as(riid, ppv); } return REGDB_E_CLASSNOTREG; } CATCH_RETURN(); __control_entrypoint(DllExport) STDAPI DllCanUnloadNow() { return Factory::HasReferences() ? S_FALSE : S_OK; } STDAPI DllGetActivationFactory(HSTRING classId, void** factory) try { RETURN_HR_IF(E_POINTER, !factory); *factory = nullptr; winrt::Windows::Foundation::IUnknown result; if (Factory::IsCLSID(classId)) { result = winrt::make(classId).as(); } if (result) { return result.as(winrt::guid_of(), factory); } return REGDB_E_CLASSNOTREG; } CATCH_RETURN(); ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/packages.config ================================================  ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/pch.cpp ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" ================================================ FILE: src/Microsoft.Management.Deployment.OutOfProc/pch.h ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include ================================================ FILE: src/Microsoft.Management.Deployment.Projection/ClassModel.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection { using System; using System.Collections.Generic; public enum ClsidContext { // In process InProc, // Out of process production OutOfProc, // Out of process development OutOfProcDev } internal class ClassModel { /// /// Interface type corresponding to the object's IID /// public Type InterfaceType { init; get; } /// /// Projected class type by CsWinRT /// public Type ProjectedClassType { init; get; } /// /// Clsids for each context (e.g. InProc, OutOfProc, OutOfProcDev) /// public IReadOnlyDictionary Clsids { init; get; } /// /// Get CLSID based on the provided context /// /// Context /// CLSID for the provided context, or throw an exception if not found. /// public Guid GetClsid(ClsidContext context) { if (!Clsids.TryGetValue(context, out Guid clsid)) { throw new InvalidOperationException($"{ProjectedClassType.FullName} is not implemented in context {context}"); } return clsid; } /// /// Get IID corresponding to the COM object /// /// IID. public Guid GetIid() { return InterfaceType.GUID; } } } ================================================ FILE: src/Microsoft.Management.Deployment.Projection/ClassesDefinition.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection { using System; using System.Collections.Generic; internal static class ClassesDefinition { private static Dictionary Classes { get; set; } = new() { [typeof(PackageManager)] = new() { ProjectedClassType = typeof(PackageManager), InterfaceType = typeof(IPackageManager), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("2DDE4456-64D9-4673-8F7E-A4F19A2E6CC3"), [ClsidContext.OutOfProc] = new Guid("C53A4F16-787E-42A4-B304-29EFFB4BF597"), [ClsidContext.OutOfProcDev] = new Guid("74CB3139-B7C5-4B9E-9388-E6616DEA288C"), } }, [typeof(FindPackagesOptions)] = new() { ProjectedClassType = typeof(FindPackagesOptions), InterfaceType = typeof(IFindPackagesOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("96B9A53A-9228-4DA0-B013-BB1B2031AB3D"), [ClsidContext.OutOfProc] = new Guid("572DED96-9C60-4526-8F92-EE7D91D38C1A"), [ClsidContext.OutOfProcDev] = new Guid("1BD8FF3A-EC50-4F69-AEEE-DF4C9D3BAA96"), } }, [typeof(CreateCompositePackageCatalogOptions)] = new() { ProjectedClassType = typeof(CreateCompositePackageCatalogOptions), InterfaceType = typeof(ICreateCompositePackageCatalogOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("768318A6-2EB5-400D-84D0-DF3534C30F5D"), [ClsidContext.OutOfProc] = new Guid("526534B8-7E46-47C8-8416-B1685C327D37"), [ClsidContext.OutOfProcDev] = new Guid("EE160901-B317-4EA7-9CC6-5355C6D7D8A7"), } }, [typeof(InstallOptions)] = new() { ProjectedClassType = typeof(InstallOptions), InterfaceType = typeof(IInstallOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("E2AF3BA8-8A88-4766-9DDA-AE4013ADE286"), [ClsidContext.OutOfProc] = new Guid("1095F097-EB96-453B-B4E6-1613637F3B14"), [ClsidContext.OutOfProcDev] = new Guid("44FE0580-62F7-44D4-9E91-AA9614AB3E86"), } }, [typeof(UninstallOptions)] = new() { ProjectedClassType = typeof(UninstallOptions), InterfaceType = typeof(IUninstallOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("869CB959-EB54-425C-A1E4-1A1C291C64E9"), [ClsidContext.OutOfProc] = new Guid("E1D9A11E-9F85-4D87-9C17-2B93143ADB8D"), [ClsidContext.OutOfProcDev] = new Guid("AA2A5C04-1AD9-46C4-B74F-6B334AD7EB8C"), } }, [typeof(DownloadOptions)] = new() { ProjectedClassType = typeof(DownloadOptions), InterfaceType = typeof(IDownloadOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("4288DF96-FDC9-4B68-B403-193DBBF56A24"), [ClsidContext.OutOfProc] = new Guid("4CBABE76-7322-4BE4-9CEA-2589A80682DC"), [ClsidContext.OutOfProcDev] = new Guid("8EF324ED-367C-4880-83E5-BB2ABD0B72F6"), } }, [typeof(PackageMatchFilter)] = new() { ProjectedClassType = typeof(PackageMatchFilter), InterfaceType = typeof(IPackageMatchFilter), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("57DC8962-7343-42CD-B91C-04F6A25DB1D0"), [ClsidContext.OutOfProc] = new Guid("D02C9DAF-99DC-429C-B503-4E504E4AB000"), [ClsidContext.OutOfProcDev] = new Guid("3F85B9F4-487A-4C48-9035-2903F8A6D9E8"), } }, [typeof(AuthenticationArguments)] = new() { ProjectedClassType = typeof(AuthenticationArguments), InterfaceType = typeof(IAuthenticationArguments), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("8D593114-1CF1-43B9-8722-4DBB30103296"), [ClsidContext.OutOfProc] = new Guid("BA580786-BDE3-4F6C-B8F3-44698AC8711A"), [ClsidContext.OutOfProcDev] = new Guid("6484A61D-50FA-41F0-B71E-F4370C6EB37C"), } }, [typeof(PackageManagerSettings)] = new() { ProjectedClassType = typeof(PackageManagerSettings), InterfaceType = typeof(IPackageManagerSettings), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("80CF9D63-5505-4342-B9B4-BB87895CA8BB"), } }, [typeof(RepairOptions)] = new () { ProjectedClassType = typeof(RepairOptions), InterfaceType = typeof(IRepairOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("30C024C4-852C-4DD4-9810-1348C51EF9BB"), [ClsidContext.OutOfProc] = new Guid("0498F441-3097-455F-9CAF-148F28293865"), [ClsidContext.OutOfProcDev] = new Guid("E62BB1E7-C7B2-4AEC-9E28-FB649B30FF03"), } }, [typeof(AddPackageCatalogOptions)] = new() { ProjectedClassType = typeof(AddPackageCatalogOptions), InterfaceType = typeof(IAddPackageCatalogOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("24E6F1FA-E4C3-4ACD-965D-DF213FD58F15"), [ClsidContext.OutOfProc] = new Guid("DB9D012D-00D7-47EE-8FB1-606E10AC4F51"), [ClsidContext.OutOfProcDev] = new Guid("D58C7E4C-70E6-476C-A5D4-80341ED80252"), } }, [typeof(RemovePackageCatalogOptions)] = new() { ProjectedClassType = typeof(RemovePackageCatalogOptions), InterfaceType = typeof(IRemovePackageCatalogOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("1125D3A6-E2CE-479A-91D5-71A3F6F8B00B"), [ClsidContext.OutOfProc] = new Guid("032B1C58-B975-469B-A013-E632B6ECE8D8"), [ClsidContext.OutOfProcDev] = new Guid("87A96609-1A39-4955-BE72-7174E147B7DC"), } }, [typeof(EditPackageCatalogOptions)] = new() { ProjectedClassType = typeof(EditPackageCatalogOptions), InterfaceType = typeof(IEditPackageCatalogOptions), Clsids = new Dictionary() { [ClsidContext.InProc] = new Guid("E8E12FE1-AB77-40C4-A562-E91FB51B4E82"), [ClsidContext.OutOfProc] = new Guid("A9F5E736-68CE-463C-BA6D-DE968F0CCE04"), [ClsidContext.OutOfProcDev] = new Guid("29B19238-81AD-4A8E-A2FC-ADF17C38CAEB"), } } }; /// /// Get CLSID based on the provided context for the specified type /// /// Projected class type /// Context /// CLSID for the provided context and type, or throw an exception if not found. public static Guid GetClsid(ClsidContext context) { ValidateType(typeof(T)); return Classes[typeof(T)].GetClsid(context); } /// /// Get IID corresponding to the COM object /// /// Projected class type /// IID or throw an exception if not found. public static Guid GetIid() { ValidateType(typeof(T)); return Classes[typeof(T)].GetIid(); } /// /// Validate that the provided type is defined. /// /// Projected class type /// private static void ValidateType(Type type) { if (!Classes.ContainsKey(type)) { throw new InvalidOperationException($"{type.Name} is not a projected class type."); } } } } ================================================ FILE: src/Microsoft.Management.Deployment.Projection/Initializers/ActivationFactoryInstanceInitializer.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection { using Microsoft.Management.Deployment.Projection.Initializers; /// /// Activation factory instance initializer requires that: /// - DllGetActivationFactory is exported /// More details: https://github.com/microsoft/CsWinRT/blob/master/docs/hosting.md#exports /// - Host dll file name should be Microsoft.Management.Deployment.dll or a child namespace /// to match the projected classes (e.g. PackageManager) namespace /// More details: https://docs.microsoft.com/en-us/windows/apps/develop/platform/csharp-winrt/#winrt-type-activation /// public class ActivationFactoryInstanceInitializer : PolicyEnforcedInstanceInitializer { /// /// In-process context /// public override ClsidContext Context => ClsidContext.InProc; /// /// Calls default projected class constructor implemented by CsWinRT. /// Default constructor uses DllGetActivationFactory to create an object /// based on the full name of the projected class. /// /// Projected class type /// Instance of the provided type. protected override T CreateInstanceInternal() => new(); } } ================================================ FILE: src/Microsoft.Management.Deployment.Projection/Initializers/IInstanceInitializer.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection { public interface IInstanceInitializer { /// /// CLSID context. /// public ClsidContext Context { get; } /// /// Create an in-process or out-of process instance. /// /// Projected class typ /// Projected class instance public T CreateInstance() where T : new(); } } ================================================ FILE: src/Microsoft.Management.Deployment.Projection/Initializers/LocalServerInstanceInitializer.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection { using Microsoft.Management.Deployment.Projection.Initializers; using WinRT; // Out-of-process COM instance initializer. public class LocalServerInstanceInitializer : PolicyEnforcedInstanceInitializer { /// /// Out-of-process context. /// public override ClsidContext Context => UseDevClsids ? ClsidContext.OutOfProcDev : ClsidContext.OutOfProc; /// /// Allow lower trust registration. /// This is useful when running COM client with admin privileges. (e.g. E2E tests) /// public bool AllowLowerTrustRegistration { init; get; } /// /// Use Prod or Dev Clsids /// public bool UseDevClsids { init; get; } /// /// Create instance of the provided type. /// /// Projected class type. /// Instance of the provided type. protected override T CreateInstanceInternal() { var clsid = ClassesDefinition.GetClsid(Context); var iid = ClassesDefinition.GetIid(); var instanceInPtr = ComUtils.CoCreateInstanceLocalServer(clsid, iid, AllowLowerTrustRegistration); return MarshalGeneric.FromAbi(instanceInPtr); } } } ================================================ FILE: src/Microsoft.Management.Deployment.Projection/Initializers/PolicyEnforcedInstanceInitializer.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection.Initializers { using Microsoft.WinGet.SharedLib.Exceptions; using Microsoft.WinGet.SharedLib.PolicySettings; /// /// An abstract base that enforces group policy before creating derived class instance. /// public abstract class PolicyEnforcedInstanceInitializer : IInstanceInitializer { /// /// CLSID context. /// public abstract ClsidContext Context { get; } /// /// Create instance of the provided type. /// /// Projected class type. /// Instance of the provided type. public T CreateInstance() where T : new() { GroupPolicy groupPolicy = GroupPolicy.GetInstance(); if (!groupPolicy.IsEnabled(Policy.WinGet)) { throw new GroupPolicyException(Policy.WinGet, GroupPolicyFailureType.BlockedByPolicy); } return this.CreateInstanceInternal(); } protected abstract T CreateInstanceInternal() where T : new(); } } ================================================ FILE: src/Microsoft.Management.Deployment.Projection/Microsoft.Management.Deployment.Projection.csproj ================================================ net8.0-windows $(SolutionDir)$(Platform)\$(Configuration)\Microsoft.Management.Deployment.Projection\ false x64;x86;arm64 Library Debug;Release;ReleaseStatic 10.0.26100.0 Microsoft.Management.Deployment;Windows.Foundation;Windows.System.ProcessorArchitecture Windows.Foundation.Diagnostics Content PreserveNewest True ================================================ FILE: src/Microsoft.Management.Deployment.Projection/Utils/ComUtils.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection { using System; using System.Runtime.InteropServices; internal static class ComUtils { [DllImport("api-ms-win-core-com-l1-1-0.dll")] private static extern unsafe int CoCreateInstance(ref Guid clsid, IntPtr outer, uint clsContext, ref Guid iid, IntPtr* instance); /// /// CLSCTX enumeration /// https://docs.microsoft.com/en-us/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx /// private enum CLSCTX : uint { CLSCTX_INPROC_SERVER = 0x1, CLSCTX_LOCAL_SERVER = 0x4, CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION = 0x4000000 } /// /// CoCreateInstance function /// https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance /// /// CLSID /// CLSCTX /// IID /// Interface pointer, or throw an exception if HRESULT was not successful. private static unsafe IntPtr CoCreateInstance(Guid clsid, CLSCTX clsContext, Guid iid) { IntPtr instanceIntPtr; int hr = CoCreateInstance(ref clsid, IntPtr.Zero, (uint)clsContext, ref iid, &instanceIntPtr); Marshal.ThrowExceptionForHR(hr); return instanceIntPtr; } /// /// CoCreateInstance with an out-of-process context. /// /// CLSID /// CLSCTX /// Allow lower trust registration /// > public static IntPtr CoCreateInstanceLocalServer(Guid clsid, Guid iid, bool allowLowerTrustRegistration = false) { CLSCTX clsctx = CLSCTX.CLSCTX_LOCAL_SERVER; if (allowLowerTrustRegistration) { clsctx |= CLSCTX.CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION; } return CoCreateInstance(clsid, clsctx, iid); } } } ================================================ FILE: src/Microsoft.Management.Deployment.Projection/WinGetProjectionFactory.cs ================================================ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Management.Deployment.Projection { /// /// Factory class to created CsWinRT projected class instances for in-process or out-of-process objects. /// public class WinGetProjectionFactory { public WinGetProjectionFactory(IInstanceInitializer instanceInitializer) { InstanceInitializer = instanceInitializer; } private IInstanceInitializer InstanceInitializer { get; set; } public ClsidContext Context => InstanceInitializer.Context; public PackageManager CreatePackageManager() => InstanceInitializer.CreateInstance(); public FindPackagesOptions CreateFindPackagesOptions() => InstanceInitializer.CreateInstance(); public CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions() => InstanceInitializer.CreateInstance(); public InstallOptions CreateInstallOptions() => InstanceInitializer.CreateInstance(); public UninstallOptions CreateUninstallOptions() => InstanceInitializer.CreateInstance(); public DownloadOptions CreateDownloadOptions() => InstanceInitializer.CreateInstance(); public PackageMatchFilter CreatePackageMatchFilter() => InstanceInitializer.CreateInstance(); public AuthenticationArguments CreateAuthenticationArguments() => InstanceInitializer.CreateInstance(); public PackageManagerSettings CreatePackageManagerSettings() => InstanceInitializer.CreateInstance(); public RepairOptions CreateRepairOptions() => InstanceInitializer.CreateInstance(); public AddPackageCatalogOptions CreateAddPackageCatalogOptions() => InstanceInitializer.CreateInstance(); public RemovePackageCatalogOptions CreateRemovePackageCatalogOptions() => InstanceInitializer.CreateInstance(); public EditPackageCatalogOptions CreateEditPackageCatalogOptions() => InstanceInitializer.CreateInstance(); } } ================================================ FILE: src/PowerShell/CommonFiles/PowerShellCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Common.Command { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Management.Automation; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; using Microsoft.WinGet.Resources; using Microsoft.WinGet.SharedLib.Exceptions; using Microsoft.WinGet.SharedLib.PolicySettings; /// /// This must be the base class for every cmdlet for winget PowerShell modules. /// It supports: /// - Async operations. /// - Execute on an MTA. If the thread is already running on an MTA it will executed it, otherwise /// it will create a new MTA thread. /// Wait must be used to synchronously wait con the task. /// public abstract class PowerShellCmdlet { private const string Debug = "Debug"; private static readonly string[] WriteInformationTags = new string[] { "PSHOST" }; private readonly PSCmdlet psCmdlet; private readonly Thread pwshThread; private readonly CancellationTokenSource source = new (); private readonly SemaphoreSlim semaphore = new (1, 1); private readonly ManualResetEventSlim pwshThreadActionReady = new (false); private readonly ManualResetEventSlim pwshThreadActionCompleted = new (false); private BlockingCollection queuedStreams = new (); private int progressActivityId = 0; private ConcurrentDictionary progressRecords = new (); private Action? pwshThreadAction = null; private ExceptionDispatchInfo? pwshThreadEdi = null; /// /// Initializes a new instance of the class. /// /// PSCmdlet. /// Policies. public PowerShellCmdlet(PSCmdlet psCmdlet, HashSet policies) { // Passing Debug will make all the message actions to be Inquire. For async operations // and the current queue message implementation this doesn't make sense. // PowerShell will inquire for any message giving the impression that the task is // paused, but the async operation is still running. if (psCmdlet.MyInvocation.BoundParameters.ContainsKey(Debug)) { throw new NotSupportedException(Resources.DebugNotSupported); } this.ValidatePolicies(policies); this.psCmdlet = psCmdlet; this.pwshThread = Thread.CurrentThread; } /// /// Request cancellation for this command. /// public void Cancel() { this.source.Cancel(); } /// /// Execute the delegate in a MTA thread. /// Caller must wait on task. /// /// Function to execute. /// A representing the asynchronous operation. internal Task RunOnMTA(Func func) { // .NET 4.8 doesn't support TaskCompletionSource. #if POWERSHELL_WINDOWS throw new NotImplementedException(); #else // This must be called in the main thread. if (this.pwshThread != Thread.CurrentThread) { throw new InvalidOperationException(); } if (Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA) { this.Write(StreamType.Verbose, "Already running on MTA"); try { Task result = func(); result.ContinueWith((task) => this.Complete(), TaskContinuationOptions.ExecuteSynchronously); return result; } catch { this.Complete(); throw; } } this.Write(StreamType.Verbose, "Creating MTA thread"); var tcs = new TaskCompletionSource(); var thread = new Thread(() => { try { func().GetAwaiter().GetResult(); tcs.SetResult(); } catch (Exception e) { tcs.SetException(e); } finally { this.Complete(); } }); thread.SetApartmentState(ApartmentState.MTA); thread.Start(); return tcs.Task; #endif } /// /// Execute the delegate in a MTA thread. /// Caller must wait on task. /// /// Function to execute. /// Return type of function. /// A representing the asynchronous operation. internal Task RunOnMTA(Func> func) { // This must be called in the main thread. if (this.pwshThread != Thread.CurrentThread) { throw new InvalidOperationException(); } if (Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA) { this.Write(StreamType.Verbose, "Already running on MTA"); try { Task result = func(); result.ContinueWith((task) => this.Complete(), TaskContinuationOptions.ExecuteSynchronously); return result; } catch { this.Complete(); throw; } } this.Write(StreamType.Verbose, "Creating MTA thread"); var tcs = new TaskCompletionSource(); var thread = new Thread(() => { try { var result = func().GetAwaiter().GetResult(); tcs.SetResult(result); } catch (Exception e) { tcs.SetException(e); } finally { this.Complete(); } }); thread.SetApartmentState(ApartmentState.MTA); thread.Start(); return tcs.Task; } /// /// Execute the delegate in a MTA thread. /// Synchronous call. /// /// Function to execute. /// Return type of function. /// A representing the asynchronous operation. internal TResult RunOnMTA(Func func) { // This must be called in the main thread. if (this.pwshThread != Thread.CurrentThread) { throw new InvalidOperationException(); } if (Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA) { this.Write(StreamType.Verbose, "Already running on MTA"); try { return func(); } finally { this.Complete(); } } this.Write(StreamType.Verbose, "Creating MTA thread"); var tcs = new TaskCompletionSource(); var thread = new Thread(() => { try { var result = func(); tcs.SetResult(result); } catch (Exception e) { tcs.SetException(e); } finally { this.Complete(); } }); thread.SetApartmentState(ApartmentState.MTA); thread.Start(); this.Wait(tcs.Task); return tcs.Task.Result; } /// /// Executes an action in the main thread. /// Blocks until call is executed. /// /// Action to perform. internal void ExecuteInPowerShellThread(Action action) { if (this.pwshThread == Thread.CurrentThread) { action(); return; } this.WaitForOurTurn(); this.pwshThreadAction = action; this.pwshThreadActionReady.Set(); this.WaitMainThreadActionCompletion(); } /// /// Waits for the task to be completed. This MUST be called from the main thread. /// /// Task to wait for. /// The cmdlet that can write to PowerShell. internal void Wait(Task runningTask, PowerShellCmdlet? writeCmdlet = null) { writeCmdlet ??= this; // This must be called in the main thread. if (this.pwshThread != Thread.CurrentThread) { throw new InvalidOperationException(); } do { if (this.pwshThreadActionReady.IsSet) { // Someone needs the main thread. this.pwshThreadActionReady.Reset(); if (this.pwshThreadAction != null) { try { this.pwshThreadEdi = null; this.pwshThreadAction(); } catch (Exception e) { // Make sure we don't throw in the PowerShell thread, this way // we'll get a more meaningful stack by Get-Error. this.pwshThreadEdi = ExceptionDispatchInfo.Capture(e); } this.pwshThreadAction = null; } // Done. this.pwshThreadActionCompleted.Set(); } // Take from the blocking collection. if (!this.queuedStreams.IsCompleted && this.queuedStreams.Count > 0) { try { var queuedOutput = this.queuedStreams.Take(); if (queuedOutput != null) { this.CmdletWrite(queuedOutput.Type, queuedOutput.Data, writeCmdlet); } } catch (InvalidOperationException) { // An InvalidOperationException means that Take() was called on a completed collection. } } } while (!(runningTask.IsCompleted && this.queuedStreams.IsCompleted)); if (runningTask.IsFaulted) { // If IsFaulted is true, the task's Status is equal to Faulted, // and its Exception property will be non-null. AggregateException? ae = runningTask.Exception! as AggregateException; if (ae != null && ae.InnerExceptions.Count == 1) { ExceptionDispatchInfo.Capture(ae.InnerExceptions[0]).Throw(); } throw runningTask.Exception!; } } /// /// Writes into the corresponding stream if running on the main thread. /// Otherwise queue the message. /// Is the caller responsibility to use the correct types. /// /// Stream type. /// Data. internal void Write(StreamType type, object data) { if (type == StreamType.Progress) { ProgressRecord progressRecord = (ProgressRecord)data; if (progressRecord.RecordType == ProgressRecordType.Completed) { throw new NotSupportedException("Use CompleteProgress"); } // Keep track of all progress activity. _ = this.progressRecords.TryAdd(progressRecord.ActivityId, progressRecord.RecordType); } if (this.pwshThread == Thread.CurrentThread) { this.CmdletWrite(type, data, this); return; } this.queuedStreams.Add(new QueuedStream(type, data)); } /// /// Helper to compute percentage and write progress for processing activities. /// /// Activity id. /// The activity in progress. /// The status of the activity. /// Number of completed actions. /// The expected total. internal void WriteProgressWithPercentage(int activityId, string activity, string status, int completed, int total) { double percentComplete = (double)completed / total; var record = new ProgressRecord(activityId, activity, status) { RecordType = ProgressRecordType.Processing, PercentComplete = (int)(100.0 * percentComplete), }; this.Write(StreamType.Progress, record); } /// /// Helper to complete progress records. /// /// Activity id. /// The activity in progress. /// The status of the activity. /// Force write complete progress. internal void CompleteProgress(int activityId, string activity, string status, bool force = false) { var record = new ProgressRecord(activityId, activity, status) { RecordType = ProgressRecordType.Completed, PercentComplete = 100, }; if (!this.progressRecords.TryAdd(activityId, record.RecordType)) { _ = this.progressRecords.TryUpdate(activityId, record.RecordType, ProgressRecordType.Processing); } if (this.pwshThread == Thread.CurrentThread) { this.CmdletWrite(StreamType.Progress, record, this); } else { // You should only use force if you know the cmdlet that is completing this progress is a sync cmdlet that // is running in an async context. A sync cmdlet is anything that doesn't start with Start-* if (force) { this.ExecuteInPowerShellThread(() => this.CmdletWrite(StreamType.Progress, record, this)); } else { this.queuedStreams.Add(new QueuedStream(StreamType.Progress, record)); } } } /// /// Writes to PowerShell streams. /// This method must be called in the original thread. /// WARNING: You must only call this when the task is completed. /// /// The cmdlet that can write to PowerShell. internal void ConsumeAndWriteStreams(PowerShellCmdlet writeCmdlet) { // This must be called in the main thread. if (this.pwshThread != Thread.CurrentThread) { throw new InvalidOperationException(); } // Take from the blocking collection until is completed. try { while (true) { var queuedOutput = this.queuedStreams.Take(); if (queuedOutput != null) { this.CmdletWrite(queuedOutput.Type, queuedOutput.Data, writeCmdlet); } } } catch (InvalidOperationException) { // We are done. // An InvalidOperationException means that Take() was called on a completed collection. } } /// /// Gets a new progress activity id. /// /// The new progress record id. internal int GetNewProgressActivityId() { return Interlocked.Increment(ref this.progressActivityId); } /// /// Gets the cancellation token. /// /// CancellationToken. internal CancellationToken GetCancellationToken() { return this.source.Token; } /// /// Gets the current file system location from the cmdlet. /// /// Path. internal string GetCurrentFileSystemLocation() { return this.psCmdlet.SessionState.Path.CurrentFileSystemLocation.Path; } /// /// Sets a variable. /// /// Variable name. /// Value. internal void SetVariable(string variableName, object value) { this.psCmdlet.SessionState.PSVariable.Set(variableName, value); } /// /// Prompts the user if it should continue processing if possible. /// /// Message. /// If the operation should continue. internal bool ShouldProcess(string target) { // If not on the main thread just continue. if (this.pwshThread != Thread.CurrentThread) { return true; } return this.psCmdlet.ShouldProcess(target); } private void Complete() { this.queuedStreams.CompleteAdding(); } private void CmdletWrite(StreamType streamType, object data, PowerShellCmdlet writeCmdlet) { switch (streamType) { case StreamType.Debug: throw new NotSupportedException(); case StreamType.Verbose: writeCmdlet.psCmdlet.WriteVerbose((string)data); break; case StreamType.Warning: writeCmdlet.psCmdlet.WriteWarning((string)data); break; case StreamType.Error: writeCmdlet.psCmdlet.WriteError((ErrorRecord)data); break; case StreamType.Progress: // If the activity is already completed don't write progress. var progressRecord = (ProgressRecord)data; if (this.progressRecords[progressRecord.ActivityId] == progressRecord.RecordType) { writeCmdlet.psCmdlet.WriteProgress(progressRecord); } break; case StreamType.Object: writeCmdlet.psCmdlet.WriteObject(data); break; case StreamType.Information: writeCmdlet.psCmdlet.WriteInformation(data, WriteInformationTags); break; } } private void ValidatePolicies(HashSet policies) { GroupPolicy groupPolicy = GroupPolicy.GetInstance(); if (policies.Contains(Policy.WinGet)) { if (!groupPolicy.IsEnabled(Policy.WinGet)) { throw new GroupPolicyException(Policy.WinGet, GroupPolicyFailureType.BlockedByPolicy); } policies.Remove(Policy.WinGet); } if (policies.Contains(Policy.Configuration)) { if (!groupPolicy.IsEnabled(Policy.Configuration)) { throw new GroupPolicyException(Policy.Configuration, GroupPolicyFailureType.BlockedByPolicy); } policies.Remove(Policy.Configuration); } if (policies.Contains(Policy.WinGetCommandLineInterfaces)) { if (!groupPolicy.IsEnabled(Policy.WinGetCommandLineInterfaces)) { throw new GroupPolicyException(Policy.WinGetCommandLineInterfaces, GroupPolicyFailureType.BlockedByPolicy); } policies.Remove(Policy.WinGetCommandLineInterfaces); } if (policies.Count > 0) { throw new NotSupportedException($"Invalid policies {string.Join(",", policies)}"); } } private void WaitForOurTurn() { this.semaphore.Wait(this.GetCancellationToken()); this.pwshThreadActionCompleted.Reset(); } private void WaitMainThreadActionCompletion() { WaitHandle.WaitAny(new[] { this.GetCancellationToken().WaitHandle, this.pwshThreadActionCompleted.WaitHandle, }); try { if (this.pwshThreadEdi != null) { this.pwshThreadEdi.Throw(); } } finally { this.semaphore.Release(); } } private class QueuedStream { public QueuedStream(StreamType type, object data) { this.Type = type; this.Data = data; } public StreamType Type { get; } public object Data { get; } } } } ================================================ FILE: src/PowerShell/CommonFiles/StreamType.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Common.Command { /// /// The write stream type of the cmdlet. /// public enum StreamType { /// /// Debug. /// Debug, /// /// Verbose. /// Verbose, /// /// Warning. /// Warning, /// /// Error. /// Error, /// /// Progress. /// Progress, /// /// Object. /// Object, /// /// Information. /// Information, } } ================================================ FILE: src/PowerShell/CommonFiles/WinGetAssemblyLoadContext.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- #if !POWERSHELL_WINDOWS namespace Microsoft.WinGet.Resolver { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Loader; /// /// Custom assembly load context for this module. /// This helps us load our dependencies without carrying about apps importing this module. /// All dependencies except the Engine dll needs to be under a Dependencies directory. /// internal class WinGetAssemblyLoadContext : AssemblyLoadContext { // The assemblies must be loaded in the default context. // Loading WinRT.Runtime.dll in an ALC when is already loaded in the default context // will result on 'Attempt to update previously set global instance.' private static readonly IEnumerable DefaultContextAssemblies = new string[] { @"WinRT.Runtime.dll", }; private static readonly string SharedDependencyPath; private static readonly string SharedArchDependencyPath; private static readonly string DirectDependencyPath; private static readonly WinGetAssemblyLoadContext WinGetAcl = new (); static WinGetAssemblyLoadContext() { var self = typeof(WinGetAssemblyLoadContext).Assembly; SharedDependencyPath = Path.Combine( Path.GetDirectoryName(self.Location), "SharedDependencies"); SharedArchDependencyPath = Path.Combine( SharedDependencyPath, RuntimeInformation.ProcessArchitecture.ToString().ToLower()); DirectDependencyPath = Path.Combine( Path.GetDirectoryName(self.Location), "DirectDependencies"); } private WinGetAssemblyLoadContext() : base("WinGetAssemblyLoadContext", isCollectible: false) { } /// /// Handler to resolve assemblies. /// /// Assembly load context. /// Assembly name. /// The assembly, null if not in our assembly location. internal static Assembly ResolvingHandler(AssemblyLoadContext context, AssemblyName assemblyName) { string name = $"{assemblyName.Name}.dll"; if (DefaultContextAssemblies.Any(a => a.Equals(name, StringComparison.OrdinalIgnoreCase))) { string sharedPath = Path.Combine(SharedDependencyPath, name); if (File.Exists(sharedPath)) { return AssemblyLoadContext.Default.LoadFromAssemblyPath(sharedPath); } } string path = Path.Combine(DirectDependencyPath, name); if (File.Exists(path)) { return WinGetAcl.LoadFromAssemblyName(assemblyName); } return null; } /// /// Handler to resolve unmanaged assemblies. /// /// Assembly initiating the unmanaged load. /// Unmanaged dll name. /// The assembly ptr, zero if not in our assembly location. internal static IntPtr ResolvingUnmanagedDllHandler(Assembly assembly, string unmanagedDllName) { return WinGetAcl.LoadUnmanagedDll(unmanagedDllName); } /// protected override Assembly Load(AssemblyName assemblyName) { string name = $"{assemblyName.Name}.dll"; if (DefaultContextAssemblies.Any(a => a.Equals(name, StringComparison.OrdinalIgnoreCase))) { return null; } string path = Path.Combine(SharedDependencyPath, name); if (File.Exists(path)) { return this.LoadFromAssemblyPath(path); } path = Path.Combine(SharedArchDependencyPath, name); if (File.Exists(path)) { return this.LoadFromAssemblyPath(path); } path = Path.Combine(DirectDependencyPath, name); if (File.Exists(path)) { return this.LoadFromAssemblyPath(path); } return null; } /// protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { string path = Path.Combine(SharedArchDependencyPath, unmanagedDllName); if (File.Exists(path)) { return this.LoadUnmanagedDllFromPath(path); } return IntPtr.Zero; } } } #endif ================================================ FILE: src/PowerShell/ExternalModules/PackageManagement/1.4.8.1/DSCResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 ================================================ # # Copyright (c) Microsoft Corporation. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # This PS DSC resource enables installing a package. The resource uses Install-Package cmdlet # to install the package from various providers/sources. Import-LocalizedData -BindingVariable LocalizedData -filename MSFT_PackageManagement.strings.psd1 Import-Module -Name "$PSScriptRoot\..\PackageManagementDscUtilities.psm1" function Get-TargetResource { <# .SYNOPSIS This DSC resource provides a mechanism to download and install packages on a computer. Get-TargetResource returns the current state of the resource. .PARAMETER Name Specifies the name of the Package to be installed or uninstalled. .PARAMETER Source Specifies the name of the package source where the package can be found. This can either be a URI or a source registered with Register-PackageSource cmdlet. The DSC resource MSFT_PackageManagementSource can also register a package source. .PARAMETER RequiredVersion Specifies the exact version of the package that you want to install. If you do not specify this parameter, this DSC resource installs the newest available version of the package that also satisfies any maximum version specified by the MaximumVersion parameter. .PARAMETER MaximumVersion Specifies the maximum allowed version of the package that you want to install. If you do not specify this parameter, this DSC resource installs the highest-numbered available version of the package. .PARAMETER MinimumVersion Specifies the minimum allowed version of the package that you want to install. If you do not add this parameter, this DSC resource intalls the highest available version of the package that also satisfies any maximum specified version specified by the MaximumVersion parameter. .PARAMETER SourceCredential Specifies a user account that has rights to install a package for a specified package provider or source. .PARAMETER ProviderName Specifies a package provider name to which to scope your package search. You can get package provider names by running the Get-PackageProvider cmdlet. .PARAMETER AdditionalParameters Provider specific parameters that are passed as an Hashtable. For example, for NuGet provider you can pass additional parameters like DestinationPath. #> [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.String] $MaximumVersion, [Parameter()] [System.String] $Source, [Parameter()] [PSCredential] $SourceCredential, [Parameter()] [System.String] $ProviderName, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]]$AdditionalParameters ) $ensure = "Absent" $null = $PSBoundParameters.Remove("Source") $null = $PSBoundParameters.Remove("SourceCredential") if ($AdditionalParameters) { foreach($instance in $AdditionalParameters) { Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value) $null = $PSBoundParameters.Add($instance.Key, $instance.Value) } } $null = $PSBoundParameters.Remove("AdditionalParameters") $verboseMessage =$localizedData.StartGetPackage -f (GetMessageFromParameterDictionary $PSBoundParameters),$env:PSModulePath Write-Verbose -Message $verboseMessage $result = PackageManagement\Get-Package @PSBoundParameters -ErrorAction SilentlyContinue -WarningAction SilentlyContinue if ($result.count -eq 1) { Write-Verbose -Message ($localizedData.PackageFound -f $Name) $ensure = "Present" } elseif ($result.count -gt 1) { Write-Verbose -Message ($localizedData.MultiplePackagesFound -f $Name) $ensure = "Present" } else { Write-Verbose -Message ($localizedData.PackageNotFound -f $($Name)) } Write-Debug -Message "Source $($Name) is $($ensure)" if ($ensure -eq 'Absent') { return @{ Ensure = $ensure Name = $Name ProviderName = $ProviderName RequiredVersion = $RequiredVersion MinimumVersion = $MinimumVersion MaximumVersion = $MaximumVersion } } else { if ($result.Count -gt 1) { $result = $result[0] } return @{ Ensure = $ensure Name = $result.Name ProviderName = $result.ProviderName Source = $result.source RequiredVersion = $result.Version } } } function Test-TargetResource { <# .SYNOPSIS This DSC resource provides a mechanism to download and install packages on a computer. Test-TargetResource returns a boolean which determines whether the resource is in desired state or not. .PARAMETER Name Specifies the name of the Package to be installed or uninstalled. .PARAMETER Source Specifies the name of the package source where the package can be found. This can either be a URI or a source registered with Register-PackageSource cmdlet. The DSC resource MSFT_PackageManagementSource can also register a package source. .PARAMETER RequiredVersion Specifies the exact version of the package that you want to install. If you do not specify this parameter, this DSC resource installs the newest available version of the package that also satisfies any maximum version specified by the MaximumVersion parameter. .PARAMETER MaximumVersion Specifies the maximum allowed version of the package that you want to install. If you do not specify this parameter, this DSC resource installs the highest-numbered available version of the package. .PARAMETER MinimumVersion Specifies the minimum allowed version of the package that you want to install. If you do not add this parameter, this DSC resource intalls the highest available version of the package that also satisfies any maximum specified version specified by the MaximumVersion parameter. .PARAMETER SourceCredential Specifies a user account that has rights to install a package for a specified package provider or source. .PARAMETER ProviderName Specifies a package provider name to which to scope your package search. You can get package provider names by running the Get-PackageProvider cmdlet. .PARAMETER AdditionalParameters Provider specific parameters that are passed as an Hashtable. For example, for NuGet provider you can pass additional parameters like DestinationPath. #> [CmdletBinding()] [OutputType([bool])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.String] $MaximumVersion, [Parameter()] [System.String] $Source, [Parameter()] [PSCredential] $SourceCredential, [ValidateSet("Present","Absent")] [System.String] $Ensure="Present", [Parameter()] [System.String] $ProviderName, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]]$AdditionalParameters ) Write-Verbose -Message ($localizedData.StartTestPackage -f (GetMessageFromParameterDictionary $PSBoundParameters)) $null = $PSBoundParameters.Remove("Ensure") $temp = Get-TargetResource @PSBoundParameters if ($temp.Ensure -eq $ensure) { Write-Verbose -Message ($localizedData.InDesiredState -f $Name, $Ensure, $temp.Ensure) return $True } else { Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name,$ensure,$temp.ensure) return [bool] $False } } function Set-TargetResource { <# .SYNOPSIS This DSC resource provides a mechanism to download and install packages on a computer. Set-TargetResource either intalls or uninstall a package as defined by the vaule of Ensure parameter. .PARAMETER Name Specifies the name of the Package to be installed or uninstalled. .PARAMETER Source Specifies the name of the package source where the package can be found. This can either be a URI or a source registered with Register-PackageSource cmdlet. The DSC resource MSFT_PackageManagementSource can also register a package source. .PARAMETER RequiredVersion Specifies the exact version of the package that you want to install. If you do not specify this parameter, this DSC resource installs the newest available version of the package that also satisfies any maximum version specified by the MaximumVersion parameter. .PARAMETER MaximumVersion Specifies the maximum allowed version of the package that you want to install. If you do not specify this parameter, this DSC resource installs the highest-numbered available version of the package. .PARAMETER MinimumVersion Specifies the minimum allowed version of the package that you want to install. If you do not add this parameter, this DSC resource intalls the highest available version of the package that also satisfies any maximum specified version specified by the MaximumVersion parameter. .PARAMETER SourceCredential Specifies a user account that has rights to install a package for a specified package provider or source. .PARAMETER ProviderName Specifies a package provider name to which to scope your package search. You can get package provider names by running the Get-PackageProvider cmdlet. .PARAMETER AdditionalParameters Provider specific parameters that are passed as an Hashtable. For example, for NuGet provider you can pass additional parameters like DestinationPath. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.String] $MaximumVersion, [Parameter()] [System.String] $Source, [Parameter()] [PSCredential] $SourceCredential, [ValidateSet("Present","Absent")] [System.String] $Ensure="Present", [Parameter()] [System.String] $ProviderName, [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]]$AdditionalParameters ) Write-Verbose -Message ($localizedData.StartSetPackage -f (GetMessageFromParameterDictionary $PSBoundParameters)) $null = $PSBoundParameters.Remove("Ensure") if ($PSBoundParameters.ContainsKey("SourceCredential")) { $PSBoundParameters.Add("Credential", $SourceCredential) $null = $PSBoundParameters.Remove("SourceCredential") } if ($AdditionalParameters) { foreach($instance in $AdditionalParameters) { Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value) $null = $PSBoundParameters.Add($instance.Key, $instance.Value) } } $PSBoundParameters.Remove("AdditionalParameters") # We do not want others to control the behavior of ErrorAction # while calling Install-Package/Uninstall-Package. $PSBoundParameters.Remove("ErrorAction") if ($Ensure -eq "Present") { PackageManagement\Install-Package @PSBoundParameters -ErrorAction Stop } else { # we dont source location for uninstalling an already # installed package $PSBoundParameters.Remove("Source") # Ensure is Absent PackageManagement\Uninstall-Package @PSBoundParameters -ErrorAction Stop } } function GetMessageFromParameterDictionary { <# Returns a strng of form "ParameterName:ParameterValue" Used with Write-Verbose message. The input is mostly $PSBoundParameters #> param([System.Collections.IDictionary] $paramDictionary) $returnValue = "" $paramDictionary.Keys | ForEach-Object { $returnValue += "-{0} {1} " -f $_,$paramDictionary[$_] } return $returnValue } Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource # SIG # Begin signature block # MIInoQYJKoZIhvcNAQcCoIInkjCCJ44CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBNHExVsaD0kyJT # faO9Fdru5c5dZNw/I2f/WOIZwqnZQKCCDYEwggX/MIID56ADAgECAhMzAAACUosz # qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I # sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O # L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA # v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o # RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8 # q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3 # uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp # kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7 # l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u # TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1 # o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti # yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z # 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf # 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK # WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW # esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F # 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZdjCCGXICAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg8R1Pmfow # cdRZqYArE66BbwlcVzLXx5t2tD5hSPvyDEowQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQAeXps4tjAFtlAdjZLwORhr3TtESrcKHZnVBHZ0zYbM # unaWRYc0OysS4qo+BomxtH9p6wL8QAOILHlQvCYdMoEI2jbAbBzb61wFHb8g2qeX # l5dLpSgZnOU+Ej+r2EieXi4DSYANTCOsEVnHWa+9tYZ1qZYB7dDysy0hMJ9F0lpI # FLhooei9J7aB5z1sSUEpBqoEgSn/+unhSlHMTasmYyzxBsb+KoFbwzavZPFluy5R # HmWt7m0HSfkzScoYM0CUpnCRAyQELq/ubtCIbxbDfddl4JGJToAABc3akuXY9WDc # 2MdT2gqPPX0+Acp861u/hlYfiZipnoE0K3cSZ6WJH2JvoYIXADCCFvwGCisGAQQB # gjcDAwExghbsMIIW6AYJKoZIhvcNAQcCoIIW2TCCFtUCAQMxDzANBglghkgBZQME # AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEICEiVK6WSVgxKzH21qRCzrs8poUE/CmvuRL5UN+N # A1gpAgZitMncVAUYEzIwMjIwNzAxMjEwMDAwLjM2NVowBIACAfSggdCkgc0wgcox # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # RVNOOjIyNjQtRTMzRS03ODBDMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNloIIRVzCCBwwwggT0oAMCAQICEzMAAAGYdrOMxdAFoQEAAQAAAZgw # DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN # MjExMjAyMTkwNTE1WhcNMjMwMjI4MTkwNTE1WjCByjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046MjI2NC1FMzNFLTc4 # MEMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0G # CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG1JWsVksp8xG4sLMnfxfit3ShI+7G # 1MfTT+5XvQzuAOe8r5MRAFITTmjFxzoLFfmaxLvPVlmDgkDi0rqsOs9Al9jVwYSF # VF/wWC2+B76OysiyRjw+NPj5A4cmMhPqIdNkRLCE+wtuI/wCaq3/Lf4koDGudIcE # YRgMqqToOOUIV4e7EdYb3k9rYPN7SslwsLFSp+Fvm/Qcy5KqfkmMX4S3oJx7HdiQ # hKbK1C6Zfib+761bmrdPLT6eddlnywls7hCrIIuFtgUbUj6KJIZn1MbYY8hrAM59 # tvLpeGmFW3GjeBAmvBxAn7o9Lp2nykT1w9I0s9ddwpFnjLT2PK74GDSsxFUZG1Ut # Lypi/kZcg9WenPAZpUtPFfO5Mtif8Ja8jXXLIP6K+b5LiQV8oIxFSBfgFN7/TL2t # SSfQVcvqX1mcSOrx/tsgq3L6YAxI6Pl4h1zQrcAmToypEoPYNc/RlSBk6ljmNyND # sX3gtK8p6c7HCWUhF+YjMgfanQmMjUYsbjdEsCyL6QAojZ0f6kteN4cV6obFwcUE # viYygWbedaT86OGe9LEOxPuhzgFv2ZobVr0J8hl1FVdcZFbfFN/gdjHZ/ncDDqLN # WgcoMoEhwwzo7FAObqKaxfB5zCBqYSj45miNO5g3hP8AgC0eSCHl3rK7JPMr1B+8 # JTHtwRkSKz/+cwIDAQABo4IBNjCCATIwHQYDVR0OBBYEFG6RhHKNpsg3mgons7LR # 5YHTzeE3MB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRY # MFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p # Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEF # BQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w # a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo # MSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI # hvcNAQELBQADggIBACT6B6F33i/89zXTgqQ8L6CYMHx9BiaHOV+wk53JOriCzeaL # jYgRyssJhmnnJ/CdHa5qjcSwvRptWpZJPVK5sxhOIjRBPgs/3+ER0vS87IA+aGbf # 7NF7LZZlxWPOl/yFBg9qZ3tpOGOohQInQn5zpV23hWopaN4c49jGJHLPAfy9u7+Z # SGQuw14CsW/XRLELHT18I60W0uKOBa5Pm2ViohMovcbpNUCEERqIO9WPwzIwMRRw # 34/LgjuslHJop+/1Ve/CfyNqweUmwepQHJrd+wTLUlgm4ENbXF6i52jFfYpESwLd # An56o/pj+grsd2LrAEPQRyh49rWvI/qZfOhtT2FWmzFw6IJvZ7CzT1O+Fc0gIDBN # qass5QbmkOkKYy9U7nFA6qn3ZZ+MrZMsJTj7gxAf0yMkVqwYWZRk4brY9q8JDPmc # fNSjRrVfpYyzEVEqemGanmxvDDTzS2wkSBa3zcNwOgYhWBTmJdLgyiWJGeqyj1m5 # bwNgnOw6NzXCiVMzfbztdkqOdTR88LtAJGNRjevWjQd5XitGuegSp2mMJglFzRwk # ncQau1BJsCj/1aDY4oMiO8conkmaWBrYe11QCS896/sZwSdnEUJak0qpnBRFB+TH # RIxIivCKNbxG2QRZ8dh95cOXgo0YvBN5a1p+iJ3vNwzneU2AIC7z3rrIbN2fMIIH # cTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB # iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp # TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEw # OTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIh # C3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNx # WuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFc # UTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAc # nVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUo # veO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyzi # YrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9 # fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdH # GO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7X # KHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiE # R9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/ # eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3 # FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAd # BgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEE # AYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMI # MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB # Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud # HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By # b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO # MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy # dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4IC # AQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pk # bHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gng # ugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3 # lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHC # gRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6 # MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEU # BHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvsh # VGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+ # fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrp # NPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHI # qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAs4wggI3AgEBMIH4 # oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw # IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1U # aGFsZXMgVFNTIEVTTjoyMjY0LUUzM0UtNzgwQzElMCMGA1UEAxMcTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUA8ywe/iF5M8fIU2aT # 6yQ3vnPpV5OggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN # BgkqhkiG9w0BAQUFAAIFAOZp1AIwIhgPMjAyMjA3MDIwNDEzNTRaGA8yMDIyMDcw # MzA0MTM1NFowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA5mnUAgIBADAKAgEAAgIN # NAIB/zAHAgEAAgIRtDAKAgUA5mslggIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor # BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUA # A4GBAIcnMC784m6T/edBW3cq9r/yFVNA0QEvStf82Kw/R7tlaHDt097cO2b04KMC # V009JquDovHm5hKa95HNl/EcSVOy0XSzCkkFRCFfHvQyCepHU+f2lAfJkKPfQoBH # UYXvMMVFDB2X/i6sM5rB50pw+es9vpAGic/VoX7HQkPz/x74MYIEDTCCBAkCAQEw # gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAGYdrOMxdAFoQEA # AQAAAZgwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B # CRABBDAvBgkqhkiG9w0BCQQxIgQgPSBQh4cqea+akWI4bmlSCCKf/KlFhtOwg6A6 # 6ghYSTQwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCC/ps4GOTn/9wO1NhHM # 9Qfe0loB3slkw1FF3r+bh21WxDCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAABmHazjMXQBaEBAAEAAAGYMCIEIES78xtiuDaV9ywXzTl8 # XWUH8mqeO6XZWBLLhxayXSClMA0GCSqGSIb3DQEBCwUABIICAJXsTUtHPNUMQu1x # h6OSxx18cwjD6ncubNtd0T2LpuIaJ6P+Dog/ZNDl2qUBHPn6UvCdrCQSVXxWyVKj # bsPvxf7gLJPmCOlCgQ+F4ocGoMx759e0H9NWGUD1OczeBEkcVtwycN4ZboslQbiP # W32gbaZFOaujV+Xigrl4OtIebOZXH47ys+B8rSHX+sb2wejdUy5UyUexiLg6t1Jj # Y253gM9vW5GqRpseJwAZ7gukmjvEjeCpC16l2hNi++vo8ktAkduWDsz6RbDBqqz+ # g9FjOkAPZ//+eA0BGHrOwn2Piw8UCSl48hInvC/hqvS67T2ysb+nTy/Wn2BUHdaa # 6bfaGh0nqclQjWjkRfcxPxk1b61mYErRaBmqZvgrQK/FiFA/TYt1vegvpDKrz2Ey # /hKY2GaAKlDE2fjRRvUgzVXw7j9ZbJfmrTC/QzXvnfYyAGvdj5M93DwmOWL4rfRH # QDQVxegfmlrolnZUE5BirFk4mdZT7mihghMO8POWpo2oG5ehqasflzQNaXyq97w7 # tLe78SK0CBIqDuEbOVznD1/wDCLha0iYwKmBHmhk5ElB36whfabC6PvHS5jndpSa # STUZG+2PcqUTGR3CVrZOVarkLXshyHgDSJfkS5aCW4GPDsqWmhznCoIZWDdlkak1 # vE5R3wCfHoZ7HLzzkdvfZlIhuwOn # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PackageManagement/1.4.8.1/DSCResources/PackageManagementDscUtilities.psm1 ================================================ # # Copyright (c) Microsoft Corporation. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # #Helper functions for PackageManagement DSC Resouces Import-LocalizedData -BindingVariable LocalizedData -filename PackageManagementDscUtilities.strings.psd1 Function ExtractArguments { <# .SYNOPSIS This is a helper function that extract the parameters from a given table. .PARAMETER FunctionBoundParameters Specifies the hashtable containing a set of parameters to be extracted .PARAMETER ArgumentNames Specifies A list of arguments you want to extract #> Param ( [parameter(Mandatory = $true)] [System.Collections.Hashtable] $FunctionBoundParameters, #A list of arguments you want to extract [parameter(Mandatory = $true)] [System.String[]]$ArgumentNames ) Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) $returnValue=@{} foreach ($arg in $ArgumentNames) { if($FunctionBoundParameters.ContainsKey($arg)) { #Found an argument we are looking for, so we add it to return collection $returnValue.Add($arg,$FunctionBoundParameters[$arg]) } } return $returnValue } function ThrowError { <# .SYNOPSIS This is a helper function that throws an error. .PARAMETER ExceptionName Specifies the type of errors, e.g. System.ArgumentException .PARAMETER ExceptionMessage Specifies the exception message .PARAMETER ErrorId Specifies an identifier of the error .PARAMETER ErrorCategory Specifies the error category, e.g., InvalidArgument defined in System.Management.Automation. #> param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ExceptionName, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ExceptionMessage, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ErrorId, [parameter(Mandatory = $true)] [ValidateNotNull()] [System.Management.Automation.ErrorCategory] $ErrorCategory ) Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) $exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage; $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList ($exception, $ErrorId, $ErrorCategory, $null) throw $errorRecord } Function ValidateArgument { <# .SYNOPSIS This is a helper function that validates the arguments. .PARAMETER Argument Specifies the argument to be validated. .PARAMETER Type Specifies the type of argument. #> [CmdletBinding()] param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Argument, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$Type, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$ProviderName ) Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) switch ($Type) { "SourceUri" { # Checks whether given URI represents specific scheme # Most common schemes: file, http, https, ftp $scheme =@('http', 'https', 'file', 'ftp') $newUri = $Argument -as [System.URI] $returnValue = ($newUri -and $newUri.AbsoluteURI -and ($scheme -icontains $newuri.Scheme)) if ($returnValue -eq $false) { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` -ErrorId "InValidUri" ` -ErrorCategory InvalidArgument } #Check whether it's a valid uri. Wait for the response within 2mins. <#$result = Invoke-WebRequest $newUri -TimeoutSec 120 -UseBasicParsing -ErrorAction SilentlyContinue if ($null -eq (([xml]$result.Content).service )) { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.InValidUri -f $Argument)` -ErrorId "InValidUri" ` -ErrorCategory InvalidArgument }#> } "DestinationPath" { $returnValue = Test-Path -Path $Argument if ($returnValue -eq $false) { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.PathDoesNotExist -f $Argument)` -ErrorId "PathDoesNotExist" ` -ErrorCategory InvalidArgument } } "PackageSource" { #Argument can be either the package source Name or source Uri. #Check if the source is a uri $uri = $Argument -as [System.URI] if($uri -and $uri.AbsoluteURI) { # Check if it's a valid Uri ValidateArgument -Argument $Argument -Type "SourceUri" -ProviderName $ProviderName } else { #Check if it's a registered package source name $source = PackageManagement\Get-PackageSource -Name $Argument -ProviderName $ProviderName -verbose -ErrorVariable ev if ((-not $source) -or $ev) { #We do not need to throw error here as Get-PackageSource does already Write-Verbose -Message ($LocalizedData.SourceNotFound -f $source) } } } default { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.UnexpectedArgument -f $Type)` -ErrorId "UnexpectedArgument" ` -ErrorCategory InvalidArgument } } } Function ValidateVersionArgument { <# .SYNOPSIS This is a helper function that does the version validation. .PARAMETER RequiredVersion Provides the required version. .PARAMETER MaximumVersion Provides the maximum version. .PARAMETER MinimumVersion Provides the minimum version. #> [CmdletBinding()] param ( [string]$RequiredVersion, [string]$MinimumVersion, [string]$MaximumVersion ) Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) $isValid = $false #Case 1: No further check required if a user provides either none or one of these: minimumVersion, maximumVersion, and requiredVersion if ($PSBoundParameters.Count -le 1) { return $true } #Case 2: #If no RequiredVersion is provided if (-not $PSBoundParameters.ContainsKey('RequiredVersion')) { #If no RequiredVersion, both MinimumVersion and MaximumVersion are provided. Otherwise fall into the Case #1 $isValid = $PSBoundParameters['MinimumVersion'] -le $PSBoundParameters['MaximumVersion'] } #Case 3: RequiredVersion is provided. # In this case MinimumVersion and/or MaximumVersion also are provided. Otherwise fall in to Case #1. # This is an invalid case. When RequiredVersion is provided, others are not allowed. so $isValid is false, which is already set in the init if ($isValid -eq $false) { ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage ($LocalizedData.VersionError)` -ErrorId "VersionError" ` -ErrorCategory InvalidArgument } } Function Get-InstallationPolicy { <# .SYNOPSIS This is a helper function that retrives the InstallationPolicy from the given repository. .PARAMETER RepositoryName Provides the repository Name. #> Param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String]$RepositoryName ) Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.mycommand)) $repositoryobj = PackageManagement\Get-PackageSource -Name $RepositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue if ($repositoryobj) { return $repositoryobj.IsTrusted } } # SIG # Begin signature block # MIInoQYJKoZIhvcNAQcCoIInkjCCJ44CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD+xe8u4YoS6UEO # jtW70wceL89huvuluOvdcbeefpOXLqCCDYEwggX/MIID56ADAgECAhMzAAACUosz # qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I # sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O # L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA # v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o # RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8 # q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3 # uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp # kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7 # l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u # TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1 # o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti # yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z # 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf # 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK # WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW # esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F # 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZdjCCGXICAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgCZ2K5xbK # 27tyibqtMV5AHpyNN7lNy3nCNEZ+gshCPtAwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQCyqH1a6wilw9tLp1PBwdpHqB1Ami+jaRaJh0DD1pMa # 0Wv61B/88vYVxgOsFfBcBqZvVoQsYtlAjEcx/dg5Vamacy+LfO+8cm8uRq1uSXOq # gn3FDJ6Xy0j3pcU44/X1uzh8KoUaQJzNpcjLn8WpqABD0w7WfIA7A+o1yMZUcUsP # XejK8HpbV8Qrtz9okvl/hsK0zQuajckvm/odt+IgmwBr4yomlRFJ1AAqyKYp//4H # 7gPo5CBjh3H7wHn0mXMkES24T37LXmxxSncAShECTMvJMhiuM01TN6PziqI6ER0S # XFvKYHyfqwXvsfn71rcKAIWWjtWXz4a1mIZjkXJfUcf7oYIXADCCFvwGCisGAQQB # gjcDAwExghbsMIIW6AYJKoZIhvcNAQcCoIIW2TCCFtUCAQMxDzANBglghkgBZQME # AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIB/6Ll6BcpT6c9557ujfliR3kAn4lZRikouo1ahJ # /NiIAgZitNXDgmUYEzIwMjIwNzAxMjEwMDAwLjQ0OVowBIACAfSggdCkgc0wgcox # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # RVNOOjhBODItRTM0Ri05RERBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNloIIRVzCCBwwwggT0oAMCAQICEzMAAAGZyI+vrbZ9vosAAQAAAZkw # DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN # MjExMjAyMTkwNTE2WhcNMjMwMjI4MTkwNTE2WjCByjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046OEE4Mi1FMzRGLTlE # REExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0G # CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC4E/lXXKMsy9rVa2a8bRb0Ar/Pj4+b # KiAgMgKayvCMFn3ddGof8eWFgJWp5JdKjWjrnmW1r9tHpcP2kFpjXp2Udrj55jt5 # NYi1MERcoIo+E29XuCwFAMJftGdvsWea/OTQPIFsZEWqEteXdRncyVwct5xFzBIC # 1JWCdmfc7R59RMIyvgWjIz8356mweowkOstN1fe53KIJ8flrYILIQWsNRMOT3znA # GwIb9kyL54C6jZjFxOSusGYmVQ+Gr/qZQELw1ipx9s5jNP1LSpOpfTEBFu+y9KLN # BmMBARkSPpTFkGEyGSwGGgSdOi6BU6FPK+6urZ830jrRemK4JkIJ9tQhlGcIhAjh # cqZStn+38lRjVvrfbBI5EpI2NwlVIK2ibGW7sWeTAz/yNPNISUbQhGAJse/OgGj/ # 1qz/Ha9mqfYZ8BHchNxn08nWkqyrjrKicQyxuD8mCatTrVSbOJYfQyZdHR9a4vgy # GeZEXBYQNAlIuB37QCOAgs/VeDU8M4dc/IlrTyC0uV1SS4Gk8zV+5X5eRu+XORN8 # FWqzI6k/9y6cWwOWMK6aUN1XqLcaF/sm9rX84eKW2lhDc3C31WLjp8UOfOHZfPuy # y54xfilnhhCPy4QKJ9jggoqqeeEhCEfgDYjy+PByV/e5HDB2xHdtlL93wltAkI3a # Cxo84kVPBCa0OwIDAQABo4IBNjCCATIwHQYDVR0OBBYEFI26Vrg+nGWvrvIh0dQP # EonENR0QMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRY # MFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p # Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEF # BQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w # a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo # MSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI # hvcNAQELBQADggIBAHGzWh29ibBNro3ns8E3EOHGsLB1Gzk90SFYUKBilIu4jDbR # 7qbvXNd8nnl/z5D9LKgw3T81jqy5tMiWp+p4jYBBk3PRx1ySqLUfhF5ZMWolRzW+ # cQZGXV38iSmdAUG0CpR5x1rMdPIrTczVUFsOYGqmkoUQ/dRiVL4iAXJLCNTj4x3Y # wIQcCPt0ijJVinPIMAYzA8f99BbeiskyI0BHGAd0kGUX2I2/puYnlyS8toBnANjh # 21xgvEuaZ2dvRqvWk/i1XIlO67au/XCeMTvXhPOIUmq80U32Tifw3SSiBKTyir7m # oWH1i7H2q5QAnrBxuyy//ZsDfARDV/Atmj5jr6ATfRHDdUanQpeoBS+iylNU6RAR # u8g+TMCu/ZndZmrs9w+8galUIGg+GmlNk07fXJ58Oc+qFqgNAsNkMi+dSzKkWGA4 # /klJFn0XichXL8+t7KOayXKGzQja6CdtCjisnyS8hbv4PKhaeMtf68wJWKKOs0tt # 2AJfYC5vSbH9ck8BGj2e/yQXEZEu88L5/fHK5XUk/IKXx3zaLkxXTSZ43Ea/WKXV # BzMasHZ3Pmny0moEekAXx1UhLNNYv4Vum33VirxSB6r/GKQxFSHu7yFfrWQpYyyD # H119TmhAedS8T1VabqdtO5ZP2E14TK82Vyxy3xEPelOo4dRIlhm7XY6k9B68MIIH # cTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB # iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp # TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEw # OTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIh # C3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNx # WuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFc # UTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAc # nVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUo # veO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyzi # YrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9 # fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdH # GO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7X # KHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiE # R9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/ # eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3 # FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAd # BgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEE # AYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMI # MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB # Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud # HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By # b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO # MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy # dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4IC # AQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pk # bHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gng # ugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3 # lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHC # gRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6 # MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEU # BHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvsh # VGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+ # fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrp # NPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHI # qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAs4wggI3AgEBMIH4 # oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw # IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1U # aGFsZXMgVFNTIEVTTjo4QTgyLUUzNEYtOUREQTElMCMGA1UEAxMcTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAku/zYujnqapN6BJ9 # MJ5jtgDrlOuggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN # BgkqhkiG9w0BAQUFAAIFAOZpNz0wIhgPMjAyMjA3MDExNzA1MDFaGA8yMDIyMDcw # MjE3MDUwMVowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA5mk3PQIBADAKAgEAAgIR # FQIB/zAHAgEAAgIRszAKAgUA5mqIvQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor # BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUA # A4GBAFPnj5aYeiMxAJhCBC3XyLLyNYf/OubzwI5EX68hNX+TC87/MMvxocAgfMk1 # zLkJek8rPLlWGWf2oeGQYwyOQ0OMjD5YU4LnkVjN0l027+vwT2Rafc6RIWFiQbIG # RRBFfXDFgyuM97dVEf8ICSKsRlCCgFAV8JMNdND7JtN+xEb6MYIEDTCCBAkCAQEw # gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAGZyI+vrbZ9vosA # AQAAAZkwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B # CRABBDAvBgkqhkiG9w0BCQQxIgQgkB6lWYhfhU1/DxjvSurvGL3RL89ss6hg4476 # tkRKHTwwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCBmfWn58qKN7WWpBTYO # rUO1BSCSnKPLC/G7wCOIc2JsfjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAABmciPr622fb6LAAEAAAGZMCIEILRj7rb7JnV73e8wLbPq # CULQreKtMxdbry8TgOxho5edMA0GCSqGSIb3DQEBCwUABIICAKDNJpuPLp51vjon # QG0U01L3EGiaTgFCweFx7+x4vL5mVQ8JQBPaYJoy+34wnXF2U+0FwXW0SiTzzIHb # 0lFXKRkSMqCPnpW/1Oz474m9w95SvR86JgRC/kuJWtyD+TZg++zCafJeaLAXnuj3 # LbpJlbNp7P6jW5Kz05J8xrz/Q+5g16VQ7RqqHImBKubLqAHEWrRcZiOXZZuIyDT4 # 2L1vyDMMVHjGZfghQO5on6LsAmBmS2Ne7VteWUQxVWI/0yl1e81xjLzPFmiU0PrL # oWwI+fYVuKCYo5bwYQEhlVb5iVhNYYeNo0Voyh5Nnl75ClP/Xp/jeRb4hI9w6cdf # rlHWIhrKmNDb2Hmu7fVOF+A4O2j32EXG7sJPMNmboMFOeQf8eTcohw0LImS/eay/ # 6vAcPeJw7jhHpJG20zDfusn4HDR1nD4oKTENvK7EIMSheRvsxAR1KS90sQLoPkyc # k5rkNTr8MHhvE4Nz5eeNfO7FIw2xPxwRST4p/hLes9xUw4H59TdSe9H0N92q118J # NNo/xo7e5d6lSLoonrL/TIYbvuoqM5PGw8HPS8ve09kV00MXTAGcnxpr2j1CzMAv # Y4i7KMGesQfOB5ounL187ZV/e+bhhHfLA0mgSnehHjXtQP/MNOvQ9/EXIK3/xcXg # mr4TmGPRZkuxUnXFoqN0pPOr9NOY # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PackageManagement/1.4.8.1/PSGetModuleInfo.xml ================================================ Microsoft.PowerShell.Commands.PSRepositoryItemInfo System.Management.Automation.PSCustomObject System.Object PackageManagement 1.4.8.1 Module PackageManagement (a.k.a. OneGet) is a new way to discover and install software packages from around the web._x000D__x000A_ It is a manager or multiplexor of existing package managers (also called package providers) that unifies Windows package management with a single Windows PowerShell interface. With PackageManagement, you can do the following._x000D__x000A_ - Manage a list of software repositories in which packages can be searched, acquired and installed_x000D__x000A_ - Discover software packages_x000D__x000A_ - Seamlessly install, uninstall, and inventory packages from one or more software repositories Microsoft Corporation System.Object[] System.Array System.Object PowerShellTeam alerickson NateLehman krishnayalavarthi anamnavi (C) Microsoft Corporation. All rights reserved.
2022-07-01T21:21:49-07:00
https://oneget.org/ PackageManagement PSEdition_Core PSEdition_Desktop Linux Mac PSModule System.Collections.Hashtable System.Object Command Find-Package Get-Package Get-PackageProvider Get-PackageSource Install-Package Import-PackageProvider Find-PackageProvider Install-PackageProvider Register-PackageSource Set-PackageSource Unregister-PackageSource Uninstall-Package Save-Package Function Cmdlet Find-Package Get-Package Get-PackageProvider Get-PackageSource Install-Package Import-PackageProvider Find-PackageProvider Install-PackageProvider Register-PackageSource Set-PackageSource Unregister-PackageSource Uninstall-Package Save-Package DscResource MSFT_PackageManagement MSFT_PackageManagementSource Workflow RoleCapability ## 1.4.8.1_x000D__x000A_- Update PackageManagement's strong name signing_x000D__x000A__x000D__x000A_## 1.4.8_x000D__x000A_- Add NuGet as a source when generating nuget.config file for user in the NuGet Provider_x000D__x000A__x000D__x000A_## 1.4.7_x000D__x000A_- Update security protocol to use TLS 1.2_x000D__x000A_- Remove catalog file_x000D__x000A__x000D__x000A_## 1.4.6_x000D__x000A_- Update `HelpInfoUri` to point to the latest content_x000D__x000A__x000D__x000A_## 1.4.5_x000D__x000A_- Bug fix for deadlock when getting parameters in an event_x000D__x000A__x000D__x000A_## 1.4.4_x000D__x000A_- Bug fix when installing modules from private feeds_x000D__x000A__x000D__x000A_ ## 1.4.3_x000D__x000A_- Another bug fix when registering repositories with PowerShellGet_x000D__x000A__x000D__x000A_## 1.4.2_x000D__x000A_- Bug fix for passing credentials from PowerShellGet when registering repositories_x000D__x000A__x000D__x000A_## 1.4.1_x000D__x000A_- Bug fix for using credential provider installed in Visual Studio_x000D__x000A__x000D__x000A_## 1.4_x000D__x000A_- Allow credential persistance for registering private repositories and finding or installing packages from those repositories_x000D__x000A__x000D__x000A_## 1.3.2_x000D__x000A_- Enable bootstrap on PSCore_x000D__x000A_- Bug fix to run on .NET Core 3.0_x000D__x000A__x000D__x000A_## 1.3.1_x000D__x000A_- Targets net452 and netstandard2.0 instead of net451, netcoreapp2.0, and netstandard1.6_x000D__x000A_ _x000D__x000A_## Previous releases are not included in this Changelog https://www.powershellgallery.com/api/v2 PSGallery NuGet System.Management.Automation.PSCustomObject System.Object (C) Microsoft Corporation. All rights reserved. PackageManagement (a.k.a. OneGet) is a new way to discover and install software packages from around the web._x000D__x000A_ It is a manager or multiplexor of existing package managers (also called package providers) that unifies Windows package management with a single Windows PowerShell interface. With PackageManagement, you can do the following._x000D__x000A_ - Manage a list of software repositories in which packages can be searched, acquired and installed_x000D__x000A_ - Discover software packages_x000D__x000A_ - Seamlessly install, uninstall, and inventory packages from one or more software repositories False ## 1.4.8.1_x000D__x000A_- Update PackageManagement's strong name signing_x000D__x000A__x000D__x000A_## 1.4.8_x000D__x000A_- Add NuGet as a source when generating nuget.config file for user in the NuGet Provider_x000D__x000A__x000D__x000A_## 1.4.7_x000D__x000A_- Update security protocol to use TLS 1.2_x000D__x000A_- Remove catalog file_x000D__x000A__x000D__x000A_## 1.4.6_x000D__x000A_- Update `HelpInfoUri` to point to the latest content_x000D__x000A__x000D__x000A_## 1.4.5_x000D__x000A_- Bug fix for deadlock when getting parameters in an event_x000D__x000A__x000D__x000A_## 1.4.4_x000D__x000A_- Bug fix when installing modules from private feeds_x000D__x000A__x000D__x000A_ ## 1.4.3_x000D__x000A_- Another bug fix when registering repositories with PowerShellGet_x000D__x000A__x000D__x000A_## 1.4.2_x000D__x000A_- Bug fix for passing credentials from PowerShellGet when registering repositories_x000D__x000A__x000D__x000A_## 1.4.1_x000D__x000A_- Bug fix for using credential provider installed in Visual Studio_x000D__x000A__x000D__x000A_## 1.4_x000D__x000A_- Allow credential persistance for registering private repositories and finding or installing packages from those repositories_x000D__x000A__x000D__x000A_## 1.3.2_x000D__x000A_- Enable bootstrap on PSCore_x000D__x000A_- Bug fix to run on .NET Core 3.0_x000D__x000A__x000D__x000A_## 1.3.1_x000D__x000A_- Targets net452 and netstandard2.0 instead of net451, netcoreapp2.0, and netstandard1.6_x000D__x000A_ _x000D__x000A_## Previous releases are not included in this Changelog True True 38794156 128454435 1153134 7/1/2022 9:21:49 PM -07:00 7/1/2022 9:21:49 PM -07:00 7/5/2022 6:42:00 PM -07:00 3/21/2023 6:34:18 PM -07:00 PackageManagement PSEdition_Core PSEdition_Desktop Linux Mac PSModule PSCmdlet_Find-Package PSCommand_Find-Package PSCmdlet_Get-Package PSCommand_Get-Package PSCmdlet_Get-PackageProvider PSCommand_Get-PackageProvider PSCmdlet_Get-PackageSource PSCommand_Get-PackageSource PSCmdlet_Install-Package PSCommand_Install-Package PSCmdlet_Import-PackageProvider PSCommand_Import-PackageProvider PSCmdlet_Find-PackageProvider PSCommand_Find-PackageProvider PSCmdlet_Install-PackageProvider PSCommand_Install-PackageProvider PSCmdlet_Register-PackageSource PSCommand_Register-PackageSource PSCmdlet_Set-PackageSource PSCommand_Set-PackageSource PSCmdlet_Unregister-PackageSource PSCommand_Unregister-PackageSource PSCmdlet_Uninstall-Package PSCommand_Uninstall-Package PSCmdlet_Save-Package PSCommand_Save-Package PSIncludes_Cmdlet PSDscResource_MSFT_PackageManagement PSDscResource_MSFT_PackageManagementSource PSIncludes_DscResource False 2023-03-21T18:34:18Z 1.4.8.1 Microsoft Corporation false Module PackageManagement.nuspec|PackageManagement.format.ps1xml|PackageManagement.psd1|PackageManagement.psm1|PackageManagement.Resources.psd1|PackageProviderFunctions.psm1|DSCResources\PackageManagementDscUtilities.psm1|DSCResources\PackageManagementDscUtilities.strings.psd1|fullclr\Microsoft.PackageManagement.ArchiverProviders.dll|fullclr\Microsoft.PackageManagement.CoreProviders.dll|fullclr\Microsoft.PackageManagement.dll|fullclr\Microsoft.PackageManagement.MetaProvider.PowerShell.dll|fullclr\Microsoft.PackageManagement.MsiProvider.dll|fullclr\Microsoft.PackageManagement.MsuProvider.dll|fullclr\Microsoft.PackageManagement.NuGetProvider.dll|fullclr\Microsoft.PowerShell.PackageManagement.dll|coreclr\netstandard2.0\Microsoft.PackageManagement.ArchiverProviders.dll|coreclr\netstandard2.0\Microsoft.PackageManagement.CoreProviders.dll|coreclr\netstandard2.0\Microsoft.PackageManagement.dll|coreclr\netstandard2.0\Microsoft.PackageManagement.MetaProvider.PowerShell.dll|coreclr\netstandard2.0\Microsoft.PackageManagement.NuGetProvider.dll|coreclr\netstandard2.0\Microsoft.PowerShell.PackageManagement.dll|DSCResources\MSFT_PackageManagement\MSFT_PackageManagement.psm1|DSCResources\MSFT_PackageManagement\MSFT_PackageManagement.schema.mof|DSCResources\MSFT_PackageManagement\MSFT_PackageManagement.strings.psd1|DSCResources\MSFT_PackageManagementSource\MSFT_PackageManagementSource.psm1|DSCResources\MSFT_PackageManagementSource\MSFT_PackageManagementSource.schema.mof|DSCResources\MSFT_PackageManagementSource\MSFT_PackageManagementSource.strings.psd1 4ae9fd46-338a-459c-8186-07f910774cb8 3.0 4.0 Microsoft Corporation D:\mspkg\src\PowerShell\ExternalModules\PackageManagement\1.4.8.1
================================================ FILE: src/PowerShell/ExternalModules/PackageManagement/1.4.8.1/PackageManagement.Resources.psd1 ================================================ ######################################################################################### # # Copyright (c) Microsoft Corporation. All rights reserved. # # Localized PackageManagement.Resources.psd1 # ######################################################################################### ConvertFrom-StringData @' ###PSLOC OldPowerShellCoreVersion=PackageManagement no longer supports PowerShell Core '{0}'. Please install the latest version of PowerShell Core from 'https://aka.ms/i6t6o3' and try again. ###PSLOC '@ # SIG # Begin signature block # MIInogYJKoZIhvcNAQcCoIInkzCCJ48CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBIBsoYrH/WngZG # wcwl3oPmFWjeg9nVV4OusKRd1BszsaCCDYUwggYDMIID66ADAgECAhMzAAACU+OD # 3pbexW7MAAAAAAJTMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMzAwWhcNMjIwOTAxMTgzMzAwWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDLhxHwq3OhH+4J+SX4qS/VQG8HybccH7tnG+BUqrXubfGuDFYPZ29uCuHfQlO1 # lygLgMpJ4Geh6/6poQ5VkDKfVssn6aA1PCzIh8iOPMQ9Mju3sLF9Sn+Pzuaie4BN # rp0MuZLDEXgVYx2WNjmzqcxC7dY9SC3znOh5qUy2vnmWygC7b9kj0d3JrGtjc5q5 # 0WfV3WLXAQHkeRROsJFBZfXFGoSvRljFFUAjU/zdhP92P+1JiRRRikVy/sqIhMDY # +7tVdzlE2fwnKOv9LShgKeyEevgMl0B1Fq7E2YeBZKF6KlhmYi9CE1350cnTUoU4 # YpQSnZo0YAnaenREDLfFGKTdAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUlZpLWIccXoxessA/DRbe26glhEMw # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ2NzU5ODAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AKVY+yKcJVVxf9W2vNkL5ufjOpqcvVOOOdVyjy1dmsO4O8khWhqrecdVZp09adOZ # 8kcMtQ0U+oKx484Jg11cc4Ck0FyOBnp+YIFbOxYCqzaqMcaRAgy48n1tbz/EFYiF # zJmMiGnlgWFCStONPvQOBD2y/Ej3qBRnGy9EZS1EDlRN/8l5Rs3HX2lZhd9WuukR # bUk83U99TPJyo12cU0Mb3n1HJv/JZpwSyqb3O0o4HExVJSkwN1m42fSVIVtXVVSa # YZiVpv32GoD/dyAS/gyplfR6FI3RnCOomzlycSqoz0zBCPFiCMhVhQ6qn+J0GhgR # BJvGKizw+5lTfnBFoqKZJDROz+uGDl9tw6JvnVqAZKGrWv/CsYaegaPePFrAVSxA # yUwOFTkAqtNC8uAee+rv2V5xLw8FfpKJ5yKiMKnCKrIaFQDr5AZ7f2ejGGDf+8Tz # OiK1AgBvOW3iTEEa/at8Z4+s1CmnEAkAi0cLjB72CJedU1LAswdOCWM2MDIZVo9j # 0T74OkJLTjPd3WNEyw0rBXTyhlbYQsYt7ElT2l2TTlF5EmpVixGtj4ChNjWoKr9y # TAqtadd2Ym5FNB792GzwNwa631BPCgBJmcRpFKXt0VEQq7UXVNYBiBRd+x4yvjqq # 5aF7XC5nXCgjbCk7IXwmOphNuNDNiRq83Ejjnc7mxrJGMIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGXMwghlvAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAJT44Pelt7FbswAAAAA # AlMwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEII0M # q4L3/1947bwnMVvGP5Dmwbb971etV96SBKgFIvZRMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAobv6NU6XVA64f3V2uUUkdzyvJ6z+Fh3abvbP # G8Jes921e5G3kFIkII8uqVjju3kaR0V6CprgswF4sRa6DG8q6i5XGczd7uNgt4fn # 11MLiky6r6km9N53YZHfSQunYurfHKee4D4qfYluaLCPQCSQ5Jri0I8cBAbYfb3k # hKJaiMV+DesnimsETP0eExyCjzYgt9+calYTAZc1yCMzQxoshOp5AIsj/VFv184V # 9p/QsfHKIfbK+eNb9VclXcSu1Nbxi/D82w5SdVVjyjViE7GyEMG76J9UZveKUlth # FYZ+IPWT9xZ9POk4+wzGHP49jCVInEWFnmObLgF1kE/LJ4K79KGCFv0wghb5Bgor # BgEEAYI3AwMBMYIW6TCCFuUGCSqGSIb3DQEHAqCCFtYwghbSAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCA6fmswW5cfPwuOva/mZGh76WqyLmVw68W5 # Kyj7PkdiJQIGYrThpN06GBMyMDIyMDcwMTIxMDAwMS45NjNaMASAAgH0oIHQpIHN # MIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL # ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjpFQUNFLUUzMTYtQzkxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCEVQwggcMMIIE9KADAgECAhMzAAABmsB1osQhbT6FAAEA # AAGaMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTIxMTIwMjE5MDUxN1oXDTIzMDIyODE5MDUxN1owgcoxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVy # aWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkVBQ0UtRTMx # Ni1DOTFEMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2nIGrCort2RhFP5q+gObfaFw # IG7AiatDZzrvueM2T7fWP7axB0k5aRNp+I7muFZ2nROLH9jYPMX1MQ0DzuFW/91B # 4YXR4gpy6FCLFt8LRNjj8xxYQHFDc8bkqZOuu6JuKPxnGj5cIiDeGXQ8Ujs+qI0j # U/Ws7Cl8EBQHLuHPbbL14rpffbInwt7NnRBCdPwYch4iQMLHFODdp5tVA3+LjAHw # tQe0gUGS99LLD8olI1O4CIo69SEZQQHQWJoskdBe0Sb88vnYsI5tCLI93/G7FSKv # YGZFFscRZCmS3wcpXhKOATJkTGRPfgH06a0J3upnI7VQHQS0Sl714y0lz0eoeeKb # bbEoSmldyD+g6em10X9hm9gn3VUsbctxxwFMmV7hcILiFdjlt4Bd5BUCt7i+kGbz # fGuigdIbaNOlffDrXstTkzr59ZkZwL1buFo/H9XXPvXDj3T4LRc+HHd+5kUTxJAH # V9mGnk4KXDRMWvowmzkjfvlbTUnMcLuAIz6E30I7kPi9afEjGX4IE/JIWl2llmfb # y7zuzyMCGeG9kit/15lqZNAJmk4WuUBtH7ubr3eGGf8S7iP5IsB1nE8pL4gGTpcJ # K57KGGSSdN0bCAFr+lB52IwCPBt1IAhRZQJtJ4LkN6yF+eKZro0vN5YK5tWKmy9i # 65YZovfDJNpLQhwlykcCAwEAAaOCATYwggEyMB0GA1UdDgQWBBRftp5Z8JzbUeml # Wb0KlcitNivRcDAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNV # HR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2Ny # bC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYI # KwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAy # MDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0G # CSqGSIb3DQEBCwUAA4ICAQAAE7uHzEbUR9tPpzcxgFxcXVxKUT032zNCyQ3jXuEA # sY9BTPsKyXbulCqzNsELjt9VA3EOJ61CQXvNTeltkbxGvMTV42ztKszYrcFHzlS3 # maeh1RnDU7WBDALyvZP/9HWgRcW6dOAczGiMmh0cu8vyv82fXJBMO4xfVbCapa8K # pMfR6iPyAbAqSXZU7SgZf/i0Ww/LVr8OhQ60pL/yA4inGqzxNAVOv/2xV72ef4e3 # YhNd3ar+Qz1OSp+PfR71DgHBxt9YK/0yTxH7aqiuNHX6QftWwT0swHn+fKycUSVz # SeutRmzmeXuuBLsiEL9FaOWabWlmYn7UOaYJs7WmQrjSCL8TxwsryAI5kn0bl+1M # pHtJNva0k67kbAVSLInxt/YJXbG8ozr5Aze0t6SbU8CVdE6AuFVoNNJKbp5O9jzk # bqd9WoVvfX1N48QYdnx44nn42VGtPHf50EHS1gs2nbbaZGbwoB/3XPDLbNgsK3MQ # j2eafVbhnKshYStiOj0tDzpzLn+9Ed5a5eWPO3TvH+Cr/N25IauYPiK2OSry3CBB # EeZLebrqK6VsyZgTRgfutjlTTM/dmCRZfy7fjb5BhU7hmcvekyzD3S3KzUqTxlea # h6px5a/8FM/VAFYkyiQK70m75P7IlO5otvaKkcW9GoQeKGFTzbr+3HB0wRqjTRqJ # eDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZIhvcNAQEL # BQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNV # BAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4X # DTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDk4aZM # 57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25PhdgM/9cT8dm # 95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPFdvWGUNzB # RMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6GnszrYBb # fowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBpDco2LXCO # Mcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50ZuyjLVwIYw # XE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3EXzTdEonW # /aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0lBw0gg/w # EPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1qGFphAXPK # Z6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ+QuJYfM2 # BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PAPBXbGjfH # CBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkwEgYJKwYB # BAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxGNSnPEP8v # BO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARVMFMwUQYM # KwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAKBggrBgEF # BQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBW # BgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUH # AQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # L2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG9w0BAQsF # AAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0xM7U518Jx # Nj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmCVgADsAW+ # iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449xvNo32X2 # pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wMnosZiefw # C2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDSPeZKPmY7 # T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2dY3RILLFO # Ry3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxnGSgkujhL # mm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+CrvsQWY9af3L # wUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokLjzbaukz5 # m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL6Xu/OHBE # 0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggLLMIICNAIB # ATCB+KGB0KSBzTCByjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEmMCQGA1UE # CxMdVGhhbGVzIFRTUyBFU046RUFDRS1FMzE2LUM5MUQxJTAjBgNVBAMTHE1pY3Jv # c29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAAG6rjJ1Ampv # 5uzsdVL/xjbNY5rvoIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTAwDQYJKoZIhvcNAQEFBQACBQDmaULZMCIYDzIwMjIwNzAxMTc1NDMzWhgPMjAy # MjA3MDIxNzU0MzNaMHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAOZpQtkCAQAwBwIB # AAICBV0wBwIBAAICEd8wCgIFAOZqlFkCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYK # KwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQUF # AAOBgQBE0fx25fV4T83pZxB4CQQfrko37LMB2A+I//kXKtd0XKnuTaaJ9YQIdTEd # 1QWiTcnM/TW+yNCfE5QNhZFzxVxS5WXksZaiA0wLTqJfA+jm2Mi0FfZefFr4L6DU # pyKjEWvhlLiUfperOIuvHm37XN0f3LDOHWx+6CbZ1xT+jNePMzGCBA0wggQJAgEB # MIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV # BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABmsB1osQhbT6F # AAEAAAGaMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcN # AQkQAQQwLwYJKoZIhvcNAQkEMSIEIF2O7hc1ZhwoL1uHKtS6plCa34qF+9dG3vi7 # tPHcCrGRMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgAU5A4zgRFH2Z5YoC # Yi+d/S8fp7K/zRVU5yhV9N9IjWAwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFt # cCBQQ0EgMjAxMAITMwAAAZrAdaLEIW0+hQABAAABmjAiBCDlYijfDa/nMtdfyLez # 76eMSHmBEDD3QIEOBB5OFeXcFjANBgkqhkiG9w0BAQsFAASCAgAJWWMLeusd6nZn # 4Q8YPFoh1x2D0C3ApP00erz4ctpJSw2NSvnCQgSKrGdzehx62PNh/yYbmUqYd6Gp # LP3ErMpA56cVVekiF0ECBa5llP4a1UTpEcO0eZnApz93o7UJ8g0RDspFydd459dS # 8389MfETeVGw4WiD3FV97IawqrZ2bpMzAn7nhD2s8oI36NZ6fhbykA68nzLGJXdj # HmmoG1DSJIIgHL0IR5u8OY7LarpcJQUGcMH09bN29J8J2Z2Uzrz52Q7t4LVGb9Oj # kT6LnOYe+zGtFZAPWm20xFHAHnDa6GkemWGGmm6kZHdBTsJSN7TmBbKEcT9k9AkJ # yv5N7grvD6YcpOE949BRaGzGQ26JKJDzUqjJ7VUycWyEq35+QZFTGj3LS4xlgeoA # 1en1N5dfxGEHVbhIgmfaLYrmq1L5mMZFnXrxQdaj6/6ew1ddxAnq/fcs2EIhZcWm # htBTC+qsi5WmUxoglCGjj2iFNGs61a9B5yDFVVQaZIk/Mi2hjGINj65XF0r0vuYd # XDklHpWC0UQe41KckrKEliYTmWQkIMjaraCubRxjpywXFwecO8TDDW6031xVwd0r # t77jQA1+tAVjzEg5jo3T3woTA73hLZ1qCRHGUZzrpe+wRFrAGZM5eZhTHwqhYTC0 # ym9JZhmcOB5iC6VOR/jxKibZ3kyfgg== # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PackageManagement/1.4.8.1/PackageManagement.format.ps1xml ================================================ package Microsoft.PackageManagement.Packaging.SoftwareIdentity Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity 30 16 16 Name Version Source Summary GetPackage Microsoft.PackageManagement.Packaging.SoftwareIdentity#GetPackage Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity#GetPackage 30 16 32 Name Version Source ProviderName packageWithLongSourceName Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongSourceName Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongSourceName 30 16 32 Name Version Source Summary packageWithLongSourceNameAndCulture Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongSourceName#DisplayCulture Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongSourceName#DisplayCulture 30 16 16 32 Name Version Culture Source Summary packageWithLongSourceNameAndName Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongSourceName#DisplayLongName Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongSourceName#DisplayLongName 45 16 32 Name Version Source Summary packageWithLongSourceNameAndCultureAndName Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongSourceName#DisplayCulture#DisplayLongName Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongSourceName#DisplayCulture#DisplayLongName 42 16 16 24 Name Version Culture Source Summary PackageWithCulture Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayCulture Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayCulture 30 16 16 16 Name Version Culture Source Summary PackageWithLongName Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongName Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayLongName 52 16 16 Name Version Source Summary PackageWithCultureAndLongName Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayCulture#DisplayLongName Deserialized.Microsoft.PackageManagement.Packaging.SoftwareIdentity#DisplayCulture#DisplayLongName 52 16 16 16 Name Version Culture Source Summary PackageSource Microsoft.PackageManagement.Packaging.PackageSource Deserialized.Microsoft.PackageManagement.Packaging.PackageSource 32 16 10 Name ProviderName IsTrusted Location PackageProvider Microsoft.PackageManagement.Implementation.PackageProvider Deserialized.Microsoft.PackageManagement.Implementation.PackageProvider 24 16 Name Version $options = $_.DynamicOptions.Name | select-object -unique; $options -join ", " ================================================ FILE: src/PowerShell/ExternalModules/PackageManagement/1.4.8.1/PackageManagement.psd1 ================================================ ### # ==++== # # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ### @{ GUID = "4ae9fd46-338a-459c-8186-07f910774cb8" Author = "Microsoft Corporation" CompanyName = "Microsoft Corporation" Copyright = "(C) Microsoft Corporation. All rights reserved." HelpInfoUri = "https://go.microsoft.com/fwlink/?linkid=2113634" ModuleVersion = "1.4.8.1" PowerShellVersion = "3.0" ClrVersion = "4.0" RootModule = "PackageManagement.psm1" Description = 'PackageManagement (a.k.a. OneGet) is a new way to discover and install software packages from around the web. It is a manager or multiplexor of existing package managers (also called package providers) that unifies Windows package management with a single Windows PowerShell interface. With PackageManagement, you can do the following. - Manage a list of software repositories in which packages can be searched, acquired and installed - Discover software packages - Seamlessly install, uninstall, and inventory packages from one or more software repositories' CmdletsToExport = @( 'Find-Package', 'Get-Package', 'Get-PackageProvider', 'Get-PackageSource', 'Install-Package', 'Import-PackageProvider' 'Find-PackageProvider' 'Install-PackageProvider' 'Register-PackageSource', 'Set-PackageSource', 'Unregister-PackageSource', 'Uninstall-Package' 'Save-Package' ) FormatsToProcess = @('PackageManagement.format.ps1xml') PrivateData = @{ PSData = @{ Tags = @('PackageManagement', 'PSEdition_Core', 'PSEdition_Desktop', 'Linux', 'Mac') ProjectUri = 'https://oneget.org' ReleaseNotes = @' ## 1.4.8.1 - Update PackageManagement's strong name signing ## 1.4.8 - Add NuGet as a source when generating nuget.config file for user in the NuGet Provider ## 1.4.7 - Update security protocol to use TLS 1.2 - Remove catalog file ## 1.4.6 - Update `HelpInfoUri` to point to the latest content ## 1.4.5 - Bug fix for deadlock when getting parameters in an event ## 1.4.4 - Bug fix when installing modules from private feeds ## 1.4.3 - Another bug fix when registering repositories with PowerShellGet ## 1.4.2 - Bug fix for passing credentials from PowerShellGet when registering repositories ## 1.4.1 - Bug fix for using credential provider installed in Visual Studio ## 1.4 - Allow credential persistance for registering private repositories and finding or installing packages from those repositories ## 1.3.2 - Enable bootstrap on PSCore - Bug fix to run on .NET Core 3.0 ## 1.3.1 - Targets net452 and netstandard2.0 instead of net451, netcoreapp2.0, and netstandard1.6 ## Previous releases are not included in this Changelog '@ } } } # SIG # Begin signature block # MIInoQYJKoZIhvcNAQcCoIInkjCCJ44CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCANw97w1D+bi5LY # 8ZEuubcA0tI0Z0h+CImFRYop+IIqQaCCDYEwggX/MIID56ADAgECAhMzAAACUosz # qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I # sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O # L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA # v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o # RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8 # q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3 # uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp # kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7 # l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u # TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1 # o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti # yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z # 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf # 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK # WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW # esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F # 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZdjCCGXICAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgiRM0SyHn # 1V4nqTIo3jXRvJycxDAVVlA2FtX6n3qWqfwwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQCPJRvOZr6CasCkw1WMLfBA6LopbQ7shmcqRwM1eDEa # 20F09jJVj45dL1xe5F1nEFixo3ejN83D6X9Tm96NfNf5vjqC4AfG8M+4/NGo4Vta # doFQs1KjdLELNAZZFY7i241P9+ayzDTh5ON5F6BehV4Sex00Kbg0mHB76GhBKUJL # PqfBbc4KIurIA/Rk1wtXWTuGKdL3+eOVn+DscOEv+0Jjlgzr5UvkFw7tVXhS2Y9z # dxL8TBqkugR60I+IHB0yiAQwePAqfnVHwPoX0EeldwdwN/B9Iidei1wOa6+ddWr7 # jxz8J4fLzH7k3V3xRDF+z14+6BbOL7dnX3Q7TEDnTIJgoYIXADCCFvwGCisGAQQB # gjcDAwExghbsMIIW6AYJKoZIhvcNAQcCoIIW2TCCFtUCAQMxDzANBglghkgBZQME # AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIMfYTjqpmIZRLR5Dn8vRnb89EjdawUtU1A43DUFK # mjO1AgZitJ9dXI0YEzIwMjIwNzAxMjEwMDAwLjQ4N1owBIACAfSggdCkgc0wgcox # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # RVNOOkU1QTYtRTI3Qy01OTJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNloIIRVzCCBwwwggT0oAMCAQICEzMAAAGVt/wN1uM3MSUAAQAAAZUw # DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN # MjExMjAyMTkwNTEyWhcNMjMwMjI4MTkwNTEyWjCByjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RTVBNi1FMjdDLTU5 # MkUxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0G # CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCfbUEMZ7ZLOz9aoRCeJL4hhT9Q8JZB # 2xaVlMNCt3bwhcTI5GLPrt2e93DAsmlqOzw1cFiPPg6S5sLCXz7LbbUQpLha8S4v # 2qccMtTokEaDQS+QJErnAsl6VSmRvAy0nlj+C/PaZuLb3OzY0ARw7UeCZLpyWPPH # +k5MdYj6NUDTNoXqbzQHCuPs+fgIoro5y3DHoO077g6Ir2THIx1yfVFEt5zDcFPO # YMg4yBi4A6Xc3hm9tZ6w849nBvVKwm5YALfH3y/f3n4LnN61b1wzAx3ZCZjf13UK # bpE7p6DYJrHRB/+pwFjG99TwHH6uXzDeZT6/r6qH7AABwn8fpYc1TmleFY8YRuVz # zjp9VkPHV8VzvzLL7QK2kteeXLL/Y4lvjL6hzyOmE+1LVD3lEbYho1zCt+F7bU+F # pjyBfTC4i/wHsptb218YlbkQt1i1B6llmJwVFwCLX7gxQ48QIGUacMy8kp1+zczY # +SxlpaEgNmQkfc1raPh9y5sMa6X48+x0K7B8OqDoXcTiECIjJetxwtuBlQseJ05H # RfisfgFm09kG7vdHEo3NbUuMMBFikc4boN9Ufm0iUhq/JtqV0Kwrv9Cv3ayDgdNw # EWiL2a65InEWSpRTYfsCQ03eqEh5A3rwV/KfUFcit+DrP+9VcDpjWRsCokZv4tgn # 5qAXNMtHa8NiqQIDAQABo4IBNjCCATIwHQYDVR0OBBYEFKuX02ICFFdXgrcCBmDJ # fH5v/KkXMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRY # MFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p # Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEF # BQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w # a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo # MSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI # hvcNAQELBQADggIBAOCzNt4fJ+jOvQuq0Itn37IZrYNBGswAi+IAFM3YGK/wGQlE # ncgjmNBuac95W2fAL6xtFVfMfkeqSLMLqoidVsU9Bm4DEBjaWNOT9uX/tcYiJSfF # QM0rDbrl8V4nM88RZF56G/qJW9g5dIqOSoimzKUt/Q7WH6VByW0sar5wGvgovK3q # FadwKShzRYcEqTkHH2zip5e73jezPHx2+taYqJG5xJzdDErZ1nMixRjaHs3Kpcsm # ZYuxsIRfBYOJvAFGymTGRv5PuwsNps9Ech1Aasq84H/Y/8xN3GQj4P3MiDn8izUB # DCuXIfHYk39bqnaAmFbUiCby+WWpuzdk4oDKz/sWwrnsoQ72uEGVEN7+kyw9+HSo # 5i8l8Zg1Ymj9tUgDpVUGjAduoLyHQ7XqknKmS9kJSBKk4okEDg0Id6LeKLQwH1e4 # aVeTyUYwcBX3wg7pLJQWvR7na2SGrtl/23YGQTudmWOryhx9lnU7KBGV/aNvz0tT # pcsucsK+cZFKDEkWB/oUFVrtyun6ND5pYZNj0CgRup5grVACq/Agb+EOGLCD+zEt # GNop4tfKvsYb64257NJ9XrMHgpCib76WT34RPmCBByxLUkHxHq5zCyYNu0IFXAt1 # AVicw14M+czLYIVM7NOyVpFdcB1B9MiJik7peSii0XTRdl5/V/KscTaCBFz3MIIH # cTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB # iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp # TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEw # OTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIh # C3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNx # WuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFc # UTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAc # nVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUo # veO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyzi # YrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9 # fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdH # GO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7X # KHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiE # R9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/ # eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3 # FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAd # BgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEE # AYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMI # MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB # Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud # HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By # b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO # MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy # dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4IC # AQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pk # bHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gng # ugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3 # lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHC # gRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6 # MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEU # BHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvsh # VGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+ # fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrp # NPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHI # qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAs4wggI3AgEBMIH4 # oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw # IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1U # aGFsZXMgVFNTIEVTTjpFNUE2LUUyN0MtNTkyRTElMCMGA1UEAxMcTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUA0Y+CyLezGgVHWFNm # KI1LuE/hY6uggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN # BgkqhkiG9w0BAQUFAAIFAOZpqYQwIhgPMjAyMjA3MDIwMTEyMzZaGA8yMDIyMDcw # MzAxMTIzNlowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA5mmphAIBADAKAgEAAgIe # WAIB/zAHAgEAAgIR3TAKAgUA5mr7BAIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor # BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUA # A4GBAGizLxBkK6B2oQmB80+aM7sOfXuAgkYj93m3glu2ZQ242mTevtDINaDoV+qc # 7BhagNfrWoCVESVZEjaqCYrV/VVxnmuKU0VXJoUnLcNW0cmK3aTV1fENg1wMmi/r # TFKkd1udFGkqk+aoUxPKMtV19/TbRjUL/5qxCuVE0uACxjXOMYIEDTCCBAkCAQEw # gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAGVt/wN1uM3MSUA # AQAAAZUwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B # CRABBDAvBgkqhkiG9w0BCQQxIgQg7wxBntfZfrgc+uuByghKGEFJWZuIL6DEXRVE # WWTTqNowgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCBc5kvhjZALe2mhIz/Q # d7keVOmA/cC1dzKZT4ybLEkCxzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAABlbf8DdbjNzElAAEAAAGVMCIEIKYVsiXt/OMHtVXLwf4B # pSdKHBzozlb2nRtmmiDl84fKMA0GCSqGSIb3DQEBCwUABIICAIVHstuzLLngbRuA # f6xugLOjcbg1MNZdn9l1UIHXSFSPGQWqiL2dK4o6sjWpDFkdt2muopWbZRGFBmGw # VIuZeYRztWON1VcW177otKa1hlV7WyF/VvEqytpXyjybORUwmCkKyLOxl9X6yzWi # INaJPOYk09kAQkUFt/MNYIsxr++dQX14DOoJxm9tpCEvIYl9mnUU+iQnE5B9AEMw # +nC4D7IMA1+6smM7fbSJa7o4BHfyje8PHB3w9GF223mZTG0EhBlultQkMSpV/c88 # 9hsbwx16Cr5sY9M/lSRt4oC3qzSuTmYd6VYJ/ILt9ptrpOkaYCiXXRx8Cfz7w53w # Au/J8xJjNWvrKxkcc8XiUXPfGGTXujyiS2MqvztBkg6wCduFKqogmvOtQiiwQQxE # G6lU/rss27omoTUc41EawOr1km5y+fUS9aoYX9K8NNhFH6TSni3dp/+Hiyif1T7X # g0cBy4yHuYxMmRrFcmGeplW3KhXHfkJjbHaVs1QgnRfkgFuypwF5YoFWrW7Xgj+a # ZCDKSoYq45E4v0ryIvyu0shBoHQXREAzpBv3L9h5A9vEFQG4alCI57oSbdqJ1YIa # ggkTQHR2CWdB7FnQilCqqZjSnAtXYZh/RD+PX6fg1UyUUQf5ohnw951pQeKYTYHm # Fwut+RibzdbHEF/kLZr6SZsDupCv # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PackageManagement/1.4.8.1/PackageManagement.psm1 ================================================ # # Script module for module 'PackageManagement' # Set-StrictMode -Version Latest Microsoft.PowerShell.Utility\Import-LocalizedData LocalizedData -filename PackageManagement.Resources.psd1 # Summary: PackageManagement is supported on Windows PowerShell 3.0 or later, Nano Server and PowerShellCore $isCore = ($PSVersionTable.Keys -contains "PSEdition") -and ($PSVersionTable.PSEdition -ne 'Desktop') $binarySubPath = '' if ($isCore) { $binarySubPath = Join-Path -Path 'coreclr' -ChildPath 'netstandard2.0' } else { $binarySubPath = 'fullclr' } # Set up some helper variables to make it easier to work with the module $script:PSModule = $ExecutionContext.SessionState.Module $script:PSModuleRoot = $script:PSModule.ModuleBase $script:PkgMgmt = 'Microsoft.PackageManagement.dll' $script:PSPkgMgmt = 'Microsoft.PowerShell.PackageManagement.dll' # Try to import the OneGet assemblies at the same directory regardless fullclr or coreclr $OneGetModulePath = Join-Path -Path $script:PSModuleRoot -ChildPath $script:PkgMgmt $binaryModuleRoot = $script:PSModuleRoot if(-not (Test-Path -Path $OneGetModulePath)) { # Import the appropriate nested binary module based on the current PowerShell version $binaryModuleRoot = Join-Path -Path $script:PSModuleRoot -ChildPath $binarySubPath $OneGetModulePath = Join-Path -Path $binaryModuleRoot -ChildPath $script:PkgMgmt } $PSOneGetModulePath = Join-Path -Path $binaryModuleRoot -ChildPath $script:PSPkgMgmt $OneGetModule = Import-Module -Name $OneGetModulePath -PassThru $PSOneGetModule = Import-Module -Name $PSOneGetModulePath -PassThru # When the module is unloaded, remove the nested binary module that was loaded with it if($OneGetModule) { $script:PSModule.OnRemove = { Remove-Module -ModuleInfo $OneGetModule } } if($PSOneGetModule) { $script:PSModule.OnRemove = { Remove-Module -ModuleInfo $PSOneGetModule } } # SIG # Begin signature block # MIInpQYJKoZIhvcNAQcCoIInljCCJ5ICAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAu+2bjkKZwIc24 # sUnQmGnTVuZ6xttIA9Ea89zWbpdeVaCCDYUwggYDMIID66ADAgECAhMzAAACU+OD # 3pbexW7MAAAAAAJTMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMzAwWhcNMjIwOTAxMTgzMzAwWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDLhxHwq3OhH+4J+SX4qS/VQG8HybccH7tnG+BUqrXubfGuDFYPZ29uCuHfQlO1 # lygLgMpJ4Geh6/6poQ5VkDKfVssn6aA1PCzIh8iOPMQ9Mju3sLF9Sn+Pzuaie4BN # rp0MuZLDEXgVYx2WNjmzqcxC7dY9SC3znOh5qUy2vnmWygC7b9kj0d3JrGtjc5q5 # 0WfV3WLXAQHkeRROsJFBZfXFGoSvRljFFUAjU/zdhP92P+1JiRRRikVy/sqIhMDY # +7tVdzlE2fwnKOv9LShgKeyEevgMl0B1Fq7E2YeBZKF6KlhmYi9CE1350cnTUoU4 # YpQSnZo0YAnaenREDLfFGKTdAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUlZpLWIccXoxessA/DRbe26glhEMw # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ2NzU5ODAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AKVY+yKcJVVxf9W2vNkL5ufjOpqcvVOOOdVyjy1dmsO4O8khWhqrecdVZp09adOZ # 8kcMtQ0U+oKx484Jg11cc4Ck0FyOBnp+YIFbOxYCqzaqMcaRAgy48n1tbz/EFYiF # zJmMiGnlgWFCStONPvQOBD2y/Ej3qBRnGy9EZS1EDlRN/8l5Rs3HX2lZhd9WuukR # bUk83U99TPJyo12cU0Mb3n1HJv/JZpwSyqb3O0o4HExVJSkwN1m42fSVIVtXVVSa # YZiVpv32GoD/dyAS/gyplfR6FI3RnCOomzlycSqoz0zBCPFiCMhVhQ6qn+J0GhgR # BJvGKizw+5lTfnBFoqKZJDROz+uGDl9tw6JvnVqAZKGrWv/CsYaegaPePFrAVSxA # yUwOFTkAqtNC8uAee+rv2V5xLw8FfpKJ5yKiMKnCKrIaFQDr5AZ7f2ejGGDf+8Tz # OiK1AgBvOW3iTEEa/at8Z4+s1CmnEAkAi0cLjB72CJedU1LAswdOCWM2MDIZVo9j # 0T74OkJLTjPd3WNEyw0rBXTyhlbYQsYt7ElT2l2TTlF5EmpVixGtj4ChNjWoKr9y # TAqtadd2Ym5FNB792GzwNwa631BPCgBJmcRpFKXt0VEQq7UXVNYBiBRd+x4yvjqq # 5aF7XC5nXCgjbCk7IXwmOphNuNDNiRq83Ejjnc7mxrJGMIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGXYwghlyAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAJT44Pelt7FbswAAAAA # AlMwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMQt # y3cqdLHMkORDousYFit1gZHgDA5G8ruNWC2M1yt8MEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEACK1h1k5yJH9t/LbulIT8EoMZ45iY6fTsCiXj # iBdhKfo1OenarZN4LivqwI6cxXSZWNzvIQ2umswdxzfMJy+0aBwrQ5zc++MxF4p8 # p04vI4uuwSnZpFgBSS57hdZd64NoBXkan+YMMz01THWrz0AFFBI4euN9ASE5lF5r # rZ1YvyTPhsgleGVXcV45YsM6Xp0sTnvtgdmHeyUknVefJwR4ZmaNp6EC3QKuHkna # B686mvfbKsMXtPvOMt6tN2MTMPCPrSJ/+D2aV+JKwjqk+MmIAZ0JeTXq7Oy3H4i3 # eR66oB24MwbOPL57sgVN35CK3trEV9eSwJbks6fIplb+UZlW9aGCFwAwghb8Bgor # BgEEAYI3AwMBMYIW7DCCFugGCSqGSIb3DQEHAqCCFtkwghbVAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCBu5SYV/TjMU+1To3jN1ZjWGQGhTTO1qrzJ # 2j1cR5AwHgIGYrTVw4J4GBMyMDIyMDcwMTIxMDAwMS4wODNaMASAAgH0oIHQpIHN # MIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL # ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjo4QTgyLUUzNEYtOUREQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCEVcwggcMMIIE9KADAgECAhMzAAABmciPr622fb6LAAEA # AAGZMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTIxMTIwMjE5MDUxNloXDTIzMDIyODE5MDUxNlowgcoxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVy # aWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjhBODItRTM0 # Ri05RERBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuBP5V1yjLMva1WtmvG0W9AK/ # z4+PmyogIDICmsrwjBZ93XRqH/HlhYCVqeSXSo1o655lta/bR6XD9pBaY16dlHa4 # +eY7eTWItTBEXKCKPhNvV7gsBQDCX7Rnb7Fnmvzk0DyBbGRFqhLXl3UZ3MlcHLec # RcwSAtSVgnZn3O0efUTCMr4FoyM/N+epsHqMJDrLTdX3udyiCfH5a2CCyEFrDUTD # k985wBsCG/ZMi+eAuo2YxcTkrrBmJlUPhq/6mUBC8NYqcfbOYzT9S0qTqX0xARbv # svSizQZjAQEZEj6UxZBhMhksBhoEnTougVOhTyvurq2fN9I60XpiuCZCCfbUIZRn # CIQI4XKmUrZ/t/JUY1b632wSORKSNjcJVSCtomxlu7FnkwM/8jTzSElG0IRgCbHv # zoBo/9as/x2vZqn2GfAR3ITcZ9PJ1pKsq46yonEMsbg/JgmrU61UmziWH0MmXR0f # WuL4MhnmRFwWEDQJSLgd+0AjgILP1Xg1PDOHXPyJa08gtLldUkuBpPM1fuV+Xkbv # lzkTfBVqsyOpP/cunFsDljCumlDdV6i3Ghf7Jva1/OHiltpYQ3Nwt9Vi46fFDnzh # 2Xz7ssueMX4pZ4YQj8uECifY4IKKqnnhIQhH4A2I8vjwclf3uRwwdsR3bZS/d8Jb # QJCN2gsaPOJFTwQmtDsCAwEAAaOCATYwggEyMB0GA1UdDgQWBBSNula4Ppxlr67y # IdHUDxKJxDUdEDAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNV # HR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2Ny # bC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYI # KwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAy # MDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0G # CSqGSIb3DQEBCwUAA4ICAQBxs1odvYmwTa6N57PBNxDhxrCwdRs5PdEhWFCgYpSL # uIw20e6m71zXfJ55f8+Q/SyoMN0/NY6subTIlqfqeI2AQZNz0cdckqi1H4ReWTFq # JUc1vnEGRl1d/IkpnQFBtAqUecdazHTyK03M1VBbDmBqppKFEP3UYlS+IgFySwjU # 4+Md2MCEHAj7dIoyVYpzyDAGMwPH/fQW3orJMiNARxgHdJBlF9iNv6bmJ5ckvLaA # ZwDY4dtcYLxLmmdnb0ar1pP4tVyJTuu2rv1wnjE714TziFJqvNFN9k4n8N0kogSk # 8oq+5qFh9Yux9quUAJ6wcbssv/2bA3wEQ1fwLZo+Y6+gE30Rw3VGp0KXqAUvospT # VOkQEbvIPkzArv2Z3WZq7PcPvIGpVCBoPhppTZNO31yefDnPqhaoDQLDZDIvnUsy # pFhgOP5JSRZ9F4nIVy/Preyjmslyhs0I2ugnbQo4rJ8kvIW7+DyoWnjLX+vMCVii # jrNLbdgCX2Aub0mx/XJPARo9nv8kFxGRLvPC+f3xyuV1JPyCl8d82i5MV00meNxG # v1il1QczGrB2dz5p8tJqBHpAF8dVISzTWL+Fbpt91Yq8Ugeq/xikMRUh7u8hX61k # KWMsgx9dfU5oQHnUvE9VWm6nbTuWT9hNeEyvNlcsct8RD3pTqOHUSJYZu12OpPQe # vDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZIhvcNAQEL # BQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNV # BAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4X # DTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDk4aZM # 57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25PhdgM/9cT8dm # 95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPFdvWGUNzB # RMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6GnszrYBb # fowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBpDco2LXCO # Mcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50ZuyjLVwIYw # XE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3EXzTdEonW # /aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0lBw0gg/w # EPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1qGFphAXPK # Z6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ+QuJYfM2 # BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PAPBXbGjfH # CBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkwEgYJKwYB # BAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxGNSnPEP8v # BO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARVMFMwUQYM # KwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAKBggrBgEF # BQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBW # BgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUH # AQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # L2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG9w0BAQsF # AAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0xM7U518Jx # Nj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmCVgADsAW+ # iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449xvNo32X2 # pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wMnosZiefw # C2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDSPeZKPmY7 # T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2dY3RILLFO # Ry3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxnGSgkujhL # mm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+CrvsQWY9af3L # wUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokLjzbaukz5 # m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL6Xu/OHBE # 0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggLOMIICNwIB # ATCB+KGB0KSBzTCByjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEmMCQGA1UE # CxMdVGhhbGVzIFRTUyBFU046OEE4Mi1FMzRGLTlEREExJTAjBgNVBAMTHE1pY3Jv # c29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAJLv82Lo56mq # TegSfTCeY7YA65TroIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTAwDQYJKoZIhvcNAQEFBQACBQDmaTc9MCIYDzIwMjIwNzAxMTcwNTAxWhgPMjAy # MjA3MDIxNzA1MDFaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFAOZpNz0CAQAwCgIB # AAICERUCAf8wBwIBAAICEbMwCgIFAOZqiL0CAQAwNgYKKwYBBAGEWQoEAjEoMCYw # DAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0B # AQUFAAOBgQBT54+WmHojMQCYQgQt18iy8jWH/zrm88CORF+vITV/kwvO/zDL8aHA # IHzJNcy5CXpPKzy5Vhln9qHhkGMMjkNDjIw+WFOC55FYzdJdNu/r8E9kWn3OkSFh # YkGyBkUQRX1wxYMrjPe3VRH/CAkirEZQgoBQFfCTDXTQ+ybTfsRG+jGCBA0wggQJ # AgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAk # BgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABmciPr622 # fb6LAAEAAAGZMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZI # hvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIE7ONnfPZ+1TgSlm38TzyWp8xzS0ag8S # us6x6ktqqn9FMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgZn1p+fKije1l # qQU2Dq1DtQUgkpyjywvxu8AjiHNibH4wgZgwgYCkfjB8MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T # dGFtcCBQQ0EgMjAxMAITMwAAAZnIj6+ttn2+iwABAAABmTAiBCC0Y+62+yZ1e93v # MC2z6glC0K3irTMXW68vE4DsYaOXnTANBgkqhkiG9w0BAQsFAASCAgBbNcmClGcW # 0/bHfTnMXyPfcg+udBEUubR+oR3GKng5bIH4LXzBiLXiGeChHgHzN81BtET8g+84 # mgQKCHEKer9K1IVHhn5meD6PzDmknpB5U+ZvzmNXgRVL23iK81mDJdEq3RwrfwSJ # f86PUlrVmK3MdyRzfP92miVpeIzcqTPNQJzzOfFPgi07dyLVCZ2ZT11uAz+WksxW # XjrD+DGzxK0EQDbltXlfIxcrT+FIF+yYA3HVs/g4HUMgT2MsOFjzyGu3MD/rNbb6 # 8p2wGoYB1WkVzuEvV3S4L/ZO/oTUB59aM4XnvFhi0tsQKNJYuqysTAdIdRaJWH3p # vp4OG/R0RTRXfmGueTwOUP6xbZXSluQa+a3PlHgGsnP8nwB6Mhty3tgzy46SSmdC # feHAd+MN+x5Qx9ujIaMLa9bqWf/Q9lCgwIHbxNOIsoC6O3N15IAI/t39U0qMBXe1 # Cm7DBSk7ldHponGKcteZ27k/Aqc5R8blqt0zgruhMRq5G6Rp8+m3VwWAwZiDjlC8 # hRUuIcE8AuGCrTtu0omm8tNyjDFd0ypl81x5HceIlHrpwCIUaVd5gTGNd1/dJyW3 # CTgdG0/M7ELkTjNfLi16NGtEeS+i+UYe2Cm32pPxXwx+q+YJ0N9wojtS6450z19x # StE87RWDz8GuJ4o02QeRpwQQwVTHpnjaAA== # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PackageManagement/1.4.8.1/PackageProviderFunctions.psm1 ================================================ ### # ==++== # # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ### <# Overrides the default Write-Debug so that the output gets routed back thru the $request.Debug() function #> function Write-Debug { param( [Parameter(Mandatory=$true)][string] $message, [parameter(ValueFromRemainingArguments=$true)] [object[]] $args= @() ) if( -not $request ) { if( -not $args ) { Microsoft.PowerShell.Utility\write-verbose $message return } $msg = [system.string]::format($message, $args) Microsoft.PowerShell.Utility\write-verbose $msg return } if( -not $args ) { $null = $request.Debug($message); return } $null = $request.Debug($message,$args); } function Write-Error { param( [Parameter(Mandatory=$true)][string] $Message, [Parameter()][string] $Category, [Parameter()][string] $ErrorId, [Parameter()][string] $TargetObject ) $null = $request.Warning($Message); } <# Overrides the default Write-Verbose so that the output gets routed back thru the $request.Verbose() function #> function Write-Progress { param( [CmdletBinding()] [Parameter(Position=0)] [string] $Activity, # This parameter is not supported by request object [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] $Status, [Parameter(Position=2)] [ValidateRange(0,[int]::MaxValue)] [int] $Id, [Parameter()] [int] $PercentComplete=-1, # This parameter is not supported by request object [Parameter()] [int] $SecondsRemaining=-1, # This parameter is not supported by request object [Parameter()] [string] $CurrentOperation, [Parameter()] [ValidateRange(-1,[int]::MaxValue)] [int] $ParentID=-1, [Parameter()] [switch] $Completed, # This parameter is not supported by request object [Parameter()] [int] $SourceID, [object[]] $args= @() ) $params = @{} if ($PSBoundParameters.ContainsKey("Activity")) { $params.Add("Activity", $PSBoundParameters["Activity"]) } if ($PSBoundParameters.ContainsKey("Status")) { $params.Add("Status", $PSBoundParameters["Status"]) } if ($PSBoundParameters.ContainsKey("PercentComplete")) { $params.Add("PercentComplete", $PSBoundParameters["PercentComplete"]) } if ($PSBoundParameters.ContainsKey("Id")) { $params.Add("Id", $PSBoundParameters["Id"]) } if ($PSBoundParameters.ContainsKey("ParentID")) { $params.Add("ParentID", $PSBoundParameters["ParentID"]) } if ($PSBoundParameters.ContainsKey("Completed")) { $params.Add("Completed", $PSBoundParameters["Completed"]) } if( -not $request ) { if( -not $args ) { Microsoft.PowerShell.Utility\Write-Progress @params return } $params["Activity"] = [system.string]::format($Activity, $args) Microsoft.PowerShell.Utility\Write-Progress @params return } if( -not $args ) { $request.Progress($Activity, $Status, $Id, $PercentComplete, $SecondsRemaining, $CurrentOperation, $ParentID, $Completed) } } function Write-Verbose{ param( [Parameter(Mandatory=$true)][string] $message, [parameter(ValueFromRemainingArguments=$true)] [object[]] $args= @() ) if( -not $request ) { if( -not $args ) { Microsoft.PowerShell.Utility\write-verbose $message return } $msg = [system.string]::format($message, $args) Microsoft.PowerShell.Utility\write-verbose $msg return } if( -not $args ) { $null = $request.Verbose($message); return } $null = $request.Verbose($message,$args); } <# Overrides the default Write-Warning so that the output gets routed back thru the $request.Warning() function #> function Write-Warning{ param( [Parameter(Mandatory=$true)][string] $message, [parameter(ValueFromRemainingArguments=$true)] [object[]] $args= @() ) if( -not $request ) { if( -not $args ) { Microsoft.PowerShell.Utility\write-warning $message return } $msg = [system.string]::format($message, $args) Microsoft.PowerShell.Utility\write-warning $msg return } if( -not $args ) { $null = $request.Warning($message); return } $null = $request.Warning($message,$args); } <# Creates a new instance of a PackageSource object #> function New-PackageSource { param( [Parameter(Mandatory=$true)][string] $name, [Parameter(Mandatory=$true)][string] $location, [Parameter(Mandatory=$true)][bool] $trusted, [Parameter(Mandatory=$true)][bool] $registered, [bool] $valid = $false, [System.Collections.Hashtable] $details = $null ) return New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.PackageSource -ArgumentList $name,$location,$trusted,$registered,$valid,$details } <# Creates a new instance of a SoftwareIdentity object #> function New-SoftwareIdentity { param( [Parameter(Mandatory=$true)][string] $fastPackageReference, [Parameter(Mandatory=$true)][string] $name, [Parameter(Mandatory=$true)][string] $version, [Parameter(Mandatory=$true)][string] $versionScheme, [Parameter(Mandatory=$true)][string] $source, [string] $summary, [string] $searchKey = $null, [string] $fullPath = $null, [string] $filename = $null, [System.Collections.Hashtable] $details = $null, [System.Collections.ArrayList] $entities = $null, [System.Collections.ArrayList] $links = $null, [bool] $fromTrustedSource = $false, [System.Collections.ArrayList] $dependencies = $null, [string] $tagId = $null, [string] $culture = $null, [string] $destination = $null ) return New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.SoftwareIdentity -ArgumentList $fastPackageReference, $name, $version, $versionScheme, $source, $summary, $searchKey, $fullPath, $filename , $details , $entities, $links, $fromTrustedSource, $dependencies, $tagId, $culture, $destination } <# Creates a new instance of a SoftwareIdentity object based on an xml string #> function New-SoftwareIdentityFromXml { param( [Parameter(Mandatory=$true)][string] $xmlSwidtag, [bool] $commitImmediately = $false ) return New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.SoftwareIdentity -ArgumentList $xmlSwidtag, $commitImmediately } <# Creates a new instance of a DyamicOption object #> function New-DynamicOption { param( [Parameter(Mandatory=$true)][Microsoft.PackageManagement.MetaProvider.PowerShell.OptionCategory] $category, [Parameter(Mandatory=$true)][string] $name, [Parameter(Mandatory=$true)][Microsoft.PackageManagement.MetaProvider.PowerShell.OptionType] $expectedType, [Parameter(Mandatory=$true)][bool] $isRequired, [System.Collections.ArrayList] $permittedValues = $null ) if( -not $permittedValues ) { return New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.DynamicOption -ArgumentList $category,$name, $expectedType, $isRequired } return New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.DynamicOption -ArgumentList $category,$name, $expectedType, $isRequired, $permittedValues.ToArray() } <# Creates a new instance of a Feature object #> function New-Feature { param( [Parameter(Mandatory=$true)][string] $name, [System.Collections.ArrayList] $values = $null ) if( -not $values ) { return New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.Feature -ArgumentList $name } return New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.Feature -ArgumentList $name, $values.ToArray() } <# Duplicates the $request object and overrides the client-supplied data with the specified values. #> function New-Request { param( [System.Collections.Hashtable] $options = $null, [System.Collections.ArrayList] $sources = $null, [PSCredential] $credential = $null ) return $request.CloneRequest( $options, $sources, $credential ) } function New-Entity { param( [Parameter(Mandatory=$true)][string] $name, [Parameter(Mandatory=$true,ParameterSetName="role")][string] $role, [Parameter(Mandatory=$true,ParameterSetName="roles")][System.Collections.ArrayList]$roles, [string] $regId = $null, [string] $thumbprint= $null ) $o = New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.Entity $o.Name = $name # support role as a NMTOKENS string or an array of strings if( $role ) { $o.Role = $role } if( $roles ) { $o.Roles = $roles } $o.regId = $regId $o.thumbprint = $thumbprint return $o } function New-Link { param( [Parameter(Mandatory=$true)][string] $HRef, [Parameter(Mandatory=$true)][string] $relationship, [string] $mediaType = $null, [string] $ownership = $null, [string] $use= $null, [string] $appliesToMedia= $null, [string] $artifact = $null ) $o = New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.Link $o.HRef = $HRef $o.Relationship =$relationship $o.MediaType =$mediaType $o.Ownership =$ownership $o.Use = $use $o.AppliesToMedia = $appliesToMedia $o.Artifact = $artifact return $o } function New-Dependency { param( [Parameter(Mandatory=$true)][string] $providerName, [Parameter(Mandatory=$true)][string] $packageName, [string] $version= $null, [string] $source = $null, [string] $appliesTo = $null ) $o = New-Object -TypeName Microsoft.PackageManagement.MetaProvider.PowerShell.Dependency $o.ProviderName = $providerName $o.PackageName =$packageName $o.Version =$version $o.Source =$source $o.AppliesTo = $appliesTo return $o } # SIG # Begin signature block # MIInsQYJKoZIhvcNAQcCoIInojCCJ54CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDs5FoY/w7B0Miq # Kfi0Wb7Z3JGNbdfm1vY/fLsAXNclnKCCDYUwggYDMIID66ADAgECAhMzAAACU+OD # 3pbexW7MAAAAAAJTMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMzAwWhcNMjIwOTAxMTgzMzAwWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDLhxHwq3OhH+4J+SX4qS/VQG8HybccH7tnG+BUqrXubfGuDFYPZ29uCuHfQlO1 # lygLgMpJ4Geh6/6poQ5VkDKfVssn6aA1PCzIh8iOPMQ9Mju3sLF9Sn+Pzuaie4BN # rp0MuZLDEXgVYx2WNjmzqcxC7dY9SC3znOh5qUy2vnmWygC7b9kj0d3JrGtjc5q5 # 0WfV3WLXAQHkeRROsJFBZfXFGoSvRljFFUAjU/zdhP92P+1JiRRRikVy/sqIhMDY # +7tVdzlE2fwnKOv9LShgKeyEevgMl0B1Fq7E2YeBZKF6KlhmYi9CE1350cnTUoU4 # YpQSnZo0YAnaenREDLfFGKTdAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUlZpLWIccXoxessA/DRbe26glhEMw # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ2NzU5ODAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AKVY+yKcJVVxf9W2vNkL5ufjOpqcvVOOOdVyjy1dmsO4O8khWhqrecdVZp09adOZ # 8kcMtQ0U+oKx484Jg11cc4Ck0FyOBnp+YIFbOxYCqzaqMcaRAgy48n1tbz/EFYiF # zJmMiGnlgWFCStONPvQOBD2y/Ej3qBRnGy9EZS1EDlRN/8l5Rs3HX2lZhd9WuukR # bUk83U99TPJyo12cU0Mb3n1HJv/JZpwSyqb3O0o4HExVJSkwN1m42fSVIVtXVVSa # YZiVpv32GoD/dyAS/gyplfR6FI3RnCOomzlycSqoz0zBCPFiCMhVhQ6qn+J0GhgR # BJvGKizw+5lTfnBFoqKZJDROz+uGDl9tw6JvnVqAZKGrWv/CsYaegaPePFrAVSxA # yUwOFTkAqtNC8uAee+rv2V5xLw8FfpKJ5yKiMKnCKrIaFQDr5AZ7f2ejGGDf+8Tz # OiK1AgBvOW3iTEEa/at8Z4+s1CmnEAkAi0cLjB72CJedU1LAswdOCWM2MDIZVo9j # 0T74OkJLTjPd3WNEyw0rBXTyhlbYQsYt7ElT2l2TTlF5EmpVixGtj4ChNjWoKr9y # TAqtadd2Ym5FNB792GzwNwa631BPCgBJmcRpFKXt0VEQq7UXVNYBiBRd+x4yvjqq # 5aF7XC5nXCgjbCk7IXwmOphNuNDNiRq83Ejjnc7mxrJGMIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGYIwghl+AgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAJT44Pelt7FbswAAAAA # AlMwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFtW # lCf/wF6GbazDLehu9mS8WGiUm3tcu/5KpjXCCIesMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAwETHSA6ffhhSRTPNmFZi9dFIoroMG7jii7/9 # LMoCAZgwSeSzaZs3vo1EaRFFZvaKoQkPITRK9rBjINt9jLGalSs2jrwdM/E5wa8k # bKFY4VEB/aJffdDIGiAsMu+gOWxCOFS8xdYzlZfRlFe9f6VVRtCyubOok3BSZRrq # cCFpCprCa34w/J8erd7FF54qJ2IrO3bEd4w+23w3qg1BwtPr/vqLchKyIEBjzpcL # 78/CJEUpFbtz/VuvyLfeVoSEnj6Ctf0tFDvRDNXGPqp1ZJSitKDsrQseb2K5/pZO # nyykNPzpxJmIaLHDgBjtGlXuiHUEuJAFV96oucoIydX1dqQal6GCFwwwghcIBgor # BgEEAYI3AwMBMYIW+DCCFvQGCSqGSIb3DQEHAqCCFuUwghbhAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFVBgsqhkiG9w0BCRABBKCCAUQEggFAMIIBPAIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCCmK+qErRnVeAaOr2etmjZMIBNv7JyifxQE # HuWjeTnqvAIGYrGiJBRoGBMyMDIyMDcwMTIxMDAwMS42ODRaMASAAgH0oIHUpIHR # MIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQL # EyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhh # bGVzIFRTUyBFU046NDYyRi1FMzE5LTNGMjAxJTAjBgNVBAMTHE1pY3Jvc29mdCBU # aW1lLVN0YW1wIFNlcnZpY2WgghFfMIIHEDCCBPigAwIBAgITMwAAAaQHz+OPo7pv # 1gABAAABpDANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg # MjAxMDAeFw0yMjAzMDIxODUxMThaFw0yMzA1MTExODUxMThaMIHOMQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3NvZnQg # T3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046 # NDYyRi1FMzE5LTNGMjAxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl # cnZpY2UwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDAR44A+hT8vNT1 # IXDiFRoeGzkmqut+GPk41toTRfQZZ1sSyQhLjIlemBecemEzO09WSzOjZx9MIT8q # Ys921WUZsIBsk1ESn1cjyfPUd1mmfxzL3ACWZwjIC/pjqcRPeIMECQ/6qPFKrjqw # igmP33I3IcVfMjJHyKj+vR51n1tK2rZPiNhmRdiEhckbbxLsSb2nCBQxZEF49x/l # 8vSB8zaqovoOeIkIzgDerN7OvJouq6r+vg/Qz1T4NXr+sKKyNxZWM6zywiLp7G7W # Ld18N2hyjHwPkh/AleIqif3hGVD9bhSU+dDADzUJSMFhEWunHHElQeZjdmIB3/Mw # 1KkFOJNvw1sPteIi5MK4DZX3Wd/Fd8ZsQvZmXPWJ8BXN9sYtHMz8zdeQvMImRCKg # nXcW8IpnPtC7Tymp3UV5NoTH8INF6WWicQ3y04L2I1VOT104AddJoVgAP2KLIGwf # Cs7wMVz56xJ2IN1y1pIAWfpTqx76orM5RQhkAvayj1RTwgrHst+elYX3F5b8ACWr # gJO1dJy1U4MIv+SC8h33xLmWA568emvrJ6g0xy/2akbAeRx6tFwaP4uwVbjF50kl # 5RQqNzp/CDpfCTikOAqyJa4valiWDMbEiArHKLYDg6GDjuJZl5bSjgdJdCAIRF8E # kiiA+UAGvcE6SGoHmtoc4yOklGNVvwIDAQABo4IBNjCCATIwHQYDVR0OBBYEFOLQ # E5+s+AgS9sWUHdI4zekp4yTCMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1 # GelyMF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w # a2lvcHMvY3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEp # LmNybDBsBggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUy # MFBDQSUyMDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYB # BQUHAwgwDQYJKoZIhvcNAQELBQADggIBAAlWHFDRDJck7jwwRoYmdVOePLLBeido # PUBJVhG9nGeHS9PuRvO9tf4IkbUz74MUIQxeayQoxxo/JxUqjhPH52M/b4G9mHJW # B75KCllCTg8Y4VkvktOmS0f5w0vOR3gwA9BRnbgAPNEO7xs5Jylto8aDR02++CkB # DFolCtTNjwzfniEj1z4T7nRlRi2yBAJNRqI+VY820LiyoZtk5OGttq5F5HhPfIMj # aIx5QYR22+53sd8xgUwRpFbcLdrne6jdq3KbiYbCf7y/9F2C7cjpO3kkGXX8ntE0 # 9f6o9fIklx7CFw4RzrkyqgYomraKOFJ8JO7hsjNJb9/Gba/mKWo0j/qdDxDER/UX # X6ykZuGx1eQpjkyMwJnOPWGbeNIYZVcJQpRQODPs593Mi5hBsHzag+vd4Q+Vt73K # Z4X98YWW1Vk1aSR9Qjxk5keMuVPZMcMrCvFZXwhUcGFGueuNCrICL9bSYRfS13pl # iDxJ7sPSZ8x2d4ksOXW00l6fR5nTiSM7Dvv7Y0MGVgUhap2smhr92PMNSmIkCUvH # CiYcJ4RoAT28mp/hOQ/U8mPXSpWdxYpLLcDOISmBhFJYN7amlhIpVsGvUmjXrTcY # 0n4Goe/Nqs2400IcA4HOiX9OxdmpNGDJzSRR7AW9TT8O+3YZqPZIvL6yzgfvnehp # tmf4w6QzkrLfMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg # VGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+ # F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU # 88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqY # O7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzp # cGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0Xn # Rm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1 # zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZN # N3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLR # vWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTY # uVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUX # k8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB # 2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKR # PEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0g # BFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5t # aWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQM # MAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQE # AwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQ # W9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNv # bS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBa # BggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqG # SIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOX # PTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6c # qYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/z # jj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz # /AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyR # gNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdU # bZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo # 3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4K # u+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10Cga # iQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9 # vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGC # AtIwggI7AgEBMIH8oYHUpIHRMIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8g # UmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046NDYyRi1FMzE5LTNGMjAxJTAj # BgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMC # GgMVADQcKOKTa3xC+g1aPrcPerxiby6foIGDMIGApH4wfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDmaU8/MCIYDzIwMjIwNzAx # MTQ0NzI3WhgPMjAyMjA3MDIxNDQ3MjdaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIF # AOZpTz8CAQAwCgIBAAICCNwCAf8wBwIBAAICEp4wCgIFAOZqoL8CAQAwNgYKKwYB # BAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGG # oDANBgkqhkiG9w0BAQUFAAOBgQChh1QY1GFw/ekl4Qr+s3lJOVBWDUpw+EfxjUrG # H7ud+AMLNhAxbx7uEfNyNYsi5CnaaXgA2vO00zzDNvaugU/yTYxEm2j0wybQTDfb # BMyUPQB6VhcFgCAOC4CQalIa1F5xOFEYV2P5/OY3Fza/pCGQKiBEDyRG6PfCbmAZ # qvg7cTGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # AhMzAAABpAfP44+jum/WAAEAAAGkMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG # 9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIADlNEKhG3BnRkhq # cXD/tb/UR5BXxdcKHp5Lt8yia4ZxMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCB # vQQgBfzgoyEmcKTASfDCd1sDAhd6jmuWBxRuieLh42rqefgwgZgwgYCkfjB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAaQHz+OPo7pv1gABAAABpDAi # BCBoSzA9nCaw+Pk/XGE5FyHMvYr0QOQne8MeefMn3+/YZjANBgkqhkiG9w0BAQsF # AASCAgAVCHZ4zg4+oaqb2mTbyBn1iELiCCLutlG+ij8/By/6KzRjhS79sCiJwxuR # 0X6tVul89m21uZwJPqhV7v4FkoAC9phxUr5T7YxGrPXzHpJitYLWCG9GewR2PfPr # y7JhUCPAN4hNHR4Z8GLJFTXv1eQyKkeDlSxivC29GWn1X7eTfAKCwtDyjkWeoe4c # hrHzgdREjT7p7z0UKXn5zT4CYsdCYYePTcrtcPQ8YKRZfQhVhAcHPzo1f2r5bRdr # EkAmFHpx0hhKEPRke/dszJ14ykLOqEgRMAqyzgE9WUGc87TGdhGrsB7kZHPgnDLn # MyP5FReopTZDLlmEg++XN1/FCfVr1aUVXwlr8U5BQLMhhz/L14pX7Rpho15O13WR # FpuqsPpy+Age70j5efz9BiSnT4jyUzkFdKSbUf0512UrfhVsqw7POXkuvd+VSqH0 # i3VpF6E7MlPUw/xWBE8xyfGISmMM4awoQBjOs4ir60ktDowkvckkmoSGgf9YLFXt # jwnh65g7XvREpcLsJpOx6PvMKxWQ0ePPy1kl1uKWWpSe7kNwdMywl60FBFC1hWVi # ktBE98W2j6B4MwGSL2F7hWz7LNs1ROI8YAPaj6+2gK7oYEQvd0wVvsNyzDQ6oIiJ # VU2Rt5sblCaZvMF4XMba1eIRpNOrm45iPMmRsypgZdBSy4wtOw== # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/DscResources/MSFT_PSModule/MSFT_PSModule.psm1 ================================================ # # Copyright (c) Microsoft Corporation. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # $resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent # Import localization helper functions. $helperName = 'PowerShellGet.LocalizationHelper' $dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" Import-Module -Name $dscResourcesFolderFilePath $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_PSModule' -ScriptRoot $PSScriptRoot # Import resource helper functions. $helperName = 'PowerShellGet.ResourceHelper' $dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" Import-Module -Name $dscResourcesFolderFilePath <# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell Gallery and install it on your computer. Get-TargetResource returns the current state of the resource. .PARAMETER Name Specifies the name of the PowerShell module to be installed or uninstalled. .PARAMETER Repository Specifies the name of the module source repository where the module can be found. .PARAMETER RequiredVersion Provides the version of the module you want to install or uninstall. .PARAMETER MaximumVersion Provides the maximum version of the module you want to install or uninstall. .PARAMETER MinimumVersion Provides the minimum version of the module you want to install or uninstall. .PARAMETER Force Forces the installation of modules. If a module of the same name and version already exists on the computer, this parameter overwrites the existing module with one of the same name that was found by the command. .PARAMETER AllowClobber Allows the installation of modules regardless of if other existing module on the computer have cmdlets of the same name. .PARAMETER SkipPublisherCheck Allows the installation of modules that have not been catalog signed. #> function Get-TargetResource { <# These suppressions are added because this repository have other Visual Studio Code workspace settings than those in DscResource.Tests DSC test framework. Only those suppression that contradict this repository guideline is added here. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-ForEachStatement', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $Repository = 'PSGallery', [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MaximumVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.Boolean] $Force, [Parameter()] [System.Boolean] $AllowClobber, [Parameter()] [System.Boolean] $SkipPublisherCheck ) $returnValue = @{ Ensure = 'Absent' Name = $Name Repository = $Repository Description = $null Guid = $null ModuleBase = $null ModuleType = $null Author = $null InstalledVersion = $null RequiredVersion = $RequiredVersion MinimumVersion = $MinimumVersion MaximumVersion = $MaximumVersion Force = $Force AllowClobber = $AllowClobber SkipPublisherCheck = $SkipPublisherCheck InstallationPolicy = $null } Write-Verbose -Message ($localizedData.GetTargetResourceMessage -f $Name) $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ('Name', 'Repository', 'MinimumVersion', 'MaximumVersion', 'RequiredVersion') # Get the module with the right version and repository properties. $modules = Get-RightModule @extractedArguments -ErrorAction SilentlyContinue -WarningAction SilentlyContinue # If the module is found, the count > 0 if ($modules.Count -gt 0) { Write-Verbose -Message ($localizedData.ModuleFound -f $Name) # Find a module with the latest version and return its properties. $latestModule = $modules[0] foreach ($module in $modules) { if ($module.Version -gt $latestModule.Version) { $latestModule = $module } } # Check if the repository matches. $repositoryName = Get-ModuleRepositoryName -Module $latestModule -ErrorAction SilentlyContinue -WarningAction SilentlyContinue if ($repositoryName) { $installationPolicy = Get-InstallationPolicy -RepositoryName $repositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } if ($installationPolicy) { $installationPolicyReturnValue = 'Trusted' } else { $installationPolicyReturnValue = 'Untrusted' } $returnValue.Ensure = 'Present' $returnValue.Repository = $repositoryName $returnValue.Description = $latestModule.Description $returnValue.Guid = $latestModule.Guid $returnValue.ModuleBase = $latestModule.ModuleBase $returnValue.ModuleType = $latestModule.ModuleType $returnValue.Author = $latestModule.Author $returnValue.InstalledVersion = $latestModule.Version $returnValue.InstallationPolicy = $installationPolicyReturnValue } else { Write-Verbose -Message ($localizedData.ModuleNotFound -f $Name) } return $returnValue } <# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell Gallery and install it on your computer. Test-TargetResource validates whether the resource is currently in the desired state. .PARAMETER Ensure Determines whether the module to be installed or uninstalled. .PARAMETER Name Specifies the name of the PowerShell module to be installed or uninstalled. .PARAMETER Repository Specifies the name of the module source repository where the module can be found. .PARAMETER InstallationPolicy Determines whether you trust the source repository where the module resides. .PARAMETER RequiredVersion Provides the version of the module you want to install or uninstall. .PARAMETER MaximumVersion Provides the maximum version of the module you want to install or uninstall. .PARAMETER MinimumVersion Provides the minimum version of the module you want to install or uninstall. .PARAMETER Force Forces the installation of modules. If a module of the same name and version already exists on the computer, this parameter overwrites the existing module with one of the same name that was found by the command. .PARAMETER AllowClobber Allows the installation of modules regardless of if other existing module on the computer have cmdlets of the same name. .PARAMETER SkipPublisherCheck Allows the installation of modules that have not been catalog signed. #> function Test-TargetResource { <# These suppressions are added because this repository have other Visual Studio Code workspace settings than those in DscResource.Tests DSC test framework. Only those suppression that contradict this repository guideline is added here. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $Repository = 'PSGallery', [Parameter()] [ValidateSet('Trusted', 'Untrusted')] [System.String] $InstallationPolicy = 'Untrusted', [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MaximumVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.Boolean] $Force, [Parameter()] [System.Boolean] $AllowClobber, [Parameter()] [System.Boolean] $SkipPublisherCheck ) Write-Verbose -Message ($localizedData.TestTargetResourceMessage -f $Name) $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ('Name', 'Repository', 'MinimumVersion', 'MaximumVersion', 'RequiredVersion') $status = Get-TargetResource @extractedArguments # The ensure returned from Get-TargetResource is not equal to the desired $Ensure. if ($status.Ensure -ieq $Ensure) { Write-Verbose -Message ($localizedData.InDesiredState -f $Name) return $true } else { Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name) return $false } } <# .SYNOPSIS This DSC resource provides a mechanism to download PowerShell modules from the PowerShell Gallery and install it on your computer. Set-TargetResource sets the resource to the desired state. "Make it so". .PARAMETER Ensure Determines whether the module to be installed or uninstalled. .PARAMETER Name Specifies the name of the PowerShell module to be installed or uninstalled. .PARAMETER Repository Specifies the name of the module source repository where the module can be found. .PARAMETER InstallationPolicy Determines whether you trust the source repository where the module resides. .PARAMETER RequiredVersion Provides the version of the module you want to install or uninstall. .PARAMETER MaximumVersion Provides the maximum version of the module you want to install or uninstall. .PARAMETER MinimumVersion Provides the minimum version of the module you want to install or uninstall. .PARAMETER Force Forces the installation of modules. If a module of the same name and version already exists on the computer, this parameter overwrites the existing module with one of the same name that was found by the command. .PARAMETER AllowClobber Allows the installation of modules regardless of if other existing module on the computer have cmdlets of the same name. .PARAMETER SkipPublisherCheck Allows the installation of modules that have not been catalog signed. #> function Set-TargetResource { <# These suppressions are added because this repository have other Visual Studio Code workspace settings than those in DscResource.Tests DSC test framework. Only those suppression that contradict this repository guideline is added here. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-ForEachStatement', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-TryStatement', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-CatchClause', '')] [CmdletBinding()] param ( [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $Repository = 'PSGallery', [Parameter()] [ValidateSet('Trusted', 'Untrusted')] [System.String] $InstallationPolicy = 'Untrusted', [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MaximumVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.Boolean] $Force, [Parameter()] [System.Boolean] $AllowClobber, [Parameter()] [System.Boolean] $SkipPublisherCheck ) # Validate the repository argument if ($PSBoundParameters.ContainsKey('Repository')) { Test-ParameterValue -Value $Repository -Type 'PackageSource' -ProviderName 'PowerShellGet' -Verbose } if ($Ensure -ieq 'Present') { # Version check $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ('MinimumVersion', 'MaximumVersion', 'RequiredVersion') $null = Test-VersionParameter @extractedArguments try { $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ('Name', 'Repository', 'MinimumVersion', 'MaximumVersion', 'RequiredVersion') Write-Verbose -Message ($localizedData.StartFindModule -f $Name) $modules = Find-Module @extractedArguments -ErrorVariable ev } catch { $errorMessage = $script:localizedData.ModuleNotFoundInRepository -f $Name New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } $trusted = $null $moduleFound = $null foreach ($m in $modules) { # Check for the installation policy. $trusted = Get-InstallationPolicy -RepositoryName $m.Repository -ErrorAction SilentlyContinue -WarningAction SilentlyContinue # Stop the loop if found a trusted repository. if ($trusted) { $moduleFound = $m break; } } try { # The repository is trusted, so we install it. if ($trusted) { Write-Verbose -Message ($localizedData.StartInstallModule -f $Name, $moduleFound.Version.toString(), $moduleFound.Repository) # Extract the installation options. $extractedSwitches = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters -ArgumentNames ('Force', 'AllowClobber', 'SkipPublisherCheck') $moduleFound | Install-Module @extractedSwitches 2>&1 | out-string | Write-Verbose } # The repository is untrusted but user's installation policy is trusted, so we install it with a warning. elseif ($InstallationPolicy -ieq 'Trusted') { Write-Warning -Message ($localizedData.InstallationPolicyWarning -f $Name, $modules[0].Repository, $InstallationPolicy) # Extract installation options (Force implied by InstallationPolicy). $extractedSwitches = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters -ArgumentNames ('AllowClobber', 'SkipPublisherCheck') # If all the repositories are untrusted, we choose the first one. $modules[0] | Install-Module @extractedSwitches -Force 2>&1 | out-string | Write-Verbose } # Both user and repository is untrusted else { $errorMessage = $script:localizedData.InstallationPolicyFailed -f $InstallationPolicy, 'Untrusted' New-InvalidOperationException -Message $errorMessage } Write-Verbose -Message ($localizedData.InstalledSuccess -f $Name) } catch { $errorMessage = $script:localizedData.FailToInstall -f $Name New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } } # Ensure=Absent else { $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ('Name', 'Repository', 'MinimumVersion', 'MaximumVersion', 'RequiredVersion') # Get the module with the right version and repository properties. $modules = Get-RightModule @extractedArguments if (-not $modules) { $errorMessage = $script:localizedData.ModuleWithRightPropertyNotFound -f $Name New-InvalidOperationException -Message $errorMessage } foreach ($module in $modules) { # Get the path where the module is installed. $path = $module.ModuleBase Write-Verbose -Message ($localizedData.StartUnInstallModule -f $Name) try { <# There is no Uninstall-Module cmdlet for Windows PowerShell 4.0, so we will remove the ModuleBase folder as an uninstall operation. #> Microsoft.PowerShell.Management\Remove-Item -Path $path -Force -Recurse Write-Verbose -Message ($localizedData.UnInstalledSuccess -f $module.Name) } catch { $errorMessage = $script:localizedData.FailToUninstall -f $module.Name New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ } } # foreach } # Ensure=Absent } <# .SYNOPSIS This is a helper function. It returns the modules that meet the specified versions and the repository requirements. .PARAMETER Name Specifies the name of the PowerShell module. .PARAMETER RequiredVersion Provides the version of the module you want to install or uninstall. .PARAMETER MaximumVersion Provides the maximum version of the module you want to install or uninstall. .PARAMETER MinimumVersion Provides the minimum version of the module you want to install or uninstall. .PARAMETER Repository Specifies the name of the module source repository where the module can be found. #> function Get-RightModule { <# These suppressions are added because this repository have other Visual Studio Code workspace settings than those in DscResource.Tests DSC test framework. Only those suppression that contradict this repository guideline is added here. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-ForEachStatement', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Name, [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.String] $MaximumVersion, [Parameter()] [System.String] $Repository ) Write-Verbose -Message ($localizedData.StartGetModule -f $($Name)) $modules = Microsoft.PowerShell.Core\Get-Module -Name $Name -ListAvailable -ErrorAction SilentlyContinue -WarningAction SilentlyContinue if (-not $modules) { return $null } <# As Get-Module does not take RequiredVersion, MinimumVersion, MaximumVersion, or Repository, below we need to check whether the modules are containing the right version and repository location. #> $extractedArguments = New-SplatParameterHashTable -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames ('MaximumVersion', 'MinimumVersion', 'RequiredVersion') $returnVal = @() foreach ($m in $modules) { $versionMatch = $false $installedVersion = $m.Version # Case 1 - a user provides none of RequiredVersion, MinimumVersion, MaximumVersion if ($extractedArguments.Count -eq 0) { $versionMatch = $true } # Case 2 - a user provides RequiredVersion elseif ($extractedArguments.ContainsKey('RequiredVersion')) { # Check if it matches with the installed version $versionMatch = ($installedVersion -eq [System.Version] $RequiredVersion) } else { # Case 3 - a user provides MinimumVersion if ($extractedArguments.ContainsKey('MinimumVersion')) { $versionMatch = ($installedVersion -ge [System.Version] $extractedArguments['MinimumVersion']) } # Case 4 - a user provides MaximumVersion if ($extractedArguments.ContainsKey('MaximumVersion')) { $isLessThanMax = ($installedVersion -le [System.Version] $extractedArguments['MaximumVersion']) if ($extractedArguments.ContainsKey('MinimumVersion')) { $versionMatch = $versionMatch -and $isLessThanMax } else { $versionMatch = $isLessThanMax } } # Case 5 - Both MinimumVersion and MaximumVersion are provided. It's covered by the above. # Do not return $false yet to allow the foreach to continue if (-not $versionMatch) { Write-Verbose -Message ($localizedData.VersionMismatch -f $Name, $installedVersion) $versionMatch = $false } } # Case 6 - Version matches but need to check if the module is from the right repository. if ($versionMatch) { # A user does not provide Repository, we are good if (-not $PSBoundParameters.ContainsKey('Repository')) { Write-Verbose -Message ($localizedData.ModuleFound -f "$Name $installedVersion") $returnVal += $m } else { # Check if the Repository matches $sourceName = Get-ModuleRepositoryName -Module $m if ($Repository -ieq $sourceName) { Write-Verbose -Message ($localizedData.ModuleFound -f "$Name $installedVersion") $returnVal += $m } else { Write-Verbose -Message ($localizedData.RepositoryMismatch -f $($Name), $($sourceName)) } } } } # foreach return $returnVal } <# .SYNOPSIS This is a helper function that returns the module's repository name. .PARAMETER Module Specifies the name of the PowerShell module. #> function Get-ModuleRepositoryName { <# These suppressions are added because this repository have other Visual Studio Code workspace settings than those in DscResource.Tests DSC test framework. Only those suppression that contradict this repository guideline is added here. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Object] $Module ) <# RepositorySourceLocation property is supported in PS V5 only. To work with the earlier PowerShell version, we need to do a different way. PSGetModuleInfo.xml exists for any PowerShell modules downloaded through PSModule provider. #> $psGetModuleInfoFileName = 'PSGetModuleInfo.xml' $psGetModuleInfoPath = Microsoft.PowerShell.Management\Join-Path -Path $Module.ModuleBase -ChildPath $psGetModuleInfoFileName Write-Verbose -Message ($localizedData.FoundModulePath -f $psGetModuleInfoPath) if (Microsoft.PowerShell.Management\Test-path -Path $psGetModuleInfoPath) { $psGetModuleInfo = Microsoft.PowerShell.Utility\Import-Clixml -Path $psGetModuleInfoPath return $psGetModuleInfo.Repository } } # SIG # Begin signature block # MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCig4H1kVMWxA7U # uX1JaRBJmDsHSZHmuBUTkDBwWHafCqCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg2t+ikpO2 # 0WdDEf/JRKsEEOWFz2O6CEnKmWwPbrvPFuIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQBaj/JIwAbud/IMJN3SHVwenJwEjzrmM3cPznYlvkyE # rnB6RiDYnohm+72wkCmVWobuualV9Xw8OGA9w7MvJjZuEnFP9TwDjlWinR4FpdVl # YX49u4g50iMR2isaKTxMA2nA2LbyFpTLMqi6ynPLnv251xTdumj/XRLZjHdpr4Pt # p6HQz45/jMWRtlh8x3piJPrgpINuJtmO91tnMQnykF++QgteDqKSboXOu2Wg0oCn # Ao0eOifIujyUp2ez9Cqx2hzkMOqNa5jZD4JetiDTWBTkN8XHRbsfCVwyEQzxrpXn # wAFT7jMzuI8o8xdCuCvuDokIzcnm+rKZJIy6Rwp6Cn11oYIS8TCCEu0GCisGAQQB # gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIL/3GmTbcOXlRJe8Pu+1/QVBo0Mk83o/PGK7iApB # aAutAgZfYQoJ7SUYEzIwMjAwOTIyMjIxOTUxLjg4NlowBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjo4OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABLCKvRZd1+RvuAAAA # AAEsMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTE5MTIxOTAxMTUwM1oXDTIxMDMxNzAxMTUwM1owgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4OTdB # LUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPK1zgSSq+MxAYo3qpCt # QDxSMPPJy6mm/wfEJNjNUnYtLFBwl1BUS5trEk/t41ldxITKehs+ABxYqo4Qxsg3 # Gy1ugKiwHAnYiiekfC+ZhptNFgtnDZIn45zC0AlVr/6UfLtsLcHCh1XElLUHfEC0 # nBuQcM/SpYo9e3l1qY5NdMgDGxCsmCKdiZfYXIu+U0UYIBhdzmSHnB3fxZOBVcr5 # htFHEBBNt/rFJlm/A4yb8oBsp+Uf0p5QwmO/bCcdqB15JpylOhZmWs0sUfJKlK9E # rAhBwGki2eIRFKsQBdkXS9PWpF1w2gIJRvSkDEaCf+lbGTPdSzHSbfREWOF9wY3i # Yj8CAwEAAaOCARswggEXMB0GA1UdDgQWBBRRahZSGfrCQhCyIyGH9DkiaW7L0zAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBPFxHIwi4vAH49w9Svmz6K3tM55RlW # 5pPeULXdut2Rqy6Ys0+VpZsbuaEoxs6Z1C3hMbkiqZFxxyltxJpuHTyGTg61zfNI # F5n6RsYF3s7IElDXNfZznF1/2iWc6uRPZK8rxxUJ/7emYXZCYwuUY0XjsCpP9pbR # RKeJi6r5arSyI+NfKxvgoM21JNt1BcdlXuAecdd/k8UjxCscffanoK2n6LFw1PcZ # lEO7NId7o+soM2C0QY5BYdghpn7uqopB6ixyFIIkDXFub+1E7GmAEwfU6VwEHL7y # 9rNE8bd+JrQs+yAtkkHy9FmXg/PsGq1daVzX1So7CJ6nyphpuHSN3VfTMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4 # OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUADE5OKSMoNx/mYxYWap1RTOohbJ2ggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOMUwsMwIhgPMjAyMDA5MjIyMjM2NTFaGA8yMDIwMDkyMzIyMzY1MVowdzA9Bgor # BgEEAYRZCgQBMS8wLTAKAgUA4xTCwwIBADAKAgEAAgIlfwIB/zAHAgEAAgIRzTAK # AgUA4xYUQwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAEmZCk5PsMA6oG40 # YulPUpxDtQA+JAuXyNrLI4wJcdmv34Bzmj9gk3gv6ZxQZxlvZjA1O1P3lcKGIYco # 9UkRdAeP6Zzfim6U3UoiUz1AHKuSm6iLux7Z/hNtgEs2ZpYXxOK5JZ/9Pa2I5mg0 # VxsGdXBxl6TOZqbB6aGNG1ZKytRWMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAEsIq9Fl3X5G+4AAAAAASwwDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQgioHn9SQtTXybUUTzgPmt61UPBGskDV9AErEC62Y8+H4wgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCBbn/0uFFh42hTM5XOoKdXevBaiSxmYK9Ilcn9n # u5ZH4TCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # LCKvRZd1+RvuAAAAAAEsMCIEIJmkMFyuUW5yiXAemMCWHyk8T1EMMlB1cEiVD4/E # +vhqMA0GCSqGSIb3DQEBCwUABIIBAOyFm0zpO+VogyMfTuCFeJjMyyDnAJRy/KhW # jtq11pPg3e11HU0LuNa+ZpCrJXbSWlF+tLkU9eQXPpGPzO7ITGhxhnq5wyhfzA8a # 9TRGlDMUNHp+6WhH6BZ86zUqMpcYx5zN0H9TaJTpC8JJ7HrNTW8zp032ocr6IXVr # M6mOa0MUZsE366+OXRpCCm0fokT50uvXWbcRdngS9ZT5xmz134T1iS4Fds7ZOC5W # m0VofKEuaTYbTc5aF0WN/OGMvowHRxlJhG7zhMX8ZuXGvbgzANq/o7tZu0HmGFTN # rPs5smC3VEWVpNWgOTfrO3bfaWkAdsuBeLVXZUog+M4wmTDFSPs= # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mfl ================================================ #pragma namespace("\\\\.\\root\\default") instance of __namespace{ name="MS_409";}; #pragma namespace("\\\\.\\root\\default\\MS_409") [AMENDMENT, LOCALE("MS_409")] class MSFT_PSModule : OMI_BaseResource { [Key,Description("Name of the module\n") : Amended] String Name; [Description("Whether the module is to be installed or uninstalled.\nPresent {default} \nAbsent \n") : Amended] String Ensure; [Description("The name of the module source where the module can be found.\n") : Amended] String Repository; [Description("Whether the package is trusted or untrusted.\nTrusted {default} \nUntrusted \n") : Amended] String InstallationPolicy; [Description("The required version of the module.\n") : Amended] String RequiredVersion; [Description("The minimum version of the module.\n") : Amended] String MinimumVersion; [Description("The maximum version of the module.\n") : Amended] String MaximumVersion; [Description("Forces the installation of the module.\n" : Amended] Boolean Force; [Description("Allows installation when existing cmdlets of the same name exist.\n" : Amended] Boolean AllowClobber; [Description("Allows installation when module is not signed.\n" : Amended] Boolean SkipPublisherCheck; [Description("The brief description of the module.\n") : Amended] string Description; [Description("The version of the module that is installed.\n") : Amended] String InstalledVersion; [Description("The identifier of the module.\n") : Amended] String Guid; [Description("The base location where the module is installed.\n") : Amended] String ModuleBase; [Description("The type of the module.\n") : Amended] String ModuleType; [Description("The author of the module.\n") : Amended] String Author; }; ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/DscResources/MSFT_PSModule/MSFT_PSModule.schema.mof ================================================ [ClassVersion("1.0.0.0"),FriendlyName("PSModule")] class MSFT_PSModule : OMI_BaseResource { [Key] String Name; [Write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] String Ensure; [Write] String Repository; [Write,ValueMap{"Trusted", "Untrusted"},Values{"Trusted", "Untrusted"}] String InstallationPolicy; [Write] String RequiredVersion; [Write] String MaximumVersion; [Write] String MinimumVersion; [Write] Boolean Force; [Write] Boolean AllowClobber; [Write] Boolean SkipPublisherCheck; [Read] string Description; [Read] String InstalledVersion; [Read] String Guid; [Read] String ModuleBase; [Read] String ModuleType; [Read] String Author; }; /* SIG # Begin signature block */ /* MIIjjwYJKoZIhvcNAQcCoIIjgDCCI3wCAQExDzANBglghkgBZQMEAgEFADB5Bgor */ /* BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG */ /* KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCaI9+NOjQE85sM */ /* /KcWYxUhOUYZXXI8vIpWzKiXV2ZNwqCCDYEwggX/MIID56ADAgECAhMzAAABh3IX */ /* chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD */ /* VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy */ /* b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p */ /* bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw */ /* CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u */ /* ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy */ /* b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB */ /* AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB */ /* znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH */ /* sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d */ /* weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ */ /* itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV */ /* Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE */ /* AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw */ /* UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 */ /* ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu */ /* ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu */ /* bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w */ /* Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 */ /* Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx */ /* MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy */ /* S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K */ /* NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV */ /* BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr */ /* qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx */ /* zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe */ /* yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g */ /* yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf */ /* AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI */ /* 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 */ /* GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea */ /* jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS */ /* AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK */ /* V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 */ /* IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 */ /* ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla */ /* MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS */ /* ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT */ /* H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB */ /* AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG */ /* OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S */ /* 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz */ /* y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 */ /* 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u */ /* M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 */ /* X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl */ /* XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP */ /* 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB */ /* l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF */ /* RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM */ /* CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ */ /* BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud */ /* DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO */ /* 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 */ /* LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y */ /* Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p */ /* Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y */ /* Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB */ /* FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw */ /* cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA */ /* XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY */ /* 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj */ /* 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd */ /* d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ */ /* Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf */ /* wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ */ /* aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j */ /* NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B */ /* xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 */ /* eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 */ /* r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I */ /* RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZDCCFWACAQEwgZUwfjELMAkG */ /* A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx */ /* HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z */ /* b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN */ /* BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor */ /* BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgbSsvKN6e */ /* SGOiJuBXioZwDwxnu8TBuBkBb03bbCVXW+owQgYKKwYBBAGCNwIBDDE0MDKgFIAS */ /* AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN */ /* BgkqhkiG9w0BAQEFAASCAQBSFT3/XsOM9n4ZVz3k681dMHXA/TGCr6vmQTOG0Cp6 */ /* Djy4e61zDfO7lsVtGoDqeKYpSUFXc8FZfmegYDcOXrylItYtw3g9iva1sT0zTvCn */ /* OOZZKJMqa9ZLfxiS9kTOXR0WWbypJmpfgRgqVPdrg5Heh8vfo3NmWhWXwMqX6iUL */ /* u5HZcE20KAqgiyOqFasvdF+cMut2RzS0UypvroI9LVboBhmQLdTT4ELi65MtvwdH */ /* gpJ25Mi3uHvhQ9R2TH/Oh8qCsPJqwiEy+CnvArN4TKJ+q1T23NqMEo5R8zLBhUsa */ /* FcMjYglszXUbCSUSj07/CAOF5adQ1AZoPNdOOuMeLnJOoYIS7jCCEuoGCisGAQQB */ /* gjcDAwExghLaMIIS1gYJKoZIhvcNAQcCoIISxzCCEsMCAQMxDzANBglghkgBZQME */ /* AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB */ /* MDEwDQYJYIZIAWUDBAIBBQAEILnfg6zi07buNel3T6pFx+y5i6dvEoPLGn0OX8/q */ /* AfPcAgZfYQkjShsYEzIwMjAwOTIyMjIxOTUxLjA4NlowBIACAfSggdSkgdEwgc4x */ /* CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt */ /* b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p */ /* Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg */ /* VFNTIEVTTjpGNzdGLUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt */ /* U3RhbXAgU2VydmljZaCCDkEwggT1MIID3aADAgECAhMzAAABKugXlviGp++jAAAA */ /* AAEqMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo */ /* aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y */ /* cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw */ /* MB4XDTE5MTIxOTAxMTUwMloXDTIxMDMxNzAxMTUwMlowgc4xCzAJBgNVBAYTAlVT */ /* MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK */ /* ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy */ /* YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpGNzdG */ /* LUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj */ /* ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ/flYGkhdJtxSsHBu9l */ /* mXF/UXxPF7L45nEhmtd01KDosWbY8y54BN7+k9DMvzqToP39v8/Z+NtEzKj8Bf5E */ /* QoG1/pJfpzCJe80HZqyqMo0oQ9EugVY6YNVNa2T1u51d96q1hFmu1dgxt8uD2g7I */ /* pBQdhS2tpc3j3HEzKvV/vwEr7/BcTuwqUHqrrBgHc971epVR4o5bNKsjikawmMw9 */ /* D/tyrTciy3F9Gq9pEgk8EqJfOdAabkanuAWTjlmBhZtRiO9W1qFpwnu9G5qVvdNK */ /* RKxQdtxMC04pWGfnxzDac7+jIql532IEC5QSnvY84szEpxw31QW/LafSiDmAtYWH */ /* pm8CAwEAAaOCARswggEXMB0GA1UdDgQWBBRw9MUtdCs/rhN2y9EkE6ZI9O8TaTAf */ /* BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH */ /* hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU */ /* aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF */ /* BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 */ /* YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG */ /* AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQCKwDT0CnHVo46OWyUbrPIj8QIcf+PT */ /* jBVYpKg1K2D15Z6xEuvmf+is6N8gj9f1nkFIALvh+iGkx8GgGa/oA9IhXNEFYPNF */ /* aHwHan/UEw1P6Tjdaqy3cvLC8f8zE1CR1LhXNofq6xfoT9HLGFSg9skPLM1TQ+RA */ /* QX9MigEm8FFlhhsQ1iGB1399x8d92h9KspqGDnO96Z9Aj7ObDtdU6RoZrsZkiRQN */ /* nXmnX1I+RuwtLu8MN8XhJLSl5wqqHM3rqaaMvSAISVtKySpzJC5Zh+5kJlqFdSiI */ /* HW8Q+8R6EWG8ILb9Pf+w/PydyK3ZTkVXUpFA+JhWjcyzphVGw9ffj0YKMIIGcTCC */ /* BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC */ /* VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV */ /* BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv */ /* b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN */ /* MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv */ /* bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 */ /* aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw */ /* DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 */ /* VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw */ /* RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe */ /* dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx */ /* Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G */ /* kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA */ /* AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 */ /* fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC */ /* AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX */ /* zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v */ /* cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI */ /* KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j */ /* b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g */ /* AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 */ /* d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB */ /* BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA */ /* bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh */ /* IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS */ /* +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK */ /* kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon */ /* /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi */ /* PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ */ /* fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII */ /* YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 */ /* cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a */ /* KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ */ /* cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ */ /* NR4Iuto229Nfj950iEkSoYICzzCCAjgCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT */ /* AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD */ /* VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP */ /* cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpG */ /* NzdGLUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy */ /* dmljZaIjCgEBMAcGBSsOAwIaAxUA6rLmrKHyIMP76ePl321xKUJ3YX+ggYMwgYCk */ /* fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH */ /* UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD */ /* Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF */ /* AOMUwdcwIhgPMjAyMDA5MjIyMjMyNTVaGA8yMDIwMDkyMzIyMzI1NVowdDA6Bgor */ /* BgEEAYRZCgQBMSwwKjAKAgUA4xTB1wIBADAHAgEAAgIOXDAHAgEAAgIRhjAKAgUA */ /* 4xYTVwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID */ /* B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAFJC6fwWgNxSFO+SkoKy */ /* mrM+JaRXiSnNavdEe/YjzI5HVrpNB8zCRPz/pvX9TbuSnQ2/WN4ZPI2AoBMGQpWk */ /* aG1jl9uC0QwsFTL8Bz97Tpk7GmS9yzjX0+srsaxIVCGg9geXr7A4IoUp0WVxR8JR */ /* MNinkAYlZ0lpKG2lFOA0GBPWMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMx */ /* EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT */ /* FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt */ /* U3RhbXAgUENBIDIwMTACEzMAAAEq6BeW+Ian76MAAAAAASowDQYJYIZIAWUDBAIB */ /* BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx */ /* IgQgFyTxUrHDtuJBsfu1xDMA0kEsv2/5h+RllgO7I/JdUFowgfoGCyqGSIb3DQEJ */ /* EAIvMYHqMIHnMIHkMIG9BCBDmDWEWvc6fhs5t4Woo5Q+FMFCcaIgV4yUP4CpuBmL */ /* mTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw */ /* DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x */ /* JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABKugX */ /* lviGp++jAAAAAAEqMCIEIPYicL/1lyN2zZsc2WZ8NPhhliGEGAUOVDFr1il2rjsl */ /* MA0GCSqGSIb3DQEBCwUABIIBAAXT1QnIHmDbDzSQJOywe0TeYzVPNHWyEx/vc0nu */ /* EaUOByznS0km1nwRtIdc9btdG7R7Y5O4sHdlVemnnCRChyie8tQQXYpBsDYZ3x9r */ /* cZEdyjgqJg5LUf4F9BBLLA1kTgP63a/tDOkF1KqImsHXog+nTECkzC1RxCauEFvn */ /* pMuCOa8D1dBptSXhUxaDRBYtSXlT76E+7RtoTLLojBl+a/h+MIpSvzg9Tj8rWD5O */ /* lBq8D1cFSDYk8wGti6d+LbDIokFmh8UB25pmQ6dFqAL3seIOZ0bA3bXOqDo7qZ0x */ /* Mday0m5Qc9ghm0X1Pgls3bGM9fu08AmUKJXNpcIMg75MNgA= */ /* SIG # End signature block */ ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/DscResources/MSFT_PSModule/en-US/MSFT_PSModule.strings.psd1 ================================================ # # Copyright (c) Microsoft Corporation. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # culture = "en-US" ConvertFrom-StringData -StringData @' FailToUninstall = Failed to uninstall the module '{0}'. FailToInstall = Failed to install the module '{0}'. InDesiredState = Resource '{0}' is in the desired state. NotInDesiredState = Resource '{0}' is not in the desired state. ModuleFound = Module '{0}' is found on the node. ModuleNotFound = Module '{0}' is not found on the node. ModuleWithRightPropertyNotFound = Module '{0}' with the right version or other properties not found in the node. ModuleNotFoundInRepository = Module '{0}' with the right version or other properties not found in the repository. StartGetModule = Begin invoking Get-Module '{0}'. StartFindModule = Begin invoking Find-Module '{0}'. StartInstallModule = Begin invoking Install-Module '{0}' version '{1}' from '{2}' repository. StartUnInstallModule = Begin invoking Remove-Item to remove the module '{0}' from the file system. InstalledSuccess = Successfully installed the module '{0}' UnInstalledSuccess = Successfully uninstalled the module '{0}' VersionMismatch = The installed module '{0}' has the version: '{1}' RepositoryMismatch = The installed module '{0}' is from the '{1}' repository. FoundModulePath = Found the module path: '{0}'. InstallationPolicyWarning = The module '{0}' was installed from the untrusted repository' {1}'. The InstallationPolicy is set to '{2}' to override the repository installation policy. If you trust the repository, set the repository installation policy to 'Trusted', that will also remove this warning. InstallationPolicyFailed = The current installation policy do not allow installation from this repository. Your current installation policy is '{0}' and the repository installation policy is '{1}'. If you trust the repository, either change the repository installation policy, or set the parameter InstallationPolicy to 'Trusted' to override the repository installation policy. GetTargetResourceMessage = Getting the current state of the module '{0}'. TestTargetResourceMessage = Determining if the module '{0}' is in the desired state. '@ # SIG # Begin signature block # MIIjjwYJKoZIhvcNAQcCoIIjgDCCI3wCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD3aU/LXFNAmh3o # ImcuJKT1RWLilph3AMcVu5/5cons4KCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZDCCFWACAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQggnuHAoEc # 77NUAlz1iYFplyTNHrObqDxEEH6I0sx83rwwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQDHiQmHNyxnMkjG/Ta0iocARMREpmraxXw0YVRwVDWu # 7Fwk6AHjvaq4lG4wBThP2nMw1pepa8wp9vvq+p0v6/HneS0RTMpnKOnioidAvbtx # zYcqGW4OBzW7TP2pPJc1pLP5CcpndQKxuWPuop//9GT3qH/yw5MqKfV+NEE/13Dd # zGEL7MVwjq8YWr7hoc77YdIw7A/xmY7gCTn15q0pWMmoBMOllKdBWTRB8QY186SL # B4SCrN4XLUQlxTx81UkQG4mYCkCEvyRk4ljM4mcGlL391r0pl+MoFgp3EnePeieZ # ihN3IHuyg1SkqmUKcFv7b4C5XxfSZxyNE49bx2rfSADAoYIS7jCCEuoGCisGAQQB # gjcDAwExghLaMIIS1gYJKoZIhvcNAQcCoIISxzCCEsMCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIB/oVtJoO9r6dLRNmcUzxfDW4EtVcph4CRbFLdpd # e6cDAgZfYPebj+4YEzIwMjAwOTIyMjIxOTUyLjMzMVowBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjo2MEJDLUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkEwggT1MIID3aADAgECAhMzAAABJt+6SyK5goIHAAAA # AAEmMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTE5MTIxOTAxMTQ1OVoXDTIxMDMxNzAxMTQ1OVowgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo2MEJD # LUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ4wvoacTvMNlXQTtfF/ # Cx5Ol3X0fcjUNMvjLgTmO5+WHYJFbp725P3+qvFKDRQHWEI1Sz0gB24urVDIjXjB # h5NVNJVMQJI2tltv7M4/4IbhZJb3xzQW7LolEoZYUZanBTUuyly9osCg4o5joViT # 2GtmyxK+Fv5kC20l2opeaeptd/E7ceDAFRM87hiNCsK/KHyC+8+swnlg4gTOey6z # QqhzgNsG6HrjLBuDtDs9izAMwS2yWT0T52QA9h3Q+B1C9ps2fMKMe+DHpG+0c61D # 94Yh6cV2XHib4SBCnwIFZAeZE2UJ4qPANSYozI8PH+E5rCT3SVqYvHou97HsXvP2 # I3MCAwEAAaOCARswggEXMB0GA1UdDgQWBBRJq6wfF7B+mEKN0VimX8ajNA5hQTAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBAlvudaOlv9Cfzv56bnX41czF6tLtH # LB46l6XUch+qNN45ZmOTFwLot3JjwSrn4oycQ9qTET1TFDYd1QND0LiXmKz9OqBX # ai6S8XdyCQEZvfL82jIAs9pwsAQ6XvV9jNybPStRgF/sOAM/Deyfmej9Tg9FcRwX # ank2qgzdZZNb8GoEze7f1orcTF0Q89IUXWIlmwEwQFYF1wjn87N4ZxL9Z/xA2m/R # 1zizFylWP/mpamCnVfZZLkafFLNUNVmcvc+9gM7vceJs37d3ydabk4wR6ObR34sW # aLppmyPlsI1Qq5Lu6bJCWoXzYuWpkoK6oEep1gML6SRC3HKVS3UscZhtMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYICzzCCAjgCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo2 # MEJDLUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUACmcyOWmZxErpq06B8dy6oMZ6//yggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOMUsDowIhgPMjAyMDA5MjIyMTE3NDZaGA8yMDIwMDkyMzIxMTc0NlowdDA6Bgor # BgEEAYRZCgQBMSwwKjAKAgUA4xSwOgIBADAHAgEAAgII+zAHAgEAAgIRDTAKAgUA # 4xYBugIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAGFtTfLVXgP5nTlAW4+t # qfeH51GOeG4NIbJTmRivKyHJM/AjTJCfaazyngryVGsVed/19YBgxoB07IS1Jlmm # KNq9XofCcAXBUcoBsPLnGHr8vBVOpAPDLDYgr7ME8Qgofa5CjOmCQMYZyjsW2q5j # 2OkaGPVN1YtLAZq82zgDGI+QMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAAEm37pLIrmCggcAAAAAASYwDQYJYIZIAWUDBAIB # BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx # IgQg4R9WsNqSRA41TdlwJ+Gl9iyX+HsrWWraUjisH0d+K7IwgfoGCyqGSIb3DQEJ # EAIvMYHqMIHnMIHkMIG9BCA2/c/vnr1ecAzvapOWZ2xGfAkzrkfpGcrvMW07CQl1 # DzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABJt+6 # SyK5goIHAAAAAAEmMCIEILioZM02FaPhX5O1/7sR3BeH9w6NUS1x/c+Q88996cSH # MA0GCSqGSIb3DQEBCwUABIIBAJe/stVSoax/AYbLjdRsdlN60oiL97/W3FU+joo2 # WRwoqLTFscKwlFxQSK5dRXEnn0gU1IQCMsWTeiRrLPV8gtaiUxtczpdK4QvuXJnd # 8sdla5oQNu3vuEVSjnFLl8w6pFBai4ztvz76ZGEJlUfiXF8aCMDHoiA9kqhu3iSm # Ky46sAfmEYRDjfIH14LzEbL+KRr3bM5G74fv125+4DyCbRcphNAHQ++gQ1WJkXF4 # 8BUAbKTWLjIDQAgq15jE0JSeBJ4RxuTVt+Ha8NAnvmDpC1fUWvldymt4TZKd3EE9 # iujPnESHGHysaE+G1MZfl/BrpYKq9o3khCHwUBdWzUtSDHw= # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/DscResources/MSFT_PSRepository/MSFT_PSRepository.psm1 ================================================ $resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent # Import localization helper functions. $helperName = 'PowerShellGet.LocalizationHelper' $dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" Import-Module -Name $dscResourcesFolderFilePath $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_PSRepository' -ScriptRoot $PSScriptRoot # Import resource helper functions. $helperName = 'PowerShellGet.ResourceHelper' $dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" Import-Module -Name $dscResourcesFolderFilePath <# .SYNOPSIS Returns the current state of the repository. .PARAMETER Name Specifies the name of the repository to manage. #> function Get-TargetResource { <# These suppressions are added because this repository have other Visual Studio Code workspace settings than those in DscResource.Tests DSC test framework. Only those suppression that contradict this repository guideline is added here. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $Name ) $returnValue = @{ Ensure = 'Absent' Name = $Name SourceLocation = $null ScriptSourceLocation = $null PublishLocation = $null ScriptPublishLocation = $null InstallationPolicy = $null PackageManagementProvider = $null Trusted = $false Registered = $false } Write-Verbose -Message ($localizedData.GetTargetResourceMessage -f $Name) $repository = Get-PSRepository -Name $Name -ErrorAction 'SilentlyContinue' if ($repository) { $returnValue.Ensure = 'Present' $returnValue.SourceLocation = $repository.SourceLocation $returnValue.ScriptSourceLocation = $repository.ScriptSourceLocation $returnValue.PublishLocation = $repository.PublishLocation $returnValue.ScriptPublishLocation = $repository.ScriptPublishLocation $returnValue.InstallationPolicy = $repository.InstallationPolicy $returnValue.PackageManagementProvider = $repository.PackageManagementProvider $returnValue.Trusted = $repository.Trusted $returnValue.Registered = $repository.Registered } else { Write-Verbose -Message ($localizedData.RepositoryNotFound -f $Name) } return $returnValue } <# .SYNOPSIS Determines if the repository is in the desired state. .PARAMETER Ensure If the repository should be present or absent on the server being configured. Default values is 'Present'. .PARAMETER Name Specifies the name of the repository to manage. .PARAMETER SourceLocation Specifies the URI for discovering and installing modules from this repository. A URI can be a NuGet server feed, HTTP, HTTPS, FTP or file location. .PARAMETER ScriptSourceLocation Specifies the URI for the script source location. .PARAMETER PublishLocation Specifies the URI of the publish location. For example, for NuGet-based repositories, the publish location is similar to http://someNuGetUrl.com/api/v2/Packages. .PARAMETER ScriptPublishLocation Specifies the URI for the script publish location. .PARAMETER InstallationPolicy Specifies the installation policy. Valid values are 'Trusted' or 'Untrusted'. The default value is 'Untrusted'. .PARAMETER PackageManagementProvider Specifies a OneGet package provider. Default value is 'NuGet'. #> function Test-TargetResource { <# These suppressions are added because this repository have other Visual Studio Code workspace settings than those in DscResource.Tests DSC test framework. Only those suppression that contradict this repository guideline is added here. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $SourceLocation, [Parameter()] [System.String] $ScriptSourceLocation, [Parameter()] [System.String] $PublishLocation, [Parameter()] [System.String] $ScriptPublishLocation, [Parameter()] [ValidateSet('Trusted', 'Untrusted')] [System.String] $InstallationPolicy = 'Untrusted', [Parameter()] [System.String] $PackageManagementProvider = 'NuGet' ) Write-Verbose -Message ($localizedData.TestTargetResourceMessage -f $Name) $returnValue = $false $getTargetResourceResult = Get-TargetResource -Name $Name if ($Ensure -eq $getTargetResourceResult.Ensure) { if ($getTargetResourceResult.Ensure -eq 'Present' ) { $returnValue = Test-DscParameterState ` -CurrentValues $getTargetResourceResult ` -DesiredValues $PSBoundParameters ` -ValuesToCheck @( 'SourceLocation' 'ScriptSourceLocation' 'PublishLocation' 'ScriptPublishLocation' 'InstallationPolicy' 'PackageManagementProvider' ) } else { $returnValue = $true } } if ($returnValue) { Write-Verbose -Message ($localizedData.InDesiredState -f $Name) } else { Write-Verbose -Message ($localizedData.NotInDesiredState -f $Name) } return $returnValue } <# .SYNOPSIS Creates, removes or updates the repository. .PARAMETER Ensure If the repository should be present or absent on the server being configured. Default values is 'Present'. .PARAMETER Name Specifies the name of the repository to manage. .PARAMETER SourceLocation Specifies the URI for discovering and installing modules from this repository. A URI can be a NuGet server feed, HTTP, HTTPS, FTP or file location. .PARAMETER ScriptSourceLocation Specifies the URI for the script source location. .PARAMETER PublishLocation Specifies the URI of the publish location. For example, for NuGet-based repositories, the publish location is similar to http://someNuGetUrl.com/api/v2/Packages. .PARAMETER ScriptPublishLocation Specifies the URI for the script publish location. .PARAMETER InstallationPolicy Specifies the installation policy. Valid values are 'Trusted' or 'Untrusted'. The default value is 'Untrusted'. .PARAMETER PackageManagementProvider Specifies a OneGet package provider. Default value is 'NuGet'. #> function Set-TargetResource { <# These suppressions are added because this repository have other Visual Studio Code workspace settings than those in DscResource.Tests DSC test framework. Only those suppression that contradict this repository guideline is added here. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-FunctionBlockBraces', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-IfStatement', '')] [CmdletBinding()] param ( [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String] $SourceLocation, [Parameter()] [System.String] $ScriptSourceLocation, [Parameter()] [System.String] $PublishLocation, [Parameter()] [System.String] $ScriptPublishLocation, [Parameter()] [ValidateSet('Trusted', 'Untrusted')] [System.String] $InstallationPolicy = 'Untrusted', [Parameter()] [System.String] $PackageManagementProvider = 'NuGet' ) $getTargetResourceResult = Get-TargetResource -Name $Name # Determine if the repository should be present or absent. if ($Ensure -eq 'Present') { $repositoryParameters = New-SplatParameterHashTable ` -FunctionBoundParameters $PSBoundParameters ` -ArgumentNames @( 'Name' 'SourceLocation' 'ScriptSourceLocation' 'PublishLocation' 'ScriptPublishLocation' 'InstallationPolicy' 'PackageManagementProvider' ) # Determine if the repository is already present. if ($getTargetResourceResult.Ensure -eq 'Present') { Write-Verbose -Message ($localizedData.RepositoryExist -f $Name) # Repository exist, update the properties. Set-PSRepository @repositoryParameters -ErrorAction 'Stop' } else { Write-Verbose -Message ($localizedData.RepositoryDoesNotExist -f $Name) # Repository did not exist, create the repository. Register-PSRepository @repositoryParameters -ErrorAction 'Stop' } } else { if ($getTargetResourceResult.Ensure -eq 'Present') { Write-Verbose -Message ($localizedData.RemoveExistingRepository -f $Name) # Repository did exist, remove the repository. Unregister-PSRepository -Name $Name -ErrorAction 'Stop' } } } # SIG # Begin signature block # MIIjjgYJKoZIhvcNAQcCoIIjfzCCI3sCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBlZKKmsIDI2MiW # t2Bt6XXxjORDDSKWDjB5NwOM5+2tUaCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVYzCCFV8CAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgs+RU2J5w # NacSfRVCp+ToPyznw3pclb/0hPnXb8LPCq4wQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQA8PvaOO0+Ef6lk0kKMSevhLul270IbDPTD4V/sXD20 # rOpGDeRISfE2EWaZD3nv9H3icTFeFOTguBphXaQNh2PImj+7qzf3/lKP2ETu0xvI # ZJii75+Pe25WL3ozwCd2eKaAG5ceSmAuCXCcEyw1BR0OW1mWTA25ya0tIa5v1466 # sbykvU3vNEC4eN6dO56w6Y2WmziZx9YFn5feMjCSdJigMxN6SONXkVMTTe9x0s2+ # I2Sjc8I+MPxDHfjwOtA7pHNk6KEve4UE3pWzVYJBe0JdXHIaxBUwcti2bue7DDDN # xa0QnEzVd+eD55gR8rudUbiD30fQQzDzl+KvHmgrgdAVoYIS7TCCEukGCisGAQQB # gjcDAwExghLZMIIS1QYJKoZIhvcNAQcCoIISxjCCEsICAQMxDzANBglghkgBZQME # AgEFADCCAVQGCyqGSIb3DQEJEAEEoIIBQwSCAT8wggE7AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEINsdx97zcSFVxkNkK+d8VvGiJ0Fsuev3S05KAcoZ # Uxj9AgZfYQkjSiYYEjIwMjAwOTIyMjIxOTUxLjM2WjAEgAIB9KCB1KSB0TCBzjEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWlj # cm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBU # U1MgRVNOOkY3N0YtRTM1Ni01QkFFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T # dGFtcCBTZXJ2aWNloIIOQTCCBPUwggPdoAMCAQICEzMAAAEq6BeW+Ian76MAAAAA # ASowDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw # HhcNMTkxMjE5MDExNTAyWhcNMjEwMzE3MDExNTAyWjCBzjELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJh # dGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkY3N0Yt # RTM1Ni01QkFFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn9+VgaSF0m3FKwcG72WZ # cX9RfE8XsvjmcSGa13TUoOixZtjzLngE3v6T0My/OpOg/f2/z9n420TMqPwF/kRC # gbX+kl+nMIl7zQdmrKoyjShD0S6BVjpg1U1rZPW7nV33qrWEWa7V2DG3y4PaDsik # FB2FLa2lzePccTMq9X+/ASvv8FxO7CpQequsGAdz3vV6lVHijls0qyOKRrCYzD0P # +3KtNyLLcX0ar2kSCTwSol850BpuRqe4BZOOWYGFm1GI71bWoWnCe70bmpW900pE # rFB23EwLTilYZ+fHMNpzv6MiqXnfYgQLlBKe9jzizMSnHDfVBb8tp9KIOYC1hYem # bwIDAQABo4IBGzCCARcwHQYDVR0OBBYEFHD0xS10Kz+uE3bL0SQTpkj07xNpMB8G # A1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeG # RWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Rp # bVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUH # MAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3Rh # UENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYB # BQUHAwgwDQYJKoZIhvcNAQELBQADggEBAIrANPQKcdWjjo5bJRus8iPxAhx/49OM # FVikqDUrYPXlnrES6+Z/6Kzo3yCP1/WeQUgAu+H6IaTHwaAZr+gD0iFc0QVg80Vo # fAdqf9QTDU/pON1qrLdy8sLx/zMTUJHUuFc2h+rrF+hP0csYVKD2yQ8szVND5EBB # f0yKASbwUWWGGxDWIYHXf33Hx33aH0qymoYOc73pn0CPs5sO11TpGhmuxmSJFA2d # eadfUj5G7C0u7ww3xeEktKXnCqoczeuppoy9IAhJW0rJKnMkLlmH7mQmWoV1KIgd # bxD7xHoRYbwgtv09/7D8/J3IrdlORVdSkUD4mFaNzLOmFUbD19+PRgowggZxMIIE # WaADAgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9v # dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0y # NTA3MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RU # ENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBE # D/FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50 # YWeRX4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd # /XcfPfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaR # togINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQAB # o4IB5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8 # RhvFM2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIB # hjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fO # mhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w # a2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggr # BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSAB # Af8EgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEF # BQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBt # AGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Eh # b7Prpsz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7 # uVOMzPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqR # UgCvOA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9 # Va8v/rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8 # +n99lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+ # Y1klD3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh # 2rBQHm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRy # zR30uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoo # uLGp25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx # 16HSxVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341 # Hgi62jbb01+P3nSISRKhggLPMIICOAIBATCB/KGB1KSB0TCBzjELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9w # ZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkY3 # N0YtRTM1Ni01QkFFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2 # aWNloiMKAQEwBwYFKw4DAhoDFQDqsuasofIgw/vp4+XfbXEpQndhf6CBgzCBgKR+ # MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT # HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA # 4xTB1zAiGA8yMDIwMDkyMjIyMzI1NVoYDzIwMjAwOTIzMjIzMjU1WjB0MDoGCisG # AQQBhFkKBAExLDAqMAoCBQDjFMHXAgEAMAcCAQACAg5cMAcCAQACAhGGMAoCBQDj # FhNXAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMH # oSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAUkLp/BaA3FIU75KSgrKa # sz4lpFeJKc1q90R79iPMjkdWuk0HzMJE/P+m9f1Nu5KdDb9Y3hk8jYCgEwZClaRo # bWOX24LRDCwVMvwHP3tOmTsaZL3LONfT6yuxrEhUIaD2B5evsDgihSnRZXFHwlEw # 2KeQBiVnSWkobaUU4DQYE9YxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T # dGFtcCBQQ0EgMjAxMAITMwAAASroF5b4hqfvowAAAAABKjANBglghkgBZQMEAgEF # AKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEi # BCAOGPARROnr2oqQEu2OY1Bf2p1llcPXBS/KQIsljnOGKjCB+gYLKoZIhvcNAQkQ # Ai8xgeowgecwgeQwgb0EIEOYNYRa9zp+Gzm3haijlD4UwUJxoiBXjJQ/gKm4GYuZ # MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEm # MCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAEq6BeW # +Ian76MAAAAAASowIgQg9iJwv/WXI3bNmxzZZnw0+GGWIYQYBQ5UMWvWKXauOyUw # DQYJKoZIhvcNAQELBQAEggEAQmz84vhytrXC6N3xTngSm+jXmE60CX4FWbIY7/sc # aLR29t/ayGUM7tb48SpnUgk8Ogt32xMS02zZjvjGqGraktCUIeD+P5lDz1x9ouBd # Oe4yHI6FV7Do6jIz7aje8R66z7TKkSSX/5z5i51aRuOOGmp31ZVSW58MtAkNy3WR # 4i9F7OEqt0uZaokblXF2Cl3gMAYqMScQyAWDwc2IxLH3JbNTUAB2Vtdp0MUTJKqo # RUoIlB3ZJd77W+kHkJk/kLLCWUy7Xr6T7ZFw3AfoXDfsdKOpci3YRpZci4zg95R8 # Pf6al6GKk9Ge7Rus7feYG7PItce08fWvT3SWOT+HNUNwKg== # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mfl ================================================ #pragma namespace("\\\\.\\root\\default") instance of __namespace{ name="MS_409";}; #pragma namespace("\\\\.\\root\\default\\MS_409") [AMENDMENT, LOCALE("MS_409")] class MSFT_PSModule : OMI_BaseResource { [Key, Description("Specifies the name of the repository to manage.") : Amended] String Name; [Description("If the repository should be present or absent on the server being configured. Default values is 'Present'.") : Amended] String Ensure; [Description("Specifies the URI for discovering and installing modules from this repository. A URI can be a NuGet server feed, HTTP, HTTPS, FTP or file location.") : Amended] String SourceLocation; [Description("Specifies the URI for the script source location.") : Amended] String ScriptSourceLocation; [Description("Specifies the URI of the publish location. For example, for NuGet-based repositories, the publish location is similar to http://someNuGetUrl.com/api/v2/Packages.") : Amended] String PublishLocation; [Description("Specifies the URI for the script publish location.") : Amended] String ScriptPublishLocation; [Description("Specifies the installation policy. Valid values are 'Trusted' or 'Untrusted'. The default value is 'Untrusted'.") : Amended] String InstallationPolicy; [Description("Specifies a OneGet package provider. Default value is 'NuGet'.") : Amended] String PackageManagementProvider; [Description("Specifies if the repository is trusted.") : Amended] Boolean Trusted; [Description("Specifies if the repository is registered.") : Amended] Boolean Registered; }; ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/DscResources/MSFT_PSRepository/MSFT_PSRepository.schema.mof ================================================ [ClassVersion("1.0.0.0"),FriendlyName("PSRepository")] class MSFT_PSRepository : OMI_BaseResource { [Key] String Name; [Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write] String SourceLocation; [Write] String ScriptSourceLocation; [Write] String PublishLocation; [Write] String ScriptPublishLocation; [Write, ValueMap{"Trusted","Untrusted"}, Values{"Trusted","Untrusted"}] String InstallationPolicy; [Write] String PackageManagementProvider; [Read] Boolean Trusted; [Read] Boolean Registered; }; /* SIG # Begin signature block */ /* MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor */ /* BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG */ /* KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAUB2bP/4Ied6bW */ /* IFA1D31XybyRSF4DCKxuisoZB3v8laCCDYEwggX/MIID56ADAgECAhMzAAABh3IX */ /* chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD */ /* VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy */ /* b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p */ /* bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw */ /* CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u */ /* ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy */ /* b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB */ /* AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB */ /* znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH */ /* sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d */ /* weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ */ /* itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV */ /* Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE */ /* AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw */ /* UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 */ /* ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu */ /* ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu */ /* bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w */ /* Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 */ /* Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx */ /* MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy */ /* S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K */ /* NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV */ /* BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr */ /* qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx */ /* zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe */ /* yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g */ /* yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf */ /* AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI */ /* 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 */ /* GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea */ /* jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS */ /* AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK */ /* V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 */ /* IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 */ /* ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla */ /* MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS */ /* ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT */ /* H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB */ /* AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG */ /* OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S */ /* 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz */ /* y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 */ /* 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u */ /* M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 */ /* X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl */ /* XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP */ /* 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB */ /* l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF */ /* RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM */ /* CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ */ /* BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud */ /* DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO */ /* 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 */ /* LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y */ /* Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p */ /* Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y */ /* Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB */ /* FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw */ /* cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA */ /* XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY */ /* 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj */ /* 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd */ /* d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ */ /* Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf */ /* wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ */ /* aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j */ /* NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B */ /* xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 */ /* eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 */ /* r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I */ /* RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG */ /* A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx */ /* HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z */ /* b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN */ /* BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor */ /* BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgMC/orGY1 */ /* gfB1hsQjG1lyTu9MAGbhJ+T22w7TA3J3lU8wQgYKKwYBBAGCNwIBDDE0MDKgFIAS */ /* AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN */ /* BgkqhkiG9w0BAQEFAASCAQCjrMwnWdiDhFgDpoWhqWMJ473gHqjSpuGt18jSbPyW */ /* whPOmBvwYOb/CAiQ/kX32DaYn7EQJOKdReWwiZrqtf2TqrAXYeaoC2qCqkZOqqMC */ /* c+79Ov9NQ4MsxTFiRa023+eWg5KE266QIh/WJaQaBSPpoXbIcHcEnQ8lHdb/DO9v */ /* yZ/Zp/t8PuXOap+Go6jkSeD/zU4B8FswbqSJXWrLCzEWSjAQUksoZXYVsiUvb6+w */ /* 9t79CK4ugNLJS6ILDxn5ncOQfHv47d1e4H2Y+OWOP/Z45kcD1fl4QRnM+WQEa859 */ /* spOcWWk9pnTuvfCo5X86LOP+jdQa304GVxDeAhJjtvquoYIS8TCCEu0GCisGAQQB */ /* gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME */ /* AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB */ /* MDEwDQYJYIZIAWUDBAIBBQAEIBPLNFthVEbZ8wwcxOxfPOwIYuIklub6TutRWTzR */ /* A5CWAgZfYPphXqQYEzIwMjAwOTIyMjIxOTUxLjI0OVowBIACAfSggdSkgdEwgc4x */ /* CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt */ /* b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p */ /* Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg */ /* VFNTIEVTTjowQTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt */ /* U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABJy9uo++RqBmoAAAA */ /* AAEnMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo */ /* aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y */ /* cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw */ /* MB4XDTE5MTIxOTAxMTQ1OVoXDTIxMDMxNzAxMTQ1OVowgc4xCzAJBgNVBAYTAlVT */ /* MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK */ /* ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy */ /* YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowQTU2 */ /* LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj */ /* ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPgB3nERnk6fS40vvWeD */ /* 3HCgM9Ep4xTIQiPnJXE9E+HkZVtTsPemoOyhfNAyF95E/rUvXOVTUcJFL7Xb16jT */ /* KPXONsCWY8DCixSDIiid6xa30TiEWVcIZRwiDlcx29D467OTav5rA1G6TwAEY5rQ */ /* jhUHLrOoJgfJfakZq6IHjd+slI0/qlys7QIGakFk2OB6mh/ln/nS8G4kNRK6Do4g */ /* xDtnBSFLNfhsSZlRSMDJwFvrZ2FCkaoexd7rKlUNOAAScY411IEqQeI1PwfRm3aW */ /* bS8IvAfJPC2Ah2LrtP8sKn5faaU8epexje7vZfcZif/cbxgUKStJzqbdvTBNc93n */ /* /Z8CAwEAAaOCARswggEXMB0GA1UdDgQWBBTl9JZVgF85MSRbYlOJXbhY022V8jAf */ /* BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH */ /* hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU */ /* aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF */ /* BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 */ /* YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG */ /* AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAKyo180VXHBqVnjZwQy7NlzXbo2+W5 */ /* qfHxR7ANV5RBkRkdGamkwUcDNL+DpHObFPJHa0oTeYKE0Zbl1MvvfS8RtGGdhGYG */ /* CJf+BPd/gBCs4+dkZdjvOzNyuVuDPGlqQ5f7HS7iuQ/cCyGHcHYJ0nXVewF2Lk+J */ /* lrWykHpTlLwPXmCpNR+gieItPi/UMF2RYTGwojW+yIVwNyMYnjFGUxEX5/DtJjRZ */ /* mg7PBHMrENN2DgO6wBelp4ptyH2KK2EsWT+8jFCuoKv+eJby0QD55LN5f8SrUPRn */ /* K86fh7aVOfCglQofo5ABZIGiDIrg4JsV4k6p0oBSIFOAcqRAhiH+1spCMIIGcTCC */ /* BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC */ /* VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV */ /* BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv */ /* b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN */ /* MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv */ /* bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 */ /* aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw */ /* DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 */ /* VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw */ /* RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe */ /* dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx */ /* Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G */ /* kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA */ /* AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 */ /* fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC */ /* AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX */ /* zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v */ /* cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI */ /* KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j */ /* b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g */ /* AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 */ /* d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB */ /* BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA */ /* bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh */ /* IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS */ /* +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK */ /* kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon */ /* /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi */ /* PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ */ /* fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII */ /* YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 */ /* cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a */ /* KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ */ /* cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ */ /* NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT */ /* AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD */ /* VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP */ /* cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow */ /* QTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy */ /* dmljZaIjCgEBMAcGBSsOAwIaAxUAs5W4TmyDHMRM7iz6mgGojqvXHzOggYMwgYCk */ /* fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH */ /* UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD */ /* Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF */ /* AOMUsu8wIhgPMjAyMDA5MjIyMTI5MTlaGA8yMDIwMDkyMzIxMjkxOVowdzA9Bgor */ /* BgEEAYRZCgQBMS8wLTAKAgUA4xSy7wIBADAKAgEAAgIVPgIB/zAHAgEAAgIRtjAK */ /* AgUA4xYEbwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB */ /* AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAEMD4esQRMLwQdhk */ /* Co1zgvmclcwl3lYYpk1oMh1ndsU3+97Rt6FV3adS4Hezc/K94oQKjcxtMVzLzQhG */ /* agM6XlqB31VD8n2nxVuaWD1yp2jm/0IvfL9nFMHJRhgANMiBdHqvqNrd86c/Kryq */ /* sI0Ch0sOx9wg3BozzqQhmdNjf9c6MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC */ /* VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV */ /* BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp */ /* bWUtU3RhbXAgUENBIDIwMTACEzMAAAEnL26j75GoGagAAAAAAScwDQYJYIZIAWUD */ /* BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B */ /* CQQxIgQg/U1ThwYjl65L4baBOXDEDb7//jQrQlYA0cYE9FlfO3swgfoGCyqGSIb3 */ /* DQEJEAIvMYHqMIHnMIHkMIG9BCAbkuhLEoYdahb/BUyVszO2VDi6kB3MSaof/+8u */ /* 7SM+IjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u */ /* MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp */ /* b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB */ /* Jy9uo++RqBmoAAAAAAEnMCIEIK4r6N3NISekswMCG1kSBJCCCePrlLDQWbMKz0wt */ /* Lj6CMA0GCSqGSIb3DQEBCwUABIIBACDLuq6BoGBriDoXannpgEinZyZN3Q033i3n */ /* dqST50fVElRabEBHd70IaqgBBovB75obPAEecVLMP24ti411wt2k4gs/zor+3I34 */ /* r/hFZRQEJbMESNsRDpCDFVm5fT55uwFUTg6esiQgsMXcvYlpkypKmq8fr4I2GT4V */ /* leW9JafateqTA7f4oQeLvSq+Y/QrMVQIf08db1vXi3J0crsIFeVRmAB34kW9rrqT */ /* pvQKpJHm8SKqBQMMfQXxyomEbwDeJ8Eu/9/FwL+NfvzdsNwxWzjA05IgZdEelwun */ /* f6B4E0kWDJvgeedsXIvsQ92hAoQubWQ2F9pYDygONBjoxnqijzs= */ /* SIG # End signature block */ ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/DscResources/MSFT_PSRepository/en-US/MSFT_PSRepository.strings.psd1 ================================================ # # Copyright (c) Microsoft Corporation. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # culture = "en-US" ConvertFrom-StringData -StringData @' GetTargetResourceMessage = Return the current state of the repository '{0}'. RepositoryNotFound = The repository '{0}' was not found. TestTargetResourceMessage = Determining if the repository '{0}' is in the desired state. InDesiredState = Repository '{0}' is in the desired state. NotInDesiredState = Repository '{0}' is not in the desired state. RepositoryExist = Updating the properties of the repository '{0}'. RepositoryDoesNotExist = Creating the repository '{0}'. RemoveExistingRepository = Removing the repository '{0}'. '@ # SIG # Begin signature block # MIIjjwYJKoZIhvcNAQcCoIIjgDCCI3wCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDIsy85n1MNq+kW # xzRs7tzuriysf98xKvvABEQcTZkh4aCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZDCCFWACAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgmA+n9QXz # 8dT2ub5mX+3kqHOkIOPzbPjen0qPL4vqih4wQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQBPossKE/JJRgLki6udUal6kKvvd+cj8yT23YFiJ+GG # 7YvWIt/u67qqWA+PSN0PCH8bY1HCUkMGat4UTqKJ/dALKx5oOlfsbUSACyX1BU9x # mXDPzak96AnHy+oQICcENAWYBT4deS8jrFv1InsRT6lg4pEGQvCx+fZbdAe5HRp5 # cAlIVYapHXxDBQvAbpMoMMi0TertRaBx9t3bWWLGguBUR92ra8a3HFOt7KHT04Xa # 7JgLsQoTUTlavt67fWUABX5pSVwP25v9IXUSclgz5l1xXrBHTmZPGRsjBu881s+M # 168vsc6P9RW8JjsKwDajs2oWkRY3F68wb7xLa2ZQ4X5koYIS7jCCEuoGCisGAQQB # gjcDAwExghLaMIIS1gYJKoZIhvcNAQcCoIISxzCCEsMCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIHikZhYxRsySx5WAopqolokj5LdR1qw/Qj19bR6x # FpA5AgZfYPebj+kYEzIwMjAwOTIyMjIxOTUxLjQ1NVowBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjo2MEJDLUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkEwggT1MIID3aADAgECAhMzAAABJt+6SyK5goIHAAAA # AAEmMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTE5MTIxOTAxMTQ1OVoXDTIxMDMxNzAxMTQ1OVowgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo2MEJD # LUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ4wvoacTvMNlXQTtfF/ # Cx5Ol3X0fcjUNMvjLgTmO5+WHYJFbp725P3+qvFKDRQHWEI1Sz0gB24urVDIjXjB # h5NVNJVMQJI2tltv7M4/4IbhZJb3xzQW7LolEoZYUZanBTUuyly9osCg4o5joViT # 2GtmyxK+Fv5kC20l2opeaeptd/E7ceDAFRM87hiNCsK/KHyC+8+swnlg4gTOey6z # QqhzgNsG6HrjLBuDtDs9izAMwS2yWT0T52QA9h3Q+B1C9ps2fMKMe+DHpG+0c61D # 94Yh6cV2XHib4SBCnwIFZAeZE2UJ4qPANSYozI8PH+E5rCT3SVqYvHou97HsXvP2 # I3MCAwEAAaOCARswggEXMB0GA1UdDgQWBBRJq6wfF7B+mEKN0VimX8ajNA5hQTAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBAlvudaOlv9Cfzv56bnX41czF6tLtH # LB46l6XUch+qNN45ZmOTFwLot3JjwSrn4oycQ9qTET1TFDYd1QND0LiXmKz9OqBX # ai6S8XdyCQEZvfL82jIAs9pwsAQ6XvV9jNybPStRgF/sOAM/Deyfmej9Tg9FcRwX # ank2qgzdZZNb8GoEze7f1orcTF0Q89IUXWIlmwEwQFYF1wjn87N4ZxL9Z/xA2m/R # 1zizFylWP/mpamCnVfZZLkafFLNUNVmcvc+9gM7vceJs37d3ydabk4wR6ObR34sW # aLppmyPlsI1Qq5Lu6bJCWoXzYuWpkoK6oEep1gML6SRC3HKVS3UscZhtMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYICzzCCAjgCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo2 # MEJDLUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUACmcyOWmZxErpq06B8dy6oMZ6//yggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOMUsDowIhgPMjAyMDA5MjIyMTE3NDZaGA8yMDIwMDkyMzIxMTc0NlowdDA6Bgor # BgEEAYRZCgQBMSwwKjAKAgUA4xSwOgIBADAHAgEAAgII+zAHAgEAAgIRDTAKAgUA # 4xYBugIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAGFtTfLVXgP5nTlAW4+t # qfeH51GOeG4NIbJTmRivKyHJM/AjTJCfaazyngryVGsVed/19YBgxoB07IS1Jlmm # KNq9XofCcAXBUcoBsPLnGHr8vBVOpAPDLDYgr7ME8Qgofa5CjOmCQMYZyjsW2q5j # 2OkaGPVN1YtLAZq82zgDGI+QMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAAEm37pLIrmCggcAAAAAASYwDQYJYIZIAWUDBAIB # BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx # IgQgTg7YbUxqwEPryPvBnm6tKeHXdMGcW0dFqzF8v32xEgQwgfoGCyqGSIb3DQEJ # EAIvMYHqMIHnMIHkMIG9BCA2/c/vnr1ecAzvapOWZ2xGfAkzrkfpGcrvMW07CQl1 # DzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABJt+6 # SyK5goIHAAAAAAEmMCIEILioZM02FaPhX5O1/7sR3BeH9w6NUS1x/c+Q88996cSH # MA0GCSqGSIb3DQEBCwUABIIBAEZEbtwLgdlFNIt4umG1gw1nrQVpGXIkyojlPtcb # hQu757SIEuY7tS0MmwFimKjHmCbeVS12PJ1PHjxXs8YmpOusFH9wtCgPPHHm3IEx # naynU0x6jl4MXGtmTIF5E8iHA1y0YeSoeeinaez2Lc78G+jHN6uzf+TJozwpqkle # aZIVhuqPAqJZvfVDT3Bd7N4J2j4D6H0lxJ+yn/ENLkNEl4jVMutEMDQuDSikB8/V # hpcMRx83R4M8gEIrFIORJVwqT5axQI5C9HQuWFB9xyTvU5Q/OaU4ybRj5jUTPNZs # 3/emFZ3aTnt8arfvfztIvFw2NbnM6rIPVjm4YDT9YUD2xNM= # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/Modules/PowerShellGet.LocalizationHelper/PowerShellGet.LocalizationHelper.psm1 ================================================ <# .SYNOPSIS Creates and throws an invalid argument exception. .PARAMETER Message The message explaining why this error is being thrown. .PARAMETER ArgumentName The name of the invalid argument that is causing this error to be thrown. #> function New-InvalidArgumentException { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Message, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ArgumentName ) $argumentException = New-Object -TypeName 'ArgumentException' ` -ArgumentList @($Message, $ArgumentName) $newObjectParameters = @{ TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) } $errorRecord = New-Object @newObjectParameters throw $errorRecord } <# .SYNOPSIS Creates and throws an invalid operation exception. .PARAMETER Message The message explaining why this error is being thrown. .PARAMETER ErrorRecord The error record containing the exception that is causing this terminating error. #> function New-InvalidOperationException { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Message, [Parameter()] [ValidateNotNull()] [System.Management.Automation.ErrorRecord] $ErrorRecord ) if ($null -eq $ErrorRecord) { $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` -ArgumentList @($Message) } else { $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` -ArgumentList @($Message, $ErrorRecord.Exception) } $newObjectParameters = @{ TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $invalidOperationException.ToString(), 'MachineStateIncorrect', 'InvalidOperation', $null ) } $errorRecordToThrow = New-Object @newObjectParameters throw $errorRecordToThrow } <# .SYNOPSIS Creates and throws an object not found exception. .PARAMETER Message The message explaining why this error is being thrown. .PARAMETER ErrorRecord The error record containing the exception that is causing this terminating error. #> function New-ObjectNotFoundException { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Message, [Parameter()] [ValidateNotNull()] [System.Management.Automation.ErrorRecord] $ErrorRecord ) if ($null -eq $ErrorRecord) { $exception = New-Object -TypeName 'System.Exception' ` -ArgumentList @($Message) } else { $exception = New-Object -TypeName 'System.Exception' ` -ArgumentList @($Message, $ErrorRecord.Exception) } $newObjectParameters = @{ TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $exception.ToString(), 'MachineStateIncorrect', 'ObjectNotFound', $null ) } $errorRecordToThrow = New-Object @newObjectParameters throw $errorRecordToThrow } <# .SYNOPSIS Creates and throws an invalid result exception. .PARAMETER Message The message explaining why this error is being thrown. .PARAMETER ErrorRecord The error record containing the exception that is causing this terminating error. #> function New-InvalidResultException { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Message, [Parameter()] [ValidateNotNull()] [System.Management.Automation.ErrorRecord] $ErrorRecord ) if ($null -eq $ErrorRecord) { $exception = New-Object -TypeName 'System.Exception' ` -ArgumentList @($Message) } else { $exception = New-Object -TypeName 'System.Exception' ` -ArgumentList @($Message, $ErrorRecord.Exception) } $newObjectParameters = @{ TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $exception.ToString(), 'MachineStateIncorrect', 'InvalidResult', $null ) } $errorRecordToThrow = New-Object @newObjectParameters throw $errorRecordToThrow } <# .SYNOPSIS Retrieves the localized string data based on the machine's culture. Falls back to en-US strings if the machine's culture is not supported. .PARAMETER ResourceName The name of the resource as it appears before '.strings.psd1' of the localized string file. For example: For WindowsOptionalFeature: MSFT_WindowsOptionalFeature For Service: MSFT_ServiceResource For Registry: MSFT_RegistryResource For Helper: SqlServerDscHelper .PARAMETER ScriptRoot Optional. The root path where to expect to find the culture folder. This is only needed for localization in helper modules. This should not normally be used for resources. #> function Get-LocalizedData { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ResourceName, [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $ScriptRoot ) if ( -not $ScriptRoot ) { $resourceDirectory = Join-Path -Path $PSScriptRoot -ChildPath $ResourceName $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath $PSUICulture } else { $localizedStringFileLocation = Join-Path -Path $ScriptRoot -ChildPath $PSUICulture } if (-not (Test-Path -Path $localizedStringFileLocation)) { # Fallback to en-US if ( -not $ScriptRoot ) { $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath 'en-US' } else { $localizedStringFileLocation = Join-Path -Path $ScriptRoot -ChildPath 'en-US' } } Import-LocalizedData ` -BindingVariable 'localizedData' ` -FileName "$ResourceName.strings.psd1" ` -BaseDirectory $localizedStringFileLocation return $localizedData } Export-ModuleMember -Function @( 'New-InvalidArgumentException', 'New-InvalidOperationException', 'New-ObjectNotFoundException', 'New-InvalidResultException', 'Get-LocalizedData' ) # SIG # Begin signature block # MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCjPo12DZaAMOkK # EoYmV1li2n5EmeiwcqQzHkVYm5pRSqCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg2UNl19jp # ZkbG4N4XLYPrxRhH3aoWga/DT446FyrX8kUwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQBRCB3fJQ7H+bgUBGyRzfnLylN75t/0j3IsTW+XQtm1 # d/Sjzt0SMKSEo5OHh910EmAVWdld50KeAHRY3uQ1mWTBHRUaHNTxJg5kDfPPhMGy # q2PxhW6oQStS1SYUacVtJByF4XWHFSXezrJ7NJn0S9mRiC0dA8pSY4eQ+NuAIpMU # VvXeeb1/OrBXeghR6oagGfVlfEnH2HR6W9RQi8+9qzXFNZRfx9YewEcNsV21iA85 # QK1J9X/StSqGw1T9Zj5g8bDYLY6x876yrOGNV22NkLVh12bpifZ550iO4LPdEm/e # 29md4h0srONw1+Ov/I68VDm8tEuMPtkbUIamCfFmmUB7oYIS8TCCEu0GCisGAQQB # gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEINFMLSXQQ6FisqDCVPG1elrJ+Eq+frhsWlbuloDJ # TRWiAgZfYPq2eAgYEzIwMjAwOTIyMjIxOTUxLjA0OFowBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjo3ODgwLUUzOTAtODAxNDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABKKAOgeE21U/CAAAA # AAEoMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTE5MTIxOTAxMTUwMFoXDTIxMDMxNzAxMTUwMFowgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo3ODgw # LUUzOTAtODAxNDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ2Rsdb3VNuGPs2/Dgpc # 9gt77LG0JPkD4VWTlEJLkqznTJl+RoZfiOwN6iWfPu4k/kj8nwY7pvLs1OsBy494 # yusg4rHLwHNUJPtw1Tc54MOLgdcosA4Nxki73fDyqWwDtjOdk6H7kNczBPqADD6B # 98ot77/wSACBJIxm9qAUudquS5fczCF0++aWUavDu46U3cv6HEjIdV2ZdJTUKg4W # UIdTYMQXI082+qSs45WBZjcK98/tIfx8uq8q8ksWF9+zUjGTFiMaKHhn7cSCoEj7 # E1tVmW08ISpS678WFP2+A0OQwaWcJKNACK+J+La7Lz2bGupCidOGz5XDewc1lD9n # LPcCAwEAAaOCARswggEXMB0GA1UdDgQWBBSE4vKD8X61N5vUAcNOdH9QBMum8jAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQCLX2ZHGIULgDk/iccHWUywjDyAsBHl # hkmtmBp4lldwL3dNo0bXZZHiSZB+c2KzvPqY64BlECjS/Pqur2m9UaT1N0BeUowR # HQT88wdzd94gYqKXmLDbVR8yeVgBkcP/JiVWbXdQzcz1ETHgWrh+uzA8BwUgAaHJ # w+nXYccIuDgPJM1UTeNl9R5Ovf+6zR2E5ZI4DrIqvS4jH4QsoMPTn27AjN7VZt4a # moRxMLEcQAS7vPT1JUUaRFpFHmkUYVln1YMsw///6968aRvy3cmClS44uxkkaILb # hh1h09ejZjHhrEn+k9McVkWiuY724jJ/57tylM7A/jzIWNj1F8VlhkyyMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo3 # ODgwLUUzOTAtODAxNDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAMT1LG/KAEj0XsiL9n7mxmX1afZuggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOMUs1YwIhgPMjAyMDA5MjIyMTMxMDJaGA8yMDIwMDkyMzIxMzEwMlowdzA9Bgor # BgEEAYRZCgQBMS8wLTAKAgUA4xSzVgIBADAKAgEAAgIhfwIB/zAHAgEAAgIRnTAK # AgUA4xYE1gIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBACCIjMz039kePvi0 # +QSEAAGe16AJMB2ZvhrAKxWUoyZpnRrWDZizu7wON92D59Wr+LVSvcGrHYflMWt0 # KGA+A/i0fhPG8HWUfiRkGSg5MY5jl8DyE6MMkbWEDwmvZkzuIiQ3/4IsaRgc7vLI # DM10XS4g59T/1aPBaVnQCn2i8+D+MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAEooA6B4TbVT8IAAAAAASgwDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQgEnw4Ge50R6cM9Hq5+HXFUxJ9mTDd+r571wbhdC3OQJkwgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCC8RWqLrwVSd+/cGxDfBqS4b1tPXhoPFrC615vV # 1ugU2jCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # KKAOgeE21U/CAAAAAAEoMCIEIETuk5SkV1/sElaASDGqqK9PNF5z7kvwZ0c4bkPK # DQQkMA0GCSqGSIb3DQEBCwUABIIBAJnsZe3Pa/JdSEJL3mXUC7341+wXLrKreqxn # K1RdfgfUitNX5Kv7kxzUrm81hN3TMIjQwj7o2sqdGuQIThRoTCWIBaw4oPafeFsV # LWaz9BvhRgRjsu36ofO57eS2JlwzZi/zMIWF3G9931glwdTYRhmcDqrOXwVTyno5 # dWqxg36RI3B2DIqNMGDrVBwHbwKQmfRXuZqvw+efCFlQyktjCo0j4ngZLNw2v9J3 # 6S0AXqHMk7M/Wipf/qbp/A11CcUqZ4LnAtiF2vBVyFqrvTGWfKtG+xb7A8aP+rSq # YpqvL27egErpjjIS1YaF0UF5voxrJqp4POcWLJJ/y88oQgBMoiY= # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/Modules/PowerShellGet.ResourceHelper/PowerShellGet.ResourceHelper.psm1 ================================================ # # Copyright (c) Microsoft Corporation. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # <# Helper functions for PowerShellGet DSC Resources. #> # Import localization helper functions. $helperName = 'PowerShellGet.LocalizationHelper' $resourceModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent $dscResourcesFolderFilePath = Join-Path -Path $resourceModuleRoot -ChildPath "Modules\$helperName\$helperName.psm1" Import-Module -Name $dscResourcesFolderFilePath # Import Localization Strings $script:localizedData = Get-LocalizedData -ResourceName 'PowerShellGet.ResourceHelper' -ScriptRoot $PSScriptRoot <# .SYNOPSIS This is a helper function that extract the parameters from a given table. .PARAMETER FunctionBoundParameters Specifies the hash table containing a set of parameters to be extracted. .PARAMETER ArgumentNames Specifies a list of arguments you want to extract. #> function New-SplatParameterHashTable { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.Collections.Hashtable] $FunctionBoundParameters, [Parameter(Mandatory = $true)] [System.String[]] $ArgumentNames ) Write-Verbose -Message ($script:localizedData.CallingFunction -f $($MyInvocation.MyCommand)) $returnValue = @{} foreach ($arg in $ArgumentNames) { if ($FunctionBoundParameters.ContainsKey($arg)) { # Found an argument we are looking for, so we add it to return collection. $returnValue.Add($arg, $FunctionBoundParameters[$arg]) } } return $returnValue } <# .SYNOPSIS This is a helper function that validate that a value is correct and used correctly. .PARAMETER Value Specifies the value to be validated. .PARAMETER Type Specifies the type of argument. .PARAMETER Type Specifies the name of the provider. .OUTPUTS None. Throws an error if the test fails. #> function Test-ParameterValue { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Value, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Type, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ProviderName ) Write-Verbose -Message ($script:localizedData.CallingFunction -f $($MyInvocation.MyCommand)) switch ($Type) { 'SourceUri' { # Checks whether given URI represents specific scheme # Most common schemes: file, http, https, ftp $scheme = @('http', 'https', 'file', 'ftp') $newUri = $Value -as [System.URI] $returnValue = ($newUri -and $newUri.AbsoluteURI -and ($scheme -icontains $newUri.Scheme)) if ($returnValue -eq $false) { $errorMessage = $script:localizedData.InValidUri -f $Value New-InvalidArgumentException -ArgumentName $Type -Message $errorMessage } } 'DestinationPath' { $returnValue = Test-Path -Path $Value if ($returnValue -eq $false) { $errorMessage = $script:localizedData.PathDoesNotExist -f $Value New-InvalidArgumentException -ArgumentName $Type -Message $errorMessage } } 'PackageSource' { # Value can be either the package source Name or source Uri. # Check if the source is a Uri. $uri = $Value -as [System.URI] if ($uri -and $uri.AbsoluteURI) { # Check if it's a valid Uri. Test-ParameterValue -Value $Value -Type 'SourceUri' -ProviderName $ProviderName } else { # Check if it's a registered package source name. $source = PackageManagement\Get-PackageSource -Name $Value -ProviderName $ProviderName -ErrorVariable ev if ((-not $source) -or $ev) { # We do not need to throw error here as Get-PackageSource does already. Write-Verbose -Message ($script:localizedData.SourceNotFound -f $source) } } } default { $errorMessage = $script:localizedData.UnexpectedArgument -f $Type New-InvalidArgumentException -ArgumentName $Type -Message $errorMessage } } } <# .SYNOPSIS This is a helper function that does the version validation. .PARAMETER RequiredVersion Provides the required version. .PARAMETER MaximumVersion Provides the maximum version. .PARAMETER MinimumVersion Provides the minimum version. #> function Test-VersionParameter { [CmdletBinding()] param ( [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.String] $MaximumVersion ) Write-Verbose -Message ($localizedData.CallingFunction -f $($MyInvocation.MyCommand)) $isValid = $false # Case 1: No further check required if a user provides either none or one of these: minimumVersion, maximumVersion, and requiredVersion. if ($PSBoundParameters.Count -le 1) { return $true } # Case 2: #If no RequiredVersion is provided. if (-not $PSBoundParameters.ContainsKey('RequiredVersion')) { # If no RequiredVersion, both MinimumVersion and MaximumVersion are provided. Otherwise fall into the Case #1. $isValid = $PSBoundParameters['MinimumVersion'] -le $PSBoundParameters['MaximumVersion'] } # Case 3: RequiredVersion is provided. # In this case MinimumVersion and/or MaximumVersion also are provided. Otherwise fall in to Case #1. # This is an invalid case. When RequiredVersion is provided, others are not allowed. so $isValid is false, which is already set in the init. if ($isValid -eq $false) { $errorMessage = $script:localizedData.VersionError New-InvalidArgumentException ` -ArgumentName 'RequiredVersion, MinimumVersion or MaximumVersion' ` -Message $errorMessage } } <# .SYNOPSIS This is a helper function that retrieves the InstallationPolicy from the given repository. .PARAMETER RepositoryName Provides the repository Name. #> function Get-InstallationPolicy { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $RepositoryName ) Write-Verbose -Message ($LocalizedData.CallingFunction -f $($MyInvocation.MyCommand)) $repositoryObject = PackageManagement\Get-PackageSource -Name $RepositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue if ($repositoryObject) { return $repositoryObject.IsTrusted } } <# .SYNOPSIS This method is used to compare current and desired values for any DSC resource. .PARAMETER CurrentValues This is hash table of the current values that are applied to the resource. .PARAMETER DesiredValues This is a PSBoundParametersDictionary of the desired values for the resource. .PARAMETER ValuesToCheck This is a list of which properties in the desired values list should be checked. If this is empty then all values in DesiredValues are checked. #> function Test-DscParameterState { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Collections.Hashtable] $CurrentValues, [Parameter(Mandatory = $true)] [System.Object] $DesiredValues, [Parameter()] [System.Array] $ValuesToCheck ) $returnValue = $true if (($DesiredValues.GetType().Name -ne 'HashTable') ` -and ($DesiredValues.GetType().Name -ne 'CimInstance') ` -and ($DesiredValues.GetType().Name -ne 'PSBoundParametersDictionary')) { $errorMessage = $script:localizedData.PropertyTypeInvalidForDesiredValues -f $($DesiredValues.GetType().Name) New-InvalidArgumentException -ArgumentName 'DesiredValues' -Message $errorMessage } if (($DesiredValues.GetType().Name -eq 'CimInstance') -and ($null -eq $ValuesToCheck)) { $errorMessage = $script:localizedData.PropertyTypeInvalidForValuesToCheck New-InvalidArgumentException -ArgumentName 'ValuesToCheck' -Message $errorMessage } if (($null -eq $ValuesToCheck) -or ($ValuesToCheck.Count -lt 1)) { $keyList = $DesiredValues.Keys } else { $keyList = $ValuesToCheck } $keyList | ForEach-Object -Process { if (($_ -ne 'Verbose')) { if (($CurrentValues.ContainsKey($_) -eq $false) ` -or ($CurrentValues.$_ -ne $DesiredValues.$_) ` -or (($DesiredValues.GetType().Name -ne 'CimInstance' -and $DesiredValues.ContainsKey($_) -eq $true) -and ($null -ne $DesiredValues.$_ -and $DesiredValues.$_.GetType().IsArray))) { if ($DesiredValues.GetType().Name -eq 'HashTable' -or ` $DesiredValues.GetType().Name -eq 'PSBoundParametersDictionary') { $checkDesiredValue = $DesiredValues.ContainsKey($_) } else { # If DesiredValue is a CimInstance. $checkDesiredValue = $false if (([System.Boolean]($DesiredValues.PSObject.Properties.Name -contains $_)) -eq $true) { if ($null -ne $DesiredValues.$_) { $checkDesiredValue = $true } } } if ($checkDesiredValue) { $desiredType = $DesiredValues.$_.GetType() $fieldName = $_ if ($desiredType.IsArray -eq $true) { if (($CurrentValues.ContainsKey($fieldName) -eq $false) ` -or ($null -eq $CurrentValues.$fieldName)) { Write-Verbose -Message ($script:localizedData.PropertyValidationError -f $fieldName) -Verbose $returnValue = $false } else { $arrayCompare = Compare-Object -ReferenceObject $CurrentValues.$fieldName ` -DifferenceObject $DesiredValues.$fieldName if ($null -ne $arrayCompare) { Write-Verbose -Message ($script:localizedData.PropertiesDoesNotMatch -f $fieldName) -Verbose $arrayCompare | ForEach-Object -Process { Write-Verbose -Message ($script:localizedData.PropertyThatDoesNotMatch -f $_.InputObject, $_.SideIndicator) -Verbose } $returnValue = $false } } } else { switch ($desiredType.Name) { 'String' { if (-not [System.String]::IsNullOrEmpty($CurrentValues.$fieldName) -or ` -not [System.String]::IsNullOrEmpty($DesiredValues.$fieldName)) { Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose $returnValue = $false } } 'Int32' { if (-not ($DesiredValues.$fieldName -eq 0) -or ` -not ($null -eq $CurrentValues.$fieldName)) { Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose $returnValue = $false } } { $_ -eq 'Int16' -or $_ -eq 'UInt16'} { if (-not ($DesiredValues.$fieldName -eq 0) -or ` -not ($null -eq $CurrentValues.$fieldName)) { Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose $returnValue = $false } } default { Write-Warning -Message ($script:localizedData.UnableToCompareProperty ` -f $fieldName, $desiredType.Name) $returnValue = $false } } } } } } } return $returnValue } # SIG # Begin signature block # MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBH3QB1YDOZOOwJ # V+KQVBF6PeRCZ7EOOlwmL67tD15ypKCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgAchP/Cco # Y2JAjut9Hy3IC65/Rf67ea66bxFJosTcn0EwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQCA7tKfqWfEy22S/6wr4OyvRkvECIOlgOTLpZGyP7iT # 16UqNis22r/tHfj0nQMVembrz5WOUvHZPrU72+fJAZqMdCB5H0wHmeBOmie166xn # CDvlSHtJr5sPZeRDNQFeJN2nN+I47eFwThr5ndx6Gp548DoyDWBnFQz2tgvVpIWA # TBFrWf8ZulTYcMH3QhzlgEmmgEsU2vT5nbBmmK2QICDzcWA3tIzHSQ7jQxqL+9Oj # 9EVvuJTf0e81f05xNUmunrwWuoHzIYOF7LmpZqWFp4m8rg5Davtum5Rq60TcofSH # gKzh10SD1+hAHm7/aqz640BMXsMJCBHofYDzXY0Un7sWoYIS8TCCEu0GCisGAQQB # gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIJeyBGsDoIyvzKdclhGjO92lMFh4eXEiupSWXMgA # yrYhAgZfYQdYOoMYEzIwMjAwOTIyMjIxOTUwLjk4M1owBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjpGODdBLUUzNzQtRDdCOTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABL7GnF3lWlBeHAAAA # AAEvMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTE5MTIxOTAxMTUwNloXDTIxMDMxNzAxMTUwNlowgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpGODdB # LUUzNzQtRDdCOTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKh8VkvVIwXD8sn0zT2n # EdyEZ9UNHY7ACbOZA4obAHvD1hauw9K1Z2lRWG+m8Ars9l35GoMXdPgshM3hZKQW # fhrLnF9/GDZoilhc2LhMqNPXs06rAJ8YODB6i0Cg1CFCYnyOYvywXKY3xGJN09Dg # PXWfczEm2P/a3rmrXMrK5EFc3ahxrC51c+UuAMKV9xJyzJVLShPwPBJl+CjdMDPJ # f24DZXIYec3gCN2xean1DFCI0gaqJprMeL4Om1KY2AZMIgBPEkoY1N7AI5e7ybkI # L8+Mz3inijb4rDTkXk86ztUwy4bdc1MyKe2j2odT+QIDA2+M8cMTIGlKn7EyD2NN # XU8CAwEAAaOCARswggEXMB0GA1UdDgQWBBSml/VRpBNFkAMDiqcoqWi85j/qljAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQB4q6ilv2SlGvJD/7dbfoIKZBO2i6YA # wckw57TpCrt2+SAx2dcF7JvRMCPhLCSgqjyNcJRs40cEXPbLdzZMJHzcv73AF7L6 # mWZXg2aBjG1Sc5qM4jjE/nwIX+C6/odm5/asU4JIlFCuUZjzqdir18HkRVQve2Hw # V0lCXHQs+V3m9DyyA9b6LSIk3GOFZu7F11Wyx/5dVXisPPTPwh9JXfMD9W173M1+ # ZZycmO03lUc4G1FilgpxWNdgWn/DO9ZhoW5yN6+BUddnJ4cCcCjcg8sB5rktPP8p # VZAQ7aUqkAeqo+FuCkAUAdJRESCpR5wgSPtVvFPMjONE36DbKtfzkfiHMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpG # ODdBLUUzNzQtRDdCOTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAM/CZCUpclQ9qfr/r3y9osIIPSmSggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOMUwAMwIhgPMjAyMDA5MjIyMjI1MDdaGA8yMDIwMDkyMzIyMjUwN1owdzA9Bgor # BgEEAYRZCgQBMS8wLTAKAgUA4xTAAwIBADAKAgEAAgIeewIB/zAHAgEAAgIQ0jAK # AgUA4xYRgwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAB7Ib0ORN4cnD/0V # iEDrzl7vxjx6981A8Z/8I+X0n7qQHdeGFJcXGEEO5pDD62VMfd28ZrxPWgMd0Chx # mj5EdvR3U6+gRYzyYY5Vi5srADcWf3pMoYsEQhZuYeohtFQuYFJt2t26GskSlEy3 # qXFkFqIoBCjz1SsmfA5nTtE2zXHSMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAEvsacXeVaUF4cAAAAAAS8wDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQgDY7vkk/uubZSKGprOnZ2WnFZqhRkEJ7KVJ5R6hogo/AwgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCBC5RecGZvugnvVXg80zlrGv1uV35LNk+H9dBj3 # ChFPvTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # L7GnF3lWlBeHAAAAAAEvMCIEIAt7o8jjQ4zf04GvFGrnuSSwfsToGMPMzYw+3a3k # vP0dMA0GCSqGSIb3DQEBCwUABIIBABIE9hcJhxcSYL9zQrK7AunfYI2qZBNrD9dB # FIYwBnLNOYAPgpjC5Nxrp/4fSrWPyWFkXzwB6rjofda3zWpOywWKMc4lK0yHRYrO # 7r0FmXd9zxdaJKpbHpc8xG2vb6pI9dh/eXFGNS8KRu+oKZFX2/l5IxRAz4QRMxq6 # /heHcZpWjeIknMhP+bMAqaMV9h3jEojCli0hdwawbTpq8Oyvo6wlwNqLvmtBc7Xs # 8of9f6I/zTCn6yhbyDEVXxPhn3qjrQzi790XXe3xvmAKkkEW/NO2ZOS/2EpfjFms # wYhh6tKYg2k2j6mfCGmwvx2cx32zR/m4/ieGClCeKPiRlPvgip0= # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/Modules/PowerShellGet.ResourceHelper/en-US/PowerShellGet.ResourceHelper.strings.psd1 ================================================ # # Copyright (c) Microsoft Corporation. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # culture = "en-US" ConvertFrom-StringData -StringData @' ###PSLOC InValidUri = InValid Uri: '{0}'. A sample valid uri: https://www.powershellgallery.com/api/v2/. PathDoesNotExist = Path: '{0}' does not exist. VersionError = MinimumVersion should be less than the MaximumVersion. The MinimumVersion or MaximumVersion cannot be used with the RequiredVersion in the same command. UnexpectedArgument = Unexpected argument type: '{0}'. SourceNotFound = Source '{0}' not found. Please make sure you register it. CallingFunction = Calling function '{0}'. PropertyTypeInvalidForDesiredValues = Property 'DesiredValues' must be either a [System.Collections.Hashtable], [CimInstance] or [PSBoundParametersDictionary]. The type detected was {0}. PropertyTypeInvalidForValuesToCheck = If 'DesiredValues' is a CimInstance, then property 'ValuesToCheck' must contain a value. PropertyValidationError = Expected to find an array value for property {0} in the current values, but it was either not present or was null. This has caused the test method to return false. PropertiesDoesNotMatch = Found an array for property {0} in the current values, but this array does not match the desired state. Details of the changes are below. PropertyThatDoesNotMatch = {0} - {1} ValueOfTypeDoesNotMatch = {0} value for property {1} does not match. Current state is '{2}' and desired state is '{3}'. UnableToCompareProperty = Unable to compare property {0} as the type {1} is not handled by the Test-SQLDSCParameterState cmdlet. ###PSLOC '@ # SIG # Begin signature block # MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBIoCdfhdi5Cjss # ngqx6qsfpPMDALYWOvgm/jaEU9DaSKCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgRtNNlbVF # dlkMIAy8Jt0BeSlkgxnR3ktF5+8UaxbiK1gwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQBikiCJbAZhOZ7PLbYTjUnSEz9oaiBq80NUlYUDz8p+ # uGQfC0LU5aqPrFJ2lwmhVkhLmfAaP3OseRq1JdSHe3xxBndAN9Oh2HTlXm47fL5H # zJmnEdFq5YaXoWBoUn1O7sE4zMWU+JQm3I9YtgHVSgGzxX9ao/BjFW2s3wtfcXmp # 3zhN+cbAfBY/8u6J4Zui1mYzlMplYSwb66ikLo6kkSs3OnKN9frtB0McoazPtOxn # M4bae/0xVVJyt7vT3Vru3bDBIFOmEHnYPAhdz+QDxS8wLXCnKNWgMizg3s/iFVDO # G1VtPErpnu9oraXqkmHOMy8naFl85xuR/hhRpkiooEfKoYIS8TCCEu0GCisGAQQB # gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIMtX3JJD1gPDRiGuV8rCirgOBq8Lt6eGHeZdjPKC # pH3qAgZfYRqWlCoYEzIwMjAwOTIyMjIxOTUyLjU2NlowBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjo0NjJGLUUzMTktM0YyMDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABJMvNAqEXcFyaAAAA # AAEkMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTE5MTIxOTAxMTQ1N1oXDTIxMDMxNzAxMTQ1N1owgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo0NjJG # LUUzMTktM0YyMDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJCbKjgNnhjvMlnRNAtx # 7X3N5ZZkSfUFULyWsVJ1pnyMsSITimg1q3OQ1Ikf0/3gg8UG5TIRm7wH8sjBtoB3 # nuzFz11CegIFcanYnt050JvnrUTKeAPUR5pLpTeP3QEgL+CWOc4lTg/XxjnQv01F # D7TTn9DEuO3kp0GQ87Mjd5ssxK0K1q4IWNFAyRpx5n8Vm3Vm1iiVL5FMDUKsl5G/ # SqQdiEDn8cqYbqWMVzWH94PdKdw1mIHToBRCNsR9BHHWzNkSS+R0WRipBSSorKT7 # cuLlEBYhDo8AY3uMGlv0kLRLHASZ+sz2nfkpW2CVt+bHhVmM6/5qiu2f7eYoTYJu # cFECAwEAAaOCARswggEXMB0GA1UdDgQWBBS7HdFyrGKIhDxvypLA1lD/wGRSsDAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBo3QzNuNRgzdflwA4U7f3e2CcGlwdg # 6ii498cBiSrTpWKO3qqz5pvgHAk4hh6y/FLY80R59inLwcVuyD24S3JEdSie4y1t # C5JptweR1qlxRJCRM4vG7nPtIC4eAMKcXgovu0mTFv7xpFAVpRuvuepR91gIde32 # 8lv1HTTJCV/LBBk83Xi7nCGPF59FxeIrcE32xt4YJgEpEAikeMqvWCTMyPqlmvx9 # J92fxU3cQcw2j2EWwqOD5T3Nz2HWfPV80sihD1A6Y5HhjpS9taDPs7CI58I211F3 # ysegNyOesG3MTrSJHyPMLKYFDxcG1neV0liktv+TW927sUOVczcSUhQLMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo0 # NjJGLUUzMTktM0YyMDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAlwPlNCq+Un54UfxLe/wKS1Xc4nqggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOMU004wIhgPMjAyMDA5MjIyMzQ3MjZaGA8yMDIwMDkyMzIzNDcyNlowdzA9Bgor # BgEEAYRZCgQBMS8wLTAKAgUA4xTTTgIBADAKAgEAAgIgZwIB/zAHAgEAAgIS3TAK # AgUA4xYkzgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAJQEuwCens9exo3y # oRx0EpWssZYEoYGDA5Vo9cCDxGOjhqFjMJu/dmJcoeWqqCmHy66EAonaDmopQyNg # lRRfmDjEiQhc8am2i9RlvZac+vEFY6AP1GPEj+toN+LsaPXRU53/6LK/MR3BkTCm # iOD4gr6KZBDzEEjT2HA2pS7pSHevMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAEky80CoRdwXJoAAAAAASQwDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQgjlF6gXuuf73GPP9MP86hJ0HAK3FFZWGekdUUKTNpWjkwgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCBiOOHoohqL+X7Xa/25jp1wTrQxYlYGLszis/nA # TirjIDCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # JMvNAqEXcFyaAAAAAAEkMCIEINH5vtu0jUcUjiR+HX2ME9dlInYB6kXi3msE3oBJ # sB5sMA0GCSqGSIb3DQEBCwUABIIBABh16uxHs4RlKV2M0xneFNXFfIQNS8R1n2eb # YRGKKZ9fTl1xaJvd7S6pCc5Rb8aBCmnXOpz5f/fB1EJgfhR4BIBZmDwWE8tppqVL # 1nEM7CZkWPmo001AQrX19vPIQ3ukJk/Wn/9cD83zxWGBLAw3+YIm7zfYZOPoif5p # r04h63L9MtLQI99OF9XQM+T7o5bz5jTOt6Bcw3vRPCY41l1SGTrWjL1QQXDbkuDv # 3VmM//FceCvjkqFcxYwC24sCKjnig2x9ujnG5C4IiB+pTsUdmrC+tneCXXQkOT6V # M5UZDs3GY4Yqt/1aFcr4EZE0VaZoSuZn8hKlyXxXJLM7UlCPLxc= # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/PSGet.Format.ps1xml ================================================ PSRepositoryItemInfo Microsoft.PowerShell.Commands.PSRepositoryItemInfo 20 35 20 Version Name Repository Description PSRepository Microsoft.PowerShell.Commands.PSRepository 25 20 Name InstallationPolicy SourceLocation PSScriptInfo Microsoft.PowerShell.Commands.PSScriptInfo 20 25 20 Version Name Author Description PSGetDscResourceInfo Microsoft.PowerShell.Commands.PSGetCommandInfo Microsoft.PowerShell.Commands.PSGetDscResourceInfo 35 10 35 Name Version ModuleName Repository PSGetRoleCapabilityInfo Microsoft.PowerShell.Commands.PSGetRoleCapabilityInfo 35 10 35 Name Version ModuleName Repository PSGetPath Microsoft.PowerShell.Commands.PSGetPath AllUsersModules AllUsersScripts CurrentUserModules CurrentUserScripts ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/PSGet.Resource.psd1 ================================================ ######################################################################################### # # Copyright (c) Microsoft Corporation. All rights reserved. # # Localized PSGet.Resource.psd1 # ######################################################################################### ConvertFrom-StringData @' ###PSLOC InstallModulewhatIfMessage=Version '{1}' of module '{0}' InstallScriptwhatIfMessage=Version '{1}' of script '{0}' UpdateModulewhatIfMessage=Version '__OLDVERSION__' of module '{0}', updating to version '{1}' UpdateScriptwhatIfMessage=Version '__OLDVERSION__' of script '{0}', updating to version '{1}' PublishModulewhatIfMessage=Version '{0}' of module '{1}' PublishScriptwhatIfMessage=Version '{0}' of script '{1}' NewScriptFileInfowhatIfMessage=Creating the '{0}' PowerShell Script file UpdateScriptFileInfowhatIfMessage=Updating the '{0}' PowerShell Script file NameShouldNotContainWildcardCharacters=The specified name '{0}' should not contain any wildcard characters, please correct it and try again. AllVersionsCannotBeUsedWithOtherVersionParameters=You cannot use the parameter AllVersions with RequiredVersion, MinimumVersion or MaximumVersion in the same command. VersionRangeAndRequiredVersionCannotBeSpecifiedTogether=You cannot use the parameters RequiredVersion and either MinimumVersion or MaximumVersion in the same command. Specify only one of these parameters in your command. RequiredVersionAllowedOnlyWithSingleModuleName=The RequiredVersion parameter is allowed only when a single module name is specified as the value of the Name parameter, without any wildcard characters. MinimumVersionIsGreaterThanMaximumVersion=The specified MinimumVersion '{0}' is greater than the specified MaximumVersion '{1}'. AllowPrereleaseRequiredToUsePrereleaseStringInVersion=The '-AllowPrerelease' parameter must be specified when using the Prerelease string in MinimumVersion, MaximumVersion, or RequiredVersion. UpdateModuleAdminPrivilegeRequiredForAllUsersScope=Administrator rights are required to update modules in '{0}'. Log on to the computer with an account that has Administrator rights, and then try again, or update '{1}' by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). InstallModuleAdminPrivilegeRequiredForAllUsersScope=Administrator rights are required to install modules in '{0}'. Log on to the computer with an account that has Administrator rights, and then try again, or install '{1}' by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). InstallScriptAdminPrivilegeRequiredForAllUsersScope=Administrator rights are required to install scripts in '{0}'. Log on to the computer with an account that has Administrator rights, and then try again, or install '{1}' by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). AdministratorRightsNeededOrSpecifyCurrentUserScope=Administrator rights are required to install or update. Log on to the computer with an account that has Administrator rights, and then try again, or install by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). VersionParametersAreAllowedOnlyWithSingleName=The RequiredVersion, MinimumVersion, MaximumVersion, AllVersions or AllowPrerelease parameters are allowed only when you specify a single name as the value of the Name parameter, without any wildcard characters. PathIsNotADirectory=The specified path '{0}' is not a valid directory. ModuleAlreadyInstalled=Version '{0}' of module '{1}' is already installed at '{2}'. To delete version '{3}' and install version '{4}', run Install-Module, and add the -Force parameter. ScriptAlreadyInstalled=Version '{0}' of script '{1}' is already installed at '{2}'. To delete version '{3}' and install version '{4}', run Install-Script, and add the -Force parameter. CommandAlreadyAvailable=A command with name '{0}' is already available on this system. This script '{0}' may override the existing command. If you still want to install this script '{0}', use -Force parameter. ModuleAlreadyInstalledSxS=Version '{0}' of module '{1}' is already installed at '{2}'. To install version '{3}', run Install-Module and add the -Force parameter, this command will install version '{5}' side-by-side with version '{4}'. ModuleAlreadyInstalledVerbose=Version '{0}' of module '{1}' is already installed at '{2}'. ScriptAlreadyInstalledVerbose=Version '{0}' of script '{1}' is already installed at '{2}'. ModuleWithRequiredVersionAlreadyInstalled=Version '{0}' of module '{1}' is already installed at '{2}'. To reinstall this version '{3}', run Install-Module or Updated-Module cmdlet with the -Force parameter. InvalidPSModule=The module '{0}' cannot be installed or updated because it is not a properly-formed module. InvalidPowerShellScriptFile=The script '{0}' cannot be installed or updated because it is not a properly-formed script. InvalidAuthenticodeSignature=The module '{0}' cannot be installed or updated because the Authenticode signature for the file '{1}' is not valid. ModuleNotInstalledOnThisMachine=Module '{0}' was not updated because no valid module was found in the module directory. Verify that the module is located in the folder specified by $env:PSModulePath. ScriptNotInstalledOnThisMachine=Script '{0}' was not updated because no valid script was found in the script directories '{1}' and '{2}'. AdminPrivilegesRequiredForUpdate=Module '{0}' (installed at '{1}') cannot be updated because Administrator rights are required to change that directory. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). AdminPrivilegesRequiredForScriptUpdate=Script '{0}' (installed at '{1}') cannot be updated because Administrator rights are required to change that script. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). ModuleNotInstalledUsingPowerShellGet=Module '{0}' was not installed by using Install-Module, so it cannot be updated. ScriptNotInstalledUsingPowerShellGet=Script '{0}' was not installed by using Install-Script, so it cannot be updated. DownloadingModuleFromGallery=Downloading module '{0}' with version '{1}' from the repository '{2}'. DownloadingScriptFromGallery=Downloading script '{0}' with version '{1}' from the repository '{2}'. NoUpdateAvailable=No updates were found for module '{0}'. NoScriptUpdateAvailable=No updates were found for script '{0}'. FoundModuleUpdate=An update for the module '{0}' was found with version '{1}'. FoundScriptUpdate=An update for the script '{0}' was found with version '{1}'. InvalidPSModuleDuringUpdate= Module '{0}' was not updated because the module in the repository '{1}' is not a valid Windows PowerShell module. ModuleGotUpdated=Module '{0}' has been updated successfully. TestingModuleInUse=Testing if the module to update is in use. ModuleDestination= The specified module will be installed in '{0}'. ScriptDestination= The specified script will be installed in '{0}' and its dependent modules will be installed in '{1}'. ModuleIsInUse=Module '{0}' is in currently in use or you don't have the required permissions. ModuleInstalledSuccessfully=Module '{0}' was installed successfully to path '{1}'. ModuleSavedSuccessfully=Module '{0}' was saved successfully to path '{1}'. ScriptInstalledSuccessfully=Script '{0}' was installed successfully to path '{1}'. ScriptSavedSuccessfully=Script '{0}' was saved successfully to path '{1}'. CheckingForModuleUpdate= Checking for updates for module '{0}'. CheckingForScriptUpdate= Checking for updates for script '{0}'. ModuleInUseWithProcessDetails=The version '{0}' of module '{1}' is currently in use. Retry the operation after closing the following applications: '{2}'. ModuleVersionInUse=The version '{0}' of module '{1}' is currently in use. Retry the operation after closing the applications. ModuleNotAvailableLocally=The specified module '{0}' was not published because no module with that name was found in any module directory. InvalidModulePathToPublish=The specified module with path '{0}' was not published because no valid module was found with that path. ModuleWithRequiredVersionNotAvailableLocally= The specified module '{0}' with version '{1}' was not published because no module with that name and version was found in any module directory. AmbiguousModuleName=Modules with the name '{0}' are available under multiple paths. Add the -RequiredVersion parameter or the -Path parameter to specify the module to publish. AmbiguousModulePath=Multiple versions are available under the specified module path '{0}'. Specify the full path to the module to be published. PublishModuleLocation=Module '{0}' was found in '{1}'. InvalidModuleToPublish=Module '{0}' cannot be published because it does not have a module manifest file. Run New-ModuleManifest -Path to create a module manifest with metadata before publishing. MissingRequiredManifestKeys=Module '{0}' cannot be published because it is missing required metadata. Verify that the module manifest specifies Description and Author. InvalidCharactersInPrereleaseString=The Prerelease string '{0}' contains invalid characters. Please ensure that only characters 'a-zA-Z0-9' and possibly hyphen ('-') at the beginning are in the Prerelease string. IncorrectVersionPartsCountForPrereleaseStringUsage=Version '{0}' must have exactly 3 parts for a Prerelease string to be used. ModuleVersionShouldBeGreaterThanGalleryVersion=Module '{0}' with version '{1}' cannot be published. The version must exceed the current version '{2}' that exists in the repository '{3}', or you must specify -Force. ModuleVersionIsAlreadyAvailableInTheGallery=The module '{0}' with version '{1}' cannot be published as the current version '{2}' is already available in the repository '{3}'. CouldNotInstallNuGetProvider=NuGet provider is required to interact with NuGet-based repositories. Please ensure that '{0}' or newer version of NuGet provider is installed. CouldNotInstallNuGetExe=NuGet.exe version '{0}' or newer, or dotnet command version '{1}' or newer is required to interact with NuGet-based repositories. Please ensure that NuGet.exe or dotnet command is available under one of the paths specified in PATH environment variable value. CouldNotUpgradeNuGetExe=NuGet.exe version '{0}' or newer, or dotnet command version '{1}' or newer is required to interact with NuGet-based repositories. Please ensure that required version of NuGet.exe or dotnet command is available under one of the paths specified in PATH environment variable value. CouldNotFindDotnetCommand=For publish operations, dotnet command version '{0}' or newer is required to interact with the NuGet-based repositories. Please ensure that dotnet command version '{0}' or newer is installed and available under one of the paths specified in PATH environment variable value. You can also install the dotnet command by following the instructions specified at '{1}'. CouldNotInstallNuGetBinaries2=PowerShellGet requires NuGet.exe (or dotnet command) and NuGet provider version '{0}' or newer to interact with the NuGet-based repositories. Please ensure that '{0}' or newer version of NuGet provider is installed and NuGet.exe (or dotnet command) is available under one of the paths specified in PATH environment variable value. InstallNugetBinariesUpgradeShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe and minimum version '{1}' of NuGet provider to publish an item to NuGet-based repositories. The NuGet provider must be available in '{2}' or '{3}'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion {0} -Force'. NuGet.exe must be available in '{4}' or '{5}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://go.microsoft.com/fwlink/?linkid=875534. Do you want PowerShellGet to upgrade NuGet.exe to the latest version and install the NuGet provider now? InstallNuGetBinariesShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe and minimum version '{1}' of NuGet provider to publish an item to NuGet-based repositories. The NuGet provider must be available in '{3}' or '{3}'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion {0} -Force'. NuGet.exe must be available in '{4}' or '{5}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://go.microsoft.com/fwlink/?linkid=875534. Do you want PowerShellGet to install both the latest NuGet.exe and NuGet provider now? InstallNugetExeUpgradeShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in '{1}' or '{2}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://aka.ms/installing-powershellget . Do you want PowerShellGet to upgrade to the latest version of NuGet.exe now? InstallNuGetExeShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in '{1}' or '{2}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://aka.ms/installing-powershellget . Do you want PowerShellGet to install the latest version of NuGet.exe now? InstallNuGetProviderShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet provider to publish an item to NuGet-based repositories. The NuGet provider must be available in '{1}' or '{2}'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion {0} -Force'. Do you want PowerShellGet to install and import the NuGet provider now? InstallNuGetBinariesUpgradeShouldContinueCaption=NuGet.exe upgrade and NuGet provider installation are required to continue InstallNuGetBinariesShouldContinueCaption=NuGet.exe and NuGet provider installation are required to continue InstallNuGetExeUpgradeShouldContinueCaption=NuGet.exe upgrade is required to continue InstallNuGetExeShouldContinueCaption=NuGet.exe is required to continue InstallNuGetProviderShouldContinueCaption=NuGet provider is required to continue DownloadingNugetExe=Installing NuGet.exe. DownloadingNugetProvider=Installing NuGet provider. ModuleNotFound=Module '{0}' was not found. NoMatchFound=No match was found for the specified search criteria and module names '{0}'. NoMatchFoundForScriptName=No match was found for the specified search criteria and script names '{0}'. MatchInvalidType=The name '{0}' is a {1} not a {2}. FailedToCreateCompressedModule=Failed to generate the compressed file for module '{0}'. FailedToPublish=Failed to publish module '{0}': '{1}'. PublishedSuccessfully=Successfully published module '{0}' to the module publish location '{1}'. Please allow few minutes for '{2}' to show up in the search results. InvalidWebUri=The specified Uri '{0}' for parameter '{1}' is an invalid Web Uri. Please ensure that it meets the Web Uri requirements. RepositoryAlreadyRegistered=The repository could not be registered because there exists a registered repository with Name '{0}' and SourceLocation '{1}'. To register another repository with Name '{2}', please unregister the existing repository using the Unregister-PSRepository cmdlet. RepositoryToBeUnregisteredNotFound=The repository '{0}' was not removed because no repository was found with that name. Please run Get-PSRepository and ensure that a repository of that name is present. RepositoryCannotBeUnregistered=The specified repository '{0}' cannot be unregistered. RepositoryNotFound=No repository with the name '{0}' was found. PSGalleryNotFound=Unable to find repository '{0}'. Use Get-PSRepository to see all available repositories. Try again after specifying a valid repository name. You can use 'Register-PSRepository -Default' to register the PSGallery repository. ParameterIsNotAllowedWithPSGallery=The PSGallery repository has pre-defined locations. The '{0}' parameter is not allowed, try again after removing the '{0}' parameter. UseDefaultParameterSetOnRegisterPSRepository=Use 'Register-PSRepository -Default' to register the PSGallery repository. RepositoryNameContainsWildCards=The repository name '{0}' should not have wildcards, correct it and try again. InvalidRepository=The specified repository '{0}' is not a valid registered repository name. Please ensure that '{1}' is a registered repository. RepositoryCannotBeRegistered=The specified repository '{0}' is unauthorized and cannot be registered. Try running with -Credential. RepositoryRegistered=Successfully registered the repository '{0}' with source location '{1}'. RepositoryUnregistered=Successfully unregistered the repository '{0}'. PSGalleryPublishLocationIsMissing=The specified repository '{0}' does not have a valid PublishLocation. Retry after setting the PublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. PSRepositoryScriptPublishLocationIsMissing=The specified repository '{0}' does not have a valid ScriptPublishLocation. Retry after setting the ScriptPublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. ScriptSourceLocationIsMissing=The specified repository '{0}' does not have a valid ScriptSourceLocation. Retry after setting the ScriptSourceLocation for repository '{0}' to a valid NuGet endpoint for scripts using the Set-PSRepository cmdlet. PublishModuleSupportsOnlyNuGetBasedPublishLocations=Publish-Module only supports the NuGet-based publish locations. The PublishLocation '{0}' of the repository '{1}' is not a NuGet-based publish location. Retry after setting the PublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. PublishScriptSupportsOnlyNuGetBasedPublishLocations=Publish-Script only supports the NuGet-based publish locations. The ScriptPublishLocation '{0}' of the repository '{1}' is not a NuGet-based publish location. Retry after setting the ScriptPublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. DynamicParameterHelpMessage=The dynamic parameter '{0}' is required for Find-Module and Install-Module when using the PackageManagement provider '{1}' and source location '{2}'. Please enter your value for the '{3}' dynamic parameter: ProviderApiDebugMessage=In PowerShellGet Provider - '{0}'. ModuleUninstallNotSupported=Module uninstallation is not supported. To remove a module, please delete the module folder. FastPackageReference=The FastPackageReference is '{0}'. PackageManagementProviderIsNotAvailable=The specified PackageManagement provider '{0}' is not available. SpecifiedSourceName=Using the specified source names : '{0}'. SpecifiedLocationAndOGP=The specified Location is '{0}' and PackageManagementProvider is '{1}'. NoSourceNameIsSpecified=The -Repository parameter was not specified. PowerShellGet will use all of the registered repositories. GettingPackageManagementProviderObject=Getting the provider object for the PackageManagement Provider '{0}'. InvalidInputObjectValue=Invalid value is specified for InputObject parameter. SpecifiedInstallationScope=The installation scope is specified to be '{0}'. SourceLocationValueForPSGalleryCannotBeChanged=The SourceLocation value for the PSGallery repository can not be changed. PublishLocationValueForPSGalleryCannotBeChanged=The PublishLocation value for the PSGallery repository can not be changed. SpecifiedProviderName=The specified PackageManagement provider name '{0}'. ProviderNameNotSpecified=User did not specify the PackageManagement provider name, trying with the provider name '{0}'. SpecifiedProviderNotAvailable=The specified PackageManagement provider '{0}' is not available. SpecifiedProviderDoesnotSupportPSModules=The specified PackageManagement Provider '{0}' does not support PowerShell Modules. PackageManagement Providers must support the 'supports-powershell-modules' feature. PollingPackageManagementProvidersForLocation=Polling available PackageManagement Providers to find one that can support the specified source location '{0}'. PollingSingleProviderForLocation=Resolving the source location '{0}' with PackageManagement Provider '{1}'. FoundProviderForLocation=The PackageManagement provider '{0}' supports the source location '{1}'. SpecifiedLocationCannotBeRegistered=The specified location '{0}' cannot be registered. RepositoryDetails=Repository details, Name = '{0}', Location = '{1}'; IsTrusted = '{2}'; IsRegistered = '{3}'. NotSupportedPowerShellGetFormatVersion=The specified module '{0}' with PowerShellGetFormatVersion '{1}' is not supported by the current version of PowerShellGet. Get the latest version of the PowerShellGet module to install this module, '{2}'. NotSupportedPowerShellGetFormatVersionScripts=The specified script '{0}' with PowerShellGetFormatVersion '{1}' is not supported by the current version of PowerShellGet. Get the latest version of the PowerShellGet module to install this script, '{2}'. PathNotFound=Cannot find the path '{0}' because it does not exist. ModuleIsNotTrusted=Untrusted module '{0}'. ScriptIsNotTrusted=Untrusted script '{0}'. SkippedModuleDependency=Because dependent module '{0}' was skipped in the module dependencies list, users might not know how to install it. MissingExternallyManagedModuleDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current module '{1}', ensure that its dependent module '{2}' is installed. ExternallyManagedModuleDependencyIsInstalled=The externally managed, dependent module '{0}' is already installed on this computer. ScriptMissingExternallyManagedModuleDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current script '{1}', ensure that its dependent module '{2}' is installed. ScriptMissingExternallyManagedScriptDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current script '{1}', ensure that its dependent script '{2}' is installed. ScriptExternallyManagedScriptDependencyIsInstalled=The externally managed, dependent script '{0}' is already installed on this computer. UnableToResolveModuleDependency=PowerShellGet cannot resolve the module dependency '{0}' of the module '{1}' on the repository '{2}'. Verify that the dependent module '{3}' is available in the repository '{4}'. If this dependent module '{5}' is managed externally, add it to the ExternalModuleDependencies entry in the PSData section of the module manifest. FindingModuleDependencies=Finding module dependencies for version '{1}' of the module '{0}' from repository '{2}'. InstallingDependencyModule=Installing the dependency module '{0}' with version '{1}' for the module '{2}'. InstallingDependencyScript=Installing the dependency script '{0}' with version '{1}' for the script '{2}'. SavingDependencyModule=Saving the dependency module '{0}' with version '{1}' for the module '{2}'. SavingDependencyScript=Saving the dependency script '{0}' with version '{1}' for the script '{2}'. ModuleUninstallationSucceeded=Successfully uninstalled the module '{0}' from module base '{1}'. ScriptUninstallationSucceeded=Successfully uninstalled the script '{0}' from script base '{1}'. AdminPrivilegesRequiredForUninstall=You cannot uninstall the module '{0}' from '{1}' because Administrator rights are required to uninstall from that folder. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). AdminPrivilegesRequiredForScriptUninstall=You cannot uninstall the script '{0}' from '{1}' because Administrator rights are required to uninstall from that folder. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). ModuleUninstallationNotPossibleAsItIsNotInstalledUsingPowerShellGet=Module '{0}' was not installed on this computer by using either the PowerShellGet cmdlets or the PowerShellGet provider, so it cannot be uninstalled. ScriptUninstallationNotPossibleAsItIsNotInstalledUsingPowerShellGet=Script '{0}' was not installed on this computer by using either the PowerShellGet cmdlets or the PowerShellGet provider, so it cannot be uninstalled. UnableToUninstallModuleVersion=The module '{0}' of version '{1}' in module base folder '{2}' was installed without side-by-side version support. Some versions are installed in this module base with side-by-side version support. Uninstall other versions of this module before uninstalling the most current version. UnableToUninstallAsOtherModulesNeedThisModule=The module '{0}' of version '{1}' in module base folder '{2}' cannot be uninstalled, because one or more other modules '{3}' are dependent on this module. Uninstall the modules that depend on this module before uninstalling module '{4}'. UnableToUninstallAsOtherScriptsNeedThisScript=The script '{0}' of version '{1}' in script base folder '{2}' cannot be uninstalled, because one or more other scripts '{3}' are dependent on this script. Uninstall the scripts that depend on this script before uninstalling script '{4}'. RepositoryIsNotTrusted=Untrusted repository QueryInstallUntrustedPackage=You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from '{1}'? QueryInstallUntrustedScriptPackage=You are installing the scripts from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the scripts from '{1}'? QuerySaveUntrustedPackage=You are downloading the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to download the modules from '{1}'? QuerySaveUntrustedScriptPackage=You are downloading the scripts from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to download the scripts from '{1}'? SourceNotFound=Unable to find repository '{0}'. Use Get-PSRepository to see all available repositories. PSGalleryApiV2Deprecated=PowerShell Gallery v2 has been deprecated. Please run 'Update-Module -Name PowerShellGet' to update to PowerShell Gallery v3. For more information, please visit our website at 'https://www.powershellgallery.com'. PSGalleryApiV2Discontinued=PowerShell Gallery v2 has been discontinued. Please run 'Update-Module -Name PowerShellGet' to update to PowerShell Gallery v3. For more information, please visit our website at 'https://www.powershellgallery.com'. PowerShellGalleryUnavailable=PowerShell Gallery is currently unavailable. Please try again later. PowerShellGetModuleIsNotInstalledProperly=The PowerShellGet module was not installed properly. Be sure that only one instance or version of the PowerShellGet module is installed in the path '{0}'. PowerShelLGetModuleGotUpdated=The PowerShellGet module was updated successfully. Restart the process to use the updated version of the PowerShellGet module. TagsShouldBeIncludedInManifestFile=Tags are now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' in '{1}' with the newest tag changes. You can run Update-ModuleManifest -Tags to update the manifest with tags. ReleaseNotesShouldBeIncludedInManifestFile=ReleaseNotes is now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' in '{1}' with the newest ReleaseNotes changes. You can run Update-ModuleManifest -ReleaseNotes to update the manifest with ReleaseNotes. LicenseUriShouldBeIncludedInManifestFile=LicenseUri is now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' with the newest LicenseUri changes. You can run Update-ModuleManifest -LicenseUri to update the manifest with LicenseUri. IconUriShouldBeIncludedInManifestFile=IconUri is now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' in '{1}' with the newest IconUri changes. You can run Update-ModuleManifest -IconUri to update the manifest with IconUri. ProjectUriShouldBeIncludedInManifestFile=ProjectUri is now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' in '{1}' with the newest ProjectUri changes. You can run Update-ModuleManifest -ProjectUri to update the manifest with ProjectUri. ShouldIncludeFunctionsToExport=This module '{0}' has exported functions. As a best practice, include exported functions in the module manifest file(.psd1). You can run Update-ModuleManifest -FunctionsToExport to update the manifest with ExportedFunctions field. ShouldIncludeCmdletsToExport=This module '{0}' has exported cmdlets. As a best practice, include exported cmdlets in the module manifest file(.psd1). You can run Update-ModuleManifest -CmdletsToExport to update the manifest with ExportedCmdlets field. ShouldIncludeDscResourcesToExport=This module '{0}' has exported DscResources. As a best practice, include exported DSC resources in the module manifest file(.psd1). If your PowerShell version is higher than 5.0, run Update-ModuleManifest -DscResourcesToExport to update the manifest with ExportedDscResources field. UpdateModuleManifestPathCannotFound=Cannot load the manifest file '{0}' properly. Please specify the correct manifest path. UpdatedModuleManifestNotValid=Cannot update the manifest file '{0}' because the manifest is not valid. Verify that the manifest file is valid, and then try again.'{1}' ExportedDscResourcesNotSupportedOnLowerPowerShellVersion=The ExportedDscResources property is not supported in module manifests on PowerShell versions that are older than 5.0. Remove the value for the parameter 'DscResourcesToExport', and then try again. CompatiblePSEditionsNotSupportedOnLowerPowerShellVersion=The CompatiblePSEditions property is not supported in module manifests on PowerShell versions that are older than 5.1. Remove the value for the parameter 'CompatiblePSEditions', and then try again. ExternalModuleDependenciesNotSpecifiedInRequiredOrNestedModules='{0}' is listed in ExternalModuleDependencies, but it is not found in either the RequiredModules or NestedModules properties. Verify that this module is required for ExternalModuleDependencies, and then add it to NestedModules or RequiredModules. TestModuleManifestFail=Cannot update the manifest properly. '{0}' PackageManagementProvidersNotInModuleBaseFolder=PackageManagementProvider '{0}' is not found in the module base '{1}'. Verify that the PackageManagementProvider specified is within the module base. UpdateManifestContentMessage=Update manifest file with new contents: InvalidPackageManagementProviderValue=The PackageManagementProvider value cannot be '{0}'. Valid values for provider names include '{1}', and the default value for this parameter is '{2}'. PowerShellGetUpdateIsNotSupportedOnLowerPSVersions=Self update of the PowerShellGet module is supported only in PowerShell 5.0 and newer releases. It is not supported in PowerShell 3.0 or 4.0. ScriptVersionShouldBeGreaterThanGalleryVersion=Script '{0}' with version '{1}' cannot be published. The version must exceed the current version '{2}' that exists in the repository '{3}', or you must specify -Force. ScriptVersionIsAlreadyAvailableInTheGallery=The script '{0}' with version '{1}' cannot be published as the current version '{2}' is already available in the repository '{3}'. ScriptPrereleaseStringShouldBeGreaterThanGalleryPrereleaseString=Script '{0}' with version '{1}' and prerelease '{2}' cannot be published. The prerelease string must exceed the current prerelease string '{3}' that exists in the repository '{4}', or you must specify -Force. ScriptParseError=The specified script file '{0}' has parse errors, try again after fixing the parse errors. InvalidScriptToPublish=Script file '{0}' cannot be published because it does not have the required script metadata. Run Update-ScriptFileInfo -Path '{1}' to add the script metadata. FailedToCreateCompressedScript=Failed to generate the compressed file for script '{0}'. FailedToPublishScript=Failed to publish script '{0}': '{1}'. PublishedScriptSuccessfully=Successfully published script '{0}' to the publish location '{1}'. Please allow few minutes for '{2}' to show up in the search results. UnableToResolveScriptDependency=PowerShellGet cannot resolve the {0} dependency '{1}' of the script '{2}' on the repository '{3}'. Verify that the dependent {0} '{1}' is available in the repository '{3}'. If this dependent {0} '{1}' is managed externally, add it to the '{4}' entry in the script metadata. InvalidVersion=Cannot convert value '{0}' to type 'System.Version'. InvalidGuid=Cannot convert value '{0}' to type 'System.Guid'. InvalidParameterValue=The specified value '{0}' for the parameter '{1}' is invalid. Ensure that it does not contain '<#' or '#>'. MissingPSScriptInfo=PSScriptInfo is not specified in the script file '{0}'. You can use the Update-ScriptFileInfo with -Force or New-ScriptFileInfo cmdlet to add the PSScriptInfo to the script file. MissingRequiredPSScriptInfoProperties=Script '{0}' is missing required metadata properties. Verify that the script file has Version, Guid, Description and Author properties. You can use the Update-ScriptFileInfo or New-ScriptFileInfo cmdlet to add or update the PSScriptInfo to the script file. SkippedScriptDependency=Because dependent script '{0}' was skipped in the script dependencies list, users might not know how to install it. SourceLocationPathsForModulesAndScriptsShouldBeEqual=SourceLocation '{0}' and ScriptSourceLocation '{1}' should be same for SMB Share or Local directory based repositories. PublishLocationPathsForModulesAndScriptsShouldBeEqual=PublishLocation '{0}' and ScriptPublishLocation '{1}' should be same for SMB Share or Local directory based repositories. SpecifiedNameIsAlearyUsed=The specified name '{0}' is already used for a different item on the specified repository '{1}'. Run '{2} -Name {0} -Repository {1}' to check whether the specified name '{0}' is already taken. InvalidScriptFilePath=The script file path '{0}' is not valid. The value of the Path argument must resolve to a single file that has a '.ps1' extension. Change the value of the Path argument to point to a valid ps1 file, and then try again. NuGetApiKeyIsRequiredForNuGetBasedGalleryService=NuGetApiKey is required for publishing a module or script file to the specified repository '{0}' whose publish location is '{1}'. Try again after specifying a valid value for the NuGetApiKey parameter. To get your API key, view your profile page. ScriptFileExist=The specified script file '{0}' already exists. InvalidEnvironmentVariableName=The specified environment variable name '{0}' exceeded the allowed limit of '{1}' characters. PublishLocation=Publish Location:'{0}'. ScriptPATHPromptCaption=PATH Environment Variable Change ScriptPATHPromptQuery=Your system has not been configured with a default script installation path yet, which means you can only run a script by specifying the full path to the script file. This action places the script into the folder '{0}', and adds that folder to your PATH environment variable. Do you want to add the script installation path '{0}' to the PATH environment variable? AddedScopePathToProcessSpecificPATHVariable=Added scripts installation location '{0}' for '{1}' scope to process specific PATH environment variable. AddedScopePathToPATHVariable=Added scripts installation location '{0}' for '{1}' scope to PATH environment variable. FilePathInFileListNotWithinModuleBase=Path '{0}' defined in FileList is not within module base '{1}'. Provide the correct FileList parameters and then try again. ManifestFileReadWritePermissionDenied=The current user does not have read-write permissions for the file:'{0}'. Check the file permissions and then try again. MissingTheRequiredPathOrPassThruParameter=The Path or PassThru parameter is required for creating the script file info. A new script file will be created with the script file info when the Path parameter is specified. Script file info will be returned if the PassThru parameter is specified. Try again after specifying the required parameter. DescriptionParameterIsMissingForAddingTheScriptFileInfo=Description parameter is missing for adding the metadata to the script file. Try again after specifying the description. UnableToAddPSScriptInfo=Unable to add PSScriptInfo to the script file '{0}'. You can use the New-ScriptFileInfo cmdlet to add the metadata to the existing script file. RegisterVSTSFeedAsNuGetPackageSource=Publishing to a VSTS package management feed '{0}' requires it to be registered as a NuGet package source. Retry after adding this source '{0}' as NuGet package source by following the instructions specified at '{1}' InvalidModuleAuthenticodeSignature=The module '{0}' cannot be installed or updated because the authenticode signature of the file '{1}' is not valid. InvalidCatalogSignature=The module '{0}' cannot be installed because the catalog signature in '{1}' does not match the hash generated from the module. AuthenticodeIssuerMismatch=Authenticode issuer '{0}' of the new module '{1}' with version '{2}' from root certificate authority '{3}' is not matching with the authenticode issuer '{4}' of the previously-installed module '{5}' with version '{6}' from root certificate authority '{7}'. If you still want to install or update, use -SkipPublisherCheck parameter. ModuleCommandAlreadyAvailable=The following commands are already available on this system:'{0}'. This module '{1}' may override the existing commands. If you still want to install this module '{1}', use -AllowClobber parameter. CatalogFileFound=Found the catalog file '{0}' in the module '{1}' contents. CatalogFileNotFoundInAvailableModule=Catalog file '{0}' is not found in the contents of the previously-installed module '{1}' with the same name. CatalogFileNotFoundInNewModule=Catalog file '{0}' is not found in the contents of the module '{1}' being installed. ValidAuthenticodeSignature=Valid authenticode signature found in the catalog file '{0}' for the module '{1}'. ValidAuthenticodeSignatureInFile=Valid authenticode signature found in the file '{0}' for the module '{1}'. ValidatingCatalogSignature=Validating the '{0}' module files for catalog signing using the catalog file '{1}'. AuthenticodeIssuerMatch=Authenticode issuer '{0}' of the new module '{1}' with version '{2}' matches with the authenticode issuer '{3}' of the previously-installed module '{4}' with version '{5}'. ValidCatalogSignature=The catalog signature in '{0}' of the module '{1}' is valid and matches with the hash generated from the module contents. SkippingPublisherCheck=Skipping the Publisher check for the version '{0}' of module '{1}'. SourceModuleDetailsForPublisherValidation=For publisher validation, using the previously-installed module '{0}' with version '{1}' under '{2}' with publisher name '{3}' from root certificate authority '{4}'. Is this module signed by Microsoft: '{5}'. NewModuleVersionDetailsForPublisherValidation=For publisher validation, current module '{0}' with version '{1}' with publisher name '{2}' from root certificate authority '{3}'. Is this module signed by Microsoft: '{4}'. PublishersMatch=Publisher '{0}' of the new module '{1}' with version '{2}' matches with the publisher '{3}' of the previously-installed module '{4}' with version '{5}'. Both versions are signed with a Microsoft root certificate. PublishersMismatch=A Microsoft-signed module named '{0}' with version '{1}' that was previously installed conflicts with the new module '{2}' from publisher '{3}' with version '{4}'. Installing the new module may result in system instability. If you still want to install or update, use -SkipPublisherCheck parameter. ModuleIsNotCatalogSigned=The version '{0}' of the module '{1}' being installed is not catalog signed. Ensure that the version '{0}' of the module '{1}' has the catalog file '{2}' and signed with the same publisher '{3}' as the previously-installed module '{1}' with version '{4}' under the directory '{5}'. If you still want to install or update, use -SkipPublisherCheck parameter. SentEnvironmentVariableChangeMessage=Successfully broadcasted the Environment variable changes. UnableToSendEnvironmentVariableChangeMessage=Error in broadcasting the Environment variable changes. LicenseUriNotSpecified='LicenseUri' is not specified. 'LicenseUri' must be provided when user license acceptance is required. LicenseTxtNotFound=License.txt not Found. License.txt must be provided when user license acceptance is required. LicenseTxtEmpty=License.txt is empty. requireLicenseAcceptanceNotSupported=Require License Acceptance is not supported on Format version '{0}'. AcceptanceLicenseQuery=Do you accept the license terms for module '{0}'. ForceAcceptLicense=License Acceptance is required for module '{0}'. Please specify '-AcceptLicense' to perform this operation. InvalidValueBoolean=The specified value '{0}' for the parameter '{1}' is invalid. It should be $true or $false. UserDeclinedLicenseAcceptance=User declined license acceptance. AcceptLicense=License Acceptance RequiredScriptVersion=REQUIREDSCRIPTS: Required version of script '{0}' is '{1}'. RequiredScriptVersoinFormat=, :, :[], :[,], :[,] FailedToParseRequiredScripts=Cannot parse REQUIREDSCRIPTS '{0}'. Acceptable formats are: '{1}'. FailedToParseRequiredScriptsVersion=Version format error: {0}, '{1}'. Acceptable formats are: '{2}'. PublishersMismatchAsWarning=Module '{0}' version '{1}' published by '{2}' will be superceded by version '{3}' published by '{4}'. If you do not trust the new publisher, uninstall the module. UnableToDownloadThePackage=The PackageManagement provider '{0}' is unable to download the package '{1}' version '{2}' to '{3}' path. ValidatingTheModule=Validating the '{0}' module contents under '{1}' path. ModuleValidationFailed=Unable to validate the '{0}' module contents under '{1}' path. ValidatedModuleManifestFile=Test-ModuleManifest successfully validated the module manifest file '{0}'. ValidateModuleAuthenticodeSignature=Validating the authenticode signature and publisher of the catalog file or module manifest file of the module '{0}'. ValidateModuleCommandAlreadyAvailable=Checking for possible command collisions for the module '{0}' commands. UnauthorizedAccessError=Access to the path '{0}' is denied. ###PSLOC '@ # SIG # Begin signature block # MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAUMj4wb0r0V933 # Kxwf6eV800i5N8GnGMPseytvd1lnEKCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgLIu7f8le # gQ0ndTd8gyAEe0fnNBrhCsEUfHD+upEyb7owQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQCWjkfbjU+QuaidvjCSfT9ZWmVRt7eVzD7wasyZEXwC # uPWX5GwoR4xPMYzA/ohAElfSdrP6HWH9R+2sdXnUCJK+7w6N9wbB+S9Da0r3UcHj # acV7JEftJK6ZwZ9EyebTgHv6Oa0xhuaRh/BINH4OhfIZEy3A2Lg4KhvNMjGKHTi2 # agmGkKLUqySkyv48c/HCApLbZ3ugNqI06C284kostGCqpuSg6HRDcGPDzZSkT6l4 # u1veNMmcpeGzd3NTNy5fkt8jQt3OUQ9NmfYN5nxh1BgDB1kyDm5lWMiZqFqlLS9T # sG/jgYnqnprYVqTa8rYsSdmn8y1idSkQ1M715PQlOYNuoYIS8TCCEu0GCisGAQQB # gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIOjiV1RbUVB0CleqHpkpsACXwys4XtATfTNyD7Bu # Zv+1AgZfYPphXsEYEzIwMjAwOTIyMjIxOTUxLjg2N1owBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjowQTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABJy9uo++RqBmoAAAA # AAEnMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTE5MTIxOTAxMTQ1OVoXDTIxMDMxNzAxMTQ1OVowgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowQTU2 # LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPgB3nERnk6fS40vvWeD # 3HCgM9Ep4xTIQiPnJXE9E+HkZVtTsPemoOyhfNAyF95E/rUvXOVTUcJFL7Xb16jT # KPXONsCWY8DCixSDIiid6xa30TiEWVcIZRwiDlcx29D467OTav5rA1G6TwAEY5rQ # jhUHLrOoJgfJfakZq6IHjd+slI0/qlys7QIGakFk2OB6mh/ln/nS8G4kNRK6Do4g # xDtnBSFLNfhsSZlRSMDJwFvrZ2FCkaoexd7rKlUNOAAScY411IEqQeI1PwfRm3aW # bS8IvAfJPC2Ah2LrtP8sKn5faaU8epexje7vZfcZif/cbxgUKStJzqbdvTBNc93n # /Z8CAwEAAaOCARswggEXMB0GA1UdDgQWBBTl9JZVgF85MSRbYlOJXbhY022V8jAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAKyo180VXHBqVnjZwQy7NlzXbo2+W5 # qfHxR7ANV5RBkRkdGamkwUcDNL+DpHObFPJHa0oTeYKE0Zbl1MvvfS8RtGGdhGYG # CJf+BPd/gBCs4+dkZdjvOzNyuVuDPGlqQ5f7HS7iuQ/cCyGHcHYJ0nXVewF2Lk+J # lrWykHpTlLwPXmCpNR+gieItPi/UMF2RYTGwojW+yIVwNyMYnjFGUxEX5/DtJjRZ # mg7PBHMrENN2DgO6wBelp4ptyH2KK2EsWT+8jFCuoKv+eJby0QD55LN5f8SrUPRn # K86fh7aVOfCglQofo5ABZIGiDIrg4JsV4k6p0oBSIFOAcqRAhiH+1spCMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow # QTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAs5W4TmyDHMRM7iz6mgGojqvXHzOggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOMUsu8wIhgPMjAyMDA5MjIyMTI5MTlaGA8yMDIwMDkyMzIxMjkxOVowdzA9Bgor # BgEEAYRZCgQBMS8wLTAKAgUA4xSy7wIBADAKAgEAAgIVPgIB/zAHAgEAAgIRtjAK # AgUA4xYEbwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAEMD4esQRMLwQdhk # Co1zgvmclcwl3lYYpk1oMh1ndsU3+97Rt6FV3adS4Hezc/K94oQKjcxtMVzLzQhG # agM6XlqB31VD8n2nxVuaWD1yp2jm/0IvfL9nFMHJRhgANMiBdHqvqNrd86c/Kryq # sI0Ch0sOx9wg3BozzqQhmdNjf9c6MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAEnL26j75GoGagAAAAAAScwDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQg6wLE4god37E/zRxYUqM58DbGQBLJxEvt6c6xU1jsZqYwgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCAbkuhLEoYdahb/BUyVszO2VDi6kB3MSaof/+8u # 7SM+IjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # Jy9uo++RqBmoAAAAAAEnMCIEIK4r6N3NISekswMCG1kSBJCCCePrlLDQWbMKz0wt # Lj6CMA0GCSqGSIb3DQEBCwUABIIBACY3cZHtQ2k6zfVG4NotND24Zp0mlZAFe4A+ # BFDISaaIXS86mO142Y9Hkm9rAMBCIJbKB/Yj1yf1gbygqNCoJDHFxZbdmx5XbShg # PVfAQl00TuyQ86VYnFJtvdDODUnwuxErAFR1zyiGGBKYtQWhXNRmbhb1kRkWp5Fg # s9mwzZUoGI2UzXDtYb6HkjNIOMxCbWW/27YfCBnOkiP2tJMIU79R4xY4vwQeWkKW # dgtyHv+d7eBOd1aOi8HvqCstH6x06RC3DsYfZ/NbYSGuZpjSaSDDC7A1KvdPo6ti # UYayEGi9iXfqnAv4Iyzo3OWIN2M2iqepP2xwL8Ne2q1ImLpiZF0= # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/PSGetModuleInfo.xml ================================================ Microsoft.PowerShell.Commands.PSRepositoryItemInfo System.Management.Automation.PSCustomObject System.Object PowerShellGet 2.2.5 Module PowerShell module with commands for discovering, installing, updating and publishing the PowerShell artifacts like Modules, DSC Resources, Role Capabilities and Scripts. Microsoft Corporation System.Object[] System.Array System.Object PowerShellTeam alerickson anamnavi (c) Microsoft Corporation. All rights reserved.
2020-09-22T22:42:00-07:00
https://go.microsoft.com/fwlink/?LinkId=829061 https://go.microsoft.com/fwlink/?LinkId=828955 Packagemanagement Provider PSEdition_Desktop PSEdition_Core Linux Mac PSModule System.Collections.Hashtable System.Object Command Find-Command Find-DSCResource Find-Module Find-RoleCapability Find-Script Get-CredsFromCredentialProvider Get-InstalledModule Get-InstalledScript Get-PSRepository Install-Module Install-Script New-ScriptFileInfo Publish-Module Publish-Script Register-PSRepository Save-Module Save-Script Set-PSRepository Test-ScriptFileInfo Uninstall-Module Uninstall-Script Unregister-PSRepository Update-Module Update-ModuleManifest Update-Script Update-ScriptFileInfo Function Find-Command Find-DSCResource Find-Module Find-RoleCapability Find-Script Get-CredsFromCredentialProvider Get-InstalledModule Get-InstalledScript Get-PSRepository Install-Module Install-Script New-ScriptFileInfo Publish-Module Publish-Script Register-PSRepository Save-Module Save-Script Set-PSRepository Test-ScriptFileInfo Uninstall-Module Uninstall-Script Unregister-PSRepository Update-Module Update-ModuleManifest Update-Script Update-ScriptFileInfo Cmdlet DscResource PSModule PSRepository Workflow RoleCapability ### 2.2.5_x000D__x000A_- Security patch for code injection bug_x000D__x000A__x000D__x000A_### 2.2.4.1_x000D__x000A_- Remove catalog file_x000D__x000A__x000D__x000A_### 2.2.3_x000D__x000A_- Update `HelpInfoUri` to point to the latest content (#560)_x000D__x000A_- Improve discovery of usable nuget.exe binary (Thanks bwright86!) (#558)_x000D__x000A__x000D__x000A_### 2.2.2_x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Update casing of DscResources output_x000D__x000A__x000D__x000A_### 2.2.1_x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Allow DscResources to work on case sensitive platforms (#521)_x000D__x000A_- Fix for failure to return credential provider when using private feeds (#521)_x000D__x000A__x000D__x000A_## 2.2_x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Fix for prompting for credentials when passing in -Credential parameter when using Register-PSRepository_x000D__x000A__x000D__x000A_## 2.1.5_x000D__x000A_New Features_x000D__x000A__x000D__x000A_- Add and remove nuget based repositories as a nuget source when nuget client tool is installed (#498)_x000D__x000A__x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Fix for 'Failed to publish module' error thrown when publishing modules (#497)_x000D__x000A__x000D__x000A_## 2.1.4_x000D__x000A_- Fixed hang while publishing some packages (#478)_x000D__x000A__x000D__x000A_## 2.1.3_x000D__x000A_New Features_x000D__x000A__x000D__x000A_- Added -Scope parameter to Update-Module (Thanks @lwajswaj!) (#471)_x000D__x000A_- Added -Exclude parameter to Publish-Module (Thanks @Benny1007!) (#191)_x000D__x000A_- Added -SkipAutomaticTags parameter to Publish-Module (Thanks @awickham10!) (#452)_x000D__x000A__x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Fixed issue with finding modules using macOS and .NET Core 3.0_x000D__x000A__x000D__x000A_## 2.1.2_x000D__x000A__x000D__x000A_New Feature_x000D__x000A__x000D__x000A_- Added support for registering repositories with special characters_x000D__x000A__x000D__x000A_## 2.1.1_x000D__x000A__x000D__x000A_- Fix DSC resource folder structure_x000D__x000A__x000D__x000A_## 2.1.0_x000D__x000A__x000D__x000A_Breaking Change_x000D__x000A__x000D__x000A_- Default installation scope for Update-Module and Update-Script has changed to match Install-Module and Install-Script. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times._x000D__x000A_ For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser. (#421)_x000D__x000A__x000D__x000A_Bug Fixes_x000D__x000A__x000D__x000A_- Update-ModuleManifest no longer clears FunctionsToExport, AliasesToExport, nor NestModules (#415 & #425) (Thanks @pougetat and @tnieto88!)_x000D__x000A_- Update-Module no longer changes repository URL (#407)_x000D__x000A_- Update-ModuleManifest no longer preprends 'PSGet_' to module name (#403) (Thanks @ThePoShWolf)_x000D__x000A_- Update-ModuleManifest now throws error and fails to update when provided invalid entries (#398) (Thanks @pougetat!)_x000D__x000A_- Ignore files no longer being included when uploading modules (#396)_x000D__x000A__x000D__x000A_New Features_x000D__x000A__x000D__x000A_- New DSC resource, PSRepository (#426) (Thanks @johlju!)_x000D__x000A_- Piping of PS respositories (#420)_x000D__x000A_- utf8 support for .nuspec (#419)_x000D__x000A__x000D__x000A_## 2.0.4_x000D__x000A__x000D__x000A_Bug Fix_x000D__x000A_* Remove PSGallery availability checks (#374)_x000D__x000A__x000D__x000A_## 2.0.3_x000D__x000A__x000D__x000A_Bug fixes and Improvements_x000D__x000A_* Fix CommandAlreadyAvailable error for PackageManagement module (#333)_x000D__x000A_* Remove trailing whitespace when value is not provided for Get-PSScriptInfoString (#337) (Thanks @thomasrayner)_x000D__x000A_* Expanded aliases for improved readability (#338) (Thanks @lazywinadmin)_x000D__x000A_* Improvements for Catalog tests (#343)_x000D__x000A_* Fix Update-ScriptInfoFile to preserve PrivateData (#346) (Thanks @tnieto88)_x000D__x000A_* Import modules with many commands faster (#351)_x000D__x000A__x000D__x000A_New Features_x000D__x000A_* Tab completion for -Repository parameter (#339) and for Publish-Module -Name (#359) (Thanks @matt9ucci)_x000D__x000A__x000D__x000A_## 2.0.1_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Resolved Publish-Module doesn't report error but fails to publish module (#316)_x000D__x000A_- Resolved CommandAlreadyAvailable error while installing the latest version of PackageManagement module (#333)_x000D__x000A__x000D__x000A_## 2.0.0_x000D__x000A__x000D__x000A_Breaking Change_x000D__x000A_- Default installation scope for Install-Module, Install-Script, and Install-Package has changed. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times._x000D__x000A_ For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser._x000D__x000A__x000D__x000A_## 1.6.7_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Resolved Install/Save-Module error in PSCore 6.1.0-preview.4 on Ubuntu 18.04 OS (WSL/Azure) (#313)_x000D__x000A_- Updated error message in Save-Module cmdlet when the specified path is not accessible (#313)_x000D__x000A_- Added few additional verbose messages (#313)_x000D__x000A__x000D__x000A_## 1.6.6_x000D__x000A__x000D__x000A_Dependency Updates_x000D__x000A_* Add dependency on version 4.1.0 or newer of NuGet.exe_x000D__x000A_* Update NuGet.exe bootstrap URL to https://aka.ms/psget-nugetexe_x000D__x000A__x000D__x000A_Build and Code Cleanup Improvements_x000D__x000A_* Improved error handling in network connectivity tests._x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Change Update-ModuleManifest so that prefix is not added to CmdletsToExport._x000D__x000A_- Change Update-ModuleManifest so that parameters will not reset to default values._x000D__x000A_- Specify AllowPrereleseVersions provider option only when AllowPrerelease is specified on the PowerShellGet cmdlets._x000D__x000A__x000D__x000A_## 1.6.5_x000D__x000A__x000D__x000A_New features_x000D__x000A_* Allow Pester/PSReadline installation when signed by non-Microsoft certificate (#258)_x000D__x000A_ - Whitelist installation of non-Microsoft signed Pester and PSReadline over Microsoft signed Pester and PSReadline._x000D__x000A__x000D__x000A_Build and Code Cleanup Improvements_x000D__x000A_* Splitting of functions (#229) (Thanks @Benny1007)_x000D__x000A_ - Moves private functions into respective private folder._x000D__x000A_ - Moves public functions as defined in PSModule.psd1 into respective public folder._x000D__x000A_ - Removes all functions from PSModule.psm1 file._x000D__x000A_ - Dot sources the functions from PSModule.psm1 file._x000D__x000A_ - Uses Export-ModuleMember to export the public functions from PSModule.psm1 file._x000D__x000A__x000D__x000A_* Add build step to construct a single .psm1 file (#242) (Thanks @Benny1007)_x000D__x000A_ - Merged public and private functions into one .psm1 file to increase load time performance._x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Fix null parameter error caused by MinimumVersion in Publish-PackageUtility (#201)_x000D__x000A_- Change .ExternalHelp link from PSGet.psm1-help.xml to PSModule-help.xml in PSModule.psm1 file (#215)_x000D__x000A_- Change Publish-* to allow version comparison instead of string comparison (#219)_x000D__x000A_- Ensure Get-InstalledScript -RequiredVersion works when versions have a leading 0 (#260)_x000D__x000A_- Add positional path to Save-Module and Save-Script (#264, #266)_x000D__x000A_- Ensure that Get-AuthenticodePublisher verifies publisher and that installing or updating a module checks for approprite catalog signature (#272)_x000D__x000A_- Update HelpInfoURI to 'http://go.microsoft.com/fwlink/?linkid=855963' (#274)_x000D__x000A__x000D__x000A__x000D__x000A_## 1.6.0_x000D__x000A__x000D__x000A_New features_x000D__x000A_* Prerelease Version Support (#185)_x000D__x000A_ - Implemented prerelease versions functionality in PowerShellGet cmdlets._x000D__x000A_ - Enables publishing, discovering, and installing the prerelease versions of modules and scripts from the PowerShell Gallery._x000D__x000A_ - [Documentation](https://docs.microsoft.com/en-us/powershell/gallery/psget/module/PrereleaseModule)_x000D__x000A__x000D__x000A_* Enabled publish cmdlets on PWSH and Nano Server (#196)_x000D__x000A_ - Dotnet command version 2.0.0 or newer should be installed by the user prior to using the publish cmdlets on PWSH and Windows Nano Server._x000D__x000A_ - Users can install the dotnet command by following the instructions specified at https://aka.ms/dotnet-install-script._x000D__x000A_ - On Windows, users can install the dotnet command by running *Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile '.\dotnet-install.ps1'; & '.\dotnet-install.ps1' -Channel Current -Version '2.0.0'*_x000D__x000A_ - Publish cmdlets on Windows PowerShell supports using the dotnet command for publishing operations._x000D__x000A__x000D__x000A_Breaking Change_x000D__x000A_- PWSH: Changed the installation location of AllUsers scope to the parent of $PSHOME instead of $PSHOME. It is the SHARED_MODULES folder on PWSH._x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Update HelpInfoURI to 'https://go.microsoft.com/fwlink/?linkid=855963' (#195)_x000D__x000A_- Ensure MyDocumentsPSPath path is correct (#179) (Thanks @lwsrbrts)_x000D__x000A__x000D__x000A__x000D__x000A_## 1.5.0.0_x000D__x000A__x000D__x000A_New features_x000D__x000A_* Added support for modules requiring license acceptance (#150)_x000D__x000A_ - [Documentation](https://docs.microsoft.com/en-us/powershell/gallery/psget/module/RequireLicenseAcceptance)_x000D__x000A__x000D__x000A_* Added version for REQUIREDSCRIPTS (#162)_x000D__x000A_ - Enabled following scenarios for REQUIREDSCRIPTS_x000D__x000A_ - [1.0] - RequiredVersion_x000D__x000A_ - [1.0,2.0] - Min and Max Version_x000D__x000A_ - (,1.0] - Max Version_x000D__x000A_ - 1.0 - Min Version_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_* Fixed empty version value in nuspec (#157)_x000D__x000A__x000D__x000A__x000D__x000A_## 1.1.3.2_x000D__x000A_* Disabled PowerShellGet Telemetry on PS Core as PowerShell Telemetry APIs got removed in PowerShell Core beta builds. (#153)_x000D__x000A_* Fixed for DateTime format serialization issue. (#141)_x000D__x000A_* Update-ModuleManifest should add ExternalModuleDependencies value as a collection. (#129)_x000D__x000A__x000D__x000A_## 1.1.3.1_x000D__x000A__x000D__x000A_New features_x000D__x000A_* Added `PrivateData` field to ScriptFileInfo. (#119)_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A__x000D__x000A_## 1.1.2.0_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A__x000D__x000A_## 1.1.1.0_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A__x000D__x000A_## 1.1.0.0_x000D__x000A__x000D__x000A_* Initial release from GitHub._x000D__x000A_* PowerShellCore support._x000D__x000A__x000D__x000A_## For full history of release notes see changelog:_x000D__x000A_https://github.com/PowerShell/PowerShellGet/blob/master/CHANGELOG.md System.Collections.Specialized.OrderedDictionary System.Object Name PackageManagement MinimumVersion 1.4.4 CanonicalId nuget:PackageManagement/1.4.4 https://www.powershellgallery.com/api/v2 PSGallery NuGet System.Management.Automation.PSCustomObject System.Object (c) Microsoft Corporation. All rights reserved. PowerShell module with commands for discovering, installing, updating and publishing the PowerShell artifacts like Modules, DSC Resources, Role Capabilities and Scripts. False ### 2.2.5_x000D__x000A_- Security patch for code injection bug_x000D__x000A__x000D__x000A_### 2.2.4.1_x000D__x000A_- Remove catalog file_x000D__x000A__x000D__x000A_### 2.2.3_x000D__x000A_- Update `HelpInfoUri` to point to the latest content (#560)_x000D__x000A_- Improve discovery of usable nuget.exe binary (Thanks bwright86!) (#558)_x000D__x000A__x000D__x000A_### 2.2.2_x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Update casing of DscResources output_x000D__x000A__x000D__x000A_### 2.2.1_x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Allow DscResources to work on case sensitive platforms (#521)_x000D__x000A_- Fix for failure to return credential provider when using private feeds (#521)_x000D__x000A__x000D__x000A_## 2.2_x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Fix for prompting for credentials when passing in -Credential parameter when using Register-PSRepository_x000D__x000A__x000D__x000A_## 2.1.5_x000D__x000A_New Features_x000D__x000A__x000D__x000A_- Add and remove nuget based repositories as a nuget source when nuget client tool is installed (#498)_x000D__x000A__x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Fix for 'Failed to publish module' error thrown when publishing modules (#497)_x000D__x000A__x000D__x000A_## 2.1.4_x000D__x000A_- Fixed hang while publishing some packages (#478)_x000D__x000A__x000D__x000A_## 2.1.3_x000D__x000A_New Features_x000D__x000A__x000D__x000A_- Added -Scope parameter to Update-Module (Thanks @lwajswaj!) (#471)_x000D__x000A_- Added -Exclude parameter to Publish-Module (Thanks @Benny1007!) (#191)_x000D__x000A_- Added -SkipAutomaticTags parameter to Publish-Module (Thanks @awickham10!) (#452)_x000D__x000A__x000D__x000A_Bug Fix_x000D__x000A__x000D__x000A_- Fixed issue with finding modules using macOS and .NET Core 3.0_x000D__x000A__x000D__x000A_## 2.1.2_x000D__x000A__x000D__x000A_New Feature_x000D__x000A__x000D__x000A_- Added support for registering repositories with special characters_x000D__x000A__x000D__x000A_## 2.1.1_x000D__x000A__x000D__x000A_- Fix DSC resource folder structure_x000D__x000A__x000D__x000A_## 2.1.0_x000D__x000A__x000D__x000A_Breaking Change_x000D__x000A__x000D__x000A_- Default installation scope for Update-Module and Update-Script has changed to match Install-Module and Install-Script. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times._x000D__x000A_ For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser. (#421)_x000D__x000A__x000D__x000A_Bug Fixes_x000D__x000A__x000D__x000A_- Update-ModuleManifest no longer clears FunctionsToExport, AliasesToExport, nor NestModules (#415 & #425) (Thanks @pougetat and @tnieto88!)_x000D__x000A_- Update-Module no longer changes repository URL (#407)_x000D__x000A_- Update-ModuleManifest no longer preprends 'PSGet_' to module name (#403) (Thanks @ThePoShWolf)_x000D__x000A_- Update-ModuleManifest now throws error and fails to update when provided invalid entries (#398) (Thanks @pougetat!)_x000D__x000A_- Ignore files no longer being included when uploading modules (#396)_x000D__x000A__x000D__x000A_New Features_x000D__x000A__x000D__x000A_- New DSC resource, PSRepository (#426) (Thanks @johlju!)_x000D__x000A_- Piping of PS respositories (#420)_x000D__x000A_- utf8 support for .nuspec (#419)_x000D__x000A__x000D__x000A_## 2.0.4_x000D__x000A__x000D__x000A_Bug Fix_x000D__x000A_* Remove PSGallery availability checks (#374)_x000D__x000A__x000D__x000A_## 2.0.3_x000D__x000A__x000D__x000A_Bug fixes and Improvements_x000D__x000A_* Fix CommandAlreadyAvailable error for PackageManagement module (#333)_x000D__x000A_* Remove trailing whitespace when value is not provided for Get-PSScriptInfoString (#337) (Thanks @thomasrayner)_x000D__x000A_* Expanded aliases for improved readability (#338) (Thanks @lazywinadmin)_x000D__x000A_* Improvements for Catalog tests (#343)_x000D__x000A_* Fix Update-ScriptInfoFile to preserve PrivateData (#346) (Thanks @tnieto88)_x000D__x000A_* Import modules with many commands faster (#351)_x000D__x000A__x000D__x000A_New Features_x000D__x000A_* Tab completion for -Repository parameter (#339) and for Publish-Module -Name (#359) (Thanks @matt9ucci)_x000D__x000A__x000D__x000A_## 2.0.1_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Resolved Publish-Module doesn't report error but fails to publish module (#316)_x000D__x000A_- Resolved CommandAlreadyAvailable error while installing the latest version of PackageManagement module (#333)_x000D__x000A__x000D__x000A_## 2.0.0_x000D__x000A__x000D__x000A_Breaking Change_x000D__x000A_- Default installation scope for Install-Module, Install-Script, and Install-Package has changed. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times._x000D__x000A_ For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser._x000D__x000A__x000D__x000A_## 1.6.7_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Resolved Install/Save-Module error in PSCore 6.1.0-preview.4 on Ubuntu 18.04 OS (WSL/Azure) (#313)_x000D__x000A_- Updated error message in Save-Module cmdlet when the specified path is not accessible (#313)_x000D__x000A_- Added few additional verbose messages (#313)_x000D__x000A__x000D__x000A_## 1.6.6_x000D__x000A__x000D__x000A_Dependency Updates_x000D__x000A_* Add dependency on version 4.1.0 or newer of NuGet.exe_x000D__x000A_* Update NuGet.exe bootstrap URL to https://aka.ms/psget-nugetexe_x000D__x000A__x000D__x000A_Build and Code Cleanup Improvements_x000D__x000A_* Improved error handling in network connectivity tests._x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Change Update-ModuleManifest so that prefix is not added to CmdletsToExport._x000D__x000A_- Change Update-ModuleManifest so that parameters will not reset to default values._x000D__x000A_- Specify AllowPrereleseVersions provider option only when AllowPrerelease is specified on the PowerShellGet cmdlets._x000D__x000A__x000D__x000A_## 1.6.5_x000D__x000A__x000D__x000A_New features_x000D__x000A_* Allow Pester/PSReadline installation when signed by non-Microsoft certificate (#258)_x000D__x000A_ - Whitelist installation of non-Microsoft signed Pester and PSReadline over Microsoft signed Pester and PSReadline._x000D__x000A__x000D__x000A_Build and Code Cleanup Improvements_x000D__x000A_* Splitting of functions (#229) (Thanks @Benny1007)_x000D__x000A_ - Moves private functions into respective private folder._x000D__x000A_ - Moves public functions as defined in PSModule.psd1 into respective public folder._x000D__x000A_ - Removes all functions from PSModule.psm1 file._x000D__x000A_ - Dot sources the functions from PSModule.psm1 file._x000D__x000A_ - Uses Export-ModuleMember to export the public functions from PSModule.psm1 file._x000D__x000A__x000D__x000A_* Add build step to construct a single .psm1 file (#242) (Thanks @Benny1007)_x000D__x000A_ - Merged public and private functions into one .psm1 file to increase load time performance._x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Fix null parameter error caused by MinimumVersion in Publish-PackageUtility (#201)_x000D__x000A_- Change .ExternalHelp link from PSGet.psm1-help.xml to PSModule-help.xml in PSModule.psm1 file (#215)_x000D__x000A_- Change Publish-* to allow version comparison instead of string comparison (#219)_x000D__x000A_- Ensure Get-InstalledScript -RequiredVersion works when versions have a leading 0 (#260)_x000D__x000A_- Add positional path to Save-Module and Save-Script (#264, #266)_x000D__x000A_- Ensure that Get-AuthenticodePublisher verifies publisher and that installing or updating a module checks for approprite catalog signature (#272)_x000D__x000A_- Update HelpInfoURI to 'http://go.microsoft.com/fwlink/?linkid=855963' (#274)_x000D__x000A__x000D__x000A__x000D__x000A_## 1.6.0_x000D__x000A__x000D__x000A_New features_x000D__x000A_* Prerelease Version Support (#185)_x000D__x000A_ - Implemented prerelease versions functionality in PowerShellGet cmdlets._x000D__x000A_ - Enables publishing, discovering, and installing the prerelease versions of modules and scripts from the PowerShell Gallery._x000D__x000A_ - [Documentation](https://docs.microsoft.com/en-us/powershell/gallery/psget/module/PrereleaseModule)_x000D__x000A__x000D__x000A_* Enabled publish cmdlets on PWSH and Nano Server (#196)_x000D__x000A_ - Dotnet command version 2.0.0 or newer should be installed by the user prior to using the publish cmdlets on PWSH and Windows Nano Server._x000D__x000A_ - Users can install the dotnet command by following the instructions specified at https://aka.ms/dotnet-install-script._x000D__x000A_ - On Windows, users can install the dotnet command by running *Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile '.\dotnet-install.ps1'; & '.\dotnet-install.ps1' -Channel Current -Version '2.0.0'*_x000D__x000A_ - Publish cmdlets on Windows PowerShell supports using the dotnet command for publishing operations._x000D__x000A__x000D__x000A_Breaking Change_x000D__x000A_- PWSH: Changed the installation location of AllUsers scope to the parent of $PSHOME instead of $PSHOME. It is the SHARED_MODULES folder on PWSH._x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_- Update HelpInfoURI to 'https://go.microsoft.com/fwlink/?linkid=855963' (#195)_x000D__x000A_- Ensure MyDocumentsPSPath path is correct (#179) (Thanks @lwsrbrts)_x000D__x000A__x000D__x000A__x000D__x000A_## 1.5.0.0_x000D__x000A__x000D__x000A_New features_x000D__x000A_* Added support for modules requiring license acceptance (#150)_x000D__x000A_ - [Documentation](https://docs.microsoft.com/en-us/powershell/gallery/psget/module/RequireLicenseAcceptance)_x000D__x000A__x000D__x000A_* Added version for REQUIREDSCRIPTS (#162)_x000D__x000A_ - Enabled following scenarios for REQUIREDSCRIPTS_x000D__x000A_ - [1.0] - RequiredVersion_x000D__x000A_ - [1.0,2.0] - Min and Max Version_x000D__x000A_ - (,1.0] - Max Version_x000D__x000A_ - 1.0 - Min Version_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A_* Fixed empty version value in nuspec (#157)_x000D__x000A__x000D__x000A__x000D__x000A_## 1.1.3.2_x000D__x000A_* Disabled PowerShellGet Telemetry on PS Core as PowerShell Telemetry APIs got removed in PowerShell Core beta builds. (#153)_x000D__x000A_* Fixed for DateTime format serialization issue. (#141)_x000D__x000A_* Update-ModuleManifest should add ExternalModuleDependencies value as a collection. (#129)_x000D__x000A__x000D__x000A_## 1.1.3.1_x000D__x000A__x000D__x000A_New features_x000D__x000A_* Added `PrivateData` field to ScriptFileInfo. (#119)_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A__x000D__x000A_## 1.1.2.0_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A__x000D__x000A_## 1.1.1.0_x000D__x000A__x000D__x000A_Bug fixes_x000D__x000A__x000D__x000A_## 1.1.0.0_x000D__x000A__x000D__x000A_* Initial release from GitHub._x000D__x000A_* PowerShellCore support._x000D__x000A__x000D__x000A_## For full history of release notes see changelog:_x000D__x000A_https://github.com/PowerShell/PowerShellGet/blob/master/CHANGELOG.md True False 65804037 114539696 270249 9/22/2020 10:42:00 PM -07:00 9/22/2020 10:42:00 PM -07:00 3/21/2023 6:34:18 PM -07:00 Packagemanagement Provider PSEdition_Desktop PSEdition_Core Linux Mac PSModule False 2023-03-21T18:34:18Z 2.2.5 Microsoft Corporation false Module PowerShellGet.nuspec|PowerShellGet.psd1|PSGet.Format.ps1xml|PSGet.Resource.psd1|PSModule.psm1|DscResources\MSFT_PSModule\MSFT_PSModule.psm1|DscResources\MSFT_PSModule\MSFT_PSModule.schema.mfl|DscResources\MSFT_PSModule\MSFT_PSModule.schema.mof|DscResources\MSFT_PSModule\en-US\MSFT_PSModule.strings.psd1|DscResources\MSFT_PSRepository\MSFT_PSRepository.psm1|DscResources\MSFT_PSRepository\MSFT_PSRepository.schema.mfl|DscResources\MSFT_PSRepository\MSFT_PSRepository.schema.mof|DscResources\MSFT_PSRepository\en-US\MSFT_PSRepository.strings.psd1|en-US\PSGet.Resource.psd1|Modules\PowerShellGet.LocalizationHelper\PowerShellGet.LocalizationHelper.psm1|Modules\PowerShellGet.ResourceHelper\PowerShellGet.ResourceHelper.psm1|Modules\PowerShellGet.ResourceHelper\en-US\PowerShellGet.ResourceHelper.strings.psd1 Find-Command Find-DSCResource Find-Module Find-RoleCapability Find-Script Get-CredsFromCredentialProvider Get-InstalledModule Get-InstalledScript Get-PSRepository Install-Module Install-Script New-ScriptFileInfo Publish-Module Publish-Script Register-PSRepository Save-Module Save-Script Set-PSRepository Test-ScriptFileInfo Uninstall-Module Uninstall-Script Unregister-PSRepository Update-Module Update-ModuleManifest Update-Script Update-ScriptFileInfo PSModule PSRepository 1d73a601-4a6c-43c5-ba3f-619b18bbb404 3.0 Microsoft Corporation D:\mspkg\src\PowerShell\ExternalModules\PowerShellGet\2.2.5
================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/PowerShellGet.psd1 ================================================ @{ RootModule = 'PSModule.psm1' ModuleVersion = '2.2.5' GUID = '1d73a601-4a6c-43c5-ba3f-619b18bbb404' Author = 'Microsoft Corporation' CompanyName = 'Microsoft Corporation' Copyright = '(c) Microsoft Corporation. All rights reserved.' Description = 'PowerShell module with commands for discovering, installing, updating and publishing the PowerShell artifacts like Modules, DSC Resources, Role Capabilities and Scripts.' PowerShellVersion = '3.0' FormatsToProcess = 'PSGet.Format.ps1xml' FunctionsToExport = @( 'Find-Command', 'Find-DSCResource', 'Find-Module', 'Find-RoleCapability', 'Find-Script', 'Get-CredsFromCredentialProvider', 'Get-InstalledModule', 'Get-InstalledScript', 'Get-PSRepository', 'Install-Module', 'Install-Script', 'New-ScriptFileInfo', 'Publish-Module', 'Publish-Script', 'Register-PSRepository', 'Save-Module', 'Save-Script', 'Set-PSRepository', 'Test-ScriptFileInfo', 'Uninstall-Module', 'Uninstall-Script', 'Unregister-PSRepository', 'Update-Module', 'Update-ModuleManifest', 'Update-Script', 'Update-ScriptFileInfo') VariablesToExport = 'PSGetPath' AliasesToExport = @('inmo', 'fimo', 'upmo', 'pumo') FileList = @('PSModule.psm1', 'PSGet.Format.ps1xml', 'PSGet.Resource.psd1') RequiredModules = @(@{ModuleName = 'PackageManagement'; ModuleVersion = '1.4.4' }) PrivateData = @{ "PackageManagementProviders" = 'PSModule.psm1' "SupportedPowerShellGetFormatVersions" = @('1.x', '2.x') PSData = @{ Tags = @('Packagemanagement', 'Provider', 'PSEdition_Desktop', 'PSEdition_Core', 'Linux', 'Mac') ProjectUri = 'https://go.microsoft.com/fwlink/?LinkId=828955' LicenseUri = 'https://go.microsoft.com/fwlink/?LinkId=829061' ReleaseNotes = @' ### 2.2.5 - Security patch for code injection bug ### 2.2.4.1 - Remove catalog file ### 2.2.3 - Update `HelpInfoUri` to point to the latest content (#560) - Improve discovery of usable nuget.exe binary (Thanks bwright86!) (#558) ### 2.2.2 Bug Fix - Update casing of DscResources output ### 2.2.1 Bug Fix - Allow DscResources to work on case sensitive platforms (#521) - Fix for failure to return credential provider when using private feeds (#521) ## 2.2 Bug Fix - Fix for prompting for credentials when passing in -Credential parameter when using Register-PSRepository ## 2.1.5 New Features - Add and remove nuget based repositories as a nuget source when nuget client tool is installed (#498) Bug Fix - Fix for 'Failed to publish module' error thrown when publishing modules (#497) ## 2.1.4 - Fixed hang while publishing some packages (#478) ## 2.1.3 New Features - Added -Scope parameter to Update-Module (Thanks @lwajswaj!) (#471) - Added -Exclude parameter to Publish-Module (Thanks @Benny1007!) (#191) - Added -SkipAutomaticTags parameter to Publish-Module (Thanks @awickham10!) (#452) Bug Fix - Fixed issue with finding modules using macOS and .NET Core 3.0 ## 2.1.2 New Feature - Added support for registering repositories with special characters ## 2.1.1 - Fix DSC resource folder structure ## 2.1.0 Breaking Change - Default installation scope for Update-Module and Update-Script has changed to match Install-Module and Install-Script. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times. For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser. (#421) Bug Fixes - Update-ModuleManifest no longer clears FunctionsToExport, AliasesToExport, nor NestModules (#415 & #425) (Thanks @pougetat and @tnieto88!) - Update-Module no longer changes repository URL (#407) - Update-ModuleManifest no longer preprends 'PSGet_' to module name (#403) (Thanks @ThePoShWolf) - Update-ModuleManifest now throws error and fails to update when provided invalid entries (#398) (Thanks @pougetat!) - Ignore files no longer being included when uploading modules (#396) New Features - New DSC resource, PSRepository (#426) (Thanks @johlju!) - Piping of PS respositories (#420) - utf8 support for .nuspec (#419) ## 2.0.4 Bug Fix * Remove PSGallery availability checks (#374) ## 2.0.3 Bug fixes and Improvements * Fix CommandAlreadyAvailable error for PackageManagement module (#333) * Remove trailing whitespace when value is not provided for Get-PSScriptInfoString (#337) (Thanks @thomasrayner) * Expanded aliases for improved readability (#338) (Thanks @lazywinadmin) * Improvements for Catalog tests (#343) * Fix Update-ScriptInfoFile to preserve PrivateData (#346) (Thanks @tnieto88) * Import modules with many commands faster (#351) New Features * Tab completion for -Repository parameter (#339) and for Publish-Module -Name (#359) (Thanks @matt9ucci) ## 2.0.1 Bug fixes - Resolved Publish-Module doesn't report error but fails to publish module (#316) - Resolved CommandAlreadyAvailable error while installing the latest version of PackageManagement module (#333) ## 2.0.0 Breaking Change - Default installation scope for Install-Module, Install-Script, and Install-Package has changed. For Windows PowerShell (version 5.1 or below), the default scope is AllUsers when running in an elevated session, and CurrentUser at all other times. For PowerShell version 6.0.0 and above, the default installation scope is always CurrentUser. ## 1.6.7 Bug fixes - Resolved Install/Save-Module error in PSCore 6.1.0-preview.4 on Ubuntu 18.04 OS (WSL/Azure) (#313) - Updated error message in Save-Module cmdlet when the specified path is not accessible (#313) - Added few additional verbose messages (#313) ## 1.6.6 Dependency Updates * Add dependency on version 4.1.0 or newer of NuGet.exe * Update NuGet.exe bootstrap URL to https://aka.ms/psget-nugetexe Build and Code Cleanup Improvements * Improved error handling in network connectivity tests. Bug fixes - Change Update-ModuleManifest so that prefix is not added to CmdletsToExport. - Change Update-ModuleManifest so that parameters will not reset to default values. - Specify AllowPrereleseVersions provider option only when AllowPrerelease is specified on the PowerShellGet cmdlets. ## 1.6.5 New features * Allow Pester/PSReadline installation when signed by non-Microsoft certificate (#258) - Whitelist installation of non-Microsoft signed Pester and PSReadline over Microsoft signed Pester and PSReadline. Build and Code Cleanup Improvements * Splitting of functions (#229) (Thanks @Benny1007) - Moves private functions into respective private folder. - Moves public functions as defined in PSModule.psd1 into respective public folder. - Removes all functions from PSModule.psm1 file. - Dot sources the functions from PSModule.psm1 file. - Uses Export-ModuleMember to export the public functions from PSModule.psm1 file. * Add build step to construct a single .psm1 file (#242) (Thanks @Benny1007) - Merged public and private functions into one .psm1 file to increase load time performance. Bug fixes - Fix null parameter error caused by MinimumVersion in Publish-PackageUtility (#201) - Change .ExternalHelp link from PSGet.psm1-help.xml to PSModule-help.xml in PSModule.psm1 file (#215) - Change Publish-* to allow version comparison instead of string comparison (#219) - Ensure Get-InstalledScript -RequiredVersion works when versions have a leading 0 (#260) - Add positional path to Save-Module and Save-Script (#264, #266) - Ensure that Get-AuthenticodePublisher verifies publisher and that installing or updating a module checks for approprite catalog signature (#272) - Update HelpInfoURI to 'http://go.microsoft.com/fwlink/?linkid=855963' (#274) ## 1.6.0 New features * Prerelease Version Support (#185) - Implemented prerelease versions functionality in PowerShellGet cmdlets. - Enables publishing, discovering, and installing the prerelease versions of modules and scripts from the PowerShell Gallery. - [Documentation](https://docs.microsoft.com/en-us/powershell/gallery/psget/module/PrereleaseModule) * Enabled publish cmdlets on PWSH and Nano Server (#196) - Dotnet command version 2.0.0 or newer should be installed by the user prior to using the publish cmdlets on PWSH and Windows Nano Server. - Users can install the dotnet command by following the instructions specified at https://aka.ms/dotnet-install-script. - On Windows, users can install the dotnet command by running *Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile '.\dotnet-install.ps1'; & '.\dotnet-install.ps1' -Channel Current -Version '2.0.0'* - Publish cmdlets on Windows PowerShell supports using the dotnet command for publishing operations. Breaking Change - PWSH: Changed the installation location of AllUsers scope to the parent of $PSHOME instead of $PSHOME. It is the SHARED_MODULES folder on PWSH. Bug fixes - Update HelpInfoURI to 'https://go.microsoft.com/fwlink/?linkid=855963' (#195) - Ensure MyDocumentsPSPath path is correct (#179) (Thanks @lwsrbrts) ## 1.5.0.0 New features * Added support for modules requiring license acceptance (#150) - [Documentation](https://docs.microsoft.com/en-us/powershell/gallery/psget/module/RequireLicenseAcceptance) * Added version for REQUIREDSCRIPTS (#162) - Enabled following scenarios for REQUIREDSCRIPTS - [1.0] - RequiredVersion - [1.0,2.0] - Min and Max Version - (,1.0] - Max Version - 1.0 - Min Version Bug fixes * Fixed empty version value in nuspec (#157) ## 1.1.3.2 * Disabled PowerShellGet Telemetry on PS Core as PowerShell Telemetry APIs got removed in PowerShell Core beta builds. (#153) * Fixed for DateTime format serialization issue. (#141) * Update-ModuleManifest should add ExternalModuleDependencies value as a collection. (#129) ## 1.1.3.1 New features * Added `PrivateData` field to ScriptFileInfo. (#119) Bug fixes ## 1.1.2.0 Bug fixes ## 1.1.1.0 Bug fixes ## 1.1.0.0 * Initial release from GitHub. * PowerShellCore support. ## For full history of release notes see changelog: https://github.com/PowerShell/PowerShellGet/blob/master/CHANGELOG.md '@ } } HelpInfoURI = 'http://go.microsoft.com/fwlink/?linkid=2113539' } # SIG # Begin signature block # MIIjkQYJKoZIhvcNAQcCoIIjgjCCI34CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCRNShWem0qs5De # OoNTMpUMZmnlgRrJHCT4bl+44h9Jy6CCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZjCCFWICAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg3TxELXso # oRSF8xKPexxNhyDHV3uwUf4mRHTftOyVvf4wQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQC2MdGNwwt17U4YHP6Gn/7I/Z5/XKA08BGacJ6NSvXC # 7BK55Looz5q9JuOjKgY0J5Zv3s3cBCBiABOF/E+kfjMqCtK1x9xC0dz+ZXGjKyFb # MFuuzueIFHKnZWjLXueiAXmXuhQEV1lmw2X2Cv5lCV7fhoUyYP47yJvS+nHk5c2u # 8OSZ4iVgBTcazWLWZTDu4zbVKk2g2HawdlGEwsCBn21gwhyqIelNUa/Cs4ab+a30 # Ach8Jd12YE/ZmrSsoUV1hVDFA+/Z7oeZfuENa2mUUlrZ7Y+uLpbw8GZ4vge7TO23 # r2cFGEV5g6q1/ykxFisUUheyYfjfsWqPVhXe6+9Mvk94oYIS8DCCEuwGCisGAQQB # gjcDAwExghLcMIIS2AYJKoZIhvcNAQcCoIISyTCCEsUCAQMxDzANBglghkgBZQME # AgEFADCCAVQGCyqGSIb3DQEJEAEEoIIBQwSCAT8wggE7AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIFoqO98TuKz1Zz96t7blTSp0BuwKzzxkArukjb8S # 0+wRAgZfYPq2eAYYEjIwMjAwOTIyMjIxOTUwLjkzWjAEgAIB9KCB1KSB0TCBzjEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWlj # cm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBU # U1MgRVNOOjc4ODAtRTM5MC04MDE0MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T # dGFtcCBTZXJ2aWNloIIORDCCBPUwggPdoAMCAQICEzMAAAEooA6B4TbVT8IAAAAA # ASgwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw # HhcNMTkxMjE5MDExNTAwWhcNMjEwMzE3MDExNTAwWjCBzjELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJh # dGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjc4ODAt # RTM5MC04MDE0MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnZGx1vdU24Y+zb8OClz2 # C3vssbQk+QPhVZOUQkuSrOdMmX5Ghl+I7A3qJZ8+7iT+SPyfBjum8uzU6wHLj3jK # 6yDiscvAc1Qk+3DVNzngw4uB1yiwDg3GSLvd8PKpbAO2M52TofuQ1zME+oAMPoH3 # yi3vv/BIAIEkjGb2oBS52q5Ll9zMIXT75pZRq8O7jpTdy/ocSMh1XZl0lNQqDhZQ # h1NgxBcjTzb6pKzjlYFmNwr3z+0h/Hy6ryrySxYX37NSMZMWIxooeGftxIKgSPsT # W1WZbTwhKlLrvxYU/b4DQ5DBpZwko0AIr4n4trsvPZsa6kKJ04bPlcN7BzWUP2cs # 9wIDAQABo4IBGzCCARcwHQYDVR0OBBYEFITi8oPxfrU3m9QBw050f1AEy6byMB8G # A1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeG # RWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Rp # bVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUH # MAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3Rh # UENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYB # BQUHAwgwDQYJKoZIhvcNAQELBQADggEBAItfZkcYhQuAOT+JxwdZTLCMPICwEeWG # Sa2YGniWV3Avd02jRtdlkeJJkH5zYrO8+pjrgGUQKNL8+q6vab1RpPU3QF5SjBEd # BPzzB3N33iBiopeYsNtVHzJ5WAGRw/8mJVZtd1DNzPURMeBauH67MDwHBSABocnD # 6ddhxwi4OA8kzVRN42X1Hk69/7rNHYTlkjgOsiq9LiMfhCygw9OfbsCM3tVm3hqa # hHEwsRxABLu89PUlRRpEWkUeaRRhWWfVgyzD///r3rxpG/LdyYKVLji7GSRogtuG # HWHT16NmMeGsSf6T0xxWRaK5jvbiMn/nu3KUzsD+PMhY2PUXxWWGTLIwggZxMIIE # WaADAgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9v # dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0y # NTA3MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RU # ENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBE # D/FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50 # YWeRX4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd # /XcfPfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaR # togINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQAB # o4IB5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8 # RhvFM2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIB # hjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fO # mhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w # a2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggr # BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSAB # Af8EgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEF # BQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBt # AGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Eh # b7Prpsz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7 # uVOMzPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqR # UgCvOA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9 # Va8v/rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8 # +n99lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+ # Y1klD3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh # 2rBQHm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRy # zR30uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoo # uLGp25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx # 16HSxVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341 # Hgi62jbb01+P3nSISRKhggLSMIICOwIBATCB/KGB1KSB0TCBzjELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9w # ZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjc4 # ODAtRTM5MC04MDE0MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2 # aWNloiMKAQEwBwYFKw4DAhoDFQAxPUsb8oASPReyIv2fubGZfVp9m6CBgzCBgKR+ # MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT # HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA # 4xSzVjAiGA8yMDIwMDkyMjIxMzEwMloYDzIwMjAwOTIzMjEzMTAyWjB3MD0GCisG # AQQBhFkKBAExLzAtMAoCBQDjFLNWAgEAMAoCAQACAiF/AgH/MAcCAQACAhGdMAoC # BQDjFgTWAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEA # AgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAIIiMzPTf2R4++LT5 # BIQAAZ7XoAkwHZm+GsArFZSjJmmdGtYNmLO7vA433YPn1av4tVK9wasdh+Uxa3Qo # YD4D+LR+E8bwdZR+JGQZKDkxjmOXwPITowyRtYQPCa9mTO4iJDf/gixpGBzu8sgM # zXRdLiDn1P/Vo8FpWdAKfaLz4P4xggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt # ZS1TdGFtcCBQQ0EgMjAxMAITMwAAASigDoHhNtVPwgAAAAABKDANBglghkgBZQME # AgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJ # BDEiBCDvMyofVUL0InlfJG07Yi8ZRMCLjc3rNDrU8nhvTqbq+DCB+gYLKoZIhvcN # AQkQAi8xgeowgecwgeQwgb0EILxFaouvBVJ379wbEN8GpLhvW09eGg8WsLrXm9XW # 6BTaMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAEo # oA6B4TbVT8IAAAAAASgwIgQgRO6TlKRXX+wSVoBIMaqor080XnPuS/BnRzhuQ8oN # BCQwDQYJKoZIhvcNAQELBQAEggEAZM529j3yYoEo0YZVvE7ZwiKlil8xSVU3JNZE # gHE391SCUFpeLu012jhMt3ikeJjApyUuhuQpyWkU1pkIMJHP78SOq4VejIMhm57l # iIUo1txBy4P6NZcNmIihQa/R484b4k1Zz1GP7jx+Wmvq65FHPv3SFlmJOs6GuE1R # lIhZdc8VNW7DsyGShMOIKx5lYb68+rsUES5bRExAxAj0ykGPpmiQt3QRmBNlDEgc # 7GF2FGWFD7ZdAn6+W66UmqxkU7MpBrVwggRi92YXMbZI2A6ts8UR8G8JeJZbaHRz # hnAaK3NPwhZhfD/2oRN172Rr7uRQtH9FesspG0TlfkjEe6P9zw== # SIG # End signature block ================================================ FILE: src/PowerShell/ExternalModules/PowerShellGet/2.2.5/en-US/PSGet.Resource.psd1 ================================================ ######################################################################################### # # Copyright (c) Microsoft Corporation. All rights reserved. # # Localized PSGet.Resource.psd1 # ######################################################################################### ConvertFrom-StringData @' ###PSLOC InstallModulewhatIfMessage=Version '{1}' of module '{0}' InstallScriptwhatIfMessage=Version '{1}' of script '{0}' UpdateModulewhatIfMessage=Version '__OLDVERSION__' of module '{0}', updating to version '{1}' UpdateScriptwhatIfMessage=Version '__OLDVERSION__' of script '{0}', updating to version '{1}' PublishModulewhatIfMessage=Version '{0}' of module '{1}' PublishScriptwhatIfMessage=Version '{0}' of script '{1}' NewScriptFileInfowhatIfMessage=Creating the '{0}' PowerShell Script file UpdateScriptFileInfowhatIfMessage=Updating the '{0}' PowerShell Script file NameShouldNotContainWildcardCharacters=The specified name '{0}' should not contain any wildcard characters, please correct it and try again. AllVersionsCannotBeUsedWithOtherVersionParameters=You cannot use the parameter AllVersions with RequiredVersion, MinimumVersion or MaximumVersion in the same command. VersionRangeAndRequiredVersionCannotBeSpecifiedTogether=You cannot use the parameters RequiredVersion and either MinimumVersion or MaximumVersion in the same command. Specify only one of these parameters in your command. RequiredVersionAllowedOnlyWithSingleModuleName=The RequiredVersion parameter is allowed only when a single module name is specified as the value of the Name parameter, without any wildcard characters. MinimumVersionIsGreaterThanMaximumVersion=The specified MinimumVersion '{0}' is greater than the specified MaximumVersion '{1}'. AllowPrereleaseRequiredToUsePrereleaseStringInVersion=The '-AllowPrerelease' parameter must be specified when using the Prerelease string in MinimumVersion, MaximumVersion, or RequiredVersion. UpdateModuleAdminPrivilegeRequiredForAllUsersScope=Administrator rights are required to update modules in '{0}'. Log on to the computer with an account that has Administrator rights, and then try again, or update '{1}' by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). InstallModuleAdminPrivilegeRequiredForAllUsersScope=Administrator rights are required to install modules in '{0}'. Log on to the computer with an account that has Administrator rights, and then try again, or install '{1}' by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). InstallScriptAdminPrivilegeRequiredForAllUsersScope=Administrator rights are required to install scripts in '{0}'. Log on to the computer with an account that has Administrator rights, and then try again, or install '{1}' by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). AdministratorRightsNeededOrSpecifyCurrentUserScope=Administrator rights are required to install or update. Log on to the computer with an account that has Administrator rights, and then try again, or install by adding "-Scope CurrentUser" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). VersionParametersAreAllowedOnlyWithSingleName=The RequiredVersion, MinimumVersion, MaximumVersion, AllVersions or AllowPrerelease parameters are allowed only when you specify a single name as the value of the Name parameter, without any wildcard characters. PathIsNotADirectory=The specified path '{0}' is not a valid directory. ModuleAlreadyInstalled=Version '{0}' of module '{1}' is already installed at '{2}'. To delete version '{3}' and install version '{4}', run Install-Module, and add the -Force parameter. ScriptAlreadyInstalled=Version '{0}' of script '{1}' is already installed at '{2}'. To delete version '{3}' and install version '{4}', run Install-Script, and add the -Force parameter. CommandAlreadyAvailable=A command with name '{0}' is already available on this system. This script '{0}' may override the existing command. If you still want to install this script '{0}', use -Force parameter. ModuleAlreadyInstalledSxS=Version '{0}' of module '{1}' is already installed at '{2}'. To install version '{3}', run Install-Module and add the -Force parameter, this command will install version '{5}' side-by-side with version '{4}'. ModuleAlreadyInstalledVerbose=Version '{0}' of module '{1}' is already installed at '{2}'. ScriptAlreadyInstalledVerbose=Version '{0}' of script '{1}' is already installed at '{2}'. ModuleWithRequiredVersionAlreadyInstalled=Version '{0}' of module '{1}' is already installed at '{2}'. To reinstall this version '{3}', run Install-Module or Updated-Module cmdlet with the -Force parameter. InvalidPSModule=The module '{0}' cannot be installed or updated because it is not a properly-formed module. InvalidPowerShellScriptFile=The script '{0}' cannot be installed or updated because it is not a properly-formed script. InvalidAuthenticodeSignature=The module '{0}' cannot be installed or updated because the Authenticode signature for the file '{1}' is not valid. ModuleNotInstalledOnThisMachine=Module '{0}' was not updated because no valid module was found in the module directory. Verify that the module is located in the folder specified by $env:PSModulePath. ScriptNotInstalledOnThisMachine=Script '{0}' was not updated because no valid script was found in the script directories '{1}' and '{2}'. AdminPrivilegesRequiredForUpdate=Module '{0}' (installed at '{1}') cannot be updated because Administrator rights are required to change that directory. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). AdminPrivilegesRequiredForScriptUpdate=Script '{0}' (installed at '{1}') cannot be updated because Administrator rights are required to change that script. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). ModuleNotInstalledUsingPowerShellGet=Module '{0}' was not installed by using Install-Module, so it cannot be updated. ScriptNotInstalledUsingPowerShellGet=Script '{0}' was not installed by using Install-Script, so it cannot be updated. DownloadingModuleFromGallery=Downloading module '{0}' with version '{1}' from the repository '{2}'. DownloadingScriptFromGallery=Downloading script '{0}' with version '{1}' from the repository '{2}'. NoUpdateAvailable=No updates were found for module '{0}'. NoScriptUpdateAvailable=No updates were found for script '{0}'. FoundModuleUpdate=An update for the module '{0}' was found with version '{1}'. FoundScriptUpdate=An update for the script '{0}' was found with version '{1}'. InvalidPSModuleDuringUpdate= Module '{0}' was not updated because the module in the repository '{1}' is not a valid Windows PowerShell module. ModuleGotUpdated=Module '{0}' has been updated successfully. TestingModuleInUse=Testing if the module to update is in use. ModuleDestination= The specified module will be installed in '{0}'. ScriptDestination= The specified script will be installed in '{0}' and its dependent modules will be installed in '{1}'. ModuleIsInUse=Module '{0}' is in currently in use or you don't have the required permissions. ModuleInstalledSuccessfully=Module '{0}' was installed successfully to path '{1}'. ModuleSavedSuccessfully=Module '{0}' was saved successfully to path '{1}'. ScriptInstalledSuccessfully=Script '{0}' was installed successfully to path '{1}'. ScriptSavedSuccessfully=Script '{0}' was saved successfully to path '{1}'. CheckingForModuleUpdate= Checking for updates for module '{0}'. CheckingForScriptUpdate= Checking for updates for script '{0}'. ModuleInUseWithProcessDetails=The version '{0}' of module '{1}' is currently in use. Retry the operation after closing the following applications: '{2}'. ModuleVersionInUse=The version '{0}' of module '{1}' is currently in use. Retry the operation after closing the applications. ModuleNotAvailableLocally=The specified module '{0}' was not published because no module with that name was found in any module directory. InvalidModulePathToPublish=The specified module with path '{0}' was not published because no valid module was found with that path. ModuleWithRequiredVersionNotAvailableLocally= The specified module '{0}' with version '{1}' was not published because no module with that name and version was found in any module directory. AmbiguousModuleName=Modules with the name '{0}' are available under multiple paths. Add the -RequiredVersion parameter or the -Path parameter to specify the module to publish. AmbiguousModulePath=Multiple versions are available under the specified module path '{0}'. Specify the full path to the module to be published. PublishModuleLocation=Module '{0}' was found in '{1}'. InvalidModuleToPublish=Module '{0}' cannot be published because it does not have a module manifest file. Run New-ModuleManifest -Path to create a module manifest with metadata before publishing. MissingRequiredManifestKeys=Module '{0}' cannot be published because it is missing required metadata. Verify that the module manifest specifies Description and Author. InvalidCharactersInPrereleaseString=The Prerelease string '{0}' contains invalid characters. Please ensure that only characters 'a-zA-Z0-9' and possibly hyphen ('-') at the beginning are in the Prerelease string. IncorrectVersionPartsCountForPrereleaseStringUsage=Version '{0}' must have exactly 3 parts for a Prerelease string to be used. ModuleVersionShouldBeGreaterThanGalleryVersion=Module '{0}' with version '{1}' cannot be published. The version must exceed the current version '{2}' that exists in the repository '{3}', or you must specify -Force. ModuleVersionIsAlreadyAvailableInTheGallery=The module '{0}' with version '{1}' cannot be published as the current version '{2}' is already available in the repository '{3}'. CouldNotInstallNuGetProvider=NuGet provider is required to interact with NuGet-based repositories. Please ensure that '{0}' or newer version of NuGet provider is installed. CouldNotInstallNuGetExe=NuGet.exe version '{0}' or newer, or dotnet command version '{1}' or newer is required to interact with NuGet-based repositories. Please ensure that NuGet.exe or dotnet command is available under one of the paths specified in PATH environment variable value. CouldNotUpgradeNuGetExe=NuGet.exe version '{0}' or newer, or dotnet command version '{1}' or newer is required to interact with NuGet-based repositories. Please ensure that required version of NuGet.exe or dotnet command is available under one of the paths specified in PATH environment variable value. CouldNotFindDotnetCommand=For publish operations, dotnet command version '{0}' or newer is required to interact with the NuGet-based repositories. Please ensure that dotnet command version '{0}' or newer is installed and available under one of the paths specified in PATH environment variable value. You can also install the dotnet command by following the instructions specified at '{1}'. CouldNotInstallNuGetBinaries2=PowerShellGet requires NuGet.exe (or dotnet command) and NuGet provider version '{0}' or newer to interact with the NuGet-based repositories. Please ensure that '{0}' or newer version of NuGet provider is installed and NuGet.exe (or dotnet command) is available under one of the paths specified in PATH environment variable value. InstallNugetBinariesUpgradeShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe and minimum version '{1}' of NuGet provider to publish an item to NuGet-based repositories. The NuGet provider must be available in '{2}' or '{3}'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion {0} -Force'. NuGet.exe must be available in '{4}' or '{5}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://go.microsoft.com/fwlink/?linkid=875534. Do you want PowerShellGet to upgrade NuGet.exe to the latest version and install the NuGet provider now? InstallNuGetBinariesShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe and minimum version '{1}' of NuGet provider to publish an item to NuGet-based repositories. The NuGet provider must be available in '{3}' or '{3}'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion {0} -Force'. NuGet.exe must be available in '{4}' or '{5}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://go.microsoft.com/fwlink/?linkid=875534. Do you want PowerShellGet to install both the latest NuGet.exe and NuGet provider now? InstallNugetExeUpgradeShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in '{1}' or '{2}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://aka.ms/installing-powershellget . Do you want PowerShellGet to upgrade to the latest version of NuGet.exe now? InstallNuGetExeShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in '{1}' or '{2}', or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://aka.ms/installing-powershellget . Do you want PowerShellGet to install the latest version of NuGet.exe now? InstallNuGetProviderShouldContinueQuery=This version of PowerShellGet requires minimum version '{0}' of NuGet provider to publish an item to NuGet-based repositories. The NuGet provider must be available in '{1}' or '{2}'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion {0} -Force'. Do you want PowerShellGet to install and import the NuGet provider now? InstallNuGetBinariesUpgradeShouldContinueCaption=NuGet.exe upgrade and NuGet provider installation are required to continue InstallNuGetBinariesShouldContinueCaption=NuGet.exe and NuGet provider installation are required to continue InstallNuGetExeUpgradeShouldContinueCaption=NuGet.exe upgrade is required to continue InstallNuGetExeShouldContinueCaption=NuGet.exe is required to continue InstallNuGetProviderShouldContinueCaption=NuGet provider is required to continue DownloadingNugetExe=Installing NuGet.exe. DownloadingNugetProvider=Installing NuGet provider. ModuleNotFound=Module '{0}' was not found. NoMatchFound=No match was found for the specified search criteria and module names '{0}'. NoMatchFoundForScriptName=No match was found for the specified search criteria and script names '{0}'. MatchInvalidType=The name '{0}' is a {1} not a {2}. FailedToCreateCompressedModule=Failed to generate the compressed file for module '{0}'. FailedToPublish=Failed to publish module '{0}': '{1}'. PublishedSuccessfully=Successfully published module '{0}' to the module publish location '{1}'. Please allow few minutes for '{2}' to show up in the search results. InvalidWebUri=The specified Uri '{0}' for parameter '{1}' is an invalid Web Uri. Please ensure that it meets the Web Uri requirements. RepositoryAlreadyRegistered=The repository could not be registered because there exists a registered repository with Name '{0}' and SourceLocation '{1}'. To register another repository with Name '{2}', please unregister the existing repository using the Unregister-PSRepository cmdlet. RepositoryToBeUnregisteredNotFound=The repository '{0}' was not removed because no repository was found with that name. Please run Get-PSRepository and ensure that a repository of that name is present. RepositoryCannotBeUnregistered=The specified repository '{0}' cannot be unregistered. RepositoryNotFound=No repository with the name '{0}' was found. PSGalleryNotFound=Unable to find repository '{0}'. Use Get-PSRepository to see all available repositories. Try again after specifying a valid repository name. You can use 'Register-PSRepository -Default' to register the PSGallery repository. ParameterIsNotAllowedWithPSGallery=The PSGallery repository has pre-defined locations. The '{0}' parameter is not allowed, try again after removing the '{0}' parameter. UseDefaultParameterSetOnRegisterPSRepository=Use 'Register-PSRepository -Default' to register the PSGallery repository. RepositoryNameContainsWildCards=The repository name '{0}' should not have wildcards, correct it and try again. InvalidRepository=The specified repository '{0}' is not a valid registered repository name. Please ensure that '{1}' is a registered repository. RepositoryCannotBeRegistered=The specified repository '{0}' is unauthorized and cannot be registered. Try running with -Credential. RepositoryRegistered=Successfully registered the repository '{0}' with source location '{1}'. RepositoryUnregistered=Successfully unregistered the repository '{0}'. PSGalleryPublishLocationIsMissing=The specified repository '{0}' does not have a valid PublishLocation. Retry after setting the PublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. PSRepositoryScriptPublishLocationIsMissing=The specified repository '{0}' does not have a valid ScriptPublishLocation. Retry after setting the ScriptPublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. ScriptSourceLocationIsMissing=The specified repository '{0}' does not have a valid ScriptSourceLocation. Retry after setting the ScriptSourceLocation for repository '{0}' to a valid NuGet endpoint for scripts using the Set-PSRepository cmdlet. PublishModuleSupportsOnlyNuGetBasedPublishLocations=Publish-Module only supports the NuGet-based publish locations. The PublishLocation '{0}' of the repository '{1}' is not a NuGet-based publish location. Retry after setting the PublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. PublishScriptSupportsOnlyNuGetBasedPublishLocations=Publish-Script only supports the NuGet-based publish locations. The ScriptPublishLocation '{0}' of the repository '{1}' is not a NuGet-based publish location. Retry after setting the ScriptPublishLocation for repository '{1}' to a valid NuGet publishing endpoint using the Set-PSRepository cmdlet. DynamicParameterHelpMessage=The dynamic parameter '{0}' is required for Find-Module and Install-Module when using the PackageManagement provider '{1}' and source location '{2}'. Please enter your value for the '{3}' dynamic parameter: ProviderApiDebugMessage=In PowerShellGet Provider - '{0}'. ModuleUninstallNotSupported=Module uninstallation is not supported. To remove a module, please delete the module folder. FastPackageReference=The FastPackageReference is '{0}'. PackageManagementProviderIsNotAvailable=The specified PackageManagement provider '{0}' is not available. SpecifiedSourceName=Using the specified source names : '{0}'. SpecifiedLocationAndOGP=The specified Location is '{0}' and PackageManagementProvider is '{1}'. NoSourceNameIsSpecified=The -Repository parameter was not specified. PowerShellGet will use all of the registered repositories. GettingPackageManagementProviderObject=Getting the provider object for the PackageManagement Provider '{0}'. InvalidInputObjectValue=Invalid value is specified for InputObject parameter. SpecifiedInstallationScope=The installation scope is specified to be '{0}'. SourceLocationValueForPSGalleryCannotBeChanged=The SourceLocation value for the PSGallery repository can not be changed. PublishLocationValueForPSGalleryCannotBeChanged=The PublishLocation value for the PSGallery repository can not be changed. SpecifiedProviderName=The specified PackageManagement provider name '{0}'. ProviderNameNotSpecified=User did not specify the PackageManagement provider name, trying with the provider name '{0}'. SpecifiedProviderNotAvailable=The specified PackageManagement provider '{0}' is not available. SpecifiedProviderDoesnotSupportPSModules=The specified PackageManagement Provider '{0}' does not support PowerShell Modules. PackageManagement Providers must support the 'supports-powershell-modules' feature. PollingPackageManagementProvidersForLocation=Polling available PackageManagement Providers to find one that can support the specified source location '{0}'. PollingSingleProviderForLocation=Resolving the source location '{0}' with PackageManagement Provider '{1}'. FoundProviderForLocation=The PackageManagement provider '{0}' supports the source location '{1}'. SpecifiedLocationCannotBeRegistered=The specified location '{0}' cannot be registered. RepositoryDetails=Repository details, Name = '{0}', Location = '{1}'; IsTrusted = '{2}'; IsRegistered = '{3}'. NotSupportedPowerShellGetFormatVersion=The specified module '{0}' with PowerShellGetFormatVersion '{1}' is not supported by the current version of PowerShellGet. Get the latest version of the PowerShellGet module to install this module, '{2}'. NotSupportedPowerShellGetFormatVersionScripts=The specified script '{0}' with PowerShellGetFormatVersion '{1}' is not supported by the current version of PowerShellGet. Get the latest version of the PowerShellGet module to install this script, '{2}'. PathNotFound=Cannot find the path '{0}' because it does not exist. ModuleIsNotTrusted=Untrusted module '{0}'. ScriptIsNotTrusted=Untrusted script '{0}'. SkippedModuleDependency=Because dependent module '{0}' was skipped in the module dependencies list, users might not know how to install it. MissingExternallyManagedModuleDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current module '{1}', ensure that its dependent module '{2}' is installed. ExternallyManagedModuleDependencyIsInstalled=The externally managed, dependent module '{0}' is already installed on this computer. ScriptMissingExternallyManagedModuleDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current script '{1}', ensure that its dependent module '{2}' is installed. ScriptMissingExternallyManagedScriptDependency=The externally managed, dependent module '{0}' is not installed on this computer. To use the current script '{1}', ensure that its dependent script '{2}' is installed. ScriptExternallyManagedScriptDependencyIsInstalled=The externally managed, dependent script '{0}' is already installed on this computer. UnableToResolveModuleDependency=PowerShellGet cannot resolve the module dependency '{0}' of the module '{1}' on the repository '{2}'. Verify that the dependent module '{3}' is available in the repository '{4}'. If this dependent module '{5}' is managed externally, add it to the ExternalModuleDependencies entry in the PSData section of the module manifest. FindingModuleDependencies=Finding module dependencies for version '{1}' of the module '{0}' from repository '{2}'. InstallingDependencyModule=Installing the dependency module '{0}' with version '{1}' for the module '{2}'. InstallingDependencyScript=Installing the dependency script '{0}' with version '{1}' for the script '{2}'. SavingDependencyModule=Saving the dependency module '{0}' with version '{1}' for the module '{2}'. SavingDependencyScript=Saving the dependency script '{0}' with version '{1}' for the script '{2}'. ModuleUninstallationSucceeded=Successfully uninstalled the module '{0}' from module base '{1}'. ScriptUninstallationSucceeded=Successfully uninstalled the script '{0}' from script base '{1}'. AdminPrivilegesRequiredForUninstall=You cannot uninstall the module '{0}' from '{1}' because Administrator rights are required to uninstall from that folder. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). AdminPrivilegesRequiredForScriptUninstall=You cannot uninstall the script '{0}' from '{1}' because Administrator rights are required to uninstall from that folder. Log on to the computer with an account that has Administrator rights, and then try again. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). ModuleUninstallationNotPossibleAsItIsNotInstalledUsingPowerShellGet=Module '{0}' was not installed on this computer by using either the PowerShellGet cmdlets or the PowerShellGet provider, so it cannot be uninstalled. ScriptUninstallationNotPossibleAsItIsNotInstalledUsingPowerShellGet=Script '{0}' was not installed on this computer by using either the PowerShellGet cmdlets or the PowerShellGet provider, so it cannot be uninstalled. UnableToUninstallModuleVersion=The module '{0}' of version '{1}' in module base folder '{2}' was installed without side-by-side version support. Some versions are installed in this module base with side-by-side version support. Uninstall other versions of this module before uninstalling the most current version. UnableToUninstallAsOtherModulesNeedThisModule=The module '{0}' of version '{1}' in module base folder '{2}' cannot be uninstalled, because one or more other modules '{3}' are dependent on this module. Uninstall the modules that depend on this module before uninstalling module '{4}'. UnableToUninstallAsOtherScriptsNeedThisScript=The script '{0}' of version '{1}' in script base folder '{2}' cannot be uninstalled, because one or more other scripts '{3}' are dependent on this script. Uninstall the scripts that depend on this script before uninstalling script '{4}'. RepositoryIsNotTrusted=Untrusted repository QueryInstallUntrustedPackage=You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from '{1}'? QueryInstallUntrustedScriptPackage=You are installing the scripts from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the scripts from '{1}'? QuerySaveUntrustedPackage=You are downloading the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to download the modules from '{1}'? QuerySaveUntrustedScriptPackage=You are downloading the scripts from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to download the scripts from '{1}'? SourceNotFound=Unable to find repository '{0}'. Use Get-PSRepository to see all available repositories. PSGalleryApiV2Deprecated=PowerShell Gallery v2 has been deprecated. Please run 'Update-Module -Name PowerShellGet' to update to PowerShell Gallery v3. For more information, please visit our website at 'https://www.powershellgallery.com'. PSGalleryApiV2Discontinued=PowerShell Gallery v2 has been discontinued. Please run 'Update-Module -Name PowerShellGet' to update to PowerShell Gallery v3. For more information, please visit our website at 'https://www.powershellgallery.com'. PowerShellGalleryUnavailable=PowerShell Gallery is currently unavailable. Please try again later. PowerShellGetModuleIsNotInstalledProperly=The PowerShellGet module was not installed properly. Be sure that only one instance or version of the PowerShellGet module is installed in the path '{0}'. PowerShelLGetModuleGotUpdated=The PowerShellGet module was updated successfully. Restart the process to use the updated version of the PowerShellGet module. TagsShouldBeIncludedInManifestFile=Tags are now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' in '{1}' with the newest tag changes. You can run Update-ModuleManifest -Tags to update the manifest with tags. ReleaseNotesShouldBeIncludedInManifestFile=ReleaseNotes is now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' in '{1}' with the newest ReleaseNotes changes. You can run Update-ModuleManifest -ReleaseNotes to update the manifest with ReleaseNotes. LicenseUriShouldBeIncludedInManifestFile=LicenseUri is now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' with the newest LicenseUri changes. You can run Update-ModuleManifest -LicenseUri to update the manifest with LicenseUri. IconUriShouldBeIncludedInManifestFile=IconUri is now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' in '{1}' with the newest IconUri changes. You can run Update-ModuleManifest -IconUri to update the manifest with IconUri. ProjectUriShouldBeIncludedInManifestFile=ProjectUri is now supported in the module manifest file (.psd1). Update the module manifest file of module '{0}' in '{1}' with the newest ProjectUri changes. You can run Update-ModuleManifest -ProjectUri to update the manifest with ProjectUri. ShouldIncludeFunctionsToExport=This module '{0}' has exported functions. As a best practice, include exported functions in the module manifest file(.psd1). You can run Update-ModuleManifest -FunctionsToExport to update the manifest with ExportedFunctions field. ShouldIncludeCmdletsToExport=This module '{0}' has exported cmdlets. As a best practice, include exported cmdlets in the module manifest file(.psd1). You can run Update-ModuleManifest -CmdletsToExport to update the manifest with ExportedCmdlets field. ShouldIncludeDscResourcesToExport=This module '{0}' has exported DscResources. As a best practice, include exported DSC resources in the module manifest file(.psd1). If your PowerShell version is higher than 5.0, run Update-ModuleManifest -DscResourcesToExport to update the manifest with ExportedDscResources field. UpdateModuleManifestPathCannotFound=Cannot load the manifest file '{0}' properly. Please specify the correct manifest path. UpdatedModuleManifestNotValid=Cannot update the manifest file '{0}' because the manifest is not valid. Verify that the manifest file is valid, and then try again.'{1}' ExportedDscResourcesNotSupportedOnLowerPowerShellVersion=The ExportedDscResources property is not supported in module manifests on PowerShell versions that are older than 5.0. Remove the value for the parameter 'DscResourcesToExport', and then try again. CompatiblePSEditionsNotSupportedOnLowerPowerShellVersion=The CompatiblePSEditions property is not supported in module manifests on PowerShell versions that are older than 5.1. Remove the value for the parameter 'CompatiblePSEditions', and then try again. ExternalModuleDependenciesNotSpecifiedInRequiredOrNestedModules='{0}' is listed in ExternalModuleDependencies, but it is not found in either the RequiredModules or NestedModules properties. Verify that this module is required for ExternalModuleDependencies, and then add it to NestedModules or RequiredModules. TestModuleManifestFail=Cannot update the manifest properly. '{0}' PackageManagementProvidersNotInModuleBaseFolder=PackageManagementProvider '{0}' is not found in the module base '{1}'. Verify that the PackageManagementProvider specified is within the module base. UpdateManifestContentMessage=Update manifest file with new contents: InvalidPackageManagementProviderValue=The PackageManagementProvider value cannot be '{0}'. Valid values for provider names include '{1}', and the default value for this parameter is '{2}'. PowerShellGetUpdateIsNotSupportedOnLowerPSVersions=Self update of the PowerShellGet module is supported only in PowerShell 5.0 and newer releases. It is not supported in PowerShell 3.0 or 4.0. ScriptVersionShouldBeGreaterThanGalleryVersion=Script '{0}' with version '{1}' cannot be published. The version must exceed the current version '{2}' that exists in the repository '{3}', or you must specify -Force. ScriptVersionIsAlreadyAvailableInTheGallery=The script '{0}' with version '{1}' cannot be published as the current version '{2}' is already available in the repository '{3}'. ScriptPrereleaseStringShouldBeGreaterThanGalleryPrereleaseString=Script '{0}' with version '{1}' and prerelease '{2}' cannot be published. The prerelease string must exceed the current prerelease string '{3}' that exists in the repository '{4}', or you must specify -Force. ScriptParseError=The specified script file '{0}' has parse errors, try again after fixing the parse errors. InvalidScriptToPublish=Script file '{0}' cannot be published because it does not have the required script metadata. Run Update-ScriptFileInfo -Path '{1}' to add the script metadata. FailedToCreateCompressedScript=Failed to generate the compressed file for script '{0}'. FailedToPublishScript=Failed to publish script '{0}': '{1}'. PublishedScriptSuccessfully=Successfully published script '{0}' to the publish location '{1}'. Please allow few minutes for '{2}' to show up in the search results. UnableToResolveScriptDependency=PowerShellGet cannot resolve the {0} dependency '{1}' of the script '{2}' on the repository '{3}'. Verify that the dependent {0} '{1}' is available in the repository '{3}'. If this dependent {0} '{1}' is managed externally, add it to the '{4}' entry in the script metadata. InvalidVersion=Cannot convert value '{0}' to type 'System.Version'. InvalidGuid=Cannot convert value '{0}' to type 'System.Guid'. InvalidParameterValue=The specified value '{0}' for the parameter '{1}' is invalid. Ensure that it does not contain '<#' or '#>'. MissingPSScriptInfo=PSScriptInfo is not specified in the script file '{0}'. You can use the Update-ScriptFileInfo with -Force or New-ScriptFileInfo cmdlet to add the PSScriptInfo to the script file. MissingRequiredPSScriptInfoProperties=Script '{0}' is missing required metadata properties. Verify that the script file has Version, Guid, Description and Author properties. You can use the Update-ScriptFileInfo or New-ScriptFileInfo cmdlet to add or update the PSScriptInfo to the script file. SkippedScriptDependency=Because dependent script '{0}' was skipped in the script dependencies list, users might not know how to install it. SourceLocationPathsForModulesAndScriptsShouldBeEqual=SourceLocation '{0}' and ScriptSourceLocation '{1}' should be same for SMB Share or Local directory based repositories. PublishLocationPathsForModulesAndScriptsShouldBeEqual=PublishLocation '{0}' and ScriptPublishLocation '{1}' should be same for SMB Share or Local directory based repositories. SpecifiedNameIsAlearyUsed=The specified name '{0}' is already used for a different item on the specified repository '{1}'. Run '{2} -Name {0} -Repository {1}' to check whether the specified name '{0}' is already taken. InvalidScriptFilePath=The script file path '{0}' is not valid. The value of the Path argument must resolve to a single file that has a '.ps1' extension. Change the value of the Path argument to point to a valid ps1 file, and then try again. NuGetApiKeyIsRequiredForNuGetBasedGalleryService=NuGetApiKey is required for publishing a module or script file to the specified repository '{0}' whose publish location is '{1}'. Try again after specifying a valid value for the NuGetApiKey parameter. To get your API key, view your profile page. ScriptFileExist=The specified script file '{0}' already exists. InvalidEnvironmentVariableName=The specified environment variable name '{0}' exceeded the allowed limit of '{1}' characters. PublishLocation=Publish Location:'{0}'. ScriptPATHPromptCaption=PATH Environment Variable Change ScriptPATHPromptQuery=Your system has not been configured with a default script installation path yet, which means you can only run a script by specifying the full path to the script file. This action places the script into the folder '{0}', and adds that folder to your PATH environment variable. Do you want to add the script installation path '{0}' to the PATH environment variable? AddedScopePathToProcessSpecificPATHVariable=Added scripts installation location '{0}' for '{1}' scope to process specific PATH environment variable. AddedScopePathToPATHVariable=Added scripts installation location '{0}' for '{1}' scope to PATH environment variable. FilePathInFileListNotWithinModuleBase=Path '{0}' defined in FileList is not within module base '{1}'. Provide the correct FileList parameters and then try again. ManifestFileReadWritePermissionDenied=The current user does not have read-write permissions for the file:'{0}'. Check the file permissions and then try again. MissingTheRequiredPathOrPassThruParameter=The Path or PassThru parameter is required for creating the script file info. A new script file will be created with the script file info when the Path parameter is specified. Script file info will be returned if the PassThru parameter is specified. Try again after specifying the required parameter. DescriptionParameterIsMissingForAddingTheScriptFileInfo=Description parameter is missing for adding the metadata to the script file. Try again after specifying the description. UnableToAddPSScriptInfo=Unable to add PSScriptInfo to the script file '{0}'. You can use the New-ScriptFileInfo cmdlet to add the metadata to the existing script file. RegisterVSTSFeedAsNuGetPackageSource=Publishing to a VSTS package management feed '{0}' requires it to be registered as a NuGet package source. Retry after adding this source '{0}' as NuGet package source by following the instructions specified at '{1}' InvalidModuleAuthenticodeSignature=The module '{0}' cannot be installed or updated because the authenticode signature of the file '{1}' is not valid. InvalidCatalogSignature=The module '{0}' cannot be installed because the catalog signature in '{1}' does not match the hash generated from the module. AuthenticodeIssuerMismatch=Authenticode issuer '{0}' of the new module '{1}' with version '{2}' from root certificate authority '{3}' is not matching with the authenticode issuer '{4}' of the previously-installed module '{5}' with version '{6}' from root certificate authority '{7}'. If you still want to install or update, use -SkipPublisherCheck parameter. ModuleCommandAlreadyAvailable=The following commands are already available on this system:'{0}'. This module '{1}' may override the existing commands. If you still want to install this module '{1}', use -AllowClobber parameter. CatalogFileFound=Found the catalog file '{0}' in the module '{1}' contents. CatalogFileNotFoundInAvailableModule=Catalog file '{0}' is not found in the contents of the previously-installed module '{1}' with the same name. CatalogFileNotFoundInNewModule=Catalog file '{0}' is not found in the contents of the module '{1}' being installed. ValidAuthenticodeSignature=Valid authenticode signature found in the catalog file '{0}' for the module '{1}'. ValidAuthenticodeSignatureInFile=Valid authenticode signature found in the file '{0}' for the module '{1}'. ValidatingCatalogSignature=Validating the '{0}' module files for catalog signing using the catalog file '{1}'. AuthenticodeIssuerMatch=Authenticode issuer '{0}' of the new module '{1}' with version '{2}' matches with the authenticode issuer '{3}' of the previously-installed module '{4}' with version '{5}'. ValidCatalogSignature=The catalog signature in '{0}' of the module '{1}' is valid and matches with the hash generated from the module contents. SkippingPublisherCheck=Skipping the Publisher check for the version '{0}' of module '{1}'. SourceModuleDetailsForPublisherValidation=For publisher validation, using the previously-installed module '{0}' with version '{1}' under '{2}' with publisher name '{3}' from root certificate authority '{4}'. Is this module signed by Microsoft: '{5}'. NewModuleVersionDetailsForPublisherValidation=For publisher validation, current module '{0}' with version '{1}' with publisher name '{2}' from root certificate authority '{3}'. Is this module signed by Microsoft: '{4}'. PublishersMatch=Publisher '{0}' of the new module '{1}' with version '{2}' matches with the publisher '{3}' of the previously-installed module '{4}' with version '{5}'. Both versions are signed with a Microsoft root certificate. PublishersMismatch=A Microsoft-signed module named '{0}' with version '{1}' that was previously installed conflicts with the new module '{2}' from publisher '{3}' with version '{4}'. Installing the new module may result in system instability. If you still want to install or update, use -SkipPublisherCheck parameter. ModuleIsNotCatalogSigned=The version '{0}' of the module '{1}' being installed is not catalog signed. Ensure that the version '{0}' of the module '{1}' has the catalog file '{2}' and signed with the same publisher '{3}' as the previously-installed module '{1}' with version '{4}' under the directory '{5}'. If you still want to install or update, use -SkipPublisherCheck parameter. SentEnvironmentVariableChangeMessage=Successfully broadcasted the Environment variable changes. UnableToSendEnvironmentVariableChangeMessage=Error in broadcasting the Environment variable changes. LicenseUriNotSpecified='LicenseUri' is not specified. 'LicenseUri' must be provided when user license acceptance is required. LicenseTxtNotFound=License.txt not Found. License.txt must be provided when user license acceptance is required. LicenseTxtEmpty=License.txt is empty. requireLicenseAcceptanceNotSupported=Require License Acceptance is not supported on Format version '{0}'. AcceptanceLicenseQuery=Do you accept the license terms for module '{0}'. ForceAcceptLicense=License Acceptance is required for module '{0}'. Please specify '-AcceptLicense' to perform this operation. InvalidValueBoolean=The specified value '{0}' for the parameter '{1}' is invalid. It should be $true or $false. UserDeclinedLicenseAcceptance=User declined license acceptance. AcceptLicense=License Acceptance RequiredScriptVersion=REQUIREDSCRIPTS: Required version of script '{0}' is '{1}'. RequiredScriptVersoinFormat=, :, :[], :[,], :[,] FailedToParseRequiredScripts=Cannot parse REQUIREDSCRIPTS '{0}'. Acceptable formats are: '{1}'. FailedToParseRequiredScriptsVersion=Version format error: {0}, '{1}'. Acceptable formats are: '{2}'. PublishersMismatchAsWarning=Module '{0}' version '{1}' published by '{2}' will be superceded by version '{3}' published by '{4}'. If you do not trust the new publisher, uninstall the module. UnableToDownloadThePackage=The PackageManagement provider '{0}' is unable to download the package '{1}' version '{2}' to '{3}' path. ValidatingTheModule=Validating the '{0}' module contents under '{1}' path. ModuleValidationFailed=Unable to validate the '{0}' module contents under '{1}' path. ValidatedModuleManifestFile=Test-ModuleManifest successfully validated the module manifest file '{0}'. ValidateModuleAuthenticodeSignature=Validating the authenticode signature and publisher of the catalog file or module manifest file of the module '{0}'. ValidateModuleCommandAlreadyAvailable=Checking for possible command collisions for the module '{0}' commands. UnauthorizedAccessError=Access to the path '{0}' is denied. ###PSLOC '@ # SIG # Begin signature block # MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAUMj4wb0r0V933 # Kxwf6eV800i5N8GnGMPseytvd1lnEKCCDYEwggX/MIID56ADAgECAhMzAAABh3IX # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB # znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH # sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d # weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ # itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV # Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy # S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K # NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV # BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr # qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx # zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe # yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g # yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf # AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI # 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 # GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea # jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgLIu7f8le # gQ0ndTd8gyAEe0fnNBrhCsEUfHD+upEyb7owQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQCWjkfbjU+QuaidvjCSfT9ZWmVRt7eVzD7wasyZEXwC # uPWX5GwoR4xPMYzA/ohAElfSdrP6HWH9R+2sdXnUCJK+7w6N9wbB+S9Da0r3UcHj # acV7JEftJK6ZwZ9EyebTgHv6Oa0xhuaRh/BINH4OhfIZEy3A2Lg4KhvNMjGKHTi2 # agmGkKLUqySkyv48c/HCApLbZ3ugNqI06C284kostGCqpuSg6HRDcGPDzZSkT6l4 # u1veNMmcpeGzd3NTNy5fkt8jQt3OUQ9NmfYN5nxh1BgDB1kyDm5lWMiZqFqlLS9T # sG/jgYnqnprYVqTa8rYsSdmn8y1idSkQ1M715PQlOYNuoYIS8TCCEu0GCisGAQQB # gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIOjiV1RbUVB0CleqHpkpsACXwys4XtATfTNyD7Bu # Zv+1AgZfYPphXsEYEzIwMjAwOTIyMjIxOTUxLjg2N1owBIACAfSggdSkgdEwgc4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjowQTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABJy9uo++RqBmoAAAA # AAEnMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # MB4XDTE5MTIxOTAxMTQ1OVoXDTIxMDMxNzAxMTQ1OVowgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowQTU2 # LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPgB3nERnk6fS40vvWeD # 3HCgM9Ep4xTIQiPnJXE9E+HkZVtTsPemoOyhfNAyF95E/rUvXOVTUcJFL7Xb16jT # KPXONsCWY8DCixSDIiid6xa30TiEWVcIZRwiDlcx29D467OTav5rA1G6TwAEY5rQ # jhUHLrOoJgfJfakZq6IHjd+slI0/qlys7QIGakFk2OB6mh/ln/nS8G4kNRK6Do4g # xDtnBSFLNfhsSZlRSMDJwFvrZ2FCkaoexd7rKlUNOAAScY411IEqQeI1PwfRm3aW # bS8IvAfJPC2Ah2LrtP8sKn5faaU8epexje7vZfcZif/cbxgUKStJzqbdvTBNc93n # /Z8CAwEAAaOCARswggEXMB0GA1UdDgQWBBTl9JZVgF85MSRbYlOJXbhY022V8jAf # BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG # AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAKyo180VXHBqVnjZwQy7NlzXbo2+W5 # qfHxR7ANV5RBkRkdGamkwUcDNL+DpHObFPJHa0oTeYKE0Zbl1MvvfS8RtGGdhGYG # CJf+BPd/gBCs4+dkZdjvOzNyuVuDPGlqQ5f7HS7iuQ/cCyGHcHYJ0nXVewF2Lk+J # lrWykHpTlLwPXmCpNR+gieItPi/UMF2RYTGwojW+yIVwNyMYnjFGUxEX5/DtJjRZ # mg7PBHMrENN2DgO6wBelp4ptyH2KK2EsWT+8jFCuoKv+eJby0QD55LN5f8SrUPRn # K86fh7aVOfCglQofo5ABZIGiDIrg4JsV4k6p0oBSIFOAcqRAhiH+1spCMIIGcTCC # BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN # MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 # VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw # RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe # dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx # Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G # kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA # AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 # fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g # AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB # BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA # bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh # IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS # +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK # kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon # /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi # PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ # fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII # YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow # QTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAs5W4TmyDHMRM7iz6mgGojqvXHzOggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOMUsu8wIhgPMjAyMDA5MjIyMTI5MTlaGA8yMDIwMDkyMzIxMjkxOVowdzA9Bgor # BgEEAYRZCgQBMS8wLTAKAgUA4xSy7wIBADAKAgEAAgIVPgIB/zAHAgEAAgIRtjAK # AgUA4xYEbwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAEMD4esQRMLwQdhk # Co1zgvmclcwl3lYYpk1oMh1ndsU3+97Rt6FV3adS4Hezc/K94oQKjcxtMVzLzQhG # agM6XlqB31VD8n2nxVuaWD1yp2jm/0IvfL9nFMHJRhgANMiBdHqvqNrd86c/Kryq # sI0Ch0sOx9wg3BozzqQhmdNjf9c6MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAEnL26j75GoGagAAAAAAScwDQYJYIZIAWUD # BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # CQQxIgQg6wLE4god37E/zRxYUqM58DbGQBLJxEvt6c6xU1jsZqYwgfoGCyqGSIb3 # DQEJEAIvMYHqMIHnMIHkMIG9BCAbkuhLEoYdahb/BUyVszO2VDi6kB3MSaof/+8u # 7SM+IjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # Jy9uo++RqBmoAAAAAAEnMCIEIK4r6N3NISekswMCG1kSBJCCCePrlLDQWbMKz0wt # Lj6CMA0GCSqGSIb3DQEBCwUABIIBACY3cZHtQ2k6zfVG4NotND24Zp0mlZAFe4A+ # BFDISaaIXS86mO142Y9Hkm9rAMBCIJbKB/Yj1yf1gbygqNCoJDHFxZbdmx5XbShg # PVfAQl00TuyQ86VYnFJtvdDODUnwuxErAFR1zyiGGBKYtQWhXNRmbhb1kRkWp5Fg # s9mwzZUoGI2UzXDtYb6HkjNIOMxCbWW/27YfCBnOkiP2tJMIU79R4xY4vwQeWkKW # dgtyHv+d7eBOd1aOi8HvqCstH6x06RC3DsYfZ/NbYSGuZpjSaSDDC7A1KvdPo6ti # UYayEGi9iXfqnAv4Iyzo3OWIN2M2iqepP2xwL8Ne2q1ImLpiZF0= # SIG # End signature block ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Add-WinGetSource.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Add-WinGetSource --- # Add-WinGetSource ## SYNOPSIS Adds a new source. ## SYNTAX ``` Add-WinGetSource -Name -Argument [-Type ] [-TrustLevel {Default | None | Trusted}] [-Explicit] [] ``` ## DESCRIPTION Adds a new source. A source provides the data for you to discover and install packages. Only add a new source if you trust it as a secure location. This command must be executed with administrator permissions. ## EXAMPLES ### Example 1: Add new REST source named mysource ```powershell Add-WinGetSource -Name mysource -Argument https://contoso.com/ -Type Microsoft.Rest ``` This example adds a new REST source to WinGet named `mysource` with the root URL `https://contoso.com/`. The source must respond with the WinGet REST source API. ## PARAMETERS ### -Argument The URL or UNC of either a pre-indexed WinGet source or a WinGet REST source API. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: True Position: Named Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### -Name The name used to identify the WinGet source. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: True Position: Named Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### -Explicit Excludes a source from discovery unless specified. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -TrustLevel Specify the trust level of the WinGet source. The parameter accepts the following values: - `None` - `Trusted` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSSourceTrustLevel Parameter Sets: (All) Aliases: Accepted values: None, Trusted Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Type The type of the WinGet source. Most sources are `Microsoft.Rest`. The WinGet community repository is `Microsoft.PreIndexed.Package`. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ## OUTPUTS ### System.Object ## NOTES ## RELATED LINKS [Remove-WinGetSource](Remove-WinGetSource.md) [Reset-WinGetSource](Reset-WinGetSource.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Assert-WinGetPackageManager.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Assert-WinGetPackageManager --- # Assert-WinGetPackageManager ## SYNOPSIS Verifies that WinGet is installed properly. ## SYNTAX ### IntegrityVersionSet (Default) ``` Assert-WinGetPackageManager [-Version ] [] ``` ### IntegrityLatestSet ``` Assert-WinGetPackageManager [-Latest] [-IncludePrerelease] [] ``` ## DESCRIPTION Verifies that WinGet is installed properly. > [!NOTE] > The cmdlet doesn't ensure that the latest version of WinGet is installed. It just verifies that > the installed version of Winget is supported by installed version of the Microsoft.WinGet.Client > module. ## EXAMPLES ### Example 1: Default usage ```powershell Assert-WinGetPackageManager ``` If the current version of WinGet is installed correctly, the command returns without error. ### Example 2: Check if latest stable version is installed ```powershell Assert-WinGetPackageManager -Latest ``` If the latest version of WinGet is compatible with the installed Microsoft.WinGet.Client module, the command returns without error. ### Example 3: Check if latest preview version is installed ```powershell Assert-WinGetPackageManager -IncludePreRelease ``` If the prerelease version of WinGet is compatible with the installed Microsoft.WinGet.Client module, the command returns without error. ### Example 4: Check if specific version is installed ```powershell Assert-WinGetPackageManager -Version v1.8.1911 ``` If the specified version of WinGet is compatible with the installed Microsoft.WinGet.Client module, the command returns without error. ## PARAMETERS ### -IncludePreRelease Include preview versions of WinGet. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: IntegrityLatestSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Latest Verify that the latest version of WinGet is compatible with the installed version of the Microsoft.WinGet.Client module. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: IntegrityLatestSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Version Verify that a specific version of WinGet is installed correctly. ```yaml Type: System.String Parameter Sets: IntegrityVersionSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ### System.Management.Automation.SwitchParameter ## OUTPUTS ### System.Object ## NOTES When using the `-Latest` or `-Version` parameters, this cmdlet makes GitHub API requests to query release information. Unauthenticated requests are subject to [GitHub API rate limits](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api), which can cause failures in CI/CD pipelines. If the `GH_TOKEN` or `GITHUB_TOKEN` environment variable is set, the cmdlet automatically uses it to authenticate requests, which significantly increases the rate limit. `GH_TOKEN` takes precedence over `GITHUB_TOKEN`, matching [GitHub CLI behavior](https://cli.github.com/manual/gh_help_environment). In GitHub Actions, you can make `GITHUB_TOKEN` available to the cmdlet by mapping it as an environment variable in your workflow step (e.g., `env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}`). Use `-Verbose` to see which token source is being used. ## RELATED LINKS [Get-WinGetVersion](Get-WinGetVersion.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Disable-WinGetSetting.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Disable-WinGetSetting --- # Disable-WinGetSetting ## SYNOPSIS Disables an administrative setting. ## SYNTAX ``` Disable-WinGetSetting [-Name] [] ``` ## DESCRIPTION This command disables an administrative setting. The command must be run from an elevated session using the **Run as Administrator** option. This command support the following administrative settings: - LocalManifestFiles - BypassCertificatePinningForMicrosoftStore - InstallerHashOverride - LocalArchiveMalwareScanOverride - ProxyCommandLineOptions Administrative settings are disabled by default. Administrative settings can also be managed using Group Policy objects. ## EXAMPLES ### Example 1: Disable the use of local manifest files ```powershell Disable-WinGetSetting -Name LocalManifestFiles ``` This example shows how to disable the use of local manifest files. ## PARAMETERS ### -Name The name of the WinGet administrative setting. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: True Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ## OUTPUTS ### System.Object ## NOTES ## RELATED LINKS [Get-WinGetSetting](Get-WinGetSetting.md) [Set-WinGetUserSetting](Set-WinGetUserSetting.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Enable-WinGetSetting.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Enable-WinGetSetting --- # Enable-WinGetSetting ## SYNOPSIS Enables WinGet administrative settings. ## SYNTAX ``` Enable-WinGetSetting [-Name] [] ``` ## DESCRIPTION This command enables an administrative setting. The command must be run from an elevated session using the **Run as Administrator** option. This command support the following administrative settings: - LocalManifestFiles - BypassCertificatePinningForMicrosoftStore - InstallerHashOverride - LocalArchiveMalwareScanOverride - ProxyCommandLineOptions Administrative settings are disabled by default. Administrative settings can also be managed using Group Policy objects. ## EXAMPLES ### Example 1 ```powershell Enable-WinGetSetting -Name LocalManifestFiles ``` This example shows how to enable the use of local manifest files. ## PARAMETERS ### -Name The name of the WinGet administrative setting. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: True Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ## OUTPUTS ### System.Object ## NOTES ## RELATED LINKS [Get-WinGetSetting](Get-WinGetSetting.md) [Disable-WinGetSetting](Disable-WinGetSetting.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Export-WinGetPackage.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Export-WinGetPackage --- # Export-WinGetPackage ## SYNOPSIS Downloads a WinGet package and its dependencies. ## SYNTAX ### FoundSet (Default) ``` Export-WinGetPackage [-DownloadDirectory ] [-AllowHashMismatch] [-Architecture ] [-InstallerType ] [-Locale ] [-Scope ] [-SkipDependencies] [-Version ] [-Id ] [-Name ] [-Moniker ] [-Source ] [[-Query] ] [-MatchOption ] [-WhatIf] [-Confirm] [] ``` ### GivenSet ``` Export-WinGetPackage [-DownloadDirectory ] [-AllowHashMismatch] [-Architecture ] [-InstallerType ] [-Locale ] [-Scope ] [-SkipDependencies] [[-PSCatalogPackage] ] [-Version ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION This command downloads a WinGet package from a configured source. The command downloads the package, its manifest, as well as its dependencies and their manifests. By default, the packages are downloaded to your `Downloads` folder. You can use the **DownloadDirectory** parameter to change the location. For Microsoft Azure users, if the package is coming from the Microsoft Store, the command also downloads the package license file. The user needs to have one of the following Roles in Azure: - Global Administrator - User Administrator - License Administrator ## EXAMPLES ### Example 1: Download Microsoft.PowerShell to the default location ```powershell Export-WinGetPackage -Id Microsoft.PowerShell ``` ```Output Id Name Source Status ExtendedErrorCode CorrelationData -- ---- ------ ------ ----------------- --------------- Microsoft.PowerShell PowerShell winget Ok ``` ```powershell dir ~\Downloads\Microsoft.PowerShell_7.4.4.0 ``` ```Output Directory: C:\Users\user1\Downloads\Microsoft.PowerShell_7.4.4.0 Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 8/7/2024 2:05 PM 108404736 PowerShell_7.4.4.0_Machine_X64_wix_en-US.msi -a--- 8/7/2024 2:05 PM 3838 PowerShell_7.4.4.0_Machine_X64_wix_en-US.yaml ``` This example shows how to download a package for the current machine and scope. For this example, the command downloads the latest version of the installer package and manifest for Microsoft.PowerShell. The command puts the files a new folder in your `Downloads` location. The folder name is created by concatenating the package identifier and version number. ## PARAMETERS ### -AllowHashMismatch Allows you to download package even when the SHA256 hash for an installer or a dependency does not match the SHA256 hash in the WinGet package manifest. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Architecture Specify the processor architecture for the WinGet package installer. ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSProcessorArchitecture Parameter Sets: (All) Aliases: Accepted values: Default, X86, Arm, X64, Arm64 Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -DownloadDirectory Specify the location where you want to store the downloaded files. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Id Specify the WinGet package identifier ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -InstallerType Specify the type of installer package to download. This parameter accepts the following values: - `Default` - `Inno` - `Wix` - `Msi` - `Nullsoft` - `Zip` - `Msix` - `Exe` - `Burn` - `MSStore` - `Portable` > [!NOTE] > The installer type must be available in the WinGet package manifest. ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageInstallerType Parameter Sets: (All) Aliases: Accepted values: Default, Inno, Wix, Msi, Nullsoft, Zip, Msix, Exe, Burn, MSStore, Portable Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Locale Specify the locale of the installer package. The locale must provided in the BCP 47 format, such as `en-US`. For more information, see [Standard locale names](/globalization/locale/standard-locale-names). > [!NOTE] > The locale must be available in the WinGet package manifest. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -MatchOption Specify the match option for a WinGet package query. This parameter accepts the following values: - `Equals` - `EqualsCaseInsensitive` - `StartsWithCaseInsensitive` - `ContainsCaseInsensitive` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption Parameter Sets: FoundSet Aliases: Accepted values: Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Moniker Specify the moniker of the WinGet package to download. For example, the moniker for the Microsoft.PowerShell package is `pwsh`. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Name Specify the name of the WinGet package. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -PSCatalogPackage Provide **PSCatalogPackage** object. You can get a **PSCatalogPackage** object by using the `Find-WinGetPackage` command. ```yaml Type: Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage Parameter Sets: GivenSet Aliases: InputObject Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### -Query One or more strings to search for in the WinGet package. The command searches for matching strings in the following properties of the package manifest: - `PackageName` - `PackageIdentifier` - `Moniker` - `Tags` ```yaml Type: System.String[] Parameter Sets: FoundSet Aliases: Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Scope Specify WinGet package installer scope. The parameter accepts the following values: - `Any` - `User` - `System` - `UserOrUnknown` - `SystemOrUnknown` > [!NOTE] > The installer scope must be available in the WinGet package manifest. ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageInstallScope Parameter Sets: (All) Aliases: Accepted values: Any, User, System, UserOrUnknown, SystemOrUnknown Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -SkipDependencies Specifies that the command shouldn't download the WinGet package dependencies. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Source Specifies WinGet source. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Version Specifies the version of the WinGet package to download. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Confirm Prompts you for confirmation before running the cmdlet. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: cf Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet isn't run. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: wi Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ### System.Management.Automation.SwitchParameter ### Microsoft.WinGet.Client.PSObjects.PSProcessorArchitecture ### Microsoft.WinGet.Client.PSObjects.PSPackageInstallerType ### Microsoft.WinGet.Client.PSObjects.PSPackageInstallScope ### Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage ### System.String[] ### Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption ## OUTPUTS ### Microsoft.WinGet.Client.Engine.PSObjects.PSDownloadResult ## NOTES ## RELATED LINKS [Find-WinGetPackage](Find-WinGetPackage.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Find-WinGetPackage.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Find-WinGetPackage --- # Find-WinGetPackage ## SYNOPSIS Searches for packages from configured sources. ## SYNTAX ``` Find-WinGetPackage [-Tag ] [-Command ] [-Count ] [-Id ] [-Name ] [-Moniker ] [-Source ] [[-Query] ] [-MatchOption ] [] ``` ## DESCRIPTION Searches for packages from configured sources. ## EXAMPLES ### Example 1: Search for PowerShell ```powershell Find-WinGetPackage PowerShell ``` This example shows how to search for packages related to PowerShell. By default, the command searches all configured sources. The command compares the value provided to the following package manifest properties: - `PackageIdentifier` - `PackageName` - `Moniker` - `Tags` The command does a case-insensitive substring comparison of these properties. ### Example 2: Search for Microsoft.PowerShell by id ```powershell Find-WinGetPackage -Id Microsoft.PowerShell ``` This example shows how to search for packages by package identifier. By default, the command searches all configured sources. The command performs a case-insensitive substring match against the **PackageIdentifier** property of the packages. ### Example 3: Search for Microsoft.PowerShell by exact id ```powershell Find-WinGetPackage -Id Microsoft.PowerShell -MatchOption Equals ``` This example shows how to search for packages by exact package identifier. By default, the command searches all configured sources. The command performs a case-sensitive full text match against the **PackageIdentifier** property of the packages. ## PARAMETERS ### -Command Specify the name of the command defined in the package manifest. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Count Specify the maximum number of results to return. ```yaml Type: System.UInt32 Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Id Specify the package identifier to search for. By default, the command does a case-insensitive substring match. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -MatchOption Specify matching logic used for search. The parameter accepts the following values: - `Equals` - `EqualsCaseInsensitive` - `StartsWithCaseInsensitive` - `ContainsCaseInsensitive` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption Parameter Sets: (All) Aliases: Accepted values: Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Moniker Specify the moniker of the package you are searching for. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Name Specify the name of the package to search for. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Query Specify one or more strings to search for. By default, the command searches all configured sources. The command compares the value provided to the following package manifest properties: - `PackageIdentifier` - `PackageName` - `Moniker` - `Tags` The command does a case-insensitive substring comparison of these properties. ```yaml Type: System.String[] Parameter Sets: (All) Aliases: Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Source Specify the name of the WinGet source to search. The most common sources are `msstore` and `winget`. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Tag Specify a package tag to search for. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ### System.UInt32 ### System.String[] ### Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption ## OUTPUTS ### Microsoft.WinGet.Client.Engine.PSObjects.PSFoundCatalogPackage ## NOTES ## RELATED LINKS [Export-WinGetPackage](Export-WinGetPackage.md) [Install-WinGetPackage](Install-WinGetPackage.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Get-WinGetPackage.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Get-WinGetPackage --- # Get-WinGetPackage ## SYNOPSIS Lists installed packages. ## SYNTAX ``` Get-WinGetPackage [-Tag ] [-Command ] [-Count ] [-Id ] [-Name ] [-Moniker ] [-Source ] [[-Query] ] [-MatchOption ] [] ``` ## DESCRIPTION This command lists all of the packages installed on your system. The output includes packages installed from WinGet sources and packages installed by other methods. Packages that have package identifiers starting with `MSIX` or `ARP` could not be correlated to a WinGet source. ## EXAMPLES ### Example 1: Default example ```powershell Get-WinGetPackage ``` This example shows how to list all packages installed on your system. ### Example 2: Get package by Id ```powershell Get-WinGetPackage -Id "Microsoft.PowerShell" ``` This example shows how to get an installed package by its package identifier. ### Example 3: Get package(s) by name ```powershell Get-WinGetPackage -Name "PowerShell" ``` This example shows how to get installed packages that match a name value. The command does a substring comparison of the provided name with installed package names. ### Example 4: List all packages with an available update ```powershell Get-WinGetPackage | Where-Object IsUpdateAvailable ``` This example shows how to list all packages that have an available upgrade from one of the configured sources. ## PARAMETERS ### -Command Specify the name of the command defined in the package manifest. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Count Limits the number of items returned by the command. ```yaml Type: System.UInt32 Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Id Specify the package identifier for the package you want to list. By default, the command does a case-insensitive substring comparison on the package identifier. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -MatchOption Specify matching logic used for search. The parameter accepts the following values: - `Equals` - `EqualsCaseInsensitive` - `StartsWithCaseInsensitive` - `ContainsCaseInsensitive` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption Parameter Sets: (All) Aliases: Accepted values: Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Moniker Specify the moniker of the package you want to list. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Name Specify the name of the package to list. By default, the command does a case-insensitive comparison of the package name. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Query Specify one or more strings to search for. By default, the command searches all configured sources. The command compares the value provided to the following package manifest properties: - `PackageIdentifier` - `PackageName` - `Moniker` - `Tags` The command does a case-insensitive substring comparison of these properties. ```yaml Type: System.String[] Parameter Sets: (All) Aliases: Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Source Specify the name of the WinGet source of the package. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Tag Specify a package tag to search for. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ### System.UInt32 ### System.String[] ### Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption ## OUTPUTS ### Microsoft.WinGet.Client.Engine.PSObjects.PSInstalledCatalogPackage ## NOTES ## RELATED LINKS [Uninstall-WinGetPackage](Uninstall-WinGetPackage.md) [Update-WinGetPackage](Update-WinGetPackage.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Get-WinGetSetting.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Get-WinGetSetting --- # Get-WinGetSetting ## SYNOPSIS Gets WinGet configuration settings. ## SYNTAX ``` Get-WinGetSetting [-AsPlainText] [] ``` ## DESCRIPTION This command gets the WinGet configuration settings. The settings include: - the schema - administrative settings - the location of the user settings file For more information about WinGet settings, see [WinGet CLI Settings](https://aka.ms/winget-settings). ## EXAMPLES ### Example 1 - Display the WinGet configuration settings ```powershell Get-WinGetSetting ``` ```Output Name Value ---- ----- $schema https://aka.ms/winget-settings-export.schema.json userSettingsFile C:\Users\user1\AppData\Local\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\Local… adminSettings {[ProxyCommandLineOptions, False], [LocalArchiveMalwareScanOverride, False], [InstallerH… ``` ### Example 2 - Display the administrative settings in WinGet configuration ```powershell Get-WinGetSetting | Select-Object -ExpandProperty adminSettings ``` ```Output Name Value ---- ----- InstallerHashOverride False ProxyCommandLineOptions False BypassCertificatePinningForMicrosoftStore False LocalArchiveMalwareScanOverride False LocalManifestFiles True ``` ## PARAMETERS ### -AsPlainText Output results as plain text ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.Management.Automation.SwitchParameter ## OUTPUTS ### System.Object ## NOTES ## RELATED LINKS [Get-WinGetUserSetting](Get-WinGetUserSetting.md) [Set-WinGetUserSetting](Set-WinGetUserSetting.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Get-WinGetSource.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Get-WinGetSource --- # Get-WinGetSource ## SYNOPSIS Lists configured WinGet sources. ## SYNTAX ``` Get-WinGetSource [[-Name] ] [] ``` ## DESCRIPTION Lists the configured WinGet sources. ## EXAMPLES ### Example 1: List all configured WinGet sources ```powershell Get-WinGetSource ``` ```Output Name Argument Type ---- -------- ---- msstore https://storeedgefd.dsx.mp.microsoft.com/v9.0 Microsoft.Rest winget https://cdn.winget.microsoft.com/cache Microsoft.PreIndexed.Package ``` ## PARAMETERS ### -Name Specify the name of the WinGet source to be displayed. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ## OUTPUTS ### Microsoft.WinGet.Client.Engine.PSObjects.PSSourceResult ## NOTES ## RELATED LINKS [Add-WinGetSource](Add-WinGetSource.md) [Remove-WinGetSource](Remove-WinGetSource.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Get-WinGetUserSetting.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Get-WinGetUserSetting --- # Get-WinGetUserSetting ## SYNOPSIS Gets user settings for WinGet. ## SYNTAX ``` Get-WinGetUserSetting [] ``` ## DESCRIPTION This command displays the WinGet settings for the current user. The settings are stored in `$env:LOCALAPPDATA\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json`. This file only exists if you have changed a user setting, for example, using the `Set-WinGetUserSetting` command. ## EXAMPLES ### Example 1: Get the WinGet settings for the current user ```powershell Get-WinGetUserSetting ``` ## PARAMETERS ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### None ## OUTPUTS ### System.Collections.Hashtable ## NOTES ## RELATED LINKS [Get-WinGetSetting](Get-WinGetSetting.md) [Set-WinGetUserSetting](Set-WinGetUserSetting.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Get-WinGetVersion.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Get-WinGetVersion --- # Get-WinGetVersion ## SYNOPSIS Gets the installed version of WinGet. ## SYNTAX ``` Get-WinGetVersion [] ``` ## DESCRIPTION Gets the installed version of WinGet. ## EXAMPLES ### Example 1: Default example ```powershell Get-WinGetVersion ``` Gets the installed version of WinGet. ## PARAMETERS ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### None ## OUTPUTS ### System.String ## NOTES ## RELATED LINKS ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Install-WinGetPackage.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Install-WinGetPackage --- # Install-WinGetPackage ## SYNOPSIS Installs a WinGet Package. ## SYNTAX ### FoundSet (Default) ``` Install-WinGetPackage [-Mode ] [-Override ] [-Custom ] [-Location ] [-Log ] [-Force] [-Header ] [-AllowHashMismatch] [-Architecture ] [-InstallerType ] [-Locale ] [-Scope ] [-SkipDependencies] [-Version ] [-Id ] [-Name ] [-Moniker ] [-Source ] [[-Query] ] [-MatchOption ] [-WhatIf] [-Confirm] [] ``` ### GivenSet ``` Install-WinGetPackage [-Mode ] [-Override ] [-Custom ] [-Location ] [-Log ] [-Force] [-Header ] [-AllowHashMismatch] [-Architecture ] [-InstallerType ] [-Locale ] [-Scope ] [-SkipDependencies] [[-PSCatalogPackage] ] [-Version ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION This command installs a WinGet package from a configured source. The command includes parameters to specify values used to search for packages in the configured sources. By default, the command searches all sources. By default, all string-based searches are case-insensitive substring searches. Wildcards are not supported. You can change the search behavior using the **MatchOption** parameter. ## EXAMPLES ### Example 1: Install a package using a query ```powershell Install-WinGetPackage Microsoft.PowerShell ``` This example show how to install a package using a query. The **Query** parameter is positional, so you don't need to include the parameter name before the query string. ### Example 2: Install a package by Id ```powershell Install-WinGetPackage -Id Microsoft.PowerShell ``` This example shows how to install a package by the specifying the package identifier. If the package identifier is available from more than one source, you must provide additional search criteria to select a specific instance of the package. > **If more than one source is configured with the same package identifier, the user must disambiguate** ### Example 3: Install a package by Name ```powershell Install-WinGetPackage -Name "PowerToys (Preview)" ``` This example shows how to install a package by specifying the package name. ### Example 4: Install a specific version of a package ```powershell Install-WinGetPackage Microsoft.PowerShell -Version 7.4.4.0 ``` This example shows how to install a specific version of a package using a query. The command does a query search for packages matching `Microsoft.PowerShell`. The results of the search a limited to matches with the version of `7.4.4.0`. ## PARAMETERS ### -AllowHashMismatch Allows you to download package even when the SHA256 hash for an installer or a dependency does not match the SHA256 hash in the WinGet package manifest. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Architecture Specify the processor architecture for the WinGet package installer. ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSProcessorArchitecture Parameter Sets: (All) Aliases: Accepted values: Default, X86, Arm, X64, Arm64 Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Custom Use this parameter to pass additional arguments to the installer. The parameter takes a single string value. To add multiple arguments, include the arguments in the string. The arguments must be provided in the format expected by the installer. If the string contains spaces, it must be enclosed in quotes. This string is added to the arguments defined in the package manifest. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Force Force the installer to run even when other checks WinGet would perform would prevent this action. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Header Custom value to be passed via HTTP header to WinGet REST sources. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Id Specify the package identifier to search for. The command does a case-insensitive full text match, rather than a substring match. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -InstallerType A package may contain multiple installer types. Use this parameter to select the installer you want to use. The parameter accepts the following values: - `Default` - `Inno` - `Wix` - `Msi` - `Nullsoft` - `Zip` - `Msix` - `Exe` - `Burn` - `MSStore` - `Portable` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageInstallerType Parameter Sets: (All) Aliases: Accepted values: Default, Inno, Wix, Msi, Nullsoft, Zip, Msix, Exe, Burn, MSStore, Portable Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Locale Specify the locale of the installer package. The locale must provided in the BCP 47 format, such as `en-US`. For more information, see [Standard locale names](/globalization/locale/standard-locale-names). ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Location Specify the file path where you want the packed to be installed. The installer must be able to support alternate install locations. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Log Specify the location for the installer log. The value can be a fully-qualified or relative path and must include the file name. For example: `$env:TEMP\package.log`. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -MatchOption Specify the match option for a WinGet package query. This parameter accepts the following values: - `Equals` - `EqualsCaseInsensitive` - `StartsWithCaseInsensitive` - `ContainsCaseInsensitive` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption Parameter Sets: FoundSet Aliases: Accepted values: Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Mode Specify the output mode for the installer. The parameter accepts the following values: - `Default` - `Silent` - `Interactive` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageInstallMode Parameter Sets: (All) Aliases: Accepted values: Default, Silent, Interactive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Moniker Specify the moniker of the WinGet package to install. For example, the moniker for the Microsoft.PowerShell package is `pwsh`. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Name Specify the name of the package to be installed. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Override Use this parameter to override the existing arguments passed to the installer. The parameter takes a single string value. To add multiple arguments, include the arguments in the string. The arguments must be provided in the format expected by the installer. If the string contains spaces, it must be enclosed in quotes. This string overrides the arguments specified in the package manifest. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -PSCatalogPackage Provide **PSCatalogPackage** object. You can get a **PSCatalogPackage** object by using the `Find-WinGetPackage` command. ```yaml Type: Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage Parameter Sets: GivenSet Aliases: InputObject Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### -Query Specify one or more strings to search for. By default, the command searches all configured sources. The command compares the value provided to the following package manifest properties: - `PackageIdentifier` - `PackageName` - `Moniker` - `Tags` The command does a case-insensitive substring comparison of these properties. ```yaml Type: System.String[] Parameter Sets: FoundSet Aliases: Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Scope Specify WinGet package installer scope. The parameter accepts the following values: - `Any` - `User` - `System` - `UserOrUnknown` - `SystemOrUnknown` > [!NOTE] > The installer scope must be available in the WinGet package manifest. ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageInstallScope Parameter Sets: (All) Aliases: Accepted values: Any, User, System, UserOrUnknown, SystemOrUnknown Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -SkipDependencies Specifies that the command shouldn't install the WinGet package dependencies. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Source Specify the name of the WinGet source from which the package should be installed. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Version Specify the version of the package. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Confirm Prompts you for confirmation before running the cmdlet. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: cf Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet isn't run. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: wi Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### Microsoft.WinGet.Client.PSObjects.PSPackageInstallMode ### System.String ### System.Management.Automation.SwitchParameter ### Microsoft.WinGet.Client.PSObjects.PSProcessorArchitecture ### Microsoft.WinGet.Client.PSObjects.PSPackageInstallerType ### Microsoft.WinGet.Client.PSObjects.PSPackageInstallScope ### Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage ### System.String[] ### Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption ## OUTPUTS ### Microsoft.WinGet.Client.Engine.PSObjects.PSInstallResult ## NOTES ## RELATED LINKS [Find-WinGetPackage](Find-WinGetPackage.md) [Update-WinGetPackage](Update-WinGetPackage.md) [Uninstall-WinGetPackage](Uninstall-WinGetPackage.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Microsoft.WinGet.Client.md ================================================ --- Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 Module Guid: e11157e2-cd24-4250-83b8-c6654ea4926a Download Help Link: Help Version: 1.8.0 Locale: en-US title: Microsoft.WinGet.Client Module --- # Microsoft.WinGet.Client Module ## Description Microsoft WinGet Client Module for the Windows Package Manager ## Microsoft.WinGet.Client Cmdlets ### [Add-WinGetSource](Add-WinGetSource.md) Adds a new WinGet source. ### [Assert-WinGetPackageManager](Assert-WinGetPackageManager.md) Verifies WinGet is installed properly. ### [Disable-WinGetSetting](Disable-WinGetSetting.md) Disables an administrative setting. ### [Enable-WinGetSetting](Enable-WinGetSetting.md) Enables WinGet administrative settings. ### [Export-WinGetPackage](Export-WinGetPackage.md) Downloads a package. ### [Find-WinGetPackage](Find-WinGetPackage.md) Searches configured sources for packages. ### [Get-WinGetPackage](Get-WinGetPackage.md) Gets installed packages. ### [Get-WinGetSetting](Get-WinGetSetting.md) Gets WinGet settings. ### [Get-WinGetSource](Get-WinGetSource.md) Gets configured WinGet sources. ### [Get-WinGetUserSetting](Get-WinGetUserSetting.md) Gets user settings for WinGet. ### [Get-WinGetVersion](Get-WinGetVersion.md) Gets the installed version of WinGet. ### [Install-WinGetPackage](Install-WinGetPackage.md) Install a WinGet Package. ### [Remove-WinGetSource](Remove-WinGetSource.md) Removes a configured source. ### [Repair-WinGetPackage](Repair-WinGetPackage.md) Repairs a WinGet Package. ### [Repair-WinGetPackageManager](Repair-WinGetPackageManager.md) Repairs the WinGet client. ### [Reset-WinGetSource](Reset-WinGetSource.md) Resets default WinGet sources. ### [Set-WinGetUserSetting](Set-WinGetUserSetting.md) Sets WinGet settings. ### [Test-WinGetUserSetting](Test-WinGetUserSetting.md) Tests WinGet settings. ### [Uninstall-WinGetPackage](Uninstall-WinGetPackage.md) Uninstalls a WinGet Package. ### [Update-WinGetPackage](Update-WinGetPackage.md) Updates a WinGet Package. ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Remove-WinGetSource.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Remove-WinGetSource --- # Remove-WinGetSource ## SYNOPSIS Removes a configured source. ## SYNTAX ``` Remove-WinGetSource -Name [] ``` ## DESCRIPTION This command removes a configured WinGet source. By default, there are two sources registered: `msstore` and `winget`. You can add more sources using `Add-WinGetSource`. This command must be executed with administrator permissions. ## EXAMPLES ### Example 1: Remove a single source by name ```powershell Remove-WinGetSource -Name msstore ``` The example shows how to remove a WinGet source by name. ## PARAMETERS ### -Name Specify the name of the source to be removed. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: True Position: Named Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ## OUTPUTS ### System.Object ## NOTES ## RELATED LINKS [Add-WinGetSource](Add-WinGetSource.md) [Reset-WinGetSource](Reset-WinGetSource.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Repair-WinGetPackage.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/26/2024 online version: schema: 2.0.0 --- # Repair-WinGetPackage ## SYNOPSIS Repairs a WinGet Package. ## SYNTAX ### FoundSet (Default) ``` Repair-WinGetPackage [-Mode ] [-Log ] [-Version ] [-Id ] [-Name ] [-Moniker ] [-Source ] [[-Query] ] [-MatchOption ] [-ProgressAction ] [-WhatIf] [-Confirm] [] ``` ### GivenSet ``` Repair-WinGetPackage [-Mode ] [-Log ] [[-PSCatalogPackage] ] [-Version ] [-ProgressAction ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION This command repairs a WinGet package from your computer, provided the package includes repair support. The command includes parameters to specify values used to search for installed packages. By default, all string-based searches are case-insensitive substring searches. Wildcards are not supported. You can change the search behavior using the **MatchOption** parameter. > **Note: Not all packages support repair.** ## EXAMPLES ### Example 1: Repair a package using a query ```powershell Repair-WinGetPackage -Query "Microsoft.GDK.2406" ``` This example shows how to repair a package using a query. The **Query** parameter is positional, so you don't need to include the parameter name before the query string. ### Example 3 : Repair a package by Id ```powershell Repair-WinGetPackage -Id "Microsoft.GDK.2406" ``` This example shows how to repair a package by specifying the package identifier. If the package identifier is available from more than one source, you must provide additional search criteria to select a specific instance of the package. ### Example 3: Repair a package using by Name ```powershell Repair-WinGetPackage -Name "Microsoft Game Development Kit - 240602 (June 2024 Update 2)" ``` This example shows how to repair a package using the package name. > **Note: Please note that the examples mentioned above are mainly reference examples for the repair cmdlet and may not be operational as is, since many installers don't support repair as a standard functionality. For the Microsoft.GDK.2406 example, the assumption is that Microsoft.GDK.2406 supports repair capability and the author of the installer has provided the necessary repair context/switches in the Package Manifest in the Package Source referenced by the WinGet Client.** ## PARAMETERS ### -Id Specify the package identifier to search for. By default, the command does a case-insensitive substring match. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Log Specify the location for the installer repair log. The value can be a fully-qualified or relative path and must include the file name. For example: `$env:TEMP\package.log`. > **Note: Not all installers support this option.** ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### -MatchOption Specify the match option for a WinGet package query. This parameter accepts the following values: ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption Parameter Sets: FoundSet Aliases: Accepted values: Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Mode Specify the output mode for the installer. The parameter accepts the following values: ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageRepairMode Parameter Sets: (All) Aliases: Accepted values: Default, Silent, Interactive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Moniker Specify the moniker of the WinGet package to repair. For example, the moniker for the Microsoft.PowerShell package is `pwsh`. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Name Specify the name of the WinGet package name. The name contains space, you must enclose the name in quotes. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -PSCatalogPackage Provide **PSCatalogPackage** object. You can get a **PSCatalogPackage** object by using the `Find-WinGetPackage` or `Get-WingetPackage` commands. ```yaml Type: Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage Parameter Sets: GivenSet Aliases: InputObject Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### -Query Specify one or more strings to search for. By default, the command searches all configured sources. Wildcards are not supported. The command compares the value provided to the following package manifest properties: - `PackageIdentifier` - `PackageName` - `Moniker` - `Tags` The command does a case-insensitive substring comparison of these properties. ```yaml Type: System.String[] Parameter Sets: FoundSet Aliases: Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Source Specify the name of a configured WinGet source. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Version Specify the version of the package to be repaired. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Confirm Prompts you for confirmation before running the cmdlet. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: cf Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet is not run. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: wi Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### Microsoft.WinGet.Client.PSObjects.PSPackageRepairMode ### Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage ### System.String ### System.String[] ### Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption ## OUTPUTS ### Microsoft.WinGet.Client.Engine.PSObjects.PSRepairResult ## NOTES ## RELATED LINKS [Find-WinGetPackage](Find-WinGetPackage.md) [Install-WinGetPackage](Install-WinGetPackage.md) [Uninstall-WinGetPackage](Uninstall-WinGetPackage.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Repair-WinGetPackageManager.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Repair-WinGetPackageManager --- # Repair-WinGetPackageManager ## SYNOPSIS Repairs the installation of the WinGet client on your computer. ## SYNTAX ### IntegrityVersionSet (Default) ``` Repair-WinGetPackageManager [-AllUsers] [-Force] [-Version ] [-IncludePreRelease] [] ``` ### IntegrityLatestSet ``` Repair-WinGetPackageManager [-AllUsers] [-Force] [-Latest] [-IncludePreRelease] [] ``` ## DESCRIPTION This command repairs the installation of the WinGet client on your computer by installing the specified version or the latest version of the client. This command can also install the WinGet client if it is not already installed on your machine. It ensures that the client is installed in a working state. ## EXAMPLES ### Example 1: Repair the WinGet client ```powershell Repair-WinGetPackageManager ``` Ensures that the current installed version of WinGet is functioning properly. ### Example 2: Force install the latest version ```powershell Repair-WinGetPackageManager -Latest -Force ``` This example shows how to repair they WinGet client by installing the latest version and ensuring it functions properly. The **Force** parameter shuts down the version that is currently running so that it can update the application files. ### Example 3: Install a version with wildcards ```powershell Repair-WinGetPackageManager -Version "1.12.*" -Force ``` This example shows how to repair the WinGet client by installing a version that matches the specified version pattern. The **Force** parameter shuts down the version that is currently running so that it can update the application files. ## PARAMETERS ### -AllUsers Use this parameter to repair the WinGet client for all user accounts on the computer. The command must run the command with administrator permissions. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Force The **Force** parameter shuts down the version that is currently running so that it can update the application files. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -IncludePreRelease Use this parameter to include prerelease versions of the WinGet client. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: IntegrityLatestSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Latest Use this parameter to install the latest available version of the WinGet client. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: IntegrityLatestSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Version Specifies the version of the WinGet client to install or repair. You can provide an exact version number or use wildcard characters (for example, `"1.*.1*"`) to match and install the latest version that fits the pattern. ```yaml Type: System.String Parameter Sets: IntegrityVersionSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.Management.Automation.SwitchParameter ### System.String ## OUTPUTS ### System.Int32 ## NOTES This cmdlet makes GitHub API requests to query release information. Unauthenticated requests are subject to [GitHub API rate limits](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api), which can cause failures in CI/CD pipelines. If the `GH_TOKEN` or `GITHUB_TOKEN` environment variable is set, the cmdlet automatically uses it to authenticate requests, which significantly increases the rate limit. `GH_TOKEN` takes precedence over `GITHUB_TOKEN`, matching [GitHub CLI behavior](https://cli.github.com/manual/gh_help_environment). In GitHub Actions, you can make `GITHUB_TOKEN` available to the cmdlet by mapping it as an environment variable in your workflow step (e.g., `env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}`). Use `-Verbose` to see which token source is being used. ## RELATED LINKS [Get-WinGetVersion](Get-WinGetVersion.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Reset-WinGetSource.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Reset-WinGetSource --- # Reset-WinGetSource ## SYNOPSIS Resets WinGet sources. ## SYNTAX ### DefaultSet (Default) ``` Reset-WinGetSource -Name [] ``` ### OptionalSet ``` Reset-WinGetSource -All [] ``` ## DESCRIPTION Resets a named WinGet source by removing the source configuration. You can reset all configured sources and add the default source configurations using the **All** switch parameter. This command must be executed with administrator permissions. ## EXAMPLES ### Example 1: Reset the msstore source ```powershell Reset-WinGetSource -Name msstore ``` This example resets the configured source named 'msstore' by removing it. ### Example 2: Reset all sources ```powershell Reset-WinGetSource -All ``` This example resets all configured sources and adds the default sources. ## PARAMETERS ### -All Reset all sources and add the default sources. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (OptionalSet) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Name The name of the source. ```yaml Type: System.String Parameter Sets: (DefaultSet) Aliases: Required: True Position: Named Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.String ## OUTPUTS ### System.Object ## NOTES ## RELATED LINKS [Add-WinGetSource](Add-WinGetSource.md) [Remove-WinGetSource](Remove-WinGetSource.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Set-WinGetUserSetting.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Set-WinGetUserSetting --- # Set-WinGetUserSetting ## SYNOPSIS Sets configuration settings of the WinGet client for the current user. ## SYNTAX ``` Set-WinGetUserSetting -UserSettings [-Merge] [] ``` ## DESCRIPTION This command sets configuration settings of the WinGet client for the current user. The user settings file doesn't exist until you set a specific value. The file is stored in `$env:LOCALAPPDATA\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json`. For more information about WinGet settings, see [WinGet CLI Settings](https://aka.ms/winget-settings). ## EXAMPLES ### Example 1: Set progress bar theme ```powershell Set-WinGetUserSetting -UserSettings @{ visual = @{ progressBar = 'rainbow' } } ``` Sets the theme of the progress bar to rainbow. ### Example 2: Merge install behavior settings ```powershell Set-WinGetUserSetting -Merge -UserSettings @{ installBehavior = @{ preferences = @{ scope = 'user' } } } ``` Appends the user scope preference setting to the existing WinGet settings configuration. ### Example 3: Change multiple settings ```powershell Set-WinGetUserSetting -UserSettings @{ visual = @{ progressBar = 'rainbow' anonymizeDisplayedPaths = $true } } ``` ## PARAMETERS ### -Merge By default, the command overwrites the current setting with the values provided. Use this parameter to append the new settings to the existing configuration. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -UserSettings A hashtable containing the key value pairs representing the WinGet settings. ```yaml Type: System.Collections.Hashtable Parameter Sets: (All) Aliases: Required: True Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.Collections.Hashtable ### System.Management.Automation.SwitchParameter ## OUTPUTS ### System.Collections.Hashtable ## NOTES ## RELATED LINKS [Get-WinGetUserSetting](Get-WinGetUserSetting.md) [Test-WinGetUserSetting](Test-WinGetUserSetting.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Test-WinGetUserSetting.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Test-WinGetUserSetting --- # Test-WinGetUserSetting ## SYNOPSIS Tests the current state of WinGet user settings. ## SYNTAX ``` Test-WinGetUserSetting -UserSettings [-IgnoreNotSet] [] ``` ## DESCRIPTION This command tests the current state of WinGet user settings against a provided set of values. ## EXAMPLES ### Example 1: Test for exact match ```powershell Test-WinGetUserSetting -UserSettings @{ installBehavior = @{ preferences = @{ scope = 'user' } } } ``` This example shows how to confirm that your current user settings match specific values. The command returns `$false` if it is not an exact match. ### Example 2: Test only progress bar setting ```powershell Test-WinGetUserSetting -IgnoreNotSet -UserSettings @{ visual = @{ progressBar = 'rainbow' } } ``` This examples tests whether the progress bar theme is set to `rainbow`. When you use the **IgnoreNotSet** parameter, the command only tests the provide values and doesn't include other WinGet settings in the comparison. ## PARAMETERS ### -IgnoreNotSet When you use the **IgnoreNotSet** parameter, the command only tests the provide values and doesn't include other WinGet settings in the comparison. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -UserSettings A hashtable containing the key value pairs representing the WinGet settings. ```yaml Type: System.Collections.Hashtable Parameter Sets: (All) Aliases: Required: True Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.Collections.Hashtable ### System.Management.Automation.SwitchParameter ## OUTPUTS ### System.Boolean ## NOTES ## RELATED LINKS [Get-WinGetUserSetting](Get-WinGetUserSetting.md) [Set-WinGetUserSetting](Set-WinGetUserSetting.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Uninstall-WinGetPackage.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Uninstall-WinGetPackage --- # Uninstall-WinGetPackage ## SYNOPSIS Uninstalls a WinGet Package. ## SYNTAX ### FoundSet (Default) ``` Uninstall-WinGetPackage [-Mode ] [-Force] [-Log ] [-Version ] [-Id ] [-Name ] [-Moniker ] [-Source ] [[-Query] ] [-MatchOption ] [-WhatIf] [-Confirm] [] ``` ### GivenSet ``` Uninstall-WinGetPackage [-Mode ] [-Force] [-Log ] [[-PSCatalogPackage] ] [-Version ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION This command uninstalls a WinGet package from your computer. The command includes parameters to specify values used to search for installed packages. By default, all string-based searches are case-insensitive substring searches. Wildcards are not supported. You can change the search behavior using the **MatchOption** parameter. ## EXAMPLES ### Example 1: Uninstall a package using a query ```powershell Uninstall-WinGetPackage Microsoft.PowerShell ``` This example show how to uninstall a package using a query. The **Query** parameter is positional, so you don't need to include the parameter name before the query string. ### Example 2: Uninstall a package by Id ```powershell Uninstall-WinGetPackage -Id Microsoft.PowerShell ``` This example shows how to uninstall a package by the specifying the package identifier. If the package identifier is available from more than one source, you must provide additional search criteria to select a specific instance of the package. ### Example 3: Uninstall a package by Name ```powershell Uninstall-WinGetPackage -Name "PowerToys (Preview)" ``` This sample uninstalls the PowerToys package by the specifying the package name. ### Example 4: Uninstall a specific version of a package ```powershell Uninstall-WinGetPackage Microsoft.PowerShell -Version 7.4.4.0 ``` This example shows how to uninstall a specific version of a package using a query. The command does a query search for packages matching `Microsoft.PowerShell`. The results of the search a limited to matches with the version of `7.4.4.0`. ## PARAMETERS ### -Force Force the uninstall to run. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Id Specify the package identifier to search for. By default, the command does a case-insensitive substring match. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Log Specify the location for the uninstaller log. The value can be a fully-qualified or relative path and must include the file name. For example: `$env:TEMP\package.log`. > **Note: Not all uninstallers support this option.** ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -MatchOption Specify the match option for a WinGet package query. This parameter accepts the following values: - `Equals` - `EqualsCaseInsensitive` - `StartsWithCaseInsensitive` - `ContainsCaseInsensitive` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption Parameter Sets: FoundSet Aliases: Accepted values: Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Mode Specify the output mode for the installer. The parameter accepts the following values: - `Default` - `Silent` - `Interactive` > [!NOTE] > Not all uninstallers support all modes. ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageUninstallMode Parameter Sets: (All) Aliases: Accepted values: Default, Silent, Interactive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Moniker Specify the moniker of the WinGet package to download. For example, the moniker for the Microsoft.PowerShell package is `pwsh`. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Name Specify the name of the WinGet package name. The name contains space, you must enclose the name in quotes. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -PSCatalogPackage Provide **PSCatalogPackage** object. You can get a **PSCatalogPackage** object by using the `Find-WinGetPackage` or `Get-WingetPackage` commands. ```yaml Type: Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage Parameter Sets: GivenSet Aliases: InputObject Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### -Query Specify one or more strings to search for. By default, the command searches all configured sources. Wildcards are not supported. The command compares the value provided to the following package manifest properties: - `PackageIdentifier` - `PackageName` - `Moniker` - `Tags` The command does a case-insensitive substring comparison of these properties. ```yaml Type: System.String[] Parameter Sets: FoundSet Aliases: Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Source Specify the name of a configured WinGet source. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Version Specify the version of the package to be uninstalled. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Confirm Prompts you for confirmation before running the cmdlet. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: cf Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet isn't run. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: wi Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### Microsoft.WinGet.Client.PSObjects.PSPackageUninstallMode ### System.Management.Automation.SwitchParameter ### System.String ### Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage ### System.String[] ### Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption ## OUTPUTS ### Microsoft.WinGet.Client.Engine.PSObjects.PSUninstallResult ## NOTES ## RELATED LINKS [Get-WinGetPackage](Get-WinGetPackage.md) [Install-WinGetPackage](Install-WinGetPackage.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.Client/Update-WinGetPackage.md ================================================ --- external help file: Microsoft.WinGet.Client.Cmdlets.dll-Help.xml Module Name: Microsoft.WinGet.Client ms.date: 08/01/2024 online version: schema: 2.0.0 title: Update-WinGetPackage --- # Update-WinGetPackage ## SYNOPSIS Installs a newer version of a previously installed WinGet package. ## SYNTAX ### FoundSet (Default) ``` Update-WinGetPackage [-IncludeUnknown] [-Mode ] [-Override ] [-Custom ] [-Location ] [-Log ] [-Force] [-Header ] [-AllowHashMismatch] [-Architecture ] [-InstallerType ] [-Locale ] [-Scope ] [-SkipDependencies] [-Version ] [-Id ] [-Name ] [-Moniker ] [-Source ] [[-Query] ] [-MatchOption ] [-WhatIf] [-Confirm] [] ``` ### GivenSet ``` Update-WinGetPackage [-IncludeUnknown] [-Mode ] [-Override ] [-Custom ] [-Location ] [-Log ] [-Force] [-Header ] [-AllowHashMismatch] [-Architecture ] [-InstallerType ] [-Locale ] [-Scope ] [-SkipDependencies] [[-PSCatalogPackage] ] [-Version ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION This command searches the packages installed on your system and installs a newer version of the matching WinGet package. The command includes parameters to specify values used to search for packages in the configured sources. By default, the command searches all sources. By default, all string-based searches are case-insensitive substring searches. Wildcards are not supported. You can change the search behavior using the **MatchOption** parameter. ## EXAMPLES ### Example 1: Update a package using a query ```powershell Update-WinGetPackage Microsoft.PowerShell ``` This example show how to update a package using a query. The **Query** parameter is positional, so you don't need to include the parameter name before the query string. ### Example 2: Update a package by Id ```powershell Update-WinGetPackage -Id Microsoft.PowerShell ``` This example shows how to update a package by the specifying the package identifier. If the package identifier is available from more than one source, you must provide additional search criteria to select a specific instance of the package. ### Example 3: Update a package by Name ```powershell Update-WinGetPackage -Name "PowerToys (Preview)" ``` This sample updates the PowerToys package by the specifying the package name. ### Example 4: Update to a specific version of a package ```powershell Update-WinGetPackage Microsoft.PowerShell -Version 7.4.4.0 ``` This example shows how to update a specific version of a package using a query. The command does a query search for packages matching `Microsoft.PowerShell`. The results of the search a limited to matches with the version of `7.4.4.0`. ### Example 5: Update all packages ```powershell Get-WinGetPackage | Where-Object IsUpdateAvailable | Update-WinGetPackage ``` This example shows how to update all packages that have an available upgrade from one of the configured sources. ## PARAMETERS ### -AllowHashMismatch Allows you to download package even when the SHA256 hash for an installer or a dependency does not match the SHA256 hash in the WinGet package manifest. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Architecture Specify the processor architecture for the WinGet package installer. The parameter accepts the following values: - `Default` - `X86` - `Arm` - `X64` - `Arm64` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSProcessorArchitecture Parameter Sets: (All) Aliases: Accepted values: Default, X86, Arm, X64, Arm64 Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Custom Use this parameter to pass additional arguments to the installer. The parameter takes a single string value. To add multiple arguments, include the arguments in the string. The arguments must be provided in the format expected by the installer. If the string contains spaces, it must be enclosed in quotes. This string is added to the arguments defined in the package manifest. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Force Force the update to run the installer. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Header Specify a custom value in the HTTP header to the REST source. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Id Specify the package identifier to search for. By default, the command does a case-insensitive substring match. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -IncludeUnknown Use this parameter to upgrade the package when the installed version is not specified in the registry. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -InstallerType A package may contain multiple installer types. Use this parameter to select the installer you want to use. The parameter accepts the following values: - `Default` - `Inno` - `Wix` - `Msi` - `Nullsoft` - `Zip` - `Msix` - `Exe` - `Burn` - `MSStore` - `Portable` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageInstallerType Parameter Sets: (All) Aliases: Accepted values: Default, Inno, Wix, Msi, Nullsoft, Zip, Msix, Exe, Burn, MSStore, Portable Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Locale Specify the locale of the installer package. The locale must provided in the BCP 47 format, such as `en-US`. For more information, see [Standard locale names](/globalization/locale/standard-locale-names). ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Location Specify the file path where you want the packed to be installed. The installer must be able to support alternate install locations. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Log Specify the location for the installer log. The value can be a fully-qualified or relative path and must include the file name. For example: `$env:TEMP\package.log`. > **Note: Not all installers support this property.** ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -MatchOption Specify the match option for a WinGet package query. This parameter accepts the following values: - `Equals` - `EqualsCaseInsensitive` - `StartsWithCaseInsensitive` - `ContainsCaseInsensitive` ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption Parameter Sets: FoundSet Aliases: Accepted values: Equals, EqualsCaseInsensitive, StartsWithCaseInsensitive, ContainsCaseInsensitive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Mode Specify the output mode for the installer. The parameter accepts the following values: - `Default` - `Silent` - `Interactive` Not all installers support all modes. ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageInstallMode Parameter Sets: (All) Aliases: Accepted values: Default, Silent, Interactive Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Moniker Specify the moniker of the WinGet package to update. For example, the moniker for the Microsoft.PowerShell package is `pwsh`. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Name Specify the name of the package to be updated. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Override Use this parameter to override the existing arguments passed to the installer. The parameter takes a single string value. To add multiple arguments, include the arguments in the string. The arguments must be provided in the format expected by the installer. If the string contains spaces, it must be enclosed in quotes. This string overrides the arguments specified in the package manifest. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -PSCatalogPackage Provide **PSCatalogPackage** object. You can get a **PSCatalogPackage** object by using the `Find-WinGetPackage` or `Get-WinGetPackage` commands. ```yaml Type: Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage Parameter Sets: GivenSet Aliases: InputObject Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` ### -Query Specify one or more strings to search for. By default, the command searches all configured sources. Wildcards are not supported. The command compares the value provided to the following package manifest properties: - `PackageIdentifier` - `PackageName` - `Moniker` - `Tags` The command does a case-insensitive substring comparison of these properties. ```yaml Type: System.String[] Parameter Sets: FoundSet Aliases: Required: False Position: 0 Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Scope Specify WinGet package installer scope. The parameter accepts the following values: - `Any` - `User` - `System` - `UserOrUnknown` - `SystemOrUnknown` > [!NOTE] > The installer scope must be available in the WinGet package manifest. ```yaml Type: Microsoft.WinGet.Client.PSObjects.PSPackageInstallScope Parameter Sets: (All) Aliases: Accepted values: Any, User, System, UserOrUnknown, SystemOrUnknown Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -SkipDependencies Specifies that the command shouldn't install the WinGet package dependencies. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Source Specify the name of a configured WinGet source. ```yaml Type: System.String Parameter Sets: FoundSet Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Version Specify package version. ```yaml Type: System.String Parameter Sets: (All) Aliases: Required: False Position: Named Default value: None Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` ### -Confirm Prompts you for confirmation before running the cmdlet. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: cf Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### -WhatIf Shows what would happen if the cmdlet runs. The cmdlet isn't run. ```yaml Type: System.Management.Automation.SwitchParameter Parameter Sets: (All) Aliases: wi Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### System.Management.Automation.SwitchParameter ### Microsoft.WinGet.Client.PSObjects.PSPackageInstallMode ### System.String ### Microsoft.WinGet.Client.PSObjects.PSProcessorArchitecture ### Microsoft.WinGet.Client.PSObjects.PSPackageInstallerType ### Microsoft.WinGet.Client.PSObjects.PSPackageInstallScope ### Microsoft.WinGet.Client.Engine.PSObjects.PSCatalogPackage ### System.String[] ### Microsoft.WinGet.Client.PSObjects.PSPackageFieldMatchOption ## OUTPUTS ### Microsoft.WinGet.Client.Engine.PSObjects.PSInstallResult ## NOTES ## RELATED LINKS [Get-WinGetPackage](Get-WinGetPackage.md) [Uninstall-WinGetPackage](Uninstall-WinGetPackage.md) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.DSC/WinGetAdminSettings.md ================================================ --- external help file: Microsoft.WinGet.DSC.psm1-Help.xml Module Name: Microsoft.WinGet.DSC ms.date: 08/28/2024 online version: schema: 2.0.0 title: WinGetAdminSettings --- # WinGetAdminSettings ## SYNOPSIS Configures WinGet administrator settings. ## DESCRIPTION Allows the administrator settings to be configured or retrieved. Setting administrator settings requires administrator privileges. ## PARAMETERS **Parameter**|**Attribute**|**DataType**|**Description**|**Allowed Values** :-----|:-----|:-----|:-----|:----- `SID`|Key|String|The SID of the user to target, or an empty string to target the current user. **Only the empty string is currently supported.**|See [security identifiers](https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers) `Settings`|Mandatory|Hashtable|The administrator settings as a hashtable.|Inspect the `adminSettings` property of the output from `Get-WinGetSettings` ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.DSC/WinGetPackage.md ================================================ --- external help file: Microsoft.WinGet.DSC.psm1-Help.xml Module Name: Microsoft.WinGet.DSC ms.date: 08/28/2024 online version: schema: 2.0.0 title: WinGetPackage --- # WinGetPackage ## SYNOPSIS Configures packages through WinGet. ## DESCRIPTION Allows WinGet package state to be configured. ## PARAMETERS **Parameter**|**Attribute**|**DataType**|**Description**|**Allowed Values** :-----|:-----|:-----|:-----|:----- `Id`|Key, Mandatory|String|The identifier of a WinGet package.|Use `Find-WinGetPackage` to search for packages `Source`|Key|String|The name of the source that provides the package. If not provided, all configured sources will be searched for the `Id`.|Use the `WinGetSources` resource to configure a source or `Get-WinGetSource` to discover the default sources `Version`|Optional|String|The version of the package.|See the `AvailableVersions` property of output from `Find-WinGetPackage` `Ensure`|Optional|WinGetEnsure|Whether the package should be installed (`Present`) or not (`Absent`).|`Present` (default), `Absent` `MatchOption`|Optional|WinGetMatchOption|The method used to compare the `Id` parameter with available packages.|`Equals`, `EqualsCaseInsensitive` (default), `StartsWithCaseInsensitive`, `ContainsCaseInsensitive` `UseLatest`|Optional|Boolean|Whether the package should updated to the latest available version. If true, takes precedence over `Version`.|`True`, `False` (default) `InstallMode`|Optional|WinGetInstallMode|The interactivity level requested when installing.|`Default`, `Silent` (default), `Interactive` ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.DSC/WinGetPackageManager.md ================================================ --- external help file: Microsoft.WinGet.DSC.psm1-Help.xml Module Name: Microsoft.WinGet.DSC ms.date: 08/28/2024 online version: schema: 2.0.0 title: WinGetPackageManager --- # WinGetPackageManager ## SYNOPSIS Configures the WinGet package manager package itself. ## DESCRIPTION Allows the WinGet package manager package version to be configured. If multiple of the version targeting parameters are True/non-empty, they will take precedence in the following order: `UseLatest`, `UseLatestPreRelease`, `Version`. ## PARAMETERS **Parameter**|**Attribute**|**DataType**|**Description**|**Allowed Values** :-----|:-----|:-----|:-----|:----- `SID`|Key|String|The SID of the user to target, or an empty string to target the current user. **Only the empty string is currently supported.**|See [security identifiers](https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers) `Version`|Optional|String|A version of the WinGet package manager package.|See [the WinGet release tags](https://github.com/microsoft/winget-cli/releases) `UseLatest`|Optional|Boolean|The latest publicly available release should be used.|`True`, `False` (default) `UseLatestPreRelease`|Optional|Boolean|The latest preview release should be used.|`True`, `False` (default) ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.DSC/WinGetSource.md ================================================ --- external help file: Microsoft.WinGet.DSC.psm1-Help.xml Module Name: Microsoft.WinGet.DSC ms.date: 08/28/2024 online version: schema: 2.0.0 title: WinGetSource --- # WinGetSource ## SYNOPSIS Configures a WinGet source. ## DESCRIPTION Allows WinGet sources to be configured or retrieved. ## PARAMETERS **Parameter**|**Attribute**|**DataType**|**Description**|**Allowed Values** :-----|:-----|:-----|:-----|:----- `Name`|Key, Mandatory|String|The name of the source.|Any string using valid characters for Windows file names `Argument`|Mandatory|String|The primary data defining the source; typically the URI prefix of the source.|Source type specific value `Type`|Optional|String|The type of the source.|`Microsoft.PreIndexed.Package`, `Microsoft.Rest` `TrustLevel`|Optional|WinGetTrustLevel|The trust level of the source, which determines how much scrutiny is placed on it when used. If undefined and the source needs to be configured, the default value is `None`.|`Undefined` (default), `None`, `Trusted` `Explicit`|Optional|WinGetOptionalBool|Whether the source is included (`False`) or not (`True`) in commands that do not specify a source. If undefined and the source needs to be configured, the default value is `False`.|`Undefined` (default), `False`, `True` `Ensure`|Optional|WinGetEnsure|Whether the source should exist (`Present`) or not (`Absent`).|`Present` (default), `Absent` ================================================ FILE: src/PowerShell/Help/Microsoft.WinGet.DSC/WinGetUserSettings.md ================================================ --- external help file: Microsoft.WinGet.DSC.psm1-Help.xml Module Name: Microsoft.WinGet.DSC ms.date: 08/28/2024 online version: schema: 2.0.0 title: WinGetUserSettings --- # WinGetUserSettings ## SYNOPSIS Configures WinGet user settings. ## DESCRIPTION Allows part or all of the user settings to be configured or retrieved. ## PARAMETERS **Parameter**|**Attribute**|**DataType**|**Description**|**Allowed Values** :-----|:-----|:-----|:-----|:----- `SID`|Key|String|The SID of the user to target, or an empty string to target the current user. **Only the empty string is currently supported.**|See [security identifiers](https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers) `Settings`|Mandatory|Hashtable|The user settings as a hashtable.|See [the schema](https://github.com/microsoft/winget-cli/blob/master/schemas/JSON/settings/settings.schema.0.2.json) `Action`|Optional|WinGetAction|Determines whether to overwrite (`Full`) or merge (`Partial`) the value provided in `Settings` with the current user settings.|`Full` (default), `Partial` ================================================ FILE: src/PowerShell/Help/markdownlint.yaml ================================================ docsmd.alert: true docsmd.codesnippet: true docsmd.column: true docsmd.image: true docsmd.moniker: true docsmd.no-loc: true docsmd.row: true docsmd.securelinks: true docsmd.syntax: true docsmd.video: true docsmd.xref: true docsmd.zone: true MD001: true # header-increment # MD002 # first-header-h1 - Superceded by MD041 MD003: # header-style style: atx MD004: # ul-style style: dash MD005: true # list-indent # MD006 # ul-start-left - Superceded by MD007's start_indented option MD007: # ul-indent indent: 2 start_indented: false # MD008 # Removed from linter; used to specify indentation for ul MD009: # no-trailing-spaces br_spaces: 2 strict: true MD010: true # no-hard-tabs MD011: true # no-reversed-links MD012: true # no-multiple-blanks MD013: # line-length code_block_line_length: 90 code_blocks: true heading_line_length: 100 headings: true line_length: 100 stern: true tables: false MD014: true # commands-show-output # MD015 # "Use of non-atx style headers" - Removed from linter, replaced by MD003 # MD016 # "Use of non-closed-atx style headers" - Removed from linter, replaced by MD003 # MD017 # "Use of non-setext style headers" - Removed from linter, replaced by MD003 MD018: true # no-missing-space-atx MD019: true # no-multiple-space-atx MD020: true # no-missing-space-closed-atx MD021: true # no-multiple-space-closed-atx MD022: true # blanks-around-headers MD023: true # header-start-left MD024: # no-duplicate-header siblings_only: true MD025: # single-h1 front_matter_title: '' level: 1 MD026: # no-trailing-punctuation punctuation: '.,;:!。,;:!?' MD027: true # no-multiple-space-blockquote MD028: true # no-blanks-blockquote MD029: # ol-prefix style: one MD030: true # list-marker-space MD031: true # blanks-around-fences MD032: true # blanks-around-lists MD033: # no-inline-html allowed_elements: - a - br - code - kbd - li - properties - sup - tags - ul MD034: true # no-bare-urls MD035: # hr-style style: '---' MD036: true # no-emphasis-as-header MD037: true # no-space-in-emphasis MD038: true # no-space-in-code MD039: true # no-space-in-links MD040: false # fenced-code-language MD041: # first-line-h1 front_matter_title: '' MD042: true # no-empty-links MD043: false # required-headers MD044: # proper-names code_blocks: false names: - PowerShell - IntelliSense - Authenticode - CentOS - Contoso - CoreOS - Debian - Ubuntu - openSUSE - RHEL - JavaScript - .NET - NuGet - VS Code - Newtonsoft MD045: true # no-alt-text MD046: # code-block-style style: fenced MD047: true # single-trailing-newline MD048: # code-fence-style style: backtick MD049: # emphasis-style style: underscore MD050: # strong-style style: asterisk MD051: true # link-fragments MD052: true # reference-links-images MD053: true # link-image-reference-definitions ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_AddRemoveSource.ps1 ================================================ <# .SYNOPSIS Example for 'Get-WinGetSource', 'Add-WinGetSource', 'Remove-WinGetSource', and 'Reset-WinGetSource' cmdlet. Cmdlets to allow you to manage sources for the Windows Package Manager. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath # List current sources. Get-WinGetSource # Add REST source Add-WinGetSource -Name 'Contoso' -Argument 'https://www.contoso.com/cache' -Type 'Microsoft.Rest' # Remove source by name Remove-WinGetSource -Name 'Contoso' # Reset to default sources Reset-WinGetSource ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_EnableSettings.ps1 ================================================ <# .SYNOPSIS Example for 'Enable-WinGetSetting' and 'Disable-WinGetSetting' cmdlets. Cmdlet for enabling/disabling a specified WinGet setting. May require elevation. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath # Enables the 'LocalManifestFiles' setting. Enable-WinGetSetting -Name LocalManifestFiles # Disables the 'LocalManifestFiles' setting. Disable-WinGetSetting -Name LocalManifestFiles ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_FindPackage.ps1 ================================================ <# .SYNOPSIS Example for 'Find-WinGetPackage' cmdlet. Displays all applications available for installation. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath # Find all available packages Find-WinGetPackage # Find package by name Find-WinGetPackage -Name git # Find 10 packages by name Find-WinGetPackage -Name git -Count 10 # Find package by package identifier Find-WinGetPackage -Id git.git # Find exact package from a specific source. Find-WinGetPackage -Id Git.Git -Source winget -Exact ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_GetPackage.ps1 ================================================ <# .SYNOPSIS Example for 'Get-WinGetPackage' cmdlet. Displays a list of the applications currently installed on your computer. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath # List all installed packages Get-WinGetPackage # List installed package by name Get-WinGetPackage -Name git # List 10 packages by name Get-WinGetPackage -Name git -Count 10 # List package by package identifier Get-WinGetPackage -Id git.git # List exact package from a specific source. Get-WinGetPackage -Id Git.Git -Source winget -Exact ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_GetVersion.ps1 ================================================ <# .SYNOPSIS Example for 'Get-WinGetVersion' cmdlet. Prints the current client version. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath $version = Get-WinGetVersion Write-Host($version); ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_InstallPackage.ps1 ================================================ <# .SYNOPSIS Example for 'Install-WinGetPackage' cmdlet. Installs the specified application based on the provided arguments. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath # Install a package by name Install-WinGetPackage -Name powertoys # Install a package by version and package identifier. Install-WinGetPackage -Id Microsoft.PowerToys -Version 0.15.2 # Install a package from a specific source Install-WinGetPackage -Id Microsoft.PowerToys -Source winget # Install a package with a specific architecture and scope. Install-WinGetPackage -Id Microsoft.PowerToys -Architecture X64 -Scope User ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_RepairPackage.ps1 ================================================ <# .SYNOPSIS Example for 'Repair-WinGetPackage' cmdlet. Repairs the specified application. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath # Repair a package by name Repair-WinGetPackage -Name "PowerToys FileLocksmith Context Menu" # Repair a package by version and package identifier. Repair-WinGetPackage -Id "MSIX\Microsoft.PowerToys.FileLocksmithContextMenu_1.0.0.0_neutral__8wekyb3d8bbwe" # Repair a package from a specific source Repair-WinGetPackage -Id "MSIX\Microsoft.PowerToys.FileLocksmithContextMenu_1.0.0.0_neutral__8wekyb3d8bbwe" -Source winget ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_UninstallPackage.ps1 ================================================ <# .SYNOPSIS Example for 'Uninstall-WinGetPackage' cmdlet. Uninstalls the specified application. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath # Uninstall a package by name Uninstall-WinGetPackage -Name powertoys # Uninstall a package by version and package identifier. Uninstall-WinGetPackage -Id Microsoft.PowerToys -Version 0.15.2 # Uninstall a package from a specific source Uninstall-WinGetPackage -Id Microsoft.PowerToys -Source winget ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/Examples/Sample_UpdatePackage.ps1 ================================================ <# .SYNOPSIS Example for 'Update-WinGetPackage' cmdlet. Updates the specified application. #> # TODO: Replace parameter with actual module name from PSGallery once module is released. Param ( [Parameter(Mandatory)] $ModulePath ) Import-Module -Name $ModulePath # Update a package by name Update-WinGetPackage -Name powertoys # Update a package by version and package identifier. Update-WinGetPackage -Id Microsoft.PowerToys -Version 0.15.2 # Update a package with silent mode Update-WinGetPackage -Id Microsoft.PowerToys -Mode Silent # Force update a package Update-WinGetPackage -Id Microsoft.PowerToys -Force ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/ModuleFiles/Format.ps1xml ================================================ Microsoft.WinGet.Client.Engine.PSObjects.PSFoundCatalogPackage Microsoft.WinGet.Client.Engine.PSObjects.PSFoundCatalogPackage $_.Name $_.Id $_.Version $_.Source Microsoft.WinGet.Client.Engine.PSObjects.PSInstalledCatalogPackage Microsoft.WinGet.Client.Engine.PSObjects.PSInstalledCatalogPackage $_.Name $_.Id $_.InstalledVersion if ($_.IsUpdateAvailable) { $_.AvailableVersions[0] } $_.Source Microsoft.WinGet.Client.Engine.PSObjects.PSInstallResult Microsoft.WinGet.Client.Engine.PSObjects.PSInstallResult $_.Id $_.Name $_.Source $_.InstallerErrorCode $_.Status $_.RebootRequired $_.ExtendedErrorCode $_.CorrelationData Microsoft.WinGet.Client.Engine.PSObjects.PSUninstallResult Microsoft.WinGet.Client.Engine.PSObjects.PSUninstallResult $_.Id $_.Name $_.Source $_.UninstallerErrorCode $_.Status $_.RebootRequired $_.ExtendedErrorCode $_.CorrelationData Microsoft.WinGet.Client.Engine.PSObjects.PSDownloadResult Microsoft.WinGet.Client.Engine.PSObjects.PSDownloadResult $_.Id $_.Name $_.Source $_.Status $_.ExtendedErrorCode $_.CorrelationData Microsoft.WinGet.Client.Engine.PSObjects.PSSourceResult Microsoft.WinGet.Client.Engine.PSObjects.PSSourceResult $_.Name $_.Argument $_.Type $_.TrustLevel $_.Explicit Microsoft.WinGet.Client.Engine.PSObjects.PSPackageVersionInfo Microsoft.WinGet.Client.Engine.PSObjects.PSPackageVersionInfo $_.DisplayName $_.Id $_.Publisher $_.Channel $_.PackageFamilyNames $_.ProductCodes Microsoft.WinGet.Client.Engine.PSObjects.PSRepairResult Microsoft.WinGet.Client.Engine.PSObjects.PSRepairResult $_.Id $_.Name $_.Source $_.RepairErrorCode $_.Status $_.RebootRequired $_.ExtendedErrorCode $_.CorrelationData ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/ModuleFiles/Microsoft.WinGet.Client.psd1 ================================================ # # Module manifest for module 'Microsoft.WinGet.Client' # # Created by: Microsoft Corporation # @{ # Script module or binary module file associated with this manifest. RootModule = if ($PSEdition -like 'Core') { "net8.0-windows10.0.26100.0\Microsoft.WinGet.Client.Cmdlets.dll" } else { "net48\Microsoft.WinGet.Client.Cmdlets.dll" } # Version number of this module. ModuleVersion = '0.1.0' # Supported PSEditions CompatiblePSEditions = @('Desktop', 'Core') # ID used to uniquely identify this module GUID = 'e11157e2-cd24-4250-83b8-c6654ea4926a' # Author of this module Author = 'Microsoft Corporation' # Company or vendor of this module CompanyName = 'Microsoft Corporation' # Copyright statement for this module Copyright = '(c) Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module Description = 'PowerShell Module for the Windows Package Manager Client.' # Minimum version of the PowerShell engine required by this module PowerShellVersion = '5.1.0' # Name of the PowerShell host required by this module # PowerShellHostName = '' # Minimum version of the PowerShell host required by this module # PowerShellHostVersion = '' # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. # DotNetFrameworkVersion = '' # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. # ClrVersion = '' # Processor architecture (None, X86, Amd64) required by this module # ProcessorArchitecture = '' # Modules that must be imported into the global environment prior to importing this module # RequiredModules = @() # Assemblies that must be loaded prior to importing this module # RequiredAssemblies = @() # Script files (.ps1) that are run in the caller's environment prior to importing this module. # ScriptsToProcess = @() # Type files (.ps1xml) to be loaded when importing this module # TypesToProcess = @() # Format files (.ps1xml) to be loaded when importing this module FormatsToProcess = 'Format.ps1xml' # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @() # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @( 'Get-WinGetVersion' 'Find-WinGetPackage' 'Get-WinGetPackage' 'Get-WinGetSource' 'Install-WinGetPackage' 'Uninstall-WinGetPackage' 'Update-WinGetPackage' 'Get-WinGetUserSetting' 'Set-WinGetUserSetting' 'Test-WinGetUserSetting' 'Assert-WinGetPackageManager' 'Repair-WinGetPackageManager' 'Enable-WinGetSetting' 'Disable-WinGetSetting' 'Get-WinGetSetting' 'Add-WinGetSource' 'Remove-WinGetSource' 'Reset-WinGetSource' 'Export-WinGetPackage' 'Repair-WinGetPackage' ) # Variables to export from this module # VariablesToExport = @() # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. AliasesToExport = @('awgs' 'awgpm' 'dwgs' 'ewgs' 'ewgp' 'fdwgp' 'gwgp' 'gwgse' 'gwgso' 'gwgus' 'gwgv' 'iswgp' 'rwgs' 'rpwgpm' 'rswgs' 'swgus' 'twgus' 'uswgp' 'udwgp', 'Get-WinGetSettings' 'Get-WinGetUserSettings' 'Set-WinGetUserSettings' 'Test-WinGetUserSettings') # DSC resources to export from this module # DscResourcesToExport = @() # List of all modules packaged with this module # ModuleList = @() # List of all files packaged with this module # FileList = @() # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. Tags = @( 'PSEdition_Desktop', 'PSEdition_Core', 'Windows', 'WindowsPackageManager', 'WinGet' ) # A URL to the license for this module. # LicenseUri = '' # A URL to the main website for this project. ProjectUri = 'https://github.com/microsoft/winget-cli' # A URL to an icon representing this module. IconUri = 'https://aka.ms/winget-icon' # ReleaseNotes of this module # ReleaseNotes = '' # Prerelease string of this module Prerelease = 'alpha' # Flag to indicate whether the module requires explicit user acceptance for install/update/save # RequireLicenseAcceptance = $false # External dependent modules of this module # ExternalModuleDependencies = @() } # End of PSData hashtable } # End of PrivateData hashtable # HelpInfo URI of this module # HelpInfoURI = '' # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. # DefaultCommandPrefix = '' } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client/README.md ================================================ # Windows Package Manager PowerShell Module The Windows Package Manager PowerShell Module is made up on two components 1. The `Microsoft.WinGet.Client.Cmdlets` project which contains cmdlet implementations. 2. The `Microsoft.WinGet.Client.Engine` project which contain the real logic for the cmdlets. ## Building the PowerShell Module Locally After building the Microsoft.WinGet.Client.Cmdlets project, the `Microsoft.WinGet.Client` PowerShell module can be found in the output directory in the `PowerShell` folder. For example if you built the project as x64 release, you should expect to find the module files in `$(SolutionDirectory)/src/x64/Release/PowerShell`. This project has after build targets that will copy all the necessary files in the correct location. ## Adding a new cmdlet In order to avoid [assembly dependency conflicts](https://learn.microsoft.com/en-us/powershell/scripting/dev-cross-plat/resolving-dependency-conflicts?view=powershell-7.3) this project uses a custom `AssemblyLoadContext` that load all dependencies. Microsoft.WinGet.Client.Cmdlets.dll is the binary that gets loaded when the module is imported. When Microsoft.WinGet.Client.Engine.dll is getting loaded the resolving handler use the custom ALC to load it. Then all the dependencies of that binary will be loaded using that custom context. The dependencies are laid out in two directories: `DirectDependencies` and `SharedDependencies`. The resolving handler looks for binaries under `DirectDependencies` and uses the custom ALC to load them. The custom ALC load any binaries in `DirectDependencies` and `SharedDependencies`. Exception: WinRT.Runtime.dll doesn't support getting loaded in multiple times in the same process, because it affects static state in the CLR itself. We special case it to get loaded in by the default loader. If the new cmdlet introduces a new dependency, please make sure to add it in the after build targets to copy it in the Dependencies directory. ## Cmdlets - Assert-WinGetPackageManager - Find-WinGetPackage - Get-WinGetPackage - Get-WinGetSource - Get-WinGetUserSetting - Get-WinGetVersion - Install-WinGetPackage - Repair-WinGetPackageManager - Set-WinGetUserSetting - Test-WinGetUserSetting - Uninstall-WinGetPackage - Update-WinGetPackage - Add-WinGetSource - Disable-WinGetSetting - Enable-WinGetSetting - Get-WinGetSetting - Remove-WinGetSource - Reset-WinGetSource - Repair-WinGetPackage ## Quick Start Guide **To run the module, make sure you are using the latest version of PowerShell (not Windows PowerShell)**. ``` winget install --id Microsoft.Powershell ``` Import the module manifest (Microsoft.WinGet.Client.psd1) by running the following command: ``` Import-Module ``` ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/AddSourceCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Cmdlets.Cmdlets { using System.Management.Automation; using Microsoft.WinGet.Client.Cmdlets.PSObjects; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Adds a source. Requires admin. /// [Cmdlet(VerbsCommon.Add, Constants.WinGetNouns.Source)] [Alias("awgs")] public sealed class AddSourceCmdlet : PSCmdlet { /// /// Gets or sets the name of the source to add. /// [Parameter( Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Name { get; set; } /// /// Gets or sets the argument of the source to add. /// [Parameter( Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Argument { get; set; } /// /// Gets or sets the type of the source to add. /// [Parameter( ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] #if AICLI_DISABLE_TEST_HOOKS [ValidateSet( "Microsoft.Rest", "Microsoft.PreIndexed.Package")] #else [ValidateSet( "Microsoft.Rest", "Microsoft.PreIndexed.Package", "Microsoft.Test.Configurable")] #endif public string Type { get; set; } /// /// Gets or sets the trust level of the source to add. /// [Parameter(ValueFromPipelineByPropertyName = true)] public PSSourceTrustLevel TrustLevel { get; set; } = PSSourceTrustLevel.Default; /// /// Gets or sets a value indicating whether the source to add is explicit. /// /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Explicit { get; set; } /// /// Gets or sets a value indicating the priority of the source. Higher values are sorted first. /// /// [Parameter(ValueFromPipelineByPropertyName = true)] public int Priority { get; set; } /// /// Adds source. /// protected override void ProcessRecord() { var command = new CliCommand(this); command.AddSource(this.Name, this.Argument, this.Type, this.ConvertPSSourceTrustLevelToString(this.TrustLevel), this.Explicit.ToBool(), this.Priority); } private string ConvertPSSourceTrustLevelToString(PSSourceTrustLevel trustLevel) => trustLevel switch { PSSourceTrustLevel.Default => string.Empty, _ => trustLevel.ToString(), }; } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/AssertWinGetPackageManagerCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Assert-WinGetPackageManager. Verifies winget is installed properly. /// [Cmdlet( VerbsLifecycle.Assert, Constants.WinGetNouns.WinGetPackageManager, DefaultParameterSetName = Constants.IntegrityVersionSet)] [Alias("awgpm")] public class AssertWinGetPackageManagerCmdlet : WinGetPackageManagerCmdlet { /// /// Validates winget is installed correctly. If not, throws an exception /// with the reason why, if any. /// protected override void ProcessRecord() { var command = new WinGetPackageManagerCommand(this); if (this.ParameterSetName == Constants.IntegrityLatestSet) { command.AssertUsingLatest(this.IncludePrerelease.ToBool()); } else { command.Assert(this.Version); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/Common/FinderCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands.Common { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.PSObjects; /// /// This is the base class for all commands that might need to search for a package. It contains an initial /// set of parameters that corresponds to the intersection of i.e., the "install" and "search" commands. /// public abstract class FinderCmdlet : PSCmdlet { /// /// Gets or sets the field that is matched against the identifier of a package. /// [Parameter( ParameterSetName = Constants.FoundSet, ValueFromPipelineByPropertyName = true)] public string Id { get; set; } /// /// Gets or sets the field that is matched against the name of a package. /// [Parameter( ParameterSetName = Constants.FoundSet, ValueFromPipelineByPropertyName = true)] public string Name { get; set; } /// /// Gets or sets the field that is matched against the moniker of a package. /// [Parameter( ParameterSetName = Constants.FoundSet, ValueFromPipelineByPropertyName = true)] public string Moniker { get; set; } /// /// Gets or sets the name of the source to search for packages. If null, then all sources are searched. /// [Parameter( ParameterSetName = Constants.FoundSet, ValueFromPipelineByPropertyName = true)] public string Source { get; set; } /// /// Gets or sets the strings that match against every field of a package. /// [Parameter( ParameterSetName = Constants.FoundSet, Position = 0, ValueFromPipelineByPropertyName = true, ValueFromRemainingArguments = true)] public string[] Query { get; set; } /// /// Gets or sets how to match against package fields. Default ContainsCaseInsensitive. /// [Parameter( ParameterSetName = Constants.FoundSet, ValueFromPipelineByPropertyName = true)] public PSPackageFieldMatchOption MatchOption { get; set; } = PSPackageFieldMatchOption.ContainsCaseInsensitive; } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/Common/FinderExtendedCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands.Common { using System.Management.Automation; using Microsoft.WinGet.Client.Common; /// /// This is the base class for the commands whose sole purpose is to filter a list of packages i.e., /// the "search" and "list" commands. This class contains an extended set of parameters suited for /// that purpose. /// public abstract class FinderExtendedCmdlet : FinderCmdlet { /// /// Gets or sets the filter that is matched against the tags of the package. /// [Parameter( ParameterSetName = Constants.FoundSet, ValueFromPipelineByPropertyName = true)] public string Tag { get; set; } /// /// Gets or sets the filter that is matched against the commands of the package. /// [Parameter( ParameterSetName = Constants.FoundSet, ValueFromPipelineByPropertyName = true)] public string Command { get; set; } /// /// Gets or sets the maximum number of results returned. /// [ValidateRange(Constants.CountLowerBound, Constants.CountUpperBound)] [Parameter( ParameterSetName = Constants.FoundSet, ValueFromPipelineByPropertyName = true)] public uint Count { get; set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/Common/InstallCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands.Common { using System.IO; using System.Management.Automation; using Microsoft.WinGet.Client.PSObjects; /// /// This is the base class for all commands that parse a FindPackagesOptions result /// from the provided parameters i.e., the "install" and "upgrade" commands. /// public abstract class InstallCmdlet : InstallerSelectionCmdlet { private string location; /// /// Gets or sets the mode to manipulate the package with. /// [Parameter(ValueFromPipelineByPropertyName = true)] public PSPackageInstallMode Mode { get; set; } = PSPackageInstallMode.Default; /// /// Gets or sets the override arguments to be passed on to the installer. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Override { get; set; } /// /// Gets or sets the arguments to be passed on to the installer in addition to the defaults. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Custom { get; set; } /// /// Gets or sets the installation location. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Location { get => this.location; set { this.location = Path.IsPathRooted(value) ? value : this.SessionState.Path.CurrentFileSystemLocation + @"\" + value; } } /// /// Gets or sets the path to the logging file. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Log { get; set; } /// /// Gets or sets a value indicating whether to continue upon non security related failures. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Force { get; set; } /// /// Gets or sets the optional HTTP Header to pass on to the REST Source. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Header { get; set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/Common/PackageCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands.Common { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.PSObjects; /// /// This is the base class for commands which operate on a specific package and version i.e., /// the "install", "uninstall", and "upgrade" commands. /// public abstract class PackageCmdlet : FinderCmdlet { /// /// Initializes a new instance of the class. /// public PackageCmdlet() { // The default match option for single package operations. this.MatchOption = PSObjects.PSPackageFieldMatchOption.EqualsCaseInsensitive; } /// /// Gets or sets the package to directly install. /// [Alias("InputObject")] [ValidateNotNull] [Parameter( ParameterSetName = Constants.GivenSet, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public PSCatalogPackage PSCatalogPackage { get; set; } = null; /// /// Gets or sets the version to install. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Version { get; set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/Common/WinGetPackageManagerCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands.Common { using System.Management.Automation; using Microsoft.WinGet.Client.Common; /// /// Common parameters for Assert-WinGetPackageManager and Repair-WinGetPackageManager. /// public abstract class WinGetPackageManagerCmdlet : PSCmdlet { /// /// Gets or sets the optional version. /// [Parameter( ParameterSetName = Constants.IntegrityVersionSet, ValueFromPipelineByPropertyName = true)] public string Version { get; set; } = string.Empty; /// /// Gets or sets a value indicating whether to use latest. /// [Parameter( ParameterSetName = Constants.IntegrityLatestSet, ValueFromPipelineByPropertyName = true)] public SwitchParameter Latest { get; set; } /// /// Gets or sets a value indicating whether to include prerelease winget versions. /// [Parameter( ParameterSetName = Constants.IntegrityLatestSet, ValueFromPipelineByPropertyName = true)] [Parameter( ParameterSetName = Constants.IntegrityVersionSet, ValueFromPipelineByPropertyName = true)] public SwitchParameter IncludePrerelease { get; set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/DisableSettingCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Cmdlets.Cmdlets { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Disables an admin setting. Requires admin. /// [Cmdlet(VerbsLifecycle.Disable, Constants.WinGetNouns.Setting)] [Alias("dwgs")] public sealed class DisableSettingCmdlet : PSCmdlet { /// /// Gets or sets the name of the setting to disable. /// [Parameter( Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateSet( "LocalManifestFiles", "BypassCertificatePinningForMicrosoftStore", "InstallerHashOverride", "LocalArchiveMalwareScanOverride", "ProxyCommandLineOptions")] public string Name { get; set; } /// /// Disables the admin setting. /// protected override void ProcessRecord() { var command = new CliCommand(this); command.DisableSetting(this.Name); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/EnableSettingCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Cmdlets.Cmdlets { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Enables an admin setting. Requires admin. /// [Cmdlet(VerbsLifecycle.Enable, Constants.WinGetNouns.Setting)] [Alias("ewgs")] public sealed class EnableSettingCmdlet : PSCmdlet { /// /// Gets or sets the name of the setting to enable. /// [Parameter( Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateSet( "LocalManifestFiles", "BypassCertificatePinningForMicrosoftStore", "InstallerHashOverride", "LocalArchiveMalwareScanOverride", "ProxyCommandLineOptions")] public string Name { get; set; } /// /// Enables the admin setting. /// protected override void ProcessRecord() { var command = new CliCommand(this); command.EnableSetting(this.Name); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/ExportPackageCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; using Microsoft.WinGet.Client.Engine.PSObjects; /// /// Downloads a package installer from the pipeline or from a configured source. /// [Cmdlet( VerbsData.Export, Constants.WinGetNouns.Package, DefaultParameterSetName = Constants.FoundSet, SupportsShouldProcess = true)] [Alias("ewgp")] [OutputType(typeof(PSDownloadResult))] public sealed class ExportPackageCmdlet : InstallerSelectionCmdlet { private DownloadCommand command = null; /// /// Gets or sets the directory where the installer will be downloaded to. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string DownloadDirectory { get; set; } /// /// Gets or sets a value indicating whether to skip acquiring the license from a Store package. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter SkipMicrosoftStoreLicense { get; set; } /// /// Gets or sets the platform to download the package for. /// [Parameter(ValueFromPipelineByPropertyName = true)] public PSObjects.PSWindowsPlatform Platform { get; set; } /// /// Gets or sets the target OS version to download for. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string TargetOSVersion { get; set; } /// /// Installs a package from the pipeline or from a configured source. /// protected override void ProcessRecord() { this.command = new DownloadCommand( this, this.PSCatalogPackage, this.Version, this.Id, this.Name, this.Moniker, this.Source, this.Query, this.AllowHashMismatch.ToBool(), this.SkipDependencies.ToBool(), this.Locale); this.command.Download( this.DownloadDirectory, this.MatchOption.ToString(), this.Scope.ToString(), this.Architecture.ToString(), this.InstallerType.ToString(), this.SkipMicrosoftStoreLicense.ToBool(), this.Platform.ToString(), this.TargetOSVersion); } /// /// Interrupts currently running code within the command. /// protected override void StopProcessing() { if (this.command != null) { this.command.Cancel(); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/FindPackageCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; using Microsoft.WinGet.Client.Engine.PSObjects; /// /// Searches configured sources for packages. /// [Cmdlet(VerbsCommon.Find, Constants.WinGetNouns.Package)] [Alias("fdwgp")] [OutputType(typeof(PSFoundCatalogPackage))] public sealed class FindPackageCmdlet : FinderExtendedCmdlet { /// /// Searches for configured sources for packages. /// protected override void ProcessRecord() { var command = new FinderPackageCommand( this, this.Id, this.Name, this.Moniker, this.Source, this.Query, this.Tag, this.Command, this.Count); command.Find(this.MatchOption.ToString()); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/GetPackageCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; using Microsoft.WinGet.Client.Engine.PSObjects; /// /// Searches configured sources for packages. /// [Cmdlet(VerbsCommon.Get, Constants.WinGetNouns.Package)] [Alias("gwgp")] [OutputType(typeof(PSInstalledCatalogPackage))] public sealed class GetPackageCmdlet : FinderExtendedCmdlet { /// /// Searches for configured sources for packages. /// protected override void ProcessRecord() { var command = new FinderPackageCommand( this, this.Id, this.Name, this.Moniker, this.Source, this.Query, this.Tag, this.Command, this.Count); command.Get(this.MatchOption.ToString()); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/GetSettingCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Cmdlets.Cmdlets { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Gets winget settings. /// [Cmdlet(VerbsCommon.Get, Constants.WinGetNouns.Setting)] [Alias("gwgse", "Get-WinGetSettings")] public sealed class GetSettingCmdlet : PSCmdlet { /// /// Gets or sets a value indicating whether to output a string or a hashtable. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter AsPlainText { get; set; } /// /// Get settings. /// protected override void ProcessRecord() { var command = new CliCommand(this); command.GetSettings(this.AsPlainText.ToBool()); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/GetSourceCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; using Microsoft.WinGet.Client.Engine.PSObjects; /// /// Retrieves the list of configured sources. /// [Cmdlet(VerbsCommon.Get, Constants.WinGetNouns.Source)] [Alias("gwgso")] [OutputType(typeof(PSSourceResult))] public sealed class GetSourceCmdlet : PSCmdlet { /// /// Gets or sets the name of the source to retrieve. /// [Parameter( Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Name { get; set; } /// /// Returns the list of configured sources. /// protected override void ProcessRecord() { var command = new SourceCommand(this); command.Get(this.Name); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/GetUserSettingCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Collections; using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Gets winget's user settings. /// [Cmdlet(VerbsCommon.Get, Constants.WinGetNouns.UserSetting)] [Alias("gwgus", "Get-WinGetUserSettings")] [OutputType(typeof(Hashtable))] public sealed class GetUserSettingCmdlet : PSCmdlet { /// /// Gets the settings file contents. /// protected override void ProcessRecord() { var command = new UserSettingsCommand(this); command.Get(); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/GetVersionCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Get-WinGetVersion. Gets the current version of winget. /// [Cmdlet(VerbsCommon.Get, Constants.WinGetNouns.Version)] [Alias("gwgv")] [OutputType(typeof(string))] public class GetVersionCmdlet : PSCmdlet { /// /// Writes the winget version. /// protected override void ProcessRecord() { var command = new VersionCommand(this); command.Get(); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/InstallPackageCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; using Microsoft.WinGet.Client.Engine.PSObjects; /// /// Installs a package from the pipeline or from a configured source. /// [Cmdlet( VerbsLifecycle.Install, Constants.WinGetNouns.Package, DefaultParameterSetName = Constants.FoundSet, SupportsShouldProcess = true)] [Alias("iswgp")] [OutputType(typeof(PSInstallResult))] public sealed class InstallPackageCmdlet : InstallCmdlet { private InstallerPackageCommand command = null; /// /// Installs a package from the pipeline or from a configured source. /// protected override void ProcessRecord() { this.command = new InstallerPackageCommand( this, this.Override, this.Custom, this.Location, this.AllowHashMismatch.ToBool(), this.Force.ToBool(), this.Header, this.PSCatalogPackage, this.Version, this.Log, this.Id, this.Name, this.Moniker, this.Source, this.Query, this.SkipDependencies.ToBool()); this.command.Install(this.MatchOption.ToString(), this.Scope.ToString(), this.Architecture.ToString(), this.Mode.ToString(), this.InstallerType.ToString()); } /// /// Interrupts currently running code within the command. /// protected override void StopProcessing() { if (this.command != null) { this.command.Cancel(); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/InstallerSelectionCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.PSObjects; /// /// This is the base class for all commands that select an installer from a given package. /// Contains shared arguments for the install, update, and download commands. /// public abstract class InstallerSelectionCmdlet : PackageCmdlet { /// /// Gets or sets a value indicating whether to skip the installer hash validation check. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter AllowHashMismatch { get; set; } /// /// Gets or sets the architecture of the installer to be downloaded. /// [Parameter(ValueFromPipelineByPropertyName = true)] public PSProcessorArchitecture Architecture { get; set; } = PSProcessorArchitecture.Default; /// /// Gets or sets the installer type to be downloaded. /// [Parameter(ValueFromPipelineByPropertyName = true)] public PSPackageInstallerType InstallerType { get; set; } = PSPackageInstallerType.Default; /// /// Gets or sets the locale of the installer to be downloaded. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Locale { get; set; } /// /// Gets or sets the scope of the installer to be downloaded. /// [Parameter(ValueFromPipelineByPropertyName = true)] public PSPackageInstallScope Scope { get; set; } = PSPackageInstallScope.Any; /// /// Gets or sets a value indicating whether skip dependencies. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter SkipDependencies { get; set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSPackageFieldMatchOption.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.PSObjects { /// /// This should mimic Microsoft.Management.Deployment.PackageFileMatchOption. /// public enum PSPackageFieldMatchOption { /// /// Equals. /// Equals, /// /// EqualsCaseInsensitive. /// EqualsCaseInsensitive, /// /// StartsWithCaseInsensitive. /// StartsWithCaseInsensitive, /// /// ContainsCaseInsensitive /// ContainsCaseInsensitive, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSPackageInstallMode.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.PSObjects { /// /// This should mimic Microsoft.Management.Deployment.PackageInstallMode. /// public enum PSPackageInstallMode { /// /// Default, /// Default, /// /// Silent, /// Silent, /// /// Interactive, /// Interactive, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSPackageInstallScope.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.PSObjects { /// /// This must match Microsoft.Management.Deployment.PackageInstallScope. /// public enum PSPackageInstallScope { /// /// Any. /// Any, /// /// User. /// User, /// /// System. /// System, /// /// User or unknown. /// UserOrUnknown, /// /// System or unknown. /// SystemOrUnknown, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSPackageInstallerType.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.PSObjects { /// /// The installer type of the package. /// public enum PSPackageInstallerType { /// /// Let winget decide. /// Default, /// /// Inno, /// Inno, /// /// Wix. /// Wix, /// /// Msi. /// Msi, /// /// Nullsoft. /// Nullsoft, /// /// Zip. /// Zip, /// /// Msix. /// Msix, /// /// Exe. /// Exe, /// /// Burn. /// Burn, /// /// MSStore, /// MSStore, /// /// Portable. /// Portable, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSPackageRepairMode.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.PSObjects { /// /// Must match Microsoft.Management.Deployment.PackageRepairMode. /// public enum PSPackageRepairMode { /// /// Default. /// Default, /// /// Silent. /// Silent, /// /// Interactive. /// Interactive, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSPackageUninstallMode.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.PSObjects { /// /// Must match Microsoft.Management.Deployment.PackageUninstallMode. /// public enum PSPackageUninstallMode { /// /// Default. /// Default, /// /// Silent. /// Silent, /// /// Interactive. /// Interactive, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSProcessorArchitecture.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.PSObjects { /// /// The processor architecture of the package to install. /// public enum PSProcessorArchitecture { /// /// Let winget decide. /// Default, /// /// x86, /// X86, /// /// ARM. /// Arm, /// /// x64. /// X64, /// /// Arm64 /// Arm64, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSSourceTrustLevel.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Cmdlets.PSObjects { /// /// This is the powershell argument equivalent of AppInstaller::Repository::SourceTrustLevel. /// public enum PSSourceTrustLevel { /// /// Let winget decide. /// Default, /// /// None. /// None, /// /// Trusted. /// Trusted, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/PSObjects/PSWindowsPlatform.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.PSObjects { /// /// The Windows platform type. /// public enum PSWindowsPlatform { /// /// Let winget decide. /// Default, /// /// Windows.Universal /// Universal, /// /// Windows.Desktop /// Desktop, /// /// Windows.IoT /// IoT, /// /// Windows.Team /// Team, /// /// Windows.Holographic /// Holographic, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/RemoveSourceCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Cmdlets { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Removes a source. Requires admin. /// [Cmdlet(VerbsCommon.Remove, Constants.WinGetNouns.Source)] [Alias("rwgs")] public sealed class RemoveSourceCmdlet : PSCmdlet { /// /// Gets or sets the name of the source to remove. /// [Parameter( Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Name { get; set; } /// /// Removes source. /// protected override void ProcessRecord() { var command = new CliCommand(this); command.RemoveSource(this.Name); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/RepairPackageCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; using Microsoft.WinGet.Client.Engine.PSObjects; using Microsoft.WinGet.Client.PSObjects; /// /// This class defines the repair package command. /// [Cmdlet( VerbsDiagnostic.Repair, Constants.WinGetNouns.Package, DefaultParameterSetName = Constants.FoundSet, SupportsShouldProcess = true)] [OutputType(typeof(PSRepairResult))] public sealed class RepairPackageCmdlet : PackageCmdlet { private RepairPackageCommand command = null; /// /// Gets or sets the desired mode for the repair process. /// [Parameter(ValueFromPipelineByPropertyName = true)] public PSPackageRepairMode Mode { get; set; } = PSPackageRepairMode.Default; /// /// Gets or sets the path to the logging file. /// [Parameter] public string Log { get; set; } /// /// Gets or sets a value indicating whether to skip the installer hash validation check. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter AllowHashMismatch { get; set; } /// /// Gets or sets a value indicating whether to continue upon non security related failures. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Force { get; set; } /// /// Repairs a package from the local system. /// protected override void ProcessRecord() { this.command = new RepairPackageCommand( this, this.AllowHashMismatch.ToBool(), this.Force.ToBool(), this.PSCatalogPackage, this.Version, this.Log, this.Id, this.Name, this.Moniker, this.Source, this.Query); this.command.Repair(this.MatchOption.ToString(), this.Mode.ToString()); } /// /// Interrupts currently running code within the command. /// protected override void StopProcessing() { if (this.command != null) { this.command.Cancel(); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/RepairWinGetPackageManagerCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Repair-WinGetPackageManager. Repairs winget if needed. /// [Cmdlet( VerbsDiagnostic.Repair, Constants.WinGetNouns.WinGetPackageManager, DefaultParameterSetName = Constants.IntegrityVersionSet)] [Alias("rpwgpm")] [OutputType(typeof(int))] public class RepairWinGetPackageManagerCmdlet : WinGetPackageManagerCmdlet { private WinGetPackageManagerCommand command = null; /// /// Gets or sets a value indicating whether to repair for all users. Requires admin. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter AllUsers { get; set; } /// /// Gets or sets a value indicating whether to force application shutdown. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Force { get; set; } /// /// Attempts to repair winget. /// TODO: consider WhatIf and Confirm options. /// protected override void ProcessRecord() { this.command = new WinGetPackageManagerCommand(this); if (this.ParameterSetName == Constants.IntegrityLatestSet) { this.command.RepairUsingLatest(this.IncludePrerelease.ToBool(), this.AllUsers.ToBool(), this.Force.ToBool()); } else { this.command.Repair(this.Version, this.AllUsers.ToBool(), this.Force.ToBool(), this.IncludePrerelease.ToBool()); } } /// /// Interrupts currently running code within the command. /// protected override void StopProcessing() { if (this.command != null) { this.command.Cancel(); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/ResetSourceCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Cmdlets.Cmdlets { using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Resets a source. Requires admin. /// [Cmdlet(VerbsCommon.Reset, Constants.WinGetNouns.Source, DefaultParameterSetName = Constants.DefaultSet)] [Alias("rswgs")] public sealed class ResetSourceCmdlet : PSCmdlet { /// /// Gets or sets the name of the source to reset. /// [Parameter( Position = 0, Mandatory = true, ParameterSetName = Constants.DefaultSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Name { get; set; } /// /// Gets or sets a value indicating whether to reset all sources. /// [Parameter(ParameterSetName = Constants.OptionalSet, ValueFromPipelineByPropertyName = true)] public SwitchParameter All { get; set; } /// /// Resets source. /// protected override void ProcessRecord() { var command = new CliCommand(this); if (!string.IsNullOrEmpty(this.Name)) { command.ResetSourceByName(this.Name); } else if (this.All) { command.ResetAllSources(); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/SetUserSettingCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Collections; using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Sets the specified user settings into the winget user settings. If the merge switch is on, merges current user /// settings with the input settings. Otherwise, overwrites the input settings. /// [Cmdlet(VerbsCommon.Set, Constants.WinGetNouns.UserSetting)] [Alias("swgus", "Set-WinGetUserSettings")] [OutputType(typeof(Hashtable))] public sealed class SetUserSettingCmdlet : PSCmdlet { /// /// Gets or sets the input user settings. /// [Parameter( Mandatory = true, ValueFromPipelineByPropertyName = true)] public Hashtable UserSettings { get; set; } /// /// Gets or sets a value indicating whether to merge the current user settings and the input settings. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Merge { get; set; } /// /// Process input of cmdlet. /// protected override void ProcessRecord() { var command = new UserSettingsCommand(this); command.Set(this.UserSettings, this.Merge.ToBool()); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/TestUserSettingCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Collections; using System.Management.Automation; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; /// /// Compare the specified user settings with the winget user settings. /// [Cmdlet(VerbsDiagnostic.Test, Constants.WinGetNouns.UserSetting)] [Alias("twgus", "Test-WinGetUserSettings")] [OutputType(typeof(bool))] public sealed class TestUserSettingCmdlet : PSCmdlet { /// /// Gets or sets the input user settings. /// [Parameter( Mandatory = true, ValueFromPipelineByPropertyName = true)] public Hashtable UserSettings { get; set; } /// /// Gets or sets a value indicating whether to ignore comparing settings that are not part of the input. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter IgnoreNotSet { get; set; } /// /// Process the cmdlet and writes the result of the comparison. /// protected override void ProcessRecord() { var command = new UserSettingsCommand(this); command.Test(this.UserSettings, this.IgnoreNotSet.ToBool()); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/UninstallPackageCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; using Microsoft.WinGet.Client.Engine.PSObjects; using Microsoft.WinGet.Client.PSObjects; /// /// Uninstalls a package from the local system. /// [Cmdlet( VerbsLifecycle.Uninstall, Constants.WinGetNouns.Package, DefaultParameterSetName = Constants.FoundSet, SupportsShouldProcess = true)] [Alias("uswgp")] [OutputType(typeof(PSUninstallResult))] public sealed class UninstallPackageCmdlet : PackageCmdlet { private UninstallPackageCommand command = null; /// /// Gets or sets the desired mode for the uninstallation process. /// [Parameter(ValueFromPipelineByPropertyName = true)] public PSPackageUninstallMode Mode { get; set; } = PSPackageUninstallMode.Default; /// /// Gets or sets a value indicating whether to continue upon non security related failures. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Force { get; set; } /// /// Gets or sets the path to the logging file. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Log { get; set; } /// /// Uninstalls a package from the local system. /// protected override void ProcessRecord() { this.command = new UninstallPackageCommand( this, this.PSCatalogPackage, this.Version, this.Log, this.Id, this.Name, this.Moniker, this.Source, this.Query); this.command.Uninstall(this.MatchOption.ToString(), this.Mode.ToString(), this.Force.ToBool()); } /// /// Interrupts currently running code within the command. /// protected override void StopProcessing() { if (this.command != null) { this.command.Cancel(); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Cmdlets/UpdatePackageCmdlet.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Commands.Common; using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Engine.Commands; using Microsoft.WinGet.Client.Engine.PSObjects; /// /// This commands updates a package from the pipeline or from the local system. /// [Cmdlet( VerbsData.Update, Constants.WinGetNouns.Package, DefaultParameterSetName = Constants.FoundSet, SupportsShouldProcess = true)] [Alias("udwgp")] [OutputType(typeof(PSInstallResult))] public sealed class UpdatePackageCmdlet : InstallCmdlet { /// /// Gets or sets a value indicating whether updating to an unknown version is allowed. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter IncludeUnknown { get; set; } /// /// Updates a package from the pipeline or from the local system. /// protected override void ProcessRecord() { var command = new InstallerPackageCommand( this, this.Override, this.Custom, this.Location, this.AllowHashMismatch.ToBool(), this.Force.ToBool(), this.Header, this.PSCatalogPackage, this.Version, this.Log, this.Id, this.Name, this.Moniker, this.Source, this.Query, this.SkipDependencies); command.Update(this.IncludeUnknown.ToBool(), this.MatchOption.ToString(), this.Scope.ToString(), this.Architecture.ToString(), this.Mode.ToString(), this.InstallerType.ToString()); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Common/Constants.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Common { /// /// This class contains all of the configurable constants for this project. /// internal static class Constants { /// /// If a command allows the specification of the maximum number of results to return, this is the lower bound for that value. /// public const uint CountLowerBound = 1; /// /// If a command allows the specification of the maximum number of results to return, this is the upper bound for that value. /// public const uint CountUpperBound = 1000; /// /// This parameter set indicates that a package was provided via a parameter or the pipeline and it can be acted on directly. /// public const string GivenSet = "GivenSet"; /// /// This parameter set indicates that a package was not provided via a parameter or the pipeline and it /// needs to be found by searching a package source. /// public const string FoundSet = "FoundSet"; /// /// This parameter set indicates the default parameters associated with a cmdlet. /// public const string DefaultSet = "DefaultSet"; /// /// This parameter set indicates the optional parameters associated with a cmdlet. /// public const string OptionalSet = "OptionalSet"; /// /// Parameter set for an specific version parameter. /// public const string IntegrityVersionSet = "IntegrityVersionSet"; /// /// Parameter set for an latest version with optional prerelease version. /// public const string IntegrityLatestSet = "IntegrityLatestSet"; /// /// Nouns used for different cmdlets. Changing this will alter the names of the related commands. /// public static class WinGetNouns { /// /// WinGet. /// public const string WinGetPackageManager = "WinGetPackageManager"; /// /// WinGetPackage. /// public const string Package = "WinGetPackage"; /// /// WinGetSource. /// public const string Source = "WinGetSource"; /// /// The noun for any user settings cmdlet. /// public const string UserSetting = "WinGetUserSetting"; /// /// The noun for winget version. /// public const string Version = "WinGetVersion"; /// /// The noun for enable/disable winget admin settings. /// public const string Setting = "WinGetSetting"; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Microsoft.WinGet.Client.Cmdlets.csproj ================================================ net8.0-windows10.0.26100.0 false net48 Debug;Release;ReleaseStatic true 10 $(SolutionDir)$(Platform)\$(Configuration)\ $(BuildOutputDirectory)$(MSBuildProjectName) $(CoreFramework);$(DesktopFramework) $(OutputPath)\Microsoft.WinGet.Client.Cmdlets.xml true true all runtime; build; native; contentfiles; analyzers; buildtransitive POWERSHELL_WINDOWS win None $(BuildOutputDirectory)PowerShell\Microsoft.WinGet.Client $(PowerShellModuleOutputDirectory)\$(TargetFramework) $(PowerShellModuleRuntimesDir)\SharedDependencies $(PowerShellModuleRuntimesDir)\DirectDependencies ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Properties/AssemblyInfo.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- #if NET using System.Runtime.Versioning; // Forcibly set the target and supported platforms due to the internal build setup. // Keep in sync with project versions. [assembly: TargetPlatform("Windows10.0.26100.0")] [assembly: SupportedOSPlatform("Windows10.0.18362.0")] #endif ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Resolver/ModuleInit.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Resolver { using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using System.Runtime.InteropServices; #if !POWERSHELL_WINDOWS using System.Runtime.Loader; #else using Microsoft.WinGet.Client.Cmdlets.Resolver; #endif /// /// Initialization class for this module. /// public class ModuleInit : IModuleAssemblyInitializer, IModuleAssemblyCleanup { private static readonly IEnumerable ValidArchs = new Architecture[] { Architecture.X86, Architecture.X64, Architecture.Arm64 }; /// public void OnImport() { var arch = RuntimeInformation.ProcessArchitecture; if (!ValidArchs.Contains(arch)) { throw new NotSupportedException(arch.ToString()); } #if !POWERSHELL_WINDOWS AssemblyLoadContext.Default.Resolving += WinGetAssemblyLoadContext.ResolvingHandler; #else // If we really need to avoid dependency conflicts, we could create a custom domain and handle the serialization boundaries. // PowerShell doesn't recommended because its complications. AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve += WinGetAppDomain.Handler; #endif } /// public void OnRemove(PSModuleInfo module) { #if !POWERSHELL_WINDOWS AssemblyLoadContext.Default.Resolving -= WinGetAssemblyLoadContext.ResolvingHandler; #else AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve -= WinGetAppDomain.Handler; #endif } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Cmdlets/Resolver/WinGetAppDomain.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- #if POWERSHELL_WINDOWS namespace Microsoft.WinGet.Client.Cmdlets.Resolver { using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; /// /// Resolver for assemblies. /// internal static class WinGetAppDomain { private static readonly string SharedDependencyPath; private static readonly string SharedArchDependencyPath; private static readonly string DirectDependencyPath; static WinGetAppDomain() { var self = typeof(WinGetAppDomain).Assembly; SharedDependencyPath = Path.Combine( Path.GetDirectoryName(self.Location), "SharedDependencies"); SharedArchDependencyPath = Path.Combine( SharedDependencyPath, RuntimeInformation.ProcessArchitecture.ToString().ToLower()); DirectDependencyPath = Path.Combine( Path.GetDirectoryName(self.Location), "DirectDependencies"); } /// /// Handler to register in the AppDomain. /// /// Source. /// Event args. /// The assembly if found. public static Assembly Handler(object source, ResolveEventArgs args) { string name = $"{new AssemblyName(args.Name).Name}.dll"; string path = Path.Combine(SharedDependencyPath, name); if (File.Exists(path)) { return Assembly.LoadFile(path); } path = Path.Combine(SharedArchDependencyPath, name); if (File.Exists(path)) { return Assembly.LoadFile(path); } path = Path.Combine(DirectDependencyPath, name); if (File.Exists(path)) { return Assembly.LoadFile(path); } return null; } } } #endif ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Attributes/FilterAttribute.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Attributes { using System; using Microsoft.Management.Deployment; /// /// A is constructed by introspecting on the inheritance tree and /// looking for parameters that are marked with this attribute. Properties that are marked with this /// attribute are added to the object. /// [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] internal class FilterAttribute : Attribute { /// /// Gets or sets the field that the filter will be matching against. /// public PackageMatchField Field { get; set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/CliCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Common.Command; /// /// Commands that just calls winget.exe underneath. /// public sealed class CliCommand : BaseCommand { /// /// Initializes a new instance of the class. /// /// PSCmdlet. public CliCommand(PSCmdlet psCmdlet) : base(psCmdlet) { } /// /// Enables admin setting. /// /// Setting name. public void EnableSetting(string name) { Utilities.VerifyAdmin(); _ = this.Run(new WinGetCLICommandBuilder("settings").AppendOption("enable", name)); } /// /// Disables admin setting. /// /// Setting name. public void DisableSetting(string name) { Utilities.VerifyAdmin(); _ = this.Run(new WinGetCLICommandBuilder("settings").AppendOption("disable", name)); } /// /// Gets winget settings. /// /// Return as string. public void GetSettings(bool asPlainText) { var result = this.Run(new WinGetCLICommandBuilder("settings").AppendSubCommand("export")); if (asPlainText) { this.Write(StreamType.Object, result.StdOut); } else { this.Write(StreamType.Object, Utilities.ConvertToHashtable(result.StdOut)); } } /// /// Adds source. /// /// Name of source. /// Arg of source. /// Type of source. /// Trust level of source. /// Make source explicit. /// Set the priority if the source. public void AddSource(string name, string arg, string type, string trustLevel, bool isExplicit, int priority) { Utilities.VerifyAdmin(); var builder = new WinGetCLICommandBuilder("source") .AppendSubCommand("add") .AppendOption("name", name) .AppendOption("arg", arg); if (!string.IsNullOrEmpty(type)) { builder.AppendOption("type", type); } if (!string.IsNullOrEmpty(trustLevel)) { builder.AppendOption("trust-level", trustLevel); } if (isExplicit) { builder.AppendSwitch("explicit"); } if (priority != 0) { builder.AppendOption("priority", priority.ToString()); } _ = this.Run(builder, 300000); } /// /// Removes source. /// /// Name of source. public void RemoveSource(string name) { Utilities.VerifyAdmin(); _ = this.Run(new WinGetCLICommandBuilder("source").AppendSubCommand("remove").AppendOption("name", name)); } /// /// Resets a source. /// /// Name of source. public void ResetSourceByName(string name) { Utilities.VerifyAdmin(); _ = this.Run(new WinGetCLICommandBuilder("source").AppendSubCommand("reset").AppendOption("name", name).AppendSwitch("force")); } /// /// Resets all sources and adds the defaults. /// public void ResetAllSources() { Utilities.VerifyAdmin(); _ = this.Run(new WinGetCLICommandBuilder("source").AppendSubCommand("reset").AppendSwitch("force")); } private WinGetCLICommandResult Run(WinGetCLICommandBuilder builder, int timeOut = 60000) { var wingetCliWrapper = new WingetCLIWrapper(); var result = wingetCliWrapper.RunCommand(this, builder, timeOut); result.VerifyExitCode(); return result; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/BaseCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands.Common { using System.Collections.Generic; using System.Management.Automation; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.SharedLib.PolicySettings; /// /// Base class for all Cmdlets. /// public abstract class BaseCommand : PowerShellCmdlet { /// /// Initializes a new instance of the class. /// /// PSCmdlet. internal BaseCommand(PSCmdlet psCmdlet) : base(psCmdlet, new HashSet { Policy.WinGet, Policy.WinGetCommandLineInterfaces }) { } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands.Common { using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using System.Reflection; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Attributes; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; /// /// This is the base class for all commands that might need to search for a package. It contains an initial /// set of parameters that corresponds to the intersection of i.e., the "install" and "search" commands. /// public abstract class FinderCommand : ManagementDeploymentCommand { /// /// Initializes a new instance of the class. /// /// PSCmdlet. internal FinderCommand(PSCmdlet psCmdlet) : base(psCmdlet) { } /// /// Gets or sets the field that is matched against the identifier of a package. /// [Filter(Field = PackageMatchField.Id)] protected string? Id { get; set; } /// /// Gets or sets the field that is matched against the name of a package. /// [Filter(Field = PackageMatchField.Name)] protected string? Name { get; set; } /// /// Gets or sets the field that is matched against the moniker of a package. /// [Filter(Field = PackageMatchField.Moniker)] protected string? Moniker { get; set; } /// /// Gets or sets the name of the source to search for packages. If null, then all sources are searched. /// protected string? Source { get; set; } /// /// Gets or sets how to match against package fields. /// #pragma warning disable SA1011 // Closing square brackets should be spaced correctly protected string[]? Query { get; set; } #pragma warning restore SA1011 // Closing square brackets should be spaced correctly private string? QueryAsJoinedString { get { return this.Query is null ? null : string.Join(" ", this.Query); } } /// /// Searches for packages based on the configured parameters. /// /// The value. /// The limit on the number of matches returned. /// The match option. /// A list of objects. internal IReadOnlyList FindPackages( CompositeSearchBehavior behavior, uint limit, PackageFieldMatchOption match) { PackageCatalog catalog = this.GetPackageCatalog(behavior); FindPackagesOptions options = this.GetFindPackagesOptions(limit, match); return this.GetMatchResults(catalog, options); } /// /// Sets the find package options for a query input. /// DO NOT pass PackageFieldMatchOption WinRT enum type in this method. /// That will cause the type to attempt to be loaded in the construction /// of this method and throw a different exception for Windows PowerShell. /// /// The options object. /// The match type as string. /// The query value. internal virtual void SetQueryInFindPackagesOptions( ref FindPackagesOptions options, string match, string? value) { var selector = ManagementDeploymentFactory.Instance.CreatePackageMatchFilter(); selector.Field = PackageMatchField.CatalogDefault; selector.Value = value ?? string.Empty; selector.Option = PSEnumHelpers.ToPackageFieldMatchOption(match); options.Selectors.Add(selector); } private void AddFilterToFindPackagesOptionsIfNotNull( ref FindPackagesOptions options, PackageMatchField field, PackageFieldMatchOption match, string? value) { if (value != null) { var filter = ManagementDeploymentFactory.Instance.CreatePackageMatchFilter(); filter.Field = field; filter.Value = value; filter.Option = match; options.Filters.Add(filter); } } private IReadOnlyList GetMatchResults( PackageCatalog catalog, FindPackagesOptions options) { FindPackagesResult result = catalog.FindPackages(options); if (result.Status == FindPackagesResultStatus.Ok) { return result.Matches; } else { throw new FindPackagesException(result.Status); } } private PackageCatalog GetPackageCatalog(CompositeSearchBehavior behavior) { PackageCatalogReference reference = this.GetPackageCatalogReference(behavior); ConnectResult result = reference.Connect(); if (result.Status == ConnectResultStatus.Ok) { return result.PackageCatalog; } else { throw new CatalogConnectException(result.ExtendedErrorCode); } } private PackageCatalogReference GetPackageCatalogReference(CompositeSearchBehavior behavior) { CreateCompositePackageCatalogOptions options = ManagementDeploymentFactory.Instance.CreateCreateCompositePackageCatalogOptions(); IReadOnlyList references = this.GetPackageCatalogReferences(this.Source); for (var i = 0; i < references.Count; i++) { var reference = references[i]; bool isExplicit = false; try { // Execute in try block to catch interface not implemented on older servers. isExplicit = reference.Info.Explicit; } catch { // Assume that any failure other than the interface not implemented to get Explicit // will result in other failures shortly after this (like the server being gone). } if (!isExplicit) { options.Catalogs.Add(reference); } } options.CompositeSearchBehavior = behavior; return PackageManagerWrapper.Instance.CreateCompositePackageCatalog(options); } private FindPackagesOptions GetFindPackagesOptions(uint limit, PackageFieldMatchOption match) { var options = ManagementDeploymentFactory.Instance.CreateFindPackagesOptions(); this.SetQueryInFindPackagesOptions(ref options, match.ToString(), this.QueryAsJoinedString); this.AddAttributedFiltersToFindPackagesOptions(ref options, match); options.ResultLimit = limit; return options; } private void AddAttributedFiltersToFindPackagesOptions( ref FindPackagesOptions options, PackageFieldMatchOption match) { IEnumerable properties = this.GetType() .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance) .Where(property => Attribute.IsDefined(property, typeof(FilterAttribute))); foreach (PropertyInfo info in properties) { if (info.GetCustomAttribute(typeof(FilterAttribute), true) is FilterAttribute attribute) { PackageMatchField field = attribute.Field; string? value = info.GetValue(this, null) as string; this.AddFilterToFindPackagesOptionsIfNotNull(ref options, field, match, value); } } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderExtendedCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands.Common { using System.Collections.Generic; using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Attributes; /// /// This is the base class for the commands whose sole purpose is to filter a list of packages i.e., /// the "search" and "list" commands. This class contains an extended set of parameters suited for /// that purpose. /// public abstract class FinderExtendedCommand : FinderCommand { /// /// Initializes a new instance of the class. /// /// PSCmdlet. internal FinderExtendedCommand(PSCmdlet psCmdlet) : base(psCmdlet) { } /// /// Gets or sets the filter that is matched against the tags of the package. /// [Filter(Field = PackageMatchField.Tag)] protected string? Tag { get; set; } /// /// Gets or sets the filter that is matched against the commands of the package. /// [Filter(Field = PackageMatchField.Command)] protected string? Command { get; set; } /// /// Gets or sets the maximum number of results returned. /// protected uint Count { get; set; } /// /// Searches for packages from configured sources. /// /// A value. /// The match option. /// A list of objects. internal IReadOnlyList FindPackages(CompositeSearchBehavior behavior, PackageFieldMatchOption match) { return this.FindPackages(behavior, this.Count, match); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/InstallCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands.Common { using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Helpers; /// /// This is the base class for all commands that parse a result /// from the provided parameters i.e., the "install" and "upgrade" commands. /// public abstract class InstallCommand : PackageCommand { /// /// Initializes a new instance of the class. /// /// PSCmdlet. internal InstallCommand(PSCmdlet psCmdlet) : base(psCmdlet) { } /// /// Gets or sets a value indicating whether to skip the installer hash validation check. /// protected bool AllowHashMismatch { get; set; } /// /// Gets or sets a value indicating whether to skip dependencies. /// protected bool SkipDependencies { get; set; } /// /// Gets or sets the override arguments to be passed on to the installer. /// protected string? Override { get; set; } /// /// Gets or sets the arguments to be passed on to the installer in addition to the defaults. /// protected string? Custom { get; set; } /// /// Gets or sets the installation location. /// protected string? Location { get; set; } /// /// Gets or sets the path to the logging file. /// protected string? Log { get; set; } /// /// Gets or sets a value indicating whether to continue upon non security related failures. /// protected bool Force { get; set; } /// /// Gets or sets the optional HTTP Header to pass on to the REST Source. /// protected string? Header { get; set; } /// /// Gets the install options from the configured parameters. /// DO NOT pass PackageInstallMode WinRT enum type in this method. /// That will cause the type to attempt to be loaded in the construction /// of this method and throw a different exception for Windows PowerShell. /// /// The to install. /// Package install mode as string. /// An instance. internal virtual InstallOptions GetInstallOptions(PackageVersionId? version, string mode) { InstallOptions options = ManagementDeploymentFactory.Instance.CreateInstallOptions(); options.AllowHashMismatch = this.AllowHashMismatch; options.SkipDependencies = this.SkipDependencies; options.Force = this.Force; options.PackageInstallMode = PSEnumHelpers.ToPackageInstallMode(mode); if (version != null) { options.PackageVersionId = version; } if (this.Log != null) { options.LogOutputPath = this.Log; } if (this.Override != null) { options.ReplacementInstallerArguments = this.Override; } // Since these arguments are appended to the installer at runtime, it doesn't make sense to append them if they are whitespace if (!string.IsNullOrWhiteSpace(this.Custom)) { options.AdditionalInstallerArguments = this.Custom; } if (this.Location != null) { options.PreferredInstallLocation = this.Location; } if (this.Header != null) { options.AdditionalPackageCatalogArguments = this.Header; } return options; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands.Common { using System; using System.Collections.Generic; using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; /// /// This is the base class for all of the commands in this module that use the COM APIs. /// public abstract class ManagementDeploymentCommand : BaseCommand { static ManagementDeploymentCommand() { WinRTHelpers.Initialize(); } /// /// Initializes a new instance of the class. /// /// psCmdlet. internal ManagementDeploymentCommand(PSCmdlet psCmdlet) : base(psCmdlet) { #if POWERSHELL_WINDOWS if (Utilities.UsesInProcWinget) { throw new WindowsPowerShellNotSupported(); } #endif } /// /// Retrieves the specified source or all sources if is null. /// /// A list of instances. /// The name of the source to retrieve. If null, then all sources are returned. /// The source does not exist. internal IReadOnlyList GetPackageCatalogReferences(string? source) { if (string.IsNullOrEmpty(source)) { return PackageManagerWrapper.Instance.GetPackageCatalogs(); } else { return new List() { PackageManagerWrapper.Instance.GetPackageCatalogByName(source!) ?? throw new InvalidSourceException(source!), }; } } /// /// Executes the cmdlet. All cmdlets that uses the COM APIs and don't call async functions MUST use this method. /// The inproc COM API may deadlock on an STA thread. /// /// The type of result of the cmdlet. /// Cmdlet function. /// The result of the cmdlet. protected TResult Execute(Func func) { if (Utilities.UsesInProcWinget) { return this.RunOnMTA(func); } return func(); } /// /// Executes the cmdlet in a different thread and waits for results. /// /// The type of result of the cmdlet. /// Cmdlet function. /// The result of the cmdlet. protected TResult Execute(Func> func) { var runningTask = this.RunOnMTA( async () => { return await func(); }); this.Wait(runningTask); return runningTask.Result; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/PackageCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands.Common { using System; using System.Collections.Generic; using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Extensions; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Client.Engine.PSObjects; /// /// This is the base class for commands which operate on a specific package and version i.e., /// the "install", "uninstall", "download", and "upgrade" commands. /// public abstract class PackageCommand : FinderCommand { /// /// Initializes a new instance of the class. /// /// PSCmdlet. internal PackageCommand(PSCmdlet psCmdlet) : base(psCmdlet) { } /// /// Gets or sets the package to directly install. /// /// /// Must match the name of the field on the class. /// protected PSCatalogPackage? CatalogPackage { get; set; } = null; /// /// Gets or sets the version to install. /// protected string? Version { get; set; } /// /// Executes a command targeting a specific package version. /// /// Type of callback's result. /// The value. /// The match option. /// The method to call after retrieving the package and version to operate upon. /// Result of the callback. internal async Task?> GetPackageAndExecuteAsync( CompositeSearchBehavior behavior, PackageFieldMatchOption match, Func> callback) where TResult : class { CatalogPackage package = this.GetCatalogPackage(behavior, match); PackageVersionId? version = this.GetPackageVersionId(package); if (this.ShouldProcess(package.ToString(version))) { var result = await callback(package, version); return new Tuple(result, package); } return null; } /// /// Sets the find package options for a query input that is looking for a specific package. /// DO NOT pass PackageFieldMatchOption WinRT enum type in this method. /// That will cause the type to attempt to be loaded in the construction /// of this method and throw a different exception for Windows PowerShell. /// /// The options object. /// The match type. /// The query value. internal override void SetQueryInFindPackagesOptions( ref FindPackagesOptions options, string match, string? value) { var matchOption = PSEnumHelpers.ToPackageFieldMatchOption(match); foreach (PackageMatchField field in new PackageMatchField[] { PackageMatchField.Id, PackageMatchField.Name, PackageMatchField.Moniker }) { var selector = ManagementDeploymentFactory.Instance.CreatePackageMatchFilter(); selector.Field = field; selector.Value = value ?? string.Empty; selector.Option = matchOption; options.Selectors.Add(selector); } } private CatalogPackage GetCatalogPackage(CompositeSearchBehavior behavior, PackageFieldMatchOption match) { if (this.CatalogPackage != null) { // The package was already provided via a parameter or the pipeline. return this.CatalogPackage.CatalogPackageCOM; } else { IReadOnlyList results = this.FindPackages(behavior, 0, match); if (results.Count == 1) { // Exactly one package matched, so we can just return it. return results[0].CatalogPackage; } else if (results.Count == 0) { // No packages matched, we need to throw an error. throw new NoPackageFoundException(); } else { if (behavior != CompositeSearchBehavior.LocalCatalogs) { List highestPriorityResults = new List(); int? highestPriority = null; for (int i = 0; i < results.Count; i++) { MatchResult result = results[i]; int? priority = result.CatalogPackage.CatalogPriority; if ((highestPriority == null && priority != null) || highestPriority < priority) { // Current priority is higher; reset. highestPriority = priority; highestPriorityResults.Clear(); } else if (highestPriority == priority) { // Priority is equal, add to the list. } else { // Current priority is lower, ignore the match. continue; } highestPriorityResults.Add(result); } if (highestPriorityResults.Count == 1) { return highestPriorityResults[0].CatalogPackage; } else { throw new VagueCriteriaException(highestPriorityResults); } } // Too many packages matched! The user needs to refine their input. throw new VagueCriteriaException(results); } } } private PackageVersionId? GetPackageVersionId(CatalogPackage package) { if (this.Version != null) { for (var i = 0; i < package.AvailableVersions.Count; i++) { PackageVersionInfo versionInfo = package.GetPackageVersionInfo(package.AvailableVersions[i]); if (versionInfo != null && versionInfo.CompareToVersion(this.Version) == CompareResult.Equal) { return package.AvailableVersions[i]; } } throw new InvalidVersionException(this.Version); } else { return null; } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/DownloadCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Client.Engine.PSObjects; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; /// /// Downloads a package installer. /// public sealed class DownloadCommand : PackageCommand { /// /// Initializes a new instance of the class. /// /// Caller cmdlet. /// PSCatalogPackage. /// Version to install. /// Package identifier. /// Name of package. /// Moniker of package. /// Source to search. If null, all are searched. /// Match against any field of a package. /// To skip the installer hash validation check. /// To skip package dependencies. /// Locale of the package. public DownloadCommand( PSCmdlet psCmdlet, PSCatalogPackage psCatalogPackage, string version, string id, string name, string moniker, string source, string[] query, bool allowHashMismatch, bool skipDependencies, string locale) : base(psCmdlet) { // PackageCommand if (psCatalogPackage != null) { this.CatalogPackage = psCatalogPackage; } this.Version = version; // FinderCommand this.Id = id; this.Name = name; this.Moniker = moniker; this.Source = source; this.Query = query; // DownloadCommand this.AllowHashMismatch = allowHashMismatch; this.SkipDependencies = skipDependencies; this.Locale = locale; } /// /// Gets or sets a value indicating whether to skip the installer hash validation check. /// private bool AllowHashMismatch { get; set; } /// /// Gets or sets a value indicating whether to skip dependencies. /// private bool SkipDependencies { get; set; } /// /// Gets or sets the locale to install. /// private string? Locale { get; set; } /// /// Process download package. /// /// The target directory where the installer will be downloaded to. /// PSPackageFieldMatchOption. /// PSPackageInstallScope. /// PSProcessorArchitecture. /// PSPackageInstallerType. /// If true, skips downloading a Store license. /// The platform to download the package for. /// The target OS version to download for. public void Download( string downloadDirectory, string psPackageFieldMatchOption, string psPackageInstallScope, string psProcessorArchitecture, string psPackageInstallerType, bool skipMicrosoftStoreLicense, string psWindowsPlatform, string targetOSVersion) { var result = this.Execute( async () => await this.GetPackageAndExecuteAsync( CompositeSearchBehavior.RemotePackagesFromRemoteCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), async (package, version) => { DownloadOptions options = this.GetDownloadOptions(version); if (!string.IsNullOrEmpty(downloadDirectory)) { options.DownloadDirectory = downloadDirectory; } if (!PSEnumHelpers.IsDefaultEnum(psProcessorArchitecture)) { options.Architecture = PSEnumHelpers.ToProcessorArchitecture(psProcessorArchitecture); } if (!PSEnumHelpers.IsDefaultEnum(psPackageInstallerType)) { options.InstallerType = PSEnumHelpers.ToPackageInstallerType(psPackageInstallerType); } options.Scope = PSEnumHelpers.ToPackageInstallScope(psPackageInstallScope); options.SkipMicrosoftStoreLicense = skipMicrosoftStoreLicense; if (!PSEnumHelpers.IsDefaultEnum(psWindowsPlatform)) { options.Platform = PSEnumHelpers.ToWindowsPlatform(psWindowsPlatform); } if (!string.IsNullOrEmpty(targetOSVersion)) { options.TargetOSVersion = targetOSVersion; } return await this.DownloadPackageAsync(package, options); })); if (result != null) { this.Write(StreamType.Object, new PSDownloadResult(result.Item1, result.Item2)); } } private DownloadOptions GetDownloadOptions(PackageVersionId? version) { var options = ManagementDeploymentFactory.Instance.CreateDownloadOptions(); if (version != null) { options.PackageVersionId = version; } if (this.Locale != null) { options.Locale = this.Locale; } options.AllowHashMismatch = this.AllowHashMismatch; options.SkipDependencies = this.SkipDependencies; return options; } private async Task DownloadPackageAsync( CatalogPackage package, DownloadOptions options) { var activity = string.Format(Resources.ProgressRecordActivityExporting, package.Name); var progressOperation = new DownloadOperationWithProgress(this, activity); return await progressOperation.ExecuteAsync( () => PackageManagerWrapper.Instance.DownloadPackageAsync(package, options)); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/FinderPackageCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Client.Engine.PSObjects; using Microsoft.WinGet.Common.Command; /// /// Searches configured sources for packages. /// public sealed class FinderPackageCommand : FinderExtendedCommand { /// /// Initializes a new instance of the class. /// /// Caller cmdlet. /// Package identifier. /// Name of package. /// Moniker of package. /// Source to search. If null, all are searched. /// Match against any field of a package. /// Tag of the package. /// Command of the package. /// Max results to return. public FinderPackageCommand( PSCmdlet psCmdlet, string id, string name, string moniker, string source, string[] query, string tag, string command, uint count) : base(psCmdlet) { // FinderCommand this.Id = id; this.Name = name; this.Moniker = moniker; this.Source = source; this.Query = query; // FinderExtendedCommand this.Tag = tag; this.Command = command; this.Count = count; } /// /// Process find package command. /// /// PSPackageFieldMatchOption. public void Find(string psPackageFieldMatchOption) { var results = this.Execute( () => this.FindPackages( CompositeSearchBehavior.RemotePackagesFromRemoteCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption))); for (var i = 0; i < results.Count; i++) { this.Write(StreamType.Object, new PSFoundCatalogPackage(results[i].CatalogPackage)); } } /// /// Process get package command. /// /// PSPackageFieldMatchOption. public void Get(string psPackageFieldMatchOption) { var results = this.Execute( () => this.FindPackages( CompositeSearchBehavior.LocalCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption))); for (var i = 0; i < results.Count; i++) { this.Write(StreamType.Object, new PSInstalledCatalogPackage(results[i].CatalogPackage)); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/InstallerPackageCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Client.Engine.PSObjects; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; /// /// Installs or updates a package from the pipeline or from a configured source. /// public sealed class InstallerPackageCommand : InstallCommand { /// /// Initializes a new instance of the class. /// /// Caller cmdlet. /// Override arguments to be passed on to the installer. /// Additional arguments. /// Installation location. /// To skip the installer hash validation check. /// To continue upon non security related failures. /// HTTP Header to pass on to the REST Source. /// PSCatalogPackage. /// Version to install. /// Logging file location. /// Package identifier. /// Name of package. /// Moniker of package. /// Source to search. If null, all are searched. /// Match against any field of a package. /// To skip package dependencies. public InstallerPackageCommand( PSCmdlet psCmdlet, string @override, string custom, string location, bool allowHashMismatch, bool force, string header, PSCatalogPackage psCatalogPackage, string version, string log, string id, string name, string moniker, string source, string[] query, bool skipDependencies) : base(psCmdlet) { // InstallCommand. this.Override = @override; this.Custom = custom; this.Location = location; this.Force = force; this.Header = header; this.AllowHashMismatch = allowHashMismatch; this.SkipDependencies = skipDependencies; this.Log = log; // PackageCommand. if (psCatalogPackage != null) { this.CatalogPackage = psCatalogPackage; } this.Version = version; // FinderCommand this.Id = id; this.Name = name; this.Moniker = moniker; this.Source = source; this.Query = query; } /// /// Process install package command. /// /// PSPackageFieldMatchOption. /// PSPackageInstallScope. /// PSProcessorArchitecture. /// PSPackageInstallMode. /// PSPackageInstallerType. public void Install( string psPackageFieldMatchOption, string psPackageInstallScope, string psProcessorArchitecture, string psPackageInstallMode, string psPackageInstallerType) { var result = this.Execute( async () => await this.GetPackageAndExecuteAsync( CompositeSearchBehavior.RemotePackagesFromRemoteCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), async (package, version) => { InstallOptions options = this.GetInstallOptions(version, psPackageInstallMode); if (!PSEnumHelpers.IsDefaultEnum(psProcessorArchitecture)) { options.AllowedArchitectures.Clear(); options.AllowedArchitectures.Add(PSEnumHelpers.ToProcessorArchitecture(psProcessorArchitecture)); } if (!PSEnumHelpers.IsDefaultEnum(psPackageInstallerType)) { options.InstallerType = PSEnumHelpers.ToPackageInstallerType(psPackageInstallerType); } options.PackageInstallScope = PSEnumHelpers.ToPackageInstallScope(psPackageInstallScope); return await this.InstallPackageAsync(package, options); })); if (result != null) { this.Write(StreamType.Object, new PSInstallResult(result.Item1, result.Item2)); } } /// /// Process update package command. /// /// If updating to an unknown version is allowed. /// PSPackageFieldMatchOption. /// PSPackageInstallScope. /// PSProcessorArchitecture. /// PSPackageInstallMode. /// PSPackageInstallerType. public void Update( bool includeUnknown, string psPackageFieldMatchOption, string psPackageInstallScope, string psProcessorArchitecture, string psPackageInstallMode, string psPackageInstallerType) { var result = this.Execute( async () => await this.GetPackageAndExecuteAsync( CompositeSearchBehavior.LocalCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), async (package, version) => { InstallOptions options = this.GetInstallOptions(version, psPackageInstallMode); options.AllowUpgradeToUnknownVersion = includeUnknown; if (!PSEnumHelpers.IsDefaultEnum(psProcessorArchitecture)) { options.AllowedArchitectures.Clear(); options.AllowedArchitectures.Add(PSEnumHelpers.ToProcessorArchitecture(psProcessorArchitecture)); } if (!PSEnumHelpers.IsDefaultEnum(psPackageInstallerType)) { options.InstallerType = PSEnumHelpers.ToPackageInstallerType(psPackageInstallerType); } options.PackageInstallScope = PSEnumHelpers.ToPackageInstallScope(psPackageInstallScope); return await this.UpgradePackageAsync(package, options); })); if (result != null) { this.Write(StreamType.Object, new PSInstallResult(result.Item1, result.Item2)); } } private async Task InstallPackageAsync( CatalogPackage package, InstallOptions options) { var installOperation = new InstallOperationWithProgress( this, string.Format(Resources.ProgressRecordActivityInstalling, package.Name)); return await installOperation.ExecuteAsync( () => PackageManagerWrapper.Instance.InstallPackageAsync(package, options)); } private async Task UpgradePackageAsync( CatalogPackage package, InstallOptions options) { var installOperation = new InstallOperationWithProgress( this, string.Format(Resources.ProgressRecordActivityUpdating, package.Name)); return await installOperation.ExecuteAsync( () => PackageManagerWrapper.Instance.UpgradePackageAsync(package, options)); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/RepairPackageCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System; using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Client.Engine.PSObjects; using Microsoft.WinGet.Resources; /// /// This class defines the repair package command. /// public sealed class RepairPackageCommand : PackageCommand { /// /// Initializes a new instance of the class. /// /// Caller cmdlet. /// PSCatalogPackage. /// To skip the installer hash validation check. /// To continue upon non security related failures. /// Version to repair. /// Logging file location. /// Package identifier. /// Name of package. /// Moniker of package. /// Source to search. if null, all are searched. /// Match against any field of a package. public RepairPackageCommand( PSCmdlet psCmdlet, bool allowHashMismatch, bool force, PSCatalogPackage psCatalogPackage, string version, string log, string id, string name, string moniker, string source, string[] query) : base(psCmdlet) { this.Force = force; this.AllowHashMismatch = allowHashMismatch; if (psCatalogPackage != null) { this.CatalogPackage = psCatalogPackage; } this.Version = version; this.Id = id; this.Name = name; this.Moniker = moniker; this.Source = source; this.Query = query; this.Log = log; } /// /// Gets or sets the path to the logging file. /// private string? Log { get; set; } /// /// Gets or sets a value indicating whether to continue upon non security related failures. /// private bool Force { get; set; } /// /// Gets or sets a value indicating whether to skip the installer hash validation check. /// private bool AllowHashMismatch { get; set; } /// /// Process repair package. /// /// PSPackageFieldMatchOption. /// PSPackageRepairMode. public void Repair( string psPackageFieldMatchOption, string psPackageRepairMode) { var result = this.Execute( async () => await this.GetPackageAndExecuteAsync( CompositeSearchBehavior.AllCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), async (package, version) => { var repairOptions = this.GetRepairOptions(version, PSEnumHelpers.ToPackageRepairMode(psPackageRepairMode)); return await this.RepairPackageAsync(package, repairOptions); })); if (result != null) { if (result.Item1.Status == RepairResultStatus.RepairError && result.Item1.ExtendedErrorCode != null) { if (result.Item1.ExtendedErrorCode.InnerException != null) { throw new WinGetRepairPackageException(result.Item1.ExtendedErrorCode.HResult, result.Item1.RepairerErrorCode, result.Item1.ExtendedErrorCode.InnerException); } throw new WinGetRepairPackageException(result.Item1.ExtendedErrorCode.HResult, result.Item1.RepairerErrorCode); } this.Write(WinGet.Common.Command.StreamType.Object, new PSRepairResult(result.Item1, result.Item2)); } } private RepairOptions GetRepairOptions( PackageVersionId? version, PackageRepairMode repairMode) { var options = ManagementDeploymentFactory.Instance.CreateRepairOptions(); options.AllowHashMismatch = this.AllowHashMismatch; options.Force = this.Force; if (this.Log != null) { options.LogOutputPath = this.Log; } options.PackageRepairMode = repairMode; if (version != null) { options.PackageVersionId = version; } return options; } private async Task RepairPackageAsync( CatalogPackage catalogPackage, RepairOptions repairOptions) { var progressOperation = new RepairOperationWithProgress( this, string.Format(Resources.ProgressRecordActivityRepairing, catalogPackage.Name)); return await progressOperation.ExecuteAsync( () => PackageManagerWrapper.Instance.RepairPackageAsync(catalogPackage, repairOptions)); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.PSObjects; using Microsoft.WinGet.Common.Command; /// /// Wrapper for source cmdlets. /// public sealed class SourceCommand : ManagementDeploymentCommand { /// /// Initializes a new instance of the class. /// Wrapper for Source commands. /// /// Caller cmdlet. public SourceCommand(PSCmdlet psCmdlet) : base(psCmdlet) { } /// /// Get-WinGetSource. /// /// Optional name. public void Get(string name) { var results = this.Execute( () => this.GetPackageCatalogReferences(name)); for (var i = 0; i < results.Count; i++) { this.Write(StreamType.Object, new PSSourceResult(results[i])); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UninstallPackageCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Client.Engine.PSObjects; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; /// /// Uninstalls a package from the local system. /// public sealed class UninstallPackageCommand : PackageCommand { /// /// Initializes a new instance of the class. /// /// Caller cmdlet. /// PSCatalogPackage. /// Version to install. /// Logging file location. /// Package identifier. /// Name of package. /// Moniker of package. /// Source to search. If null, all are searched. /// Match against any field of a package. public UninstallPackageCommand( PSCmdlet psCmdlet, PSCatalogPackage psCatalogPackage, string version, string log, string id, string name, string moniker, string source, string[] query) : base(psCmdlet) { // PackageCommand. if (psCatalogPackage != null) { this.CatalogPackage = psCatalogPackage; } this.Version = version; // FinderCommand this.Id = id; this.Name = name; this.Moniker = moniker; this.Source = source; this.Query = query; // UninstallPackageCommand this.Log = log; } /// /// Gets or sets the path to the logging file. /// private string? Log { get; set; } /// /// Process uninstall package. /// /// PSPackageFieldMatchOption. /// PSPackageUninstallMode. /// Force. public void Uninstall( string psPackageFieldMatchOption, string psPackageUninstallMode, bool force) { var result = this.Execute( async () => await this.GetPackageAndExecuteAsync( CompositeSearchBehavior.LocalCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), async (package, version) => { UninstallOptions options = this.GetUninstallOptions(version, PSEnumHelpers.ToPackageUninstallMode(psPackageUninstallMode), force); return await this.UninstallPackageAsync(package, options); })); if (result != null) { this.Write(StreamType.Object, new PSUninstallResult(result.Item1, result.Item2)); } } private UninstallOptions GetUninstallOptions( PackageVersionId? version, PackageUninstallMode packageUninstallMode, bool force) { var options = ManagementDeploymentFactory.Instance.CreateUninstallOptions(); options.Force = force; if (this.Log != null) { options.LogOutputPath = this.Log; } options.PackageUninstallMode = packageUninstallMode; if (version != null) { options.PackageVersionId = version; } return options; } private async Task UninstallPackageAsync( CatalogPackage package, UninstallOptions options) { var progressOperation = new UninstallOperationWithProgress( this, string.Format(Resources.ProgressRecordActivityUninstalling, package.Name)); return await progressOperation.ExecuteAsync( () => PackageManagerWrapper.Instance.UninstallPackageAsync(package, options)); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UserSettingsCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System; using System.Collections; using System.IO; using System.Linq; using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Common.Command; using Newtonsoft.Json; using Newtonsoft.Json.Linq; /// /// Class used by the user settings cmdlets. /// public sealed class UserSettingsCommand : BaseCommand { private const string SchemaKey = "$schema"; private const string SchemaValue = "https://aka.ms/winget-settings.schema.json"; private static string? winGetSettingsFilePath; /// /// Initializes a new instance of the class. /// /// PSCmdlet. public UserSettingsCommand(PSCmdlet psCmdlet) : base(psCmdlet) { // Doing it in the static constructor will show the user running in system context: // The type initializer for 'Microsoft.WinGet.Client.Engine.Commands.UserSettingsCommand' threw an exception. // Here would be "The specified method is not supported." if (winGetSettingsFilePath == null) { var wingetCliWrapper = new WingetCLIWrapper(); var settingsResult = wingetCliWrapper.RunCommand(this, new WinGetCLICommandBuilder("settings").AppendSubCommand("export")); // Read the user settings file property. var userSettingsFile = Utilities.ConvertToHashtable(settingsResult.StdOut)["userSettingsFile"] ?? throw new ArgumentNullException("userSettingsFile"); winGetSettingsFilePath = (string)userSettingsFile; } } /// /// Get-WinGetUserSetting. /// public void Get() { this.Write(StreamType.Object, this.GetLocalSettingsAsHashtable()); } /// /// Test-WinGetUserSetting. /// /// Input user settings. /// Ignore comparing settings that are not part of the input. public void Test(Hashtable userSettings, bool ignoreNotSet) { this.Write(StreamType.Object, this.CompareUserSettings(userSettings, ignoreNotSet)); } /// /// Set-WinGetUserSetting. /// /// Input user settings. /// Merge the current user settings and the input settings. public void Set(Hashtable userSettings, bool merge) { var newSettings = HashtableToJObject(userSettings); // Merge settings. if (merge) { var currentSettings = this.LocalSettingsFileToJObject(); // To make the input settings triumph, they need to be merged into the existing settings. currentSettings.Merge(newSettings, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Union, MergeNullValueHandling = MergeNullValueHandling.Ignore, }); newSettings = currentSettings; } // Add schema if not there. if (!newSettings.ContainsKey(SchemaKey)) { newSettings.Add(SchemaKey, SchemaValue); } var orderedSettings = this.CreateAlphabeticallyOrderedJObject(newSettings); // Write settings. var settingsJson = orderedSettings.ToString(Formatting.Indented); File.WriteAllText( winGetSettingsFilePath!, settingsJson); this.Write(StreamType.Object, Utilities.ConvertToHashtable(settingsJson)); } private static JObject HashtableToJObject(Hashtable hashtable) { return (JObject)JToken.FromObject(hashtable); } private Hashtable GetLocalSettingsAsHashtable() { var content = File.Exists(winGetSettingsFilePath) ? File.ReadAllText(winGetSettingsFilePath) : string.Empty; return Utilities.ConvertToHashtable(content); } private JObject LocalSettingsFileToJObject() { try { return File.Exists(winGetSettingsFilePath) ? JObject.Parse(File.ReadAllText(winGetSettingsFilePath)) : new JObject(); } catch (JsonReaderException e) { this.Write(StreamType.Verbose, e.Message); throw new UserSettingsReadException(e); } } private bool CompareUserSettings(Hashtable userSettings, bool ignoreNotSet) { try { var currentSettings = this.LocalSettingsFileToJObject(); var newSettings = HashtableToJObject(userSettings); // Don't fail because of the schema. if (currentSettings.ContainsKey(SchemaKey)) { currentSettings.Remove(SchemaKey); } if (newSettings.ContainsKey(SchemaKey)) { newSettings.Remove(SchemaKey); } if (ignoreNotSet) { return this.PartialDeepEquals(newSettings, currentSettings); } return JToken.DeepEquals(newSettings, currentSettings); } catch (Exception e) { this.Write(StreamType.Verbose, e.Message); return false; } } /// /// Partially compares json. All properties and values of json must exist and have the same value /// as otherJson. /// This doesn't support deep JArray object comparison, but we don't have arrays of type object so far :). /// /// Main json. /// otherJson. /// True is otherJson partially contains json. private bool PartialDeepEquals(JToken json, JToken? otherJson) { if (JToken.DeepEquals(json, otherJson)) { return true; } if (otherJson == null) { return false; } // If they are a JValue (string, integer, date, etc) or they are a JArray and DeepEquals fails then not equal. if ((json is JValue && otherJson is JValue) || (json is JArray && otherJson is JArray)) { this.Write( StreamType.Verbose, $"'{json.ToString(Formatting.None)}' != '{otherJson.ToString(Formatting.None)}'"); return false; } // If its not the same type then don't bother. if (json.Type != otherJson.Type) { this.Write( StreamType.Verbose, $"Mismatch types '{json.ToString(Formatting.None)}' '{otherJson.ToString(Formatting.None)}'"); return false; } // Look deeply. if (json.Type == JTokenType.Object) { var jObject = (JObject)json; var otherJObject = (JObject)otherJson; var properties = jObject.Properties(); foreach (var property in properties) { // If the property is not there then give up. if (!otherJObject.ContainsKey(property.Name)) { this.Write(StreamType.Verbose, $"{property.Name} not found."); return false; } if (!this.PartialDeepEquals(property.Value, otherJObject.GetValue(property.Name))) { // Found inequality within a property. We are done. return false; } } } return true; } /// /// Helper method to order alphabetically properties. Newtonsoft doesn't have a nice way /// to do it via a custom JsonConverter. /// /// JObject. /// New ordered JObject. private JObject CreateAlphabeticallyOrderedJObject(JObject jObject) { JObject newJObject = new (); var orderedProperties = jObject.Properties().OrderBy(p => p.Name, StringComparer.Ordinal); foreach (var property in orderedProperties) { if (property.Value.Type == JTokenType.Object) { newJObject.Add( property.Name, this.CreateAlphabeticallyOrderedJObject((JObject)property.Value)); } else { newJObject.Add(property); } } return newJObject; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/VersionCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Common.Command; /// /// Version commands. /// public sealed class VersionCommand : ManagementDeploymentCommand { /// /// Initializes a new instance of the class. /// /// The caller cmdlet. public VersionCommand(PSCmdlet psCmdlet) : base(psCmdlet) { } /// /// Get-WinGetVersion. Gets the currently installed winget version. /// public void Get() { this.Write(StreamType.Object, this.Execute(() => WinGetVersion.InstalledWinGetVersion(this).TagVersion)); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/WinGetPackageManagerCommand.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Commands { using System; using System.Collections.Generic; using System.Management.Automation; using System.Threading.Tasks; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; using static Microsoft.WinGet.Client.Engine.Common.Constants; /// /// Used by Repair-WinGetPackageManager and Assert-WinGetPackageManager. /// public sealed class WinGetPackageManagerCommand : ManagementDeploymentCommand { private const string EnvPath = "env:PATH"; /// /// Initializes a new instance of the class. /// /// Cmdlet being executed. public WinGetPackageManagerCommand(PSCmdlet psCmdlet) : base(psCmdlet) { } /// /// Asserts winget version is the latest version on winget-cli. /// /// Use prerelease version on GitHub. public void AssertUsingLatest(bool preRelease) { var runningTask = this.RunOnMTA( async () => { var gitHubClient = new GitHubClient(RepositoryOwner.Microsoft, RepositoryName.WinGetCli, this); string expectedVersion = await gitHubClient.GetLatestReleaseTagNameAsync(preRelease); this.Assert(expectedVersion); return true; }); this.Wait(runningTask); } /// /// Asserts the version installed is the specified. /// /// The expected version. public void Assert(string expectedVersion) { WinGetIntegrity.AssertWinGet(this, expectedVersion); } /// /// Repairs winget using the latest version on winget-cli. /// /// Use prerelease version on GitHub. /// Install for all users. Requires admin. /// Force application shutdown. public void RepairUsingLatest(bool preRelease, bool allUsers, bool force) { this.ValidateWhenAllUsers(allUsers); var runningTask = this.RunOnMTA( async () => { var gitHubClient = new GitHubClient(RepositoryOwner.Microsoft, RepositoryName.WinGetCli, this); string expectedVersion = await gitHubClient.GetLatestReleaseTagNameAsync(preRelease); await this.RepairStateMachineAsync(expectedVersion, allUsers, force); return true; }); this.Wait(runningTask); } /// /// Repairs winget if needed. /// /// The expected version, if any. /// Install for all users. Requires admin. /// Force application shutdown. /// Include prerelease versions when matching version. public void Repair(string expectedVersion, bool allUsers, bool force, bool includePrerelease) { this.ValidateWhenAllUsers(allUsers); var runningTask = this.RunOnMTA( async () => { if (!string.IsNullOrWhiteSpace(expectedVersion)) { this.Write(StreamType.Verbose, $"Attempting to resolve version '{expectedVersion}'"); var gitHubClient = new GitHubClient(RepositoryOwner.Microsoft, RepositoryName.WinGetCli, this); try { var resolvedVersion = await gitHubClient.ResolveVersionAsync(expectedVersion, includePrerelease); if (!string.IsNullOrEmpty(resolvedVersion)) { this.Write(StreamType.Verbose, $"Matching version found: {resolvedVersion}"); expectedVersion = resolvedVersion!; } else { this.Write(StreamType.Warning, $"No matching version found for {expectedVersion}"); } } catch (Exception ex) { this.Write(StreamType.Warning, $"Could not resolve version '{expectedVersion}': {ex.Message}"); } } else { this.Write(StreamType.Verbose, "No version specified."); } await this.RepairStateMachineAsync(expectedVersion, allUsers, force); return true; }); this.Wait(runningTask); } private async Task RepairStateMachineAsync(string expectedVersion, bool allUsers, bool force) { var seenCategories = new HashSet(); var cancellationToken = this.GetCancellationToken(); var currentCategory = IntegrityCategory.Unknown; while (currentCategory != IntegrityCategory.Installed) { cancellationToken.ThrowIfCancellationRequested(); try { WinGetIntegrity.AssertWinGet(this, expectedVersion); this.Write(StreamType.Verbose, $"WinGet is in a good state."); currentCategory = IntegrityCategory.Installed; } catch (WinGetIntegrityException e) { currentCategory = e.Category; if (seenCategories.Contains(currentCategory)) { this.Write(StreamType.Verbose, $"{currentCategory} encountered previously"); throw; } this.Write(StreamType.Verbose, $"Integrity category type: {currentCategory}"); seenCategories.Add(currentCategory); switch (currentCategory) { case IntegrityCategory.UnexpectedVersion: await this.InstallDifferentVersionAsync(new WinGetVersion(expectedVersion), e.InstalledVersion, allUsers, force); break; case IntegrityCategory.NotInPath: this.RepairEnvPath(); break; case IntegrityCategory.AppInstallerNotRegistered: await this.RegisterAsync(expectedVersion, allUsers); break; case IntegrityCategory.AppInstallerNotInstalled: case IntegrityCategory.AppInstallerNotSupported: case IntegrityCategory.Failure: await this.InstallAsync(expectedVersion, allUsers, force); break; case IntegrityCategory.AppInstallerNoLicense: // This requires -AllUsers in admin mode. if (allUsers && Utilities.ExecutingAsAdministrator) { await this.InstallAsync(expectedVersion, allUsers, force); } else { throw new WinGetRepairException(e); } break; case IntegrityCategory.WinGetSourceNotInstalled: await this.InstallWinGetSourceAsync(); break; case IntegrityCategory.AppExecutionAliasDisabled: case IntegrityCategory.Unknown: throw new WinGetRepairException(e); default: throw new NotSupportedException(); } } } } private async Task InstallDifferentVersionAsync(WinGetVersion toInstallVersion, WinGetVersion? installedVersion, bool allUsers, bool force) { if (installedVersion == null) { installedVersion = WinGetVersion.InstalledWinGetVersion(this); } bool isDowngrade = installedVersion.CompareAsDeployment(toInstallVersion) > 0; string message = $"Installed WinGet version '{installedVersion.TagVersion}' " + $"Installing WinGet version '{toInstallVersion.TagVersion}' " + $"Is downgrade {isDowngrade}"; this.Write( StreamType.Verbose, message); var appxModule = new AppxModuleHelper(this); await appxModule.InstallFromGitHubReleaseAsync(toInstallVersion.TagVersion, allUsers, isDowngrade, force); } private async Task InstallAsync(string toInstallVersion, bool allUsers, bool force) { // If we are here and toInstallVersion is empty, it means that they just ran Repair-WinGetPackageManager. // When there is not version specified, we don't want to assume an empty version means latest, but in // this particular case we need to. if (string.IsNullOrEmpty(toInstallVersion)) { var gitHubClient = new GitHubClient(RepositoryOwner.Microsoft, RepositoryName.WinGetCli, this); toInstallVersion = await gitHubClient.GetLatestReleaseTagNameAsync(false); } var appxModule = new AppxModuleHelper(this); await appxModule.InstallFromGitHubReleaseAsync(toInstallVersion, allUsers, false, force); } private async Task InstallWinGetSourceAsync() { this.Write(StreamType.Verbose, "Installing winget source"); var appxModule = new AppxModuleHelper(this); await appxModule.InstallWinGetSourceAsync(); } private async Task RegisterAsync(string toRegisterVersion, bool allUsers) { var appxModule = new AppxModuleHelper(this); await appxModule.RegisterAppInstallerAsync(toRegisterVersion, allUsers); } private void RepairEnvPath() { // Add windows app path to user PATH environment variable Utilities.AddWindowsAppToPath(); // Update this sessions PowerShell environment so the user doesn't have to restart the terminal. string? envPathUser = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.User); string? envPathMachine = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.Machine); string newPwshPathEnv = $"{envPathMachine};{envPathUser}"; this.SetVariable(EnvPath, newPwshPathEnv); this.Write(StreamType.Verbose, $"PATH environment variable updated"); } private void ValidateWhenAllUsers(bool allUsers) { if (allUsers) { if (Utilities.ExecutingAsSystem) { throw new NotSupportedException(); } if (!Utilities.ExecutingAsAdministrator) { throw new WinGetRepairException(Resources.RepairAllUsersMessage); } } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Common/Constants.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Common { /// /// This class contains all of the configurable constants for this project. /// internal static class Constants { /// /// WinGet package family name. /// #if USE_PROD_CLSIDS public const string WingetPackageFamilyName = "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"; #else public const string WingetPackageFamilyName = "WinGetDevCLI_8wekyb3d8bbwe"; #endif /// /// Winget executable name. /// #if USE_PROD_CLSIDS public const string WinGetExe = "winget.exe"; #else public const string WinGetExe = "wingetdev.exe"; #endif /// /// Name of PATH environment variable. /// public const string PathEnvVar = "PATH"; /// /// One MB. /// public const int OneMB = 1024 * 1024; /// /// Repository owners. /// public class RepositoryOwner { /// /// Microsoft org. /// public const string Microsoft = "microsoft"; } /// /// Repository names. /// public class RepositoryName { /// /// https://github.com/microsoft/winget-cli . /// public const string WinGetCli = "winget-cli"; /// /// https://github.com/microsoft/microsoft-ui-xaml . /// public const string UiXaml = "microsoft-ui-xaml"; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Common/ErrorCode.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Common { /// /// Error code constants. /// internal static class ErrorCode { /// /// Error code for ERROR_FILE_NOT_FOUND. /// public const int FileNotFound = unchecked((int)0x80070002); /// /// Error code for RPC_S_SERVER_UNAVAILABLE. /// public const int RpcServerUnavailable = unchecked((int)0x800706BA); /// /// Error code for RPC_S_CALL_FAILED. /// public const int RpcCallFailed = unchecked((int)0x800706BE); /// /// Error code for ERROR_PACKAGE_NOT_REGISTERED_FOR_USER. /// public const int PackageNotRegisteredForUser = unchecked((int)0x80073D35); /// /// Error code for APPINSTALLER_CLI_ERROR_NO_REPAIR_INFO_FOUND. /// public const int NoRepairInfoFound = unchecked((int)0x8A150079); /// /// Error code for APPINSTALLER_CLI_ERROR_REPAIR_NOT_APPLICABLE. /// public const int RepairNotApplicable = unchecked((int)0x8A15007A); /// /// Error code for APPINSTALLER_CLI_ERROR_EXEC_REPAIR_FAILED . /// public const int RepairerFailure = unchecked((int)0x8A15007B); /// /// Error code for APPINSTALLER_CLI_ERROR_REPAIR_NOT_SUPPORTED. /// public const int RepairNotSupported = unchecked((int)0x8A15007C); /// /// Error code for APPINSTALLER_CLI_ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED. /// public const int AdminContextRepairProhibited = unchecked((int)0x8A15007D); } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Common/IntegrityCategory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Common { /// /// The type of the integrity check failure. /// public enum IntegrityCategory { /// /// WinGet is correctly installed. /// Installed, /// /// The version installed is not what is expected. /// UnexpectedVersion, /// /// Unknown reason. /// Unknown, /// /// A failure resulted on a simple winget command that shouldn't happen. /// Failure, /// /// WindowsAppPath not in PATH environment variable. /// NotInPath, /// /// Winget's app execution alias disabled. /// AppExecutionAliasDisabled, /// /// Windows OS is not supported. /// OsNotSupported, /// /// AppInstaller package is not installed. /// AppInstallerNotInstalled, /// /// AppInstaller package is not registered. /// AppInstallerNotRegistered, /// /// Installed App Installer package is not supported. /// AppInstallerNotSupported, /// /// No applicable license found. /// AppInstallerNoLicense, /// /// WinGet source is not installed. /// WinGetSourceNotInstalled, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Common/Utilities.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Common { using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Management.Automation; using System.Security.Principal; using System.Threading; using Microsoft.WinGet.Resources; using Newtonsoft.Json; using Newtonsoft.Json.Linq; /// /// This class contains various helper methods for this project. /// internal static class Utilities { /// /// Gets a value indicating whether the current assembly is executing in an administrative context. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "Windows only API")] public static bool ExecutingAsAdministrator { get { WindowsIdentity identity = WindowsIdentity.GetCurrent(); WindowsPrincipal principal = new (identity); return principal.IsInRole(WindowsBuiltInRole.Administrator); } } /// /// Gets a value indicating whether the current assembly is executing as a SYSTEM user. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "Windows only API")] public static bool ExecutingAsSystem { get { return WindowsIdentity.GetCurrent().IsSystem; } } /// /// Gets a value indicating whether the current execution context will use in-proc winget. /// public static bool UsesInProcWinget { get { return ExecutingAsSystem; } } /// /// Gets a value indicating whether the current thread is executing as STA. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "Windows only API")] public static bool ThreadIsSTA { get { return Thread.CurrentThread.GetApartmentState() == ApartmentState.STA; } } /// /// Gets the windows app path for local app data. /// public static string LocalDataWindowsAppPath { get { return Environment.ExpandEnvironmentVariables(@"%LOCALAPPDATA%\Microsoft\WindowsApps"); } } /// /// Gets the windows app path for program files. /// public static string ProgramFilesWindowsAppPath { get { return Environment.ExpandEnvironmentVariables(@"%PROGRAMFILES%\WindowsApps"); } } /// /// Throws if not running as admin. /// public static void VerifyAdmin() { if (!Utilities.ExecutingAsAdministrator) { throw new PSNotSupportedException(Resources.RequiresAdminMessage); } } /// /// Adds the WindowsApp local app data path to the user environment path. /// public static void AddWindowsAppToPath() { var scope = EnvironmentVariableTarget.User; string? envPathValue = Environment.GetEnvironmentVariable(Constants.PathEnvVar, scope); if (string.IsNullOrEmpty(envPathValue) || !envPathValue.Contains(Utilities.LocalDataWindowsAppPath)) { Environment.SetEnvironmentVariable( Constants.PathEnvVar, $"{envPathValue};{Utilities.LocalDataWindowsAppPath}", scope); } } /// /// This is based of https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs. /// So we can convert JSON to Hashtable for Windows PowerShell and PowerShell Core. /// /// String content. /// The hashtable. public static Hashtable ConvertToHashtable(string content) { if (string.IsNullOrEmpty(content)) { return new Hashtable(); } var obj = JsonConvert.DeserializeObject( content, new JsonSerializerSettings { // This TypeNameHandling setting is required to be secure. TypeNameHandling = TypeNameHandling.None, MetadataPropertyHandling = MetadataPropertyHandling.Ignore, MaxDepth = 1024, }); // It only makes sense that the deserialized object is a dictionary to start. return obj switch { JObject dictionary => PopulateHashTableFromJDictionary(dictionary), _ => throw new InvalidDataException() }; } private static Hashtable PopulateHashTableFromJDictionary(JObject entries) { Hashtable result = new (entries.Count); foreach (var entry in entries) { switch (entry.Value) { case JArray list: { // Array var listResult = PopulateHashTableFromJArray(list); result.Add(entry.Key, listResult); break; } case JObject dic: { // Dictionary var dicResult = PopulateHashTableFromJDictionary(dic); result.Add(entry.Key, dicResult); break; } case JValue value: { result.Add(entry.Key, value.Value); break; } } } return result; } private static ICollection PopulateHashTableFromJArray(JArray list) { var result = new object?[list.Count]; for (var index = 0; index < list.Count; index++) { var element = list[index]; switch (element) { case JArray array: { // Array var listResult = PopulateHashTableFromJArray(array); result[index] = listResult; break; } case JObject dic: { // Dictionary var dicResult = PopulateHashTableFromJDictionary(dic); result[index] = dicResult; break; } case JValue value: { result[index] = value.Value; break; } } } return result; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Common { using System; using System.ComponentModel; using System.IO; using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; /// /// Validates winget runs correctly. /// internal static class WinGetIntegrity { /// /// Verifies winget runs correctly. If it doesn't, tries to find the reason why it failed. /// /// The calling cmdlet. /// Expected version. public static void AssertWinGet(PowerShellCmdlet pwshCmdlet, string expectedVersion) { // In-proc shouldn't have other dependencies and thus should be ok. if (Utilities.UsesInProcWinget) { // Only check the OS version support for in-proc if (!IsSupportedOSVersion()) { throw new WinGetIntegrityException(IntegrityCategory.OsNotSupported); } return; } WinGetCLICommandResult? versionResult = null; try { // Start by calling winget without its WindowsApp PFN path. // If it succeeds and the exit code is 0 then we are good. versionResult = WinGetVersion.RunWinGetVersionFromCLI(pwshCmdlet, false); versionResult.VerifyExitCode(); } catch (Win32Exception e) { pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Win32Exception {e.Message}"); throw new WinGetIntegrityException(GetReason(pwshCmdlet)); } catch (Exception e) when (e is WinGetCLIException || e is WinGetCLITimeoutException) { pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' WinGetCLIException {e.Message}"); throw new WinGetIntegrityException(IntegrityCategory.Failure, e); } catch (Exception e) { pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Exception {e.Message}"); throw new WinGetIntegrityException(IntegrityCategory.Unknown, e); } // WinGet is installed. Verify version if needed. if (!string.IsNullOrEmpty(expectedVersion)) { // This assumes caller knows that the version exist. WinGetVersion expectedWinGetVersion = new WinGetVersion(expectedVersion); var installedVersion = WinGetVersion.InstalledWinGetVersion(pwshCmdlet, versionResult); if (expectedWinGetVersion.CompareTo(installedVersion) != 0) { throw new WinGetIntegrityException( IntegrityCategory.UnexpectedVersion, string.Format( Resources.IntegrityUnexpectedVersionMessage, installedVersion.TagVersion, expectedVersion)) { InstalledVersion = installedVersion }; } } // Verify that the winget source is installed. var appxModule = new AppxModuleHelper(pwshCmdlet); if (!appxModule.IsWinGetSourceInstalled()) { throw new WinGetIntegrityException(IntegrityCategory.WinGetSourceNotInstalled); } } private static IntegrityCategory GetReason(PowerShellCmdlet pwshCmdlet) { // Ok, so you are here because calling winget --version failed. Lets try to figure out why. var category = IntegrityCategory.Unknown; pwshCmdlet.ExecuteInPowerShellThread(() => { // When running winget.exe on PowerShell the message of the Win32Exception will distinguish between // 'The system cannot find the file specified' and 'No applicable app licenses found' but of course // the HRESULT is the same (E_FAIL). // To not compare strings let Powershell handle it. If calling winget throws an // ApplicationFailedException then is most likely that the license is not there. try { var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); ps.AddCommand("winget").Invoke(); } catch (ApplicationFailedException e) { pwshCmdlet.Write(StreamType.Verbose, e.Message); category = IntegrityCategory.AppInstallerNoLicense; } catch (Exception) { } }); if (category != IntegrityCategory.Unknown) { return category; } // First lets check if the file is there, which means it is installed or someone is taking our place. if (File.Exists(WingetCLIWrapper.WinGetFullPath)) { // The file exists, but we couldn't call it... Well maybe winget's app execution alias is not enabled. // The trick is knowing that a magical file appears under WindowsApp when its enabled. string wingetAliasPath = Path.Combine(Utilities.LocalDataWindowsAppPath, Constants.WinGetExe); if (File.Exists(wingetAliasPath)) { // App execution alias is enabled. Then maybe the path? string? envPath = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.User); if (string.IsNullOrEmpty(envPath) || !envPath.EndsWith(Utilities.LocalDataWindowsAppPath) || !envPath.Contains($"{Utilities.LocalDataWindowsAppPath};")) { return IntegrityCategory.NotInPath; } } else { return IntegrityCategory.AppExecutionAliasDisabled; } } // Not under %LOCALAPPDATA%\\Microsoft\\WindowsApps\PFN\ // Check OS version if (!IsSupportedOSVersion()) { return IntegrityCategory.OsNotSupported; } // It could be that AppInstaller package is old or the package is not // registered at this point. To know that, call Get-AppxPackage. var appxModule = new AppxModuleHelper(pwshCmdlet); string? version = appxModule.GetAppInstallerPropertyValue("Version"); if (version is null) { // This can happen in Windows Sandbox. return IntegrityCategory.AppInstallerNotInstalled; } // Now AppInstaller version has to be greater than 1.11.11451 var minAppInstallerVersion = new Version(1, 11, 11451); var appInstallerVersion = new Version(version); if (appInstallerVersion.CompareTo(minAppInstallerVersion) < 0) { return IntegrityCategory.AppInstallerNotSupported; } // If we get here, we know the package is in the machine but not registered for the user. return IntegrityCategory.AppInstallerNotRegistered; } private static bool IsSupportedOSVersion() { // Windows version has to be equal or newer than 10.0.17763.0 var minWindowsVersion = new Version(10, 0, 17763, 0); var osVersion = Environment.OSVersion.Version; return osVersion.CompareTo(minWindowsVersion) >= 0; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/CatalogConnectException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.WinGet.Resources; /// /// Failed connecting to catalog. /// [Serializable] public class CatalogConnectException : RuntimeException { /// /// Initializes a new instance of the class. /// /// The exception that lead to this one. public CatalogConnectException(Exception inner) : base(Resources.CatalogConnectExceptionMessage, inner) { } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/FindPackagesException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Resources; /// /// Raised when there is an error searching for packages. /// [Serializable] internal class FindPackagesException : RuntimeException { /// /// Initializes a new instance of the class. /// /// A value. public FindPackagesException(FindPackagesResultStatus status) : base(string.Format( Resources.FindPackagesExceptionMessage, status.ToString())) { this.Status = status; } /// /// Gets the error status. /// public FindPackagesResultStatus Status { get; private set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidSourceException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using Microsoft.WinGet.Resources; /// /// Invalid source. /// [Serializable] public class InvalidSourceException : ArgumentException { /// /// Initializes a new instance of the class. /// /// Source name. public InvalidSourceException(string sourceName) : base(string.Format(Resources.InvalidSourceExceptionMessage, sourceName)) { this.SourceName = sourceName; } /// /// Gets the source name. /// public string SourceName { get; private set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidVersionException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using Microsoft.WinGet.Resources; /// /// Invalid version. /// [Serializable] public class InvalidVersionException : ArgumentException { /// /// Initializes a new instance of the class. /// /// Version. public InvalidVersionException(string version) : base(string.Format(Resources.InvalidVersionExceptionMessage, version)) { this.Version = version; } /// /// Gets the version. /// public string Version { get; private set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/NoPackageFoundException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.WinGet.Resources; /// /// No package found. /// [Serializable] public class NoPackageFoundException : RuntimeException { /// /// Initializes a new instance of the class. /// public NoPackageFoundException() : base(Resources.NoPackageFoundExceptionMessage) { } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/SingleThreadedApartmentException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.WinGet.Resources; /// /// No package found. /// [Serializable] public class SingleThreadedApartmentException : RuntimeException { /// /// Initializes a new instance of the class. /// public SingleThreadedApartmentException() : base(Resources.SingleThreadedApartmentNotSupportedMessage) { } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/UserSettingsReadException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.WinGet.Resources; /// /// Settings.json file is invalid. /// [Serializable] public class UserSettingsReadException : RuntimeException { /// /// Initializes a new instance of the class. /// public UserSettingsReadException() : base(Resources.UserSettingsReadException) { } /// /// Initializes a new instance of the class. /// /// Inner exception. public UserSettingsReadException(Exception inner) : base(Resources.UserSettingsReadException, inner) { } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/VagueCriteriaException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Collections.Generic; using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Extensions; using Microsoft.WinGet.Resources; /// /// Raised when search criteria for installing or updating a package is too vague. /// [Serializable] internal class VagueCriteriaException : RuntimeException { /// /// Initializes a new instance of the class. /// /// The list of conflicting packages of length at least two. public VagueCriteriaException(IReadOnlyList results) : base(string.Format( Resources.VagueCriteriaExceptionMessage, results[0].CatalogPackage.ToString(null), results[1].CatalogPackage.ToString(null), results.Count - 2)) { this.MatchResults = results; } /// /// Gets the list of conflicting packages. /// public IReadOnlyList MatchResults { get; private set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLIException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System.Management.Automation; using Microsoft.WinGet.Resources; /// /// WinGet cli exception. /// public class WinGetCLIException : RuntimeException { /// /// Initializes a new instance of the class. /// /// Command. /// Parameters. /// Exit code. /// Standard output. /// Standard error. public WinGetCLIException(string command, string? parameters, int exitCode, string stdOut, string stdErr) : base(string.Format(Resources.WinGetCLIExceptionMessage, command, parameters, exitCode)) { this.Command = command; this.Parameters = parameters; this.ExitCode = exitCode; this.StdOut = stdOut; this.StdErr = stdErr; } /// /// Gets the command. /// public string Command { get; private set; } /// /// Gets the parameters. /// public string? Parameters { get; private set; } /// /// Gets the exit code. /// public int ExitCode { get; private set; } /// /// Gets the standard output. /// public string StdOut { get; private set; } /// /// Gets the standard error. /// public string StdErr { get; private set; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLITimeoutException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using Microsoft.WinGet.Resources; /// /// Time out exception for a winget cli command. /// public class WinGetCLITimeoutException : TimeoutException { /// /// Initializes a new instance of the class. /// /// Command. /// Parameters. public WinGetCLITimeoutException(string command, string? parameters) : base(string.Format(Resources.WinGetCLITimeoutExceptionMessage, command, parameters)) { } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetIntegrityException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Resources; /// /// WinGet Integrity exception. /// public class WinGetIntegrityException : RuntimeException { /// /// Initializes a new instance of the class. /// /// Category failure. public WinGetIntegrityException(IntegrityCategory category) : base(GetMessage(category)) { this.Category = category; } /// /// Initializes a new instance of the class. /// /// Category failure. /// Inner exception. public WinGetIntegrityException(IntegrityCategory category, Exception inner) : base(GetMessage(category), inner) { this.Category = category; } /// /// Initializes a new instance of the class. /// /// Category failure. /// Message. public WinGetIntegrityException(IntegrityCategory category, string message) : base(message) { this.Category = category; } /// /// Gets the category of the integrity failure. /// public IntegrityCategory Category { get; } /// /// Gets or sets the installed version. /// internal WinGetVersion? InstalledVersion { get; set; } private static string GetMessage(IntegrityCategory category) => category switch { IntegrityCategory.Failure => Resources.IntegrityFailureMessage, IntegrityCategory.NotInPath => Resources.IntegrityNotInPathMessage, IntegrityCategory.AppExecutionAliasDisabled => Resources.IntegrityAppExecutionAliasDisabledMessage, IntegrityCategory.OsNotSupported => Resources.IntegrityOsNotSupportedMessage, IntegrityCategory.AppInstallerNotInstalled => Resources.IntegrityAppInstallerNotInstalledMessage, IntegrityCategory.AppInstallerNotRegistered => Resources.IntegrityAppInstallerNotRegisteredMessage, IntegrityCategory.AppInstallerNotSupported => Resources.IntegrityAppInstallerNotSupportedMessage, IntegrityCategory.AppInstallerNoLicense => Resources.IntegrityAppInstallerLicense, _ => Resources.IntegrityUnknownMessage, }; } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetRepairException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Resources; /// /// WinGet repair exception. /// [Serializable] public class WinGetRepairException : RuntimeException { /// /// Initializes a new instance of the class. /// /// Integrity exception. public WinGetRepairException(WinGetIntegrityException ie) : base(GetMessage(ie), ie) { } /// /// Initializes a new instance of the class. /// /// Inner exception. public WinGetRepairException(Exception e) : base(Resources.RepairFailureMessage, e) { } /// /// Initializes a new instance of the class. /// public WinGetRepairException() : base(Resources.RepairFailureMessage) { } /// /// Initializes a new instance of the class. /// /// Message.. public WinGetRepairException(string message) : base(message) { } private static string GetMessage(WinGetIntegrityException ie) { string message = Resources.RepairFailureMessage; if (ie.Category == IntegrityCategory.AppInstallerNoLicense) { message += $" {Resources.RepairAllUsersHelpMessage}"; } else if (ie.Category == IntegrityCategory.AppExecutionAliasDisabled) { message += $" {Resources.RepairAppExecutionAliasMessage}"; } return message; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetRepairPackageException.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Resources; /// /// WinGetRepairPackageException. /// [Serializable] public class WinGetRepairPackageException : RuntimeException { /// /// Initializes a new instance of the class. /// /// Repair operation ExtendedErrorCode Hresult. public WinGetRepairPackageException(int hresult) : base(GetMessage(hresult)) { } /// /// Initializes a new instance of the class. /// /// Repair operation ExtendedErrorCode Hresult. /// Repairer exit code. public WinGetRepairPackageException(int hresult, uint repairerExitCode) : base(GetMessage(hresult, repairerExitCode)) { } /// /// Initializes a new instance of the class. /// /// Repair operation ExtendedErrorCode Hresult. /// InnerException. public WinGetRepairPackageException(int hresult, Exception innerException) : base(GetMessage(hresult), innerException) { } /// /// Initializes a new instance of the class. /// /// Repair operation ExtendedErrorCode Hresult. /// Repairer exit code. /// InnerException. public WinGetRepairPackageException(int hresult, uint repairerExitCode, Exception innerException) : base(GetMessage(hresult, repairerExitCode), innerException) { } private static string GetMessage(int hresult, uint repairerExitCode = 0) { switch (hresult) { case ErrorCode.NoRepairInfoFound: return Resources.NoRepairInfoFound; case ErrorCode.RepairerFailure: return string.Format(Resources.RepairerFailure, repairerExitCode); case ErrorCode.RepairNotSupported: return Resources.RepairOperationNotSupported; case ErrorCode.RepairNotApplicable: return Resources.RepairDifferentInstallTechnology; case ErrorCode.AdminContextRepairProhibited: return Resources.NoAdminRepairForUserScopePackage; default: return string.Format(Resources.UnknownRepairFailure, hresult); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WindowsPowerShellNotSupported.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; using Microsoft.WinGet.Resources; /// /// Windows PowerShell is not supported. /// [Serializable] public class WindowsPowerShellNotSupported : RuntimeException { /// /// Initializes a new instance of the class. /// public WindowsPowerShellNotSupported() : base(Resources.WindowsPowerShellNotSupported) { } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Extensions/CatalogPackageExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Extensions { using Microsoft.Management.Deployment; /// /// Extensions for the class. /// internal static class CatalogPackageExtensions { /// /// Converts a to a string previewing the specified version. /// /// A instance. /// A instance. If null, the latest available version is used. /// A instance. public static string ToString( this CatalogPackage package, PackageVersionId? version) { if ((version != null) || (package.AvailableVersions.Count > 0)) { string versionString = (version is null) ? package.AvailableVersions[0].Version : version.Version; return $"{package.Name} [{package.Id}] Version {versionString}"; } else { // There were no available versions! return $"{package.Name} [{package.Id}]"; } } /// /// Gets the best effort source name of a that matches its Id. /// This source name is used together with Id in operation output classes for display purposes. /// /// A instance. /// The best effort source name of the package. public static string? GetSourceName(this CatalogPackage package) { for (int i = 0; i < package.AvailableVersions.Count; ++i) { var versionInfo = package.GetPackageVersionInfo(package.AvailableVersions[i]); if (versionInfo.Id == package.Id) { return versionInfo.PackageCatalog.Info.Name; } } return null; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Extensions/ReleaseExtensions.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Extensions { using System.Linq; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Resources; using Octokit; /// /// Extension methods for Octokit.Release. /// internal static class ReleaseExtensions { /// /// Gets the Asset. /// /// GitHub release. /// Name of asset. /// The asset. public static ReleaseAsset GetAsset(this Release release, string name) { var asset = TryGetAsset(release, name); if (asset != null) { return asset; } throw new WinGetRepairException(string.Format(Resources.ReleaseAssetNotFound, name)); } /// /// Gets the Asset if present. /// /// GitHub release. /// Name of asset. /// The asset, or null if not found. public static ReleaseAsset? TryGetAsset(this Release release, string name) { var assets = release.Assets.Where(a => a.Name == name); return assets.Any() ? assets.First() : null; } /// /// Gets the asset that ends with the string. /// /// GitHub release. /// Asset last part name. /// The asset. public static ReleaseAsset GetAssetEndsWith(this Release release, string name) { var assets = release.Assets.Where(a => a.Name.EndsWith(name)); if (assets.Any()) { return assets.First(); } throw new WinGetRepairException(string.Format(Resources.ReleaseAssetNotFound, name)); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/AppxModuleHelper.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.IO.Compression; using System.Linq; using System.Management.Automation; using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Extensions; using Microsoft.WinGet.Common.Command; using Newtonsoft.Json; using Octokit; using Semver; using static Microsoft.WinGet.Client.Engine.Common.Constants; /// /// Helper to make calls to the Appx module. /// internal class AppxModuleHelper { // Cmdlets private const string ImportModule = "Import-Module"; private const string GetAppxPackage = "Get-AppxPackage"; private const string AddAppxPackage = "Add-AppxPackage"; private const string AddAppxProvisionedPackage = "Add-AppxProvisionedPackage"; private const string GetCommand = "Get-Command"; // Parameters name private const string Name = "Name"; private const string Path = "Path"; private const string ErrorAction = "ErrorAction"; private const string WarningAction = "WarningAction"; private const string PackagePath = "PackagePath"; private const string LicensePath = "LicensePath"; private const string Module = "Module"; private const string StubPackageOption = "StubPackageOption"; private const string PackageTypeFilter = "PackageTypeFilter"; // Parameter Values private const string Appx = "Appx"; private const string Stop = "Stop"; private const string SilentlyContinue = "SilentlyContinue"; private const string Online = "Online"; private const string UsePreference = "UsePreference"; private const string Framework = "Framework"; // Options private const string UseWindowsPowerShell = "UseWindowsPowerShell"; private const string ForceUpdateFromAnyVersion = "ForceUpdateFromAnyVersion"; private const string Register = "Register"; private const string DisableDevelopmentMode = "DisableDevelopmentMode"; private const string ForceTargetApplicationShutdown = "ForceTargetApplicationShutdown"; private const string AllUsers = "AllUsers"; private const string AppInstallerName = "Microsoft.DesktopAppInstaller"; private const string AppxManifest = "AppxManifest.xml"; private const string PackageFullName = "PackageFullName"; private const string Version = "Version"; private const string DependencyArchitectureEnvironmentVariable = "WINGET_PACKAGE_MANAGER_REPAIR_DEPENDENCY_ARCHITECTURES"; // Assets private const string MsixBundleName = "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"; private const string DependenciesJsonName = "DesktopAppInstaller_Dependencies.json"; private const string DependenciesZipName = "DesktopAppInstaller_Dependencies.zip"; private const string License = "License1.xml"; // Format of a dependency package such as 'x64\Microsoft.VCLibs.140.00.UWPDesktop_14.0.33728.0_x64.appx' private const string ExtractedDependencyPath = "{0}\\{1}_{2}_{0}.appx"; // Dependencies // VCLibs private const string VCLibsUWPDesktop = "Microsoft.VCLibs.140.00.UWPDesktop"; private const string VCLibsUWPDesktopVersion = "14.0.30704.0"; private const string VCLibsUWPDesktopX64 = "https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx"; private const string VCLibsUWPDesktopX86 = "https://aka.ms/Microsoft.VCLibs.x86.14.00.Desktop.appx"; private const string VCLibsUWPDesktopArm64 = "https://aka.ms/Microsoft.VCLibs.arm64.14.00.Desktop.appx"; // Xaml private const string XamlPackage28 = "Microsoft.UI.Xaml.2.8"; private const string XamlReleaseTag286 = "v2.8.6"; private const string MinimumWinGetReleaseTagForXaml28 = "v1.7.10514"; private const string XamlPackage27 = "Microsoft.UI.Xaml.2.7"; private const string XamlReleaseTag273 = "v2.7.3"; // WinGet Source private const string WinGetSourceName = "Microsoft.Winget.Source"; private const string WinGetSourceMsixName = "source2.msix"; private const string WinGetSourceUrl = $"https://cdn.winget.microsoft.com/cache/{WinGetSourceMsixName}"; private readonly PowerShellCmdlet pwshCmdlet; private readonly HttpClientHelper httpClientHelper; private Lazy> frameworkArchitectures; /// /// Initializes a new instance of the class. /// /// The calling cmdlet. public AppxModuleHelper(PowerShellCmdlet pwshCmdlet) { this.pwshCmdlet = pwshCmdlet; this.httpClientHelper = new HttpClientHelper(); this.frameworkArchitectures = new Lazy>(() => this.InitFrameworkArchitectures()); } /// /// Calls Get-AppxPackage Microsoft.DesktopAppInstaller. /// /// Whether to get for all users. /// Result of Get-AppxPackage. public PSObject? GetAppInstallerObject(bool allUsers = false) { return this.GetAppxObject(AppInstallerName, allUsers); } /// /// Calls Get-AppxPackage Microsoft.Winget.Source. /// /// Result of Get-AppxPackage. public PSObject? GetWinGetSourceObject() { return this.GetAppxObject(WinGetSourceName); } /// /// Gets the string value a property from the Get-AppxPackage object of AppInstaller. /// /// Property name. /// Whether to get for all users. /// Value, null if doesn't exist. public string? GetAppInstallerPropertyValue(string propertyName, bool allUsers = false) { string? result = null; var packageObj = this.GetAppInstallerObject(allUsers); if (packageObj is not null) { var property = packageObj.Properties.Where(p => p.Name == propertyName).FirstOrDefault(); if (property is not null) { result = property.Value as string; } } return result; } /// /// Checks if winget source is installed. /// /// True if installed. public bool IsWinGetSourceInstalled() { return this.GetWinGetSourceObject() is not null; } /// /// Calls Add-AppxPackage to register with AppInstaller's AppxManifest.xml. /// /// Release tag of GitHub release. /// Whether to register for all users. /// A representing the asynchronous operation. public async Task RegisterAppInstallerAsync(string releaseTag, bool allUsers) { if (string.IsNullOrEmpty(releaseTag)) { string? versionFromLocalPackage = this.GetAppInstallerPropertyValue(Version, allUsers); if (versionFromLocalPackage == null) { throw new ArgumentNullException(Version); } var packageVersion = new Version(versionFromLocalPackage); if (packageVersion.Major == 1 && packageVersion.Minor > 15 && packageVersion.Minor < 28) { releaseTag = $"v1.{packageVersion.Minor - 15}.{packageVersion.Build}"; } else { releaseTag = $"v{packageVersion.Major}.{packageVersion.Minor}.{packageVersion.Build}"; } } // Ensure that all dependencies are present when attempting to register. // If dependencies are missing, a provisioned package can appear to only need registration, // but will fail to register. `InstallDependenciesAsync` checks for the packages before // acting, so it should be mostly a no-op if they are already available. await this.InstallDependenciesAsync(releaseTag); this.RegisterAppInstallerInternal(allUsers); } /// /// Install AppInstaller's bundle from a GitHub release. /// /// Release tag of GitHub release. /// If install for all users is needed. /// Is downgrade. /// Force application shutdown. /// A representing the asynchronous operation. public async Task InstallFromGitHubReleaseAsync(string releaseTag, bool allUsers, bool isDowngrade, bool force) { await this.InstallDependenciesAsync(releaseTag); if (isDowngrade) { // Add-AppxProvisionedPackage doesn't support downgrade. await this.AddAppInstallerBundleAsync(releaseTag, true, force); if (allUsers) { await this.AddProvisionPackageAsync(releaseTag); } } else { if (allUsers) { await this.AddProvisionPackageAsync(releaseTag); } else { await this.AddAppInstallerBundleAsync(releaseTag, false, force); } } } /// /// Installs the WinGet source by downloading and adding package. /// /// A representing the asynchronous operation. public async Task InstallWinGetSourceAsync() { await this.DownloadPackageAndAddAsync(WinGetSourceUrl, WinGetSourceMsixName, options: null); } /// /// Gets the Xaml dependency package name and release tag based on the provided WinGet release tag. /// /// WinGet release tag. /// A tuple in the format of (XamlPackageName, XamlReleaseTag). private static Tuple GetXamlDependencyVersionInfo(string releaseTag) { var targetVersion = SemVersion.Parse(releaseTag, SemVersionStyles.AllowLowerV); if (targetVersion.CompareSortOrderTo(SemVersion.Parse(MinimumWinGetReleaseTagForXaml28, SemVersionStyles.AllowLowerV)) >= 0) { return Tuple.Create(XamlPackage28, XamlReleaseTag286); } else { return Tuple.Create(XamlPackage27, XamlReleaseTag273); } } private async Task AddProvisionPackageAsync(string releaseTag) { var githubClient = new GitHubClient(RepositoryOwner.Microsoft, RepositoryName.WinGetCli, this.pwshCmdlet); var release = await githubClient.GetReleaseAsync(releaseTag); var bundleAsset = release.GetAsset(MsixBundleName); using var bundleFile = new TempFile(fileName: MsixBundleName); await this.httpClientHelper.DownloadUrlWithProgressAsync( bundleAsset.BrowserDownloadUrl, bundleFile.FullPath, this.pwshCmdlet); var licenseAsset = release.GetAssetEndsWith(License); using var licenseFile = new TempFile(fileName: licenseAsset.Name); await this.httpClientHelper.DownloadUrlWithProgressAsync( licenseAsset.BrowserDownloadUrl, licenseFile.FullPath, this.pwshCmdlet); try { this.pwshCmdlet.ExecuteInPowerShellThread( () => { var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); ps.AddCommand(AddAppxProvisionedPackage) .AddParameter(Online) .AddParameter(PackagePath, bundleFile.FullPath) .AddParameter(LicensePath, licenseFile.FullPath) .AddParameter(ErrorAction, Stop) .Invoke(); }); // Register the package after provisioning so that it is // available immediately. this.RegisterAppInstallerInternal(allUsers: true); } catch (RuntimeException e) { this.pwshCmdlet.Write(StreamType.Verbose, $"Failed installing bundle via Add-AppxProvisionedPackage {e}"); throw; } } private async Task AddAppInstallerBundleAsync(string releaseTag, bool downgrade, bool force) { var options = new List(); if (downgrade) { options.Add(ForceUpdateFromAnyVersion); } if (force) { options.Add(ForceTargetApplicationShutdown); } var parameters = new Dictionary(); if (this.IsStubPackageOptionPresent()) { parameters.Add(StubPackageOption, UsePreference); } try { var githubClient = new GitHubClient(RepositoryOwner.Microsoft, RepositoryName.WinGetCli, this.pwshCmdlet); var release = await githubClient.GetReleaseAsync(releaseTag); var bundleAsset = release.GetAsset(MsixBundleName); await this.AddAppxPackageAsUriAsync(bundleAsset.BrowserDownloadUrl, MsixBundleName, parameters, options); } catch (RuntimeException e) { this.pwshCmdlet.Write(StreamType.Verbose, $"Failed installing bundle via Add-AppxPackage {e}"); throw; } } private PSObject? GetAppxObject(string packageName, bool allUsers = false) { var options = new List(); if (allUsers) { options.Add(AllUsers); } return this.ExecuteAppxCmdlet( GetAppxPackage, new Dictionary { { Name, packageName }, }, options) .FirstOrDefault(); } private async Task InstallDependenciesAsync(string releaseTag) { bool result = await this.InstallDependenciesFromGitHubArchive(releaseTag); if (!result) { // A better implementation would use Add-AppxPackage with -DependencyPath, but // the Appx module needs to be remoted into Windows PowerShell. When the string[] parameter // gets deserialized from Core the result is a single string which breaks Add-AppxPackage. // Here we should: if we are in Windows Powershell then run Add-AppxPackage with -DependencyPath // if we are in Core, then start powershell.exe and run the same command. Right now, we just // do Add-AppxPackage for each one. // This method no longer works for versions >1.9 as the vclibs url has been deprecated. await this.InstallVCLibsDependenciesFromUriAsync(); await this.InstallUiXamlAsync(releaseTag); } } /// /// Extracts all of the architectures used by framework packages. /// /// The set of architectures used by installed framework packages. private HashSet InitFrameworkArchitectures() { HashSet architectures = new HashSet(); // Read the override from the environment variable if it exists. string? environmentVariable = Environment.GetEnvironmentVariable(DependencyArchitectureEnvironmentVariable); if (environmentVariable != null) { this.pwshCmdlet.Write(StreamType.Verbose, $"Using environment variable {DependencyArchitectureEnvironmentVariable} for frameworks: {environmentVariable}"); foreach (string architectureString in environmentVariable.Split(',', ';')) { Architecture architecture; if (Enum.TryParse(architectureString, true, out architecture)) { if (architectures.Add(architecture)) { this.pwshCmdlet.Write(StreamType.Verbose, $"Framework architecture from environment variable: {architectureString}"); } } } return architectures; } // If there are any framework packages already installed, use the same architecture as them. var result = this.ExecuteAppxCmdlet( GetAppxPackage, new Dictionary { { PackageTypeFilter, Framework }, }); if (result != null && result.Count > 0) { foreach (dynamic psobject in result) { string? architectureString = psobject?.Architecture?.ToString(); if (architectureString == null) { continue; } Architecture architecture; if (Enum.TryParse(architectureString, true, out architecture)) { if (architectures.Add(architecture)) { this.pwshCmdlet.Write(StreamType.Verbose, $"Found framework architecture: {architectureString}"); } } } } // Fall back to guessing from the current OS architecture. // This may have issues on ARM64 because RuntimeInformation.OSArchitecture seems to just lie sometimes. // See https://github.com/microsoft/winget-cli/issues/5020 if (architectures.Count == 0) { var arch = RuntimeInformation.OSArchitecture; this.pwshCmdlet.Write(StreamType.Verbose, $"OS architecture: {arch.ToString()}"); if (arch == Architecture.X64) { architectures.Add(Architecture.X64); } else if (arch == Architecture.X86) { architectures.Add(Architecture.X86); } else if (arch == Architecture.Arm64) { // Let deployment figure it out architectures.Add(Architecture.Arm64); architectures.Add(Architecture.X64); architectures.Add(Architecture.X86); } } return architectures; } private Dictionary GetDependenciesByArch(PackageDependency dependencies) { Dictionary appxPackages = new Dictionary(); foreach (var architecture in this.frameworkArchitectures.Value) { switch (architecture) { case Architecture.X86: appxPackages.Add("x86", string.Format(ExtractedDependencyPath, "x86", dependencies.Name, dependencies.Version)); break; case Architecture.X64: appxPackages.Add("x64", string.Format(ExtractedDependencyPath, "x64", dependencies.Name, dependencies.Version)); break; case Architecture.Arm64: appxPackages.Add("arm64", string.Format(ExtractedDependencyPath, "arm64", dependencies.Name, dependencies.Version)); break; default: this.pwshCmdlet.Write(StreamType.Verbose, $"GetDependenciesByArch: Ignoring {architecture}"); break; } } return appxPackages; } private void FindMissingDependencies(Dictionary dependencies, string packageName, string requiredVersion) { var result = this.ExecuteAppxCmdlet( GetAppxPackage, new Dictionary { { Name, packageName }, }); Version minimumVersion = new Version(requiredVersion); if (result != null && result.Count > 0) { foreach (dynamic psobject in result) { string? versionString = psobject?.Version?.ToString(); if (versionString == null) { continue; } Version packageVersion = new Version(versionString); if (packageVersion >= minimumVersion) { string? architectureString = psobject?.Architecture?.ToString(); if (architectureString == null) { this.pwshCmdlet.Write(StreamType.Verbose, $"{packageName} dependency has no architecture value: {psobject?.PackageFullName ?? ""}"); continue; } architectureString = architectureString.ToLower(); if (dependencies.ContainsKey(architectureString)) { this.pwshCmdlet.Write(StreamType.Verbose, $"{packageName} {architectureString} dependency satisfied by: {psobject?.PackageFullName ?? ""}"); dependencies.Remove(architectureString); } } else { this.pwshCmdlet.Write(StreamType.Verbose, $"{packageName} is lower than minimum required version [{minimumVersion}]: {psobject?.PackageFullName ?? ""}"); } } } } private async Task InstallVCLibsDependenciesFromUriAsync() { Dictionary vcLibsDependencies = this.GetVCLibsDependencies(); this.FindMissingDependencies(vcLibsDependencies, VCLibsUWPDesktop, VCLibsUWPDesktopVersion); if (vcLibsDependencies.Count != 0) { this.pwshCmdlet.Write(StreamType.Verbose, "Couldn't find required VCLibs packages"); foreach (var vclibPair in vcLibsDependencies) { string vclib = vclibPair.Value; await this.AddAppxPackageAsUriAsync(vclib, vclib.Substring(vclib.LastIndexOf('/') + 1)); } } else { this.pwshCmdlet.Write(StreamType.Verbose, $"VCLibs are updated."); } } // Returns a boolean value indicating whether dependencies were successfully installed from the GitHub release assets. private async Task InstallDependenciesFromGitHubArchive(string releaseTag) { var githubClient = new GitHubClient(RepositoryOwner.Microsoft, RepositoryName.WinGetCli, this.pwshCmdlet); var release = await githubClient.GetReleaseAsync(releaseTag); ReleaseAsset? dependenciesJsonAsset = release.TryGetAsset(DependenciesJsonName); if (dependenciesJsonAsset is null) { return false; } using var dependenciesJsonFile = new TempFile(); await this.httpClientHelper.DownloadUrlWithProgressAsync(dependenciesJsonAsset.BrowserDownloadUrl, dependenciesJsonFile.FullPath, this.pwshCmdlet); using StreamReader r = new StreamReader(dependenciesJsonFile.FullPath); string json = r.ReadToEnd(); WingetDependencies? wingetDependencies = JsonConvert.DeserializeObject(json); if (wingetDependencies is null) { this.pwshCmdlet.Write(StreamType.Verbose, $"Failed to deserialize dependencies json file."); return false; } List missingDependencies = new List(); foreach (var dependency in wingetDependencies.Dependencies) { Dictionary dependenciesByArch = this.GetDependenciesByArch(dependency); this.FindMissingDependencies(dependenciesByArch, dependency.Name, dependency.Version); foreach (var pair in dependenciesByArch) { missingDependencies.Add(pair.Value); } } if (missingDependencies.Count != 0) { using var dependenciesZipFile = new TempFile(); using var extractedDirectory = new TempDirectory(); ReleaseAsset? dependenciesZipAsset = release.TryGetAsset(DependenciesZipName); if (dependenciesZipAsset is null) { this.pwshCmdlet.Write(StreamType.Verbose, $"Dependencies zip asset not found on GitHub asset."); return false; } await this.httpClientHelper.DownloadUrlWithProgressAsync(dependenciesZipAsset.BrowserDownloadUrl, dependenciesZipFile.FullPath, this.pwshCmdlet); ZipFile.ExtractToDirectory(dependenciesZipFile.FullPath, extractedDirectory.FullDirectoryPath); foreach (var entry in missingDependencies) { string fullPath = System.IO.Path.Combine(extractedDirectory.FullDirectoryPath, entry); if (!File.Exists(fullPath)) { this.pwshCmdlet.Write(StreamType.Verbose, $"Package dependency not found in archive: {fullPath}"); return false; } _ = this.ExecuteAppxCmdlet( AddAppxPackage, new Dictionary { { Path, fullPath }, { ErrorAction, Stop }, }); } } return true; } private Dictionary GetVCLibsDependencies() { Dictionary vcLibsDependencies = new Dictionary(); foreach (var architecture in this.frameworkArchitectures.Value) { switch (architecture) { case Architecture.X86: vcLibsDependencies.Add("x86", VCLibsUWPDesktopX86); break; case Architecture.X64: vcLibsDependencies.Add("x64", VCLibsUWPDesktopX64); break; case Architecture.Arm64: vcLibsDependencies.Add("arm64", VCLibsUWPDesktopArm64); break; default: this.pwshCmdlet.Write(StreamType.Verbose, $"GetVCLibsDependencies: Ignoring {architecture}"); break; } } return vcLibsDependencies; } private async Task InstallUiXamlAsync(string releaseTag) { (string xamlPackageName, string xamlReleaseTag) = GetXamlDependencyVersionInfo(releaseTag); string xamlAssetX64 = string.Format("{0}.x64.appx", xamlPackageName); string xamlAssetX86 = string.Format("{0}.x86.appx", xamlPackageName); string xamlAssetArm64 = string.Format("{0}.arm64.appx", xamlPackageName); var uiXamlObjs = this.GetAppxObject(xamlPackageName); if (uiXamlObjs is null) { var githubRelease = new GitHubClient(RepositoryOwner.Microsoft, RepositoryName.UiXaml, this.pwshCmdlet); var xamlRelease = await githubRelease.GetReleaseAsync(xamlReleaseTag); var packagesToInstall = new List(); foreach (var architecture in this.frameworkArchitectures.Value) { switch (architecture) { case Architecture.X86: packagesToInstall.Add(xamlRelease.GetAsset(xamlAssetX86)); break; case Architecture.X64: packagesToInstall.Add(xamlRelease.GetAsset(xamlAssetX64)); break; case Architecture.Arm64: packagesToInstall.Add(xamlRelease.GetAsset(xamlAssetArm64)); break; default: this.pwshCmdlet.Write(StreamType.Verbose, $"InstallUiXamlAsync: Ignoring {architecture}"); break; } } foreach (var package in packagesToInstall) { await this.AddAppxPackageAsUriAsync(package.BrowserDownloadUrl, package.Name); } } } private async Task AddAppxPackageAsUriAsync(string packageUri, string fileName, Dictionary? parameters = null, IList? options = null) { try { var thisParams = new Dictionary { { Path, packageUri }, { ErrorAction, Stop }, }; if (parameters != null) { foreach (var param in parameters) { thisParams.Add(param.Key, param.Value); } } _ = this.ExecuteAppxCmdlet( AddAppxPackage, thisParams, options); } catch (RuntimeException e) { // If we couldn't install it via URI, try download and install. if (e.ErrorRecord.CategoryInfo.Category == ErrorCategory.OpenError) { this.pwshCmdlet.Write(StreamType.Verbose, $"Failed adding package [{packageUri}]. Retrying downloading it."); await this.DownloadPackageAndAddAsync(packageUri, fileName, options); } else { this.pwshCmdlet.Write(StreamType.Error, e.ErrorRecord); throw; } } } private async Task DownloadPackageAndAddAsync(string packageUrl, string fileName, IList? options) { using var tempFile = new TempFile(fileName: fileName); await this.httpClientHelper.DownloadUrlWithProgressAsync(packageUrl, tempFile.FullPath, this.pwshCmdlet); _ = this.ExecuteAppxCmdlet( AddAppxPackage, new Dictionary { { Path, tempFile.FullPath }, { ErrorAction, Stop }, }, options); } private Collection ExecuteAppxCmdlet(string cmdlet, Dictionary? parameters = null, IList? options = null) { Collection result = new Collection(); this.pwshCmdlet.ExecuteInPowerShellThread( () => { var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); // There's a bug in the Appx Module that it can't be loaded from Core in pre 10.0.22453.0 builds without // the -UseWindowsPowerShell option. In post 10.0.22453.0 builds there's really no difference between // using or not -UseWindowsPowerShell as it will automatically get loaded using WinPSCompatSession remoting session. // https://github.com/PowerShell/PowerShell/issues/13138. // Set warning action to silently continue to avoid the console with // 'Module Appx is loaded in Windows PowerShell using WinPSCompatSession remoting session' #if !POWERSHELL_WINDOWS ps.AddCommand(ImportModule) .AddParameter(Name, Appx) .AddParameter(UseWindowsPowerShell) .AddParameter(WarningAction, SilentlyContinue) .AddStatement(); #endif string cmd = cmdlet; ps.AddCommand(cmdlet); if (parameters != null) { foreach (var p in parameters) { cmd += $" -{p.Key} {p.Value}"; } ps.AddParameters(parameters); } if (options != null) { foreach (var option in options) { cmd += $" -{option}"; ps.AddParameter(option); } } this.pwshCmdlet.Write(StreamType.Verbose, $"Executing Appx cmdlet {cmd}"); result = ps.Invoke(); }); return result; } private bool IsStubPackageOptionPresent() { bool result = false; this.pwshCmdlet.ExecuteInPowerShellThread( () => { var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); #if !POWERSHELL_WINDOWS ps.AddCommand(ImportModule) .AddParameter(Name, Appx) .AddParameter(UseWindowsPowerShell) .AddParameter(WarningAction, SilentlyContinue) .AddStatement(); #endif var cmdInfo = ps.AddCommand(GetCommand) .AddParameter(Name, AddAppxPackage) .AddParameter(Module, Appx) .Invoke() .FirstOrDefault(); result = cmdInfo != null && cmdInfo.Parameters.ContainsKey(StubPackageOption); }); return result; } private void RegisterAppInstallerInternal(bool allUsers = false) { string? packageFullName = this.GetAppInstallerPropertyValue(PackageFullName, allUsers); if (packageFullName == null) { throw new ArgumentNullException(PackageFullName); } string appxManifestPath = System.IO.Path.Combine( Utilities.ProgramFilesWindowsAppPath, packageFullName, AppxManifest); _ = this.ExecuteAppxCmdlet( AddAppxPackage, new Dictionary { { Path, appxManifestPath }, }, new List { Register, DisableDevelopmentMode, }); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/DownloadOperationWithProgress.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; using Windows.Foundation; /// /// Handler progress for package download. /// internal class DownloadOperationWithProgress : OperationWithProgressBase { /// /// Initializes a new instance of the class. /// /// A instance. /// Activity. public DownloadOperationWithProgress(PowerShellCmdlet pwshCmdlet, string activity) : base(pwshCmdlet, activity) { } /// public override void Progress(IAsyncOperationWithProgress operation, PackageDownloadProgress progress) { ProgressRecord record = new (this.ActivityId, this.Activity, progress.State.ToString()) { RecordType = ProgressRecordType.Processing, }; record.StatusDescription = Resources.DownloadingMessage; record.PercentComplete = (int)(progress.DownloadProgress * 100); this.PwshCmdlet.Write(StreamType.Progress, record); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/GitHubClient.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.WinGet.Common.Command; using Octokit; using static Microsoft.WinGet.Client.Engine.Common.Constants; /// /// Handles GitHub interactions. /// internal class GitHubClient { private readonly string owner; private readonly string repo; private readonly IGitHubClient gitHubClient; private readonly PowerShellCmdlet? pwshCmdlet; /// /// Initializes a new instance of the class. /// /// Owner. /// Repository. /// Optional PowerShell cmdlet for logging. public GitHubClient(string owner, string repo, PowerShellCmdlet? pwshCmdlet = null) { this.pwshCmdlet = pwshCmdlet; var octokitClient = new Octokit.GitHubClient(new ProductHeaderValue(HttpClientHelper.UserAgent)); string? token = ResolveGitHubToken(pwshCmdlet); if (!string.IsNullOrWhiteSpace(token)) { octokitClient.Credentials = new Credentials(token); } this.gitHubClient = octokitClient; this.owner = owner; this.repo = repo; } /// /// Gets a release. /// /// Release tag. /// The Release. public async Task GetReleaseAsync(string releaseTag) { return await this.gitHubClient.Repository.Release.Get(this.owner, this.repo, releaseTag); } /// /// Gets the latest released and waits. /// /// Include prerelease. /// Latest version. public async Task GetLatestReleaseTagNameAsync(bool includePrerelease) { return (await this.GetLatestReleaseAsync(includePrerelease)).TagName; } /// /// Gets the latest released version. /// /// Include prerelease. /// Latest version. public async Task GetLatestReleaseAsync(bool includePrerelease) { var allReleases = await this.GetAllReleasesAsync(); allReleases = includePrerelease ? allReleases : allReleases.Where(r => !r.Prerelease).ToList(); return allReleases.Select(r => new { Release = r, WinGetVersion = new WinGetVersion(r.TagName) }) .OrderBy(rv => rv.WinGetVersion.Version) .Last().Release; } /// /// Gets all releases. /// /// All releases. public async Task> GetAllReleasesAsync() { return await this.gitHubClient.Repository.Release.GetAll(this.owner, this.repo); } /// /// Resolve a version string to the latest matching version from GitHub releases. /// /// Version string to resolve. Can include wildcards (*). /// Whether to include prerelease versions in the search. /// Resolved version string or null if no match found. public async Task ResolveVersionAsync(string version, bool includePrerelease) { if (!WinGetVersion.VersionHasWildcard(version)) { return version; } var allReleases = await this.GetAllReleasesAsync(); var allWinGetReleases = allReleases.Select(r => new WinGetVersion(r.TagName)); if (TryGetLatestMatchingVersion(allWinGetReleases, version, includePrerelease, out var latestVersion)) { return latestVersion!.TagVersion; } return null; } /// /// Reads all known GitHub token environment variables, logs their presence, /// and selects the one to use based on precedence. /// GH_TOKEN takes precedence over GITHUB_TOKEN, matching GitHub CLI behavior. /// See: https://cli.github.com/manual/gh_help_environment. /// /// Optional PowerShell cmdlet for logging. /// The selected token value, or null if none found. internal static string? ResolveGitHubToken(PowerShellCmdlet? pwshCmdlet = null) { string? ghToken = Environment.GetEnvironmentVariable("GH_TOKEN"); string? githubToken = Environment.GetEnvironmentVariable("GITHUB_TOKEN"); bool hasGhToken = !string.IsNullOrWhiteSpace(ghToken); bool hasGithubToken = !string.IsNullOrWhiteSpace(githubToken); pwshCmdlet?.Write(StreamType.Verbose, $"GH_TOKEN environment variable: {(hasGhToken ? "found" : "not found")}"); pwshCmdlet?.Write(StreamType.Verbose, $"GITHUB_TOKEN environment variable: {(hasGithubToken ? "found" : "not found")}"); if (hasGhToken) { pwshCmdlet?.Write(StreamType.Verbose, "Using authenticated GitHub API requests via GH_TOKEN environment variable."); return ghToken; } else if (hasGithubToken) { pwshCmdlet?.Write(StreamType.Verbose, "Using authenticated GitHub API requests via GITHUB_TOKEN environment variable."); return githubToken; } pwshCmdlet?.Write(StreamType.Verbose, "No GitHub token found. Using unauthenticated GitHub API requests."); return null; } /// /// Tries to get the latest version matching the pattern. /// /// /// Pattern only supports leading and trailing wildcards. /// - For example, the pattern can be: 1.11.*, 1.11.3*, 1.11.*3 /// - But it cannot be: 1.*1*.1 or 1.1*1.1. /// /// List of versions to match against. /// Pattern to match. /// Include prerelease versions. /// The resulting version. /// True if a matching version was found. private static bool TryGetLatestMatchingVersion(IEnumerable versions, string pattern, bool includePrerelease, out WinGetVersion? result) { pattern = string.IsNullOrWhiteSpace(pattern) ? "*" : pattern; var parts = pattern.Split('.'); var major = parts.ElementAtOrDefault(0); var minor = parts.ElementAtOrDefault(1); var build = parts.ElementAtOrDefault(2); var revision = parts.ElementAtOrDefault(3); if (!includePrerelease) { versions = versions.Where(v => !v.IsPrerelease); } versions = versions .Where(v => VersionPartMatch(major, v.Version.Major) && VersionPartMatch(minor, v.Version.Minor) && VersionPartMatch(build, v.Version.Build) && VersionPartMatch(revision, v.Version.Revision)) .OrderBy(f => f.Version); if (!versions.Any()) { result = null!; return false; } result = versions.Last(); return true; } /// /// Checks if a version part matches a pattern. /// /// Version part pattern. /// Version part value. /// True if the part matches the pattern. private static bool VersionPartMatch(string? partPattern, int partValue) { if (string.IsNullOrWhiteSpace(partPattern)) { return true; } if (partPattern!.StartsWith("*")) { return partValue.ToString().EndsWith(partPattern.TrimStart('*')); } if (partPattern!.EndsWith("*")) { return partValue.ToString().StartsWith(partPattern.TrimEnd('*')); } return partPattern == partValue.ToString(); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/HttpClientHelper.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.Diagnostics; using System.IO; using System.Management.Automation; using System.Net.Http; using System.Threading.Tasks; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; /// /// Helper class for HttpClient calls. /// internal class HttpClientHelper { /// /// The user agent of this module. /// public const string UserAgent = "winget-powershell"; private static readonly HttpClient Client; static HttpClientHelper() { Client = new HttpClient(); } /// /// Downloads a file from a url. /// /// Url. /// File name. /// /// PowershellCmdlet. /// A representing the asynchronous operation. public async Task DownloadUrlWithProgressAsync(string url, string fileName, PowerShellCmdlet pwshCmdlet) { pwshCmdlet.Write(StreamType.Verbose, $"Downloading {url}"); using var request = new HttpRequestMessage(HttpMethod.Get, url); request.Headers.Add("User-Agent", UserAgent); var cancellationToken = pwshCmdlet.GetCancellationToken(); using var response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); response.EnsureSuccessStatusCode(); try { long? contentLength = response.Content.Headers.ContentLength; var responseStream = await response.Content.ReadAsStreamAsync(); using var fileStream = File.Open(fileName, FileMode.OpenOrCreate); if (contentLength.HasValue) { pwshCmdlet.Write(StreamType.Verbose, $"Size {contentLength} bytes"); byte[] buffer = new byte[Constants.OneMB]; int bytesRead, totalBytes = 0; var activityId = pwshCmdlet.GetNewProgressActivityId(); double lengthInMB = (double)contentLength.Value / Constants.OneMB; try { int maxPercentComplete = 0; while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) { await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken); totalBytes += bytesRead; int percentComplete = (int)((double)totalBytes / contentLength * 100); if (percentComplete > maxPercentComplete) { maxPercentComplete = percentComplete; ProgressRecord record = new (activityId, url, Resources.DownloadingMessage) { RecordType = ProgressRecordType.Processing, }; double progress = (double)totalBytes / Constants.OneMB; record.StatusDescription = $"{progress:0.0} MB / {lengthInMB:0.0} MB"; record.PercentComplete = percentComplete; pwshCmdlet.Write(StreamType.Progress, record); } } } finally { pwshCmdlet.CompleteProgress(activityId, url, Resources.DownloadingMessage, true); } } else { pwshCmdlet.Write(StreamType.Verbose, $"Content-Length not found in response"); await responseStream.CopyToAsync(fileStream); } } catch (Exception) { if (File.Exists(fileName)) { File.Delete(fileName); } throw; } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/InstallOperationWithProgress.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Common.Command; using Windows.Foundation; /// /// Handlers install or update operations with progress. /// internal class InstallOperationWithProgress : OperationWithProgressBase { /// /// Initializes a new instance of the class. /// /// A instance. /// Activity. public InstallOperationWithProgress(PowerShellCmdlet pwshCmdlet, string activity) : base(pwshCmdlet, activity) { } /// public override void Progress(IAsyncOperationWithProgress operation, InstallProgress progress) { ProgressRecord record = new (this.ActivityId, this.Activity, progress.State.ToString()) { RecordType = ProgressRecordType.Processing, }; if (progress.State == PackageInstallProgressState.Downloading && progress.BytesRequired != 0) { double downloaded = (double)progress.BytesDownloaded / Constants.OneMB; double total = (double)progress.BytesRequired / Constants.OneMB; record.StatusDescription = $"{downloaded:0.0} MB / {total:0.0} MB"; record.PercentComplete = (int)(progress.DownloadProgress * 100); } else if (progress.State == PackageInstallProgressState.Installing) { record.PercentComplete = (int)(progress.InstallationProgress * 100); } this.PwshCmdlet.Write(StreamType.Progress, record); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/ManagementDeploymentFactory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; #if NET using WinRT; #endif /// /// Constructs instances of classes from the namespace. /// internal sealed class ManagementDeploymentFactory { #if USE_PROD_CLSIDS private static readonly Guid PackageManagerClsid = Guid.Parse("C53A4F16-787E-42A4-B304-29EFFB4BF597"); private static readonly Guid FindPackagesOptionsClsid = Guid.Parse("572DED96-9C60-4526-8F92-EE7D91D38C1A"); private static readonly Guid CreateCompositePackageCatalogOptionsClsid = Guid.Parse("526534B8-7E46-47C8-8416-B1685C327D37"); private static readonly Guid InstallOptionsClsid = Guid.Parse("1095F097-EB96-453B-B4E6-1613637F3B14"); private static readonly Guid UninstallOptionsClsid = Guid.Parse("E1D9A11E-9F85-4D87-9C17-2B93143ADB8D"); private static readonly Guid PackageMatchFilterClsid = Guid.Parse("D02C9DAF-99DC-429C-B503-4E504E4AB000"); private static readonly Guid DownloadOptionsClsid = Guid.Parse("4CBABE76-7322-4BE4-9CEA-2589A80682DC"); private static readonly Guid RepairOptionsClsid = Guid.Parse("0498F441-3097-455F-9CAF-148F28293865"); #else private static readonly Guid PackageManagerClsid = Guid.Parse("74CB3139-B7C5-4B9E-9388-E6616DEA288C"); private static readonly Guid FindPackagesOptionsClsid = Guid.Parse("1BD8FF3A-EC50-4F69-AEEE-DF4C9D3BAA96"); private static readonly Guid CreateCompositePackageCatalogOptionsClsid = Guid.Parse("EE160901-B317-4EA7-9CC6-5355C6D7D8A7"); private static readonly Guid InstallOptionsClsid = Guid.Parse("44FE0580-62F7-44D4-9E91-AA9614AB3E86"); private static readonly Guid UninstallOptionsClsid = Guid.Parse("AA2A5C04-1AD9-46C4-B74F-6B334AD7EB8C"); private static readonly Guid PackageMatchFilterClsid = Guid.Parse("3F85B9F4-487A-4C48-9035-2903F8A6D9E8"); private static readonly Guid DownloadOptionsClsid = Guid.Parse("8EF324ED-367C-4880-83E5-BB2ABD0B72F6"); private static readonly Guid RepairOptionsClsid = Guid.Parse("E62BB1E7-C7B2-4AEC-9E28-FB649B30FF03"); #endif [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static readonly Type? PackageManagerType = Type.GetTypeFromCLSID(PackageManagerClsid); [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static readonly Type? FindPackagesOptionsType = Type.GetTypeFromCLSID(FindPackagesOptionsClsid); [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static readonly Type? CreateCompositePackageCatalogOptionsType = Type.GetTypeFromCLSID(CreateCompositePackageCatalogOptionsClsid); [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static readonly Type? InstallOptionsType = Type.GetTypeFromCLSID(InstallOptionsClsid); [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static readonly Type? UninstallOptionsType = Type.GetTypeFromCLSID(UninstallOptionsClsid); [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static readonly Type? PackageMatchFilterType = Type.GetTypeFromCLSID(PackageMatchFilterClsid); [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static readonly Type? DownloadOptionsType = Type.GetTypeFromCLSID(DownloadOptionsClsid); [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static readonly Type? RepairOptionsType = Type.GetTypeFromCLSID(RepairOptionsClsid); // These GUIDs correspond to the CSWinRT interface IIDs generated for Microsoft.Management.Deployment.Projection // and are auto-generated by the WinRT tool. private static readonly Guid PackageManagerIid = Guid.Parse("B375E3B9-F2E0-5C93-87A7-B67497F7E593"); private static readonly Guid FindPackagesOptionsIid = Guid.Parse("A5270EDD-7DA7-57A3-BACE-F2593553561F"); private static readonly Guid CreateCompositePackageCatalogOptionsIid = Guid.Parse("21ABAA76-089D-51C5-A745-C85EEFE70116"); private static readonly Guid InstallOptionsIid = Guid.Parse("6EE9DB69-AB48-5E72-A474-33A924CD23B3"); private static readonly Guid UninstallOptionsIid = Guid.Parse("3EBC67F0-8339-594B-8A42-F90B69D02BBE"); private static readonly Guid PackageMatchFilterIid = Guid.Parse("D981ECA3-4DE5-5AD7-967A-698C7D60FC3B"); private static readonly Guid DownloadOptionsIid = Guid.Parse("94C92C4B-43F5-5CA3-BBBE-9F432C9546BC"); private static readonly Guid RepairOptionsIid = Guid.Parse("263F0546-2D7E-53A0-B8D1-75B74817FF18"); private static readonly IEnumerable ValidArchs = new Architecture[] { Architecture.X86, Architecture.X64 }; private static readonly Lazy Lazy = new (() => new ManagementDeploymentFactory()); /// /// Initializes static members of the class. /// static ManagementDeploymentFactory() { if (Utilities.UsesInProcWinget) { PackageManagerSettings settings = new PackageManagerSettings(); settings.SetCallerIdentifier("PowerShellInProc"); } } private ManagementDeploymentFactory() { } /// /// Gets the instance object. /// public static ManagementDeploymentFactory Instance { get { return Lazy.Value; } } /// /// Creates an instance of the class. /// /// A instance. public PackageManager CreatePackageManager() { var result = Create(PackageManagerType, PackageManagerIid); if (!Utilities.UsesInProcWinget) { _ = CoAllowSetForegroundWindow(result, IntPtr.Zero); } return result; } /// /// Creates an instance of the class. /// /// A instance. public FindPackagesOptions CreateFindPackagesOptions() { return Create(FindPackagesOptionsType, FindPackagesOptionsIid); } /// /// Creates an instance of the class. /// /// A instance. public CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions() { return Create(CreateCompositePackageCatalogOptionsType, CreateCompositePackageCatalogOptionsIid); } /// /// Creates an instance of the class. /// /// An instance. public InstallOptions CreateInstallOptions() { return Create(InstallOptionsType, InstallOptionsIid); } /// /// Creates an instance of the class. /// /// A instance. public UninstallOptions CreateUninstallOptions() { return Create(UninstallOptionsType, UninstallOptionsIid); } /// /// Creates an instance of the class. /// /// A instance. public DownloadOptions CreateDownloadOptions() { return Create(DownloadOptionsType, DownloadOptionsIid); } /// /// Creates an instance of the class. /// /// A instance. public PackageMatchFilter CreatePackageMatchFilter() { return Create(PackageMatchFilterType, PackageMatchFilterIid); } /// /// Creates an instance of the class. /// /// A instance. public RepairOptions CreateRepairOptions() { return Create(RepairOptionsType, RepairOptionsIid); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "COM only usage.")] private static T Create(Type? type, in Guid iid) where T : new() { if (type == null) { throw new ArgumentNullException(iid.ToString()); } if (Utilities.UsesInProcWinget) { // This doesn't work on Windows PowerShell // If we want to support it, we need something that loads the // Microsoft.Management.Deployment.dll for .NET framework as CsWinRT // does for .NET Core return new T(); } object? instance = null; if (Utilities.ExecutingAsAdministrator) { int hr = WinRTHelpers.ManualActivation(type.GUID, iid, 0, out instance); if (hr < 0) { if (hr == ErrorCode.FileNotFound || hr == ErrorCode.PackageNotRegisteredForUser) { throw new WinGetIntegrityException(IntegrityCategory.AppInstallerNotInstalled); } else { throw new COMException($"Failed to create instance: {hr}", hr); } } } else { instance = Activator.CreateInstance(type); } if (instance == null) { throw new ArgumentNullException(); } #if NET IntPtr pointer = Marshal.GetIUnknownForObject(instance); return MarshalInterface.FromAbi(pointer); #else return (T)instance; #endif } [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = true)] private static extern int CoAllowSetForegroundWindow([MarshalAs(UnmanagedType.IUnknown)] object pUnk, IntPtr reserved); } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/OperationWithProgressBase.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; using Windows.Foundation; /// /// Base class for async operations with progress. /// /// The operation result. /// Progress data. internal abstract class OperationWithProgressBase { private static bool isProgressEnabled; static OperationWithProgressBase() { // Progress on arm64 will produce an AV because there's an OS bug where marshaling structs over a certain size fail. // Fix is in 10.0.26068.0, for build before that disable progress. if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { var minWindowsVersion = new Version(10, 0, 26068, 0); var osVersion = Environment.OSVersion.Version; isProgressEnabled = osVersion.CompareTo(minWindowsVersion) >= 0; } else { isProgressEnabled = true; } } /// /// Initializes a new instance of the class. /// /// A instance. /// Activity. public OperationWithProgressBase( PowerShellCmdlet pwshCmdlet, string activity) { this.PwshCmdlet = pwshCmdlet; this.ActivityId = pwshCmdlet.GetNewProgressActivityId(); this.Activity = activity; } /// /// Gets the PowerShellCmdlet. /// protected PowerShellCmdlet PwshCmdlet { get; } /// /// Gets the progress activity id. /// protected int ActivityId { get; } /// /// Gets the activity. /// protected string Activity { get; } /// /// Progress callback. /// /// Async operation in progress. /// Progress data. public abstract void Progress(IAsyncOperationWithProgress operation, TProgressData progress); /// /// Starts the operation and executes it as task. /// Supports cancellation. /// /// Lambda with operation. /// TOperationReturn. public async Task ExecuteAsync(Func> func) { var operation = func(); if (isProgressEnabled) { operation.Progress = this.Progress; } try { return await operation.AsTask(this.PwshCmdlet.GetCancellationToken()); } finally { this.Complete(); } } /// /// Completes progress for this activity. /// protected virtual void Complete() { this.PwshCmdlet.CompleteProgress(this.ActivityId, this.Activity, Resources.Completed, true); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PSEnumHelpers.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using Microsoft.Management.Deployment; using Newtonsoft.Json.Linq; using Windows.System; /// /// Extension methods for PS Enum wrappers for Microsoft.Management.Deployment enums. /// internal static class PSEnumHelpers { /// /// Checks if the provided enum string value matches the 'Default' value for PS Enums. /// /// Enum string value. /// Boolean value. public static bool IsDefaultEnum(string value) { return string.Equals(value, "Default", StringComparison.OrdinalIgnoreCase); } /// /// Converts PSPackageInstallMode string value to PackageInstallMode. /// /// PSPackageInstallMode to string value. /// PackageInstallMode. public static PackageInstallMode ToPackageInstallMode(string value) { return value switch { "Default" => PackageInstallMode.Default, "Silent" => PackageInstallMode.Silent, "Interactive" => PackageInstallMode.Interactive, _ => throw new InvalidOperationException(), }; } /// /// Converts PSPackageInstallScope string value to PackageInstallScope. /// /// PSPackageInstallScope to string value. /// PackageInstallScope. public static PackageInstallScope ToPackageInstallScope(string value) { return value switch { "Any" => PackageInstallScope.Any, "User" => PackageInstallScope.User, "System" => PackageInstallScope.System, "UserOrUnknown" => PackageInstallScope.UserOrUnknown, "SystemOrUnknown" => PackageInstallScope.SystemOrUnknown, _ => throw new InvalidOperationException(), }; } /// /// Converts PSProcessorArchitecture string value to ProcessorArchitecture. /// /// PSProcessorArchitecture to string value. /// ProcessorArchitecture. public static ProcessorArchitecture ToProcessorArchitecture(string value) { return value switch { "X86" => ProcessorArchitecture.X86, "Arm" => ProcessorArchitecture.Arm, "X64" => ProcessorArchitecture.X64, "Arm64" => ProcessorArchitecture.Arm64, _ => throw new InvalidOperationException(), }; } /// /// Converts PSPackageUninstallMode string value to PackageUninstallMode. /// /// PSPackageUninstallMode string value. /// PackageUninstallMode. public static PackageUninstallMode ToPackageUninstallMode(string value) { return value switch { "Default" => PackageUninstallMode.Default, "Silent" => PackageUninstallMode.Silent, "Interactive" => PackageUninstallMode.Interactive, _ => throw new InvalidOperationException(), }; } /// /// Converts PSPackageFieldMatchOption string value to PackageFieldMatchOption. /// /// PSPackageFieldMatchOption string value. /// PackageFieldMatchOption. public static PackageFieldMatchOption ToPackageFieldMatchOption(string value) { return value switch { "Equals" => PackageFieldMatchOption.Equals, "EqualsCaseInsensitive" => PackageFieldMatchOption.EqualsCaseInsensitive, "StartsWithCaseInsensitive" => PackageFieldMatchOption.StartsWithCaseInsensitive, "ContainsCaseInsensitive" => PackageFieldMatchOption.ContainsCaseInsensitive, _ => throw new InvalidOperationException(), }; } /// /// Converts PSPackageInstallerType string value to PackageInstallerType. /// /// PSPackageInstallerType string value. /// PackageInstallerType. public static PackageInstallerType ToPackageInstallerType(string value) { return value switch { "Unknown" => PackageInstallerType.Unknown, "Inno" => PackageInstallerType.Inno, "Wix" => PackageInstallerType.Wix, "Msi" => PackageInstallerType.Msi, "Nullsoft" => PackageInstallerType.Nullsoft, "Zip" => PackageInstallerType.Zip, "Msix" => PackageInstallerType.Msix, "Exe" => PackageInstallerType.Exe, "Burn" => PackageInstallerType.Burn, "MSStore" => PackageInstallerType.MSStore, "Portable" => PackageInstallerType.Portable, _ => throw new InvalidOperationException(), }; } /// /// Converts PSPackageRepairMode string value to PackageRepairMode. /// /// PSPackageRepairMode string value. /// PackageRepairMode. public static PackageRepairMode ToPackageRepairMode(string value) { return value switch { "Default" => PackageRepairMode.Default, "Silent" => PackageRepairMode.Silent, "Interactive" => PackageRepairMode.Interactive, _ => throw new InvalidOperationException(), }; } /// /// Converts PSWindowsPlatform string value to WindowsPlatform. /// /// PSWindowsPlatform string value. /// WindowsPlatform. public static WindowsPlatform ToWindowsPlatform(string value) { return value switch { "Default" => WindowsPlatform.Unknown, "Universal" => WindowsPlatform.Universal, "Desktop" => WindowsPlatform.Desktop, "IoT" => WindowsPlatform.IoT, "Team" => WindowsPlatform.Team, "Holographic" => WindowsPlatform.Holographic, _ => throw new InvalidOperationException(), }; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PackageDependency.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { /// /// A single package dependency. /// internal class PackageDependency { /// /// Gets or sets the name of the dependency. /// public string Name { get; set; } = string.Empty; /// /// Gets or sets the version of the dependency. /// public string Version { get; set; } = string.Empty; } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PackageManagerWrapper.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Windows.Foundation; /// /// Wrapper for PackageManager that handles rpc disconnections. /// The object is disconnected when the server is killed or on an update. /// internal sealed class PackageManagerWrapper { private static readonly Lazy Lazy = new (() => new PackageManagerWrapper()); private PackageManager packageManager = null!; private PackageManagerWrapper() { } /// /// Gets the instance object. /// public static PackageManagerWrapper Instance { get { return Lazy.Value; } } /// /// Wrapper for InstallPackageAsync. /// /// The package to install. /// The install options. /// An async operation with progress. public IAsyncOperationWithProgress InstallPackageAsync(CatalogPackage package, InstallOptions options) { return this.Execute( () => this.packageManager.InstallPackageAsync(package, options), false); } /// /// Wrapper for UpgradePackageAsync. /// /// The package to upgrade. /// The install options. /// An async operation with progress. public IAsyncOperationWithProgress UpgradePackageAsync(CatalogPackage package, InstallOptions options) { return this.Execute( () => this.packageManager.UpgradePackageAsync(package, options), false); } /// /// Wrapper for UninstallPackageAsync. /// /// The package to uninstall. /// The uninstall options. /// An async operation with progress. public IAsyncOperationWithProgress UninstallPackageAsync(CatalogPackage package, UninstallOptions options) { return this.Execute( () => this.packageManager.UninstallPackageAsync(package, options), false); } /// /// Wrapper for DownloadPackageAsync. /// /// The package to download. /// The download options. /// An async operation with progress. public IAsyncOperationWithProgress DownloadPackageAsync(CatalogPackage package, DownloadOptions options) { return this.Execute( () => this.packageManager.DownloadPackageAsync(package, options), false); } /// /// Wrapper for RepairPackagesAsync. /// /// The package to repair. /// The repair options. /// An async operation with progress. public IAsyncOperationWithProgress RepairPackageAsync(CatalogPackage package, RepairOptions options) { return this.Execute( () => this.packageManager.RepairPackageAsync(package, options), false); } /// /// Wrapper for GetPackageCatalogs. /// /// A list of PackageCatalogReferences. public IReadOnlyList GetPackageCatalogs() { return this.Execute( () => this.packageManager.GetPackageCatalogs(), true); } /// /// Wrapper for GetPackageCatalogByName. /// /// The name of the source. /// A PackageCatalogReference. public PackageCatalogReference GetPackageCatalogByName(string source) { return this.Execute( () => this.packageManager.GetPackageCatalogByName(source), true); } /// /// Wrapper for CreateCompositePackageCatalog. /// /// CreateCompositePackageCatalogOptions. /// A PackageCatalogReference. public PackageCatalogReference CreateCompositePackageCatalog(CreateCompositePackageCatalogOptions options) { return this.Execute( () => this.packageManager.CreateCompositePackageCatalog(options), false); } /// /// Gets the version of the package manager that is running. /// /// The version string. public string? GetVersion() { try { return this.Execute(() => this.packageManager.Version, true); } catch { return null; } } private TReturn Execute(Func func, bool canRetry) { if (Utilities.UsesInProcWinget && Utilities.ThreadIsSTA) { // If you failed here, then you didn't wrap your call in ManagementDeploymentCommand.Execute throw new SingleThreadedApartmentException(); } bool stopRetry = false; while (true) { if (this.packageManager == null) { this.packageManager = ManagementDeploymentFactory.Instance.CreatePackageManager(); } try { return func(); } catch (COMException ex) when (ex.HResult == ErrorCode.RpcServerUnavailable || ex.HResult == ErrorCode.RpcCallFailed) { this.packageManager = null!; if (stopRetry || !canRetry) { throw; } stopRetry = true; } } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/RepairOperationWithProgress.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Resources; using Windows.Foundation; /// /// Handler for Repair Operation with Progress. /// internal class RepairOperationWithProgress : OperationWithProgressBase { /// /// Initializes a new instance of the class. /// /// instance. /// Activity. public RepairOperationWithProgress(PowerShellCmdlet pwshCmdlet, string activity) : base(pwshCmdlet, activity) { } /// public override void Progress(IAsyncOperationWithProgress operation, RepairProgress progress) { ProgressRecord record = new (this.ActivityId, this.Activity, progress.State.ToString()) { RecordType = ProgressRecordType.Processing, }; record.StatusDescription = Resources.Repairing; record.PercentComplete = (int)(progress.RepairCompletionProgress * 100); this.PwshCmdlet.Write(StreamType.Progress, record); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempDirectory.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.IO; /// /// Creates a temporary directory in the user's temporary directory. /// internal class TempDirectory : IDisposable { private readonly bool cleanup; private bool disposed = false; /// /// Initializes a new instance of the class. /// /// Optional directory name. If null, creates a random directory name. /// Delete directory if already exists. Default true. /// Deletes directory at disposing time. Default true. public TempDirectory( string? directoryName = null, bool deleteIfExists = true, bool cleanup = true) { if (directoryName is null) { this.DirectoryName = Path.GetRandomFileName(); } else { this.DirectoryName = directoryName; } this.FullDirectoryPath = Path.Combine(Path.GetTempPath(), this.DirectoryName); if (deleteIfExists && Directory.Exists(this.FullDirectoryPath)) { Directory.Delete(this.FullDirectoryPath, true); } Directory.CreateDirectory(this.FullDirectoryPath); this.cleanup = cleanup; } /// /// Gets the directory name. /// public string DirectoryName { get; } /// /// Gets the full directory name. /// public string FullDirectoryPath { get; } /// /// IDisposable.Dispose . /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } /// /// Copies all contents of a directory into this directory. /// /// Source directory. public void CopyDirectory(string sourceDir) { this.CopyDirectory(sourceDir, this.FullDirectoryPath); } /// /// Protected disposed. /// /// Disposing. protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (this.cleanup && Directory.Exists(this.FullDirectoryPath)) { Directory.Delete(this.FullDirectoryPath, true); } this.disposed = true; } } private void CopyDirectory(string sourceDir, string destinationDir) { var dir = new DirectoryInfo(sourceDir); if (!dir.Exists) { throw new DirectoryNotFoundException(dir.FullName); } Directory.CreateDirectory(destinationDir); foreach (FileInfo file in dir.GetFiles()) { file.CopyTo(Path.Combine(destinationDir, file.Name)); } foreach (DirectoryInfo subDir in dir.GetDirectories()) { this.CopyDirectory(subDir.FullName, Path.Combine(destinationDir, subDir.Name)); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempFile.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.IO; /// /// Creates a temporary file in the user's temporary directory. /// internal class TempFile : IDisposable { private readonly bool cleanup; private bool disposed = false; /// /// Initializes a new instance of the class. /// /// Optional file name. If null, creates a random file name. /// Delete file if already exists. Default true. /// Optional content. If not null or empty, creates file and writes to it. /// Deletes file at disposing time. Default true. public TempFile( string? fileName = null, bool deleteIfExists = true, string? content = null, bool cleanup = true) { if (fileName is null) { this.FileName = Path.GetRandomFileName(); this.FullPath = Path.Combine(Path.GetTempPath(), this.FileName); } else { this.FileName = fileName; var randomDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(randomDir); this.FullPath = Path.Combine(randomDir, this.FileName); } if (deleteIfExists && File.Exists(this.FullPath)) { File.Delete(this.FullPath); } if (!string.IsNullOrWhiteSpace(content)) { this.CreateFile(content); } this.cleanup = cleanup; } /// /// Gets the file name. /// public string FileName { get; } /// /// Gets the full path. /// public string FullPath { get; } /// /// IDisposable.Dispose. /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } /// /// Creates the file. /// /// Content. public void CreateFile(string? content = null) { if (content is null) { using var fs = File.Create(this.FullPath); } else { File.WriteAllText(this.FullPath, content); } } /// /// Protected disposed. /// /// Disposing. protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (this.cleanup && File.Exists(this.FullPath)) { File.Delete(this.FullPath); } this.disposed = true; } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/UninstallOperationWithProgress.cs ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WinGetCLICommandBuilder.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System.Collections.Generic; using System.Text; /// /// Represents a builder for WinGet CLI commands. /// public class WinGetCLICommandBuilder { private readonly List commands; private readonly List parameters; /// /// Initializes a new instance of the class. /// /// The commands to initialize the builder with. public WinGetCLICommandBuilder(params string[] commands) { this.commands = new (commands); this.parameters = new (); } /// /// Gets the main command (e.g. [empty], settings, source). /// public string Command => string.Join(" ", this.commands); /// /// Gets the constructed parameters string. /// public string Parameters => string.Join(" ", this.parameters); /// /// Appends a switch to the command. /// /// The name of the switch to append. /// The current instance of . public WinGetCLICommandBuilder AppendSwitch(string switchName) { this.parameters.Add($"--{switchName}"); return this; } /// /// Appends a sub-command to the command. /// /// The sub-command to append. /// The current instance of . public WinGetCLICommandBuilder AppendSubCommand(string subCommand) { this.commands.Add(subCommand); return this; } /// /// Appends an option with its value to the command. /// /// The name of the option to append. /// The value of the option to append. /// The current instance of . public WinGetCLICommandBuilder AppendOption(string option, string? value) { if (value == null) { return this; } this.parameters.Add($"--{option} {this.Escape(value)}"); return this; } /// /// Converts the command builder to its string representation. /// /// The string representation of the command. public override string ToString() { var parametersString = this.Parameters; var commandString = this.Command; if (string.IsNullOrEmpty(commandString)) { return parametersString; } if (string.IsNullOrEmpty(parametersString)) { return commandString; } return $"{commandString} {parametersString}"; } /// /// Escapes a command-line argument according to Windows command-line parsing rules. /// References: /// - https://devblogs.microsoft.com/oldnewthing/20100917-00/?p=12833 /// - https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments. /// /// The argument to escape. /// The escaped argument. private string Escape(string arg) { if (string.IsNullOrEmpty(arg)) { return "\"\""; } var sb = new StringBuilder(arg.Length + 2); sb.Append('"'); int bs = 0; foreach (char c in arg) { if (c == '\\') { bs++; } else if (c == '"') { sb.Append('\\', (bs * 2) + 1); sb.Append('"'); bs = 0; } else { sb.Append('\\', bs); sb.Append(c); bs = 0; } } if (bs > 0) { sb.Append('\\', bs * 2); } sb.Append('"'); return sb.ToString(); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WinGetCLICommandResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using Microsoft.WinGet.Client.Engine.Exceptions; /// /// Winget cli command result. /// internal class WinGetCLICommandResult { /// /// Initializes a new instance of the class. /// /// Command. /// Parameters. /// Exit code. /// Standard output. /// Standard error. public WinGetCLICommandResult(string command, string? parameters, int exitCode, string stdOut, string stdErr) { this.Command = command; this.Parameters = parameters; this.ExitCode = exitCode; this.StdOut = stdOut; this.StdErr = stdErr; } /// /// Gets the command. /// public string Command { get; private set; } /// /// Gets the parameters. /// public string? Parameters { get; private set; } /// /// Gets the exit code. /// public int ExitCode { get; private set; } /// /// Gets the standard output. /// public string StdOut { get; private set; } /// /// Gets the standard error. /// public string StdErr { get; private set; } /// /// Verifies exit code. /// /// Optional exit code. public void VerifyExitCode(int exitCode = 0) { if (this.ExitCode != exitCode) { throw new WinGetCLIException( this.Command, this.Parameters, this.ExitCode, this.StdOut, this.StdErr); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WinGetVersion.cs ================================================ [File too large to display: 6.0 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WinRTHelpers.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.IO; using System.Runtime.InteropServices; /// /// Helper class for winrtact.dll calls. /// internal static class WinRTHelpers { #if POWERSHELL_WINDOWS private static readonly string ArchDependencyPath; static WinRTHelpers() { ArchDependencyPath = Path.Combine( Path.GetDirectoryName( Path.GetDirectoryName(typeof(WinRTHelpers).Assembly.Location)), "SharedDependencies", RuntimeInformation.ProcessArchitecture.ToString().ToLower()); } #endif /// /// Calls winrtact_Initialize. /// public static void Initialize() { #if POWERSHELL_WINDOWS SetDllDirectoryW(ArchDependencyPath); try { #endif InitializeUndockedRegFreeWinRT(); #if POWERSHELL_WINDOWS } finally { SetDllDirectoryW(null); } #endif } /// /// Calls WinGetServerManualActivation_CreateInstance. /// /// Class id. /// IID. /// Flags. /// Out object. /// Result of WinGetServerManualActivation_CreateInstance. public static int ManualActivation(Guid clsid, Guid iid, uint flags, out object instance) { #if POWERSHELL_WINDOWS SetDllDirectoryW(ArchDependencyPath); try { #endif return WinGetServerManualActivation_CreateInstance(clsid, iid, flags, out instance); #if POWERSHELL_WINDOWS } finally { SetDllDirectoryW(null); } #endif } [DllImport("winrtact.dll", EntryPoint = "winrtact_Initialize", ExactSpelling = true, PreserveSig = true)] private static extern void InitializeUndockedRegFreeWinRT(); [DllImport("winrtact.dll", EntryPoint = "WinGetServerManualActivation_CreateInstance", ExactSpelling = true, PreserveSig = true)] private static extern int WinGetServerManualActivation_CreateInstance( [In, MarshalAs(UnmanagedType.LPStruct)] Guid clsid, [In, MarshalAs(UnmanagedType.LPStruct)] Guid iid, uint flags, [Out, MarshalAs(UnmanagedType.IUnknown)] out object instance); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetDllDirectoryW([MarshalAs(UnmanagedType.LPWStr)] string? directory); } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WingetCLIWrapper.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System; using System.Diagnostics; using System.IO; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Common.Command; /// /// Calls winget directly. /// internal class WingetCLIWrapper { /// /// The file name to use in start info. /// private readonly string wingetPath; /// /// Initializes a new instance of the class. /// When app execution alias is disabled the path of the exe is /// in the package family name directory in the local app data windows app directory. If its enabled then there's /// link in the windows app data directory. To avoid checking if its enabled or not, just look in the package /// family name directory. /// For test, point to the wingetdev executable. /// /// Use full path or not. public WingetCLIWrapper(bool fullPath = true) { if (Utilities.ExecutingAsSystem) { throw new NotSupportedException(); } if (fullPath) { this.wingetPath = WinGetFullPath; } else { this.wingetPath = Constants.WinGetExe; } } /// /// Gets the full path of winget executable. /// public static string WinGetFullPath { get { return Path.Combine( Utilities.LocalDataWindowsAppPath, Constants.WingetPackageFamilyName, Constants.WinGetExe); } } /// /// Runs winget command with parameters. /// /// PowerShell cmdlet. /// The command builder. /// Time out. /// WinGetCommandResult. internal WinGetCLICommandResult RunCommand(PowerShellCmdlet pwshCmdlet, WinGetCLICommandBuilder builder, int timeOut = 60000) { var args = builder.ToString(); pwshCmdlet.Write(StreamType.Verbose, $"Running {this.wingetPath} with {args}"); Process p = new () { StartInfo = new (this.wingetPath, args) { UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, }, }; p.Start(); if (p.WaitForExit(timeOut)) { return new WinGetCLICommandResult( builder.Command, builder.Parameters, p.ExitCode, p.StandardOutput.ReadToEnd(), p.StandardError.ReadToEnd()); } throw new WinGetCLITimeoutException(builder.Command, builder.Parameters); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WingetDependencies.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.Helpers { using System.Collections.Generic; /// /// An object representation of the dependencies json. /// internal class WingetDependencies { /// /// Gets or sets a list of required package dependencies. /// public List Dependencies { get; set; } = new List(); } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj ================================================  10.0.26100.0 net8.0-windows$(TargetWindowsVersion) false net48 Debug;Release;ReleaseStatic false true $(SolutionDir)$(Platform)\$(Configuration)\ $(BuildOutputDirectory)$(MSBuildProjectName) $(CoreFramework);$(DesktopFramework) $(OutputPath)\Microsoft.WinGet.Client.Engine.xml 10.0.18362.0 enable None all runtime; build; native; contentfiles; analyzers; buildtransitive Content Always Content Always Content Always Content Always 1591 true true win 10 $(DefineConstants);POWERSHELL_WINDOWS True True Resources.resx ResXFileCodeGenerator Resources.Designer.cs Microsoft.WinGet.Resources Microsoft.Management.Deployment; Windows.Data.Text.TextSegmen; Windows.Devices.Geolocation; Windows.Foundation; Windows.Globalization.DayOfWee; Windows.Networking.Connectivity; Windows.Networking.DomainNameTyp; Windows.Networking.EndpointPai; Windows.Networking.IEndpointPai; Windows.Networking.HostNam; Windows.Networking.IHostNam; Windows.Security.Cryptography.Certificates; Windows.Storage; Windows.Storage.Provider.FileUpdateStatu; Windows.System.ProcessorArchitectur; Windows.System.Use; Windows.System.IUse; Windows.Foundation.PropertyType; Windows.Storage.Provider; $(TargetWindowsVersion) 10.0.17763.0 ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSCatalogPackage.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.PSObjects { using System.Linq; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Extensions; /// /// CatalogPackage wrapper object for displaying to PowerShell. /// public abstract class PSCatalogPackage { /// /// Initializes a new instance of the class. /// /// CatalogPackage COM object. internal PSCatalogPackage(Management.Deployment.CatalogPackage catalogPackage) { this.CatalogPackageCOM = catalogPackage; } /// /// Gets the name of the catalog package. /// public string Name { get { return this.CatalogPackageCOM.Name; } } /// /// Gets the id of the catalog package. /// public string Id { get { return this.CatalogPackageCOM.Id; } } /// /// Gets a value indicating whether an update is available. /// public bool IsUpdateAvailable { get { return this.CatalogPackageCOM.IsUpdateAvailable; } } /// /// Gets the source name of the catalog package. /// public string? Source { get { return this.CatalogPackageCOM.GetSourceName(); } } /// /// Gets list of strings representing the available versions. /// public string[] AvailableVersions { get { return this.AvailablePackageVersionIds.Select(i => i.Version).ToArray(); } } /// /// Gets the catalog package COM object. /// internal CatalogPackage CatalogPackageCOM { get; private set; } /// /// Gets a list of available package version ids for the package. /// private PackageVersionId[] AvailablePackageVersionIds { get { return this.CatalogPackageCOM.AvailableVersions.ToArray(); } } /// /// Checks the installed status of the catalog package. /// /// CheckInstalledStatus string. public string CheckInstalledStatus() { return this.CatalogPackageCOM.CheckInstalledStatus().Status.ToString(); } /// /// Gets the PackageVersionInfo PSObject that corresponds with the version string. /// /// Version string. /// PackageVersionInfo PSObject. /// Throws an exception if no package is found. public PSPackageVersionInfo GetPackageVersionInfo(string version) { // get specific version that matches PackageVersionId? packageVersionId = this.AvailablePackageVersionIds.FirstOrDefault(x => x.Version == version); if (packageVersionId != null) { return new PSPackageVersionInfo(this.CatalogPackageCOM.GetPackageVersionInfo(packageVersionId)); } else { throw new NoPackageFoundException(); } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSCompareResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.PSObjects { /// /// Must match Microsoft.Management.Deployment.CompareResult. /// public enum PSCompareResult { /// /// Unknown, /// Unknown, /// /// Lesser. /// Lesser, /// /// Equal, /// Equal, /// /// Greater, /// Greater, } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSDownloadResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.PSObjects { using System; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Extensions; /// /// PSDownloadResult. /// public sealed class PSDownloadResult { private readonly DownloadResult downloadResult; private readonly CatalogPackage catalogPackage; /// /// Initializes a new instance of the class. /// /// The download result COM object. /// The catalog package COM object. internal PSDownloadResult(DownloadResult downloadResult, CatalogPackage catalogPackage) { this.downloadResult = downloadResult; this.catalogPackage = catalogPackage; } /// /// Gets the id of the downloaded package. /// public string Id { get { return this.catalogPackage.Id; } } /// /// Gets the name of the downloaded package. /// public string Name { get { return this.catalogPackage.Name; } } /// /// Gets the source name of the downloaded package. /// public string? Source { get { return this.catalogPackage.GetSourceName(); } } /// /// Gets the correlation data of the downloaded result. /// public string CorrelationData { get { return this.downloadResult.CorrelationData; } } /// /// Gets the extended error code exception of the failed download result. /// public Exception ExtendedErrorCode { get { return this.downloadResult.ExtendedErrorCode; } } /// /// Gets the status of the download. /// public string Status { get { return this.downloadResult.Status.ToString(); } } /// /// If the download succeeded. /// /// True if installation succeeded. public bool Succeeded() { return this.downloadResult.Status == DownloadResultStatus.Ok; } /// /// Message with error information. /// /// Error message. public string ErrorMessage() { return $"DownloadStatus '{this.Status}' ExtendedError '{this.ExtendedErrorCode.HResult}'"; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSFoundCatalogPackage.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.PSObjects { using Microsoft.Management.Deployment; /// /// FoundCatalogPackage wrapper object for displaying to PowerShell. /// public sealed class PSFoundCatalogPackage : PSCatalogPackage { /// /// Initializes a new instance of the class. /// /// The catalog package COM object. internal PSFoundCatalogPackage(CatalogPackage catalogPackage) : base(catalogPackage) { } /// /// Gets the default install version of the catalog package. /// public string? Version { get { return this.CatalogPackageCOM.DefaultInstallVersion?.Version; } } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSInstallResult.cs ================================================ [File too large to display: 3.9 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSInstalledCatalogPackage.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.PSObjects { using System; using Microsoft.Management.Deployment; /// /// InstalledCatalogPackage wrapper object for displaying to PowerShell. /// public sealed class PSInstalledCatalogPackage : PSCatalogPackage { /// /// Initializes a new instance of the class. /// /// The catalog package COM object. internal PSInstalledCatalogPackage(CatalogPackage catalogPackage) : base(catalogPackage) { } /// /// Gets the installed version of the catalog package. /// public string InstalledVersion { get { return this.CatalogPackageCOM.InstalledVersion.Version; } } /// /// Compares versions. /// /// Version. /// PSCompareResult. public PSCompareResult CompareToVersion(string version) { return this.CatalogPackageCOM.InstalledVersion.CompareToVersion(version) switch { CompareResult.Unknown => PSCompareResult.Unknown, CompareResult.Lesser => PSCompareResult.Lesser, CompareResult.Equal => PSCompareResult.Equal, CompareResult.Greater => PSCompareResult.Greater, _ => throw new InvalidOperationException(), }; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSPackageVersionInfo.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.PSObjects { using System.Linq; using Microsoft.Management.Deployment; /// /// PackageVersionInfo wrapper object for displaying to PowerShell. /// public sealed class PSPackageVersionInfo { private PackageVersionInfo packageVersionInfo; /// /// Initializes a new instance of the class. /// /// PackageVersionInfo COM object. internal PSPackageVersionInfo(PackageVersionInfo packageVersionInfo) { this.packageVersionInfo = packageVersionInfo; } /// /// Gets the name of the package version info. /// public string DisplayName { get { return this.packageVersionInfo.DisplayName; } } /// /// Gets the id of the package version info. /// public string Id { get { return this.packageVersionInfo.Id; } } /// /// Gets the publisher of the package version info. /// public string Publisher { get { return this.packageVersionInfo.Publisher; } } /// /// Gets the channel of the package version info. /// public string Channel { get { return this.packageVersionInfo.Channel; } } /// /// Gets the list of package family names of the package version info. /// public string[] PackageFamilyNames { get { return this.packageVersionInfo.PackageFamilyNames.ToArray(); } } /// /// Gets the list of product codes of the package version info. /// public string[] ProductCodes { get { return this.packageVersionInfo.ProductCodes.ToArray(); } } /// /// Compares the version string with the package version info and returns the CompareResult. /// /// Version string. /// CompareResult string. public string CompareToVersion(string version) { return this.packageVersionInfo.CompareToVersion(version).ToString(); } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSRepairResult.cs ================================================ [File too large to display: 3.9 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSSourceResult.cs ================================================ [File too large to display: 2.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSUninstallResult.cs ================================================ // ----------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- namespace Microsoft.WinGet.Client.Engine.PSObjects { using System; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Extensions; /// /// UninstallResult wrapper object for displaying to PowerShell. /// public sealed class PSUninstallResult { private readonly UninstallResult uninstallResult; private readonly CatalogPackage catalogPackage; /// /// Initializes a new instance of the class. /// /// The uninstall result COM object. /// The catalog package COM object. internal PSUninstallResult(UninstallResult uninstallResult, CatalogPackage catalogPackage) { this.uninstallResult = uninstallResult; this.catalogPackage = catalogPackage; } /// /// Gets the id of the uninstalled package. /// public string Id { get { return this.catalogPackage.Id; } } /// /// Gets the name of the uninstalled package. /// public string Name { get { return this.catalogPackage.Name; } } /// /// Gets the source name of the uninstalled package. /// public string? Source { get { return this.catalogPackage.GetSourceName(); } } /// /// Gets the correlation data of the uninstall result. /// public string CorrelationData { get { return this.uninstallResult.CorrelationData; } } /// /// Gets the extended error code exception of the failed uninstall result. /// public Exception ExtendedErrorCode { get { return this.uninstallResult.ExtendedErrorCode; } } /// /// Gets a value indicating whether a reboot is required. /// public bool RebootRequired { get { return this.uninstallResult.RebootRequired; } } /// /// Gets the status of the uninstall. /// public string Status { get { return this.uninstallResult.Status.ToString(); } } /// /// Gets the error code of an uninstall. /// public uint UninstallerErrorCode { get { return this.uninstallResult.UninstallerErrorCode; } } /// /// If the uninstall succeeded. /// /// True if uninstall succeeded. public bool Succeeded() { return this.uninstallResult.Status == UninstallResultStatus.Ok; } /// /// Message with error information. /// /// Error message. public string ErrorMessage() { return $"UninstallStatus '{this.Status}' UninstallerErrorCode '{this.UninstallerErrorCode}' ExtendedError '{this.ExtendedErrorCode.HResult}'"; } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/AssemblyInfo.cs ================================================ [File too large to display: 741 B] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.Designer.cs ================================================ [File too large to display: 18.6 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.resx ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 No source matches the given value: {0} {Locked="{0}"} {0} - The name of the source that was not found. An error occurred while searching for packages: {0} {Locked="{0}"} {0} - A string representation of the error status returned by the catalog. Installing '{0}' {Locked="{0}"} {0} - The name of the package being installed. Uninstalling '{0}' {Locked="{0}"} {0} - The name of the package being uninstalled. Updating '{0}' {Locked="{0}"} {0} - The name of the package being updated. An error occurred while connecting to the catalog. No versions matched the given value: {0} {Locked="{0}"} {0} - The version string provided by the user. No packages matched the given input criteria. {0}, {1}, and {2} other packages matched the input criteria. Please refine the input. {Locked="{0}","{1}","{2}"} {0} - The first conflicting package as a string. {1} - The second conflicting package. {2} - The number of other packages that also matched the input criteria. Unable to execute winget command. Winget command '{0}' with parameters '{1}' failed with exit code '{2}'. {Locked="{0}","{1}","{2}"} {0} - The winget command executed. {1} - The parameters of the command. {2} - The exit code. User settings file is invalid. The App Execution Alias for the Windows Package Manager is disabled. The App Installer is not installed. The App Installer does not contain the Windows Package Manager. Windows Package Manager returned an unexcepted result. The App Installer did not automatically add the PATH environment variable. The Windows Package Manager requires Windows Version 1809 (October 2018 Update) or later. Winget command timed out: {0} {1} {Locked="{0}","{1}"} {0} - The winget command executed. {1} - The parameters of the command. The App Installer is not registered. The App Execution Alias for the Windows Package Manager is disabled. You should enable the App Execution Alias for the Windows Package Manager. Go to App execution aliases option in Apps & features Settings to enable it. The installed winget version doesn't match the expectation. Installer version '{0}' Expected version '{1}' {Locked="{0}","{1}"} {0} - The winget current installed winget version. {1} - The expected winget version. Single threaded apartment (STA) is not currently supported in this context; run PowerShell in Multi-threaded apartment mode (MTA). {Locked="STA","MTA"} This cmdlet is not supported in Windows PowerShell. No applicable license found. Try running with -AllUsers in administrator mode. {Locked="-AllUsers"} -AllUsers requires administrator mode. {Locked="-AllUsers"} Failed to repair winget. This cmdlet requires administrator privileges to execute. Debug parameter not supported Downloading Completed Uninstalling Cannot find asset {0} {Locked="{0}"} {0} - The asset name Exporting '{0}' {Locked="{0}"} {0} - The name of the package being exported. Repairing '{0}' {Locked="{0}"} {0} - The name of the package being repaired. Repairing The repair command for this package is not available in the Package Manifest. Please reach out to the package publisher for assistance. The installer technology in use does not match the version currently installed. The repair operation was unsuccessful, exiting with Repairer error code: {0}. {Locked="{0}"} {0} - The error code of the repairer. The current installer technology does not support repair. Please reach out to the package vendor for assistance. Repair operations involving administrator privileges are not permitted on packages installed within the user scope. An unexpected error happened while trying to repair the package. Error code:{0} {Locked="{0}"} {0} - The error code from the repair operation. ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration/Examples/Sample_InvokeConfiguration.ps1 ================================================ [File too large to display: 784 B] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration/Examples/Sample_StartConfiguration.ps1 ================================================ <# .SYNOPSIS Performs a WinGet Configuration asynchronously. Use Start-WinGetConfiguration to perform a WinGet Configuration. #> param ( [Parameter(Mandatory)] $configFile ) if (-not(Get-Module -Name Microsoft.WinGet.Configuration -ListAvailable)) { Install-Module Microsoft.WinGet.Configuration -AllowPrerelease } Import-Module Microsoft.WinGet.Configuration # Starts the configuration in the background $configJob = Get-WinGetConfiguration -File $configFile | Start-WinGetConfiguration # This will block until the configuration is completed. Or print the results if already done. Complete-WinGetConfiguration -ConfigurationJob $configJob ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration/ModuleFiles/Microsoft.WinGet.Configuration.psd1 ================================================ @{ RootModule = "Microsoft.WinGet.Configuration.Cmdlets.dll" ModuleVersion = '0.0.1' CompatiblePSEditions = 'Core' GUID = '79b6b07b-7be5-4673-9cd1-fcbe3d79ba82' Author = 'Microsoft Corporation' CompanyName = 'Microsoft Corporation' Copyright = '(c) Microsoft Corporation. All rights reserved.' Description = 'PowerShell Module for the Windows Package Manager Configuration.' PowerShellVersion = '7.4.6' FunctionsToExport = @() AliasesToExport = @('cmpwgc', 'cnwgc', 'ctwgcy', 'gwgc', 'gwgcd', 'iwgc', 'rwgch', 'sawgc', 'spwgc','twgc') CmdletsToExport = @( "Complete-WinGetConfiguration" "Get-WinGetConfiguration" "Get-WinGetConfigurationDetails" "Invoke-WinGetConfiguration" "Start-WinGetConfiguration" "Test-WinGetConfiguration" "Confirm-WinGetConfiguration" "Stop-WinGetConfiguration" "Remove-WinGetConfigurationHistory" "ConvertTo-WinGetConfigurationYaml" ) PrivateData = @{ PSData = @{ Tags = @( 'WindowsPackageManager', 'WinGet' ) ProjectUri = 'https://github.com/microsoft/winget-cli' IconUri = 'https://aka.ms/winget-icon' Prerelease = 'alpha' } } } ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration/README.md ================================================ [File too large to display: 3.6 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/Common/OpenConfiguration.cs ================================================ [File too large to display: 2.9 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/CompleteWinGetConfigurationCmdlet.cs ================================================ [File too large to display: 2.0 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/ConfirmWinGetConfigurationCmdlet.cs ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/ConvertToWinGetConfigurationYamlCmdlet.cs ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/GetWinGetConfigurationCmdlet.cs ================================================ [File too large to display: 2.2 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/GetWinGetConfigurationDetailsCmdlet.cs ================================================ [File too large to display: 1.9 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/InvokeWinGetConfigurationCmdlet.cs ================================================ [File too large to display: 2.6 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/RemoveWinGetConfigurationHistoryCmdlet.cs ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/StartWinGetConfigurationCmdlet.cs ================================================ [File too large to display: 2.2 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/StopWinGetConfigurationCmdlet.cs ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Cmdlets/TestWinGetConfigurationCmdlet.cs ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Helpers/Constants.cs ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Helpers/Utilities.cs ================================================ [File too large to display: 2.6 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Microsoft.WinGet.Configuration.Cmdlets.csproj ================================================ [File too large to display: 4.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Properties/AssemblyInfo.cs ================================================ [File too large to display: 620 B] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Cmdlets/Resolver/ModuleInit.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/ConfigurationCommand.cs ================================================ [File too large to display: 29.6 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/ApplyConfigurationException.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/ErrorCodes.cs ================================================ [File too large to display: 3.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/ErrorRecordErrorId.cs ================================================ [File too large to display: 672 B] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/GetDetailsException.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/OpenConfigurationSetException.cs ================================================ [File too large to display: 2.8 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Extensions/ValueSetExtensions.cs ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ApplyConfigurationSetProgressOutput.cs ================================================ [File too large to display: 3.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationSetProgressOutputBase.cs ================================================ [File too large to display: 4.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationUnitInformation.cs ================================================ [File too large to display: 11.9 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/GetConfigurationSetDetailsProgressOutput.cs ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/OpenConfigurationParameters.cs ================================================ [File too large to display: 7.9 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/TestConfigurationSetProgressOutput.cs ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/Utilities.cs ================================================ [File too large to display: 5.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSApplyConfigurationSetResult.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSApplyConfigurationUnitResult.cs ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationJob.cs ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationProcessor.cs ================================================ [File too large to display: 3.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationSet.cs ================================================ [File too large to display: 4.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationTestResult.cs ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationUnitState.cs ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSGetConfigurationDetailsResult.cs ================================================ [File too large to display: 1.9 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSTestConfigurationSetResult.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSTestConfigurationUnitResult.cs ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSUnitResult.cs ================================================ [File too large to display: 6.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSValidateConfigurationSetResult.cs ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSValidateConfigurationUnitResult.cs ================================================ [File too large to display: 1.0 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Properties/AssemblyInfo.cs ================================================ [File too large to display: 620 B] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Resources/Resources.Designer.cs ================================================ [File too large to display: 26.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.Configuration.Engine/Resources/Resources.resx ================================================ [File too large to display: 18.8 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psd1 ================================================ [File too large to display: 5.0 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psm1 ================================================ [File too large to display: 17.7 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/Exceptions/ErrorCodes.cs ================================================ [File too large to display: 845 B] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/Exceptions/GroupPolicyException.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/Extensions/EnumPolicyExtension.cs ================================================ [File too large to display: 4.4 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/Microsoft.WinGet.SharedLib.csproj ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/PolicySettings/Enums.cs ================================================ [File too large to display: 3.0 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/PolicySettings/GroupPolicy.cs ================================================ [File too large to display: 4.6 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/PolicySettings/TogglePolicy.cs ================================================ [File too large to display: 7.6 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/Resources/GroupPolicyResource.Designer.cs ================================================ [File too large to display: 9.7 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.SharedLib/Resources/GroupPolicyResource.resx ================================================ [File too large to display: 8.5 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.UnitTests/GitHubClientTests.cs ================================================ [File too large to display: 4.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.UnitTests/Microsoft.WinGet.UnitTests.csproj ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/PowerShell/Microsoft.WinGet.UnitTests/WinGetCLICommandBuilderTests.cs ================================================ [File too large to display: 7.3 KB] ================================================ FILE: src/PowerShell/scripts/Execute-WinGetTests.ps1 ================================================ [File too large to display: 4.2 KB] ================================================ FILE: src/PowerShell/scripts/Initialize-LocalWinGetModules.ps1 ================================================ [File too large to display: 8.5 KB] ================================================ FILE: src/PowerShell/scripts/samples/WinGetAdminSettingsResourceSample.ps1 ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/PowerShell/scripts/samples/WinGetPackageManagerSample.ps1 ================================================ [File too large to display: 3.5 KB] ================================================ FILE: src/PowerShell/scripts/samples/WinGetPackageResourceSample.ps1 ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/PowerShell/scripts/samples/WinGetSourceSample.ps1 ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/PowerShell/scripts/samples/WinGetUserSettingsPackageManagerSample.ps1 ================================================ [File too large to display: 1.9 KB] ================================================ FILE: src/PowerShell/tests/Microsoft.WinGet.Client.Tests.ps1 ================================================ [File too large to display: 38.9 KB] ================================================ FILE: src/PowerShell/tests/Microsoft.WinGet.Configuration.Tests.ps1 ================================================ [File too large to display: 40.8 KB] ================================================ FILE: src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1 ================================================ [File too large to display: 10.7 KB] ================================================ FILE: src/PowerShell/tests/RunTests.ps1 ================================================ [File too large to display: 2.8 KB] ================================================ FILE: src/PureLib/PureLib.vcxitems ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/PureLib/pure/AUTHORS ================================================ [File too large to display: 134 B] ================================================ FILE: src/PureLib/pure/LICENSE ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/PureLib/pure/README.md ================================================ [File too large to display: 9.6 KB] ================================================ FILE: src/PureLib/pure/make-errors.js ================================================ [File too large to display: 14.7 KB] ================================================ FILE: src/PureLib/pure/make-signatures.js ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/PureLib/pure/make-tests.js ================================================ [File too large to display: 3.2 KB] ================================================ FILE: src/PureLib/pure/package.json.backup ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/PureLib/pure/pure ================================================ [File too large to display: 471 B] ================================================ FILE: src/PureLib/pure/pure.h ================================================ [File too large to display: 58.8 KB] ================================================ FILE: src/PureLib/pure/pure_constants.h ================================================ [File too large to display: 2.4 KB] ================================================ FILE: src/PureLib/pure/pure_errors.h ================================================ [File too large to display: 20.2 KB] ================================================ FILE: src/PureLib/pure/pure_routines.h ================================================ [File too large to display: 5.8 KB] ================================================ FILE: src/PureLib/pure/pure_signatures.h ================================================ [File too large to display: 1.4 KB] ================================================ FILE: src/PureLib/pure/test.js ================================================ [File too large to display: 732 B] ================================================ FILE: src/PureLib/readme.md ================================================ [File too large to display: 884 B] ================================================ FILE: src/SfsClient/SfsClient.vcxproj ================================================ [File too large to display: 16.4 KB] ================================================ FILE: src/SfsClient/SfsClient.vcxproj.filters ================================================ [File too large to display: 6.7 KB] ================================================ FILE: src/SfsClient/readme.md ================================================ [File too large to display: 953 B] ================================================ FILE: src/SfsClient/sfs-client/.clang-format ================================================ [File too large to display: 1.0 KB] ================================================ FILE: src/SfsClient/sfs-client/.cmake-format.json ================================================ [File too large to display: 142 B] ================================================ FILE: src/SfsClient/sfs-client/.github/CODEOWNERS ================================================ [File too large to display: 136 B] ================================================ FILE: src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/bug_report.md ================================================ [File too large to display: 457 B] ================================================ FILE: src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/feature_request.md ================================================ [File too large to display: 595 B] ================================================ FILE: src/SfsClient/sfs-client/.github/dependabot.yml ================================================ [File too large to display: 707 B] ================================================ FILE: src/SfsClient/sfs-client/.github/pull_request_template.md ================================================ [File too large to display: 597 B] ================================================ FILE: src/SfsClient/sfs-client/.github/workflows/initialize-codeql/action.yml ================================================ [File too large to display: 250 B] ================================================ FILE: src/SfsClient/sfs-client/.github/workflows/install-winget/action.yml ================================================ [File too large to display: 2.3 KB] ================================================ FILE: src/SfsClient/sfs-client/.github/workflows/main-build-ubuntu.yml ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/SfsClient/sfs-client/.github/workflows/main-build-windows.yml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/SfsClient/sfs-client/.github/workflows/pr.yml ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/SfsClient/sfs-client/.gitignore ================================================ [File too large to display: 399 B] ================================================ FILE: src/SfsClient/sfs-client/API.md ================================================ [File too large to display: 2.8 KB] ================================================ FILE: src/SfsClient/sfs-client/CMakeLists.txt ================================================ [File too large to display: 2.6 KB] ================================================ FILE: src/SfsClient/sfs-client/CODE_OF_CONDUCT.md ================================================ [File too large to display: 444 B] ================================================ FILE: src/SfsClient/sfs-client/DEVELOPMENT.md ================================================ [File too large to display: 600 B] ================================================ FILE: src/SfsClient/sfs-client/LICENSE ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/SfsClient/sfs-client/NOTICE.md ================================================ [File too large to display: 17.7 KB] ================================================ FILE: src/SfsClient/sfs-client/README.md ================================================ [File too large to display: 8.4 KB] ================================================ FILE: src/SfsClient/sfs-client/SECURITY.md ================================================ [File too large to display: 2.6 KB] ================================================ FILE: src/SfsClient/sfs-client/SUPPORT.md ================================================ [File too large to display: 383 B] ================================================ FILE: src/SfsClient/sfs-client/TEST.md ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/SfsClient/sfs-client/cgmanifest.json ================================================ [File too large to display: 2.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/CMakeLists.txt ================================================ [File too large to display: 5.0 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/AppContent.h ================================================ [File too large to display: 2.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/AppFile.h ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/ApplicabilityDetails.h ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/ClientConfig.h ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/Content.h ================================================ [File too large to display: 2.0 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/ContentId.h ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/File.h ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/Logging.h ================================================ [File too large to display: 616 B] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/RequestParams.h ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/Result.h ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/include/sfsclient/SFSClient.h ================================================ [File too large to display: 2.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/AppContent.cpp ================================================ [File too large to display: 2.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/AppFile.cpp ================================================ [File too large to display: 1.9 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/ApplicabilityDetails.cpp ================================================ [File too large to display: 1.0 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/Content.cpp ================================================ [File too large to display: 2.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/ContentId.cpp ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/File.cpp ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/Logging.cpp ================================================ [File too large to display: 478 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/Result.cpp ================================================ [File too large to display: 2.8 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/SFSClient.cpp ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/ContentUtil.cpp ================================================ [File too large to display: 4.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/ContentUtil.h ================================================ [File too large to display: 2.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/CorrelationVector.cpp ================================================ [File too large to display: 1.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/CorrelationVector.h ================================================ [File too large to display: 827 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/Env.cpp ================================================ [File too large to display: 1.9 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/Env.h ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/ErrorHandling.cpp ================================================ [File too large to display: 2.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/ErrorHandling.h ================================================ [File too large to display: 6.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/OSInfo.cpp ================================================ [File too large to display: 1.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/OSInfo.h ================================================ [File too large to display: 249 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/ReportingHandler.cpp ================================================ [File too large to display: 906 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/ReportingHandler.h ================================================ [File too large to display: 3.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/SFSClientImpl.cpp ================================================ [File too large to display: 16.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/SFSClientImpl.h ================================================ [File too large to display: 4.1 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/SFSClientInterface.h ================================================ [File too large to display: 3.6 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/SFSException.cpp ================================================ [File too large to display: 623 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/SFSException.h ================================================ [File too large to display: 620 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.cpp ================================================ [File too large to display: 2.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.h ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/TestOverride.cpp ================================================ [File too large to display: 1.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/TestOverride.h ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/UrlBuilder.cpp ================================================ [File too large to display: 5.8 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/UrlBuilder.h ================================================ [File too large to display: 4.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/Util.cpp ================================================ [File too large to display: 548 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/Util.h ================================================ [File too large to display: 306 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/Connection.cpp ================================================ [File too large to display: 516 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/Connection.h ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.cpp ================================================ [File too large to display: 415 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.h ================================================ [File too large to display: 727 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.cpp ================================================ [File too large to display: 329 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.h ================================================ [File too large to display: 661 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.cpp ================================================ [File too large to display: 14.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.h ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.cpp ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.h ================================================ [File too large to display: 545 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.cpp ================================================ [File too large to display: 978 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.h ================================================ [File too large to display: 344 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.cpp ================================================ [File too large to display: 521 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.h ================================================ [File too large to display: 546 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.cpp ================================================ [File too large to display: 524 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.h ================================================ [File too large to display: 548 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/entity/ContentType.cpp ================================================ [File too large to display: 384 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/entity/ContentType.h ================================================ [File too large to display: 270 B] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/entity/FileEntity.cpp ================================================ [File too large to display: 10.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/entity/FileEntity.h ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.cpp ================================================ [File too large to display: 6.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.h ================================================ [File too large to display: 1.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/CMakeLists.txt ================================================ [File too large to display: 2.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/functional/SFSClientTests.cpp ================================================ [File too large to display: 16.9 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/functional/details/CurlConnectionTests.cpp ================================================ [File too large to display: 23.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/functional/details/SFSClientImplTests.cpp ================================================ [File too large to display: 14.4 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/mock/MockWebServer.cpp ================================================ [File too large to display: 25.0 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/mock/MockWebServer.h ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/mock/ProxyServer.cpp ================================================ [File too large to display: 2.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/mock/ProxyServer.h ================================================ [File too large to display: 633 B] ================================================ FILE: src/SfsClient/sfs-client/client/tests/mock/ServerCommon.cpp ================================================ [File too large to display: 3.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/mock/ServerCommon.h ================================================ [File too large to display: 2.0 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/AppContentTests.cpp ================================================ [File too large to display: 10.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/AppFileTests.cpp ================================================ [File too large to display: 5.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/ApplicabilityDetailsTests.cpp ================================================ [File too large to display: 2.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/ContentIdTests.cpp ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/ContentTests.cpp ================================================ [File too large to display: 6.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/FileTests.cpp ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/ResultTests.cpp ================================================ [File too large to display: 2.0 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/SFSClientTests.cpp ================================================ [File too large to display: 11.9 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionManagerTests.cpp ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionTests.cpp ================================================ [File too large to display: 3.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/EnvTests.cpp ================================================ [File too large to display: 3.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/ErrorHandlingTests.cpp ================================================ [File too large to display: 7.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/ReportingHandlerTests.cpp ================================================ [File too large to display: 5.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/SFSClientImplTests.cpp ================================================ [File too large to display: 15.7 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/SFSUrlBuilderTests.cpp ================================================ [File too large to display: 4.6 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/TestOverrideTests.cpp ================================================ [File too large to display: 4.3 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/UrlBuilderTests.cpp ================================================ [File too large to display: 5.9 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/UtilTests.cpp ================================================ [File too large to display: 824 B] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/entity/FileEntityTests.cpp ================================================ [File too large to display: 29.8 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/unit/details/entity/VersionEntityTests.cpp ================================================ [File too large to display: 14.2 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.cpp ================================================ [File too large to display: 1.9 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.h ================================================ [File too large to display: 2.0 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/util/TestHelper.cpp ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/SfsClient/sfs-client/client/tests/util/TestHelper.h ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/SfsClient/sfs-client/cmake/SFSOptions.cmake ================================================ [File too large to display: 535 B] ================================================ FILE: src/SfsClient/sfs-client/cmake/sfsclient-config.cmake.in ================================================ [File too large to display: 394 B] ================================================ FILE: src/SfsClient/sfs-client/pre-commit-wrapper.sh ================================================ [File too large to display: 585 B] ================================================ FILE: src/SfsClient/sfs-client/pre-commit.sh ================================================ [File too large to display: 5.8 KB] ================================================ FILE: src/SfsClient/sfs-client/samples/CMakeLists.txt ================================================ [File too large to display: 142 B] ================================================ FILE: src/SfsClient/sfs-client/samples/README.md ================================================ [File too large to display: 464 B] ================================================ FILE: src/SfsClient/sfs-client/samples/integration-do-client/CMakeLists.txt ================================================ [File too large to display: 2.1 KB] ================================================ FILE: src/SfsClient/sfs-client/samples/integration-do-client/IntegrationDOClient.cpp ================================================ [File too large to display: 9.8 KB] ================================================ FILE: src/SfsClient/sfs-client/samples/tool/CMakeLists.txt ================================================ [File too large to display: 671 B] ================================================ FILE: src/SfsClient/sfs-client/samples/tool/SFSClientTool.cpp ================================================ [File too large to display: 16.3 KB] ================================================ FILE: src/SfsClient/sfs-client/scripts/Build.ps1 ================================================ [File too large to display: 3.3 KB] ================================================ FILE: src/SfsClient/sfs-client/scripts/Setup.ps1 ================================================ [File too large to display: 6.2 KB] ================================================ FILE: src/SfsClient/sfs-client/scripts/Test.ps1 ================================================ [File too large to display: 627 B] ================================================ FILE: src/SfsClient/sfs-client/scripts/build.sh ================================================ [File too large to display: 4.5 KB] ================================================ FILE: src/SfsClient/sfs-client/scripts/check-format.py ================================================ [File too large to display: 2.1 KB] ================================================ FILE: src/SfsClient/sfs-client/scripts/pip.requirements.txt ================================================ [File too large to display: 42 B] ================================================ FILE: src/SfsClient/sfs-client/scripts/setup.sh ================================================ [File too large to display: 5.0 KB] ================================================ FILE: src/SfsClient/sfs-client/scripts/test.sh ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/portfile.cmake ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/usage ================================================ [File too large to display: 157 B] ================================================ FILE: src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/vcpkg.json ================================================ [File too large to display: 702 B] ================================================ FILE: src/SfsClient/sfs-client/vcpkg-custom-triplets/x64-windows-static-custom.cmake ================================================ [File too large to display: 168 B] ================================================ FILE: src/SfsClient/sfs-client/vcpkg.json ================================================ [File too large to display: 1.4 KB] ================================================ FILE: src/Update-VcxprojNugetPackageVersions.ps1 ================================================ [File too large to display: 11.4 KB] ================================================ FILE: src/VcpkgCustomTriplets/arm64-release-static.cmake ================================================ [File too large to display: 151 B] ================================================ FILE: src/VcpkgCustomTriplets/arm64-release.cmake ================================================ [File too large to display: 152 B] ================================================ FILE: src/VcpkgCustomTriplets/arm64.cmake ================================================ [File too large to display: 121 B] ================================================ FILE: src/VcpkgCustomTriplets/common.cmake ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/VcpkgCustomTriplets/fuzzing.cmake ================================================ [File too large to display: 462 B] ================================================ FILE: src/VcpkgCustomTriplets/x64-fuzzing.cmake ================================================ [File too large to display: 113 B] ================================================ FILE: src/VcpkgCustomTriplets/x64-release-static.cmake ================================================ [File too large to display: 149 B] ================================================ FILE: src/VcpkgCustomTriplets/x64-release.cmake ================================================ [File too large to display: 150 B] ================================================ FILE: src/VcpkgCustomTriplets/x64.cmake ================================================ [File too large to display: 119 B] ================================================ FILE: src/VcpkgCustomTriplets/x86-fuzzing.cmake ================================================ [File too large to display: 113 B] ================================================ FILE: src/VcpkgCustomTriplets/x86-release-static.cmake ================================================ [File too large to display: 149 B] ================================================ FILE: src/VcpkgCustomTriplets/x86-release.cmake ================================================ [File too large to display: 150 B] ================================================ FILE: src/VcpkgCustomTriplets/x86.cmake ================================================ [File too large to display: 119 B] ================================================ FILE: src/VcpkgPortOverlay/CreatePortOverlay.ps1 ================================================ [File too large to display: 10.0 KB] ================================================ FILE: src/VcpkgPortOverlay/README.md ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch ================================================ [File too large to display: 9.5 KB] ================================================ FILE: src/VcpkgPortOverlay/cpprestsdk/fix-find-openssl.patch ================================================ [File too large to display: 918 B] ================================================ FILE: src/VcpkgPortOverlay/cpprestsdk/fix-uwp.patch ================================================ [File too large to display: 975 B] ================================================ FILE: src/VcpkgPortOverlay/cpprestsdk/fix_narrowing.patch ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/VcpkgPortOverlay/cpprestsdk/portfile.cmake ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/VcpkgPortOverlay/cpprestsdk/test.patch ================================================ [File too large to display: 871 B] ================================================ FILE: src/VcpkgPortOverlay/cpprestsdk/vcpkg.json ================================================ [File too large to display: 2.4 KB] ================================================ FILE: src/VcpkgPortOverlay/detours/find-jmp-bounds-arm64.patch ================================================ [File too large to display: 830 B] ================================================ FILE: src/VcpkgPortOverlay/detours/portfile.cmake ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/VcpkgPortOverlay/detours/usage ================================================ [File too large to display: 273 B] ================================================ FILE: src/VcpkgPortOverlay/detours/vcpkg.json ================================================ [File too large to display: 278 B] ================================================ FILE: src/VcpkgPortOverlay/libyaml/export-pkgconfig.patch ================================================ [File too large to display: 587 B] ================================================ FILE: src/VcpkgPortOverlay/libyaml/fix-POSIX_name.patch ================================================ [File too large to display: 296 B] ================================================ FILE: src/VcpkgPortOverlay/libyaml/portfile.cmake ================================================ [File too large to display: 951 B] ================================================ FILE: src/VcpkgPortOverlay/libyaml/vcpkg.json ================================================ [File too large to display: 332 B] ================================================ FILE: src/WinGetMCPServer/Exceptions/ToolResponseException.cs ================================================ [File too large to display: 784 B] ================================================ FILE: src/WinGetMCPServer/Extensions/PackageListExtensions.cs ================================================ [File too large to display: 3.4 KB] ================================================ FILE: src/WinGetMCPServer/Program.cs ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/WinGetMCPServer/Response/FindPackageResult.cs ================================================ [File too large to display: 864 B] ================================================ FILE: src/WinGetMCPServer/Response/InstallOperationResult.cs ================================================ [File too large to display: 795 B] ================================================ FILE: src/WinGetMCPServer/Response/PackageIdentityErrorResult.cs ================================================ [File too large to display: 833 B] ================================================ FILE: src/WinGetMCPServer/Response/PackageResponse.cs ================================================ [File too large to display: 7.4 KB] ================================================ FILE: src/WinGetMCPServer/Response/ToolResponse.cs ================================================ [File too large to display: 3.2 KB] ================================================ FILE: src/WinGetMCPServer/ServerConnection.cs ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/WinGetMCPServer/WinGetMCPServer.csproj ================================================ [File too large to display: 2.6 KB] ================================================ FILE: src/WinGetMCPServer/WingetPackageTools.cs ================================================ [File too large to display: 11.8 KB] ================================================ FILE: src/WinGetSchemas/PackagesSchema.h ================================================ [File too large to display: 230 B] ================================================ FILE: src/WinGetSchemas/WinGetSchemas.rc ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/WinGetSchemas/WinGetSchemas.vcxitems ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/WinGetSchemas/WinGetSchemas.vcxitems.filters ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/WinGetSchemas/resource.h ================================================ [File too large to display: 406 B] ================================================ FILE: src/WinGetServer/PropertySheet.props ================================================ [File too large to display: 600 B] ================================================ FILE: src/WinGetServer/Utils.cpp ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/WinGetServer/Utils.h ================================================ [File too large to display: 371 B] ================================================ FILE: src/WinGetServer/WinGetServer.exe.manifest ================================================ [File too large to display: 143 B] ================================================ FILE: src/WinGetServer/WinGetServer.idl ================================================ [File too large to display: 428 B] ================================================ FILE: src/WinGetServer/WinGetServer.vcxproj ================================================ [File too large to display: 9.5 KB] ================================================ FILE: src/WinGetServer/WinGetServer.vcxproj.filters ================================================ [File too large to display: 2.1 KB] ================================================ FILE: src/WinGetServer/WinGetServerManualActivation_Client.cpp ================================================ [File too large to display: 8.4 KB] ================================================ FILE: src/WinGetServer/WinGetServerManualActivation_Client.h ================================================ [File too large to display: 300 B] ================================================ FILE: src/WinGetServer/WinMain.cpp ================================================ [File too large to display: 9.4 KB] ================================================ FILE: src/WinGetServer/packages.config ================================================ [File too large to display: 174 B] ================================================ FILE: src/WinGetServer/resource.h ================================================ [File too large to display: 455 B] ================================================ FILE: src/WinGetSourceCreator/Helpers.cs ================================================ [File too large to display: 7.2 KB] ================================================ FILE: src/WinGetSourceCreator/ManifestTokens.cs ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/WinGetSourceCreator/Model/DynamicInstaller.cs ================================================ [File too large to display: 2.6 KB] ================================================ FILE: src/WinGetSourceCreator/Model/Installer.cs ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/WinGetSourceCreator/Model/InstallerType.cs ================================================ [File too large to display: 239 B] ================================================ FILE: src/WinGetSourceCreator/Model/LocalInstaller.cs ================================================ [File too large to display: 738 B] ================================================ FILE: src/WinGetSourceCreator/Model/LocalSource.cs ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/WinGetSourceCreator/Model/Signature.cs ================================================ [File too large to display: 694 B] ================================================ FILE: src/WinGetSourceCreator/Model/SourceInstaller.cs ================================================ [File too large to display: 1.4 KB] ================================================ FILE: src/WinGetSourceCreator/WinGetLocalSource.cs ================================================ [File too large to display: 10.4 KB] ================================================ FILE: src/WinGetSourceCreator/WinGetSourceCreator.csproj ================================================ [File too large to display: 625 B] ================================================ FILE: src/WinGetTestCommon/WinGetServerInstance.cs ================================================ [File too large to display: 7.5 KB] ================================================ FILE: src/WinGetTestCommon/WinGetTestCommon.csproj ================================================ [File too large to display: 342 B] ================================================ FILE: src/WinGetTestCommon/WindowMessage.cs ================================================ [File too large to display: 849 B] ================================================ FILE: src/WinGetUtil/Exports.cpp ================================================ [File too large to display: 23.5 KB] ================================================ FILE: src/WinGetUtil/PropertySheet.props ================================================ [File too large to display: 605 B] ================================================ FILE: src/WinGetUtil/Source.def ================================================ [File too large to display: 809 B] ================================================ FILE: src/WinGetUtil/WinGetUtil.h ================================================ [File too large to display: 12.2 KB] ================================================ FILE: src/WinGetUtil/WinGetUtil.vcxproj ================================================ [File too large to display: 22.4 KB] ================================================ FILE: src/WinGetUtil/WinGetUtil.vcxproj.filters ================================================ [File too large to display: 1.4 KB] ================================================ FILE: src/WinGetUtil/packages.config ================================================ [File too large to display: 269 B] ================================================ FILE: src/WinGetUtil/pch.cpp ================================================ [File too large to display: 95 B] ================================================ FILE: src/WinGetUtil/pch.h ================================================ [File too large to display: 495 B] ================================================ FILE: src/WinGetUtilInterop/Api/WinGetFactory.cs ================================================ [File too large to display: 8.9 KB] ================================================ FILE: src/WinGetUtilInterop/Api/WinGetInstallerMetadata.cs ================================================ [File too large to display: 3.7 KB] ================================================ FILE: src/WinGetUtilInterop/Api/WinGetLogging.cs ================================================ [File too large to display: 2.3 KB] ================================================ FILE: src/WinGetUtilInterop/Api/WinGetManifest.cs ================================================ [File too large to display: 4.2 KB] ================================================ FILE: src/WinGetUtilInterop/Api/WinGetSQLiteIndex.cs ================================================ [File too large to display: 11.3 KB] ================================================ FILE: src/WinGetUtilInterop/Api/WinGetUtilities.cs ================================================ [File too large to display: 2.4 KB] ================================================ FILE: src/WinGetUtilInterop/Build/Microsoft.WindowsPackageManager.Utils.targets ================================================ [File too large to display: 410 B] ================================================ FILE: src/WinGetUtilInterop/Common/Constants.cs ================================================ [File too large to display: 592 B] ================================================ FILE: src/WinGetUtilInterop/Common/CreateManifestResult.cs ================================================ [File too large to display: 2.3 KB] ================================================ FILE: src/WinGetUtilInterop/Common/Enums.cs ================================================ [File too large to display: 5.9 KB] ================================================ FILE: src/WinGetUtilInterop/Common/Helpers.cs ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/WinGetUtilInterop/Common/ManifestValidationResult.cs ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/WinGetUtilInterop/Exceptions/WinGetDownloadException.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/WinGetUtilInterop/Exceptions/WinGetInstallerMetadataException.cs ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/WinGetUtilInterop/Exceptions/WinGetLoggingException.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/WinGetUtilInterop/Exceptions/WinGetManifestException.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/WinGetUtilInterop/Exceptions/WinGetSQLiteIndexException.cs ================================================ [File too large to display: 1.7 KB] ================================================ FILE: src/WinGetUtilInterop/Interfaces/IWinGetFactory.cs ================================================ [File too large to display: 2.3 KB] ================================================ FILE: src/WinGetUtilInterop/Interfaces/IWinGetInstallerMetadata.cs ================================================ [File too large to display: 795 B] ================================================ FILE: src/WinGetUtilInterop/Interfaces/IWinGetLogging.cs ================================================ [File too large to display: 520 B] ================================================ FILE: src/WinGetUtilInterop/Interfaces/IWinGetManifest.cs ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/WinGetUtilInterop/Interfaces/IWinGetSQLiteIndex.cs ================================================ [File too large to display: 3.9 KB] ================================================ FILE: src/WinGetUtilInterop/Interfaces/IWinGetUtilities.cs ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/ManifestVersion.cs ================================================ [File too large to display: 2.2 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/Preview/InstallerSwitches.cs ================================================ [File too large to display: 4.5 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/Preview/Manifest.cs ================================================ [File too large to display: 9.4 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/Preview/ManifestInstaller.cs ================================================ [File too large to display: 4.1 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/Preview/ManifestLocalization.cs ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerArpEntry.cs ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerAuthentication.cs ================================================ [File too large to display: 883 B] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerDependency.cs ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerExpectedReturnCode.cs ================================================ [File too large to display: 1009 B] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerInstallationMetadata.cs ================================================ [File too large to display: 879 B] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerMarkets.cs ================================================ [File too large to display: 860 B] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerMicrosoftEntraIdAuthenticationInfo.cs ================================================ [File too large to display: 842 B] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerNestedInstallerFile.cs ================================================ [File too large to display: 820 B] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerPackageDependency.cs ================================================ [File too large to display: 873 B] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/InstallerSwitches.cs ================================================ [File too large to display: 4.7 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/Manifest.cs ================================================ [File too large to display: 17.5 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/ManifestDocumentation.cs ================================================ [File too large to display: 779 B] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/ManifestIcon.cs ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/ManifestInstaller.cs ================================================ [File too large to display: 10.5 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/ManifestInstallerFile.cs ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/ManifestLocalization.cs ================================================ [File too large to display: 5.9 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/ManifestShadow.cs ================================================ [File too large to display: 5.4 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/ManifestShadowLocalization.cs ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/MinManifestInfo.cs ================================================ [File too large to display: 6.0 KB] ================================================ FILE: src/WinGetUtilInterop/Manifest/V1/PackageAgreement.cs ================================================ [File too large to display: 924 B] ================================================ FILE: src/WinGetUtilInterop/WinGetUtilInterop.csproj ================================================ [File too large to display: 552 B] ================================================ FILE: src/WinGetUtilInterop/scripts/CreateLocalNuget.ps1 ================================================ [File too large to display: 3.5 KB] ================================================ FILE: src/WinGetUtilInterop/scripts/WinGetUtilDev.nuspec ================================================ [File too large to display: 1.9 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/APIUnitTests/ManifestUnitTests.cs ================================================ [File too large to display: 7.0 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/APIUnitTests/SQLiteIndexUnitTests.cs ================================================ [File too large to display: 13.6 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/Common/DisplayTestMethodNameAttribute.cs ================================================ [File too large to display: 1.3 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/Common/FactSkipx64CI.cs ================================================ [File too large to display: 970 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/ManifestUnitTest/ManifestEqualityUnitTests.cs ================================================ [File too large to display: 7.1 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/ManifestUnitTest/V1ManifestReadTest.cs ================================================ [File too large to display: 29.7 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/AllEquality.yaml ================================================ [File too large to display: 741 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/AllEqualityWithDescription.yaml ================================================ [File too large to display: 777 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/DifferentId.yaml ================================================ [File too large to display: 742 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/ExpectedShadowManifest.yaml ================================================ [File too large to display: 679 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/OneInstaller.yaml ================================================ [File too large to display: 441 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/PackageTest.yaml ================================================ [File too large to display: 474 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/PackageTestNewName.yaml ================================================ [File too large to display: 477 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/PackageTestNewVersion.yaml ================================================ [File too large to display: 474 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/SomeEquality.yaml ================================================ [File too large to display: 423 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/SomeEqualityWithLocalization.yaml ================================================ [File too large to display: 655 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/SomeEqualityWithoutInstallers.yaml ================================================ [File too large to display: 68 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/SomeEqualityWithoutSwitches.yaml ================================================ [File too large to display: 382 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/Test_yaml_with_bom.yaml ================================================ [File too large to display: 561 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/Test_yaml_without_bom.yaml ================================================ [File too large to display: 558 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1ManifestInfoMissingRequiredPackageLocale.yaml ================================================ [File too large to display: 733 B] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1ManifestMerged.yaml ================================================ [File too large to display: 3.8 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1ManifestNoLocalization.yaml ================================================ [File too large to display: 3.0 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1_10ManifestMerged.yaml ================================================ [File too large to display: 7.6 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1_12ManifestMerged.yaml ================================================ [File too large to display: 8.3 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1_1ManifestMerged.yaml ================================================ [File too large to display: 5.1 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1_6ManifestMerged.yaml ================================================ [File too large to display: 7.1 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1_7ManifestMerged.yaml ================================================ [File too large to display: 7.2 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/TestCollateral/V1_9ManifestMerged.yaml ================================================ [File too large to display: 7.3 KB] ================================================ FILE: src/WinGetUtilInterop.UnitTests/WinGetUtilInterop.UnitTests.csproj ================================================ [File too large to display: 5.7 KB] ================================================ FILE: src/WinGetYamlFuzzing/OneFuzzConfig.json ================================================ [File too large to display: 990 B] ================================================ FILE: src/WinGetYamlFuzzing/README.md ================================================ [File too large to display: 1.8 KB] ================================================ FILE: src/WinGetYamlFuzzing/WinGetYamlFuzzing.cpp ================================================ [File too large to display: 1.6 KB] ================================================ FILE: src/WinGetYamlFuzzing/WinGetYamlFuzzing.vcxproj ================================================ [File too large to display: 7.4 KB] ================================================ FILE: src/WinGetYamlFuzzing/WinGetYamlFuzzing.vcxproj.filters ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/WinGetYamlFuzzing/dictionary.txt ================================================ [File too large to display: 123 B] ================================================ FILE: src/WinGetYamlFuzzing/packages.config ================================================ [File too large to display: 269 B] ================================================ FILE: src/WindowsPackageManager/ConfigurationStaticFunctions.cpp ================================================ [File too large to display: 14.2 KB] ================================================ FILE: src/WindowsPackageManager/PropertySheet.props ================================================ [File too large to display: 605 B] ================================================ FILE: src/WindowsPackageManager/Source.def ================================================ [File too large to display: 692 B] ================================================ FILE: src/WindowsPackageManager/WindowsPackageManager.h ================================================ [File too large to display: 2.5 KB] ================================================ FILE: src/WindowsPackageManager/WindowsPackageManager.vcxproj ================================================ [File too large to display: 34.3 KB] ================================================ FILE: src/WindowsPackageManager/WindowsPackageManager.vcxproj.filters ================================================ [File too large to display: 1.5 KB] ================================================ FILE: src/WindowsPackageManager/main.cpp ================================================ [File too large to display: 6.4 KB] ================================================ FILE: src/WindowsPackageManager/packages.config ================================================ [File too large to display: 269 B] ================================================ FILE: src/Xlang/README.md ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/README.md ================================================ [File too large to display: 3.8 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/UndockedRegFreeWinRT.vcxproj ================================================ [File too large to display: 18.2 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/UndockedRegFreeWinRT.vcxproj.filters ================================================ [File too large to display: 2.2 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/catalog.cpp ================================================ [File too large to display: 12.5 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/catalog.h ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/cpp.hint ================================================ [File too large to display: 110 B] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/dllmain.cpp ================================================ [File too large to display: 16.1 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/extwinrt.h ================================================ [File too large to display: 111 B] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/packages.config ================================================ [File too large to display: 174 B] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/typeresolution.cpp ================================================ [File too large to display: 25.1 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/typeresolution.h ================================================ [File too large to display: 4.5 KB] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/winrtact.def ================================================ [File too large to display: 97 B] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/mwinrtact/mwinrtact.cs ================================================ [File too large to display: 330 B] ================================================ FILE: src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/mwinrtact/mwinrtact.csproj ================================================ [File too large to display: 138 B] ================================================ FILE: src/binver/Update-BinVer.ps1 ================================================ [File too large to display: 3.3 KB] ================================================ FILE: src/binver/binver/resource.h ================================================ [File too large to display: 401 B] ================================================ FILE: src/binver/binver/version.h ================================================ [File too large to display: 1.2 KB] ================================================ FILE: src/binver/binver/version.rc ================================================ [File too large to display: 2.4 KB] ================================================ FILE: src/binver/binver.vcxitems ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/manifest/shared.manifest ================================================ [File too large to display: 1.1 KB] ================================================ FILE: src/nuget.config ================================================ [File too large to display: 277 B] ================================================ FILE: src/stylecop.json ================================================ [File too large to display: 548 B] ================================================ FILE: src/targets/EmbeddedCsWinRT.targets ================================================ [File too large to display: 1.9 KB] ================================================ FILE: src/targets/ReferenceEmbeddedCsWinRTProject.targets ================================================ [File too large to display: 3.2 KB] ================================================ FILE: src/vcpkg.json ================================================ [File too large to display: 998 B] ================================================ FILE: src/vcpkg.props ================================================ [File too large to display: 4.3 KB] ================================================ FILE: templates/e2e-setup.yml ================================================ [File too large to display: 2.7 KB] ================================================ FILE: templates/e2e-test.template.yml ================================================ [File too large to display: 2.7 KB] ================================================ FILE: tools/COMTrace/ComTrace.wprp ================================================ [File too large to display: 2.2 KB] ================================================ FILE: tools/CorrelationTestbed/InSandboxScript.ps1 ================================================ [File too large to display: 3.6 KB] ================================================ FILE: tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp ================================================ [File too large to display: 26.4 KB] ================================================ FILE: tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj ================================================ [File too large to display: 9.9 KB] ================================================ FILE: tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj.filters ================================================ [File too large to display: 1.1 KB] ================================================ FILE: tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/packages.config ================================================ [File too large to display: 269 B] ================================================ FILE: tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation.sln ================================================ [File too large to display: 1.5 KB] ================================================ FILE: tools/CorrelationTestbed/Process-CorrelationResults.ps1 ================================================ [File too large to display: 3.3 KB] ================================================ FILE: tools/CorrelationTestbed/Readme.md ================================================ [File too large to display: 3.5 KB] ================================================ FILE: tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 ================================================ [File too large to display: 12.0 KB] ================================================ FILE: tools/DevInSandbox/InSandboxScript.ps1 ================================================ [File too large to display: 763 B] ================================================ FILE: tools/DevInSandbox/Launch-DevPackageInSandbox.ps1 ================================================ [File too large to display: 6.9 KB] ================================================ FILE: tools/HAMTrace/WER.HostActivityManager.wprp ================================================ [File too large to display: 13.7 KB] ================================================ FILE: tools/IndexComparisonTool/IndexComparisonTool.vcxproj ================================================ [File too large to display: 4.7 KB] ================================================ FILE: tools/IndexComparisonTool/IndexComparisonTool.vcxproj.filters ================================================ [File too large to display: 1007 B] ================================================ FILE: tools/IndexComparisonTool/WinGetUtil.h ================================================ [File too large to display: 1.1 KB] ================================================ FILE: tools/IndexComparisonTool/main.cpp ================================================ [File too large to display: 26.2 KB] ================================================ FILE: tools/IndexComparisonTool/pch.cpp ================================================ [File too large to display: 95 B] ================================================ FILE: tools/IndexComparisonTool/pch.h ================================================ [File too large to display: 474 B] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/App.cpp ================================================ [File too large to display: 4.3 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/App.h ================================================ [File too large to display: 547 B] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/App.idl ================================================ [File too large to display: 33 B] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/App.xaml ================================================ [File too large to display: 326 B] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/AppInstallerCaller.vcxproj ================================================ [File too large to display: 9.2 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/AppInstallerCaller.vcxproj.filters ================================================ [File too large to display: 1.9 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/InstallingPackageView.cpp ================================================ [File too large to display: 2.5 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/InstallingPackageView.h ================================================ [File too large to display: 1.8 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/MainPage.cpp ================================================ [File too large to display: 30.3 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/MainPage.h ================================================ [File too large to display: 5.8 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/MainPage.idl ================================================ [File too large to display: 1.3 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/MainPage.xaml ================================================ [File too large to display: 6.4 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/Package.appxmanifest ================================================ [File too large to display: 1.7 KB] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/PropertySheet.props ================================================ [File too large to display: 585 B] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/packages.config ================================================ [File too large to display: 158 B] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/pch.cpp ================================================ [File too large to display: 95 B] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller/pch.h ================================================ [File too large to display: 621 B] ================================================ FILE: tools/SampleWinGetUWPCaller/AppInstallerCaller.sln ================================================ [File too large to display: 2.2 KB]